mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-24 09:12:43 +03:00
refactor: improve bulk rename performance (#54)
This commit is contained in:
parent
ebb06789fc
commit
053652c740
@ -1,9 +1,9 @@
|
||||
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, Write}, mem, os::unix::prelude::OsStrExt, path::{Path, PathBuf}};
|
||||
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, os::unix::prelude::OsStrExt, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::{bail, Error, Result};
|
||||
use anyhow::{anyhow, bail, Error, Result};
|
||||
use config::{open::Opener, BOOT, OPEN};
|
||||
use shared::{max_common_root, Defer, Term, MIME_DIR};
|
||||
use tokio::{fs::{self, OpenOptions}, io::{AsyncReadExt, AsyncWriteExt}};
|
||||
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||
|
||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
||||
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks, Event, BLOCKER};
|
||||
@ -226,7 +226,7 @@ impl Manager {
|
||||
let mut old: Vec<_> = self.selected().iter().map(|&f| f.path()).collect();
|
||||
|
||||
let root = max_common_root(&old);
|
||||
old = old.into_iter().map(|p| p.strip_prefix(&root).unwrap().to_owned()).collect();
|
||||
old.iter_mut().for_each(|p| *p = p.strip_prefix(&root).unwrap().to_owned());
|
||||
|
||||
let tmp = BOOT.tmpfile("bulk");
|
||||
tokio::spawn(async move {
|
||||
@ -265,52 +265,56 @@ impl Manager {
|
||||
Term::clear()?;
|
||||
if old.len() != new.len() {
|
||||
println!("Number of old and new differ, press ENTER to exit");
|
||||
tokio::io::stdin().read_exact(&mut [0]).await?;
|
||||
stdin().read_exact(&mut [0]).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut todo = Vec::with_capacity(old.len());
|
||||
for (o, n) in old.into_iter().zip(new) {
|
||||
if n != o {
|
||||
stdout().write_all(o.as_os_str().as_bytes())?;
|
||||
stdout().write_all(b" -> ")?;
|
||||
stdout().write_all(n.as_os_str().as_bytes())?;
|
||||
stdout().write_all(b"\n")?;
|
||||
todo.push((root.join(o), root.join(n)));
|
||||
}
|
||||
}
|
||||
let todo: Vec<_> = old.into_iter().zip(new).filter(|(o, n)| o != n).collect();
|
||||
if todo.is_empty() {
|
||||
return Ok(());
|
||||
} else {
|
||||
print!("Continue to rename? (y/N): ");
|
||||
stdout().flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0];
|
||||
tokio::io::stdin().read_exact(&mut buf).await?;
|
||||
{
|
||||
let mut stdout = BufWriter::new(stdout().lock());
|
||||
for (o, n) in &todo {
|
||||
writeln!(stdout, "{} -> {}", o.display(), n.display())?;
|
||||
}
|
||||
write!(stdout, "Continue to rename? (y/N): ")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
stdin().read(&mut buf).await.ok();
|
||||
if buf[0] != b'y' && buf[0] != b'Y' {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut failed = Vec::new();
|
||||
for (o, n) in todo {
|
||||
if let Err(e) = fs::rename(&o, &n).await {
|
||||
failed.push((o, n, e));
|
||||
if fs::metadata(&n).await.is_ok() {
|
||||
failed.push((o, n, anyhow!("Destination already exists")));
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = fs::rename(root.join(&o), root.join(&n)).await {
|
||||
failed.push((o, n, e.into()));
|
||||
}
|
||||
}
|
||||
if failed.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
Term::clear()?;
|
||||
println!("Failed to rename:");
|
||||
Term::clear()?;
|
||||
{
|
||||
let mut stdout = BufWriter::new(stdout().lock());
|
||||
writeln!(stdout, "Failed to rename:")?;
|
||||
for (o, n, e) in failed {
|
||||
stdout().write_all(o.as_os_str().as_bytes())?;
|
||||
stdout().write_all(b" -> ")?;
|
||||
stdout().write_all(n.as_os_str().as_bytes())?;
|
||||
stdout().write_fmt(format_args!(": {e}\n"))?;
|
||||
writeln!(stdout, "{} -> {}: {e}", o.display(), n.display())?;
|
||||
}
|
||||
println!("\nPress ENTER to exit");
|
||||
tokio::io::stdin().read_exact(&mut [0]).await?;
|
||||
writeln!(stdout, "\nPress ENTER to exit")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
|
||||
stdin().read_exact(&mut [0]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ pub fn max_common_root(files: &[PathBuf]) -> PathBuf {
|
||||
return PathBuf::new();
|
||||
}
|
||||
|
||||
let mut it = files.iter().map(|p| p.components());
|
||||
let mut it = files.iter().map(|p| p.parent().unwrap_or(Path::new("")).components());
|
||||
let mut root = it.next().unwrap().collect::<PathBuf>();
|
||||
for components in it {
|
||||
let mut new_root = PathBuf::new();
|
||||
@ -177,12 +177,14 @@ pub fn max_common_root(files: &[PathBuf]) -> PathBuf {
|
||||
fn test_max_common_root() {
|
||||
assert_eq!(max_common_root(&[]).as_os_str(), "");
|
||||
assert_eq!(max_common_root(&["".into()]).as_os_str(), "");
|
||||
assert_eq!(max_common_root(&["/a/b".into()]).as_os_str(), "/a/b");
|
||||
assert_eq!(max_common_root(&["a".into()]).as_os_str(), "");
|
||||
assert_eq!(max_common_root(&["/a".into()]).as_os_str(), "/");
|
||||
assert_eq!(max_common_root(&["/a/b".into()]).as_os_str(), "/a");
|
||||
assert_eq!(max_common_root(&["/a/b/c".into(), "/a/b/d".into()]).as_os_str(), "/a/b");
|
||||
assert_eq!(max_common_root(&["/aa/bb/cc".into(), "/aa/dd/ee".into()]).as_os_str(), "/aa");
|
||||
assert_eq!(
|
||||
max_common_root(&["/aa/bb/cc".into(), "/aa/bb/cc/dd/ee".into(), "/aa/bb/cc/ff".into()])
|
||||
.as_os_str(),
|
||||
"/aa/bb/cc"
|
||||
"/aa/bb"
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user