mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Add dependencies and fix file renaming issues
This commit is contained in:
parent
454530a67a
commit
c1ab824371
5 changed files with 75 additions and 23 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -2474,6 +2474,12 @@ dependencies = [
|
||||||
"winsafe",
|
"winsafe",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
@ -2964,6 +2970,8 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"shell-words",
|
"shell-words",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"widestring",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||||
use yazi_config::{OPEN, PREVIEW};
|
use yazi_config::{OPEN, PREVIEW};
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER};
|
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;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -84,14 +84,32 @@ impl Manager {
|
||||||
for (o, n) in todo {
|
for (o, n) in todo {
|
||||||
let (old, new) = (root.join(&o), root.join(&n));
|
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")));
|
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 {
|
} 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")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use tokio::fs;
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_proxy::{InputProxy, TabProxy, WATCHER};
|
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;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -62,36 +62,48 @@ impl Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
let new = hovered.parent().unwrap().join(name);
|
let new = hovered.parent().unwrap().join(name);
|
||||||
if opt.force || !maybe_exists(&new).await || are_pathss_equal(&hovered, &new).await {
|
let overwrite_safe;
|
||||||
Self::rename_do(tab, hovered, Url::from(new)).await.ok();
|
#[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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = InputProxy::show(InputCfg::overwrite());
|
let mut result = InputProxy::show(InputCfg::overwrite());
|
||||||
if let Some(Ok(choice)) = result.recv().await {
|
if let Some(Ok(choice)) = result.recv().await {
|
||||||
if choice == "y" || choice == "Y" {
|
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_old) = old.parent_url() else { return Ok(()) };
|
||||||
let Some(p_new) = new.parent_url() else { return Ok(()) };
|
let Some(p_new) = new.parent_url() else { return Ok(()) };
|
||||||
let _permit = WATCHER.acquire().await.unwrap();
|
let _permit = WATCHER.acquire().await.unwrap();
|
||||||
|
|
||||||
let are_different = !are_pathss_equal(&old, &new).await;
|
if overwrite_safe {
|
||||||
let overwritten = symlink_realpath(&new).await;
|
if !cfg!(windows) {
|
||||||
fs::rename(&old, &new).await?;
|
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 {
|
if let Ok(p) = overwritten {
|
||||||
ok_or_not_found(fs::rename(&p, &new).await)?;
|
ok_or_not_found(fs::rename(&p, &new).await)?;
|
||||||
FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit();
|
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?;
|
let file = File::from(new.clone()).await?;
|
||||||
FilesOp::Deleting(p_old, vec![old]).emit();
|
FilesOp::Deleting(p_old, vec![old]).emit();
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ regex = "1.10.5"
|
||||||
serde = { version = "1.0.203", features = [ "derive" ] }
|
serde = { version = "1.0.203", features = [ "derive" ] }
|
||||||
shell-words = "1.1.0"
|
shell-words = "1.1.0"
|
||||||
tokio = { version = "1.38.0", features = [ "full" ] }
|
tokio = { version = "1.38.0", features = [ "full" ] }
|
||||||
|
widestring = "1.1.0"
|
||||||
|
windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] }
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn are_pathss_equal(old: impl AsRef<Path>, new: impl AsRef<Path>) -> bool {
|
#[cfg(unix)]
|
||||||
#[cfg(unix)]
|
pub async fn are_paths_equal(old: impl AsRef<Path>, new: impl AsRef<Path>) -> bool {
|
||||||
if let (Ok(canonical_old), Ok(canonical_new)) =
|
if let (Ok(canonical_old), Ok(canonical_new)) =
|
||||||
(fs::canonicalize(&old).await, fs::canonicalize(&new).await)
|
(fs::canonicalize(&old).await, fs::canonicalize(&new).await)
|
||||||
{
|
{
|
||||||
|
|
@ -40,10 +40,22 @@ pub async fn are_pathss_equal(old: impl AsRef<Path>, new: impl AsRef<Path>) -> b
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
}
|
||||||
{
|
|
||||||
// TODO: use MoveFileEx without MOVEFILE_REPLACE_EXISTING
|
#[inline]
|
||||||
}
|
#[cfg(windows)]
|
||||||
|
pub async fn rename_without_overwriting(
|
||||||
|
old: impl AsRef<Path>,
|
||||||
|
new: impl AsRef<Path>,
|
||||||
|
) -> 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<PathBuf> {
|
pub async fn symlink_realpath(path: &Path) -> Result<PathBuf> {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue