diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index adf82fc2..354a938a 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -7,9 +7,9 @@ keymap = [ { on = "", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, { on = "", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, - { on = "q", run = "quit", desc = "Exit the process" }, - { on = "Q", run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" }, - { on = "", run = "close", desc = "Close the current tab, or quit if it is last tab" }, + { on = "q", run = "quit", desc = "Quit the process" }, + { on = "Q", run = "quit --no-cwd-file", desc = "Quit the process without outputting cwd-file" }, + { on = "", run = "close", desc = "Close the current tab, or quit if it's last" }, { on = "", run = "suspend", desc = "Suspend the process" }, # Hopping @@ -74,19 +74,19 @@ keymap = [ { on = ";", run = "shell --interactive", desc = "Run a shell command" }, { on = ":", run = "shell --block --interactive", desc = "Run a shell command (block until finishes)" }, { on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" }, - { on = "s", run = "search fd", desc = "Search files by name using fd" }, - { on = "S", run = "search rg", desc = "Search files by content using ripgrep" }, + { on = "s", run = "search fd", desc = "Search files by name via fd" }, + { on = "S", run = "search rg", desc = "Search files by content via ripgrep" }, { on = "", run = "escape --search", desc = "Cancel the ongoing search" }, - { on = "z", run = "plugin zoxide", desc = "Jump to a directory using zoxide" }, - { on = "Z", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" }, + { on = "z", run = "plugin zoxide", desc = "Jump to a directory via zoxide" }, + { on = "Z", run = "plugin fzf", desc = "Jump to a file/directory via fzf" }, # Linemode - { on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" }, - { on = [ "m", "p" ], run = "linemode permissions", desc = "Set linemode to permissions" }, - { on = [ "m", "c" ], run = "linemode ctime", desc = "Set linemode to ctime" }, - { on = [ "m", "m" ], run = "linemode mtime", desc = "Set linemode to mtime" }, - { on = [ "m", "o" ], run = "linemode owner", desc = "Set linemode to owner" }, - { on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" }, + { on = [ "m", "s" ], run = "linemode size", desc = "Linemode: size" }, + { on = [ "m", "p" ], run = "linemode permissions", desc = "Linemode: permissions" }, + { on = [ "m", "c" ], run = "linemode ctime", desc = "Linemode: ctime" }, + { on = [ "m", "m" ], run = "linemode mtime", desc = "Linemode: mtime" }, + { on = [ "m", "o" ], run = "linemode owner", desc = "Linemode: owner" }, + { on = [ "m", "n" ], run = "linemode none", desc = "Linemode: none" }, # Copy { on = [ "c", "c" ], run = "copy path", desc = "Copy the file path" }, @@ -119,10 +119,10 @@ keymap = [ { on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" }, # Goto - { on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" }, - { on = [ "g", "c" ], run = "cd ~/.config", desc = "Go to the config directory" }, - { on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go to the downloads directory" }, - { on = [ "g", "" ], run = "cd --interactive", desc = "Go to a directory interactively" }, + { on = [ "g", "h" ], run = "cd ~", desc = "Go home" }, + { on = [ "g", "c" ], run = "cd ~/.config", desc = "Go to ~/.config" }, + { on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go to ~/Downloads" }, + { on = [ "g", "" ], run = "cd --interactive", desc = "Jump interactively" }, # Tabs { on = "t", run = "tab_create --current", desc = "Create a new tab with CWD" }, @@ -309,7 +309,6 @@ keymap = [ keymap = [ { on = "", run = "escape", desc = "Clear the filter, or hide the help" }, { on = "", run = "escape", desc = "Clear the filter, or hide the help" }, - { on = "q", run = "close", desc = "Exit the process" }, { on = "", run = "close", desc = "Hide the help" }, # Navigation diff --git a/yazi-config/src/theme/filetype.rs b/yazi-config/src/theme/filetype.rs index ff4059ee..184e30a6 100644 --- a/yazi-config/src/theme/filetype.rs +++ b/yazi-config/src/theme/filetype.rs @@ -18,7 +18,7 @@ impl Filetype { } self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) - || self.name.as_ref().is_some_and(|n| n.match_path(file.url(), file.is_dir())) + || self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir())) } } diff --git a/yazi-config/src/theme/icons.rs b/yazi-config/src/theme/icons.rs index bc0be9c8..7df1b4cd 100644 --- a/yazi-config/src/theme/icons.rs +++ b/yazi-config/src/theme/icons.rs @@ -43,7 +43,7 @@ impl Icons { #[inline] fn match_by_glob(&self, file: &File) -> Option<&Icon> { - self.globs.iter().find(|(p, _)| p.match_path(file.url(), file.is_dir())).map(|(_, i)| i) + self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i) } #[inline] @@ -62,7 +62,7 @@ impl Icons { #[inline] fn match_by_ext(&self, file: &File) -> Option<&Icon> { - let ext = file.url().extension()?.to_str()?; + let ext = file.url.extension()?.to_str()?; self.exts.get(ext).or_else(|| self.exts.get(&ext.to_ascii_lowercase())) } } diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 09385a98..ab32d7f5 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -16,7 +16,7 @@ impl Manager { return AppProxy::notify_warn("Bulk rename", "No text opener found"); }; - let cwd = self.cwd().url_owned(); + let cwd = self.cwd().to_owned(); let old: Vec<_> = self.selected_or_hovered(true).collect(); let root = max_common_root(&old); @@ -97,7 +97,7 @@ impl Manager { // FIXME: consider old and new in the different directories if !succeeded.is_empty() { - Pubsub::pub_from_bulk(succeeded.iter().map(|(u, f)| (u, f.url())).collect()); + Pubsub::pub_from_bulk(succeeded.iter().map(|(u, f)| (u, &f.url)).collect()); // FIXME 0 // FilesOp::Upserting(cwd, succeeded).emit(); } diff --git a/yazi-core/src/manager/commands/hover.rs b/yazi-core/src/manager/commands/hover.rs index a3d046a1..f5cdefca 100644 --- a/yazi-core/src/manager/commands/hover.rs +++ b/yazi-core/src/manager/commands/hover.rs @@ -37,28 +37,28 @@ impl Manager { // Refresh watcher let mut to_watch = HashSet::with_capacity(3 * self.tabs.len()); for tab in self.tabs.iter() { - to_watch.insert(tab.cwd().url()); + to_watch.insert(tab.cwd()); if let Some(ref p) = tab.parent { - to_watch.insert(&p.loc); + to_watch.insert(&p.url); } if let Some(h) = tab.current.hovered().filter(|&h| h.is_dir()) { - to_watch.insert(h.url()); + to_watch.insert(&h.url); } } self.watcher.watch(to_watch); // Publish through DDS - Pubsub::pub_from_hover(self.active().idx, self.hovered().map(|h| h.url())); + Pubsub::pub_from_hover(self.active().idx, self.hovered().map(|h| &h.url)); } fn hover_do(&mut self, url: Url, tab: Option) { // Hover on the file - if let Some(p) = url.strip_prefix(&self.current_or(tab).loc).map(PathBuf::from) { + if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) { render!(self.current_or_mut(tab).repos(Some(Urn::new(&p)))); } // Turn on tracing - if self.current_or(tab).hovered().is_some_and(|f| url == *f.url()) { + if self.current_or(tab).hovered().is_some_and(|f| url == f.url) { // `hover(Some)` occurs after user actions, such as create, rename, reveal, etc. // At this point, it's intuitive to track the location of this file regardless. self.current_or_mut(tab).tracing = true; diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index 5fe8b9ac..8f4ad853 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -110,9 +110,8 @@ impl Manager { }; let find = |folder: Option<&Folder>| { - folder.filter(|&f| p == *f.loc).is_some_and(|folder| { - let loc = url.to_loc(&folder.loc); - folder.files.iter().any(|f| f.is_dir() && f.urn() == loc.urn()) + folder.is_some_and(|folder| { + p == folder.url && folder.files.iter().any(|f| f.is_dir() && f.urn() == url.urn()) }) }; diff --git a/yazi-core/src/manager/commands/peek.rs b/yazi-core/src/manager/commands/peek.rs index 0721ff72..a664b87c 100644 --- a/yazi-core/src/manager/commands/peek.rs +++ b/yazi-core/src/manager/commands/peek.rs @@ -31,13 +31,13 @@ impl Manager { }; let folder = self.active().hovered_folder().map(|f| (f.offset, f.cha)); - if !self.active().preview.same_url(hovered.url()) { + if !self.active().preview.same_url(&hovered.url) { self.active_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default(); render!(self.active_mut().preview.reset()); } let opt = opt.into() as Opt; - if matches!(opt.only_if, Some(ref u) if u != hovered.url()) { + if matches!(opt.only_if, Some(u) if u != hovered.url) { return; } @@ -55,7 +55,7 @@ impl Manager { return; } - let mime = self.mimetype.get_owned(hovered.url()).unwrap_or_default(); + let mime = self.mimetype.get_owned(&hovered.url).unwrap_or_default(); if !mime.is_empty() { // Wait till mimetype is resolved to avoid flickering self.active_mut().preview.go(hovered, &mime, opt.force); diff --git a/yazi-core/src/manager/commands/refresh.rs b/yazi-core/src/manager/commands/refresh.rs index 6595a015..99b43c99 100644 --- a/yazi-core/src/manager/commands/refresh.rs +++ b/yazi-core/src/manager/commands/refresh.rs @@ -9,7 +9,7 @@ use crate::{manager::Manager, tasks::Tasks}; impl Manager { pub fn refresh(&mut self, _: Cmd, tasks: &Tasks) { env::set_current_dir(self.cwd()).ok(); - env::set_var("PWD", self.cwd().url()); + env::set_var("PWD", self.cwd()); if !MANAGER.title_format.is_empty() { execute!(std::io::stderr(), SetTitle(self.title())).ok(); @@ -31,7 +31,7 @@ impl Manager { fn title(&self) -> String { let home = dirs::home_dir().unwrap_or_default(); - let cwd = if let Some(p) = self.cwd().strip_prefix(home) { + let cwd = if let Ok(p) = self.cwd().strip_prefix(home) { format!("~{}{}", MAIN_SEPARATOR, p.display()) } else { format!("{}", self.cwd().display()) diff --git a/yazi-core/src/manager/commands/remove.rs b/yazi-core/src/manager/commands/remove.rs index b4e1919a..af383722 100644 --- a/yazi-core/src/manager/commands/remove.rs +++ b/yazi-core/src/manager/commands/remove.rs @@ -27,7 +27,7 @@ impl Manager { if !self.active_mut().try_escape_visual() { return; } - let Some(hovered) = self.hovered().map(|h| h.url()) else { + let Some(hovered) = self.hovered().map(|h| &h.url) else { return; }; diff --git a/yazi-core/src/manager/commands/seek.rs b/yazi-core/src/manager/commands/seek.rs index 7f560be0..5beb7a17 100644 --- a/yazi-core/src/manager/commands/seek.rs +++ b/yazi-core/src/manager/commands/seek.rs @@ -21,13 +21,13 @@ impl Manager { let mime = if hovered.is_dir() { MIME_DIR - } else if let Some(s) = self.mimetype.get(hovered.url()) { + } else if let Some(s) = self.mimetype.get(&hovered.url) { s } else { return render!(self.active_mut().preview.reset()); }; - let Some(previewer) = PLUGIN.previewer(hovered.url(), mime) else { + let Some(previewer) = PLUGIN.previewer(&hovered.url, mime) else { return render!(self.active_mut().preview.reset()); }; diff --git a/yazi-core/src/manager/commands/tab_create.rs b/yazi-core/src/manager/commands/tab_create.rs index 545f7aac..acf7be81 100644 --- a/yazi-core/src/manager/commands/tab_create.rs +++ b/yazi-core/src/manager/commands/tab_create.rs @@ -46,7 +46,7 @@ impl Tabs { } else { tab.conf = self.active().conf.clone(); tab.apply_files_attrs(); - tab.cd(self.active().cwd().url_owned()); + tab.cd(self.active().cwd().to_owned()); } self.items.insert(self.cursor + 1, tab); diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 934ebe33..7eb54b4a 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -47,11 +47,11 @@ impl Manager { let url = op.cwd(); tab.selected.apply_op(&op); - if url == tab.cwd().url() { + if url == tab.cwd() { Self::update_current(tab, op, tasks); - } else if matches!(&tab.parent, Some(p) if url == &*p.loc) { + } else if matches!(&tab.parent, Some(p) if *url == p.url) { Self::update_parent(tab, op); - } else if matches!(tab.current.hovered(), Some(h) if url == h.url()) { + } else if matches!(tab.current.hovered(), Some(h) if *url == h.url) { Self::update_hovered(tab, op); } else { Self::update_history(tab, op); @@ -108,7 +108,7 @@ impl Manager { } fn update_history(tab: &mut Tab, op: Cow) { - let leave = tab.parent.as_ref().and_then(|f| f.loc.parent_url().map(|p| (p, f.loc.urn()))).is_some_and( + let leave = tab.parent.as_ref().and_then(|f| f.url.parent_url().map(|p| (p, f.url.urn()))).is_some_and( |(p, n)| matches!(*op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(n)), ); diff --git a/yazi-core/src/manager/commands/update_mimetype.rs b/yazi-core/src/manager/commands/update_mimetype.rs index 69b988ba..2953e3f3 100644 --- a/yazi-core/src/manager/commands/update_mimetype.rs +++ b/yazi-core/src/manager/commands/update_mimetype.rs @@ -46,11 +46,11 @@ impl Manager { .current() .paginate(self.current().page) .iter() - .filter(|&f| updates.contains_key(f.url())) + .filter(|&f| updates.contains_key(&f.url)) .cloned() .collect(); - let repeek = self.hovered().is_some_and(|f| updates.contains_key(f.url())); + let repeek = self.hovered().is_some_and(|f| updates.contains_key(&f.url)); self.mimetype.extend(updates); if repeek { diff --git a/yazi-core/src/manager/commands/update_paged.rs b/yazi-core/src/manager/commands/update_paged.rs index 60330440..0dd8128b 100644 --- a/yazi-core/src/manager/commands/update_paged.rs +++ b/yazi-core/src/manager/commands/update_paged.rs @@ -27,7 +27,7 @@ impl Manager { return; }; - if opt.only_if.is_some_and(|u| u != *self.cwd().url()) { + if opt.only_if.is_some_and(|u| u != *self.cwd()) { return; } diff --git a/yazi-core/src/manager/manager.rs b/yazi-core/src/manager/manager.rs index 5f5afca2..5d3bb11d 100644 --- a/yazi-core/src/manager/manager.rs +++ b/yazi-core/src/manager/manager.rs @@ -2,7 +2,7 @@ use ratatui::layout::Rect; use yazi_adapter::Dimension; use yazi_config::popup::{Origin, Position}; use yazi_fs::Folder; -use yazi_shared::fs::{File, Loc, Url}; +use yazi_shared::fs::{File, Url}; use super::{Mimetype, Tabs, Watcher, Yanked}; use crate::tab::Tab; @@ -39,7 +39,7 @@ impl Manager { impl Manager { #[inline] - pub fn cwd(&self) -> &Loc { self.active().cwd() } + pub fn cwd(&self) -> &Url { self.active().cwd() } #[inline] pub fn active(&self) -> &Tab { self.tabs.active() } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index f2e114a4..df183758 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -61,7 +61,7 @@ impl Watcher { pub(super) fn trigger_dirs(&self, folders: &[&Folder]) { let todo: Vec<_> = - folders.iter().filter(|&f| f.loc.is_regular()).map(|&f| (f.loc.url_owned(), f.cha)).collect(); + folders.iter().filter(|&f| f.url.is_regular()).map(|&f| (f.url.to_owned(), f.cha)).collect(); if todo.is_empty() { return; } @@ -131,8 +131,8 @@ impl Watcher { continue; }; - let u = file.url(); - let eq = (!file.is_link() && fs::canonicalize(u).await.is_ok_and(|p| p == **u)) + let u = &file.url; + 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| s == urn._deref()._as_path()); if !eq { @@ -168,7 +168,7 @@ impl Watcher { for from in todo { let Ok(to) = fs::canonicalize(&from).await else { continue }; - if to != *from && WATCHED.read().contains(&from) { + if to != **from && WATCHED.read().contains(&from) { LINKED.write().insert(from, Url::from(to)); } } diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index 70c2daa6..c9446fe8 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -18,7 +18,7 @@ impl From for Opt { fn from(mut c: Cmd) -> Self { let mut target = c.take_first().and_then(Data::into_url).unwrap_or_default(); if target.is_regular() { - target.set_path(expand_path(&target)) + target = Url::from(expand_path(&target)); } Self { target, interactive: c.bool("interactive") } @@ -39,20 +39,20 @@ impl Tab { return self.cd_interactive(); } - if opt.target == *self.cwd().url() { + if opt.target == *self.cwd() { return; } // Take parent to history if let Some(rep) = self.parent.take() { - self.history.insert(rep.loc.url_owned(), rep); + self.history.insert(rep.url.to_owned(), rep); } // Current let rep = self.history.remove_or(&opt.target); let rep = mem::replace(&mut self.current, rep); - if rep.loc.is_regular() { - self.history.insert(rep.loc.url_owned(), rep); + if rep.url.is_regular() { + self.history.insert(rep.url.to_owned(), rep); } // Parent diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index 4f61306c..bcb30877 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -106,7 +106,7 @@ impl Tab { render!(); let urls: Vec<_> = - indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| f.url()).collect(); + indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| &f.url).collect(); let same = !self.cwd().is_search(); if !select { diff --git a/yazi-core/src/tab/commands/hidden.rs b/yazi-core/src/tab/commands/hidden.rs index 3c8fe968..b8a00d8d 100644 --- a/yazi-core/src/tab/commands/hidden.rs +++ b/yazi-core/src/tab/commands/hidden.rs @@ -14,7 +14,7 @@ impl Tab { let hovered = self.current.hovered().map(|f| f.url_owned()); self.apply_files_attrs(); - if hovered.as_ref() != self.current.hovered().map(|f| f.url()) { + if hovered.as_ref() != self.current.hovered().map(|f| &f.url) { ManagerProxy::hover(hovered, self.idx); } else if self.current.hovered().is_some_and(|f| f.is_dir()) { ManagerProxy::peek(true); diff --git a/yazi-core/src/tab/commands/leave.rs b/yazi-core/src/tab/commands/leave.rs index 5f82b80d..661fea99 100644 --- a/yazi-core/src/tab/commands/leave.rs +++ b/yazi-core/src/tab/commands/leave.rs @@ -16,7 +16,7 @@ impl Tab { .current .hovered() .and_then(|h| h.parent()) - .filter(|u| u != self.cwd().url()) + .filter(|u| u != self.cwd()) .or_else(|| self.cwd().parent_url()) .map(|u| self.cd(u)); } diff --git a/yazi-core/src/tab/commands/preview.rs b/yazi-core/src/tab/commands/preview.rs index 73c8eddb..ed838b82 100644 --- a/yazi-core/src/tab/commands/preview.rs +++ b/yazi-core/src/tab/commands/preview.rs @@ -17,7 +17,7 @@ impl TryFrom for Opt { impl Tab { pub fn preview(&mut self, opt: impl TryInto) { - let Some(hovered) = self.current.hovered().map(|h| h.url()) else { + let Some(hovered) = self.current.hovered().map(|h| &h.url) else { return render!(self.preview.reset()); }; diff --git a/yazi-core/src/tab/commands/reveal.rs b/yazi-core/src/tab/commands/reveal.rs index 46df9fb8..50410d95 100644 --- a/yazi-core/src/tab/commands/reveal.rs +++ b/yazi-core/src/tab/commands/reveal.rs @@ -11,7 +11,7 @@ impl From for Opt { fn from(mut c: Cmd) -> Self { let mut target = c.take_first().and_then(Data::into_url).unwrap_or_default(); if target.is_regular() { - target.set_path(expand_path(&target)) + target = Url::from(expand_path(&target)); } Self { target } diff --git a/yazi-core/src/tab/commands/select.rs b/yazi-core/src/tab/commands/select.rs index 537266c2..6de81ccf 100644 --- a/yazi-core/src/tab/commands/select.rs +++ b/yazi-core/src/tab/commands/select.rs @@ -26,7 +26,7 @@ impl<'a> From for Opt<'a> { impl<'a> Tab { pub fn select(&mut self, opt: impl Into>) { let opt = opt.into() as Opt; - let Some(url) = opt.url.or_else(|| self.current.hovered().map(|h| Cow::Borrowed(h.url()))) + let Some(url) = opt.url.or_else(|| self.current.hovered().map(|h| Cow::Borrowed(&h.url))) else { return; }; diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index 4a60a646..043ff442 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -24,7 +24,7 @@ impl From> for Opt { impl Tab { pub fn select_all(&mut self, opt: impl Into) { - let iter = self.current.files.iter().map(|f| f.url()); + let iter = self.current.files.iter().map(|f| &f.url); let (removal, addition): (Vec<_>, Vec<_>) = match opt.into().state { Some(true) => (vec![], iter.collect()), Some(false) => (iter.collect(), vec![]), diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index 9e29e43b..84d92386 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -20,11 +20,11 @@ pub struct Preview { impl Preview { pub fn go(&mut self, file: File, mime: &str, force: bool) { - if !force && self.content_unchanged(file.url(), file.cha) { + if !force && self.content_unchanged(&file.url, file.cha) { return; } - let Some(previewer) = PLUGIN.previewer(file.url(), mime) else { + let Some(previewer) = PLUGIN.previewer(&file.url, mime) else { self.reset(); return; }; diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 12cbfb15..562c4484 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -6,7 +6,7 @@ use tokio::task::JoinHandle; use yazi_adapter::Dimension; use yazi_config::{popup::{Origin, Position}, LAYOUT}; use yazi_fs::{Folder, FolderStage}; -use yazi_shared::{fs::{Loc, Url}, render}; +use yazi_shared::{fs::Url, render}; use super::{Backstack, Config, Finder, History, Mode, Preview}; use crate::tab::Selected; @@ -39,7 +39,7 @@ impl Tab { impl Tab { // --- Current #[inline] - pub fn cwd(&self) -> &Loc { &self.current.loc } + pub fn cwd(&self) -> &Url { &self.current.url } pub fn hovered_rect(&self) -> Option { let y = self.current.files.position(self.current.hovered()?.urn())? - self.current.offset; @@ -61,7 +61,7 @@ impl Tab { pub fn selected_or_hovered(&self, reorder: bool) -> Box + '_> { if self.selected.is_empty() { - Box::new(self.current.hovered().map(|h| vec![h.url()]).unwrap_or_default().into_iter()) + Box::new(self.current.hovered().map(|h| vec![&h.url]).unwrap_or_default().into_iter()) } else if !reorder { Box::new(self.selected.keys()) } else { @@ -75,20 +75,20 @@ impl Tab { let Some(h) = self.current.hovered() else { return Box::new(iter::empty()) }; if self.selected.is_empty() { - Box::new([h.url(), h.url()].into_iter()) + Box::new([&h.url, &h.url].into_iter()) } else if !reorder { - Box::new([h.url()].into_iter().chain(self.selected.keys())) + Box::new([&h.url].into_iter().chain(self.selected.keys())) } else { let mut vec: Vec<_> = self.selected.iter().collect(); vec.sort_unstable_by(|a, b| a.1.cmp(b.1)); - Box::new([h.url()].into_iter().chain(vec.into_iter().map(|(k, _)| k))) + Box::new([&h.url].into_iter().chain(vec.into_iter().map(|(k, _)| k))) } } // --- History #[inline] pub fn hovered_folder(&self) -> Option<&Folder> { - self.current.hovered().filter(|&h| h.is_dir()).and_then(|h| self.history.get(h.url())) + self.current.hovered().filter(|&h| h.is_dir()).and_then(|h| self.history.get(&h.url)) } pub fn apply_files_attrs(&mut self) { @@ -111,15 +111,15 @@ impl Tab { apply(parent); // The parent should always track the CWD - parent.hover(self.current.loc.urn()); - parent.tracing = parent.hovered().map(|h| h.url()) == Some(&self.current.loc); + parent.hover(self.current.url.urn()); + parent.tracing = parent.hovered().map(|h| &h.url) == Some(&self.current.url); } self .current .hovered() .filter(|h| h.is_dir()) - .and_then(|h| self.history.get_mut(h.url())) + .and_then(|h| self.history.get_mut(&h.url)) .map(apply); } } diff --git a/yazi-core/src/tasks/commands/open_with.rs b/yazi-core/src/tasks/commands/open_with.rs index 4712740f..00aba419 100644 --- a/yazi-core/src/tasks/commands/open_with.rs +++ b/yazi-core/src/tasks/commands/open_with.rs @@ -7,7 +7,7 @@ impl Tasks { if let Ok(opt) = opt.try_into() { self.process_from_opener( opt.opener, - opt.targets.into_iter().map(|u| u.into_os_string()).collect(), + opt.targets.into_iter().map(|u| u.into_path().into_os_string()).collect(), ); } } diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs index 6af581c2..c8703f72 100644 --- a/yazi-core/src/tasks/preload.rs +++ b/yazi-core/src/tasks/preload.rs @@ -10,14 +10,14 @@ impl Tasks { let mut loaded = self.scheduler.prework.loaded.lock(); let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default(); for f in paged { - let mime = if f.is_dir() { MIME_DIR } else { mimetype.get(f.url()).unwrap_or_default() }; + let mime = if f.is_dir() { MIME_DIR } else { mimetype.get(&f.url).unwrap_or_default() }; let factors = |s: &str| match s { "mime" => !mime.is_empty(), _ => false, }; - for p in PLUGIN.fetchers(f.url(), mime, factors) { - match loaded.get_mut(f.url()) { + for p in PLUGIN.fetchers(&f.url, mime, factors) { + match loaded.get_mut(&f.url) { Some(n) if *n & (1 << p.idx) != 0 => continue, Some(n) => *n |= 1 << p.idx, None => _ = loaded.insert(f.url_owned(), 1 << p.idx), @@ -37,9 +37,9 @@ impl Tasks { pub fn preload_paged(&self, paged: &[File], mimetype: &Mimetype) { let mut loaded = self.scheduler.prework.loaded.lock(); for f in paged { - let mime = if f.is_dir() { MIME_DIR } else { mimetype.get(f.url()).unwrap_or_default() }; - for p in PLUGIN.preloaders(f.url(), mime) { - match loaded.get_mut(f.url()) { + let mime = if f.is_dir() { MIME_DIR } else { mimetype.get(&f.url).unwrap_or_default() }; + for p in PLUGIN.preloaders(&f.url, mime) { + match loaded.get_mut(&f.url) { Some(n) if *n & (1 << p.idx) != 0 => continue, Some(n) => *n |= 1 << p.idx, None => _ = loaded.insert(f.url_owned(), 1 << p.idx), @@ -54,7 +54,7 @@ impl Tasks { { let mut loaded = self.scheduler.prework.loaded.lock(); for f in affected { - loaded.get_mut(f.url()).map(|n| *n &= mask); + loaded.get_mut(&f.url).map(|n| *n &= mask); } } @@ -71,10 +71,8 @@ impl Tasks { let loading = self.scheduler.prework.size_loading.read(); targets .iter() - .filter(|f| { - f.is_dir() && !targets.sizes.contains_key(f.urn()) && !loading.contains(f.url()) - }) - .map(|f| f.url()) + .filter(|f| f.is_dir() && !targets.sizes.contains_key(f.urn()) && !loading.contains(&f.url)) + .map(|f| &f.url) .collect() }; if targets.is_empty() { diff --git a/yazi-core/src/tasks/process.rs b/yazi-core/src/tasks/process.rs index 17beaa1a..82b14ba4 100644 --- a/yazi-core/src/tasks/process.rs +++ b/yazi-core/src/tasks/process.rs @@ -16,7 +16,7 @@ impl Tasks { for (opener, args) in openers { self.process_from_opener( Cow::Borrowed(opener), - args.into_iter().map(|u| u.into_os_string()).collect(), + args.into_iter().map(|u| u.into_path().into_os_string()).collect(), ); } } diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index 2985e613..c62878dd 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -43,31 +43,28 @@ impl File { }); reg.add_method("mime", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; - Ok(cx.manager.mimetype.get_owned(me.url())) + Ok(cx.manager.mimetype.get_owned(&me.url)) }); reg.add_method("prefix", |lua, me, ()| { - if !me.folder().loc.is_search() { + if !me.folder().url.is_search() { return Ok(None); } - let mut p = me.url().strip_prefix(&me.folder().loc).unwrap_or(me.url()).components(); + let mut p = me.url.strip_prefix(&me.folder().url).unwrap_or(&me.url).components(); p.next_back(); Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() }); reg.add_method("style", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; - let mime = if me.is_dir() { - MIME_DIR - } else { - cx.manager.mimetype.get(me.url()).unwrap_or_default() - }; + let mime = + if me.is_dir() { MIME_DIR } else { cx.manager.mimetype.get(&me.url).unwrap_or_default() }; Ok(THEME.filetypes.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style))) }); reg.add_method("is_hovered", |_, me, ()| Ok(me.idx == me.folder().cursor)); reg.add_method("is_yanked", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; - Ok(if !cx.manager.yanked.contains(me.url()) { + Ok(if !cx.manager.yanked.contains(&me.url) { 0u8 } else if cx.manager.yanked.cut { 2u8 @@ -77,7 +74,7 @@ impl File { }); reg.add_method("is_marked", |_, me, ()| { use yazi_core::tab::Mode::*; - if !me.tab().mode.is_visual() || me.folder().loc != me.tab().current.loc { + if !me.tab().mode.is_visual() || me.folder().url != me.tab().current.url { return Ok(0u8); } @@ -87,13 +84,13 @@ impl File { _ => 0u8, }) }); - reg.add_method("is_selected", |_, me, ()| Ok(me.tab().selected.contains_key(me.url()))); + reg.add_method("is_selected", |_, me, ()| Ok(me.tab().selected.contains_key(&me.url))); reg.add_method("in_parent", |_, me, ()| { - Ok(me.tab().parent.as_ref().is_some_and(|f| me.folder().loc == f.loc)) + Ok(me.tab().parent.as_ref().is_some_and(|f| me.folder().url == f.url)) }); - reg.add_method("in_current", |_, me, ()| Ok(me.folder().loc == me.tab().current.loc)); + reg.add_method("in_current", |_, me, ()| Ok(me.folder().url == me.tab().current.url)); reg.add_method("in_preview", |_, me, ()| { - Ok(me.tab().current.hovered().is_some_and(|f| f.url() == &*me.folder().loc)) + Ok(me.tab().current.hovered().is_some_and(|f| f.url == me.folder().url)) }); reg.add_method("found", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; @@ -101,7 +98,7 @@ impl File { return Ok(None); }; - let Some(idx) = finder.matched_idx(me.url()) else { + let Some(idx) = finder.matched_idx(&me.url) else { return Ok(None); }; @@ -113,7 +110,7 @@ impl File { let Some(finder) = &cx.manager.active().finder else { return Ok(None); }; - if me.folder().loc != me.tab().current.loc { + if me.folder().url != me.tab().current.url { return Ok(None); } let Some(h) = finder.filter.highlighted(me.name()) else { diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index 2d0e854b..e4fd4874 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -38,7 +38,7 @@ impl Folder { pub(super) fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { - reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.loc.url_owned())); + reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.url.to_owned())); reg.add_field_method_get("files", |_, me| Files::make(0..me.files.len(), me, me.tab())); reg.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage)); reg.add_field_method_get("window", |_, me| Files::make(me.window.clone(), me, me.tab())); diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index b29cd9d5..39b20224 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -24,7 +24,7 @@ impl Tab { pub(super) fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { reg.add_method("name", |lua, me, ()| { - lua.create_string(me.current.loc.name().as_encoded_bytes()) + lua.create_string(me.current.url.name().as_encoded_bytes()) }); reg.add_field_method_get("mode", |_, me| Mode::make(&me.mode)); diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index 2577a2be..e153e084 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -176,7 +176,8 @@ impl Files { ($dist:expr, $src:expr, $inc:literal) => { let mut todo: HashMap<_, _> = $src.into_iter().map(|f| (f.url_owned(), f)).collect(); for f in &$dist { - if todo.remove(f.url()).is_some() && todo.is_empty() { + // TODO: use urn instead + if todo.remove(&f.url).is_some() && todo.is_empty() { break; } } diff --git a/yazi-fs/src/folder.rs b/yazi-fs/src/folder.rs index c28b9cce..ac1d4aa0 100644 --- a/yazi-fs/src/folder.rs +++ b/yazi-fs/src/folder.rs @@ -2,14 +2,14 @@ use std::mem; use yazi_config::{LAYOUT, MANAGER}; use yazi_proxy::ManagerProxy; -use yazi_shared::fs::{Cha, File, FilesOp, Loc, Url, Urn}; +use yazi_shared::fs::{Cha, File, FilesOp, Url, Urn}; use super::FolderStage; use crate::{Files, Step}; #[derive(Default)] pub struct Folder { - pub loc: Loc, + pub url: Url, pub cha: Cha, pub files: Files, pub stage: FolderStage, @@ -22,7 +22,7 @@ pub struct Folder { } impl From<&Url> for Folder { - fn from(cwd: &Url) -> Self { Self { loc: Loc::from(cwd.clone()), ..Default::default() } } + fn from(url: &Url) -> Self { Self { url: url.clone(), ..Default::default() } } } impl Folder { @@ -101,7 +101,7 @@ impl Folder { let new = self.cursor / limit; if mem::replace(&mut self.page, new) != new || force { - ManagerProxy::update_paged_by(new, &self.loc); + ManagerProxy::update_paged_by(new, &self.url); } } diff --git a/yazi-fs/src/sorter.rs b/yazi-fs/src/sorter.rs index 98a61cad..377e5972 100644 --- a/yazi-fs/src/sorter.rs +++ b/yazi-fs/src/sorter.rs @@ -38,11 +38,11 @@ impl FilesSorter { }), SortBy::Extension => items.sort_unstable_by(|a, b| { let ord = if self.sensitive { - self.cmp(a.url().extension(), b.url().extension(), self.promote(a, b)) + self.cmp(a.url.extension(), b.url.extension(), self.promote(a, b)) } else { self.cmp( - a.url().extension().map(|s| s.to_ascii_lowercase()), - b.url().extension().map(|s| s.to_ascii_lowercase()), + a.url.extension().map(|s| s.to_ascii_lowercase()), + b.url.extension().map(|s| s.to_ascii_lowercase()), self.promote(a, b), ) }; diff --git a/yazi-plugin/src/elements/padding.rs b/yazi-plugin/src/elements/padding.rs index e695a558..212eba02 100644 --- a/yazi-plugin/src/elements/padding.rs +++ b/yazi-plugin/src/elements/padding.rs @@ -62,6 +62,7 @@ impl Padding { } pub fn register(lua: &Lua) -> mlua::Result<()> { + // TODO: remove this lua.register_userdata_type::(|reg| { reg.add_field_method_get("left", |_, me| Ok(me.left)); reg.add_field_method_get("right", |_, me| Ok(me.right)); diff --git a/yazi-plugin/src/elements/position.rs b/yazi-plugin/src/elements/position.rs index 241bb426..41a5358b 100644 --- a/yazi-plugin/src/elements/position.rs +++ b/yazi-plugin/src/elements/position.rs @@ -24,6 +24,7 @@ impl Position { } pub fn register(lua: &Lua) -> mlua::Result<()> { + // TODO: remove this lua.register_userdata_type::(|reg| { reg.add_field_method_get("x", |_, me| Ok(me.x)); reg.add_field_method_get("y", |_, me| Ok(me.y)); diff --git a/yazi-plugin/src/elements/rect.rs b/yazi-plugin/src/elements/rect.rs index a74d1806..b8bb479d 100644 --- a/yazi-plugin/src/elements/rect.rs +++ b/yazi-plugin/src/elements/rect.rs @@ -27,6 +27,7 @@ impl Rect { } pub fn register(lua: &Lua) -> mlua::Result<()> { + // TODO: remove this lua.register_userdata_type::(|reg| { reg.add_field_method_get("x", |_, me| Ok(me.x)); reg.add_field_method_get("y", |_, me| Ok(me.y)); diff --git a/yazi-plugin/src/external/fd.rs b/yazi-plugin/src/external/fd.rs index 40de3262..74aaca15 100644 --- a/yazi-plugin/src/external/fd.rs +++ b/yazi-plugin/src/external/fd.rs @@ -29,7 +29,7 @@ pub fn fd(opt: FdOpt) -> Result> { tokio::spawn(async move { while let Ok(Some(line)) = it.next_line().await { - if let Ok(file) = File::from_search_item(&opt.cwd, opt.cwd.join(line)).await { + if let Ok(file) = File::from(opt.cwd.join(line)).await { tx.send(file).ok(); } } diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 17440e2b..7ff71f2c 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -28,7 +28,7 @@ pub fn rg(opt: RgOpt) -> Result> { tokio::spawn(async move { while let Ok(Some(line)) = it.next_line().await { - if let Ok(file) = File::from_search_item(&opt.cwd, opt.cwd.join(line)).await { + if let Ok(file) = File::from(opt.cwd.join(line)).await { tx.send(file).ok(); } } diff --git a/yazi-plugin/src/file/file.rs b/yazi-plugin/src/file/file.rs index d2e101d0..cf7c0daa 100644 --- a/yazi-plugin/src/file/file.rs +++ b/yazi-plugin/src/file/file.rs @@ -1,6 +1,5 @@ use mlua::{AnyUserData, Lua, Table, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry}; use yazi_config::THEME; -use yazi_shared::fs::Loc; use crate::{bindings::{Cast, Icon}, cha::Cha, url::Url}; @@ -52,7 +51,7 @@ impl File { "File", lua.create_function(|lua, t: Table| { Self::cast(lua, yazi_shared::fs::File { - loc: Loc::from(t.raw_get::<_, AnyUserData>("url")?.take()?), + url: t.raw_get::<_, AnyUserData>("url")?.take()?, cha: t.raw_get::<_, AnyUserData>("cha")?.take()?, ..Default::default() }) diff --git a/yazi-plugin/src/url/url.rs b/yazi-plugin/src/url/url.rs index 752d56e0..125768e8 100644 --- a/yazi-plugin/src/url/url.rs +++ b/yazi-plugin/src/url/url.rs @@ -55,7 +55,7 @@ impl Url { Value::UserData(ud) => me.strip_prefix(&*ud.borrow::()?), _ => Err("must be a string or a Url".into_lua_err())?, }; - path.map(|p| Self::cast(lua, yazi_shared::fs::Url::from(p))).transpose() + path.ok().map(|p| Self::cast(lua, yazi_shared::fs::Url::from(p))).transpose() }); reg.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me == &*other)); diff --git a/yazi-plugin/src/utils/cache.rs b/yazi-plugin/src/utils/cache.rs index 1626c39e..c139d36a 100644 --- a/yazi-plugin/src/utils/cache.rs +++ b/yazi-plugin/src/utils/cache.rs @@ -11,12 +11,12 @@ impl Utils { "file_cache", lua.create_function(|lua, t: Table| { let file: FileRef = t.raw_get("file")?; - if file.url().parent() == Some(&PREVIEW.cache_dir) { + if file.url.parent() == Some(&PREVIEW.cache_dir) { return Ok(None); } let hex = { - let mut digest = Md5::new_with_prefix(file.url().as_os_str().as_encoded_bytes()); + let mut digest = Md5::new_with_prefix(file.url.as_os_str().as_encoded_bytes()); digest.update(format!("//{:?}//{}", file.cha.mtime, t.raw_get("skip").unwrap_or(0))); format!("{:x}", digest.finalize()) }; diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index 638be863..b1dc8ff5 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -3,12 +3,12 @@ use std::{cell::Cell, ffi::OsStr, fs::{FileType, Metadata}, ops::Deref}; use anyhow::Result; use tokio::fs; -use super::{Loc, Urn, UrnBuf}; +use super::{Urn, UrnBuf}; use crate::{fs::{Cha, ChaKind, Url}, theme::IconCache}; #[derive(Clone, Debug, Default)] pub struct File { - pub loc: Loc, + pub url: Url, pub cha: Cha, pub link_to: Option, pub icon: Cell, @@ -34,44 +34,13 @@ impl File { } #[inline] - pub async fn from_search_item(cwd: &Url, url: Url) -> Result { - let loc = Loc::from_search_item(cwd, url); - let meta = fs::symlink_metadata(loc.url()).await?; - Ok(Self::from_loc(loc, meta).await) - } - - #[inline] - pub async fn from_meta(url: Url, meta: Metadata) -> Self { - Self::from_loc(Loc::from(url), meta).await - } - - #[inline] - pub fn from_dummy(url: Url, ft: Option) -> Self { - Self { - loc: Loc::from(url), - cha: ft.map_or_else(Cha::dummy, Cha::from), - link_to: None, - icon: Default::default(), - } - } - - #[inline] - pub fn rebase(&self, parent: &Url) -> Self { - Self { - loc: self.loc.rebase(parent), - cha: self.cha, - link_to: self.link_to.clone(), - icon: Default::default(), - } - } - - async fn from_loc(loc: Loc, mut meta: Metadata) -> Self { + pub async fn from_meta(url: Url, mut meta: Metadata) -> Self { let mut ck = ChaKind::empty(); let (is_link, mut link_to) = (meta.is_symlink(), None); if is_link { - meta = fs::metadata(loc.url()).await.unwrap_or(meta); - link_to = fs::read_link(loc.url()).await.map(Url::from).ok(); + meta = fs::metadata(&url).await.unwrap_or(meta); + link_to = fs::read_link(&url).await.map(Url::from).ok(); } if is_link && meta.is_symlink() { @@ -81,7 +50,7 @@ impl File { } #[cfg(unix)] - if loc.urn().is_hidden() { + if url.is_hidden() { ck |= ChaKind::HIDDEN; } #[cfg(windows)] @@ -92,30 +61,47 @@ impl File { } } - Self { loc, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() } + Self { url, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() } + } + + #[inline] + pub fn from_dummy(url: Url, ft: Option) -> Self { + Self { + url, + cha: ft.map_or_else(Cha::dummy, Cha::from), + link_to: None, + icon: Default::default(), + } + } + + #[inline] + pub fn rebase(&self, parent: &Url) -> Self { + Self { + url: self.url.rebase(parent), + cha: self.cha, + link_to: self.link_to.clone(), + icon: Default::default(), + } } } impl File { - // --- Loc + // --- Url #[inline] - pub fn url(&self) -> &Url { self.loc.url() } + pub fn url_owned(&self) -> Url { self.url.to_owned() } #[inline] - pub fn url_owned(&self) -> Url { self.loc.url_owned() } + pub fn urn(&self) -> &Urn { self.url.urn() } #[inline] - pub fn urn(&self) -> &Urn { self.loc.urn() } + pub fn urn_owned(&self) -> UrnBuf { self.url.urn_owned() } #[inline] - pub fn urn_owned(&self) -> UrnBuf { self.loc.urn_owned() } + pub fn name(&self) -> &OsStr { self.url.name() } #[inline] - pub fn name(&self) -> &OsStr { self.loc.name() } + pub fn stem(&self) -> Option<&OsStr> { self.url.file_stem() } #[inline] - pub fn stem(&self) -> Option<&OsStr> { self.url().file_stem() } - - #[inline] - pub fn parent(&self) -> Option { self.url().parent_url() } + pub fn parent(&self) -> Option { self.url.parent_url() } } diff --git a/yazi-shared/src/fs/loc.rs b/yazi-shared/src/fs/loc.rs index 33562e79..8bdea196 100644 --- a/yazi-shared/src/fs/loc.rs +++ b/yazi-shared/src/fs/loc.rs @@ -1,50 +1,60 @@ -use std::{ffi::OsStr, fmt::{self, Debug, Formatter}, ops::Deref, path::Path}; +use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, ops::Deref, path::{Path, PathBuf}}; -use super::{Url, Urn, UrnBuf}; +use super::{Urn, UrnBuf}; pub struct Loc { - url: Url, + path: PathBuf, urn: *const OsStr, name: *const OsStr, } unsafe impl Send for Loc {} +unsafe impl Sync for Loc {} + impl Default for Loc { - fn default() -> Self { Self { url: Url::default(), urn: OsStr::new(""), name: OsStr::new("") } } -} - -impl Deref for Loc { - type Target = Url; - - fn deref(&self) -> &Self::Target { &self.url } -} - -impl AsRef for Loc { - fn as_ref(&self) -> &Path { self.url() } -} - -impl Eq for Loc {} - -impl PartialEq for Loc { - fn eq(&self, other: &Self) -> bool { - self.url == other.url && self.urn() == other.urn() && self.name() == other.name() + fn default() -> Self { + Self { path: PathBuf::default(), urn: OsStr::new(""), name: OsStr::new("") } } } impl Clone for Loc { fn clone(&self) -> Self { - let url = self.url.clone(); - let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - let urn = if url.is_search_item() { self.twin_urn(&url) } else { name }; - Self { url, urn, name } + let path = self.path.clone(); + let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; + let urn = if self.urn()._as_path() == self.name() { name } else { self.twin_urn(&path) }; + Self { path, urn, name } } } +impl Deref for Loc { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { &self.path } +} + +impl PartialEq for Loc { + fn eq(&self, other: &Self) -> bool { self.path == other.path } +} + +impl Eq for Loc {} + +impl Ord for Loc { + fn cmp(&self, other: &Self) -> cmp::Ordering { self.path.cmp(&other.path) } +} + +impl PartialOrd for Loc { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl Hash for Loc { + fn hash(&self, state: &mut H) { self.path.hash(state) } +} + impl Debug for Loc { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("Loc") - .field("url", &self.url) + .field("path", &self.path) .field("urn", &self.urn()) .field("name", &self.name()) .finish() @@ -52,26 +62,35 @@ impl Debug for Loc { } impl Loc { - pub fn from(url: Url) -> Self { - let urn = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - Self { url, urn, name: urn } + pub fn new(path: PathBuf) -> Self { + let urn = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; + Self { path, urn, name: urn } } - pub fn from_search_item(cwd: &Url, url: Url) -> Self { - let urn = url.strip_prefix(cwd).unwrap_or(&url).as_os_str() as *const OsStr; - let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - Self { url, urn, name } + pub fn from(base: &Path, path: PathBuf) -> Self { + let urn = path.strip_prefix(base).unwrap_or(&path).as_os_str() as *const OsStr; + let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; + Self { path, urn, name } } - pub fn rebase(&self, parent: &Url) -> Self { - let url = parent.join(self.name()); - let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - let urn = if url.is_search_item() { self.twin_urn(&url) } else { name }; - Self { url, urn, name } + pub fn base(&self) -> &Path { + let mut it = self.path.components(); + for _ in 0..self.urn()._as_path().components().count() { + it.next_back().unwrap(); + } + it.as_path() + } + + pub fn rebase(&self, parent: &Path) -> Self { + debug_assert!(self.urn()._as_path() == self.name()); + + let path = parent.join(self.name()); + let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; + Self { path, urn: name, name } } #[inline] - fn twin_urn<'a>(&self, new: &'a Url) -> &'a OsStr { + fn twin_urn<'a>(&self, new: &'a Path) -> &'a OsStr { let total = new.components().count(); let take = self.urn()._as_path().components().count(); @@ -85,12 +104,6 @@ impl Loc { } impl Loc { - #[inline] - pub fn url(&self) -> &Url { &self.url } - - #[inline] - pub fn url_owned(&self) -> Url { self.url.to_owned() } - #[inline] pub fn urn(&self) -> &Urn { Urn::new(unsafe { &*self.urn }) } @@ -99,4 +112,7 @@ impl Loc { #[inline] pub fn name(&self) -> &OsStr { unsafe { &*self.name } } + + #[inline] + pub fn into_path(self) -> PathBuf { self.path } } diff --git a/yazi-shared/src/fs/op.rs b/yazi-shared/src/fs/op.rs index 3207d5b8..74238923 100644 --- a/yazi-shared/src/fs/op.rs +++ b/yazi-shared/src/fs/op.rs @@ -57,9 +57,9 @@ impl FilesOp { let n = new.clone(); match self { - Self::Full(_, files, mtime) => Self::Full(n, files!(files), *mtime), + Self::Full(_, files, cha) => Self::Full(n, files!(files), *cha), Self::Part(_, files, ticket) => Self::Part(n, files!(files), *ticket), - Self::Done(_, mtime, ticket) => Self::Done(n, *mtime, *ticket), + Self::Done(_, cha, ticket) => Self::Done(n, *cha, *ticket), Self::Size(_, map) => Self::Size(n, map.iter().map(|(u, &s)| (u.clone(), s)).collect()), Self::IOErr(_, err) => Self::IOErr(n, *err), diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index 64a150b4..f6fedaad 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -88,17 +88,19 @@ pub async fn unique_name(mut u: Url) -> Url { .unwrap_or_default(); let mut i = 1u64; - while maybe_exists(&u).await { + let mut p = u.into_path(); + while maybe_exists(&p).await { let mut name = OsString::with_capacity(stem.len() + ext.len() + 5); name.push(&stem); name.push("_"); name.push(i.to_string()); name.push(&ext); - u.set_file_name(name); + p.set_file_name(name); i += 1; } - u + // FIXME 3: handle base path + Url::from(p) } // Parameters diff --git a/yazi-shared/src/fs/url.rs b/yazi-shared/src/fs/url.rs index d89b49c3..a34662b1 100644 --- a/yazi-shared/src/fs/url.rs +++ b/yazi-shared/src/fs/url.rs @@ -1,4 +1,4 @@ -use std::{ffi::{OsStr, OsString}, fmt::{Debug, Display, Formatter}, ops::{Deref, DerefMut}, path::{Path, PathBuf}}; +use std::{ffi::OsStr, fmt::{Debug, Display, Formatter}, ops::Deref, path::{Path, PathBuf}}; use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS}; use serde::{Deserialize, Serialize}; @@ -9,8 +9,8 @@ const ENCODE_SET: &AsciiSet = &CONTROLS.add(b'#'); #[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Url { + loc: Loc, scheme: UrlScheme, - path: PathBuf, frag: String, } @@ -24,37 +24,36 @@ pub enum UrlScheme { } impl Deref for Url { - type Target = PathBuf; + type Target = Loc; - fn deref(&self) -> &Self::Target { &self.path } -} - -impl DerefMut for Url { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.path } + fn deref(&self) -> &Self::Target { &self.loc } } impl Debug for Url { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.path.display()) } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self.scheme { + UrlScheme::Regular => write!(f, "Regular({:?})", self.loc), + UrlScheme::Search => write!(f, "Search({:?}, {})", self.loc, self.frag), + UrlScheme::SearchItem => write!(f, "SearchItem({:?})", self.loc), + UrlScheme::Archive => write!(f, "Archive({:?})", self.loc), + } + } +} + +impl From for Url { + fn from(loc: Loc) -> Self { Self { loc, ..Default::default() } } } impl From for Url { - fn from(path: PathBuf) -> Self { Self { path, ..Default::default() } } + fn from(path: PathBuf) -> Self { Loc::new(path).into() } } impl From<&PathBuf> for Url { - fn from(path: &PathBuf) -> Self { Self::from(path.clone()) } + fn from(path: &PathBuf) -> Self { path.to_owned().into() } } impl From<&Path> for Url { - fn from(path: &Path) -> Self { Self::from(path.to_path_buf()) } -} - -impl From for Url { - fn from(path: String) -> Self { Self::from(path.as_str()) } -} - -impl From<&String> for Url { - fn from(path: &String) -> Self { Self::from(path.as_str()) } + fn from(path: &Path) -> Self { path.to_owned().into() } } impl From<&str> for Url { @@ -62,11 +61,11 @@ impl From<&str> for Url { let mut url = Url::default(); match path.split_once("://").map(|(a, b)| (UrlScheme::from(a), b)) { None => { - url.path = PathBuf::from(path); + url.loc = Loc::new(PathBuf::from(path)); return url; } Some((UrlScheme::Regular, b)) => { - url.path = PathBuf::from(b); + url.loc = Loc::new(PathBuf::from(b)); return url; } Some((a, b)) => { @@ -76,10 +75,12 @@ impl From<&str> for Url { } match path.split_once('#') { None => { - url.path = percent_decode_str(path).decode_utf8_lossy().into_owned().into(); + // FIXME: use `Loc::from(base, path)` instead + url.loc = Loc::new(percent_decode_str(path).decode_utf8_lossy().into_owned().into()); } Some((a, b)) => { - url.path = percent_decode_str(a).decode_utf8_lossy().into_owned().into(); + // FIXME: use `Loc::from(base, path)` instead + url.loc = Loc::new(percent_decode_str(a).decode_utf8_lossy().into_owned().into()); url.frag = b.to_string(); } } @@ -87,22 +88,30 @@ impl From<&str> for Url { } } +impl From for Url { + fn from(path: String) -> Self { path.as_str().into() } +} + +impl From<&String> for Url { + fn from(path: &String) -> Self { path.as_str().into() } +} + impl AsRef for Url { fn as_ref(&self) -> &Url { self } } impl AsRef for Url { - fn as_ref(&self) -> &Path { &self.path } + fn as_ref(&self) -> &Path { &self.loc } } impl AsRef for Url { - fn as_ref(&self) -> &OsStr { self.path.as_os_str() } + fn as_ref(&self) -> &OsStr { self.loc.as_os_str() } } impl Display for Url { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if matches!(self.scheme, UrlScheme::Regular | UrlScheme::SearchItem) { - return f.write_str(&self.path.to_string_lossy()); + return f.write_str(&self.loc.to_string_lossy()); } let scheme = match self.scheme { @@ -110,7 +119,7 @@ impl Display for Url { UrlScheme::Search => "search://", UrlScheme::Archive => "archive://", }; - let path = percent_encode(self.path.as_os_str().as_encoded_bytes(), ENCODE_SET); + let path = percent_encode(self.loc.as_os_str().as_encoded_bytes(), ENCODE_SET); write!(f, "{scheme}{path}")?; if !self.frag.is_empty() { @@ -128,48 +137,47 @@ impl From<&Url> for String { impl Url { #[inline] pub fn join(&self, path: impl AsRef) -> Self { - let url = Self::from(self.path.join(path)); match self.scheme { - UrlScheme::Regular => url, - UrlScheme::Search => url.into_search_item(), - UrlScheme::SearchItem => url, - UrlScheme::Archive => url.into_archive(), + UrlScheme::Regular => Self::from(self.loc.join(path)), + UrlScheme::Search => { + let loc = Loc::from(&self.loc, self.loc.join(path)); + Self::from(loc).into_search_item() + } + UrlScheme::SearchItem => { + let loc = Loc::from(self.loc.base(), self.loc.join(path)); + Self::from(loc).into_search_item() + } + UrlScheme::Archive => Self::from(self.loc.join(path)).into_archive(), } } #[inline] pub fn parent_url(&self) -> Option { - self.path.parent().map(|p| { - let url = Self::from(p); - match self.scheme { - UrlScheme::Regular => url, - UrlScheme::Search => url, - UrlScheme::SearchItem => url, - UrlScheme::Archive => url, + let p = self.loc.parent()?; + Some(match self.scheme { + UrlScheme::Regular | UrlScheme::Search => Self::from(p), + UrlScheme::SearchItem => { + if p == self.loc.base() { + Self::from(p).into_search("") + } else { + Self::from(p).into_search_item() + } } + UrlScheme::Archive => Self::from(p), }) } #[inline] - pub fn pair(&self) -> Option<(Self, UrnBuf)> { - let urn = UrnBuf::_from(self.path.file_name()?); - Some((self.parent_url()?, urn)) - } + pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) } + + #[cfg(unix)] + #[inline] + pub fn is_hidden(&self) -> bool { self.loc.urn().is_hidden() } #[inline] - pub fn strip_prefix(&self, base: impl AsRef) -> Option<&Path> { - self.path.strip_prefix(base).ok() - } - - #[inline] - pub fn into_os_string(self) -> OsString { self.path.into_os_string() } - - #[inline] - pub fn to_loc(&self, cwd: &Url) -> Loc { self.clone().into_loc(cwd) } - - #[inline] - pub fn into_loc(self, cwd: &Url) -> Loc { - if self.is_search_item() { Loc::from_search_item(cwd, self) } else { Loc::from(self) } + pub fn rebase(&self, parent: &Path) -> Self { + debug_assert!(self.is_regular()); + self.loc.rebase(parent).into() } } @@ -180,7 +188,7 @@ impl Url { #[inline] pub fn to_regular(&self) -> Self { - Self { scheme: UrlScheme::Regular, path: self.path.clone(), frag: String::new() } + Self { loc: self.loc.clone(), scheme: UrlScheme::Regular, frag: String::new() } } #[inline] @@ -196,7 +204,14 @@ impl Url { #[inline] pub fn to_search(&self, frag: &str) -> Self { - Self { scheme: UrlScheme::Search, path: self.path.clone(), frag: frag.to_owned() } + Self { loc: self.loc.clone(), scheme: UrlScheme::Search, frag: frag.to_owned() } + } + + #[inline] + pub fn into_search(mut self, frag: &str) -> Self { + self.scheme = UrlScheme::Search; + self.frag = frag.to_owned(); + self } #[inline] @@ -214,7 +229,7 @@ impl Url { #[inline] pub fn to_archive(&self) -> Self { - Self { scheme: UrlScheme::Archive, path: self.path.clone(), frag: String::new() } + Self { loc: self.loc.clone(), scheme: UrlScheme::Archive, frag: String::new() } } #[inline] @@ -224,17 +239,14 @@ impl Url { self } + // --- Path + #[inline] + pub fn into_path(self) -> PathBuf { self.loc.into_path() } + // --- Scheme #[inline] pub fn scheme(&self) -> UrlScheme { self.scheme } - // --- Path - #[inline] - pub fn set_path(&mut self, path: PathBuf) { self.path = path; } - - #[inline] - pub fn into_path(self) -> PathBuf { self.path } - // --- Frag #[inline] pub fn frag(&self) -> &str { &self.frag }