From 6c2e1f6411160e43a4996ed35950ae3d18dc7ff8 Mon Sep 17 00:00:00 2001 From: Saru2003 Date: Tue, 15 Oct 2024 06:43:32 +0530 Subject: [PATCH 1/2] Change naming convention for folders to append index after the name --- yazi-shared/src/fs/path.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index 592e4f172..7279cc41a 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -100,9 +100,9 @@ pub async fn unique_name(mut u: Url) -> io::Result { let mut name = OsString::with_capacity(stem.len() + ext.len() + 5); name.push(&stem); - name.push("_"); - name.push(i.to_string()); - name.push(&ext); + name.push(&ext); + name.push("_"); + name.push(i.to_string()); p.set_file_name(name); i += 1; From 92ff55f29bef95412767162df579600cc739b18a Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 16 Oct 2024 00:37:31 +0800 Subject: [PATCH 2/2] feat: append the suffix to the end when generating unique filenames for directories, i.e., after not before the extension --- README.md | 4 ++- yazi-core/src/manager/watcher.rs | 2 +- yazi-plugin/src/fs/fs.rs | 2 +- yazi-scheduler/src/file/file.rs | 4 +-- yazi-scheduler/src/scheduler.rs | 10 +++--- yazi-shared/src/fs/cha.rs | 12 ++++--- yazi-shared/src/fs/fns.rs | 5 +++ yazi-shared/src/fs/path.rs | 55 ++++++++++++++++++++------------ 8 files changed, 58 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 5ae75e2e4..bb31463d9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ For breaking changes, see [Migrating to Yazi v0.4.0](https://github.com/sxyazi/yazi/issues/1772). +

+
Yazi logo
@@ -56,7 +58,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | [Hyper](https://github.com/vercel/hyper) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ❌ Rio doesn't correctly clear images (#1786) | | X11 / Wayland | Window system protocol | ☑️ [Überzug++](https://github.com/jstkdng/ueberzugpp) required | | Fallback | [ASCII art (Unicode block)](https://en.wikipedia.org/wiki/ASCII_art) | ☑️ [Chafa](https://hpjansson.org/chafa/) required | diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 33ebae065..1e6f41720 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -136,7 +136,7 @@ impl Watcher { }; let u = &file.url; - let eq = (!file.is_linkable() && fs::canonicalize(u).await.is_ok_and(|p| p == ***u)) + let eq = (!file.is_link() && fs::canonicalize(u).await.is_ok_and(|p| p == ***u)) || realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s); if !eq { diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index ca6544ca2..fed446ad1 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -111,7 +111,7 @@ pub fn install(lua: &Lua) -> mlua::Result<()> { ( "unique_name", lua.create_async_function(|lua, url: UrlRef| async move { - match yazi_shared::fs::unique_name(url.clone()).await { + match yazi_shared::fs::unique_name(url.clone(), async { false }).await { Ok(u) => (Url::cast(lua, u)?, Value::Nil).into_lua_multi(lua), Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua), } diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index f08aa1f11..e46cdae5b 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -163,7 +163,7 @@ impl File { let id = task.id; self.prog.send(TaskProg::New(id, cha.len))?; - if cha.is_linkable() { + if cha.is_orphan() || (cha.is_link() && !task.follow) { self.queue(FileOp::Link(task.into()), NORMAL).await?; } else { self.queue(FileOp::Paste(task), LOW).await?; @@ -208,7 +208,7 @@ impl File { let to = dest.join(from.file_name().unwrap()); self.prog.send(TaskProg::New(task.id, cha.len))?; - if cha.is_linkable() { + if cha.is_orphan() || (cha.is_link() && !task.follow) { self.queue(FileOp::Link(task.spawn(from, to, cha).into()), NORMAL).await?; } else { self.queue(FileOp::Paste(task.spawn(from, to, cha)), LOW).await?; diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 08f958f39..075e517b5 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -7,7 +7,7 @@ use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task:: use yazi_config::{TASKS, open::Opener, plugin::{Fetcher, Preloader}}; use yazi_dds::Pump; use yazi_proxy::ManagerProxy; -use yazi_shared::{Throttle, event::Data, fs::{Url, remove_dir_clean, unique_name}}; +use yazi_shared::{Throttle, event::Data, fs::{Url, must_be_dir, remove_dir_clean, unique_name}}; use super::{Ongoing, TaskProg, TaskStage}; use crate::{HIGH, LOW, NORMAL, TaskKind, TaskOp, file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}}; @@ -97,7 +97,7 @@ impl Scheduler { let file = self.file.clone(); self.send_micro(id, LOW, async move { if !force { - to = unique_name(to).await?; + to = unique_name(to, must_be_dir(&from)).await?; } file.paste(FileOpPaste { id, from, to, cha: None, cut: true, follow: false, retry: 0 }).await }); @@ -114,7 +114,7 @@ impl Scheduler { let file = self.file.clone(); self.send_micro(id, LOW, async move { if !force { - to = unique_name(to).await?; + to = unique_name(to, must_be_dir(&from)).await?; } file.paste(FileOpPaste { id, from, to, cha: None, cut: false, follow, retry: 0 }).await }); @@ -126,7 +126,7 @@ impl Scheduler { let file = self.file.clone(); self.send_micro(id, LOW, async move { if !force { - to = unique_name(to).await?; + to = unique_name(to, must_be_dir(&from)).await?; } file .link(FileOpLink { id, from, to, cha: None, resolve: false, relative, delete: false }) @@ -145,7 +145,7 @@ impl Scheduler { let file = self.file.clone(); self.send_micro(id, LOW, async move { if !force { - to = unique_name(to).await?; + to = unique_name(to, must_be_dir(&from)).await?; } file.hardlink(FileOpHardlink { id, from, to, cha: None, follow }).await }); diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 14a080898..294ffed13 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -42,6 +42,8 @@ impl From for Cha { let mut kind = ChaKind::empty(); if m.is_dir() { kind |= ChaKind::DIR; + } else if m.is_symlink() { + kind |= ChaKind::LINK; } Self { @@ -91,7 +93,7 @@ impl From for Cha { kind |= ChaKind::DIR; libc::S_IFDIR } else if t.is_symlink() { - kind |= ChaKind::ORPHAN; + kind |= ChaKind::LINK; libc::S_IFLNK } else if t.is_block_device() { libc::S_IFBLK @@ -130,8 +132,11 @@ impl Cha { let mut attached = ChaKind::empty(); if meta.is_symlink() { + attached |= ChaKind::LINK; meta = tokio::fs::metadata(path).await.unwrap_or(meta); - attached |= if meta.is_symlink() { ChaKind::ORPHAN } else { ChaKind::LINK }; + } + if meta.is_symlink() { + attached |= ChaKind::ORPHAN; } let mut cha = Self::new_nofollow(path, meta); @@ -187,9 +192,6 @@ impl Cha { #[inline] pub const fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) } - #[inline] - pub const fn is_linkable(&self) -> bool { self.is_link() || self.is_orphan() } - #[inline] pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index e33c3a78a..d0e3286aa 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -16,6 +16,11 @@ pub async fn maybe_exists(p: impl AsRef) -> bool { } } +#[inline] +pub async fn must_be_dir(p: impl AsRef) -> bool { + fs::metadata(p).await.map_or(false, |m| m.is_dir()) +} + #[inline] pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { match result { diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index 7279cc41a..48a7d7cc4 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, env, ffi::OsString, io, path::{Component, Path, PathBuf}}; +use std::{borrow::Cow, env, ffi::OsString, future::Future, io, path::{Component, Path, PathBuf}}; use tokio::fs; @@ -74,38 +74,51 @@ fn _expand_path(p: &Path) -> PathBuf { } } -pub async fn unique_name(mut u: Url) -> io::Result { +pub async fn unique_name(u: Url, append: F) -> io::Result +where + F: Future, +{ + match fs::symlink_metadata(&u).await { + Ok(_) => _unique_name(u, append.await).await, + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(u), + Err(e) => Err(e), + } +} + +async fn _unique_name(mut u: Url, append: bool) -> io::Result { let Some(stem) = u.file_stem().map(|s| s.to_owned()) else { return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); }; - let ext = u - .extension() - .map(|s| { - let mut n = OsString::with_capacity(s.len() + 1); - n.push("."); - n.push(s); - n - }) - .unwrap_or_default(); + let dot_ext = u.extension().map_or_else(OsString::new, |e| { + let mut s = OsString::with_capacity(e.len() + 1); + s.push("."); + s.push(e); + s + }); let mut i = 1u64; let mut p = u.to_path(); loop { + let mut name = OsString::with_capacity(stem.len() + dot_ext.len() + 5); + name.push(&stem); + + if append { + name.push(&dot_ext); + name.push("_"); + name.push(i.to_string()); + } else { + name.push("_"); + name.push(i.to_string()); + name.push(&dot_ext); + } + + p.set_file_name(name); match fs::symlink_metadata(&p).await { - Ok(_) => {} + Ok(_) => i += 1, Err(e) if e.kind() == io::ErrorKind::NotFound => break, Err(e) => return Err(e), } - - let mut name = OsString::with_capacity(stem.len() + ext.len() + 5); - name.push(&stem); - name.push(&ext); - name.push("_"); - name.push(i.to_string()); - - p.set_file_name(name); - i += 1; } u.set_loc(Loc::from(u.base(), p));