mirror of
https://github.com/sxyazi/yazi.git
synced 2026-05-13 08:16:40 +00:00
feat: new bulk_exit action that customizes the prompt for bulk operations (#3792)
This commit is contained in:
parent
7b61eb1595
commit
c703332b4b
11 changed files with 121 additions and 15 deletions
|
|
@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
- New `ind-hidden` and `key-hidden` DDS events to change hidden status in Lua ([#3748])
|
||||
- New `marker_symbol` option to specify the symbol used for marking files ([#3689])
|
||||
- New `--discard` for `ya pkg` that discard local changes made to packages ([#3781])
|
||||
- New `bulk_exit` action that customizes the prompt for bulk operations ([#3792])
|
||||
- New `fs.unique()` creates a unique file or directory ([#3677])
|
||||
- New `download` DDS event fires when remote files are downloaded ([#3687])
|
||||
- New `ind-which-activate` DDS event to change the which component behavior ([#3608])
|
||||
|
|
@ -1688,3 +1689,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
[#3765]: https://github.com/sxyazi/yazi/pull/3765
|
||||
[#3780]: https://github.com/sxyazi/yazi/pull/3780
|
||||
[#3781]: https://github.com/sxyazi/yazi/pull/3781
|
||||
[#3792]: https://github.com/sxyazi/yazi/pull/3792
|
||||
|
|
|
|||
19
yazi-actor/src/mgr/bulk_exit.rs
Normal file
19
yazi-actor/src/mgr/bulk_exit.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::BulkExitOpt;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct BulkExit;
|
||||
|
||||
impl Actor for BulkExit {
|
||||
type Options = BulkExitOpt;
|
||||
|
||||
const NAME: &str = "bulk_exit";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
cx.mgr.batcher.decide(opt.target, opt.accept);
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -42,8 +42,10 @@ impl Actor for BulkRename {
|
|||
selected.iter().enumerate().map(|(i, u)| Tuple::new(i, skip_url(u, root))).collect();
|
||||
|
||||
let cwd = cx.cwd().clone();
|
||||
let batcher = cx.core.mgr.batcher.clone();
|
||||
tokio::spawn(async move {
|
||||
let tmp = YAZI.preview.tmpfile("bulk");
|
||||
|
||||
Gate::default()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
|
|
@ -54,10 +56,13 @@ impl Actor for BulkRename {
|
|||
|
||||
defer! {
|
||||
let tmp = tmp.clone();
|
||||
batcher.drain(&tmp);
|
||||
tokio::spawn(async move {
|
||||
Local::regular(&tmp).remove_file().await
|
||||
});
|
||||
}
|
||||
|
||||
batcher.prime(&tmp);
|
||||
TasksProxy::process_exec(
|
||||
cwd.into(),
|
||||
Splatter::new(&[UrlCow::default(), tmp.as_url().into()]).splat(&opener.run),
|
||||
|
|
@ -79,7 +84,8 @@ impl Actor for BulkRename {
|
|||
.map(|(i, s)| Tuple::new(i, s))
|
||||
.collect();
|
||||
|
||||
Self::r#do(root, old, new, selected).await
|
||||
let decision = batcher.drain(&tmp);
|
||||
Self::r#do(root, old, new, selected, decision).await
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
|
|
@ -91,6 +97,7 @@ impl BulkRename {
|
|||
old: Vec<Tuple>,
|
||||
new: Vec<Tuple>,
|
||||
selected: Vec<UrlBuf>,
|
||||
decision: Option<bool>,
|
||||
) -> Result<()> {
|
||||
terminal_clear(TTY.writer())?;
|
||||
if old.len() != new.len() {
|
||||
|
|
@ -108,18 +115,7 @@ impl BulkRename {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
{
|
||||
let mut w = TTY.lockout();
|
||||
for (old, new) in &todo {
|
||||
writeln!(w, "{} -> {}", old.display(), new.display())?;
|
||||
}
|
||||
write!(w, "Continue to rename? (y/N): ")?;
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
_ = TTY.reader().read(&mut buf)?;
|
||||
if buf[0] != b'y' && buf[0] != b'Y' {
|
||||
if !Self::ask_continue(&todo, decision)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +161,25 @@ impl BulkRename {
|
|||
Ok(url.try_replace(take, PathDyn::with(url.kind(), rep)?)?.into_owned())
|
||||
}
|
||||
|
||||
fn ask_continue(todo: &[(Tuple, Tuple)], decision: Option<bool>) -> Result<bool> {
|
||||
if let Some(decision) = decision {
|
||||
return Ok(decision);
|
||||
}
|
||||
|
||||
{
|
||||
let mut w = TTY.lockout();
|
||||
for (old, new) in todo {
|
||||
writeln!(w, "{} -> {}", old.display(), new.display())?;
|
||||
}
|
||||
write!(w, "Continue to rename? (y/N): ")?;
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
_ = TTY.reader().read(&mut buf)?;
|
||||
Ok(buf[0] == b'y' || buf[0] == b'Y')
|
||||
}
|
||||
|
||||
async fn output_failed(failed: Vec<(Tuple, Tuple, anyhow::Error)>) -> Result<()> {
|
||||
let mut stdout = TTY.lockout();
|
||||
terminal_clear(&mut *stdout)?;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
yazi_macro::mod_flat!(
|
||||
arrow
|
||||
back
|
||||
bulk_exit
|
||||
bulk_rename
|
||||
cd
|
||||
close
|
||||
|
|
|
|||
33
yazi-core/src/mgr/batcher.rs
Normal file
33
yazi-core/src/mgr/batcher.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use std::{path::{Path, PathBuf}, sync::Arc};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use parking_lot::Mutex;
|
||||
use yazi_shared::url::AsUrl;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Batcher {
|
||||
pending: Arc<Mutex<HashMap<PathBuf, Option<bool>>>>,
|
||||
}
|
||||
|
||||
impl Batcher {
|
||||
pub fn prime<T>(&self, target: T)
|
||||
where
|
||||
T: Into<PathBuf>,
|
||||
{
|
||||
self.pending.lock().insert(target.into(), None);
|
||||
}
|
||||
|
||||
pub fn drain(&self, target: &Path) -> Option<bool> {
|
||||
self.pending.lock().remove(target).flatten()
|
||||
}
|
||||
|
||||
pub fn decide<T>(&self, target: T, decision: bool)
|
||||
where
|
||||
T: AsUrl,
|
||||
{
|
||||
let Some(path) = target.as_url().as_local() else { return };
|
||||
if let Some(value) = self.pending.lock().get_mut(path) {
|
||||
*value = value.or(Some(decision));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,14 @@ use yazi_fs::Splatable;
|
|||
use yazi_shared::url::{AsUrl, Url, UrlBuf};
|
||||
use yazi_watcher::Watcher;
|
||||
|
||||
use super::{Mimetype, Tabs, Yanked};
|
||||
use super::{Batcher, Mimetype, Tabs, Yanked};
|
||||
use crate::tab::{Folder, Tab};
|
||||
|
||||
pub struct Mgr {
|
||||
pub tabs: Tabs,
|
||||
pub yanked: Yanked,
|
||||
|
||||
pub batcher: Batcher,
|
||||
pub watcher: Watcher,
|
||||
pub mimetype: Mimetype,
|
||||
}
|
||||
|
|
@ -24,6 +25,7 @@ impl Mgr {
|
|||
tabs: Default::default(),
|
||||
yanked: Default::default(),
|
||||
|
||||
batcher: Default::default(),
|
||||
watcher: Watcher::serve(),
|
||||
mimetype: Default::default(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(mgr mimetype tabs yanked);
|
||||
yazi_macro::mod_flat!(batcher mgr mimetype tabs yanked);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub enum Spark<'a> {
|
|||
// Mgr
|
||||
Arrow(yazi_parser::ArrowOpt),
|
||||
Back(yazi_parser::VoidOpt),
|
||||
BulkExit(yazi_parser::mgr::BulkExitOpt),
|
||||
BulkRename(yazi_parser::VoidOpt),
|
||||
Cd(yazi_parser::mgr::CdOpt),
|
||||
Close(yazi_parser::mgr::CloseOpt),
|
||||
|
|
@ -207,6 +208,7 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
// Mgr
|
||||
Self::Arrow(b) => b.into_lua(lua),
|
||||
Self::Back(b) => b.into_lua(lua),
|
||||
Self::BulkExit(b) => b.into_lua(lua),
|
||||
Self::BulkRename(b) => b.into_lua(lua),
|
||||
Self::Cd(b) => b.into_lua(lua),
|
||||
Self::Close(b) => b.into_lua(lua),
|
||||
|
|
@ -378,6 +380,7 @@ try_from_spark!(yazi_parser::confirm::ShowOpt, confirm:show);
|
|||
try_from_spark!(yazi_parser::help::ToggleOpt, help:toggle);
|
||||
try_from_spark!(yazi_parser::input::CloseOpt, input:close);
|
||||
try_from_spark!(yazi_widgets::input::InputOpt, input:show);
|
||||
try_from_spark!(yazi_parser::mgr::BulkExitOpt, mgr:bulk_exit);
|
||||
try_from_spark!(yazi_parser::mgr::CdOpt, mgr:cd);
|
||||
try_from_spark!(yazi_parser::mgr::CloseOpt, mgr:close);
|
||||
try_from_spark!(yazi_parser::mgr::CopyOpt, mgr:copy);
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ impl<'a> Executor<'a> {
|
|||
on!(linemode);
|
||||
on!(search);
|
||||
on!(search_do);
|
||||
on!(bulk_exit);
|
||||
on!(bulk_rename);
|
||||
|
||||
// Filter
|
||||
|
|
|
|||
29
yazi-parser/src/mgr/bulk_exit.rs
Normal file
29
yazi-parser/src/mgr/bulk_exit.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::ActionCow, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BulkExitOpt {
|
||||
pub target: UrlCow<'static>,
|
||||
pub accept: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for BulkExitOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> {
|
||||
let Ok(target) = a.take_first::<UrlCow>() else {
|
||||
bail!("invalid target in BulkExitOpt");
|
||||
};
|
||||
|
||||
Ok(Self { target, accept: a.bool("accept") })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BulkExitOpt {
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
||||
impl IntoLua for BulkExitOpt {
|
||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
yazi_macro::mod_flat!(
|
||||
bulk_exit
|
||||
cd
|
||||
close
|
||||
copy
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue