mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: new bulk_exit action that customizes the prompt for bulk operations
This commit is contained in:
parent
7b61eb1595
commit
42f76cc8bb
10 changed files with 123 additions and 15 deletions
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,11 @@ 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");
|
||||
|
||||
batcher.prime(&tmp);
|
||||
Gate::default()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
|
|
@ -54,6 +57,7 @@ impl Actor for BulkRename {
|
|||
|
||||
defer! {
|
||||
let tmp = tmp.clone();
|
||||
batcher.drain(&tmp);
|
||||
tokio::spawn(async move {
|
||||
Local::regular(&tmp).remove_file().await
|
||||
});
|
||||
|
|
@ -79,7 +83,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 +96,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 +114,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 +160,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
|
||||
|
|
|
|||
38
yazi-core/src/mgr/batcher.rs
Normal file
38
yazi-core/src/mgr/batcher.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use std::{path::{Path, PathBuf}, sync::Arc};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use yazi_shared::url::AsUrl;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Batcher {
|
||||
pending: Arc<Mutex<(PathBuf, Option<bool>)>>,
|
||||
}
|
||||
|
||||
impl Batcher {
|
||||
pub fn prime<T>(&self, target: T)
|
||||
where
|
||||
T: Into<PathBuf>,
|
||||
{
|
||||
*self.pending.lock() = (target.into(), None);
|
||||
}
|
||||
|
||||
pub fn drain(&self, target: &Path) -> Option<bool> {
|
||||
let mut pending = self.pending.lock();
|
||||
if pending.0 != target {
|
||||
return None;
|
||||
}
|
||||
|
||||
pending.0 = PathBuf::default();
|
||||
pending.1.take()
|
||||
}
|
||||
|
||||
pub fn decide<T>(&self, target: T, decision: bool)
|
||||
where
|
||||
T: AsUrl,
|
||||
{
|
||||
let mut pending = self.pending.lock();
|
||||
if target.as_url() == pending.0.as_url() {
|
||||
pending.1 = 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
Reference in a new issue