mirror of
https://github.com/sxyazi/yazi.git
synced 2024-11-20 18:51:32 +03:00
refactor: ensure that each Url
contains the full location information (#1659)
This commit is contained in:
parent
7222e178a3
commit
e858d11ce0
@ -7,9 +7,9 @@
|
||||
keymap = [
|
||||
{ on = "<Esc>", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
|
||||
{ on = "<C-[>", 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 = "<C-c>", 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 = "<C-c>", run = "close", desc = "Close the current tab, or quit if it's last" },
|
||||
{ on = "<C-z>", 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 = "<C-s>", 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", "<Space>" ], 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", "<Space>" ], 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 = "<Esc>", run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = "<C-[>", run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = "q", run = "close", desc = "Exit the process" },
|
||||
{ on = "<C-c>", run = "close", desc = "Hide the help" },
|
||||
|
||||
# Navigation
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<usize>) {
|
||||
// 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;
|
||||
|
@ -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())
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<FilesOp>) {
|
||||
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)),
|
||||
);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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() }
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ impl From<Cmd> 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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ impl TryFrom<Cmd> for Opt {
|
||||
|
||||
impl Tab {
|
||||
pub fn preview(&mut self, opt: impl TryInto<Opt>) {
|
||||
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());
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ impl From<Cmd> 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 }
|
||||
|
@ -26,7 +26,7 @@ impl<'a> From<Cmd> for Opt<'a> {
|
||||
impl<'a> Tab {
|
||||
pub fn select(&mut self, opt: impl Into<Opt<'a>>) {
|
||||
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;
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ impl From<Option<bool>> for Opt {
|
||||
|
||||
impl Tab {
|
||||
pub fn select_all(&mut self, opt: impl Into<Opt>) {
|
||||
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![]),
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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<Rect> {
|
||||
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<dyn Iterator<Item = &Url> + '_> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -43,31 +43,28 @@ impl File {
|
||||
});
|
||||
reg.add_method("mime", |lua, me, ()| {
|
||||
let cx = lua.named_registry_value::<CtxRef>("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::<CtxRef>("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::<CtxRef>("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::<CtxRef>("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 {
|
||||
|
@ -38,7 +38,7 @@ impl Folder {
|
||||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|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()));
|
||||
|
@ -24,7 +24,7 @@ impl Tab {
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|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));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
)
|
||||
};
|
||||
|
@ -62,6 +62,7 @@ impl Padding {
|
||||
}
|
||||
|
||||
pub fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
// TODO: remove this
|
||||
lua.register_userdata_type::<ratatui::widgets::Padding>(|reg| {
|
||||
reg.add_field_method_get("left", |_, me| Ok(me.left));
|
||||
reg.add_field_method_get("right", |_, me| Ok(me.right));
|
||||
|
@ -24,6 +24,7 @@ impl Position {
|
||||
}
|
||||
|
||||
pub fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
// TODO: remove this
|
||||
lua.register_userdata_type::<ratatui::layout::Position>(|reg| {
|
||||
reg.add_field_method_get("x", |_, me| Ok(me.x));
|
||||
reg.add_field_method_get("y", |_, me| Ok(me.y));
|
||||
|
@ -27,6 +27,7 @@ impl Rect {
|
||||
}
|
||||
|
||||
pub fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
// TODO: remove this
|
||||
lua.register_userdata_type::<ratatui::layout::Rect>(|reg| {
|
||||
reg.add_field_method_get("x", |_, me| Ok(me.x));
|
||||
reg.add_field_method_get("y", |_, me| Ok(me.y));
|
||||
|
2
yazi-plugin/src/external/fd.rs
vendored
2
yazi-plugin/src/external/fd.rs
vendored
@ -29,7 +29,7 @@ pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
2
yazi-plugin/src/external/rg.rs
vendored
2
yazi-plugin/src/external/rg.rs
vendored
@ -28,7 +28,7 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -55,7 +55,7 @@ impl Url {
|
||||
Value::UserData(ud) => me.strip_prefix(&*ud.borrow::<yazi_shared::fs::Url>()?),
|
||||
_ => 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));
|
||||
|
@ -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())
|
||||
};
|
||||
|
@ -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<Url>,
|
||||
pub icon: Cell<IconCache>,
|
||||
@ -34,44 +34,13 @@ impl File {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn from_search_item(cwd: &Url, url: Url) -> Result<Self> {
|
||||
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<FileType>) -> 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<FileType>) -> 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<Url> { self.url().parent_url() }
|
||||
pub fn parent(&self) -> Option<Url> { self.url.parent_url() }
|
||||
}
|
||||
|
@ -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<Path> 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<cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
impl Hash for Loc {
|
||||
fn hash<H: Hasher>(&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 }
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<Loc> for Url {
|
||||
fn from(loc: Loc) -> Self { Self { loc, ..Default::default() } }
|
||||
}
|
||||
|
||||
impl From<PathBuf> 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<String> 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<String> 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<Url> for Url {
|
||||
fn as_ref(&self) -> &Url { self }
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Url {
|
||||
fn as_ref(&self) -> &Path { &self.path }
|
||||
fn as_ref(&self) -> &Path { &self.loc }
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> 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<Path>) -> 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<Url> {
|
||||
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<Path>) -> 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 }
|
||||
|
Loading…
Reference in New Issue
Block a user