feat: new gait for input backward and forward actions (#4012)

This commit is contained in:
三咲雅 misaki masa 2026-06-01 13:16:01 +08:00 committed by GitHub
parent e892bf7d90
commit 8e80798984
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 145 additions and 150 deletions

View file

@ -23,6 +23,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Rename `<BackTab>` to `<S-Tab>` ([#3989]) - Rename `<BackTab>` to `<S-Tab>` ([#3989])
- Remove Legacy Console Mode on Windows ([#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] ## [v26.5.6]
### Added ### 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 [#3989]: https://github.com/sxyazi/yazi/pull/3989
[#3990]: https://github.com/sxyazi/yazi/pull/3990 [#3990]: https://github.com/sxyazi/yazi/pull/3990
[#4005]: https://github.com/sxyazi/yazi/pull/4005 [#4005]: https://github.com/sxyazi/yazi/pull/4005
[#4012]: https://github.com/sxyazi/yazi/pull/4012

View file

@ -2,7 +2,7 @@ use std::io::Write;
use mlua::{BorrowedBytes, ExternalError, IntoLuaMulti, Lua, MultiValue, Table, UserData, UserDataMethods}; use mlua::{BorrowedBytes, ExternalError, IntoLuaMulti, Lua, MultiValue, Table, UserData, UserDataMethods};
use yazi_shim::mlua::{ByteString, LuaTableExt}; 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 yazi_tty::TTY;
use crate::Error; use crate::Error;
@ -14,22 +14,22 @@ impl Tty {
let mut w = TTY.writer(); let mut w = TTY.writer();
let result = match kind { let result = match kind {
b"ConfirmDrag" => { b"AgreeDrag" => {
let it = t.raw_get::<Table>("mimes")?.sequence_iter::<ByteString>(lua).flatten(); let it = t.raw_get::<Table>("mimes")?.sequence_iter::<ByteString>(lua).flatten();
write!(w, "{}", match &*t.raw_get::<BorrowedBytes>("type")? { write!(w, "{}", match &*t.raw_get::<BorrowedBytes>("type")? {
b"copy" => ConfirmDrag::Copy(it), b"copy" => AgreeDrag::Copy(it),
b"move" => ConfirmDrag::Move(it), b"move" => AgreeDrag::Move(it),
b"either" => ConfirmDrag::Either(it), b"either" => AgreeDrag::Either(it),
_ => return Err("invalid ConfirmDrag type".into_lua_err()), _ => return Err("invalid AgreeDrag type".into_lua_err()),
}) })
} }
b"ConfirmDrop" => { b"AgreeDrop" => {
let it = t.raw_get::<Table>("mimes")?.sequence_iter::<ByteString>(lua).flatten(); let it = t.raw_get::<Table>("mimes")?.sequence_iter::<ByteString>(lua).flatten();
write!(w, "{}", match &*t.raw_get::<BorrowedBytes>("type")? { write!(w, "{}", match &*t.raw_get::<BorrowedBytes>("type")? {
b"reject" => ConfirmDrop::Reject, b"reject" => AgreeDrop::Reject,
b"copy" => ConfirmDrop::Copy(it), b"copy" => AgreeDrop::Copy(it),
b"move" => ConfirmDrop::Move(it), b"move" => AgreeDrop::Move(it),
_ => return Err("invalid ConfirmDrop type".into_lua_err()), _ => return Err("invalid AgreeDrop type".into_lua_err()),
}) })
} }
b"StartDrag" => write!(w, "{StartDrag}"), b"StartDrag" => write!(w, "{StartDrag}"),

View file

@ -257,16 +257,16 @@ keymap = [
{ on = "<C-f>", run = "move 1", desc = "Move forward a character" }, { on = "<C-f>", run = "move 1", desc = "Move forward a character" },
# Word-wise movement # Word-wise movement
{ on = "b", run = "backward", desc = "Move back to the start of the current or previous word" }, { 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 = "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", 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 = "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 --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 = "E", run = "forward wide --end-of-word", desc = "Move forward to the end of the current or next WORD" },
{ on = "<A-b>", run = "backward", desc = "Move back to the start of the current or previous word" }, { on = "<A-b>", run = "backward lean", desc = "Move back to the start of the current or previous word" },
{ on = "<A-f>", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, { on = "<A-f>", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = "<C-Left>", run = "backward", desc = "Move back to the start of the current or previous word" }, { on = "<C-Left>", run = "backward lean", desc = "Move back to the start of the current or previous word" },
{ on = "<C-Right>", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, { on = "<C-Right>", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" },
# Line-wise movement # Line-wise movement
{ on = "0", run = "move bol", desc = "Move to the BOL" }, { on = "0", run = "move bol", desc = "Move to the BOL" },

View file

@ -86,7 +86,7 @@ function Current:drop(event)
if Current._dragging then if Current._dragging then
return return
elseif event.type == "enter" then 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 elseif event.type == "ready" then
rt.tty:queue("StartDrop", { idx = 1 }) rt.tty:queue("StartDrop", { idx = 1 })
elseif event.type == "arrive" then elseif event.type == "arrive" then

View file

@ -35,7 +35,7 @@ function M.offer_uri_list()
end end
local icon = string.format("%d selected file(s)", #list) 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("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("PresentDragIcon", { format = 0, opacity = 0, width = 6, height = 4, data = icon })
rt.tty:queue("StartDrag", {}) rt.tty:queue("StartDrag", {})

View file

@ -1,7 +1,7 @@
use std::str::FromStr; use std::str::FromStr;
use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; 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_config::Pattern;
use yazi_fs::{mounts::PARTITIONS, provider::{Attrs, DirReader, FileHolder}}; use yazi_fs::{mounts::PARTITIONS, provider::{Attrs, DirReader, FileHolder}};
use yazi_shared::url::{UrlCow, UrlLike}; use yazi_shared::url::{UrlCow, UrlLike};
@ -23,7 +23,6 @@ pub fn compose() -> Composer<ComposerGet, ComposerSet> {
b"remove" => remove(lua)?, b"remove" => remove(lua)?,
b"rename" => rename(lua)?, b"rename" => rename(lua)?,
b"unique" => unique(lua)?, b"unique" => unique(lua)?,
b"unique_name" => unique_name(lua)?,
b"write" => write(lua)?, b"write" => write(lua)?,
_ => return Ok(Value::Nil), _ => return Ok(Value::Nil),
} }
@ -231,16 +230,6 @@ fn unique(lua: &Lua) -> mlua::Result<Function> {
}) })
} }
fn unique_name(lua: &Lua) -> mlua::Result<Function> {
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<Function> { fn write(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move {
match provider::write(&*url, data.as_bytes()).await { match provider::write(&*url, data.as_bytes()).await {

View file

@ -1,9 +1,8 @@
use mlua::{ExternalError, Function, Lua, Table}; use mlua::{ExternalError, Function, Lua, Table};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use yazi_binding::deprecate;
use yazi_dds::Sendable; use yazi_dds::Sendable;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, Source, event::{Action, Cmd}}; use yazi_shared::{Layer, Source, event::Action};
use super::Utils; use super::Utils;
@ -16,18 +15,6 @@ impl Utils {
}) })
} }
pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result<Function> {
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<Function> { pub(super) fn exec(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (name, args): (String, Table)| async move { lua.create_async_function(|lua, (name, args): (String, Table)| async move {
let mut action = Action::new(name, Source::Emit, Layer::Mgr)?; let mut action = Action::new(name, Source::Emit, Layer::Mgr)?;

View file

@ -17,7 +17,6 @@ pub fn compose(
// Call // Call
b"emit" => Utils::emit(lua)?, b"emit" => Utils::emit(lua)?,
b"mgr_emit" => Utils::mgr_emit(lua)?,
b"exec" => Utils::exec(lua)?, b"exec" => Utils::exec(lua)?,
// Image // Image

View file

@ -1,29 +1,6 @@
use core::str; use core::str;
use std::borrow::Cow; 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 { pub fn strip_trailing_newline(mut s: String) -> String {
while s.ends_with('\n') || s.ends_with('\r') { while s.ends_with('\n') || s.ends_with('\r') {
s.pop(); s.pop();

View file

@ -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\\") } 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` /// Agree drag: `OSC 72 ; t=o:o=operation ST`
pub enum ConfirmDrag<M> { pub enum AgreeDrag<M> {
Copy(M), Copy(M),
Move(M), Move(M),
Either(M), Either(M),
} }
impl<M: Mimelist> Display for ConfirmDrag<M> { impl<M: Mimelist> Display for AgreeDrag<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes.clone())), Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes.clone())),
@ -54,14 +54,14 @@ impl<M: Mimelist> Display for ConfirmDrag<M> {
} }
} }
/// Confirm dropped data: `OSC 72 ; t=m:o=O ; MIME list ST` /// Agree drop: `OSC 72 ; t=m:o=O ; MIME list ST`
pub enum ConfirmDrop<M> { pub enum AgreeDrop<M> {
Reject, Reject,
Copy(M), Copy(M),
Move(M), Move(M),
} }
impl<M: Mimelist> Display for ConfirmDrop<M> { impl<M: Mimelist> Display for AgreeDrop<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"), Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"),

View file

@ -1,6 +1,5 @@
use std::io::{self}; use std::io::{self};
use yazi_macro::ok_or_not_found;
use yazi_shared::{strand::{StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}}; use yazi_shared::{strand::{StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}};
use crate::provider; use crate::provider;
@ -12,53 +11,6 @@ pub async fn maybe_exists(url: impl AsUrl) -> bool {
} }
} }
// TODO: deprecate
pub async fn unique_name<F>(u: UrlBuf, append: F) -> io::Result<UrlBuf>
where
F: Future<Output = bool>,
{
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<UrlBuf> {
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<UrlBuf> { pub async fn unique_file(u: UrlBuf, is_dir: bool) -> io::Result<UrlBuf> {
let result = if is_dir { let result = if is_dir {
provider::create_dir(&u).await provider::create_dir(&u).await

View file

@ -1,8 +1,8 @@
use anyhow::Result; use anyhow::Result;
use yazi_macro::{act, succ}; use yazi_macro::act;
use yazi_shared::{CharKind, data::Data}; use yazi_shared::data::Data;
use crate::input::{Input, parser::BackwardOpt}; use crate::input::{CharKind, Input, parser::BackwardOpt};
impl Input { impl Input {
pub fn backward(&mut self, opt: BackwardOpt) -> Result<Data> { pub fn backward(&mut self, opt: BackwardOpt) -> Result<Data> {
@ -16,15 +16,12 @@ impl Input {
let mut prev = CharKind::new(it.next().unwrap().1); let mut prev = CharKind::new(it.next().unwrap().1);
for (i, c) in it { for (i, c) in it {
let k = CharKind::new(c); 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)); return act!(r#move, self, -(i as isize));
} }
prev = k; prev = k;
} }
if prev != CharKind::Space { act!(r#move, self, -(snap.len() as isize))
act!(r#move, self, -(snap.len() as isize))?;
}
succ!();
} }
} }

View file

@ -1,8 +1,8 @@
use anyhow::Result; use anyhow::Result;
use yazi_macro::act; 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 { impl Input {
pub fn forward(&mut self, opt: ForwardOpt) -> Result<Data> { pub fn forward(&mut self, opt: ForwardOpt) -> Result<Data> {
@ -16,9 +16,9 @@ impl Input {
for (i, c) in it { for (i, c) in it {
let k = CharKind::new(c); let k = CharKind::new(c);
let b = if opt.end_of_word { 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 { } 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(_)) { if b && !matches!(snap.op, InputOp::None | InputOp::Select(_)) {
return act!(r#move, self, i as isize); return act!(r#move, self, i as isize);

View file

@ -2,9 +2,9 @@ use std::ops::RangeBounds;
use anyhow::Result; use anyhow::Result;
use yazi_macro::{act, render, succ}; 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 { impl Input {
pub fn kill(&mut self, opt: KillOpt) -> Result<Data> { pub fn kill(&mut self, opt: KillOpt) -> Result<Data> {

View file

@ -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,
}
}
}

View file

@ -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,
}

View file

@ -1,3 +1,3 @@
yazi_macro::mod_pub!(actor parser); 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);

View file

@ -1,13 +1,33 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use serde::Deserialize;
use yazi_shared::event::ActionCow; use yazi_shared::event::ActionCow;
#[derive(Debug)] use crate::input::Gait;
#[derive(Debug, Deserialize)]
pub struct BackwardOpt { pub struct BackwardOpt {
pub far: bool, #[serde(alias = "0", default)]
pub gait: Gait,
} }
impl From<ActionCow> for BackwardOpt { impl TryFrom<ActionCow> for BackwardOpt {
fn from(a: ActionCow) -> Self { Self { far: a.bool("far") } } type Error = anyhow::Error;
fn try_from(a: ActionCow) -> Result<Self, Self::Error> {
// 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 { impl FromLua for BackwardOpt {

View file

@ -1,14 +1,36 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use serde::Deserialize;
use yazi_shared::event::ActionCow; use yazi_shared::event::ActionCow;
#[derive(Debug)] use crate::input::Gait;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct ForwardOpt { pub struct ForwardOpt {
pub far: bool, #[serde(alias = "0", default)]
pub gait: Gait,
#[serde(default)]
pub end_of_word: bool, pub end_of_word: bool,
} }
impl From<ActionCow> for ForwardOpt { impl TryFrom<ActionCow> for ForwardOpt {
fn from(a: ActionCow) -> Self { Self { far: a.bool("far"), end_of_word: a.bool("end-of-word") } } type Error = anyhow::Error;
fn try_from(a: ActionCow) -> Result<Self, Self::Error> {
// 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 { impl FromLua for ForwardOpt {