Skip to content

Commit

Permalink
fix: different filenames should be treated as the same file on case-i…
Browse files Browse the repository at this point in the history
…nsensitive file systems (#1151)
  • Loading branch information
sxyazi authored Jun 13, 2024
1 parent 189cb81 commit 794694e
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 101 deletions.
28 changes: 14 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"0.2","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"],"language":"en","flagWords":[]}
{"language":"en","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"]}
12 changes: 6 additions & 6 deletions yazi-boot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"

[dependencies]
regex = "1.10.4"
regex = "1.10.5"
yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" }
yazi-config = { path = "../yazi-config", version = "0.2.5" }
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }

# External dependencies
clap = { version = "4.5.4", features = [ "derive" ] }
clap = { version = "4.5.7", features = [ "derive" ] }
serde = { version = "1.0.203", features = [ "derive" ] }

[build-dependencies]
clap = { version = "4.5.4", features = [ "derive" ] }
clap_complete = "4.5.2"
clap_complete_nushell = "4.5.1"
clap_complete_fig = "4.5.0"
clap = { version = "4.5.7", features = [ "derive" ] }
clap_complete = "4.5.5"
clap_complete_nushell = "4.5.2"
clap_complete_fig = "4.5.1"
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }
10 changes: 5 additions & 5 deletions yazi-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }

# External dependencies
anyhow = "1.0.86"
clap = { version = "4.5.4", features = [ "derive" ] }
clap = { version = "4.5.7", features = [ "derive" ] }
crossterm = "0.27.0"
md-5 = "0.10.6"
serde_json = "1.0.117"
Expand All @@ -23,10 +23,10 @@ toml_edit = "0.22.14"

[build-dependencies]
anyhow = "1.0.86"
clap = { version = "4.5.4", features = [ "derive" ] }
clap_complete = "4.5.2"
clap_complete_fig = "4.5.0"
clap_complete_nushell = "4.5.1"
clap = { version = "4.5.7", features = [ "derive" ] }
clap_complete = "4.5.5"
clap_complete_fig = "4.5.1"
clap_complete_nushell = "4.5.2"
serde_json = "1.0.117"
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }

Expand Down
2 changes: 1 addition & 1 deletion yazi-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ futures = "0.3.30"
notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] }
parking_lot = "0.12.3"
ratatui = "0.26.3"
regex = "1.10.4"
regex = "1.10.5"
scopeguard = "1.2.0"
serde = "1.0.203"
shell-words = "1.1.0"
Expand Down
52 changes: 33 additions & 19 deletions yazi-core/src/manager/commands/create.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::path::PathBuf;
use std::collections::HashMap;

use anyhow::Result;
use tokio::fs;
use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}};
use yazi_proxy::{InputProxy, TabProxy, WATCHER};
use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}};

use crate::manager::Manager;

Expand All @@ -24,29 +25,42 @@ impl Manager {
let Some(Ok(name)) = result.recv().await else {
return Ok(());
};
if name.is_empty() {
return Ok(());
}

let path = cwd.join(&name);
if !opt.force && maybe_exists(&path).await {
let new = cwd.join(&name);
if !opt.force && maybe_exists(&new).await {
match InputProxy::show(InputCfg::overwrite()).recv().await {
Some(Ok(c)) if c == "y" || c == "Y" => (),
_ => return Ok(()),
}
}

if name.ends_with('/') || name.ends_with('\\') {
fs::create_dir_all(&path).await?;
} else {
fs::create_dir_all(&path.parent().unwrap()).await.ok();
fs::File::create(&path).await?;
}

let child =
Url::from(path.components().take(cwd.components().count() + 1).collect::<PathBuf>());
if let Ok(f) = File::from(child.clone()).await {
FilesOp::Creating(cwd, vec![f]).emit();
ManagerProxy::hover(Some(child));
}
Ok::<(), anyhow::Error>(())
Self::create_do(new, name.ends_with('/') || name.ends_with('\\')).await
});
}

async fn create_do(new: Url, dir: bool) -> Result<()> {
let Some(parent) = new.parent_url() else { return Ok(()) };
let _permit = WATCHER.acquire().await.unwrap();

if dir {
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 {
fs::create_dir_all(&parent).await.ok();
ok_or_not_found(fs::remove_file(&new).await)?;
fs::File::create(&new).await?;
}

if let Ok(f) = File::from(new.clone()).await {
FilesOp::Upserting(parent, HashMap::from_iter([(f.url(), f)])).emit();
TabProxy::reveal(&new)
}
Ok(())
}
}
22 changes: 13 additions & 9 deletions yazi-core/src/manager/commands/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anyhow::Result;
use tokio::fs;
use yazi_config::popup::InputCfg;
use yazi_dds::Pubsub;
use yazi_proxy::{InputProxy, ManagerProxy, WATCHER};
use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}};
use yazi_proxy::{InputProxy, TabProxy, WATCHER};
use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}};

