This commit is contained in:
sxyazi 2024-06-13 20:28:44 +08:00
parent b303219b99
commit 8eb2eea5cc
No known key found for this signature in database
4 changed files with 29 additions and 10 deletions

View file

@ -4,7 +4,7 @@ use anyhow::Result;
use tokio::fs; use tokio::fs;
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, TabProxy, WATCHER}; use yazi_proxy::{InputProxy, TabProxy, WATCHER};
use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, 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;
@ -47,6 +47,10 @@ impl Manager {
if dir { if dir {
fs::create_dir_all(&new).await?; fs::create_dir_all(&new).await?;
} else if let Ok(real) = symlink_realpath(&new).await {
ok_or_not_found(fs::remove_file(&new).await)?;
FilesOp::Deleting(parent.clone(), vec![Url::from(real)]).emit();
fs::File::create(&new).await?;
} else { } else {
fs::create_dir_all(&parent).await.ok(); fs::create_dir_all(&parent).await.ok();
ok_or_not_found(fs::remove_file(&new).await)?; ok_or_not_found(fs::remove_file(&new).await)?;
@ -54,7 +58,7 @@ impl Manager {
} }
if let Ok(f) = File::from(new.clone()).await { if let Ok(f) = File::from(new.clone()).await {
FilesOp::Upserting(parent, HashMap::from_iter([(new.clone(), f)])).emit(); FilesOp::Upserting(parent, HashMap::from_iter([(f.url(), f)])).emit();
TabProxy::reveal(&new) TabProxy::reveal(&new)
} }
Ok(()) Ok(())

View file

@ -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::{maybe_exists, 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;
@ -81,7 +81,13 @@ impl Manager {
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 overwritten = symlink_realpath(&new).await;
fs::rename(&old, &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();
}
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?;

View file

@ -8,7 +8,7 @@ use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use tracing::error; use tracing::error;
use yazi_plugin::isolate; use yazi_plugin::isolate;
use yazi_proxy::WATCHER; use yazi_proxy::WATCHER;
use yazi_shared::{fs::{symlink_realpath, File, FilesOp, Url}, RoCell}; use yazi_shared::{fs::{symlink_realpath_with, File, FilesOp, Url}, RoCell};
use super::Linked; use super::Linked;
use crate::folder::{Files, Folder}; use crate::folder::{Files, Folder};
@ -111,7 +111,7 @@ impl Watcher {
}; };
let realpath = if file.is_link() { let realpath = if file.is_link() {
symlink_realpath(&url, &mut cached).await symlink_realpath_with(&url, &mut cached).await
} else { } else {
fs::canonicalize(&url).await.map(Cow::Owned) fs::canonicalize(&url).await.map(Cow::Owned)
}; };

View file

@ -24,23 +24,32 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> {
} }
} }
#[inline]
pub async fn symlink_realpath(path: &Path) -> io::Result<PathBuf> {
if fs::symlink_metadata(path).await?.is_symlink() {
symlink_realpath_with(path, &mut HashMap::new()).await.map(|p| p.into_owned())
} else {
fs::canonicalize(path).await
}
}
// realpath(3) without resolving symlinks. This is useful for case-insensitive // realpath(3) without resolving symlinks. This is useful for case-insensitive
// filesystems. // filesystems.
// //
// Make sure the file of the path exists and is a symlink. // Make sure the file of the path exists and is a symlink.
pub async fn symlink_realpath<'a>( pub async fn symlink_realpath_with<'a>(
path: &'a Path, path: &'a Path,
cached: &'a mut HashMap<PathBuf, PathBuf>, cached: &'a mut HashMap<PathBuf, PathBuf>,
) -> io::Result<Cow<'a, Path>> { ) -> io::Result<Cow<'a, Path>> {
let Some(parent) = path.parent() else {
return Ok(Cow::Borrowed(path));
};
let lowercased: PathBuf = path.as_os_str().to_ascii_lowercase().into(); let lowercased: PathBuf = path.as_os_str().to_ascii_lowercase().into();
if lowercased == path { if lowercased == path {
return Ok(Cow::Borrowed(path)); return Ok(Cow::Borrowed(path));
} }
let Some(parent) = path.parent() else {
return Ok(Cow::Borrowed(path));
};
let case = parent.as_os_str().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase()); let case = parent.as_os_str().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase());
if !cached.contains_key(parent) { if !cached.contains_key(parent) {
let mut it = fs::read_dir(parent).await?; let mut it = fs::read_dir(parent).await?;