mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-30 04:02:29 +03:00
feat: new search_do
command to make it easier to achieve a flat view (#1431)
This commit is contained in:
parent
5df6873957
commit
1e08e09899
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -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",
|
||||
|
@ -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 = "<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 fzf", desc = "Jump to a directory or reveal a file using fzf" },
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -1,65 +1,22 @@
|
||||
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 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 {
|
||||
pub fn search(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
pub fn search(&mut self, opt: impl TryInto<SearchOpt>) {
|
||||
let Ok(mut opt) = opt.try_into() else {
|
||||
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();
|
||||
}
|
||||
|
||||
@ -67,18 +24,45 @@ impl Tab {
|
||||
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 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) {
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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 }
|
||||
|
@ -1,7 +1,9 @@
|
||||
mod notify;
|
||||
mod open;
|
||||
mod process;
|
||||
mod search;
|
||||
|
||||
pub use notify::*;
|
||||
pub use open::*;
|
||||
pub use process::*;
|
||||
pub use search::*;
|
||||
|
53
yazi-proxy/src/options/search.rs
Normal file
53
yazi-proxy/src/options/search.rs
Normal 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",
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user