Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: append the suffix to the end when generating unique filenames for directories, i.e., after not before the extension #1784

Merged
merged 2 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

For breaking changes, see [Migrating to Yazi v0.4.0](https://github.com/sxyazi/yazi/issues/1772).

<br><br>

<div align="center">
<img src="assets/logo.png" alt="Yazi logo" width="20%">
</div>
Expand Down Expand Up @@ -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 |

Expand Down
2 changes: 1 addition & 1 deletion yazi-core/src/manager/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion yazi-plugin/src/fs/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down
4 changes: 2 additions & 2 deletions yazi-scheduler/src/file/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?;
Expand Down Expand Up @@ -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?;
Expand Down
10 changes: 5 additions & 5 deletions yazi-scheduler/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}};
Expand Down Expand Up @@ -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
});
Expand All @@ -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
});
Expand All @@ -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 })
Expand All @@ -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
});
Expand Down
12 changes: 7 additions & 5 deletions yazi-shared/src/fs/cha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl From<Metadata> for Cha {
let mut kind = ChaKind::empty();
if m.is_dir() {
kind |= ChaKind::DIR;
} else if m.is_symlink() {
kind |= ChaKind::LINK;
}

Self {
Expand Down Expand Up @@ -91,7 +93,7 @@ impl From<FileType> 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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) }

Expand Down
5 changes: 5 additions & 0 deletions yazi-shared/src/fs/fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ pub async fn maybe_exists(p: impl AsRef<Path>) -> bool {
}
}

#[inline]
pub async fn must_be_dir(p: impl AsRef<Path>) -> 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 {
Expand Down
55 changes: 34 additions & 21 deletions yazi-shared/src/fs/path.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -74,38 +74,51 @@ fn _expand_path(p: &Path) -> PathBuf {
}
}

pub async fn unique_name(mut u: Url) -> io::Result<Url> {
pub async fn unique_name<F>(u: Url, append: F) -> io::Result<Url>
where
F: Future<Output = bool>,
{
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<Url> {
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("_");
name.push(i.to_string());
name.push(&ext);

p.set_file_name(name);
i += 1;
}

u.set_loc(Loc::from(u.base(), p));
Expand Down