use crate::manager::Manager;

Expand Down Expand Up @@ -77,19 +77,23 @@ impl Manager {
}

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();

let overwritten = symlink_realpath(&new).await;
fs::rename(&old, &new).await?;
if old.parent() != new.parent() {
return Ok(());
}

let file = File::from(new.clone()).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);

FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit();
FilesOp::Upserting(file.parent().unwrap(), HashMap::from_iter([(old, file)])).emit();
Ok(ManagerProxy::hover(Some(new)))
let file = File::from(new.clone()).await?;
FilesOp::Deleting(p_old, vec![old]).emit();
FilesOp::Upserting(p_new, HashMap::from_iter([(new.clone(), file)])).emit();
Ok(TabProxy::reveal(&new))
}

fn empty_url_part(url: &Url, by: &str) -> String {
Expand Down
38 changes: 25 additions & 13 deletions yazi-core/src/manager/watcher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}};
use std::{borrow::Cow, collections::{HashMap, HashSet}, time::{Duration, SystemTime}};

use anyhow::Result;
use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher};
Expand All @@ -8,7 +8,7 @@ use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use tracing::error;
use yazi_plugin::isolate;
use yazi_proxy::WATCHER;
use yazi_shared::{fs::{File, FilesOp, Url}, RoCell};
use yazi_shared::{fs::{symlink_realpath_with, File, FilesOp, Url}, RoCell};

use super::Linked;
use crate::folder::{Files, Folder};
Expand All @@ -35,8 +35,8 @@ impl Watcher {
Default::default(),
);

tokio::spawn(Self::on_in(in_rx, watcher.unwrap()));
tokio::spawn(Self::on_out(out_rx));
tokio::spawn(Self::fan_in(in_rx, watcher.unwrap()));
tokio::spawn(Self::fan_out(out_rx));
Self { tx: in_tx }
}

Expand Down Expand Up @@ -65,7 +65,7 @@ impl Watcher {
});
}

async fn on_in(mut rx: watch::Receiver<HashSet<Url>>, mut watcher: RecommendedWatcher) {
async fn fan_in(mut rx: watch::Receiver<HashSet<Url>>, mut watcher: RecommendedWatcher) {
loop {
let (mut to_unwatch, mut to_watch): (HashSet<_>, HashSet<_>) = {
let (new, old) = (&*rx.borrow_and_update(), &*WATCHED.read());
Expand All @@ -91,27 +91,39 @@ impl Watcher {
}
}

async fn on_out(rx: UnboundedReceiver<Url>) {
async fn fan_out(rx: UnboundedReceiver<Url>) {
// TODO: revert this once a new notification is implemented
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(50));
pin!(rx);

while let Some(urls) = rx.next().await {
while let Some(chunk) = rx.next().await {
let urls: HashSet<_> = chunk.into_iter().collect();
let mut cached: HashMap<_, _> = HashMap::new();

let _permit = WATCHER.acquire().await.unwrap();
let mut reload = Vec::with_capacity(urls.len());

for u in urls.into_iter().collect::<HashSet<_>>() {
let Some(parent) = u.parent_url() else { continue };

let Ok(file) = File::from(u.clone()).await else {
FilesOp::Deleting(parent, vec![u]).emit();
for url in urls {
let Some(parent) = url.parent_url() else { continue };
let Ok(file) = File::from(url.clone()).await else {
FilesOp::Deleting(parent, vec![url]).emit();
continue;
};

let real = if file.is_link() {
symlink_realpath_with(&url, &mut cached).await
} else {
fs::canonicalize(&url).await.map(Cow::Owned)
};
if !real.is_ok_and(|p| p == *url) {
FilesOp::Deleting(parent, vec![url]).emit();
continue;
}

if !file.is_dir() {
reload.push(file.clone());
}
FilesOp::Upserting(parent, HashMap::from_iter([(u, file)])).emit();
FilesOp::Upserting(parent, HashMap::from_iter([(url, file)])).emit();
}

if reload.is_empty() {
Expand Down
12 changes: 8 additions & 4 deletions yazi-dds/src/body/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@ impl Body<'static> {
if matches!(
kind,
"hi"
| "hey" | "bye"
| "cd" | "hover"
| "hey"
| "bye"
| "cd"
| "hover"
| "rename"
| "bulk" | "yank"
| "move" | "trash"
| "bulk"
| "yank"
| "move"
| "trash"
| "delete"
) {
bail!("Cannot construct system event");
Expand Down
Loading

0 comments on commit 794694e

Please sign in to comment.