From 8e80798984864799cd72b3625b21edc33b1ba1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 1 Jun 2026 13:16:01 +0800 Subject: [PATCH] feat: new gait for input `backward` and `forward` actions (#4012) --- CHANGELOG.md | 5 +++ yazi-binding/src/tty.rs | 22 +++++------ yazi-config/preset/keymap-default.toml | 20 +++++----- yazi-plugin/preset/components/current.lua | 2 +- yazi-plugin/preset/plugins/dnd.lua | 2 +- yazi-plugin/src/fs/fs.rs | 13 +----- yazi-plugin/src/utils/call.rs | 15 +------ yazi-plugin/src/utils/utils.rs | 1 - yazi-shared/src/chars.rs | 23 ----------- yazi-term/src/sequence/dnd.rs | 12 +++--- yazi-vfs/src/fns.rs | 48 ----------------------- yazi-widgets/src/input/actor/backward.rs | 13 +++--- yazi-widgets/src/input/actor/forward.rs | 8 ++-- yazi-widgets/src/input/actor/kill.rs | 4 +- yazi-widgets/src/input/chars.rs | 37 +++++++++++++++++ yazi-widgets/src/input/gait.rs | 10 +++++ yazi-widgets/src/input/mod.rs | 2 +- yazi-widgets/src/input/parser/backward.rs | 28 +++++++++++-- yazi-widgets/src/input/parser/forward.rs | 30 ++++++++++++-- 19 files changed, 145 insertions(+), 150 deletions(-) create mode 100644 yazi-widgets/src/input/chars.rs create mode 100644 yazi-widgets/src/input/gait.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba1e102..af2c8f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Rename `` to `` ([#3989]) - Remove Legacy Console Mode on Windows ([#3989]) +### Deprecated + +- Deprecate `backward --far` and `forward --far` in favor of `backward wide` and `forward wide`, respectively ([#4012]) + ## [v26.5.6] ### Added @@ -1731,3 +1735,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3989]: https://github.com/sxyazi/yazi/pull/3989 [#3990]: https://github.com/sxyazi/yazi/pull/3990 [#4005]: https://github.com/sxyazi/yazi/pull/4005 +[#4012]: https://github.com/sxyazi/yazi/pull/4012 diff --git a/yazi-binding/src/tty.rs b/yazi-binding/src/tty.rs index 272133f6..2713716c 100644 --- a/yazi-binding/src/tty.rs +++ b/yazi-binding/src/tty.rs @@ -2,7 +2,7 @@ use std::io::Write; use mlua::{BorrowedBytes, ExternalError, IntoLuaMulti, Lua, MultiValue, Table, UserData, UserDataMethods}; use yazi_shim::mlua::{ByteString, LuaTableExt}; -use yazi_term::sequence::{ConfirmDrag, ConfirmDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}; +use yazi_term::sequence::{AgreeDrag, AgreeDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}; use yazi_tty::TTY; use crate::Error; @@ -14,22 +14,22 @@ impl Tty { let mut w = TTY.writer(); let result = match kind { - b"ConfirmDrag" => { + b"AgreeDrag" => { let it = t.raw_get::("mimes")?.sequence_iter::(lua).flatten(); write!(w, "{}", match &*t.raw_get::("type")? { - b"copy" => ConfirmDrag::Copy(it), - b"move" => ConfirmDrag::Move(it), - b"either" => ConfirmDrag::Either(it), - _ => return Err("invalid ConfirmDrag type".into_lua_err()), + b"copy" => AgreeDrag::Copy(it), + b"move" => AgreeDrag::Move(it), + b"either" => AgreeDrag::Either(it), + _ => return Err("invalid AgreeDrag type".into_lua_err()), }) } - b"ConfirmDrop" => { + b"AgreeDrop" => { let it = t.raw_get::
("mimes")?.sequence_iter::(lua).flatten(); write!(w, "{}", match &*t.raw_get::("type")? { - b"reject" => ConfirmDrop::Reject, - b"copy" => ConfirmDrop::Copy(it), - b"move" => ConfirmDrop::Move(it), - _ => return Err("invalid ConfirmDrop type".into_lua_err()), + b"reject" => AgreeDrop::Reject, + b"copy" => AgreeDrop::Copy(it), + b"move" => AgreeDrop::Move(it), + _ => return Err("invalid AgreeDrop type".into_lua_err()), }) } b"StartDrag" => write!(w, "{StartDrag}"), diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index 7d621ec0..900cf806 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -257,16 +257,16 @@ keymap = [ { on = "", run = "move 1", desc = "Move forward a character" }, # Word-wise movement - { on = "b", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "B", run = "backward --far", desc = "Move back to the start of the current or previous WORD" }, - { on = "w", run = "forward", desc = "Move forward to the start of the next word" }, - { on = "W", run = "forward --far", desc = "Move forward to the start of the next WORD" }, - { on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, - { on = "E", run = "forward --far --end-of-word", desc = "Move forward to the end of the current or next WORD" }, - { on = "", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, - { on = "", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "b", run = "backward", desc = "Move back to the start of the current or previous word" }, + { on = "B", run = "backward wide", desc = "Move back to the start of the current or previous WORD" }, + { on = "w", run = "forward", desc = "Move forward to the start of the next word" }, + { on = "W", run = "forward wide", desc = "Move forward to the start of the next WORD" }, + { on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "E", run = "forward wide --end-of-word", desc = "Move forward to the end of the current or next WORD" }, + { on = "", run = "backward lean", desc = "Move back to the start of the current or previous word" }, + { on = "", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "", run = "backward lean", desc = "Move back to the start of the current or previous word" }, + { on = "", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" }, # Line-wise movement { on = "0", run = "move bol", desc = "Move to the BOL" }, diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 01ea28c6..0c15db76 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -86,7 +86,7 @@ function Current:drop(event) if Current._dragging then return elseif event.type == "enter" then - rt.tty:queue("ConfirmDrop", { type = "move", mimes = { "text/uri-list" } }) + rt.tty:queue("AgreeDrop", { type = "move", mimes = { "text/uri-list" } }) elseif event.type == "ready" then rt.tty:queue("StartDrop", { idx = 1 }) elseif event.type == "arrive" then diff --git a/yazi-plugin/preset/plugins/dnd.lua b/yazi-plugin/preset/plugins/dnd.lua index fae4c6ea..6640e42f 100644 --- a/yazi-plugin/preset/plugins/dnd.lua +++ b/yazi-plugin/preset/plugins/dnd.lua @@ -35,7 +35,7 @@ function M.offer_uri_list() end local icon = string.format("%d selected file(s)", #list) - rt.tty:queue("ConfirmDrag", { type = "either", mimes = { "text/uri-list" } }) + rt.tty:queue("AgreeDrag", { type = "either", mimes = { "text/uri-list" } }) rt.tty:queue("PresentDrag", { idx = 0, data = table.concat(list, "\r\n") }) rt.tty:queue("PresentDragIcon", { format = 0, opacity = 0, width = 6, height = 4, data = icon }) rt.tty:queue("StartDrag", {}) diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index b0d4c6f1..cc9b0905 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, SizeCalculator, Url, UrlRef, deprecate}; +use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, SizeCalculator, Url, UrlRef}; use yazi_config::Pattern; use yazi_fs::{mounts::PARTITIONS, provider::{Attrs, DirReader, FileHolder}}; use yazi_shared::url::{UrlCow, UrlLike}; @@ -23,7 +23,6 @@ pub fn compose() -> Composer { b"remove" => remove(lua)?, b"rename" => rename(lua)?, b"unique" => unique(lua)?, - b"unique_name" => unique_name(lua)?, b"write" => write(lua)?, _ => return Ok(Value::Nil), } @@ -231,16 +230,6 @@ fn unique(lua: &Lua) -> mlua::Result { }) } -fn unique_name(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, url: UrlRef| async move { - deprecate!(lua, "`fs.unique_name()` is deprecated, use `fs.unique()` instead, in your {}\nSee #3677 for more details: https://github.com/sxyazi/yazi/pull/3677"); - match yazi_vfs::unique_name(url.clone(), async { false }).await { - Ok(u) => Url::new(u).into_lua_multi(&lua), - Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), - } - }) -} - fn write(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { match provider::write(&*url, data.as_bytes()).await { diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index 343ad17e..b816e685 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -1,9 +1,8 @@ use mlua::{ExternalError, Function, Lua, Table}; use tokio::sync::mpsc; -use yazi_binding::deprecate; use yazi_dds::Sendable; use yazi_macro::emit; -use yazi_shared::{Layer, Source, event::{Action, Cmd}}; +use yazi_shared::{Layer, Source, event::Action}; use super::Utils; @@ -16,18 +15,6 @@ impl Utils { }) } - pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (name, args): (String, Table)| { - deprecate!(lua, "ya.mgr_emit() has been deprecated since v25.5.28 and will soon-to-be removed in a future release. \n\nUse ya.emit() in your {} instead, see #2653 for details: https://github.com/sxyazi/yazi/pull/2653"); - emit!(Call(Action { - cmd: Cmd { name: name.into(), args: Sendable::table_to_args(lua, args)? }, - layer: Layer::Mgr, - source: Source::Emit, - })); - Ok(()) - }) - } - pub(super) fn exec(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (name, args): (String, Table)| async move { let mut action = Action::new(name, Source::Emit, Layer::Mgr)?; diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 3bcbb23a..c128e66e 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -17,7 +17,6 @@ pub fn compose( // Call b"emit" => Utils::emit(lua)?, - b"mgr_emit" => Utils::mgr_emit(lua)?, b"exec" => Utils::exec(lua)?, // Image diff --git a/yazi-shared/src/chars.rs b/yazi-shared/src/chars.rs index 40bfe8f9..3b72ce8c 100644 --- a/yazi-shared/src/chars.rs +++ b/yazi-shared/src/chars.rs @@ -1,29 +1,6 @@ use core::str; use std::borrow::Cow; -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum CharKind { - Space, - Punct, - Other, -} - -impl CharKind { - pub fn new(c: char) -> Self { - if c.is_whitespace() { - Self::Space - } else if c.is_ascii_punctuation() { - Self::Punct - } else { - Self::Other - } - } - - pub fn vary(self, other: Self, far: bool) -> bool { - if far { (self == Self::Space) != (other == Self::Space) } else { self != other } - } -} - pub fn strip_trailing_newline(mut s: String) -> String { while s.ends_with('\n') || s.ends_with('\r') { s.pop(); diff --git a/yazi-term/src/sequence/dnd.rs b/yazi-term/src/sequence/dnd.rs index 681cf612..9591ff17 100644 --- a/yazi-term/src/sequence/dnd.rs +++ b/yazi-term/src/sequence/dnd.rs @@ -37,14 +37,14 @@ impl Display for DisableDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b]72;t=A\x1b\\") } } -/// Confirm drag: `OSC 72 ; t=o:o=operation ST` -pub enum ConfirmDrag { +/// Agree drag: `OSC 72 ; t=o:o=operation ST` +pub enum AgreeDrag { Copy(M), Move(M), Either(M), } -impl Display for ConfirmDrag { +impl Display for AgreeDrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes.clone())), @@ -54,14 +54,14 @@ impl Display for ConfirmDrag { } } -/// Confirm dropped data: `OSC 72 ; t=m:o=O ; MIME list ST` -pub enum ConfirmDrop { +/// Agree drop: `OSC 72 ; t=m:o=O ; MIME list ST` +pub enum AgreeDrop { Reject, Copy(M), Move(M), } -impl Display for ConfirmDrop { +impl Display for AgreeDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"), diff --git a/yazi-vfs/src/fns.rs b/yazi-vfs/src/fns.rs index c2848c74..445b635f 100644 --- a/yazi-vfs/src/fns.rs +++ b/yazi-vfs/src/fns.rs @@ -1,6 +1,5 @@ use std::io::{self}; -use yazi_macro::ok_or_not_found; use yazi_shared::{strand::{StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}}; use crate::provider; @@ -12,53 +11,6 @@ pub async fn maybe_exists(url: impl AsUrl) -> bool { } } -// TODO: deprecate -pub async fn unique_name(u: UrlBuf, append: F) -> io::Result -where - F: Future, -{ - match provider::symlink_metadata(&u).await { - Ok(_) => _unique_name(u, append.await).await, - Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(u), - Err(e) => Err(e), - } -} - -async fn _unique_name(mut url: UrlBuf, append: bool) -> io::Result { - let Some(stem) = url.stem().map(|s| s.to_owned()) else { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); - }; - - let dot_ext = match url.ext() { - Some(e) => { - let mut s = StrandBuf::with_capacity(url.kind(), e.len() + 1); - s.push_str("."); - s.try_push(e)?; - s - } - None => StrandBuf::default(), - }; - - let mut name = StrandBuf::with_capacity(url.kind(), stem.len() + dot_ext.len() + 5); - for i in 1u64.. { - name.clear(); - name.try_push(&stem)?; - - if append { - name.try_push(&dot_ext)?; - name.push_str(format!("_{i}")); - } else { - name.push_str(format!("_{i}")); - name.try_push(&dot_ext)?; - } - - url.try_set_name(&name)?; - ok_or_not_found!(provider::symlink_metadata(&url).await, break); - } - - Ok(url) -} - pub async fn unique_file(u: UrlBuf, is_dir: bool) -> io::Result { let result = if is_dir { provider::create_dir(&u).await diff --git a/yazi-widgets/src/input/actor/backward.rs b/yazi-widgets/src/input/actor/backward.rs index b3b0ea09..de43d488 100644 --- a/yazi-widgets/src/input/actor/backward.rs +++ b/yazi-widgets/src/input/actor/backward.rs @@ -1,8 +1,8 @@ use anyhow::Result; -use yazi_macro::{act, succ}; -use yazi_shared::{CharKind, data::Data}; +use yazi_macro::act; +use yazi_shared::data::Data; -use crate::input::{Input, parser::BackwardOpt}; +use crate::input::{CharKind, Input, parser::BackwardOpt}; impl Input { pub fn backward(&mut self, opt: BackwardOpt) -> Result { @@ -16,15 +16,12 @@ impl Input { let mut prev = CharKind::new(it.next().unwrap().1); for (i, c) in it { let k = CharKind::new(c); - if prev != CharKind::Space && prev.vary(k, opt.far) { + if prev != CharKind::Space && prev.vary(k, opt.gait) { return act!(r#move, self, -(i as isize)); } prev = k; } - if prev != CharKind::Space { - act!(r#move, self, -(snap.len() as isize))?; - } - succ!(); + act!(r#move, self, -(snap.len() as isize)) } } diff --git a/yazi-widgets/src/input/actor/forward.rs b/yazi-widgets/src/input/actor/forward.rs index 846dfc60..d34d8d2e 100644 --- a/yazi-widgets/src/input/actor/forward.rs +++ b/yazi-widgets/src/input/actor/forward.rs @@ -1,8 +1,8 @@ use anyhow::Result; use yazi_macro::act; -use yazi_shared::{CharKind, data::Data}; +use yazi_shared::data::Data; -use crate::input::{Input, op::InputOp, parser::ForwardOpt}; +use crate::input::{CharKind, Input, op::InputOp, parser::ForwardOpt}; impl Input { pub fn forward(&mut self, opt: ForwardOpt) -> Result { @@ -16,9 +16,9 @@ impl Input { for (i, c) in it { let k = CharKind::new(c); let b = if opt.end_of_word { - prev != CharKind::Space && prev.vary(k, opt.far) && i != 1 + prev != CharKind::Space && prev.vary(k, opt.gait) && i != 1 } else { - k != CharKind::Space && k.vary(prev, opt.far) + k != CharKind::Space && k.vary(prev, opt.gait) }; if b && !matches!(snap.op, InputOp::None | InputOp::Select(_)) { return act!(r#move, self, i as isize); diff --git a/yazi-widgets/src/input/actor/kill.rs b/yazi-widgets/src/input/actor/kill.rs index 36bd1b2d..c2f44931 100644 --- a/yazi-widgets/src/input/actor/kill.rs +++ b/yazi-widgets/src/input/actor/kill.rs @@ -2,9 +2,9 @@ use std::ops::RangeBounds; use anyhow::Result; use yazi_macro::{act, render, succ}; -use yazi_shared::{CharKind, data::Data}; +use yazi_shared::data::Data; -use crate::input::{Input, parser::KillOpt}; +use crate::input::{CharKind, Input, parser::KillOpt}; impl Input { pub fn kill(&mut self, opt: KillOpt) -> Result { diff --git a/yazi-widgets/src/input/chars.rs b/yazi-widgets/src/input/chars.rs new file mode 100644 index 00000000..88dab060 --- /dev/null +++ b/yazi-widgets/src/input/chars.rs @@ -0,0 +1,37 @@ +use crate::input::Gait; + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) enum CharKind { + Space, + Punct, + Other, +} + +impl CharKind { + pub(crate) fn new(c: char) -> Self { + if c.is_whitespace() { + Self::Space + } else if Self::is_punct(c) { + Self::Punct + } else { + Self::Other + } + } + + pub(crate) fn vary(self, other: Self, gait: Gait) -> bool { + match gait { + Gait::Fine => self != other, + Gait::Lean => self == Self::Other && other != Self::Other, + Gait::Wide => (self == Self::Space) != (other == Self::Space), + } + } + + const fn is_punct(c: char) -> bool { + match c { + '_' => false, + c if c.is_ascii_punctuation() => true, + '!' | ',' | ';' | '?' => true, + _ => false, + } + } +} diff --git a/yazi-widgets/src/input/gait.rs b/yazi-widgets/src/input/gait.rs new file mode 100644 index 00000000..0cff9263 --- /dev/null +++ b/yazi-widgets/src/input/gait.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum Gait { + #[default] + Fine, + Lean, + Wide, +} diff --git a/yazi-widgets/src/input/mod.rs b/yazi-widgets/src/input/mod.rs index dde2b4e8..90766a7a 100644 --- a/yazi-widgets/src/input/mod.rs +++ b/yazi-widgets/src/input/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(actor parser); -yazi_macro::mod_flat!(event input mode op option snap snaps widget); +yazi_macro::mod_flat!(chars event gait input mode op option snap snaps widget); diff --git a/yazi-widgets/src/input/parser/backward.rs b/yazi-widgets/src/input/parser/backward.rs index b26c8fca..abba7aac 100644 --- a/yazi-widgets/src/input/parser/backward.rs +++ b/yazi-widgets/src/input/parser/backward.rs @@ -1,13 +1,33 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +use crate::input::Gait; + +#[derive(Debug, Deserialize)] pub struct BackwardOpt { - pub far: bool, + #[serde(alias = "0", default)] + pub gait: Gait, } -impl From for BackwardOpt { - fn from(a: ActionCow) -> Self { Self { far: a.bool("far") } } +impl TryFrom for BackwardOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + // TODO: remove + if a.bool("far") { + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + yazi_macro::emit!(Call(yazi_shared::event::Action::new_relay("app:deprecate").with( + "content", + "`backward --far` is deprecated, use `backward wide` under `[input]` instead".to_string() + ))); + } + + return Ok(Self { gait: Gait::Wide }); + } + Ok(a.deserialize()?) + } } impl FromLua for BackwardOpt { diff --git a/yazi-widgets/src/input/parser/forward.rs b/yazi-widgets/src/input/parser/forward.rs index 13415988..be01b94c 100644 --- a/yazi-widgets/src/input/parser/forward.rs +++ b/yazi-widgets/src/input/parser/forward.rs @@ -1,14 +1,36 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +use crate::input::Gait; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] pub struct ForwardOpt { - pub far: bool, + #[serde(alias = "0", default)] + pub gait: Gait, + #[serde(default)] pub end_of_word: bool, } -impl From for ForwardOpt { - fn from(a: ActionCow) -> Self { Self { far: a.bool("far"), end_of_word: a.bool("end-of-word") } } +impl TryFrom for ForwardOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + // TODO: remove + if a.bool("far") { + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + yazi_macro::emit!(Call(yazi_shared::event::Action::new_relay("app:deprecate").with( + "content", + "`forward --far` is deprecated, use `forward wide` under `[input]` instead".to_string() + ))); + } + + return Ok(Self { gait: Gait::Wide, end_of_word: a.bool("end-of-word") }); + } + Ok(a.deserialize()?) + } } impl FromLua for ForwardOpt {