feat: new search_do command to make it easier to achieve a flat view (#1431)

This commit is contained in:
三咲雅 · Misaki Masa 2024-08-07 21:36:55 +08:00 committed by GitHub
parent 5df6873957
commit 1e08e09899
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 123 additions and 72 deletions

5
Cargo.lock generated
View file

@ -2751,9 +2751,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uzers" name = "uzers"
version = "0.12.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d85875e16d59b3b1549efce83ff8251a64923b03bef94add0a1862847448de4" checksum = "4df81ff504e7d82ad53e95ed1ad5b72103c11253f39238bcc0235b90768a97dd"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -3418,6 +3418,7 @@ version = "0.3.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"mlua", "mlua",
"shell-words",
"tokio", "tokio",
"yazi-config", "yazi-config",
"yazi-shared", "yazi-shared",

View file

@ -75,7 +75,7 @@ keymap = [
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" }, { 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 fd", desc = "Search files by name using fd" },
{ on = "S", run = "search rg", desc = "Search files by content using ripgrep" }, { on = "S", run = "search rg", desc = "Search files by content using ripgrep" },
{ on = "<C-s>", run = "search none", desc = "Cancel the ongoing search" }, { on = "<C-s>", run = "escape --search", desc = "Cancel the ongoing search" },
{ on = "z", run = "plugin zoxide", desc = "Jump to a directory using zoxide" }, { 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" }, { on = "Z", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" },

View file

@ -92,12 +92,10 @@ impl Tab {
} }
pub fn escape_search(&mut self) -> bool { pub fn escape_search(&mut self) -> bool {
if !self.current.cwd.is_search() { let b = self.current.cwd.is_search();
return false;
}
self.search_stop(); self.search_stop();
render_and!(true)
render_and!(b)
} }
pub fn try_escape_visual(&mut self) -> bool { pub fn try_escape_visual(&mut self) -> bool {

View file

@ -1,65 +1,22 @@
use std::{fmt::Display, mem, time::Duration}; use std::{mem, time::Duration};
use anyhow::bail;
use tokio::pin; use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use tracing::error;
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_plugin::external; use yazi_plugin::external;
use yazi_proxy::{AppProxy, InputProxy, ManagerProxy, TabProxy}; use yazi_proxy::{options::{SearchOpt, SearchOptVia}, AppProxy, InputProxy, ManagerProxy, TabProxy};
use yazi_shared::{event::Cmd, fs::FilesOp, render}; use yazi_shared::fs::FilesOp;
use crate::tab::Tab; use crate::tab::Tab;
#[derive(PartialEq, Eq)]
pub enum OptType {
None,
Rg,
Fd,
}
impl From<String> for OptType {
fn from(value: String) -> Self {
match value.as_str() {
"rg" => Self::Rg,
"fd" => Self::Fd,
_ => Self::None,
}
}
}
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<String>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
Ok(Self {
type_: c.take_first_str().unwrap_or_default().into(),
args: shell_words::split(c.str("args").unwrap_or_default()).map_err(|_| ())?,
})
}
}
impl Tab { impl Tab {
pub fn search(&mut self, opt: impl TryInto<Opt>) { pub fn search(&mut self, opt: impl TryInto<SearchOpt>) {
let Ok(opt) = opt.try_into() else { let Ok(mut opt) = opt.try_into() else {
return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option"); return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option");
}; };
if opt.type_ == OptType::None { if opt.via == SearchOptVia::None {
return self.search_stop(); return self.search_stop();
} }
@ -67,18 +24,45 @@ impl Tab {
handle.abort(); handle.abort();
} }
tokio::spawn(async move {
let mut input =
InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject));
if let Some(Ok(subject)) = input.recv().await {
opt.subject = subject;
TabProxy::search_do(opt);
}
});
}
pub fn search_do(&mut self, opt: impl TryInto<SearchOpt>) {
let Ok(opt) = opt.try_into() else {
return error!("Failed to parse search option for `search_do`");
};
if let Some(handle) = self.search.take() {
handle.abort();
}
let mut cwd = self.current.cwd.clone(); let mut cwd = self.current.cwd.clone();
let hidden = self.conf.show_hidden; let hidden = self.conf.show_hidden;
self.search = Some(tokio::spawn(async move { self.search = Some(tokio::spawn(async move {
let mut input = InputProxy::show(InputCfg::search(&opt.type_.to_string())); cwd = cwd.into_search(opt.subject.clone());
let Some(Ok(subject)) = input.recv().await else { bail!("") }; let rx = if opt.via == SearchOptVia::Rg {
external::rg(external::RgOpt {
cwd = cwd.into_search(subject.clone()); cwd: cwd.clone(),
let rx = if opt.type_ == OptType::Rg { hidden,
external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject, args: opt.args }) subject: opt.subject,
args: opt.args,
})
} else { } 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)); 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::Part(cwd.clone(), chunk, ticket).emit();
} }
FilesOp::Done(cwd, None, ticket).emit(); FilesOp::Done(cwd, None, ticket).emit();
Ok(()) Ok(())
})); }));
render!();
} }
pub(super) fn search_stop(&mut self) { pub(super) fn search_stop(&mut self) {

View file

@ -31,4 +31,4 @@ tracing = { workspace = true }
vergen-gitcl = { version = "1.0.0", features = [ "build" ] } vergen-gitcl = { version = "1.0.0", features = [ "build" ] }
[target."cfg(unix)".dependencies] [target."cfg(unix)".dependencies]
uzers = "0.12.0" uzers = "0.12.1"

View file

@ -110,6 +110,7 @@ impl<'a> Executor<'a> {
on!(ACTIVE, hidden); on!(ACTIVE, hidden);
on!(ACTIVE, linemode); on!(ACTIVE, linemode);
on!(ACTIVE, search); on!(ACTIVE, search);
on!(ACTIVE, search_do);
// Filter // Filter
on!(ACTIVE, filter); on!(ACTIVE, filter);

View file

@ -41,7 +41,7 @@ unicode-width = { workspace = true }
yazi-prebuild = "0.1.2" yazi-prebuild = "0.1.2"
[target."cfg(unix)".dependencies] [target."cfg(unix)".dependencies]
uzers = "0.12.0" uzers = "0.12.1"
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
clipboard-win = "5.4.0" clipboard-win = "5.4.0"

View file

@ -17,6 +17,7 @@ yazi-config = { path = "../yazi-config", version = "0.3.0" }
yazi-shared = { path = "../yazi-shared", version = "0.3.0" } yazi-shared = { path = "../yazi-shared", version = "0.3.0" }
# External dependencies # External dependencies
anyhow = { workspace = true } anyhow = { workspace = true }
mlua = { workspace = true } mlua = { workspace = true }
tokio = { workspace = true } shell-words = { workspace = true }
tokio = { workspace = true }

View file

@ -1,7 +1,9 @@
mod notify; mod notify;
mod open; mod open;
mod process; mod process;
mod search;
pub use notify::*; pub use notify::*;
pub use open::*; pub use open::*;
pub use process::*; pub use process::*;
pub use search::*;

View file

@ -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<String>,
pub args_raw: String,
}
impl TryFrom<Cmd> for SearchOpt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
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<String> 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",
})
}
}

View file

@ -1,5 +1,7 @@
use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use crate::options::SearchOpt;
pub struct TabProxy; pub struct TabProxy;
impl TabProxy { impl TabProxy {
@ -12,4 +14,12 @@ impl TabProxy {
pub fn reveal(target: &Url) { pub fn reveal(target: &Url) {
emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager)); 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
));
}
} }

View file

@ -167,6 +167,7 @@ impl Url {
#[inline] #[inline]
pub fn into_regular(mut self) -> Self { pub fn into_regular(mut self) -> Self {
self.scheme = UrlScheme::Regular; self.scheme = UrlScheme::Regular;
self.frag = String::new();
self self
} }
@ -192,6 +193,7 @@ impl Url {
#[inline] #[inline]
pub fn into_archive(mut self) -> Self { pub fn into_archive(mut self) -> Self {
self.scheme = UrlScheme::Archive; self.scheme = UrlScheme::Archive;
self.frag = String::new();
self self
} }