mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge 6919b2f193 into 546920e049
This commit is contained in:
commit
8a1429f641
12 changed files with 154 additions and 91 deletions
|
|
@ -211,6 +211,11 @@ quit_content = "The following tasks are still running, are you sure you want to
|
||||||
quit_origin = "center"
|
quit_origin = "center"
|
||||||
quit_offset = [ 0, 0, 50, 15 ]
|
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]
|
[pick]
|
||||||
open_title = "Open with:"
|
open_title = "Open with:"
|
||||||
open_origin = "hovered"
|
open_origin = "hovered"
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ pub struct Confirm {
|
||||||
pub quit_content: String,
|
pub quit_content: String,
|
||||||
pub quit_origin: Origin,
|
pub quit_origin: Origin,
|
||||||
pub quit_offset: Offset,
|
pub quit_offset: Offset,
|
||||||
|
// bulk rename
|
||||||
|
pub bulk_rename_title: String,
|
||||||
|
pub bulk_rename_origin: Origin,
|
||||||
|
pub bulk_rename_offset: Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Confirm {
|
impl Confirm {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
|
use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
|
||||||
use yazi_shared::url::Url;
|
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 {
|
fn replace_number(tpl: &str, n: usize) -> String {
|
||||||
tpl.replace("{n}", &n.to_string()).replace("{s}", if n > 1 { "s" } else { "" })
|
tpl.replace("{n}", &n.to_string()).replace("{s}", if n > 1 { "s" } else { "" })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
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 anyhow::Result;
|
||||||
use crossterm::{execute, style::Print};
|
|
||||||
use scopeguard::defer;
|
use scopeguard::defer;
|
||||||
use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt};
|
use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt};
|
||||||
use yazi_config::YAZI;
|
use yazi_config::{YAZI, popup::ConfirmCfg};
|
||||||
use yazi_dds::Pubsub;
|
use yazi_fs::max_common_root;
|
||||||
use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file};
|
use yazi_proxy::{AppProxy, ConfirmProxy, TasksProxy};
|
||||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER};
|
use yazi_scheduler::Scheduler;
|
||||||
use yazi_shared::{terminal_clear, url::Url};
|
|
||||||
use yazi_term::tty::TTY;
|
|
||||||
|
|
||||||
use crate::mgr::Mgr;
|
use crate::mgr::Mgr;
|
||||||
|
|
||||||
impl Mgr {
|
impl Mgr {
|
||||||
pub(super) fn bulk_rename(&self) {
|
pub(super) fn bulk_rename(&self, sched: Arc<Scheduler>) {
|
||||||
let Some(opener) = YAZI.opener.block(YAZI.open.all("bulk-rename.txt", "text/plain")) else {
|
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");
|
return AppProxy::notify_warn("Bulk rename", "No text opener found");
|
||||||
};
|
};
|
||||||
|
|
@ -43,24 +40,27 @@ impl Mgr {
|
||||||
])
|
])
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let _permit = HIDER.acquire().await.unwrap();
|
|
||||||
defer!(AppProxy::resume());
|
|
||||||
AppProxy::stop().await;
|
|
||||||
|
|
||||||
let new: Vec<_> =
|
let new: Vec<_> =
|
||||||
fs::read_to_string(&tmp).await?.lines().take(old.len()).map(PathBuf::from).collect();
|
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<PathBuf>, new: Vec<PathBuf>) -> Result<()> {
|
async fn bulk_rename_do(
|
||||||
terminal_clear(TTY.writer())?;
|
root: PathBuf,
|
||||||
|
old: Vec<PathBuf>,
|
||||||
|
new: Vec<PathBuf>,
|
||||||
|
sched: Arc<Scheduler>,
|
||||||
|
) -> Result<()> {
|
||||||
if old.len() != new.len() {
|
if old.len() != new.len() {
|
||||||
#[rustfmt::skip]
|
AppProxy::notify_error(
|
||||||
let s = format!("Number of new and old file names mismatch (New: {}, Old: {}).\nPress <Enter> to exit...", new.len(), old.len());
|
"Bulk rename",
|
||||||
execute!(TTY.writer(), Print(s))?;
|
format!(
|
||||||
|
"Number of new and old file names mismatch (New: {}, Old: {})",
|
||||||
TTY.reader().read_exact(&mut [0])?;
|
new.len(),
|
||||||
|
old.len()
|
||||||
|
),
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,61 +70,14 @@ impl Mgr {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if !ConfirmProxy::show(ConfirmCfg::bulk_rename(&todo)).await {
|
||||||
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' {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let permit = WATCHER.acquire().await.unwrap();
|
for (old, new) in todo {
|
||||||
let (mut failed, mut succeeded) = (Vec::new(), HashMap::with_capacity(todo.len()));
|
sched.file_rename_at(&root, &old, &new);
|
||||||
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")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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_proxy::{ConfirmProxy, InputProxy, TabProxy, WATCHER};
|
||||||
use yazi_shared::{Id, event::CmdCow, url::{Url, UrnBuf}};
|
use yazi_shared::{Id, event::CmdCow, url::{Url, UrnBuf}};
|
||||||
|
|
||||||
use crate::mgr::Mgr;
|
use crate::{mgr::Mgr, tasks::Tasks};
|
||||||
|
|
||||||
struct Opt {
|
struct Opt {
|
||||||
hovered: bool,
|
hovered: bool,
|
||||||
|
|
@ -30,11 +30,11 @@ impl From<CmdCow> for Opt {
|
||||||
|
|
||||||
impl Mgr {
|
impl Mgr {
|
||||||
#[yazi_codegen::command]
|
#[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() {
|
if !self.active_mut().try_escape_visual() {
|
||||||
return;
|
return;
|
||||||
} else if !opt.hovered && !self.active().selected.is_empty() {
|
} 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 };
|
let Some(hovered) = self.hovered() else { return };
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use yazi_shared::event::Cmd;
|
||||||
use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress};
|
use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress};
|
||||||
|
|
||||||
pub struct Tasks {
|
pub struct Tasks {
|
||||||
pub(super) scheduler: Arc<Scheduler>,
|
pub scheduler: Arc<Scheduler>,
|
||||||
handle: JoinHandle<()>,
|
handle: JoinHandle<()>,
|
||||||
|
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::time::Duration;
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio::{pin, select, sync::mpsc};
|
use tokio::{pin, select, sync::mpsc};
|
||||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
use yazi_fs::{File, FilesOp};
|
||||||
use yazi_shared::{RoCell, url::Url};
|
use yazi_shared::{RoCell, url::Url};
|
||||||
|
|
||||||
use crate::{Pubsub, body::BodyMoveItem};
|
use crate::{Pubsub, body::BodyMoveItem};
|
||||||
|
|
@ -12,6 +13,7 @@ static CT: RoCell<CancellationToken> = RoCell::new();
|
||||||
static MOVE_TX: Mutex<Option<mpsc::UnboundedSender<BodyMoveItem>>> = Mutex::new(None);
|
static MOVE_TX: Mutex<Option<mpsc::UnboundedSender<BodyMoveItem>>> = Mutex::new(None);
|
||||||
static TRASH_TX: Mutex<Option<mpsc::UnboundedSender<Url>>> = Mutex::new(None);
|
static TRASH_TX: Mutex<Option<mpsc::UnboundedSender<Url>>> = Mutex::new(None);
|
||||||
static DELETE_TX: Mutex<Option<mpsc::UnboundedSender<Url>>> = Mutex::new(None);
|
static DELETE_TX: Mutex<Option<mpsc::UnboundedSender<Url>>> = Mutex::new(None);
|
||||||
|
static BULK_RENAME_TX: Mutex<Option<mpsc::UnboundedSender<(Url, File)>>> = Mutex::new(None);
|
||||||
|
|
||||||
pub struct Pump;
|
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() {
|
pub(super) fn serve() {
|
||||||
let (move_tx, move_rx) = mpsc::unbounded_channel();
|
let (move_tx, move_rx) = mpsc::unbounded_channel();
|
||||||
let (trash_tx, trash_rx) = mpsc::unbounded_channel();
|
let (trash_tx, trash_rx) = mpsc::unbounded_channel();
|
||||||
let (delete_tx, delete_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);
|
CT.with(<_>::default);
|
||||||
MOVE_TX.lock().replace(move_tx);
|
MOVE_TX.lock().replace(move_tx);
|
||||||
TRASH_TX.lock().replace(trash_tx);
|
TRASH_TX.lock().replace(trash_tx);
|
||||||
DELETE_TX.lock().replace(delete_tx);
|
DELETE_TX.lock().replace(delete_tx);
|
||||||
|
BULK_RENAME_TX.lock().replace(bulk_rename_tx);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let move_rx =
|
let move_rx =
|
||||||
|
|
@ -54,16 +65,23 @@ impl Pump {
|
||||||
UnboundedReceiverStream::new(trash_rx).chunks_timeout(1000, Duration::from_millis(500));
|
UnboundedReceiverStream::new(trash_rx).chunks_timeout(1000, Duration::from_millis(500));
|
||||||
let delete_rx =
|
let delete_rx =
|
||||||
UnboundedReceiverStream::new(delete_rx).chunks_timeout(1000, Duration::from_millis(500));
|
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!(move_rx);
|
||||||
pin!(trash_rx);
|
pin!(trash_rx);
|
||||||
pin!(delete_rx);
|
pin!(delete_rx);
|
||||||
|
pin!(bulk_rename_rx);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
Some(items) = move_rx.next() => Pubsub::pub_from_move(items),
|
Some(items) = move_rx.next() => Pubsub::pub_from_move(items),
|
||||||
Some(urls) = trash_rx.next() => Pubsub::pub_from_trash(urls),
|
Some(urls) = trash_rx.next() => Pubsub::pub_from_trash(urls),
|
||||||
Some(urls) = delete_rx.next() => Pubsub::pub_from_delete(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 => {
|
else => {
|
||||||
CT.cancel();
|
CT.cancel();
|
||||||
break;
|
break;
|
||||||
|
|
@ -77,6 +95,7 @@ impl Pump {
|
||||||
drop(MOVE_TX.lock().take());
|
drop(MOVE_TX.lock().take());
|
||||||
drop(TRASH_TX.lock().take());
|
drop(TRASH_TX.lock().take());
|
||||||
drop(DELETE_TX.lock().take());
|
drop(DELETE_TX.lock().take());
|
||||||
|
drop(BULK_RENAME_TX.lock().take());
|
||||||
CT.cancelled().await;
|
CT.cancelled().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,12 +79,11 @@ impl App {
|
||||||
|
|
||||||
fn routine(push: bool, cursor: Option<(Position, SetCursorStyle)>) {
|
fn routine(push: bool, cursor: Option<(Position, SetCursorStyle)>) {
|
||||||
static COUNT: AtomicU8 = AtomicU8::new(0);
|
static COUNT: AtomicU8 = AtomicU8::new(0);
|
||||||
if push && COUNT.fetch_add(1, Ordering::Relaxed) != 0 {
|
if (push && COUNT.fetch_add(1, Ordering::Relaxed) != 0)
|
||||||
return;
|
|| (!push && COUNT.fetch_sub(1, Ordering::Relaxed) != 1)
|
||||||
} else if !push && COUNT.fetch_sub(1, Ordering::Relaxed) != 1 {
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = if push {
|
_ = if push {
|
||||||
queue!(TTY.writer(), BeginSynchronizedUpdate)
|
queue!(TTY.writer(), BeginSynchronizedUpdate)
|
||||||
} else if let Some((Position { x, y }, shape)) = cursor {
|
} else if let Some((Position { x, y }, shape)) = cursor {
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(MGR, remove, &self.app.cx.tasks);
|
on!(MGR, remove, &self.app.cx.tasks);
|
||||||
on!(MGR, remove_do, &self.app.cx.tasks);
|
on!(MGR, remove_do, &self.app.cx.tasks);
|
||||||
on!(MGR, create);
|
on!(MGR, create);
|
||||||
on!(MGR, rename);
|
on!(MGR, rename, &self.app.cx.tasks);
|
||||||
on!(ACTIVE, copy);
|
on!(ACTIVE, copy);
|
||||||
on!(ACTIVE, shell);
|
on!(ACTIVE, shell);
|
||||||
on!(ACTIVE, hidden);
|
on!(ACTIVE, hidden);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use anyhow::{Result, anyhow};
|
||||||
use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use yazi_config::YAZI;
|
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 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};
|
use crate::{LOW, NORMAL, TaskOp, TaskProg};
|
||||||
|
|
||||||
pub struct File {
|
pub struct File {
|
||||||
|
|
@ -146,6 +146,26 @@ impl File {
|
||||||
.await??;
|
.await??;
|
||||||
self.prog.send(TaskProg::Adv(task.id, 1, task.length))?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -325,6 +345,14 @@ impl File {
|
||||||
self.succ(id)
|
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]
|
#[inline]
|
||||||
async fn cha(path: &Path, follow: bool) -> io::Result<Cha> {
|
async fn cha(path: &Path, follow: bool) -> io::Result<Cha> {
|
||||||
let meta = fs::symlink_metadata(path).await?;
|
let meta = fs::symlink_metadata(path).await?;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ pub enum FileOp {
|
||||||
Hardlink(FileOpHardlink),
|
Hardlink(FileOpHardlink),
|
||||||
Delete(FileOpDelete),
|
Delete(FileOpDelete),
|
||||||
Trash(FileOpTrash),
|
Trash(FileOpTrash),
|
||||||
|
Rename(FileOpRename),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileOp {
|
impl FileOp {
|
||||||
|
|
@ -18,6 +19,7 @@ impl FileOp {
|
||||||
Self::Hardlink(op) => op.id,
|
Self::Hardlink(op) => op.id,
|
||||||
Self::Delete(op) => op.id,
|
Self::Delete(op) => op.id,
|
||||||
Self::Trash(op) => op.id,
|
Self::Trash(op) => op.id,
|
||||||
|
Self::Rename(op) => op.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,3 +107,10 @@ pub struct FileOpTrash {
|
||||||
pub target: Url,
|
pub target: Url,
|
||||||
pub length: u64,
|
pub length: u64,
|
||||||
}
|
}
|
||||||
|
// --- Rename
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FileOpRename {
|
||||||
|
pub id: usize,
|
||||||
|
pub from: Url,
|
||||||
|
pub to: Url,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 anyhow::Result;
|
||||||
use futures::{FutureExt, future::BoxFuture};
|
use futures::{FutureExt, future::BoxFuture};
|
||||||
|
|
@ -11,7 +11,7 @@ use yazi_proxy::{MgrProxy, options::{PluginOpt, ProcessExecOpt}};
|
||||||
use yazi_shared::{Throttle, url::Url};
|
use yazi_shared::{Throttle, url::Url};
|
||||||
|
|
||||||
use super::{Ongoing, TaskProg, TaskStage};
|
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::{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 struct Scheduler {
|
||||||
pub file: Arc<File>,
|
pub file: Arc<File>,
|
||||||
|
|
@ -209,6 +209,37 @@ impl Scheduler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_rename_at(&self, root: &Path, old: &Path, new: &Path) {
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn plugin_micro(&self, opt: PluginOpt) {
|
pub fn plugin_micro(&self, opt: PluginOpt) {
|
||||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{}`", opt.id));
|
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{}`", opt.id));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue