From 6aa124c77be219f6f00de953284e9d4d5436d036 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 03:24:15 +1000 Subject: [PATCH 01/17] Fix: rename sensitivity --- yazi-core/src/manager/commands/bulk_rename.rs | 4 ++-- yazi-core/src/manager/commands/rename.rs | 15 +++++++++------ yazi-shared/src/fs/fns.rs | 8 ++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 6a22fe3dd..2e8085380 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{are_paths_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,7 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await { + if maybe_exists(&new).await && !are_paths_equal(&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())); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 6b13878d6..c5b303c8f 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{are_paths_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await { + if opt.force || !maybe_exists(&new).await || are_paths_equal(&hovered, &new).await { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } @@ -81,14 +81,17 @@ impl Manager { let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); + let are_different = !are_paths_equal(&old, &new).await; let overwritten = symlink_realpath(&new).await; fs::rename(&old, &new).await?; - if let Ok(p) = overwritten { - ok_or_not_found(fs::rename(&p, &new).await)?; - FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); + if are_different { + if let Ok(p) = overwritten { + ok_or_not_found(fs::rename(&p, &new).await)?; + FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); + } + Pubsub::pub_from_rename(tab, &old, &new); } - Pubsub::pub_from_rename(tab, &old, &new); let file = File::from(new.clone()).await?; FilesOp::Deleting(p_old, vec![old]).emit(); diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index b8c6500d5..b8bd205f1 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -23,6 +23,14 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } +#[inline] +pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { + match (fs::canonicalize(old).await, fs::canonicalize(new).await) { + (Ok(old), Ok(new)) => old == new, + _ => false, + } +} + pub async fn symlink_realpath(path: &Path) -> Result { let p = fs::canonicalize(path).await?; if p == path { From b6fc6f4a0e3a8a83a2499ab8c6a86920be0e5f4d Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 05:39:04 +1000 Subject: [PATCH 02/17] fix issues when rename symlink itself --- Cargo.lock | 7 ++++--- cspell.json | 2 +- yazi-shared/Cargo.toml | 1 + yazi-shared/src/fs/fns.rs | 23 ++++++++++++++++++++--- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d0debff6..ed81e616b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,11 +2492,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2964,6 +2964,7 @@ dependencies = [ "serde", "shell-words", "tokio", + "winapi-util", ] [[package]] diff --git a/cspell.json b/cspell.json index aed43d6cd..4a57706cc 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname"],"language":"en"} \ No newline at end of file +{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","winapi"],"language":"en"} \ No newline at end of file diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 414bba0de..369275d0c 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,6 +22,7 @@ regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } +winapi-util = "0.1.8" [target."cfg(unix)".dependencies] libc = "0.2.155" diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index b8bd205f1..c5d757bfb 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, use anyhow::{bail, Result}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; +use winapi_util::{file::information, Handle}; #[inline] pub async fn must_exists(p: impl AsRef) -> bool { fs::symlink_metadata(p).await.is_ok() } @@ -25,9 +26,25 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { #[inline] pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { - match (fs::canonicalize(old).await, fs::canonicalize(new).await) { - (Ok(old), Ok(new)) => old == new, - _ => false, + #[cfg(unix)] + { + match (fs::metadata(old).await, fs::metadata(new).await) { + (Ok(old), Ok(new)) => old.dev() == new.dev() && old.ino() == new.ino(), + _ => false, + } + } + #[cfg(windows)] + { + match (Handle::from_path_any(old), Handle::from_path_any(new)) { + (Ok(old), Ok(new)) => match (information(old), information(new)) { + (Ok(old), Ok(new)) => { + old.volume_serial_number() == new.volume_serial_number() + && old.file_index() == new.file_index() + } + _ => false, + }, + _ => false, + } } } From ff8dbf33229544cb4a8e4091f376a7019b872121 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 06:01:22 +1000 Subject: [PATCH 03/17] FIx unix branch in `are_paths_equal` --- yazi-shared/src/fs/fns.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index c5d757bfb..89f3f78fe 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -1,8 +1,7 @@ -use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, path::{Path, PathBuf}}; +use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, os::unix::fs::MetadataExt, path::{Path, PathBuf}}; use anyhow::{bail, Result}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; -use winapi_util::{file::information, Handle}; #[inline] pub async fn must_exists(p: impl AsRef) -> bool { fs::symlink_metadata(p).await.is_ok() } @@ -35,6 +34,7 @@ pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bo } #[cfg(windows)] { + use winapi_util::{file::information, Handle}; match (Handle::from_path_any(old), Handle::from_path_any(new)) { (Ok(old), Ok(new)) => match (information(old), information(new)) { (Ok(old), Ok(new)) => { From bb0b06231cfac44229eb070e4f950efa63680fa3 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 06:17:54 +1000 Subject: [PATCH 04/17] Fix typo --- yazi-shared/src/fs/fns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 89f3f78fe..39c97ca70 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -27,7 +27,7 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { #[cfg(unix)] { - match (fs::metadata(old).await, fs::metadata(new).await) { + match (fs::symlink_metadata(old).await, fs::symlink_metadata(new).await) { (Ok(old), Ok(new)) => old.dev() == new.dev() && old.ino() == new.ino(), _ => false, } From 79ea19da2d4b584f085bacf6a421e45f55016e07 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 12:54:25 +1000 Subject: [PATCH 05/17] fix: import error on Windows --- yazi-shared/src/fs/fns.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 39c97ca70..d5fe0de8b 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, os::unix::fs::MetadataExt, path::{Path, PathBuf}}; +use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, path::{Path, PathBuf}}; use anyhow::{bail, Result}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; @@ -27,6 +27,7 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { #[cfg(unix)] { + use std::os::unix::fs::MetadataExt; match (fs::symlink_metadata(old).await, fs::symlink_metadata(new).await) { (Ok(old), Ok(new)) => old.dev() == new.dev() && old.ino() == new.ino(), _ => false, From e86eeb5c16aaa2d47c0e6ed2904b23c2f904672b Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 16:29:18 +1000 Subject: [PATCH 06/17] Refactor path equality check function --- yazi-shared/src/fs/fns.rs | 42 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index d5fe0de8b..16bb6bf30 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -25,27 +25,29 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { #[inline] pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { - #[cfg(unix)] - { - use std::os::unix::fs::MetadataExt; - match (fs::symlink_metadata(old).await, fs::symlink_metadata(new).await) { - (Ok(old), Ok(new)) => old.dev() == new.dev() && old.ino() == new.ino(), - _ => false, - } + if let (Some(old), Some(new)) = ( + canonicalize_without_resolving_itself(old).await, + canonicalize_without_resolving_itself(new).await, + ) { + old == new + } else { + false } - #[cfg(windows)] - { - use winapi_util::{file::information, Handle}; - match (Handle::from_path_any(old), Handle::from_path_any(new)) { - (Ok(old), Ok(new)) => match (information(old), information(new)) { - (Ok(old), Ok(new)) => { - old.volume_serial_number() == new.volume_serial_number() - && old.file_index() == new.file_index() - } - _ => false, - }, - _ => false, - } +} + +async fn canonicalize_without_resolving_itself(path: impl AsRef) -> Option { + let meta = fs::symlink_metadata(&path).await.ok()?; + if meta.is_symlink() { + let (parent, link) = (path.as_ref().parent()?, path.as_ref().file_name()?); + let parent = fs::canonicalize(parent).await.ok()?; + let new_link = if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + Cow::Owned(link.to_ascii_lowercase()) + } else { + Cow::Borrowed(link) + }; + Some(parent.join(new_link)) + } else { + fs::canonicalize(path).await.ok() } } From 536a928ff0048c1d115c5d65c6c1f1e69b4e7ef3 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 16:32:36 +1000 Subject: [PATCH 07/17] make new function inline --- yazi-shared/src/fs/fns.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 16bb6bf30..45bf98852 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -35,6 +35,7 @@ pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bo } } +#[inline] async fn canonicalize_without_resolving_itself(path: impl AsRef) -> Option { let meta = fs::symlink_metadata(&path).await.ok()?; if meta.is_symlink() { From a9f15d45d77a8dd4426dda6279ba8119561b0f2b Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 16:35:34 +1000 Subject: [PATCH 08/17] Remove winapi-util dependency and update cspell.json --- Cargo.lock | 1 - cspell.json | 2 +- yazi-shared/Cargo.toml | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed81e616b..ae4e843ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2964,7 +2964,6 @@ dependencies = [ "serde", "shell-words", "tokio", - "winapi-util", ] [[package]] diff --git a/cspell.json b/cspell.json index 4a57706cc..aed43d6cd 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","winapi"],"language":"en"} \ No newline at end of file +{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname"],"language":"en"} \ No newline at end of file diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 369275d0c..414bba0de 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,7 +22,6 @@ regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } -winapi-util = "0.1.8" [target."cfg(unix)".dependencies] libc = "0.2.155" From 810f1d04836f5221e9c0ab96dac3b90eb0b7fb2f Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 17:14:05 +1000 Subject: [PATCH 09/17] Only comparing filename is enough --- yazi-core/src/manager/commands/bulk_rename.rs | 4 +-- yazi-core/src/manager/commands/rename.rs | 6 ++-- yazi-shared/src/fs/fns.rs | 29 +++++-------------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 2e8085380..fedfef8c1 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{are_paths_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{are_names_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,7 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await && !are_paths_equal(&old, &new).await { + if maybe_exists(&new).await && !are_names_equal(&old, &new) { failed.push((o, n, anyhow!("Destination already exists"))); } else if let Err(e) = fs::rename(&old, &new).await { failed.push((o, n, e.into())); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index c5b303c8f..398133036 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{are_paths_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{are_names_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await || are_paths_equal(&hovered, &new).await { + if opt.force || !maybe_exists(&new).await || are_names_equal(&hovered, &new) { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } @@ -81,7 +81,7 @@ impl Manager { let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); - let are_different = !are_paths_equal(&old, &new).await; + let are_different = !are_names_equal(&old, &new); let overwritten = symlink_realpath(&new).await; fs::rename(&old, &new).await?; diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 45bf98852..b8edcfb68 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -24,31 +24,16 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } #[inline] -pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { - if let (Some(old), Some(new)) = ( - canonicalize_without_resolving_itself(old).await, - canonicalize_without_resolving_itself(new).await, - ) { - old == new - } else { - false - } -} - -#[inline] -async fn canonicalize_without_resolving_itself(path: impl AsRef) -> Option { - let meta = fs::symlink_metadata(&path).await.ok()?; - if meta.is_symlink() { - let (parent, link) = (path.as_ref().parent()?, path.as_ref().file_name()?); - let parent = fs::canonicalize(parent).await.ok()?; - let new_link = if cfg!(target_os = "macos") || cfg!(target_os = "windows") { - Cow::Owned(link.to_ascii_lowercase()) +pub fn are_names_equal(old: impl AsRef, new: impl AsRef) -> bool { + if let (Some(old), Some(new)) = (old.as_ref().file_name(), new.as_ref().file_name()) { + let (old, new) = if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + (Cow::Owned(old.to_ascii_lowercase()), Cow::Owned(new.to_ascii_lowercase())) } else { - Cow::Borrowed(link) + (Cow::Borrowed(old), Cow::Borrowed(new)) }; - Some(parent.join(new_link)) + old == new } else { - fs::canonicalize(path).await.ok() + false } } From 454530a67a2384322633a781c1d7a5e605f8d752 Mon Sep 17 00:00:00 2001 From: Shupeng Xue Date: Sun, 23 Jun 2024 19:59:53 +1000 Subject: [PATCH 10/17] Refactor path equality check function to specific between unix and windows --- yazi-core/src/manager/commands/bulk_rename.rs | 4 ++-- yazi-core/src/manager/commands/rename.rs | 6 ++--- yazi-shared/src/fs/fns.rs | 23 +++++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index fedfef8c1..df1bb47a8 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{are_names_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{are_pathss_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,7 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await && !are_names_equal(&old, &new) { + if maybe_exists(&new).await && !are_pathss_equal(&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())); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 398133036..d36a84f99 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{are_names_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{are_pathss_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await || are_names_equal(&hovered, &new) { + if opt.force || !maybe_exists(&new).await || are_pathss_equal(&hovered, &new).await { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } @@ -81,7 +81,7 @@ impl Manager { let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); - let are_different = !are_names_equal(&old, &new); + let are_different = !are_pathss_equal(&old, &new).await; let overwritten = symlink_realpath(&new).await; fs::rename(&old, &new).await?; diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index b8edcfb68..20f1c9a4e 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -24,17 +24,26 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } #[inline] -pub fn are_names_equal(old: impl AsRef, new: impl AsRef) -> bool { - if let (Some(old), Some(new)) = (old.as_ref().file_name(), new.as_ref().file_name()) { - let (old, new) = if cfg!(target_os = "macos") || cfg!(target_os = "windows") { - (Cow::Owned(old.to_ascii_lowercase()), Cow::Owned(new.to_ascii_lowercase())) +pub async fn are_pathss_equal(old: impl AsRef, new: impl AsRef) -> bool { + #[cfg(unix)] + if let (Ok(canonical_old), Ok(canonical_new)) = + (fs::canonicalize(&old).await, fs::canonicalize(&new).await) + { + if let (Ok(old), Ok(new)) = + (fs::metadata(&canonical_old).await, fs::metadata(&canonical_new).await) + { + use std::os::unix::fs::MetadataExt; + old.ino() == new.ino() && old.dev() == new.dev() } else { - (Cow::Borrowed(old), Cow::Borrowed(new)) - }; - old == new + false + } } else { false } + #[cfg(windows)] + { + // TODO: use MoveFileEx without MOVEFILE_REPLACE_EXISTING + } } pub async fn symlink_realpath(path: &Path) -> Result { From c1ab824371067d56662c3e0f34c15aa4ef38d975 Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 21:28:48 +1000 Subject: [PATCH 11/17] Add dependencies and fix file renaming issues --- Cargo.lock | 8 +++++ yazi-core/src/manager/commands/bulk_rename.rs | 32 +++++++++++++++---- yazi-core/src/manager/commands/rename.rs | 32 +++++++++++++------ yazi-shared/Cargo.toml | 2 ++ yazi-shared/src/fs/fns.rs | 24 ++++++++++---- 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae4e843ec..c05245f1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2474,6 +2474,12 @@ dependencies = [ "winsafe", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -2964,6 +2970,8 @@ dependencies = [ "serde", "shell-words", "tokio", + "widestring", + "windows-sys 0.52.0", ] [[package]] diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index df1bb47a8..7f98c7ad0 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{are_pathss_equal, max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,14 +84,32 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await && !are_pathss_equal(&old, &new).await { + let overwrite_safe; + #[cfg(windows)] + { + overwrite_safe = yazi_shared::fs::rename_without_overwriting(&old, &new).await.is_ok(); + } + #[cfg(unix)] + { + overwrite_safe = yazi_shared::fs::are_paths_equal(&old, &new).await; + } + if maybe_exists(&new).await && !overwrite_safe { 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"))); + #[cfg(unix)] + 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"))); + } + #[cfg(windows)] + 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"))); + } } } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index d36a84f99..0241af90a 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{are_pathss_equal, maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,36 +62,48 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await || are_pathss_equal(&hovered, &new).await { - Self::rename_do(tab, hovered, Url::from(new)).await.ok(); + let overwrite_safe; + #[cfg(windows)] + { + overwrite_safe = yazi_shared::fs::rename_without_overwriting(&hovered, &new).await.is_ok(); + } + #[cfg(unix)] + { + overwrite_safe = yazi_shared::fs::are_paths_equal(&hovered, &new).await; + } + if opt.force || !maybe_exists(&new).await || overwrite_safe { + Self::rename_do(tab, hovered, Url::from(new), overwrite_safe).await.ok(); return; } let mut result = InputProxy::show(InputCfg::overwrite()); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { - Self::rename_do(tab, hovered, Url::from(new)).await.ok(); + Self::rename_do(tab, hovered, Url::from(new), false).await.ok(); } }; }); } - async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> { + async fn rename_do(tab: usize, old: Url, new: Url, overwrite_safe: bool) -> Result<()> { let Some(p_old) = old.parent_url() else { return Ok(()) }; let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); - let are_different = !are_pathss_equal(&old, &new).await; - let overwritten = symlink_realpath(&new).await; - fs::rename(&old, &new).await?; + if overwrite_safe { + if !cfg!(windows) { + fs::rename(&old, &new).await?; + } + } else { + let overwritten = symlink_realpath(&new).await; + fs::rename(&old, &new).await?; - if are_different { if let Ok(p) = overwritten { ok_or_not_found(fs::rename(&p, &new).await)?; FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); } - Pubsub::pub_from_rename(tab, &old, &new); } + Pubsub::pub_from_rename(tab, &old, &new); let file = File::from(new.clone()).await?; FilesOp::Deleting(p_old, vec![old]).emit(); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 414bba0de..ba31f15cb 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,6 +22,8 @@ regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } +widestring = "1.1.0" +windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] } [target."cfg(unix)".dependencies] libc = "0.2.155" diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 20f1c9a4e..99c8dcc33 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -24,8 +24,8 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } #[inline] -pub async fn are_pathss_equal(old: impl AsRef, new: impl AsRef) -> bool { - #[cfg(unix)] +#[cfg(unix)] +pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { if let (Ok(canonical_old), Ok(canonical_new)) = (fs::canonicalize(&old).await, fs::canonicalize(&new).await) { @@ -40,10 +40,22 @@ pub async fn are_pathss_equal(old: impl AsRef, new: impl AsRef) -> b } else { false } - #[cfg(windows)] - { - // TODO: use MoveFileEx without MOVEFILE_REPLACE_EXISTING - } +} + +#[inline] +#[cfg(windows)] +pub async fn rename_without_overwriting( + old: impl AsRef, + new: impl AsRef, +) -> io::Result<()> { + use widestring::U16CString; + use windows_sys::Win32::Storage::FileSystem::MoveFileExW; + let old = U16CString::from_os_str(old.as_ref().as_os_str()) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; + let new = U16CString::from_os_str(new.as_ref().as_os_str()) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; + let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) }; + if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } pub async fn symlink_realpath(path: &Path) -> Result { From f72a6fae6e7f3ed1962a3c1f125990aca4c967a5 Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 21:31:05 +1000 Subject: [PATCH 12/17] Update cspell.json with additional words --- cspell.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cspell.json b/cspell.json index aed43d6cd..a3d8a050a 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname"],"language":"en"} \ No newline at end of file +{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","widestring"],"language":"en"} \ No newline at end of file From 485e4d6974b9bf6de162f5867f817845678fb353 Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 21:45:14 +1000 Subject: [PATCH 13/17] Refactor rename_without_overwriting to use tokio::task::spawn_blocking --- yazi-shared/src/fs/fns.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 99c8dcc33..7d17466e2 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -54,8 +54,11 @@ pub async fn rename_without_overwriting( .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; let new = U16CString::from_os_str(new.as_ref().as_os_str()) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; - let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) }; - if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } + tokio::task::spawn_blocking(move || { + let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) }; + if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } + }) + .await? } pub async fn symlink_realpath(path: &Path) -> Result { From 2c9ab2d930c2740cb5304f256caa7c71b8f674f4 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 23 Jun 2024 22:34:48 +0800 Subject: [PATCH 14/17] Try another way --- Cargo.lock | 7 -- cspell.json | 2 +- yazi-core/src/manager/commands/bulk_rename.rs | 11 +-- yazi-core/src/manager/commands/rename.rs | 33 +++------ yazi-shared/Cargo.toml | 5 +- yazi-shared/src/fs/fns.rs | 70 ++++++++++--------- 6 files changed, 52 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05245f1c..5706c9703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2474,12 +2474,6 @@ dependencies = [ "winsafe", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -2970,7 +2964,6 @@ dependencies = [ "serde", "shell-words", "tokio", - "widestring", "windows-sys 0.52.0", ] diff --git a/cspell.json b/cspell.json index a3d8a050a..4eb16cfb7 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","widestring"],"language":"en"} \ No newline at end of file +{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 7f98c7ad0..e79c0e26b 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -84,16 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - let overwrite_safe; - #[cfg(windows)] - { - overwrite_safe = yazi_shared::fs::rename_without_overwriting(&old, &new).await.is_ok(); - } - #[cfg(unix)] - { - overwrite_safe = yazi_shared::fs::are_paths_equal(&old, &new).await; - } - if maybe_exists(&new).await && !overwrite_safe { + if maybe_exists(&new).await { failed.push((o, n, anyhow!("Destination already exists"))); } else { #[cfg(unix)] diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 0241af90a..6b13878d6 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -62,46 +62,31 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - let overwrite_safe; - #[cfg(windows)] - { - overwrite_safe = yazi_shared::fs::rename_without_overwriting(&hovered, &new).await.is_ok(); - } - #[cfg(unix)] - { - overwrite_safe = yazi_shared::fs::are_paths_equal(&hovered, &new).await; - } - if opt.force || !maybe_exists(&new).await || overwrite_safe { - Self::rename_do(tab, hovered, Url::from(new), overwrite_safe).await.ok(); + if opt.force || !maybe_exists(&new).await { + Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } let mut result = InputProxy::show(InputCfg::overwrite()); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { - Self::rename_do(tab, hovered, Url::from(new), false).await.ok(); + Self::rename_do(tab, hovered, Url::from(new)).await.ok(); } }; }); } - async fn rename_do(tab: usize, old: Url, new: Url, overwrite_safe: bool) -> Result<()> { + async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> { let Some(p_old) = old.parent_url() else { return Ok(()) }; let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); - if overwrite_safe { - if !cfg!(windows) { - fs::rename(&old, &new).await?; - } - } else { - let overwritten = symlink_realpath(&new).await; - fs::rename(&old, &new).await?; + let overwritten = symlink_realpath(&new).await; + fs::rename(&old, &new).await?; - if let Ok(p) = overwritten { - ok_or_not_found(fs::rename(&p, &new).await)?; - FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); - } + if let Ok(p) = overwritten { + ok_or_not_found(fs::rename(&p, &new).await)?; + FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); } Pubsub::pub_from_rename(tab, &old, &new); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index ba31f15cb..bb9afa549 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,8 +22,9 @@ regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } -widestring = "1.1.0" -windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] } [target."cfg(unix)".dependencies] libc = "0.2.155" + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.52.0", features = [ "Win32_Storage_FileSystem" ] } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 7d17466e2..342b791f2 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -23,42 +23,48 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } -#[inline] #[cfg(unix)] -pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { - if let (Ok(canonical_old), Ok(canonical_new)) = - (fs::canonicalize(&old).await, fs::canonicalize(&new).await) - { - if let (Ok(old), Ok(new)) = - (fs::metadata(&canonical_old).await, fs::metadata(&canonical_new).await) - { - use std::os::unix::fs::MetadataExt; - old.ino() == new.ino() && old.dev() == new.dev() - } else { - false - } - } else { - false - } +pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result { + use std::os::unix::fs::MetadataExt; + + let (a, b) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); + Ok(a.ino() == b.ino() && a.dev() == b.dev()) } -#[inline] #[cfg(windows)] -pub async fn rename_without_overwriting( - old: impl AsRef, - new: impl AsRef, -) -> io::Result<()> { - use widestring::U16CString; - use windows_sys::Win32::Storage::FileSystem::MoveFileExW; - let old = U16CString::from_os_str(old.as_ref().as_os_str()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; - let new = U16CString::from_os_str(new.as_ref().as_os_str()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; - tokio::task::spawn_blocking(move || { - let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) }; - if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } - }) - .await? +pub async fn paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { + use std::{ffi::OsString, os::windows::{ffi::OsStringExt, io::AsRawHandle}, path::{Path, PathBuf}}; + + use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}}; + + async fn final_name(p: &Path) -> std::io::Result { + let file = tokio::fs::OpenOptions::new() + .access_mode(0) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT) + .open(p) + .await?; + + let mut buf = [0u16; MAX_PATH as usize]; + tokio::task::spawn_blocking(move || { + let len = unsafe { + GetFinalPathNameByHandleW( + file.as_raw_handle() as HANDLE, + buf.as_mut_ptr(), + buf.len() as u32, + VOLUME_NAME_DOS, + ) + }; + + if len == 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(PathBuf::from(OsString::from_wide(&buf[0..len as usize]))) + } + }) + .await? + } + + Ok(final_name(a).await? == final_name(b).await?) } pub async fn symlink_realpath(path: &Path) -> Result { From e263a0d477b785868423a5aaf5e60a1908459ab9 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 24 Jun 2024 00:24:47 +0800 Subject: [PATCH 15/17] Handle hard links --- Cargo.lock | 262 ++++++++++++++-------------- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 2 +- yazi-scheduler/src/process/shell.rs | 4 +- yazi-shared/src/fs/fns.rs | 10 +- 5 files changed, 143 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5706c9703..351ca5a31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -72,47 +72,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -147,9 +148,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -245,9 +246,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" [[package]] name = "byteorder" @@ -278,9 +279,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" [[package]] name = "cfg-if" @@ -319,14 +320,14 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_complete" -version = "4.5.5" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" +checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58" dependencies = [ "clap", ] @@ -357,17 +358,17 @@ version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clipboard-win" @@ -386,9 +387,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "compact_str" @@ -405,9 +406,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -432,18 +433,18 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -469,9 +470,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -517,9 +518,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -527,27 +528,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.66", + "strsim", + "syn 2.0.67", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -592,9 +593,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encode_unicode" @@ -610,18 +611,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -693,9 +695,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -790,7 +792,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -835,9 +837,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -856,9 +858,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "globset" @@ -893,12 +895,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1013,6 +1009,12 @@ dependencies = [ "libc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -1077,9 +1079,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lebe" @@ -1111,9 +1113,9 @@ checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -1151,9 +1153,9 @@ dependencies = [ [[package]] name = "luajit-src" -version = "210.5.7+d06beb0" +version = "210.5.8+5790d25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d251fdacdabbf87704cf48ac1f8b1eb23d6e10855c3ee08e5beb25b4be2e9e4" +checksum = "441f18d9ad792e871fc2f7f2cb8902c386f6f56fdbddef3b835b61475e375346" dependencies = [ "cc", "which", @@ -1171,9 +1173,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" @@ -1183,9 +1185,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -1246,7 +1248,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1301,9 +1303,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1363,9 +1365,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1443,16 +1445,16 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" @@ -1537,9 +1539,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1622,9 +1624,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -1654,9 +1656,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1665,15 +1667,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1696,15 +1698,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1748,7 +1750,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1881,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1890,12 +1892,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -1913,15 +1909,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1936,9 +1932,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", @@ -1968,22 +1964,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2102,7 +2098,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2195,7 +2191,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2250,6 +2246,12 @@ dependencies = [ "windows", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -2301,9 +2303,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2318,9 +2320,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uzers" @@ -2359,7 +2361,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2423,7 +2425,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-shared", ] @@ -2445,7 +2447,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2544,7 +2546,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2555,14 +2557,14 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] @@ -2708,9 +2710,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -2984,7 +2986,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 51d4a6941..ebb7864f3 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -20,7 +20,7 @@ serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.5" +clap_complete = "4.5.6" clap_complete_nushell = "4.5.2" clap_complete_fig = "4.5.1" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 4b706ed07..1b2d04c53 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -24,7 +24,7 @@ toml_edit = "0.22.14" [build-dependencies] anyhow = "1.0.86" clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.5" +clap_complete = "4.5.6" clap_complete_fig = "4.5.1" clap_complete_nushell = "4.5.2" serde_json = "1.0.117" diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs index 4cfbe4177..fb3ad6672 100644 --- a/yazi-scheduler/src/process/shell.rs +++ b/yazi-scheduler/src/process/shell.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, io::Error, process::Stdio}; +use std::{ffi::OsString, process::Stdio}; use anyhow::Result; use tokio::process::{Child, Command}; @@ -37,7 +37,7 @@ pub fn shell(opt: ShellOpt) -> Result { .kill_on_drop(!opt.orphan) .pre_exec(move || { if opt.orphan && libc::setpgid(0, 0) < 0 { - return Err(Error::last_os_error()); + return Err(std::io::Error::last_os_error()); } Ok(()) }) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 342b791f2..ee7a2bf84 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -27,13 +27,17 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result { use std::os::unix::fs::MetadataExt; - let (a, b) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); - Ok(a.ino() == b.ino() && a.dev() == b.dev()) + let (a_, b_) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); + Ok( + a_.ino() == b_.ino() + && a_.dev() == b_.dev() + && fs::canonicalize(a).await? == fs::canonicalize(b).await?, + ) } #[cfg(windows)] pub async fn paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { - use std::{ffi::OsString, os::windows::{ffi::OsStringExt, io::AsRawHandle}, path::{Path, PathBuf}}; + use std::os::windows::{ffi::OsStringExt, io::AsRawHandle}; use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}}; From 3ad259e1b92c725708224cd9abb4f87568e3880d Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 24 Jun 2024 00:51:21 +0800 Subject: [PATCH 16/17] .. --- yazi-core/src/manager/commands/bulk_rename.rs | 23 ++++++------------- yazi-core/src/manager/commands/rename.rs | 4 ++-- yazi-shared/src/fs/fns.rs | 9 ++++++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index e79c0e26b..e10b48df0 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{max_common_root, maybe_exists, paths_to_same_file, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,23 +84,14 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await { + 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 { - #[cfg(unix)] - 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"))); - } - #[cfg(windows)] - 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"))); - } + failed.push((o, n, anyhow!("Failed to retrieve file info"))); } } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 6b13878d6..b8cfc4dd2 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, paths_to_same_file, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await { + if opt.force || !maybe_exists(&new).await || paths_to_same_file(&hovered, &new).await { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index ee7a2bf84..ffb2ef301 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -23,8 +23,13 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } +#[inline] +pub async fn paths_to_same_file(a: impl AsRef, b: impl AsRef) -> bool { + _paths_to_same_file(a.as_ref(), b.as_ref()).await.unwrap_or(false) +} + #[cfg(unix)] -pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result { +async fn _paths_to_same_file(a: &Path, b: &Path) -> io::Result { use std::os::unix::fs::MetadataExt; let (a_, b_) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); @@ -36,7 +41,7 @@ pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result { } #[cfg(windows)] -pub async fn paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { +async fn _paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { use std::os::windows::{ffi::OsStringExt, io::AsRawHandle}; use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}}; From 7bba66704330be0d371532617657698ce8384161 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 24 Jun 2024 00:52:45 +0800 Subject: [PATCH 17/17] .. --- yazi-shared/src/fs/fns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index ffb2ef301..e201807be 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -53,8 +53,8 @@ async fn _paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { .open(p) .await?; - let mut buf = [0u16; MAX_PATH as usize]; tokio::task::spawn_blocking(move || { + let mut buf = [0u16; MAX_PATH as usize]; let len = unsafe { GetFinalPathNameByHandleW( file.as_raw_handle() as HANDLE,