diff --git a/Cargo.lock b/Cargo.lock index b32c7b19b..381287958 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2751,9 +2751,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uzers" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d85875e16d59b3b1549efce83ff8251a64923b03bef94add0a1862847448de4" +checksum = "4df81ff504e7d82ad53e95ed1ad5b72103c11253f39238bcc0235b90768a97dd" dependencies = [ "libc", "log", @@ -3418,6 +3418,7 @@ version = "0.3.0" dependencies = [ "anyhow", "mlua", + "shell-words", "tokio", "yazi-config", "yazi-shared", diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 6343c8aef..d2350efe7 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -75,7 +75,7 @@ keymap = [ { on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" }, { on = "s", run = "search fd", desc = "Search files by name using fd" }, { on = "S", run = "search rg", desc = "Search files by content using ripgrep" }, - { on = "", run = "search none", desc = "Cancel the ongoing search" }, + { on = "", run = "escape --search", desc = "Cancel the ongoing search" }, { on = "z", run = "plugin zoxide", desc = "Jump to a directory using zoxide" }, { on = "Z", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" }, diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index b05b5fb34..f0854345b 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -92,12 +92,10 @@ impl Tab { } pub fn escape_search(&mut self) -> bool { - if !self.current.cwd.is_search() { - return false; - } - + let b = self.current.cwd.is_search(); self.search_stop(); - render_and!(true) + + render_and!(b) } pub fn try_escape_visual(&mut self) -> bool { diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index 700f54238..0c3ca2ea5 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -1,68 +1,45 @@ -use std::{fmt::Display, mem, time::Duration}; +use std::{mem, time::Duration}; -use anyhow::bail; use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; +use tracing::error; use yazi_config::popup::InputCfg; use yazi_plugin::external; -use yazi_proxy::{AppProxy, InputProxy, ManagerProxy, TabProxy}; -use yazi_shared::{event::Cmd, fs::FilesOp, render}; +use yazi_proxy::{options::{SearchOpt, SearchOptVia}, AppProxy, InputProxy, ManagerProxy, TabProxy}; +use yazi_shared::fs::FilesOp; use crate::tab::Tab; -#[derive(PartialEq, Eq)] -pub enum OptType { - None, - Rg, - Fd, -} +impl Tab { + pub fn search(&mut self, opt: impl TryInto) { + let Ok(mut opt) = opt.try_into() else { + return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option"); + }; -impl From for OptType { - fn from(value: String) -> Self { - match value.as_str() { - "rg" => Self::Rg, - "fd" => Self::Fd, - _ => Self::None, + if opt.via == SearchOptVia::None { + return self.search_stop(); } - } -} - -impl Display for OptType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Self::Rg => "rg", - Self::Fd => "fd", - Self::None => "none", - }) - } -} -pub struct Opt { - pub type_: OptType, - pub args: Vec, -} + if let Some(handle) = self.search.take() { + handle.abort(); + } -impl TryFrom for Opt { - type Error = (); + tokio::spawn(async move { + let mut input = + InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject)); - fn try_from(mut c: Cmd) -> Result { - Ok(Self { - type_: c.take_first_str().unwrap_or_default().into(), - args: shell_words::split(c.str("args").unwrap_or_default()).map_err(|_| ())?, - }) + if let Some(Ok(subject)) = input.recv().await { + opt.subject = subject; + TabProxy::search_do(opt); + } + }); } -} -impl Tab { - pub fn search(&mut self, opt: impl TryInto) { + pub fn search_do(&mut self, opt: impl TryInto) { let Ok(opt) = opt.try_into() else { - return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option"); + return error!("Failed to parse search option for `search_do`"); }; - if opt.type_ == OptType::None { - return self.search_stop(); - } - if let Some(handle) = self.search.take() { handle.abort(); } @@ -71,14 +48,21 @@ impl Tab { let hidden = self.conf.show_hidden; self.search = Some(tokio::spawn(async move { - let mut input = InputProxy::show(InputCfg::search(&opt.type_.to_string())); - let Some(Ok(subject)) = input.recv().await else { bail!("") }; - - cwd = cwd.into_search(subject.clone()); - let rx = if opt.type_ == OptType::Rg { - external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject, args: opt.args }) + cwd = cwd.into_search(opt.subject.clone()); + let rx = if opt.via == SearchOptVia::Rg { + external::rg(external::RgOpt { + cwd: cwd.clone(), + hidden, + subject: opt.subject, + args: opt.args, + }) } else { - external::fd(external::FdOpt { cwd: cwd.clone(), hidden, subject, args: opt.args }) + external::fd(external::FdOpt { + cwd: cwd.clone(), + hidden, + subject: opt.subject, + args: opt.args, + }) }?; let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300)); @@ -89,10 +73,9 @@ impl Tab { FilesOp::Part(cwd.clone(), chunk, ticket).emit(); } FilesOp::Done(cwd, None, ticket).emit(); + Ok(()) })); - - render!(); } pub(super) fn search_stop(&mut self) { diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 429d143d3..c5f4d605b 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -31,4 +31,4 @@ tracing = { workspace = true } vergen-gitcl = { version = "1.0.0", features = [ "build" ] } [target."cfg(unix)".dependencies] -uzers = "0.12.0" +uzers = "0.12.1" diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index ffa0222b1..b2b6fc02b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -110,6 +110,7 @@ impl<'a> Executor<'a> { on!(ACTIVE, hidden); on!(ACTIVE, linemode); on!(ACTIVE, search); + on!(ACTIVE, search_do); // Filter on!(ACTIVE, filter); diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 0a1d407b0..c95005459 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -41,7 +41,7 @@ unicode-width = { workspace = true } yazi-prebuild = "0.1.2" [target."cfg(unix)".dependencies] -uzers = "0.12.0" +uzers = "0.12.1" [target."cfg(windows)".dependencies] clipboard-win = "5.4.0" diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index ed97a7267..18f10b9ae 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -17,6 +17,7 @@ yazi-config = { path = "../yazi-config", version = "0.3.0" } yazi-shared = { path = "../yazi-shared", version = "0.3.0" } # External dependencies -anyhow = { workspace = true } -mlua = { workspace = true } -tokio = { workspace = true } +anyhow = { workspace = true } +mlua = { workspace = true } +shell-words = { workspace = true } +tokio = { workspace = true } diff --git a/yazi-proxy/src/options/mod.rs b/yazi-proxy/src/options/mod.rs index ec0de4f47..f2284af59 100644 --- a/yazi-proxy/src/options/mod.rs +++ b/yazi-proxy/src/options/mod.rs @@ -1,7 +1,9 @@ mod notify; mod open; mod process; +mod search; pub use notify::*; pub use open::*; pub use process::*; +pub use search::*; diff --git a/yazi-proxy/src/options/search.rs b/yazi-proxy/src/options/search.rs new file mode 100644 index 000000000..b985e773d --- /dev/null +++ b/yazi-proxy/src/options/search.rs @@ -0,0 +1,53 @@ +use std::fmt::Display; + +use yazi_shared::event::Cmd; + +pub struct SearchOpt { + pub via: SearchOptVia, + pub subject: String, + pub args: Vec, + pub args_raw: String, +} + +impl TryFrom for SearchOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { + Ok(Self { + // TODO: remove `c.take_first_str()` in the future + via: c.take_str("via").or_else(|| c.take_first_str()).unwrap_or_default().into(), + subject: c.take_first_str().unwrap_or_default(), + args: shell_words::split(c.str("args").unwrap_or_default()).map_err(|_| ())?, + args_raw: c.take_str("args").unwrap_or_default(), + }) + } +} + +// Via +#[derive(PartialEq, Eq)] +pub enum SearchOptVia { + // TODO: remove `None` in the future + None, + Rg, + Fd, +} + +impl From for SearchOptVia { + fn from(value: String) -> Self { + match value.as_str() { + "rg" => Self::Rg, + "fd" => Self::Fd, + _ => Self::None, + } + } +} + +impl Display for SearchOptVia { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Rg => "rg", + Self::Fd => "fd", + Self::None => "none", + }) + } +} diff --git a/yazi-proxy/src/tab.rs b/yazi-proxy/src/tab.rs index 4f01e8219..e6c4170f5 100644 --- a/yazi-proxy/src/tab.rs +++ b/yazi-proxy/src/tab.rs @@ -1,5 +1,7 @@ use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; +use crate::options::SearchOpt; + pub struct TabProxy; impl TabProxy { @@ -12,4 +14,12 @@ impl TabProxy { pub fn reveal(target: &Url) { emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager)); } + + #[inline] + pub fn search_do(opt: SearchOpt) { + emit!(Call( + Cmd::args("search_do", vec![opt.subject]).with("via", opt.via).with("args", opt.args_raw), + Layer::Manager + )); + } } diff --git a/yazi-shared/src/fs/url.rs b/yazi-shared/src/fs/url.rs index 01db98d3c..68aaebd25 100644 --- a/yazi-shared/src/fs/url.rs +++ b/yazi-shared/src/fs/url.rs @@ -167,6 +167,7 @@ impl Url { #[inline] pub fn into_regular(mut self) -> Self { self.scheme = UrlScheme::Regular; + self.frag = String::new(); self } @@ -192,6 +193,7 @@ impl Url { #[inline] pub fn into_archive(mut self) -> Self { self.scheme = UrlScheme::Archive; + self.frag = String::new(); self }