diff --git a/yazi-actor/src/mgr/yank.rs b/yazi-actor/src/mgr/yank.rs index 2a96ec7f..a68368b8 100644 --- a/yazi-actor/src/mgr/yank.rs +++ b/yazi-actor/src/mgr/yank.rs @@ -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 { 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) diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index 4c8a600a..95a82ad6 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -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 for yazi_shared::url::CovUrl { + fn from(value: Url) -> Self { CovUrl(value.inner) } +} + impl TryFrom<&[u8]> for Url { type Error = mlua::Error; diff --git a/yazi-core/src/mgr/yanked.rs b/yazi-core/src/mgr/yanked.rs index e585ceff..e585419b 100644 --- a/yazi-core/src/mgr/yanked.rs +++ b/yazi-core/src/mgr/yanked.rs @@ -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, + urls: HashSet, version: u64, revision: u64, } impl Deref for Yanked { - type Target = HashSet; + type Target = HashSet; fn deref(&self) -> &Self::Target { &self.urls } } impl Yanked { - pub fn new(cut: bool, urls: HashSet) -> Self { Self { cut, urls, ..Default::default() } } + pub fn new(cut: bool, urls: HashSet) -> 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) -> 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; } } diff --git a/yazi-core/src/tasks/file.rs b/yazi-core/src/tasks/file.rs index 65bff641..27441aad 100644 --- a/yazi-core/src/tasks/file.rs +++ b/yazi-core/src/tasks/file.rs @@ -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, dest: &Url, relative: bool, force: bool) { + pub fn file_link(&self, src: &HashSet, 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, dest: &Url, force: bool, follow: bool) { + pub fn file_hardlink(&self, src: &HashSet, 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); } } } diff --git a/yazi-dds/src/body/yank.rs b/yazi-dds/src/body/yank.rs index 3f1f443e..245a394e 100644 --- a/yazi-dds/src/body/yank.rs +++ b/yazi-dds/src/body/yank.rs @@ -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) -> Body<'a> { + pub fn borrowed(cut: bool, urls: &'a HashSet) -> Body<'a> { Self(UpdateYankedOpt { cut, urls: Cow::Borrowed(urls) }).into() } } impl BodyYank<'static> { - pub fn owned(cut: bool, _: &HashSet) -> Body<'static> { + pub fn owned(cut: bool, _: &HashSet) -> Body<'static> { Self(UpdateYankedOpt { cut, urls: Default::default() }).into() } } diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index e2db0362..a9f3795d 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -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), (cut, urls)); + pub_after!(@yank(cut: bool, urls: &HashSet), (cut, urls)); pub_after!(move(items: Vec), (&items), (items)); diff --git a/yazi-parser/src/mgr/update_yanked.rs b/yazi-parser/src/mgr/update_yanked.rs index d1f60874..0d303ce5 100644 --- a/yazi-parser/src/mgr/update_yanked.rs +++ b/yazi-parser/src/mgr/update_yanked.rs @@ -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, fn(Url) -> yazi_binding::Url>, + std::iter::Map, 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>, + pub urls: Cow<'a, HashSet>, } impl TryFrom for UpdateYankedOpt<'_> { diff --git a/yazi-shared/src/url/component.rs b/yazi-shared/src/url/component.rs index 4fcd3058..dc5d10f5 100644 --- a/yazi-shared/src/url/component.rs +++ b/yazi-shared/src/url/component.rs @@ -84,6 +84,15 @@ impl<'a> Components<'a> { 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 + } } impl<'a> Iterator for Components<'a> { diff --git a/yazi-shared/src/url/cov.rs b/yazi-shared/src/url/cov.rs index 0a9f51ab..2f33f0a3 100644 --- a/yazi-shared/src/url/cov.rs +++ b/yazi-shared/src/url/cov.rs @@ -1,8 +1,10 @@ use std::{hash::{Hash, Hasher}, ops::Deref}; +use serde::{Deserialize, Serialize}; + use crate::url::Url; -#[derive(Clone, Debug, Eq)] +#[derive(Clone, Debug, Deserialize, Eq, Serialize)] #[repr(transparent)] pub struct CovUrl(pub Url); @@ -12,6 +14,18 @@ impl Deref for CovUrl { fn deref(&self) -> &Self::Target { &self.0 } } +impl AsRef for CovUrl { + fn as_ref(&self) -> &Url { &self.0 } +} + +impl From for CovUrl { + fn from(value: Url) -> Self { Self(value) } +} + +impl From for Url { + fn from(value: CovUrl) -> Self { value.0 } +} + impl Hash for CovUrl { fn hash(&self, state: &mut H) { self.loc.hash(state); @@ -25,6 +39,10 @@ impl PartialEq for CovUrl { fn eq(&self, other: &Self) -> bool { self.covariant(other) } } +impl PartialEq for CovUrl { + fn eq(&self, other: &Url) -> bool { self.covariant(other) } +} + impl CovUrl { #[inline] pub fn new>(u: &T) -> &Self {