mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: URL covariance for selected and yanked files (#3025)
This commit is contained in:
parent
9535caad5b
commit
9c1f303f2c
20 changed files with 175 additions and 128 deletions
|
|
@ -115,12 +115,12 @@ mod tests {
|
|||
fn test_split() {
|
||||
yazi_fs::init();
|
||||
compare("foo", "", "foo");
|
||||
compare("foo\\", "foo\\", "");
|
||||
compare("foo\\bar", "foo\\", "bar");
|
||||
compare("foo\\bar\\", "foo\\bar\\", "");
|
||||
compare("C:\\", "C:\\", "");
|
||||
compare("C:\\foo", "C:\\", "foo");
|
||||
compare("C:\\foo\\", "C:\\foo\\", "");
|
||||
compare("C:\\foo\\bar", "C:\\foo\\", "bar");
|
||||
compare(r"foo\", r"foo\", "");
|
||||
compare(r"foo\bar", r"foo\", "bar");
|
||||
compare(r"foo\bar\", r"foo\bar\", "");
|
||||
compare(r"C:\", r"C:\", "");
|
||||
compare(r"C:\foo", r"C:\", "foo");
|
||||
compare(r"C:\foo\", r"C:\foo\", "");
|
||||
compare(r"C:\foo\bar", r"C:\foo\", "bar");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ impl UserData for File {
|
|||
_ => 0u8,
|
||||
})
|
||||
});
|
||||
methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains_key(&me.url)));
|
||||
methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains(&me.url)));
|
||||
methods.add_method("found", |lua, me, ()| {
|
||||
lua.named_registry_value::<AnyUserData>("cx")?.borrow_scoped(|core: &yazi_core::Core| {
|
||||
let Some(finder) = &core.active().finder else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use indexmap::IndexMap;
|
||||
use mlua::AnyUserData;
|
||||
|
||||
use super::Lives;
|
||||
|
|
@ -9,11 +8,11 @@ pub(super) struct Selected;
|
|||
|
||||
impl Selected {
|
||||
#[inline]
|
||||
pub(super) fn make(inner: &IndexMap<yazi_shared::url::Url, u64>) -> mlua::Result<AnyUserData> {
|
||||
pub(super) fn make(inner: &yazi_core::tab::Selected) -> mlua::Result<AnyUserData> {
|
||||
let inner = PtrCell::from(inner);
|
||||
|
||||
Lives::scoped_userdata(yazi_binding::Iter::new(
|
||||
inner.as_static().keys().cloned().map(yazi_binding::Url::new),
|
||||
inner.as_static().values().cloned().map(yazi_binding::Url::new),
|
||||
Some(inner.len()),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
// FIXME: VFS
|
||||
use std::{ffi::OsString, path::Path};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use yazi_macro::{act, succ};
|
||||
|
|
@ -28,13 +27,24 @@ impl Actor for Copy {
|
|||
.peekable();
|
||||
|
||||
while let Some(u) = it.next() {
|
||||
s.push(match opt.r#type.as_ref() {
|
||||
"path" => opt.separator.transform(u),
|
||||
"dirname" => opt.separator.transform(u.parent().unwrap_or(Path::new(""))),
|
||||
"filename" => opt.separator.transform(u.name()),
|
||||
"name_without_ext" => opt.separator.transform(u.file_stem().unwrap_or_default()),
|
||||
match opt.r#type.as_ref() {
|
||||
// TODO: rename to "url"
|
||||
"path" => {
|
||||
s.push(opt.separator.transform(&u.os_str()));
|
||||
}
|
||||
"dirname" => {
|
||||
if let Some(p) = u.parent_url() {
|
||||
s.push(opt.separator.transform(&p.os_str()));
|
||||
}
|
||||
}
|
||||
"filename" => {
|
||||
s.push(opt.separator.transform(u.name()));
|
||||
}
|
||||
"name_without_ext" => {
|
||||
s.push(opt.separator.transform(u.file_stem().unwrap_or_default()));
|
||||
}
|
||||
_ => bail!("Unknown copy type: {}", opt.r#type),
|
||||
});
|
||||
};
|
||||
if it.peek().is_some() {
|
||||
s.push("\n");
|
||||
}
|
||||
|
|
@ -42,7 +52,7 @@ impl Actor for Copy {
|
|||
|
||||
// Copy the CWD path regardless even if the directory is empty
|
||||
if s.is_empty() && opt.r#type == "dirname" {
|
||||
s.push(opt.separator.transform(cx.cwd()));
|
||||
s.push(opt.separator.transform(&cx.cwd().os_str()));
|
||||
}
|
||||
|
||||
futures::executor::block_on(CLIPBOARD.set(s));
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ impl Actor for ToggleAll {
|
|||
Some(true) => Right((vec![], opt.urls)),
|
||||
Some(false) if opt.urls.is_empty() => Left((it.collect(), vec![])),
|
||||
Some(false) => Right((opt.urls, vec![])),
|
||||
None if opt.urls.is_empty() => Left(it.partition(|&u| tab.selected.contains_key(u))),
|
||||
None => Right(opt.urls.into_iter().partition(|u| tab.selected.contains_key(u))),
|
||||
None if opt.urls.is_empty() => Left(it.partition(|&u| tab.selected.contains(u))),
|
||||
None => Right(opt.urls.into_iter().partition(|u| tab.selected.contains(u))),
|
||||
};
|
||||
|
||||
let warn = match either {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use yazi_core::mgr::Yanked;
|
||||
use yazi_macro::{act, render};
|
||||
use yazi_parser::mgr::YankOpt;
|
||||
use yazi_shared::event::Data;
|
||||
use yazi_shared::{event::Data, url::CovUrl};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -16,7 +16,8 @@ impl Actor for Yank {
|
|||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
cx.mgr.yanked = Yanked::new(opt.cut, cx.tab().selected_or_hovered().cloned().collect());
|
||||
cx.mgr.yanked =
|
||||
Yanked::new(opt.cut, cx.tab().selected_or_hovered().cloned().map(CovUrl).collect());
|
||||
render!(cx.mgr.yanked.catchup_revision(true));
|
||||
|
||||
act!(mgr:escape_select, cx)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_shared::url::Scheme;
|
||||
use yazi_shared::url::{CovUrl, Scheme};
|
||||
|
||||
use crate::{Urn, cached_field};
|
||||
|
||||
|
|
@ -41,6 +41,10 @@ impl<'a> From<&'a Url> for Cow<'a, yazi_shared::url::Url> {
|
|||
fn from(value: &'a Url) -> Self { Cow::Borrowed(&value.inner) }
|
||||
}
|
||||
|
||||
impl From<Url> for yazi_shared::url::CovUrl {
|
||||
fn from(value: Url) -> Self { CovUrl(value.inner) }
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Url {
|
||||
type Error = mlua::Error;
|
||||
|
||||
|
|
@ -159,10 +163,10 @@ impl UserData for Url {
|
|||
|
||||
methods.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me.inner == other.inner));
|
||||
methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
||||
lua.create_string(me.as_os_str().as_encoded_bytes())
|
||||
lua.create_string(me.os_str().as_encoded_bytes())
|
||||
});
|
||||
methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| {
|
||||
lua.create_string([lhs.as_os_str().as_encoded_bytes(), &rhs.as_bytes()].concat())
|
||||
lua.create_string([lhs.os_str().as_encoded_bytes(), &rhs.as_bytes()].concat())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,28 +3,28 @@ use std::{collections::HashSet, ops::Deref};
|
|||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_macro::err;
|
||||
use yazi_shared::url::Url;
|
||||
use yazi_shared::url::{CovUrl, Url};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Yanked {
|
||||
pub cut: bool,
|
||||
urls: HashSet<Url>,
|
||||
urls: HashSet<CovUrl>,
|
||||
|
||||
version: u64,
|
||||
revision: u64,
|
||||
}
|
||||
|
||||
impl Deref for Yanked {
|
||||
type Target = HashSet<Url>;
|
||||
type Target = HashSet<CovUrl>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.urls }
|
||||
}
|
||||
|
||||
impl Yanked {
|
||||
pub fn new(cut: bool, urls: HashSet<Url>) -> Self { Self { cut, urls, ..Default::default() } }
|
||||
pub fn new(cut: bool, urls: HashSet<CovUrl>) -> Self { Self { cut, urls, ..Default::default() } }
|
||||
|
||||
pub fn remove(&mut self, url: &Url) {
|
||||
if self.urls.remove(url) {
|
||||
if self.urls.remove(CovUrl::new(url)) {
|
||||
self.revision += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,11 +38,15 @@ impl Yanked {
|
|||
self.revision += 1;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, url: impl AsRef<Url>) -> bool { self.urls.contains(CovUrl::new(&url)) }
|
||||
|
||||
pub fn contains_in(&self, dir: &Url) -> bool {
|
||||
self.urls.iter().any(|u| {
|
||||
let mut it = u.components();
|
||||
// FIXME
|
||||
it.next_back().is_some() && it == dir.components() && u.parent_url().as_ref() == Some(dir)
|
||||
it.next_back().is_some()
|
||||
&& it.covariant(&dir.components())
|
||||
&& u.parent_url().is_some_and(|p| p == *dir)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +60,7 @@ impl Yanked {
|
|||
|
||||
if !addition.is_empty() {
|
||||
let old = self.urls.len();
|
||||
self.urls.extend(addition);
|
||||
self.urls.extend(addition.into_iter().map(CovUrl));
|
||||
self.revision += (old != self.urls.len()) as u64;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,29 @@ use std::{collections::HashMap, ops::Deref};
|
|||
|
||||
use indexmap::IndexMap;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_shared::{timestamp_us, url::Url};
|
||||
use yazi_shared::{timestamp_us, url::{CovUrl, Url}};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Selected {
|
||||
inner: IndexMap<Url, u64>,
|
||||
parents: HashMap<Url, usize>,
|
||||
}
|
||||
|
||||
impl Deref for Selected {
|
||||
type Target = IndexMap<Url, u64>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
inner: IndexMap<CovUrl, u64>,
|
||||
parents: HashMap<CovUrl, usize>,
|
||||
}
|
||||
|
||||
impl Selected {
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize { self.inner.len() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
|
||||
|
||||
#[inline]
|
||||
pub fn values(&self) -> impl Iterator<Item = &Url> { self.inner.keys().map(Deref::deref) }
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, url: impl AsRef<Url>) -> bool {
|
||||
self.inner.contains_key(CovUrl::new(&url))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add(&mut self, url: &Url) -> bool { self.add_same(&[url]) == 1 }
|
||||
|
||||
|
|
@ -33,7 +41,7 @@ impl Selected {
|
|||
fn add_same(&mut self, urls: &[impl AsRef<Url>]) -> usize {
|
||||
// If it has appeared as a parent
|
||||
let urls: Vec<_> =
|
||||
urls.iter().map(|u| u.as_ref()).filter(|&u| !self.parents.contains_key(u)).collect();
|
||||
urls.iter().map(CovUrl::new).filter(|&u| !self.parents.contains_key(u)).collect();
|
||||
if urls.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -79,12 +87,12 @@ impl Selected {
|
|||
}
|
||||
|
||||
fn remove_same(&mut self, urls: &[impl AsRef<Url>]) -> usize {
|
||||
let count = urls.iter().filter_map(|u| self.inner.swap_remove(u.as_ref())).count();
|
||||
let count = urls.iter().filter_map(|u| self.inner.swap_remove(CovUrl::new(u))).count();
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut parent = urls[0].as_ref().parent_url();
|
||||
let mut parent = CovUrl::new(&urls[0]).parent_url();
|
||||
while let Some(u) = parent {
|
||||
let n = self.parents.get_mut(&u).unwrap();
|
||||
|
||||
|
|
@ -104,7 +112,7 @@ impl Selected {
|
|||
}
|
||||
|
||||
pub fn apply_op(&mut self, op: &FilesOp) {
|
||||
let (removal, addition) = op.diff_recoverable(|u| self.contains_key(u));
|
||||
let (removal, addition) = op.diff_recoverable(|u| self.contains(u));
|
||||
if !removal.is_empty() {
|
||||
self.remove_many(&removal);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ impl Tab {
|
|||
if self.selected.is_empty() {
|
||||
Box::new(self.hovered().map(|h| &h.url).into_iter())
|
||||
} else {
|
||||
Box::new(self.selected.keys())
|
||||
Box::new(self.selected.values())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ impl Tab {
|
|||
if self.selected.is_empty() {
|
||||
Box::new([&h.url, &h.url].into_iter())
|
||||
} else {
|
||||
Box::new([&h.url].into_iter().chain(self.selected.keys()))
|
||||
Box::new([&h.url].into_iter().chain(self.selected.values()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use tracing::debug;
|
||||
use yazi_shared::url::Url;
|
||||
use yazi_shared::url::{CovUrl, Url};
|
||||
|
||||
use super::Tasks;
|
||||
|
||||
impl Tasks {
|
||||
pub fn file_cut(&self, src: &[&Url], dest: &Url, force: bool) {
|
||||
pub fn file_cut(&self, src: &[&CovUrl], dest: &Url, force: bool) {
|
||||
for &u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_cut: same file, skipping {:?}", to);
|
||||
} else {
|
||||
self.scheduler.file_cut(u.clone(), to, force);
|
||||
self.scheduler.file_cut(u.0.clone(), to, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_copy(&self, src: &[&Url], dest: &Url, force: bool, follow: bool) {
|
||||
pub fn file_copy(&self, src: &[&CovUrl], dest: &Url, force: bool, follow: bool) {
|
||||
for &u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_copy: same file, skipping {:?}", to);
|
||||
} else {
|
||||
self.scheduler.file_copy(u.clone(), to, force, follow);
|
||||
self.scheduler.file_copy(u.0.clone(), to, force, follow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_link(&self, src: &HashSet<Url>, dest: &Url, relative: bool, force: bool) {
|
||||
pub fn file_link(&self, src: &HashSet<CovUrl>, dest: &Url, relative: bool, force: bool) {
|
||||
for u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_link: same file, skipping {:?}", to);
|
||||
} else {
|
||||
self.scheduler.file_link(u.clone(), to, relative, force);
|
||||
self.scheduler.file_link(u.0.clone(), to, relative, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_hardlink(&self, src: &HashSet<Url>, dest: &Url, force: bool, follow: bool) {
|
||||
pub fn file_hardlink(&self, src: &HashSet<CovUrl>, dest: &Url, force: bool, follow: bool) {
|
||||
for u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_hardlink: same file, skipping {:?}", to);
|
||||
} else {
|
||||
self.scheduler.file_hardlink(u.clone(), to, force, follow);
|
||||
self.scheduler.file_hardlink(u.0.clone(), to, force, follow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::HashSet};
|
|||
use mlua::{IntoLua, Lua, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_parser::mgr::UpdateYankedOpt;
|
||||
use yazi_shared::url::Url;
|
||||
use yazi_shared::url::CovUrl;
|
||||
|
||||
use super::Body;
|
||||
|
||||
|
|
@ -11,13 +11,13 @@ use super::Body;
|
|||
pub struct BodyYank<'a>(UpdateYankedOpt<'a>);
|
||||
|
||||
impl<'a> BodyYank<'a> {
|
||||
pub fn borrowed(cut: bool, urls: &'a HashSet<Url>) -> Body<'a> {
|
||||
pub fn borrowed(cut: bool, urls: &'a HashSet<CovUrl>) -> Body<'a> {
|
||||
Self(UpdateYankedOpt { cut, urls: Cow::Borrowed(urls) }).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyYank<'static> {
|
||||
pub fn owned(cut: bool, _: &HashSet<Url>) -> Body<'static> {
|
||||
pub fn owned(cut: bool, _: &HashSet<CovUrl>) -> Body<'static> {
|
||||
Self(UpdateYankedOpt { cut, urls: Default::default() }).into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use mlua::Function;
|
|||
use parking_lot::RwLock;
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::FolderStage;
|
||||
use yazi_shared::{Id, RoCell, url::Url};
|
||||
use yazi_shared::{Id, RoCell, url::{CovUrl, Url}};
|
||||
|
||||
use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyHi, BodyMoveItem}};
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ impl Pubsub {
|
|||
|
||||
pub_after!(rename(tab: Id, from: &Url, to: &Url), (tab, from, to));
|
||||
|
||||
pub_after!(@yank(cut: bool, urls: &HashSet<Url>), (cut, urls));
|
||||
pub_after!(@yank(cut: bool, urls: &HashSet<CovUrl>), (cut, urls));
|
||||
|
||||
pub_after!(move(items: Vec<BodyMoveItem>), (&items), (items));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ use anyhow::bail;
|
|||
use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_binding::get_metatable;
|
||||
use yazi_shared::{event::CmdCow, url::Url};
|
||||
use yazi_shared::{event::CmdCow, url::CovUrl};
|
||||
|
||||
type Iter = yazi_binding::Iter<
|
||||
std::iter::Map<std::collections::hash_set::IntoIter<Url>, fn(Url) -> yazi_binding::Url>,
|
||||
std::iter::Map<std::collections::hash_set::IntoIter<CovUrl>, fn(CovUrl) -> yazi_binding::Url>,
|
||||
yazi_binding::Url,
|
||||
>;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct UpdateYankedOpt<'a> {
|
||||
pub cut: bool,
|
||||
pub urls: Cow<'a, HashSet<Url>>,
|
||||
pub urls: Cow<'a, HashSet<CovUrl>>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for UpdateYankedOpt<'_> {
|
||||
|
|
|
|||
|
|
@ -74,13 +74,24 @@ impl<'a> Components<'a> {
|
|||
}
|
||||
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let path = self.inner.as_path();
|
||||
if !self.scheme.is_virtual() || self.scheme_yielded {
|
||||
return Cow::Borrowed(self.inner.as_path().as_os_str());
|
||||
return path.as_os_str().into();
|
||||
}
|
||||
|
||||
let mut oss = OsString::from(format!("{}", self.scheme));
|
||||
oss.push(self.inner.as_path());
|
||||
Cow::Owned(oss)
|
||||
let mut s = OsString::from(format!("{}", self.scheme));
|
||||
s.reserve_exact(path.as_os_str().len());
|
||||
s.push(path);
|
||||
s.into()
|
||||
}
|
||||
|
||||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
match (self.scheme_yielded, other.scheme_yielded) {
|
||||
(false, false) => {}
|
||||
(true, true) if self.scheme.covariant(other.scheme) => {}
|
||||
_ => return false,
|
||||
}
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
54
yazi-shared/src/url/cov.rs
Normal file
54
yazi-shared/src/url/cov.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use std::{hash::{Hash, Hasher}, ops::Deref};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Serialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct CovUrl(pub Url);
|
||||
|
||||
impl Deref for CovUrl {
|
||||
type Target = Url;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl AsRef<Url> for CovUrl {
|
||||
fn as_ref(&self) -> &Url { &self.0 }
|
||||
}
|
||||
|
||||
impl From<Url> for CovUrl {
|
||||
fn from(value: Url) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl From<CovUrl> for Url {
|
||||
fn from(value: CovUrl) -> Self { value.0 }
|
||||
}
|
||||
|
||||
impl Hash for CovUrl {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.loc.hash(state);
|
||||
if self.scheme.is_virtual() {
|
||||
self.scheme.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for CovUrl {
|
||||
fn eq(&self, other: &Self) -> bool { self.covariant(other) }
|
||||
}
|
||||
|
||||
impl PartialEq<Url> for CovUrl {
|
||||
fn eq(&self, other: &Url) -> bool { self.covariant(other) }
|
||||
}
|
||||
|
||||
impl CovUrl {
|
||||
#[inline]
|
||||
pub fn new<T: AsRef<Url>>(u: &T) -> &Self {
|
||||
unsafe { &*(u.as_ref() as *const Url as *const Self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parent_url(&self) -> Option<CovUrl> { self.0.parent_url().map(CovUrl) }
|
||||
}
|
||||
|
|
@ -15,6 +15,10 @@ impl Deref for Loc {
|
|||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Loc {
|
||||
fn as_ref(&self) -> &Path { &self.inner }
|
||||
}
|
||||
|
||||
impl PartialEq for Loc {
|
||||
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(component display loc scheme url urn);
|
||||
yazi_macro::mod_flat!(component cov display loc scheme url urn);
|
||||
|
|
|
|||
|
|
@ -47,17 +47,9 @@ impl Scheme {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
// Local files
|
||||
(
|
||||
Self::Regular | Self::Search(_) | Self::SearchItem,
|
||||
Self::Regular | Self::Search(_) | Self::SearchItem,
|
||||
) => true,
|
||||
|
||||
// Virtual files within the same namespace
|
||||
(a, b) => a == b,
|
||||
}
|
||||
if self.is_virtual() || other.is_virtual() { self == other } else { true }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
||||
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, ops::Deref, path::{Path, PathBuf}};
|
||||
|
||||
use percent_encoding::percent_decode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use super::UrnBuf;
|
||||
use crate::{IntoOsStr, url::{Components, Display, Loc, Scheme}};
|
||||
|
||||
#[derive(Clone, Default, Eq, Ord, PartialOrd)]
|
||||
#[derive(Clone, Default, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
||||
pub struct Url {
|
||||
pub loc: Loc,
|
||||
pub scheme: Scheme,
|
||||
|
|
@ -18,19 +18,6 @@ impl Deref for Url {
|
|||
fn deref(&self) -> &Self::Target { &self.loc }
|
||||
}
|
||||
|
||||
impl Debug for Url {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { scheme, loc } = self;
|
||||
match scheme {
|
||||
Scheme::Regular => write!(f, "{scheme}{}", loc.display()),
|
||||
Scheme::Search(_) => write!(f, "{scheme}{}", loc.display()),
|
||||
Scheme::SearchItem => write!(f, "{scheme}{}", loc.display()),
|
||||
Scheme::Archive(_) => write!(f, "{scheme}{}", loc.display()),
|
||||
Scheme::Sftp(_) => write!(f, "{scheme}{}", loc.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Loc> for Url {
|
||||
fn from(loc: Loc) -> Self { Self { loc, scheme: Scheme::Regular } }
|
||||
}
|
||||
|
|
@ -165,7 +152,7 @@ impl Url {
|
|||
}
|
||||
|
||||
Some(Self {
|
||||
loc: self.loc.strip_prefix(base.loc.as_path()).ok()?.into(),
|
||||
loc: self.loc.strip_prefix(&base.loc).ok()?.into(),
|
||||
scheme: self.scheme.clone(),
|
||||
})
|
||||
}
|
||||
|
|
@ -229,36 +216,9 @@ impl Url {
|
|||
pub fn into_path(self) -> PathBuf { self.loc.into_path() }
|
||||
}
|
||||
|
||||
impl Hash for Url {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.loc.hash(state);
|
||||
|
||||
match &self.scheme {
|
||||
Scheme::Regular => {}
|
||||
Scheme::Search(_) => {
|
||||
self.scheme.hash(state);
|
||||
}
|
||||
Scheme::SearchItem => {}
|
||||
Scheme::Archive(_) => {
|
||||
self.scheme.hash(state);
|
||||
}
|
||||
Scheme::Sftp(_) => {
|
||||
self.scheme.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Url {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.loc != other.loc {
|
||||
return false;
|
||||
}
|
||||
|
||||
match (&self.scheme, &other.scheme) {
|
||||
(Scheme::Regular | Scheme::SearchItem, Scheme::Regular | Scheme::SearchItem) => true,
|
||||
_ => self.scheme == other.scheme,
|
||||
}
|
||||
impl Debug for Url {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", self.scheme, self.loc.display())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue