From f46cd3f8ee3846f8cacdefa50c56bb00202bb5a2 Mon Sep 17 00:00:00 2001 From: TD-Sky Date: Thu, 1 May 2025 14:42:52 +0800 Subject: [PATCH 1/5] confirm for bulk_rename --- yazi-config/preset/yazi-default.toml | 5 +++++ yazi-config/src/popup/confirm.rs | 4 ++++ yazi-config/src/popup/options.rs | 15 +++++++++++++++ yazi-core/src/mgr/commands/bulk_rename.rs | 15 +-------------- yazi-fm/src/app/commands/render.rs | 7 +++---- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml index 12100e71..8d6e7a52 100644 --- a/yazi-config/preset/yazi-default.toml +++ b/yazi-config/preset/yazi-default.toml @@ -212,6 +212,11 @@ quit_content = "The following tasks are still running, are you sure you want to quit_origin = "center" quit_offset = [ 0, 0, 50, 15 ] +# bulk rename +bulk_rename_title = "Rename {n} file{s}?" +bulk_rename_origin = "center" +bulk_rename_offset = [0, 0, 70, 30] + [pick] open_title = "Open with:" open_origin = "hovered" diff --git a/yazi-config/src/popup/confirm.rs b/yazi-config/src/popup/confirm.rs index 703102b2..3e86fddc 100644 --- a/yazi-config/src/popup/confirm.rs +++ b/yazi-config/src/popup/confirm.rs @@ -26,6 +26,10 @@ pub struct Confirm { pub quit_content: String, pub quit_origin: Origin, pub quit_offset: Offset, + // bulk rename + pub bulk_rename_title: String, + pub bulk_rename_origin: Origin, + pub bulk_rename_offset: Offset, } impl Confirm { diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index b27d0c65..72b0777a 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}}; use yazi_shared::url::Url; @@ -154,6 +156,19 @@ impl ConfirmCfg { ) } + pub fn bulk_rename(todo: &[(PathBuf, PathBuf)]) -> Self { + let count = todo.len(); + + Self::new( + Self::replace_number(&YAZI.confirm.bulk_rename_title, count), + (YAZI.confirm.bulk_rename_origin, YAZI.confirm.bulk_rename_offset), + None, + Some(Text::from_iter( + todo.iter().map(|(old, new)| format!("{} -> {}", old.display(), new.display())), + )), + ) + } + fn replace_number(tpl: &str, n: usize) -> String { tpl.replace("{n}", &n.to_string()).replace("{s}", if n > 1 { "s" } else { "" }) } diff --git a/yazi-core/src/mgr/commands/bulk_rename.rs b/yazi-core/src/mgr/commands/bulk_rename.rs index f70c1a70..b8c7ff69 100644 --- a/yazi-core/src/mgr/commands/bulk_rename.rs +++ b/yazi-core/src/mgr/commands/bulk_rename.rs @@ -44,8 +44,6 @@ impl Mgr { .await; let _permit = HIDER.acquire().await.unwrap(); - defer!(AppProxy::resume()); - AppProxy::stop().await; let new: Vec<_> = fs::read_to_string(&tmp).await?.lines().take(old.len()).map(PathBuf::from).collect(); @@ -70,18 +68,7 @@ impl Mgr { 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 !ConfirmProxy::show(ConfirmCfg::bulk_rename(&todo)).await { return Ok(()); } diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs index b0382de7..da5ba3ac 100644 --- a/yazi-fm/src/app/commands/render.rs +++ b/yazi-fm/src/app/commands/render.rs @@ -79,12 +79,11 @@ impl App { fn routine(push: bool, cursor: Option<(Position, SetCursorStyle)>) { static COUNT: AtomicU8 = AtomicU8::new(0); - if push && COUNT.fetch_add(1, Ordering::Relaxed) != 0 { - return; - } else if !push && COUNT.fetch_sub(1, Ordering::Relaxed) != 1 { + if (push && COUNT.fetch_add(1, Ordering::Relaxed) != 0) + || (!push && COUNT.fetch_sub(1, Ordering::Relaxed) != 1) + { return; } - _ = if push { queue!(TTY.writer(), BeginSynchronizedUpdate) } else if let Some((Position { x, y }, shape)) = cursor { From 6a70ea3c57709bed448768b62c4c416cb838aaf3 Mon Sep 17 00:00:00 2001 From: TD-Sky Date: Thu, 1 May 2025 14:57:02 +0800 Subject: [PATCH 2/5] mismatch amount error --- yazi-core/src/mgr/commands/bulk_rename.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/yazi-core/src/mgr/commands/bulk_rename.rs b/yazi-core/src/mgr/commands/bulk_rename.rs index b8c7ff69..78752660 100644 --- a/yazi-core/src/mgr/commands/bulk_rename.rs +++ b/yazi-core/src/mgr/commands/bulk_rename.rs @@ -52,13 +52,15 @@ impl Mgr { } async fn bulk_rename_do(root: PathBuf, old: Vec, new: Vec) -> Result<()> { - terminal_clear(TTY.writer())?; if old.len() != new.len() { - #[rustfmt::skip] - let s = format!("Number of new and old file names mismatch (New: {}, Old: {}).\nPress to exit...", new.len(), old.len()); - execute!(TTY.writer(), Print(s))?; - - TTY.reader().read_exact(&mut [0])?; + AppProxy::notify_error( + "Bulk rename", + format!( + "Number of new and old file names mismatch (New: {}, Old: {})", + new.len(), + old.len() + ), + ); return Ok(()); } From 82056280b3ec78fd7d3a231c6d542287c46f9ad8 Mon Sep 17 00:00:00 2001 From: TD-Sky Date: Thu, 1 May 2025 17:39:54 +0800 Subject: [PATCH 3/5] schedule rename files --- yazi-core/src/mgr/commands/bulk_rename.rs | 88 ++++++++--------------- yazi-core/src/mgr/commands/rename.rs | 6 +- yazi-core/src/tasks/tasks.rs | 2 +- yazi-fm/src/executor.rs | 2 +- yazi-scheduler/src/file/file.rs | 37 +++++++++- yazi-scheduler/src/file/op.rs | 9 +++ yazi-scheduler/src/scheduler.rs | 16 ++++- 7 files changed, 94 insertions(+), 66 deletions(-) diff --git a/yazi-core/src/mgr/commands/bulk_rename.rs b/yazi-core/src/mgr/commands/bulk_rename.rs index 78752660..1dd45034 100644 --- a/yazi-core/src/mgr/commands/bulk_rename.rs +++ b/yazi-core/src/mgr/commands/bulk_rename.rs @@ -1,20 +1,26 @@ -use std::{borrow::Cow, collections::HashMap, ffi::{OsStr, OsString}, io::{Read, Write}, path::PathBuf}; +use std::{ + borrow::Cow, + collections::HashMap, + ffi::{OsStr, OsString}, + path::PathBuf, + sync::Arc, +}; -use anyhow::{Result, anyhow}; -use crossterm::{execute, style::Print}; +use anyhow::Result; use scopeguard::defer; -use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt}; -use yazi_config::YAZI; -use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file}; -use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER}; -use yazi_shared::{terminal_clear, url::Url}; -use yazi_term::tty::TTY; +use tokio::{ + fs::{self, OpenOptions}, + io::AsyncWriteExt, +}; +use yazi_config::{YAZI, popup::ConfirmCfg}; +use yazi_fs::max_common_root; +use yazi_proxy::{AppProxy, ConfirmProxy, TasksProxy}; +use yazi_scheduler::Scheduler; use crate::mgr::Mgr; impl Mgr { - pub(super) fn bulk_rename(&self) { + pub(super) fn bulk_rename(&self, sched: Arc) { let Some(opener) = YAZI.opener.block(YAZI.open.all("bulk-rename.txt", "text/plain")) else { return AppProxy::notify_warn("Bulk rename", "No text opener found"); }; @@ -37,21 +43,25 @@ impl Mgr { .await?; defer! { tokio::spawn(fs::remove_file(tmp.clone())); } - TasksProxy::process_exec(Cow::Borrowed(opener), cwd, vec![ - OsString::new(), - tmp.to_owned().into(), - ]) + TasksProxy::process_exec( + Cow::Borrowed(opener), + cwd, + vec![OsString::new(), tmp.to_owned().into()], + ) .await; - let _permit = HIDER.acquire().await.unwrap(); - let new: Vec<_> = fs::read_to_string(&tmp).await?.lines().take(old.len()).map(PathBuf::from).collect(); - Self::bulk_rename_do(root, old, new).await + Self::bulk_rename_do(root, old, new, sched).await }); } - async fn bulk_rename_do(root: PathBuf, old: Vec, new: Vec) -> Result<()> { + async fn bulk_rename_do( + root: PathBuf, + old: Vec, + new: Vec, + sched: Arc, + ) -> Result<()> { if old.len() != new.len() { AppProxy::notify_error( "Bulk rename", @@ -74,46 +84,10 @@ impl Mgr { return Ok(()); } - let permit = WATCHER.acquire().await.unwrap(); - let (mut failed, mut succeeded) = (Vec::new(), HashMap::with_capacity(todo.len())); - for (o, n) in todo { - let (old, new) = (root.join(&o), root.join(&n)); - - if maybe_exists(&new).await && !paths_to_same_file(&old, &new).await { - failed.push((o, n, anyhow!("Destination already exists"))); - } else if let Err(e) = fs::rename(&old, &new).await { - failed.push((o, n, e.into())); - } else if let Ok(f) = File::from(new.into()).await { - succeeded.insert(Url::from(old), f); - } else { - failed.push((o, n, anyhow!("Failed to retrieve file info"))); - } + for (old, new) in todo { + sched.file_rename_at(&root, &old, &new); } - if !succeeded.is_empty() { - Pubsub::pub_from_bulk(succeeded.iter().map(|(o, n)| (o, &n.url)).collect()); - FilesOp::rename(succeeded); - } - drop(permit); - - if !failed.is_empty() { - Self::output_failed(failed).await?; - } - Ok(()) - } - - async fn output_failed(failed: Vec<(PathBuf, PathBuf, anyhow::Error)>) -> Result<()> { - let mut stdout = TTY.lockout(); - terminal_clear(&mut *stdout)?; - - writeln!(stdout, "Failed to rename:")?; - for (old, new, err) in failed { - writeln!(stdout, "{} -> {}: {err}", old.display(), new.display())?; - } - writeln!(stdout, "\nPress ENTER to exit")?; - - stdout.flush()?; - TTY.reader().read_exact(&mut [0])?; Ok(()) } diff --git a/yazi-core/src/mgr/commands/rename.rs b/yazi-core/src/mgr/commands/rename.rs index bf6e809d..5bf736fe 100644 --- a/yazi-core/src/mgr/commands/rename.rs +++ b/yazi-core/src/mgr/commands/rename.rs @@ -8,7 +8,7 @@ use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, paths_to_same_file, use yazi_proxy::{ConfirmProxy, InputProxy, TabProxy, WATCHER}; use yazi_shared::{Id, event::CmdCow, url::{Url, UrnBuf}}; -use crate::mgr::Mgr; +use crate::{mgr::Mgr, tasks::Tasks}; struct Opt { hovered: bool, @@ -30,11 +30,11 @@ impl From for Opt { impl Mgr { #[yazi_codegen::command] - pub fn rename(&mut self, opt: Opt) { + pub fn rename(&mut self, opt: Opt, tasks: &Tasks) { if !self.active_mut().try_escape_visual() { return; } else if !opt.hovered && !self.active().selected.is_empty() { - return self.bulk_rename(); + return self.bulk_rename(tasks.scheduler.clone()); } let Some(hovered) = self.hovered() else { return }; diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 11a906d2..8138b0e9 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -10,7 +10,7 @@ use yazi_shared::event::Cmd; use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress}; pub struct Tasks { - pub(super) scheduler: Arc, + pub scheduler: Arc, handle: JoinHandle<()>, pub visible: bool, diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 98119342..3714e6ab 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -115,7 +115,7 @@ impl<'a> Executor<'a> { on!(MGR, remove, &self.app.cx.tasks); on!(MGR, remove_do, &self.app.cx.tasks); on!(MGR, create); - on!(MGR, rename); + on!(MGR, rename, &self.app.cx.tasks); on!(ACTIVE, copy); on!(ACTIVE, shell); on!(ACTIVE, hidden); diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 358998c9..a08bb61b 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -4,10 +4,15 @@ use anyhow::{Result, anyhow}; use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::YAZI; -use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path}; +use yazi_fs::{ + SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, + paths_to_same_file, skip_path, +}; use yazi_shared::url::Url; -use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}; +use super::{ + FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash, +}; use crate::{LOW, NORMAL, TaskOp, TaskProg}; pub struct File { @@ -146,6 +151,26 @@ impl File { .await??; self.prog.send(TaskProg::Adv(task.id, 1, task.length))?; } + FileOp::Rename(task) => { + if maybe_exists(&task.to).await && !paths_to_same_file(&task.from, &task.to).await { + let e = anyhow!("Destination already exists"); + self.fail(task.id, format!("An error occurred while renaming: {e:?}"))?; + return Err(e); + } + + if let Err(e) = fs::rename(&task.from, &task.to).await { + self.fail(task.id, format!("An error occurred while renaming: {e}"))?; + return Err(e.into()); + } + + if yazi_fs::File::from(task.to).await.is_err() { + let e = anyhow!("Failed to retrieve file info"); + self.fail(task.id, format!("An error occurred while renaming: {e:?}"))?; + return Err(e); + } + + self.prog.send(TaskProg::Adv(task.id, 1, 0))?; + } } Ok(()) } @@ -325,6 +350,14 @@ impl File { self.succ(id) } + pub async fn rename(&self, task: FileOpRename) -> Result<()> { + let id = task.id; + + self.prog.send(TaskProg::New(id, 0))?; + self.queue(FileOp::Rename(task), NORMAL).await?; + self.succ(id) + } + #[inline] async fn cha(path: &Path, follow: bool) -> io::Result { let meta = fs::symlink_metadata(path).await?; diff --git a/yazi-scheduler/src/file/op.rs b/yazi-scheduler/src/file/op.rs index 0d87fde8..a6e165a1 100644 --- a/yazi-scheduler/src/file/op.rs +++ b/yazi-scheduler/src/file/op.rs @@ -8,6 +8,7 @@ pub enum FileOp { Hardlink(FileOpHardlink), Delete(FileOpDelete), Trash(FileOpTrash), + Rename(FileOpRename), } impl FileOp { @@ -18,6 +19,7 @@ impl FileOp { Self::Hardlink(op) => op.id, Self::Delete(op) => op.id, Self::Trash(op) => op.id, + Self::Rename(op) => op.id, } } } @@ -105,3 +107,10 @@ pub struct FileOpTrash { pub target: Url, pub length: u64, } +// --- Rename +#[derive(Clone, Debug)] +pub struct FileOpRename { + pub id: usize, + pub from: Url, + pub to: Url, +} diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 9b275456..d1be3e2a 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, future::Future, sync::Arc, time::Duration}; +use std::{ffi::OsString, future::Future, path::Path, sync::Arc, time::Duration}; use anyhow::Result; use futures::{FutureExt, future::BoxFuture}; @@ -11,7 +11,7 @@ use yazi_proxy::{MgrProxy, options::{PluginOpt, ProcessExecOpt}}; use yazi_shared::{Throttle, url::Url}; use super::{Ongoing, TaskProg, TaskStage}; -use crate::{HIGH, LOW, NORMAL, TaskKind, TaskOp, file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}}; +use crate::{file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; pub struct Scheduler { pub file: Arc, @@ -209,6 +209,18 @@ impl Scheduler { }) } + pub fn file_rename_at(&self, root: &Path, old: &Path, new: &Path) { + let id = self.ongoing.lock().add( + TaskKind::User, + format!("Rename at {}: {} -> {} ", root.display(), old.display(), new.display()), + ); + + let (from, to): (Url, Url) = (root.join(old).into(), root.join(new).into()); + + let file = self.file.clone(); + self.send_micro(id, LOW, async move { file.rename(FileOpRename { id, from, to }).await }); + } + pub fn plugin_micro(&self, opt: PluginOpt) { let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{}`", opt.id)); From 013d00f57783176a4f74f153e8654bfcd2992e1d Mon Sep 17 00:00:00 2001 From: TD-Sky Date: Thu, 1 May 2025 20:53:09 +0800 Subject: [PATCH 4/5] format --- yazi-config/src/popup/confirm.rs | 10 +++++----- yazi-core/src/mgr/commands/bulk_rename.rs | 22 ++++++---------------- yazi-core/src/tasks/tasks.rs | 2 +- yazi-scheduler/src/file/file.rs | 9 ++------- yazi-scheduler/src/file/op.rs | 4 ++-- yazi-scheduler/src/scheduler.rs | 2 +- 6 files changed, 17 insertions(+), 32 deletions(-) diff --git a/yazi-config/src/popup/confirm.rs b/yazi-config/src/popup/confirm.rs index 3e86fddc..7874671a 100644 --- a/yazi-config/src/popup/confirm.rs +++ b/yazi-config/src/popup/confirm.rs @@ -22,12 +22,12 @@ pub struct Confirm { pub overwrite_offset: Offset, // quit - pub quit_title: String, - pub quit_content: String, - pub quit_origin: Origin, - pub quit_offset: Offset, + pub quit_title: String, + pub quit_content: String, + pub quit_origin: Origin, + pub quit_offset: Offset, // bulk rename - pub bulk_rename_title: String, + pub bulk_rename_title: String, pub bulk_rename_origin: Origin, pub bulk_rename_offset: Offset, } diff --git a/yazi-core/src/mgr/commands/bulk_rename.rs b/yazi-core/src/mgr/commands/bulk_rename.rs index 1dd45034..ce4d4ad2 100644 --- a/yazi-core/src/mgr/commands/bulk_rename.rs +++ b/yazi-core/src/mgr/commands/bulk_rename.rs @@ -1,17 +1,8 @@ -use std::{ - borrow::Cow, - collections::HashMap, - ffi::{OsStr, OsString}, - path::PathBuf, - sync::Arc, -}; +use std::{borrow::Cow, collections::HashMap, ffi::{OsStr, OsString}, path::PathBuf, sync::Arc}; use anyhow::Result; use scopeguard::defer; -use tokio::{ - fs::{self, OpenOptions}, - io::AsyncWriteExt, -}; +use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt}; use yazi_config::{YAZI, popup::ConfirmCfg}; use yazi_fs::max_common_root; use yazi_proxy::{AppProxy, ConfirmProxy, TasksProxy}; @@ -43,11 +34,10 @@ impl Mgr { .await?; defer! { tokio::spawn(fs::remove_file(tmp.clone())); } - TasksProxy::process_exec( - Cow::Borrowed(opener), - cwd, - vec![OsString::new(), tmp.to_owned().into()], - ) + TasksProxy::process_exec(Cow::Borrowed(opener), cwd, vec![ + OsString::new(), + tmp.to_owned().into(), + ]) .await; let new: Vec<_> = diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 8138b0e9..bc3fe1cb 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -11,7 +11,7 @@ use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress}; pub struct Tasks { pub scheduler: Arc, - handle: JoinHandle<()>, + handle: JoinHandle<()>, pub visible: bool, pub cursor: usize, diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index a08bb61b..7c4206bb 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -4,15 +4,10 @@ use anyhow::{Result, anyhow}; use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::YAZI; -use yazi_fs::{ - SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, - paths_to_same_file, skip_path, -}; +use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, paths_to_same_file, skip_path}; use yazi_shared::url::Url; -use super::{ - FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash, -}; +use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash}; use crate::{LOW, NORMAL, TaskOp, TaskProg}; pub struct File { diff --git a/yazi-scheduler/src/file/op.rs b/yazi-scheduler/src/file/op.rs index a6e165a1..757ac8f2 100644 --- a/yazi-scheduler/src/file/op.rs +++ b/yazi-scheduler/src/file/op.rs @@ -110,7 +110,7 @@ pub struct FileOpTrash { // --- Rename #[derive(Clone, Debug)] pub struct FileOpRename { - pub id: usize, + pub id: usize, pub from: Url, - pub to: Url, + pub to: Url, } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index d1be3e2a..6b3e9fa6 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -11,7 +11,7 @@ use yazi_proxy::{MgrProxy, options::{PluginOpt, ProcessExecOpt}}; use yazi_shared::{Throttle, url::Url}; use super::{Ongoing, TaskProg, TaskStage}; -use crate::{file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; +use crate::{HIGH, LOW, NORMAL, TaskKind, TaskOp, file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpRename, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}}; pub struct Scheduler { pub file: Arc, From 6919b2f1932c36ada05f053963861f5c56770dec Mon Sep 17 00:00:00 2001 From: TD-Sky Date: Thu, 1 May 2025 22:20:16 +0800 Subject: [PATCH 5/5] pubsub for bulk rename --- yazi-dds/src/pump.rs | 21 ++++++++++++++++++++- yazi-scheduler/src/scheduler.rs | 21 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/yazi-dds/src/pump.rs b/yazi-dds/src/pump.rs index 335ae521..c2d20410 100644 --- a/yazi-dds/src/pump.rs +++ b/yazi-dds/src/pump.rs @@ -1,9 +1,10 @@ -use std::time::Duration; +use std::{collections::HashMap, time::Duration}; use parking_lot::Mutex; use tokio::{pin, select, sync::mpsc}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_util::sync::CancellationToken; +use yazi_fs::{File, FilesOp}; use yazi_shared::{RoCell, url::Url}; use crate::{Pubsub, body::BodyMoveItem}; @@ -12,6 +13,7 @@ static CT: RoCell = RoCell::new(); static MOVE_TX: Mutex>> = Mutex::new(None); static TRASH_TX: Mutex>> = Mutex::new(None); static DELETE_TX: Mutex>> = Mutex::new(None); +static BULK_RENAME_TX: Mutex>> = Mutex::new(None); pub struct Pump; @@ -37,15 +39,24 @@ impl Pump { } } + #[inline] + pub fn push_bulk_rename_pair(from: Url, to: File) { + if let Some(tx) = &*BULK_RENAME_TX.lock() { + tx.send((from, to)).ok(); + } + } + pub(super) fn serve() { let (move_tx, move_rx) = mpsc::unbounded_channel(); let (trash_tx, trash_rx) = mpsc::unbounded_channel(); let (delete_tx, delete_rx) = mpsc::unbounded_channel(); + let (bulk_rename_tx, bulk_rename_rx) = mpsc::unbounded_channel(); CT.with(<_>::default); MOVE_TX.lock().replace(move_tx); TRASH_TX.lock().replace(trash_tx); DELETE_TX.lock().replace(delete_tx); + BULK_RENAME_TX.lock().replace(bulk_rename_tx); tokio::spawn(async move { let move_rx = @@ -54,16 +65,23 @@ impl Pump { UnboundedReceiverStream::new(trash_rx).chunks_timeout(1000, Duration::from_millis(500)); let delete_rx = UnboundedReceiverStream::new(delete_rx).chunks_timeout(1000, Duration::from_millis(500)); + let bulk_rename_rx = UnboundedReceiverStream::new(bulk_rename_rx) + .chunks_timeout(1000, Duration::from_millis(500)); pin!(move_rx); pin!(trash_rx); pin!(delete_rx); + pin!(bulk_rename_rx); loop { select! { Some(items) = move_rx.next() => Pubsub::pub_from_move(items), Some(urls) = trash_rx.next() => Pubsub::pub_from_trash(urls), Some(urls) = delete_rx.next() => Pubsub::pub_from_delete(urls), + Some(items) = bulk_rename_rx.next() => { + Pubsub::pub_from_bulk(items.iter().map(|(from, to)| (from, &to.url)).collect()); + FilesOp::rename(HashMap::from_iter(items)); + } else => { CT.cancel(); break; @@ -77,6 +95,7 @@ impl Pump { drop(MOVE_TX.lock().take()); drop(TRASH_TX.lock().take()); drop(DELETE_TX.lock().take()); + drop(BULK_RENAME_TX.lock().take()); CT.cancelled().await; } } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 6b3e9fa6..5be7db8b 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -210,13 +210,32 @@ impl Scheduler { } pub fn file_rename_at(&self, root: &Path, old: &Path, new: &Path) { - let id = self.ongoing.lock().add( + let mut ongoing = self.ongoing.lock(); + let id = ongoing.add( TaskKind::User, format!("Rename at {}: {} -> {} ", root.display(), old.display(), new.display()), ); let (from, to): (Url, Url) = (root.join(old).into(), root.join(new).into()); + ongoing.hooks.insert(id, { + let from = from.clone(); + let to = to.clone(); + let ongoing = self.ongoing.clone(); + + Box::new(move |canceled: bool| { + async move { + if !canceled { + if let Ok(to) = yazi_fs::File::from(to).await { + Pump::push_bulk_rename_pair(from, to); + } + } + ongoing.lock().try_remove(id, TaskStage::Hooked); + } + .boxed() + }) + }); + let file = self.file.clone(); self.send_micro(id, LOW, async move { file.rename(FileOpRename { id, from, to }).await }); }