From 3fb8c34185bc83ba5e36ba6a99355be67c45bf5c Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 19 Mar 2025 20:44:12 +0800 Subject: [PATCH] refactor: new `yazi-widgets` crate --- Cargo.lock | 17 +++ yazi-core/Cargo.toml | 1 + yazi-core/src/cmp/commands/close.rs | 5 +- yazi-core/src/input/commands/close.rs | 12 +- yazi-core/src/input/commands/escape.rs | 29 ++-- yazi-core/src/input/commands/mod.rs | 2 +- yazi-core/src/input/commands/show.rs | 21 ++- yazi-core/src/input/input.rs | 124 ++---------------- yazi-core/src/input/mod.rs | 2 +- yazi-fm/Cargo.toml | 1 + yazi-fm/src/app/app.rs | 2 +- yazi-fm/src/executor.rs | 2 +- yazi-fm/src/input/input.rs | 2 +- yazi-proxy/src/cmp.rs | 4 +- yazi-shared/src/errors/input.rs | 4 +- yazi-widgets/Cargo.toml | 23 ++++ .../src/input/commands/backspace.rs | 0 .../src/input/commands/backward.rs | 0 .../src/input/commands/complete.rs | 14 +- .../src/input/commands/delete.rs | 0 yazi-widgets/src/input/commands/escape.rs | 27 ++++ .../src/input/commands/forward.rs | 0 .../src/input/commands/insert.rs | 0 .../src/input/commands/kill.rs | 0 yazi-widgets/src/input/commands/mod.rs | 1 + .../src/input/commands/move_.rs | 2 +- .../src/input/commands/paste.rs | 0 .../src/input/commands/redo.rs | 0 .../src/input/commands/replace.rs | 2 +- .../src/input/commands/type_.rs | 0 .../src/input/commands/undo.rs | 0 .../src/input/commands/visual.rs | 0 .../src/input/commands/yank.rs | 0 yazi-widgets/src/input/input.rs | 108 +++++++++++++++ yazi-widgets/src/input/mod.rs | 3 + {yazi-core => yazi-widgets}/src/input/mode.rs | 0 {yazi-core => yazi-widgets}/src/input/op.rs | 0 {yazi-core => yazi-widgets}/src/input/snap.rs | 14 +- .../src/input/snaps.rs | 13 +- yazi-widgets/src/lib.rs | 1 + 40 files changed, 255 insertions(+), 181 deletions(-) create mode 100644 yazi-widgets/Cargo.toml rename {yazi-core => yazi-widgets}/src/input/commands/backspace.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/backward.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/complete.rs (78%) rename {yazi-core => yazi-widgets}/src/input/commands/delete.rs (100%) create mode 100644 yazi-widgets/src/input/commands/escape.rs rename {yazi-core => yazi-widgets}/src/input/commands/forward.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/insert.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/kill.rs (100%) create mode 100644 yazi-widgets/src/input/commands/mod.rs rename {yazi-core => yazi-widgets}/src/input/commands/move_.rs (97%) rename {yazi-core => yazi-widgets}/src/input/commands/paste.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/redo.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/replace.rs (92%) rename {yazi-core => yazi-widgets}/src/input/commands/type_.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/undo.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/visual.rs (100%) rename {yazi-core => yazi-widgets}/src/input/commands/yank.rs (100%) create mode 100644 yazi-widgets/src/input/input.rs create mode 100644 yazi-widgets/src/input/mod.rs rename {yazi-core => yazi-widgets}/src/input/mode.rs (100%) rename {yazi-core => yazi-widgets}/src/input/op.rs (100%) rename {yazi-core => yazi-widgets}/src/input/snap.rs (90%) rename {yazi-core => yazi-widgets}/src/input/snaps.rs (81%) create mode 100644 yazi-widgets/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 99986894..f1fb8920 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3433,6 +3433,7 @@ dependencies = [ "yazi-proxy", "yazi-scheduler", "yazi-shared", + "yazi-widgets", ] [[package]] @@ -3501,6 +3502,7 @@ dependencies = [ "yazi-plugin", "yazi-proxy", "yazi-shared", + "yazi-widgets", ] [[package]] @@ -3630,6 +3632,21 @@ dependencies = [ "yazi-macro", ] +[[package]] +name = "yazi-widgets" +version = "25.3.7" +dependencies = [ + "futures", + "tokio", + "unicode-width 0.2.0", + "yazi-codegen", + "yazi-config", + "yazi-macro", + "yazi-plugin", + "yazi-proxy", + "yazi-shared", +] + [[package]] name = "yoke" version = "0.7.5" diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index d7ad6db4..2ad16e76 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -20,6 +20,7 @@ yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } yazi-scheduler = { path = "../yazi-scheduler", version = "25.3.7" } yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-core/src/cmp/commands/close.rs b/yazi-core/src/cmp/commands/close.rs index 81e80634..6e1d3c46 100644 --- a/yazi-core/src/cmp/commands/close.rs +++ b/yazi-core/src/cmp/commands/close.rs @@ -1,3 +1,5 @@ +use std::mem; + use yazi_macro::render; use yazi_proxy::InputProxy; use yazi_shared::event::CmdCow; @@ -23,7 +25,6 @@ impl Cmp { } self.caches.clear(); - self.visible = false; - render!(); + render!(mem::replace(&mut self.visible, false)); } } diff --git a/yazi-core/src/input/commands/close.rs b/yazi-core/src/input/commands/close.rs index a3a5bc26..d792e8d2 100644 --- a/yazi-core/src/input/commands/close.rs +++ b/yazi-core/src/input/commands/close.rs @@ -18,17 +18,15 @@ impl From for Opt { impl Input { #[yazi_codegen::command] pub fn close(&mut self, opt: Opt) { - if self.completion { - CmpProxy::close(); - } + self.visible = false; + self.ticket.next(); - if let Some(cb) = self.callback.take() { - let value = self.snap_mut().value.clone(); + if let Some(cb) = self.tx.take() { + let value = self.snap().value.clone(); _ = cb.send(if opt.submit { Ok(value) } else { Err(InputError::Canceled(value)) }); } - self.ticket = self.ticket.wrapping_add(1); - self.visible = false; + CmpProxy::close(); render!(); } } diff --git a/yazi-core/src/input/commands/escape.rs b/yazi-core/src/input/commands/escape.rs index f376a807..03811ddb 100644 --- a/yazi-core/src/input/commands/escape.rs +++ b/yazi-core/src/input/commands/escape.rs @@ -1,8 +1,9 @@ use yazi_macro::render; use yazi_proxy::CmpProxy; use yazi_shared::event::CmdCow; +use yazi_widgets::input::InputOp; -use crate::input::{Input, InputMode, op::InputOp}; +use crate::input::Input; struct Opt; @@ -16,28 +17,16 @@ impl From<()> for Opt { impl Input { #[yazi_codegen::command] pub fn escape(&mut self, _: Opt) { - 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); + use yazi_widgets::input::InputMode as M; - if self.completion { - CmpProxy::close(); - } - } - InputMode::Replace => { - snap.mode = InputMode::Normal; - } + let mode = self.snap().mode; + match mode { + M::Normal if self.snap_mut().op == InputOp::None => self.close(false), + M::Insert => CmpProxy::close(), + M::Normal | M::Replace => {} } - self.snaps.tag(self.limit()); + self.inner.escape(()); render!(); } } diff --git a/yazi-core/src/input/commands/mod.rs b/yazi-core/src/input/commands/mod.rs index 87017d3f..d23f3447 100644 --- a/yazi-core/src/input/commands/mod.rs +++ b/yazi-core/src/input/commands/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(backspace backward close complete delete escape forward insert kill move_ paste redo replace show type_ undo visual yank); +yazi_macro::mod_flat!(close escape show); diff --git a/yazi-core/src/input/commands/show.rs b/yazi-core/src/input/commands/show.rs index ac4e3879..a13ff121 100644 --- a/yazi-core/src/input/commands/show.rs +++ b/yazi-core/src/input/commands/show.rs @@ -20,7 +20,7 @@ impl TryFrom for Opt { impl Input { pub fn show(&mut self, opt: impl TryInto) { - let Ok(opt) = opt.try_into() else { return }; + let Ok(opt): Result = opt.try_into() else { return }; self.close(false); self.visible = true; @@ -28,17 +28,26 @@ impl Input { self.position = opt.cfg.position; // Typing - self.callback = Some(opt.tx); - self.realtime = opt.cfg.realtime; - self.completion = opt.cfg.completion; + self.tx = Some(opt.tx.clone()); // Shell self.highlight = opt.cfg.highlight; - // Reset snaps - self.snaps.reset(opt.cfg.value, self.limit()); + // Reset input + let ticket = self.ticket.clone(); + let cb: Box = Box::new(move |before, after| { + if opt.cfg.realtime { + opt.tx.send(Err(InputError::Typed(format!("{before}{after}")))).ok(); + } + + if opt.cfg.completion { + opt.tx.send(Err(InputError::Completed(before.to_owned(), ticket.current()))).ok(); + } + }); + self.inner = yazi_widgets::input::Input::new(opt.cfg.value, self.limit, cb); // Set cursor after reset + // TODO: remove this if let Some(cursor) = opt.cfg.cursor { self.snap_mut().cursor = cursor; self.move_(0); diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index 27dff313..879a0463 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -1,131 +1,31 @@ -use std::ops::Range; +use std::{ops::{Deref, DerefMut}, rc::Rc}; use tokio::sync::mpsc::UnboundedSender; -use unicode_width::UnicodeWidthStr; -use yazi_config::{INPUT, popup::Position}; -use yazi_plugin::CLIPBOARD; -use yazi_shared::errors::InputError; - -use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp}; +use yazi_config::popup::Position; +use yazi_shared::{Ids, errors::InputError}; #[derive(Default)] pub struct Input { - pub(super) snaps: InputSnaps, - pub ticket: usize, - pub visible: bool, + pub(super) inner: yazi_widgets::input::Input, + pub visible: bool, pub title: String, pub position: Position, // Typing - pub(super) callback: Option>>, - pub(super) realtime: bool, - pub(super) completion: bool, + pub(super) tx: Option>>, + pub(super) ticket: Rc, // Shell pub highlight: bool, } -impl Input { - #[inline] - pub(super) fn limit(&self) -> usize { - self.position.offset.width.saturating_sub(INPUT.border()) as usize - } +impl Deref for Input { + type Target = yazi_widgets::input::Input; - pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool { - let old = self.snap().clone(); - let snap = self.snap_mut(); - - match snap.op { - InputOp::None | InputOp::Select(_) => { - snap.cursor = cursor; - } - InputOp::Delete(cut, insert, _) => { - let range = snap.op.range(cursor, include).unwrap(); - let Range { start, end } = snap.idx(range.start)..snap.idx(range.end); - - let drain = snap.value.drain(start.unwrap()..end.unwrap()).collect::(); - if cut { - futures::executor::block_on(CLIPBOARD.set(&drain)); - } - - snap.op = InputOp::None; - snap.mode = if insert { InputMode::Insert } else { InputMode::Normal }; - snap.cursor = range.start; - } - InputOp::Yank(_) => { - let range = snap.op.range(cursor, include).unwrap(); - let Range { start, end } = snap.idx(range.start)..snap.idx(range.end); - let yanked = &snap.value[start.unwrap()..end.unwrap()]; - - snap.op = InputOp::None; - futures::executor::block_on(CLIPBOARD.set(yanked)); - } - }; - - snap.cursor = snap.count().saturating_sub(snap.mode.delta()).min(snap.cursor); - if snap == &old { - return false; - } - if !matches!(old.op, InputOp::None | InputOp::Select(_)) { - self.snaps.tag(self.limit()).then(|| self.flush_value()); - } - true - } - - pub(super) fn flush_value(&mut self) { - let Some(tx) = &self.callback else { return }; - self.ticket = self.ticket.wrapping_add(1); - - if self.realtime { - let value = self.snap().value.clone(); - tx.send(Err(InputError::Typed(value))).ok(); - } - - if self.completion { - let before = self.partition()[0].to_owned(); - tx.send(Err(InputError::Completed(before, self.ticket))).ok(); - } - } + fn deref(&self) -> &Self::Target { &self.inner } } -impl Input { - #[inline] - pub fn value(&self) -> &str { self.snap().slice(self.snap().window(self.limit())) } - - #[inline] - pub fn mode(&self) -> InputMode { self.snap().mode } - - #[inline] - pub fn cursor(&self) -> u16 { - let snap = self.snap(); - snap.slice(snap.offset..snap.cursor).width() as u16 - } - - pub fn selected(&self) -> Option> { - let snap = self.snap(); - let start = snap.op.start()?; - - let (start, end) = - if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) }; - - let win = snap.window(self.limit()); - let Range { start, end } = start.max(win.start)..end.min(win.end); - - let s = snap.slice(snap.offset..start).width() as u16; - Some(s..s + snap.slice(start..end).width() as u16) - } - - #[inline] - pub fn partition(&self) -> [&str; 2] { - let snap = self.snap(); - let idx = snap.idx(snap.cursor).unwrap(); - [&snap.value[..idx], &snap.value[idx..]] - } - - #[inline] - pub(super) fn snap(&self) -> &InputSnap { self.snaps.current() } - - #[inline] - pub(super) fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() } +impl DerefMut for Input { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } diff --git a/yazi-core/src/input/mod.rs b/yazi-core/src/input/mod.rs index bc6b58ab..e77b2fe8 100644 --- a/yazi-core/src/input/mod.rs +++ b/yazi-core/src/input/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(commands); -yazi_macro::mod_flat!(input mode op snap snaps); +yazi_macro::mod_flat!(input); diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 4dba97a9..7d9a93c3 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -24,6 +24,7 @@ yazi-macro = { path = "../yazi-macro", version = "25.3.7" } yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index 5d0253ea..2a16e14c 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -3,9 +3,9 @@ use std::sync::atomic::Ordering; use anyhow::Result; use crossterm::event::KeyEvent; use yazi_config::keymap::Key; -use yazi_core::input::InputMode; use yazi_macro::emit; use yazi_shared::event::{CmdCow, Event, NEED_RENDER}; +use yazi_widgets::input::InputMode; use crate::{Ctx, Executor, Router, Signals, Term, lives::Lives}; diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index a5a21700..357713b5 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -1,5 +1,5 @@ -use yazi_core::input::InputMode; use yazi_shared::{Layer, event::CmdCow}; +use yazi_widgets::input::InputMode; use crate::app::App; diff --git a/yazi-fm/src/input/input.rs b/yazi-fm/src/input/input.rs index cb27e8f3..decb2466 100644 --- a/yazi-fm/src/input/input.rs +++ b/yazi-fm/src/input/input.rs @@ -4,8 +4,8 @@ use anyhow::{Result, bail}; use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::{Block, BorderType, Paragraph, Widget}}; use syntect::easy::HighlightLines; use yazi_config::{PREVIEW, THEME}; -use yazi_core::input::InputMode; use yazi_plugin::external::Highlighter; +use yazi_widgets::input::InputMode; use crate::{Ctx, Term}; diff --git a/yazi-proxy/src/cmp.rs b/yazi-proxy/src/cmp.rs index cb02e11c..f33a9b53 100644 --- a/yazi-proxy/src/cmp.rs +++ b/yazi-proxy/src/cmp.rs @@ -1,5 +1,5 @@ use yazi_macro::emit; -use yazi_shared::event::Cmd; +use yazi_shared::{Id, event::Cmd}; pub struct CmpProxy; @@ -10,7 +10,7 @@ impl CmpProxy { } #[inline] - pub fn trigger(word: &str, ticket: usize) { + pub fn trigger(word: &str, ticket: Id) { emit!(Call(Cmd::args("cmp:trigger", &[word]).with("ticket", ticket))); } } diff --git a/yazi-shared/src/errors/input.rs b/yazi-shared/src/errors/input.rs index 9c0c1c6f..93b53fe0 100644 --- a/yazi-shared/src/errors/input.rs +++ b/yazi-shared/src/errors/input.rs @@ -1,9 +1,11 @@ use std::{error::Error, fmt::{self, Display}}; +use crate::Id; + #[derive(Debug)] pub enum InputError { Typed(String), - Completed(String, usize), + Completed(String, Id), Canceled(String), } diff --git a/yazi-widgets/Cargo.toml b/yazi-widgets/Cargo.toml new file mode 100644 index 00000000..a96cbef1 --- /dev/null +++ b/yazi-widgets/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "yazi-widgets" +version = "25.3.7" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi user interface widgets" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" +rust-version = "1.83.0" + +[dependencies] +yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" } +yazi-config = { path = "../yazi-config", version = "25.3.7" } +yazi-macro = { path = "../yazi-macro", version = "25.3.7" } +yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } +yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } +yazi-shared = { path = "../yazi-shared", version = "25.3.7" } + +# External dependencies +futures = { workspace = true } +tokio = { workspace = true } +unicode-width = { workspace = true } diff --git a/yazi-core/src/input/commands/backspace.rs b/yazi-widgets/src/input/commands/backspace.rs similarity index 100% rename from yazi-core/src/input/commands/backspace.rs rename to yazi-widgets/src/input/commands/backspace.rs diff --git a/yazi-core/src/input/commands/backward.rs b/yazi-widgets/src/input/commands/backward.rs similarity index 100% rename from yazi-core/src/input/commands/backward.rs rename to yazi-widgets/src/input/commands/backward.rs diff --git a/yazi-core/src/input/commands/complete.rs b/yazi-widgets/src/input/commands/complete.rs similarity index 78% rename from yazi-core/src/input/commands/complete.rs rename to yazi-widgets/src/input/commands/complete.rs index 065557ab..286de979 100644 --- a/yazi-core/src/input/commands/complete.rs +++ b/yazi-widgets/src/input/commands/complete.rs @@ -12,15 +12,15 @@ const SEPARATOR: [char; 2] = ['/', '\\']; const SEPARATOR: char = std::path::MAIN_SEPARATOR; struct Opt { - word: Cow<'static, str>, - ticket: usize, + word: Cow<'static, str>, + _ticket: usize, // FIXME } impl From for Opt { fn from(mut c: CmdCow) -> Self { Self { - word: c.take_first_str().unwrap_or_default(), - ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), + word: c.take_first_str().unwrap_or_default(), + _ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), } } } @@ -28,11 +28,7 @@ impl From for Opt { impl Input { #[yazi_codegen::command] pub fn complete(&mut self, opt: Opt) { - if self.ticket != opt.ticket { - return; - } - - let [before, after] = self.partition(); + let (before, after) = self.partition(); let new = if let Some((prefix, _)) = before.rsplit_once(SEPARATOR) { format!("{prefix}/{}{after}", opt.word).replace(SEPARATOR, MAIN_SEPARATOR_STR) } else { diff --git a/yazi-core/src/input/commands/delete.rs b/yazi-widgets/src/input/commands/delete.rs similarity index 100% rename from yazi-core/src/input/commands/delete.rs rename to yazi-widgets/src/input/commands/delete.rs diff --git a/yazi-widgets/src/input/commands/escape.rs b/yazi-widgets/src/input/commands/escape.rs new file mode 100644 index 00000000..9074b8b1 --- /dev/null +++ b/yazi-widgets/src/input/commands/escape.rs @@ -0,0 +1,27 @@ +use crate::input::{Input, InputMode, op::InputOp}; + +struct Opt; + +impl From<()> for Opt { + fn from(_: ()) -> Self { Self } +} + +impl Input { + #[yazi_codegen::command] + pub fn escape(&mut self, _: Opt) { + let snap = self.snap_mut(); + match snap.mode { + InputMode::Normal => { + snap.op = InputOp::None; + } + InputMode::Insert => { + snap.mode = InputMode::Normal; + self.move_(-1); + } + InputMode::Replace => { + snap.mode = InputMode::Normal; + } + } + self.snaps.tag(self.limit); + } +} diff --git a/yazi-core/src/input/commands/forward.rs b/yazi-widgets/src/input/commands/forward.rs similarity index 100% rename from yazi-core/src/input/commands/forward.rs rename to yazi-widgets/src/input/commands/forward.rs diff --git a/yazi-core/src/input/commands/insert.rs b/yazi-widgets/src/input/commands/insert.rs similarity index 100% rename from yazi-core/src/input/commands/insert.rs rename to yazi-widgets/src/input/commands/insert.rs diff --git a/yazi-core/src/input/commands/kill.rs b/yazi-widgets/src/input/commands/kill.rs similarity index 100% rename from yazi-core/src/input/commands/kill.rs rename to yazi-widgets/src/input/commands/kill.rs diff --git a/yazi-widgets/src/input/commands/mod.rs b/yazi-widgets/src/input/commands/mod.rs new file mode 100644 index 00000000..49bff105 --- /dev/null +++ b/yazi-widgets/src/input/commands/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(backspace backward complete delete escape forward insert kill move_ paste redo replace type_ undo visual yank); diff --git a/yazi-core/src/input/commands/move_.rs b/yazi-widgets/src/input/commands/move_.rs similarity index 97% rename from yazi-core/src/input/commands/move_.rs rename to yazi-widgets/src/input/commands/move_.rs index 9edc5384..64ff0db2 100644 --- a/yazi-core/src/input/commands/move_.rs +++ b/yazi-widgets/src/input/commands/move_.rs @@ -33,7 +33,7 @@ impl Input { render!(self.handle_op(opt.step.cursor(snap), false)); - let (limit, snap) = (self.limit(), self.snap_mut()); + let (limit, snap) = (self.limit, self.snap_mut()); if snap.offset > snap.cursor { snap.offset = snap.cursor; } else if snap.value.is_empty() { diff --git a/yazi-core/src/input/commands/paste.rs b/yazi-widgets/src/input/commands/paste.rs similarity index 100% rename from yazi-core/src/input/commands/paste.rs rename to yazi-widgets/src/input/commands/paste.rs diff --git a/yazi-core/src/input/commands/redo.rs b/yazi-widgets/src/input/commands/redo.rs similarity index 100% rename from yazi-core/src/input/commands/redo.rs rename to yazi-widgets/src/input/commands/redo.rs diff --git a/yazi-core/src/input/commands/replace.rs b/yazi-widgets/src/input/commands/replace.rs similarity index 92% rename from yazi-core/src/input/commands/replace.rs rename to yazi-widgets/src/input/commands/replace.rs index f028ebb2..ab7046f3 100644 --- a/yazi-core/src/input/commands/replace.rs +++ b/yazi-widgets/src/input/commands/replace.rs @@ -27,6 +27,6 @@ impl Input { } render!(); - self.snaps.tag(self.limit()).then(|| self.flush_value()); + self.snaps.tag(self.limit).then(|| self.flush_value()); } } diff --git a/yazi-core/src/input/commands/type_.rs b/yazi-widgets/src/input/commands/type_.rs similarity index 100% rename from yazi-core/src/input/commands/type_.rs rename to yazi-widgets/src/input/commands/type_.rs diff --git a/yazi-core/src/input/commands/undo.rs b/yazi-widgets/src/input/commands/undo.rs similarity index 100% rename from yazi-core/src/input/commands/undo.rs rename to yazi-widgets/src/input/commands/undo.rs diff --git a/yazi-core/src/input/commands/visual.rs b/yazi-widgets/src/input/commands/visual.rs similarity index 100% rename from yazi-core/src/input/commands/visual.rs rename to yazi-widgets/src/input/commands/visual.rs diff --git a/yazi-core/src/input/commands/yank.rs b/yazi-widgets/src/input/commands/yank.rs similarity index 100% rename from yazi-core/src/input/commands/yank.rs rename to yazi-widgets/src/input/commands/yank.rs diff --git a/yazi-widgets/src/input/input.rs b/yazi-widgets/src/input/input.rs new file mode 100644 index 00000000..63308f09 --- /dev/null +++ b/yazi-widgets/src/input/input.rs @@ -0,0 +1,108 @@ +use std::ops::Range; + +use unicode_width::UnicodeWidthStr; +use yazi_plugin::CLIPBOARD; + +use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp}; + +#[derive(Default)] +pub struct Input { + pub snaps: InputSnaps, + pub limit: usize, + pub callback: Option>, +} + +impl Input { + pub fn new(value: String, limit: usize, callback: Box) -> Self { + Self { snaps: InputSnaps::new(value, limit), limit, callback: Some(callback) } + } + + pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool { + let old = self.snap().clone(); + let snap = self.snap_mut(); + + match snap.op { + InputOp::None | InputOp::Select(_) => { + snap.cursor = cursor; + } + InputOp::Delete(cut, insert, _) => { + let range = snap.op.range(cursor, include).unwrap(); + let Range { start, end } = snap.idx(range.start)..snap.idx(range.end); + + let drain = snap.value.drain(start.unwrap()..end.unwrap()).collect::(); + if cut { + futures::executor::block_on(CLIPBOARD.set(&drain)); + } + + snap.op = InputOp::None; + snap.mode = if insert { InputMode::Insert } else { InputMode::Normal }; + snap.cursor = range.start; + } + InputOp::Yank(_) => { + let range = snap.op.range(cursor, include).unwrap(); + let Range { start, end } = snap.idx(range.start)..snap.idx(range.end); + let yanked = &snap.value[start.unwrap()..end.unwrap()]; + + snap.op = InputOp::None; + futures::executor::block_on(CLIPBOARD.set(yanked)); + } + }; + + snap.cursor = snap.count().saturating_sub(snap.mode.delta()).min(snap.cursor); + if snap == &old { + return false; + } + if !matches!(old.op, InputOp::None | InputOp::Select(_)) { + self.snaps.tag(self.limit).then(|| self.flush_value()); + } + true + } + + pub(super) fn flush_value(&mut self) { + if let Some(cb) = &self.callback { + let (before, after) = self.partition(); + cb(before, after); + } + } +} + +impl Input { + #[inline] + pub fn value(&self) -> &str { self.snap().slice(self.snap().window(self.limit)) } + + #[inline] + pub fn mode(&self) -> InputMode { self.snap().mode } + + #[inline] + pub fn cursor(&self) -> u16 { + let snap = self.snap(); + snap.slice(snap.offset..snap.cursor).width() as u16 + } + + pub fn selected(&self) -> Option> { + let snap = self.snap(); + let start = snap.op.start()?; + + let (start, end) = + if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) }; + + let win = snap.window(self.limit); + let Range { start, end } = start.max(win.start)..end.min(win.end); + + let s = snap.slice(snap.offset..start).width() as u16; + Some(s..s + snap.slice(start..end).width() as u16) + } + + #[inline] + pub fn partition(&self) -> (&str, &str) { + let snap = self.snap(); + let idx = snap.idx(snap.cursor).unwrap(); + (&snap.value[..idx], &snap.value[idx..]) + } + + #[inline] + pub fn snap(&self) -> &InputSnap { self.snaps.current() } + + #[inline] + pub fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() } +} diff --git a/yazi-widgets/src/input/mod.rs b/yazi-widgets/src/input/mod.rs new file mode 100644 index 00000000..bc6b58ab --- /dev/null +++ b/yazi-widgets/src/input/mod.rs @@ -0,0 +1,3 @@ +yazi_macro::mod_pub!(commands); + +yazi_macro::mod_flat!(input mode op snap snaps); diff --git a/yazi-core/src/input/mode.rs b/yazi-widgets/src/input/mode.rs similarity index 100% rename from yazi-core/src/input/mode.rs rename to yazi-widgets/src/input/mode.rs diff --git a/yazi-core/src/input/op.rs b/yazi-widgets/src/input/op.rs similarity index 100% rename from yazi-core/src/input/op.rs rename to yazi-widgets/src/input/op.rs diff --git a/yazi-core/src/input/snap.rs b/yazi-widgets/src/input/snap.rs similarity index 90% rename from yazi-core/src/input/snap.rs rename to yazi-widgets/src/input/snap.rs index e2931220..6b0d09e2 100644 --- a/yazi-core/src/input/snap.rs +++ b/yazi-widgets/src/input/snap.rs @@ -5,14 +5,14 @@ use unicode_width::UnicodeWidthChar; use super::{InputMode, InputOp}; #[derive(Clone, Debug, Default, PartialEq, Eq)] -pub(super) struct InputSnap { - pub(super) value: String, +pub struct InputSnap { + pub value: String, - pub(super) op: InputOp, + pub op: InputOp, - pub(super) mode: InputMode, - pub(super) offset: usize, - pub(super) cursor: usize, + pub mode: InputMode, + pub offset: usize, + pub cursor: usize, } impl InputSnap { @@ -22,7 +22,7 @@ impl InputSnap { op: Default::default(), - mode: Default::default(), + mode: Default::default(), offset: usize::MAX, cursor: usize::MAX, }; diff --git a/yazi-core/src/input/snaps.rs b/yazi-widgets/src/input/snaps.rs similarity index 81% rename from yazi-core/src/input/snaps.rs rename to yazi-widgets/src/input/snaps.rs index b1960bcf..bc3f9e7a 100644 --- a/yazi-core/src/input/snaps.rs +++ b/yazi-widgets/src/input/snaps.rs @@ -3,19 +3,16 @@ use std::mem; use super::InputSnap; #[derive(Default, PartialEq, Eq)] -pub(super) struct InputSnaps { +pub struct InputSnaps { idx: usize, versions: Vec, current: InputSnap, } impl InputSnaps { - #[inline] - pub(super) fn reset(&mut self, value: String, limit: usize) { - self.idx = 0; - self.versions.clear(); - self.versions.push(InputSnap::new(value, limit)); - self.current = self.versions[0].clone(); + pub fn new(value: String, limit: usize) -> Self { + let current = InputSnap::new(value, limit); + Self { idx: 0, versions: vec![current.clone()], current } } pub(super) fn tag(&mut self, limit: usize) -> bool { @@ -65,7 +62,7 @@ impl InputSnaps { impl InputSnaps { #[inline] - pub(super) fn current(&self) -> &InputSnap { &self.current } + pub fn current(&self) -> &InputSnap { &self.current } #[inline] pub(super) fn current_mut(&mut self) -> &mut InputSnap { &mut self.current } diff --git a/yazi-widgets/src/lib.rs b/yazi-widgets/src/lib.rs new file mode 100644 index 00000000..6ce2b0ad --- /dev/null +++ b/yazi-widgets/src/lib.rs @@ -0,0 +1 @@ +yazi_macro::mod_pub!(input);