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]
pub fn create(&self, opt: Opt) {
let cwd = self.cwd().to_owned();
let mut input = InputProxy::show(InputCfg::create(opt.dir));
tokio::spawn(async move {
let mut result = InputProxy::show(InputCfg::create(opt.dir));
let Some(Ok(name)) = result.recv().await else {
return Ok(());
};
let Some(Ok(name)) = input.recv().await else { return };
if name.is_empty() {
return Ok(());
return;
}
let new = cwd.join(&name);
@ -36,10 +35,10 @@ impl Mgr {
&& maybe_exists(&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_macro::emit;
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 crate::{mgr::Mgr, tab::Folder, tasks::Tasks};
@ -95,12 +95,10 @@ impl Mgr {
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();
tokio::spawn(async move {
let result =
yazi_proxy::PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
if let Ok(choice) = result.await {
if let Ok(choice) = pick.await {
TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls);
}
});

View file

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

View file

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

View file

@ -77,10 +77,10 @@ impl Tab {
}
fn cd_interactive(&mut self) {
tokio::spawn(async move {
let rx = InputProxy::show(InputCfg::cd());
let input = 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);
while let Some(result) = rx.next().await {

View file

@ -30,10 +30,10 @@ impl From<CmdCow> for Opt {
impl Tab {
#[yazi_codegen::command]
pub fn filter(&mut self, opt: Opt) {
tokio::spawn(async move {
let rx = InputProxy::show(InputCfg::filter());
let input = 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);
while let Some(result) = rx.next().await {

View file

@ -25,10 +25,10 @@ impl From<CmdCow> for Opt {
impl Tab {
#[yazi_codegen::command]
pub fn find(&mut self, opt: Opt) {
tokio::spawn(async move {
let rx = InputProxy::show(InputCfg::find(opt.prev));
let input = 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);
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 anyhow::bail;
use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use tracing::error;
@ -13,22 +12,18 @@ use crate::tab::Tab;
impl Tab {
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");
};
if opt.via == SearchOptVia::None {
return self.search_stop();
}
if let Some(handle) = self.search.take() {
handle.abort();
}
tokio::spawn(async move {
let mut input =
InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject));
let mut input =
InputProxy::show(InputCfg::search(opt.via.as_ref()).with_value(opt.subject.to_owned()));
tokio::spawn(async move {
if let Some(Ok(subject)) = input.recv().await {
opt.subject = Cow::Owned(subject);
TabProxy::search_do(opt);
@ -68,7 +63,6 @@ impl Tab {
subject: opt.subject.into_owned(),
args: opt.args,
}),
SearchOptVia::None => bail!("Invalid `via` option for `search` command"),
}?;
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 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 {
if opt.interactive {
let mut result =
InputProxy::show(InputCfg::shell(opt.block).with_value(opt.run).with_cursor(opt.cursor));
match result.recv().await {
if let Some(mut rx) = input {
match rx.recv().await {
Some(Ok(e)) => opt.run = Cow::Owned(e),
_ => return,
}

View file

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

View file

@ -25,7 +25,9 @@ impl TabProxy {
pub fn search_do(opt: SearchOpt) {
emit!(Call(
// 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)
));
}
}