feat: allow initializing input when opening it with commands like rename, create, find, filter, etc. (#2578)

This commit is contained in:
三咲雅 · Misaki Masa 2025-04-06 16:59:48 +08:00 committed by GitHub
parent fd007ab023
commit be00881403
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 52 additions and 60 deletions

View file

@ -22,13 +22,12 @@ impl Mgr {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn create(&self, opt: Opt) { pub fn create(&self, opt: Opt) {
let cwd = self.cwd().to_owned(); let cwd = self.cwd().to_owned();
let mut input = InputProxy::show(InputCfg::create(opt.dir));
tokio::spawn(async move { tokio::spawn(async move {
let mut result = InputProxy::show(InputCfg::create(opt.dir)); let Some(Ok(name)) = input.recv().await else { return };
let Some(Ok(name)) = result.recv().await else {
return Ok(());
};
if name.is_empty() { if name.is_empty() {
return Ok(()); return;
} }
let new = cwd.join(&name); let new = cwd.join(&name);
@ -36,10 +35,10 @@ impl Mgr {
&& maybe_exists(&new).await && maybe_exists(&new).await
&& !ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await && !ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await
{ {
return Ok(()); return;
} }
Self::create_do(new, opt.dir || name.ends_with('/') || name.ends_with('\\')).await _ = Self::create_do(new, opt.dir || name.ends_with('/') || name.ends_with('\\')).await;
}); });
} }

View file

@ -6,7 +6,7 @@ use yazi_config::{YAZI, popup::PickCfg};
use yazi_fs::File; use yazi_fs::File;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_plugin::isolate; use yazi_plugin::isolate;
use yazi_proxy::{MgrProxy, TasksProxy, options::OpenDoOpt}; use yazi_proxy::{MgrProxy, PickProxy, TasksProxy, options::OpenDoOpt};
use yazi_shared::{MIME_DIR, event::{CmdCow, EventQuit}, url::Url}; use yazi_shared::{MIME_DIR, event::{CmdCow, EventQuit}, url::Url};
use crate::{mgr::Mgr, tab::Folder, tasks::Tasks}; use crate::{mgr::Mgr, tab::Folder, tasks::Tasks};
@ -95,12 +95,10 @@ impl Mgr {
return; return;
} }
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
tokio::spawn(async move { tokio::spawn(async move {
let result = if let Ok(choice) = pick.await {
yazi_proxy::PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
if let Ok(choice) = result.await {
TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls); TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls);
} }
}); });

View file

@ -41,14 +41,14 @@ impl Mgr {
return self.remove_do(opt, tasks); return self.remove_do(opt, tasks);
} }
tokio::spawn(async move { let confirm = ConfirmProxy::show(if opt.permanently {
let result = ConfirmProxy::show(if opt.permanently { ConfirmCfg::delete(&opt.targets)
ConfirmCfg::delete(&opt.targets) } else {
} else { ConfirmCfg::trash(&opt.targets)
ConfirmCfg::trash(&opt.targets) });
});
if result.await { tokio::spawn(async move {
if confirm.await {
MgrProxy::remove_do(opt.targets, opt.permanently); MgrProxy::remove_do(opt.targets, opt.permanently);
} }
}); });

View file

