From f083369183e0b72071a3b145c5eb84bac9470b1b Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 10 Nov 2023 08:41:58 +0800 Subject: [PATCH] .. --- yazi-core/src/help/commands/arrow.rs | 56 ++++++ yazi-core/src/help/commands/escape.rs | 21 +++ yazi-core/src/help/commands/filter.rs | 17 ++ yazi-core/src/help/commands/mod.rs | 3 + yazi-core/src/help/help.rs | 69 +------ yazi-core/src/help/mod.rs | 1 + yazi-core/src/input/commands/backward.rs | 35 ++++ yazi-core/src/input/commands/close.rs | 34 ++++ yazi-core/src/input/commands/delete.rs | 35 ++++ yazi-core/src/input/commands/escape.rs | 36 ++++ yazi-core/src/input/commands/forward.rs | 42 +++++ yazi-core/src/input/commands/insert.rs | 29 +++ yazi-core/src/input/commands/mod.rs | 12 ++ yazi-core/src/input/commands/move_.rs | 57 ++++++ yazi-core/src/input/commands/paste.rs | 31 ++++ yazi-core/src/input/commands/redo.rs | 13 ++ yazi-core/src/input/commands/undo.rs | 21 +++ yazi-core/src/input/commands/visual.rs | 14 ++ yazi-core/src/input/commands/yank.rs | 30 +++ yazi-core/src/input/input.rs | 223 +---------------------- yazi-core/src/tab/commands/cd.rs | 2 +- yazi-core/src/tasks/commands/mod.rs | 1 + yazi-core/src/tasks/commands/toggle.rs | 20 ++ yazi-core/src/tasks/tasks.rs | 6 - yazi-fm/src/executor.rs | 124 +++++++------ 25 files changed, 596 insertions(+), 336 deletions(-) create mode 100644 yazi-core/src/help/commands/arrow.rs create mode 100644 yazi-core/src/help/commands/escape.rs create mode 100644 yazi-core/src/help/commands/filter.rs create mode 100644 yazi-core/src/help/commands/mod.rs create mode 100644 yazi-core/src/input/commands/backward.rs create mode 100644 yazi-core/src/input/commands/close.rs create mode 100644 yazi-core/src/input/commands/delete.rs create mode 100644 yazi-core/src/input/commands/escape.rs create mode 100644 yazi-core/src/input/commands/forward.rs create mode 100644 yazi-core/src/input/commands/insert.rs create mode 100644 yazi-core/src/input/commands/move_.rs create mode 100644 yazi-core/src/input/commands/paste.rs create mode 100644 yazi-core/src/input/commands/redo.rs create mode 100644 yazi-core/src/input/commands/undo.rs create mode 100644 yazi-core/src/input/commands/visual.rs create mode 100644 yazi-core/src/input/commands/yank.rs create mode 100644 yazi-core/src/tasks/commands/toggle.rs diff --git a/yazi-core/src/help/commands/arrow.rs b/yazi-core/src/help/commands/arrow.rs new file mode 100644 index 00000000..af4ca625 --- /dev/null +++ b/yazi-core/src/help/commands/arrow.rs @@ -0,0 +1,56 @@ +use yazi_config::keymap::Exec; + +use crate::help::Help; + +pub struct Opt { + step: isize, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { + Self { step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0) } + } +} +impl From for Opt { + fn from(step: isize) -> Self { Self { step } } +} + +impl Help { + #[inline] + pub fn arrow(&mut self, opt: impl Into) -> bool { + let max = self.bindings.len().saturating_sub(1); + self.offset = self.offset.min(max); + self.cursor = self.cursor.min(max); + + let opt = opt.into() as Opt; + if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) } + } + + fn next(&mut self, step: usize) -> bool { + let len = self.bindings.len(); + if len == 0 { + return false; + } + + let old = self.cursor; + self.cursor = (self.cursor + step).min(len - 1); + + let limit = Self::limit(); + if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) { + self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old); + } + + old != self.cursor + } + + fn prev(&mut self, step: usize) -> bool { + let old = self.cursor; + self.cursor = self.cursor.saturating_sub(step); + + if self.cursor < self.offset + 5 { + self.offset = self.offset.saturating_sub(old - self.cursor); + } + + old != self.cursor + } +} diff --git a/yazi-core/src/help/commands/escape.rs b/yazi-core/src/help/commands/escape.rs new file mode 100644 index 00000000..e27cbec3 --- /dev/null +++ b/yazi-core/src/help/commands/escape.rs @@ -0,0 +1,21 @@ +use yazi_config::keymap::Exec; + +use crate::help::Help; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Help { + pub fn escape(&mut self, _: impl Into) -> bool { + if self.in_filter.is_some() { + self.in_filter = None; + self.filter_apply(); + true + } else { + self.toggle(self.layer) + } + } +} diff --git a/yazi-core/src/help/commands/filter.rs b/yazi-core/src/help/commands/filter.rs new file mode 100644 index 00000000..54865299 --- /dev/null +++ b/yazi-core/src/help/commands/filter.rs @@ -0,0 +1,17 @@ +use yazi_config::keymap::Exec; + +use crate::help::Help; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Help { + pub fn filter(&mut self, _: impl Into) -> bool { + self.in_filter = Some(Default::default()); + self.filter_apply(); + true + } +} diff --git a/yazi-core/src/help/commands/mod.rs b/yazi-core/src/help/commands/mod.rs new file mode 100644 index 00000000..c1e9ca7a --- /dev/null +++ b/yazi-core/src/help/commands/mod.rs @@ -0,0 +1,3 @@ +mod arrow; +mod escape; +mod filter; diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index a43d3481..97696135 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -7,16 +7,16 @@ use crate::{emit, input::Input}; #[derive(Default)] pub struct Help { - pub visible: bool, - pub layer: KeymapLayer, - bindings: Vec, + pub visible: bool, + pub layer: KeymapLayer, + pub(super) bindings: Vec, // Filter - keyword: Option, - in_filter: Option, + keyword: Option, + pub(super) in_filter: Option, - offset: usize, - cursor: usize, + pub(super) offset: usize, + pub(super) cursor: usize, } impl Help { @@ -38,60 +38,7 @@ impl Help { true } - pub fn escape(&mut self) -> bool { - if self.in_filter.is_some() { - self.in_filter = None; - self.filter_apply(); - true - } else { - self.toggle(self.layer) - } - } - - #[inline] - pub fn arrow(&mut self, step: isize) -> bool { - let max = self.bindings.len().saturating_sub(1); - self.offset = self.offset.min(max); - self.cursor = self.cursor.min(max); - - if step > 0 { self.next(step as usize) } else { self.prev(step.unsigned_abs()) } - } - - pub fn next(&mut self, step: usize) -> bool { - let len = self.bindings.len(); - if len == 0 { - return false; - } - - let old = self.cursor; - self.cursor = (self.cursor + step).min(len - 1); - - let limit = Self::limit(); - if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) { - self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old); - } - - old != self.cursor - } - - pub fn prev(&mut self, step: usize) -> bool { - let old = self.cursor; - self.cursor = self.cursor.saturating_sub(step); - - if self.cursor < self.offset + 5 { - self.offset = self.offset.saturating_sub(old - self.cursor); - } - - old != self.cursor - } - - pub fn filter(&mut self) -> bool { - self.in_filter = Some(Default::default()); - self.filter_apply(); - true - } - - fn filter_apply(&mut self) -> bool { + pub(super) fn filter_apply(&mut self) -> bool { let kw = self.in_filter.as_ref().map(|i| i.value()).filter(|v| !v.is_empty()); if self.keyword.as_deref() == kw { return false; diff --git a/yazi-core/src/help/mod.rs b/yazi-core/src/help/mod.rs index 6525211a..3420abd0 100644 --- a/yazi-core/src/help/mod.rs +++ b/yazi-core/src/help/mod.rs @@ -1,3 +1,4 @@ +mod commands; mod help; pub use help::*; diff --git a/yazi-core/src/input/commands/backward.rs b/yazi-core/src/input/commands/backward.rs new file mode 100644 index 00000000..4105e414 --- /dev/null +++ b/yazi-core/src/input/commands/backward.rs @@ -0,0 +1,35 @@ +use yazi_config::keymap::Exec; +use yazi_shared::CharKind; + +use crate::input::Input; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Input { + pub fn backward(&mut self, _: impl Into) -> bool { + let snap = self.snap(); + if snap.cursor == 0 { + return self.move_(0); + } + + let idx = snap.idx(snap.cursor).unwrap_or(snap.len()); + let mut it = snap.value[..idx].chars().rev().enumerate(); + let mut prev = CharKind::new(it.next().unwrap().1); + for (i, c) in it { + let c = CharKind::new(c); + if prev != CharKind::Space && prev != c { + return self.move_(-(i as isize)); + } + prev = c; + } + + if prev != CharKind::Space { + return self.move_(-(snap.len() as isize)); + } + false + } +} diff --git a/yazi-core/src/input/commands/close.rs b/yazi-core/src/input/commands/close.rs new file mode 100644 index 00000000..0dd4d8ad --- /dev/null +++ b/yazi-core/src/input/commands/close.rs @@ -0,0 +1,34 @@ +use yazi_config::keymap::{Exec, KeymapLayer}; +use yazi_shared::InputError; + +use crate::{emit, input::Input}; + +pub struct Opt { + submit: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { Self { submit: e.named.contains_key("submit") } } +} +impl From for Opt { + fn from(submit: bool) -> Self { Self { submit } } +} + +impl Input { + pub fn close(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + + if self.completion { + emit!(Call(Exec::call("close", vec![]).vec(), KeymapLayer::Completion)); + } + + if let Some(cb) = self.callback.take() { + let value = self.snap_mut().value.clone(); + _ = cb.send(if opt.submit { Ok(value) } else { Err(InputError::Canceled(value)) }); + } + + self.ticket = self.ticket.wrapping_add(1); + self.visible = false; + true + } +} diff --git a/yazi-core/src/input/commands/delete.rs b/yazi-core/src/input/commands/delete.rs new file mode 100644 index 00000000..b36e6905 --- /dev/null +++ b/yazi-core/src/input/commands/delete.rs @@ -0,0 +1,35 @@ +use yazi_config::keymap::Exec; + +use crate::input::{op::InputOp, Input}; + +pub struct Opt { + cut: bool, + insert: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { + Self { cut: e.named.contains_key("cut"), insert: e.named.contains_key("insert") } + } +} + +impl Input { + pub fn delete(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + match self.snap().op { + InputOp::None => { + self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, self.snap().cursor); + false + } + InputOp::Select(start) => { + self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, start); + return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some(); + } + InputOp::Delete(..) => { + self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, 0); + return self.move_(self.snap().len() as isize); + } + _ => false, + } + } +} diff --git a/yazi-core/src/input/commands/escape.rs b/yazi-core/src/input/commands/escape.rs new file mode 100644 index 00000000..4e260bc8 --- /dev/null +++ b/yazi-core/src/input/commands/escape.rs @@ -0,0 +1,36 @@ +use yazi_config::keymap::{Exec, KeymapLayer}; + +use crate::{emit, input::{op::InputOp, Input, InputMode}}; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} +impl From<()> for Opt { + fn from(_: ()) -> Self { Self } +} + +impl Input { + pub fn escape(&mut self, _: impl Into) -> bool { + let snap = self.snap_mut(); + match snap.mode { + InputMode::Normal if snap.op == InputOp::None => { + self.close(false); + } + InputMode::Normal => { + snap.op = InputOp::None; + } + InputMode::Insert => { + snap.mode = InputMode::Normal; + self.move_(-1); + + if self.completion { + emit!(Call(Exec::call("close", vec![]).vec(), KeymapLayer::Completion)); + } + } + } + self.snaps.tag(); + true + } +} diff --git a/yazi-core/src/input/commands/forward.rs b/yazi-core/src/input/commands/forward.rs new file mode 100644 index 00000000..55cd685e --- /dev/null +++ b/yazi-core/src/input/commands/forward.rs @@ -0,0 +1,42 @@ +use yazi_config::keymap::Exec; +use yazi_shared::CharKind; + +use crate::input::{op::InputOp, Input}; + +pub struct Opt { + end_of_word: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { Self { end_of_word: e.named.contains_key("end-of-word") } } +} + +impl Input { + pub fn forward(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + + let snap = self.snap(); + if snap.value.is_empty() { + return self.move_(0); + } + + let mut it = snap.value.chars().skip(snap.cursor).enumerate(); + let mut prev = CharKind::new(it.next().unwrap().1); + for (i, c) in it { + let c = CharKind::new(c); + let b = if opt.end_of_word { + prev != CharKind::Space && prev != c && i != 1 + } else { + c != CharKind::Space && c != prev + }; + if b && !matches!(snap.op, InputOp::None | InputOp::Select(_)) { + return self.move_(i as isize); + } else if b { + return self.move_(if opt.end_of_word { i - 1 } else { i } as isize); + } + prev = c; + } + + self.move_(snap.len() as isize) + } +} diff --git a/yazi-core/src/input/commands/insert.rs b/yazi-core/src/input/commands/insert.rs new file mode 100644 index 00000000..4e1c8758 --- /dev/null +++ b/yazi-core/src/input/commands/insert.rs @@ -0,0 +1,29 @@ +use yazi_config::keymap::Exec; + +use crate::input::Input; + +pub struct Opt { + append: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { Self { append: e.named.contains_key("append") } } +} +impl From for Opt { + fn from(append: bool) -> Self { Self { append } } +} + +impl Input { + pub fn insert(&mut self, opt: impl Into) -> bool { + if !self.snap_mut().insert() { + return false; + } + + let opt = opt.into() as Opt; + if opt.append { + self.move_(1); + } + + true + } +} diff --git a/yazi-core/src/input/commands/mod.rs b/yazi-core/src/input/commands/mod.rs index df7f200d..da2185be 100644 --- a/yazi-core/src/input/commands/mod.rs +++ b/yazi-core/src/input/commands/mod.rs @@ -1 +1,13 @@ +mod backward; +mod close; mod complete; +mod delete; +mod escape; +mod forward; +mod insert; +mod move_; +mod paste; +mod redo; +mod undo; +mod visual; +mod yank; diff --git a/yazi-core/src/input/commands/move_.rs b/yazi-core/src/input/commands/move_.rs new file mode 100644 index 00000000..cda394e0 --- /dev/null +++ b/yazi-core/src/input/commands/move_.rs @@ -0,0 +1,57 @@ +use unicode_width::UnicodeWidthStr; +use yazi_config::keymap::Exec; + +use crate::input::{op::InputOp, snap::InputSnap, Input}; + +pub struct Opt { + step: isize, + in_operating: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { + Self { + step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0), + in_operating: e.named.contains_key("in-operating"), + } + } +} +impl From for Opt { + fn from(step: isize) -> Self { Self { step, in_operating: false } } +} + +impl Input { + pub fn move_(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + + let snap = self.snap(); + if opt.in_operating && snap.op == InputOp::None { + return false; + } + + let b = self.handle_op( + if opt.step <= 0 { + snap.cursor.saturating_sub(opt.step.unsigned_abs()) + } else { + snap.count().min(snap.cursor + opt.step as usize) + }, + false, + ); + + let snap = self.snap_mut(); + if snap.cursor < snap.offset { + snap.offset = snap.cursor; + } else if snap.value.is_empty() { + snap.offset = 0; + } else { + let delta = snap.mode.delta(); + let s = snap.slice(snap.offset..snap.cursor + delta); + if s.width() >= /*TODO: hardcode*/ 50 - 2 { + let s = s.chars().rev().collect::(); + snap.offset = snap.cursor - InputSnap::find_window(&s, 0).end.saturating_sub(delta); + } + } + + b + } +} diff --git a/yazi-core/src/input/commands/paste.rs b/yazi-core/src/input/commands/paste.rs new file mode 100644 index 00000000..5145845d --- /dev/null +++ b/yazi-core/src/input/commands/paste.rs @@ -0,0 +1,31 @@ +use yazi_config::keymap::Exec; + +use crate::{external, input::{op::InputOp, Input}}; + +pub struct Opt { + before: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { Self { before: e.named.contains_key("before") } } +} + +impl Input { + pub fn paste(&mut self, opt: impl Into) -> bool { + if let Some(start) = self.snap().op.start() { + self.snap_mut().op = InputOp::Delete(false, false, start); + self.handle_op(self.snap().cursor, true); + } + + let s = futures::executor::block_on(external::clipboard_get()).unwrap_or_default(); + if s.is_empty() { + return false; + } + + let opt = opt.into() as Opt; + self.insert(!opt.before); + self.type_str(&s.to_string_lossy()); + self.escape(()); + true + } +} diff --git a/yazi-core/src/input/commands/redo.rs b/yazi-core/src/input/commands/redo.rs new file mode 100644 index 00000000..1722d664 --- /dev/null +++ b/yazi-core/src/input/commands/redo.rs @@ -0,0 +1,13 @@ +use yazi_config::keymap::Exec; + +use crate::input::Input; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Input { + pub fn redo(&mut self, _: impl Into) -> bool { self.snaps.redo() } +} diff --git a/yazi-core/src/input/commands/undo.rs b/yazi-core/src/input/commands/undo.rs new file mode 100644 index 00000000..6ef8d81e --- /dev/null +++ b/yazi-core/src/input/commands/undo.rs @@ -0,0 +1,21 @@ +use yazi_config::keymap::Exec; + +use crate::input::{Input, InputMode}; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Input { + pub fn undo(&mut self, _: impl Into) -> bool { + if !self.snaps.undo() { + return false; + } + if self.snap().mode == InputMode::Insert { + self.escape(()); + } + true + } +} diff --git a/yazi-core/src/input/commands/visual.rs b/yazi-core/src/input/commands/visual.rs new file mode 100644 index 00000000..509dc084 --- /dev/null +++ b/yazi-core/src/input/commands/visual.rs @@ -0,0 +1,14 @@ +use yazi_config::keymap::Exec; + +use crate::input::Input; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Input { + #[inline] + pub fn visual(&mut self, _: impl Into) -> bool { self.snap_mut().visual() } +} diff --git a/yazi-core/src/input/commands/yank.rs b/yazi-core/src/input/commands/yank.rs new file mode 100644 index 00000000..e8c32c6c --- /dev/null +++ b/yazi-core/src/input/commands/yank.rs @@ -0,0 +1,30 @@ +use yazi_config::keymap::Exec; + +use crate::input::{op::InputOp, Input}; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Input { + pub fn yank(&mut self, _: impl Into) -> bool { + match self.snap().op { + InputOp::None => { + self.snap_mut().op = InputOp::Yank(self.snap().cursor); + false + } + InputOp::Select(start) => { + self.snap_mut().op = InputOp::Yank(start); + return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some(); + } + InputOp::Yank(_) => { + self.snap_mut().op = InputOp::Yank(0); + self.move_(self.snap().len() as isize); + false + } + _ => false, + } + } +} diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index d9df0339..08476fea 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -3,11 +3,11 @@ use std::ops::Range; use crossterm::event::KeyCode; use tokio::sync::mpsc::UnboundedSender; use unicode_width::UnicodeWidthStr; -use yazi_config::keymap::{Exec, Key, KeymapLayer}; -use yazi_shared::{CharKind, InputError}; +use yazi_config::keymap::Key; +use yazi_shared::InputError; use super::{mode::InputMode, op::InputOp, InputOpt, InputSnap, InputSnaps}; -use crate::{emit, external, Position}; +use crate::{external, Position}; #[derive(Default)] pub struct Input { @@ -19,9 +19,9 @@ pub struct Input { pub position: Position, // Typing - callback: Option>>, - realtime: bool, - completion: bool, + pub(super) callback: Option>>, + realtime: bool, + pub(super) completion: bool, // Shell pub(super) highlight: bool, @@ -45,157 +45,6 @@ impl Input { self.highlight = opt.highlight; } - pub fn close(&mut self, submit: bool) -> bool { - if self.completion { - emit!(Call(Exec::call("close", vec![]).vec(), KeymapLayer::Completion)); - } - - if let Some(cb) = self.callback.take() { - let value = self.snap_mut().value.clone(); - _ = cb.send(if submit { Ok(value) } else { Err(InputError::Canceled(value)) }); - } - - self.ticket = self.ticket.wrapping_add(1); - self.visible = false; - true - } - - pub fn escape(&mut self) -> bool { - let snap = self.snap_mut(); - match snap.mode { - InputMode::Normal if snap.op == InputOp::None => { - self.close(false); - } - InputMode::Normal => { - snap.op = InputOp::None; - } - InputMode::Insert => { - snap.mode = InputMode::Normal; - self.move_(-1); - - if self.completion { - emit!(Call(Exec::call("close", vec![]).vec(), KeymapLayer::Completion)); - } - } - } - self.snaps.tag(); - true - } - - pub fn insert(&mut self, append: bool) -> bool { - if !self.snap_mut().insert() { - return false; - } - if append { - self.move_(1); - } - true - } - - #[inline] - pub fn visual(&mut self) -> bool { self.snap_mut().visual() } - - #[inline] - pub fn undo(&mut self) -> bool { - if !self.snaps.undo() { - return false; - } - if self.snap().mode == InputMode::Insert { - self.escape(); - } - true - } - - #[inline] - pub fn redo(&mut self) -> bool { - if !self.snaps.redo() { - return false; - } - true - } - - pub fn move_(&mut self, step: isize) -> bool { - let snap = self.snap(); - let b = self.handle_op( - if step <= 0 { - snap.cursor.saturating_sub(step.unsigned_abs()) - } else { - snap.count().min(snap.cursor + step as usize) - }, - false, - ); - - let snap = self.snap_mut(); - if snap.cursor < snap.offset { - snap.offset = snap.cursor; - } else if snap.value.is_empty() { - snap.offset = 0; - } else { - let delta = snap.mode.delta(); - let s = snap.slice(snap.offset..snap.cursor + delta); - if s.width() >= /*TODO: hardcode*/ 50 - 2 { - let s = s.chars().rev().collect::(); - snap.offset = snap.cursor - InputSnap::find_window(&s, 0).end.saturating_sub(delta); - } - } - - b - } - - #[inline] - pub fn move_in_operating(&mut self, step: isize) -> bool { - if self.snap_mut().op == InputOp::None { false } else { self.move_(step) } - } - - pub fn backward(&mut self) -> bool { - let snap = self.snap(); - if snap.cursor == 0 { - return self.move_(0); - } - - let idx = snap.idx(snap.cursor).unwrap_or(snap.len()); - let mut it = snap.value[..idx].chars().rev().enumerate(); - let mut prev = CharKind::new(it.next().unwrap().1); - for (i, c) in it { - let c = CharKind::new(c); - if prev != CharKind::Space && prev != c { - return self.move_(-(i as isize)); - } - prev = c; - } - - if prev != CharKind::Space { - return self.move_(-(snap.len() as isize)); - } - false - } - - pub fn forward(&mut self, end: bool) -> bool { - let snap = self.snap(); - if snap.value.is_empty() { - return self.move_(0); - } - - let mut it = snap.value.chars().skip(snap.cursor).enumerate(); - let mut prev = CharKind::new(it.next().unwrap().1); - for (i, c) in it { - let c = CharKind::new(c); - let b = if end { - prev != CharKind::Space && prev != c && i != 1 - } else { - c != CharKind::Space && c != prev - }; - if b && !matches!(snap.op, InputOp::None | InputOp::Select(_)) { - return self.move_(i as isize); - } else if b { - return self.move_(if end { i - 1 } else { i } as isize); - } - prev = c; - } - - self.move_(snap.len() as isize) - } - pub fn type_(&mut self, key: &Key) -> bool { if self.mode() != InputMode::Insert { return false; @@ -238,61 +87,7 @@ impl Input { true } - pub fn delete(&mut self, cut: bool, insert: bool) -> bool { - match self.snap().op { - InputOp::None => { - self.snap_mut().op = InputOp::Delete(cut, insert, self.snap().cursor); - false - } - InputOp::Select(start) => { - self.snap_mut().op = InputOp::Delete(cut, insert, start); - return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some(); - } - InputOp::Delete(..) => { - self.snap_mut().op = InputOp::Delete(cut, insert, 0); - return self.move_(self.snap().len() as isize); - } - _ => false, - } - } - - pub fn yank(&mut self) -> bool { - match self.snap().op { - InputOp::None => { - self.snap_mut().op = InputOp::Yank(self.snap().cursor); - false - } - InputOp::Select(start) => { - self.snap_mut().op = InputOp::Yank(start); - return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some(); - } - InputOp::Yank(_) => { - self.snap_mut().op = InputOp::Yank(0); - self.move_(self.snap().len() as isize); - false - } - _ => false, - } - } - - pub fn paste(&mut self, before: bool) -> bool { - if let Some(start) = self.snap().op.start() { - self.snap_mut().op = InputOp::Delete(false, false, start); - self.handle_op(self.snap().cursor, true); - } - - let s = futures::executor::block_on(external::clipboard_get()).unwrap_or_default(); - if s.is_empty() { - return false; - } - - self.insert(!before); - self.type_str(&s.to_string_lossy()); - self.escape(); - true - } - - fn handle_op(&mut self, cursor: usize, include: bool) -> bool { + pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool { let old = self.snap().clone(); let snap = self.snaps.current_mut(); @@ -384,8 +179,8 @@ impl Input { } #[inline] - fn snap(&self) -> &InputSnap { self.snaps.current() } + pub(super) fn snap(&self) -> &InputSnap { self.snaps.current() } #[inline] - fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() } + pub(super) fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() } } diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index bf065572..c3b4a322 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -61,7 +61,7 @@ impl Tab { true } - pub fn cd_interactive(&mut self, opt: impl Into) -> bool { + fn cd_interactive(&mut self, opt: impl Into) -> bool { let opt = opt.into() as Opt; tokio::spawn(async move { diff --git a/yazi-core/src/tasks/commands/mod.rs b/yazi-core/src/tasks/commands/mod.rs index cbc6548c..e6c3cafd 100644 --- a/yazi-core/src/tasks/commands/mod.rs +++ b/yazi-core/src/tasks/commands/mod.rs @@ -1,3 +1,4 @@ mod arrow; mod cancel; mod inspect; +mod toggle; diff --git a/yazi-core/src/tasks/commands/toggle.rs b/yazi-core/src/tasks/commands/toggle.rs new file mode 100644 index 00000000..1e335701 --- /dev/null +++ b/yazi-core/src/tasks/commands/toggle.rs @@ -0,0 +1,20 @@ +use yazi_config::keymap::Exec; + +use crate::{emit, tasks::Tasks}; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} +impl From<()> for Opt { + fn from(_: ()) -> Self { Self } +} + +impl Tasks { + pub fn toggle(&mut self, _: impl Into) -> bool { + self.visible = !self.visible; + emit!(Peek); // Show/hide preview for images + true + } +} diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 60270e8c..8cc1b52f 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -31,12 +31,6 @@ impl Tasks { (Term::size().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_PADDING) as usize } - pub fn toggle(&mut self) -> bool { - self.visible = !self.visible; - emit!(Peek); // Show/hide preview for images - true - } - pub fn paginate(&self) -> Vec { let running = self.scheduler.running.read(); running.values().take(Self::limit()).map(Into::into).collect() diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 427efc61..54a7a8c7 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -138,7 +138,7 @@ impl<'a> Executor<'a> { match exec.cmd.as_bytes() { // Tasks - b"tasks_show" => self.cx.tasks.toggle(), + b"tasks_show" => self.cx.tasks.toggle(()), // Help b"help" => self.cx.help.toggle(KeymapLayer::Manager), _ => false, @@ -152,14 +152,19 @@ impl<'a> Executor<'a> { return self.cx.tasks.$name(exec); } }; + ($name:ident, $alias:literal) => { + if exec.cmd == $alias { + return self.cx.tasks.$name(exec); + } + }; } + on!(toggle, "close"); on!(arrow); on!(inspect); on!(cancel); match exec.cmd.as_str() { - "close" => self.cx.tasks.toggle(), "help" => self.cx.help.toggle(KeymapLayer::Tasks), _ => false, } @@ -184,78 +189,89 @@ impl<'a> Executor<'a> { } fn input(&mut self, exec: &Exec) -> bool { - match exec.cmd.as_str() { - "close" => return self.cx.input.close(exec.named.contains_key("submit")), - "escape" => return self.cx.input.escape(), + macro_rules! on { + ($name:ident) => { + if exec.cmd == stringify!($name) { + return self.cx.input.$name(exec); + } + }; + ($name:ident, $alias:literal) => { + if exec.cmd == $alias { + return self.cx.input.$name(exec); + } + }; + } - "move" => { - let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0); - let in_operating = exec.named.contains_key("in-operating"); - return if in_operating { - self.cx.input.move_in_operating(step) - } else { - self.cx.input.move_(step) - }; - } + on!(close); + on!(escape); + on!(move_, "move"); - "complete" => { - return if exec.args.is_empty() { - self.cx.completion.trigger(exec) - } else { - self.cx.input.complete(exec) - }; - } - _ => {} + if exec.cmd.as_str() == "complete" { + return if exec.args.is_empty() { + self.cx.completion.trigger(exec) + } else { + self.cx.input.complete(exec) + }; } match self.cx.input.mode() { - InputMode::Normal => match exec.cmd.as_str() { - "insert" => self.cx.input.insert(exec.named.contains_key("append")), - "visual" => self.cx.input.visual(), + InputMode::Normal => { + on!(insert); + on!(visual); - "backward" => self.cx.input.backward(), - "forward" => self.cx.input.forward(exec.named.contains_key("end-of-word")), - "delete" => { - self.cx.input.delete(exec.named.contains_key("cut"), exec.named.contains_key("insert")) + on!(backward); + on!(forward); + on!(delete); + + on!(yank); + on!(paste); + + on!(undo); + on!(redo); + + match exec.cmd.as_str() { + "help" => self.cx.help.toggle(KeymapLayer::Input), + _ => false, } - - "yank" => self.cx.input.yank(), - "paste" => self.cx.input.paste(exec.named.contains_key("before")), - - "undo" => self.cx.input.undo(), - "redo" => self.cx.input.redo(), - - "help" => self.cx.help.toggle(KeymapLayer::Input), - _ => false, - }, + } InputMode::Insert => false, } } fn help(&mut self, exec: &Exec) -> bool { + macro_rules! on { + ($name:ident) => { + if exec.cmd == stringify!($name) { + return self.cx.help.$name(exec); + } + }; + } + + on!(escape); + on!(arrow); + on!(filter); + match exec.cmd.as_str() { "close" => self.cx.help.toggle(KeymapLayer::Help), - "escape" => self.cx.help.escape(), - - "arrow" => { - let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0); - self.cx.help.arrow(step) - } - - "filter" => self.cx.help.filter(), - _ => false, } } fn completion(&mut self, exec: &Exec) -> bool { + macro_rules! on { + ($name:ident) => { + if exec.cmd == stringify!($name) { + return self.cx.completion.$name(exec); + } + }; + } + + on!(trigger); + on!(show); + on!(close); + on!(arrow); + match exec.cmd.as_str() { - "trigger" => self.cx.completion.trigger(exec), - "show" => self.cx.completion.show(exec), - "close" => self.cx.completion.close(exec), - - "arrow" => self.cx.completion.arrow(exec), - "help" => self.cx.help.toggle(KeymapLayer::Completion), _ => false, }