@ -53,12 +53,10 @@ impl Mgr {
let old = hovered.url_owned(); let old = hovered.url_owned();
let tab = self.tabs.active().id; let tab = self.tabs.active().id;
tokio::spawn(async move { let mut input = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let Some(Ok(name)) = result.recv().await else {
return;
};
tokio::spawn(async move {
let Some(Ok(name)) = input.recv().await else { return };
if name.is_empty() { if name.is_empty() {
return; return;
} }

View file

@ -77,10 +77,10 @@ impl Tab {
} }
fn cd_interactive(&mut self) { fn cd_interactive(&mut self) {
tokio::spawn(async move { let input = InputProxy::show(InputCfg::cd());
let rx = InputProxy::show(InputCfg::cd());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
pin!(rx); pin!(rx);
while let Some(result) = rx.next().await { while let Some(result) = rx.next().await {

View file

@ -30,10 +30,10 @@ impl From<CmdCow> for Opt {
impl Tab { impl Tab {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn filter(&mut self, opt: Opt) { pub fn filter(&mut self, opt: Opt) {
tokio::spawn(async move { let input = InputProxy::show(InputCfg::filter());
let rx = InputProxy::show(InputCfg::filter());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
pin!(rx); pin!(rx);
while let Some(result) = rx.next().await { while let Some(result) = rx.next().await {

View file

@ -25,10 +25,10 @@ impl From<CmdCow> for Opt {
impl Tab { impl Tab {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn find(&mut self, opt: Opt) { pub fn find(&mut self, opt: Opt) {
tokio::spawn(async move { let input = InputProxy::show(InputCfg::find(opt.prev));
let rx = InputProxy::show(InputCfg::find(opt.prev));
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
pin!(rx); pin!(rx);
while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await { while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await {

View file

@ -1,6 +1,5 @@
use std::{borrow::Cow, mem, time::Duration}; use std::{borrow::Cow, mem, time::Duration};
use anyhow::bail;
use tokio::pin; use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use tracing::error; use tracing::error;
@ -13,22 +12,18 @@ use crate::tab::Tab;
impl Tab { impl Tab {
pub fn search(&mut self, opt: impl TryInto<SearchOpt>) { pub fn search(&mut self, opt: impl TryInto<SearchOpt>) {
let Ok(mut opt) = opt.try_into() else { let Ok(mut opt): Result<SearchOpt, _> = 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.via == SearchOptVia::None {
return self.search_stop();
}
if let Some(handle) = self.search.take() { if let Some(handle) = self.search.take() {
handle.abort(); handle.abort();
} }
tokio::spawn(async move { let mut input =
let mut input = InputProxy::show(InputCfg::search(opt.via.as_ref()).with_value(opt.subject.to_owned()));
InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject));
tokio::spawn(async move {
if let Some(Ok(subject)) = input.recv().await { if let Some(Ok(subject)) = input.recv().await {
opt.subject = Cow::Owned(subject); opt.subject = Cow::Owned(subject);
TabProxy::search_do(opt); TabProxy::search_do(opt);
@ -68,7 +63,6 @@ impl Tab {
subject: opt.subject.into_owned(), subject: opt.subject.into_owned(),
args: opt.args, args: opt.args,
}), }),
SearchOptVia::None => bail!("Invalid `via` option for `search` command"),
}?; }?;
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500)); let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500));

View file

@ -54,11 +54,16 @@ impl Tab {
let cwd = opt.cwd.take().unwrap_or_else(|| self.cwd().clone()); let cwd = opt.cwd.take().unwrap_or_else(|| self.cwd().clone());
let selected = self.hovered_and_selected().cloned().collect(); let selected = self.hovered_and_selected().cloned().collect();
let input = opt.interactive.then(|| {
InputProxy::show(
InputCfg::shell(opt.block).with_value(opt.run.to_owned()).with_cursor(opt.cursor),
)
});
tokio::spawn(async move { tokio::spawn(async move {
if opt.interactive { if let Some(mut rx) = input {
let mut result = match rx.recv().await {
InputProxy::show(InputCfg::shell(opt.block).with_value(opt.run).with_cursor(opt.cursor));
match result.recv().await {
Some(Ok(e)) => opt.run = Cow::Owned(e), Some(Ok(e)) => opt.run = Cow::Owned(e),
_ => return, _ => return,
} }

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, fmt::Display}; use std::borrow::Cow;
use yazi_shared::event::CmdCow; use yazi_shared::event::CmdCow;
@ -35,31 +35,27 @@ impl TryFrom<CmdCow> for SearchOpt {
// Via // Via
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum SearchOptVia { pub enum SearchOptVia {
// TODO: remove `None` in the future
None,
Rg, Rg,
Fd,
Rga, Rga,
Fd,
} }
impl From<&str> for SearchOptVia { impl From<&str> for SearchOptVia {
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
match value { match value {
"rg" => Self::Rg, "rg" => Self::Rg,
"fd" => Self::Fd,
"rga" => Self::Rga, "rga" => Self::Rga,
_ => Self::None, _ => Self::Fd,
} }
} }
} }
impl Display for SearchOptVia { impl AsRef<str> for SearchOptVia {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn as_ref(&self) -> &str {
f.write_str(match self { match self {
Self::Rg => "rg", Self::Rg => "rg",
Self::Fd => "fd",
Self::Rga => "rga", Self::Rga => "rga",
Self::None => "none", Self::Fd => "fd",
}) }
} }
} }

View file

@ -25,7 +25,9 @@ impl TabProxy {
pub fn search_do(opt: SearchOpt) { pub fn search_do(opt: SearchOpt) {
emit!(Call( emit!(Call(
// TODO: use second positional argument instead of `args` parameter // TODO: use second positional argument instead of `args` parameter
Cmd::args("mgr:search_do", &[opt.subject]).with("via", opt.via).with("args", opt.args_raw) Cmd::args("mgr:search_do", &[opt.subject])
.with("via", opt.via.as_ref())
.with("args", opt.args_raw)
)); ));
} }
} }