From a8f2498471f4b300e2cab2b1f694a8d82fb332fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 29 Nov 2025 05:27:17 +0800 Subject: [PATCH] feat: new `stash` command to let users customize `back` and `forward` command behavior (#3380) --- yazi-actor/src/context.rs | 10 +- yazi-actor/src/mgr/back.rs | 2 +- yazi-actor/src/mgr/cd.rs | 18 +- yazi-actor/src/mgr/displace.rs | 59 ++++ yazi-actor/src/mgr/forward.rs | 6 +- yazi-actor/src/mgr/mod.rs | 2 + yazi-actor/src/mgr/search.rs | 15 +- yazi-actor/src/mgr/stash.rs | 31 ++ yazi-binding/src/url.rs | 4 + yazi-config/src/which/sorting.rs | 2 +- yazi-dds/src/ember/ember.rs | 6 +- yazi-dds/src/spark/kind.rs | 4 + yazi-dds/src/spark/spark.rs | 10 + yazi-fm/src/executor.rs | 1 + yazi-fs/src/path/expand.rs | 6 +- yazi-parser/src/mgr/cd.rs | 11 +- yazi-parser/src/mgr/displace_do.rs | 29 ++ yazi-parser/src/mgr/mod.rs | 2 + yazi-parser/src/mgr/stash.rs | 44 +++ yazi-parser/src/void.rs | 6 +- yazi-plugin/preset/plugins/mime-local.lua | 2 +- yazi-proxy/src/mgr.rs | 40 +-- yazi-shared/src/data/data.rs | 337 +++++++++++++++++++--- yazi-shared/src/event/cmd.rs | 8 + yazi-shared/src/event/cow.rs | 12 + yazi-shared/src/loc/able.rs | 6 + yazi-shared/src/loc/buf.rs | 5 +- yazi-shared/src/loc/loc.rs | 233 +++++++-------- yazi-shared/src/path/cow.rs | 1 + yazi-shared/src/path/path.rs | 4 +- yazi-shared/src/source.rs | 10 +- yazi-shared/src/url/buf.rs | 14 +- 32 files changed, 702 insertions(+), 238 deletions(-) create mode 100644 yazi-actor/src/mgr/displace.rs create mode 100644 yazi-actor/src/mgr/stash.rs create mode 100644 yazi-parser/src/mgr/displace_do.rs create mode 100644 yazi-parser/src/mgr/stash.rs diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs index 9361ae92..259c3160 100644 --- a/yazi-actor/src/context.rs +++ b/yazi-actor/src/context.rs @@ -118,13 +118,5 @@ impl<'a> Ctx<'a> { pub fn tasks(&self) -> &Tasks { &self.tasks } #[inline] - pub fn source(&self) -> Source { - use Source::*; - match self.source { - Key if self.level != 1 => Ind, - Emit if self.level != 1 => EmitInd, - Relay if self.level != 1 => RelayInd, - s => s, - } - } + pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } } } diff --git a/yazi-actor/src/mgr/back.rs b/yazi-actor/src/mgr/back.rs index 95f744f3..b8ba672d 100644 --- a/yazi-actor/src/mgr/back.rs +++ b/yazi-actor/src/mgr/back.rs @@ -14,7 +14,7 @@ impl Actor for Back { fn act(cx: &mut Ctx, _: Self::Options) -> Result { if let Some(u) = cx.tab_mut().backstack.shift_backward().cloned() { - return act!(mgr:cd, cx, (u, CdSource::Back)); + act!(mgr:cd, cx, (u, CdSource::Back))?; } succ!(); } diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs index 73003575..244c8e22 100644 --- a/yazi-actor/src/mgr/cd.rs +++ b/yazi-actor/src/mgr/cd.rs @@ -9,7 +9,7 @@ use yazi_fs::{File, FilesOp, path::expand_url}; use yazi_macro::{act, err, render, succ}; use yazi_parser::mgr::CdOpt; use yazi_proxy::{CmpProxy, InputProxy, MgrProxy}; -use yazi_shared::{Debounce, data::Data, errors::InputError, url::{AsUrl, UrlBuf, UrlLike}}; +use yazi_shared::{Debounce, data::Data, errors::InputError, url::{UrlBuf, UrlLike}}; use yazi_vfs::VfsFile; use crate::{Actor, Ctx}; @@ -33,18 +33,8 @@ impl Actor for Cd { } // Take parent to history - if let Some(rep) = tab.parent.take() { - tab.history.insert(rep.url.to_owned(), rep); - } - - // Backstack - if opt.source.big_jump() { - if tab.current.url.is_regular() { - tab.backstack.push(tab.current.url.as_url()); - } - if opt.target.is_regular() { - tab.backstack.push(opt.target.as_url()); - } + if let Some(t) = tab.parent.take() { + tab.history.insert(t.url.to_owned(), t); } // Current @@ -58,10 +48,12 @@ impl Actor for Cd { } err!(Pubsub::pub_after_cd(tab.id, tab.cwd())); + act!(mgr:displace, cx)?; act!(mgr:hidden, cx)?; act!(mgr:sort, cx)?; act!(mgr:hover, cx)?; act!(mgr:refresh, cx)?; + act!(mgr:stash, cx, opt).ok(); succ!(render!()); } } diff --git a/yazi-actor/src/mgr/displace.rs b/yazi-actor/src/mgr/displace.rs new file mode 100644 index 00000000..a23f1f68 --- /dev/null +++ b/yazi-actor/src/mgr/displace.rs @@ -0,0 +1,59 @@ +use anyhow::{Result, bail}; +use yazi_macro::{act, succ}; +use yazi_parser::{VoidOpt, mgr::{CdSource, DisplaceDoOpt}}; +use yazi_proxy::MgrProxy; +use yazi_shared::{data::Data, url::UrlLike}; +use yazi_vfs::provider; + +use crate::{Actor, Ctx}; + +pub struct Displace; + +impl Actor for Displace { + type Options = VoidOpt; + + const NAME: &str = "displace"; + + fn act(cx: &mut Ctx, _: Self::Options) -> Result { + if cx.cwd().is_absolute() { + succ!(); + } + + let tab = cx.tab().id; + let from = cx.cwd().to_owned(); + tokio::spawn(async move { + if let Ok(to) = provider::absolute(&from).await + && to.is_owned() + { + MgrProxy::displace_do(tab, DisplaceDoOpt { to: to.into(), from }); + } + }); + + succ!(); + } +} + +// --- Do +pub struct DisplaceDo; + +impl Actor for DisplaceDo { + type Options = DisplaceDoOpt; + + const NAME: &str = "displace_do"; + + fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + if !opt.to.is_absolute() { + bail!("Target URL must be absolute"); + } + + if cx.cwd() != opt.from { + succ!() + } else if let Some(hovered) = cx.hovered() + && let Ok(url) = opt.to.try_join(hovered.urn()) + { + act!(mgr:reveal, cx, (url, CdSource::Displace)) + } else { + act!(mgr:cd, cx, (opt.to, CdSource::Displace)) + } + } +} diff --git a/yazi-actor/src/mgr/forward.rs b/yazi-actor/src/mgr/forward.rs index a6642df5..b0a10732 100644 --- a/yazi-actor/src/mgr/forward.rs +++ b/yazi-actor/src/mgr/forward.rs @@ -13,9 +13,9 @@ impl Actor for Forward { const NAME: &str = "forward"; fn act(cx: &mut Ctx, _: Self::Options) -> Result { - match cx.tab_mut().backstack.shift_forward().cloned() { - Some(u) => act!(mgr:cd, cx, (u, CdSource::Forward)), - None => succ!(), + if let Some(u) = cx.tab_mut().backstack.shift_forward().cloned() { + act!(mgr:cd, cx, (u, CdSource::Forward))?; } + succ!() } } diff --git a/yazi-actor/src/mgr/mod.rs b/yazi-actor/src/mgr/mod.rs index 29842efb..68c0a218 100644 --- a/yazi-actor/src/mgr/mod.rs +++ b/yazi-actor/src/mgr/mod.rs @@ -6,6 +6,7 @@ yazi_macro::mod_flat!( close copy create + displace download enter escape @@ -36,6 +37,7 @@ yazi_macro::mod_flat!( shell sort spot + stash suspend tab_close tab_create diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs index 33850f03..b559f3e4 100644 --- a/yazi-actor/src/mgr/search.rs +++ b/yazi-actor/src/mgr/search.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem, time::Duration}; +use std::{borrow::Cow, time::Duration}; use anyhow::Result; use tokio::pin; @@ -6,7 +6,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; use yazi_fs::{FilesOp, cha::Cha}; use yazi_macro::{act, succ}; -use yazi_parser::{VoidOpt, mgr::{SearchOpt, SearchOptVia}}; +use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}}; use yazi_plugin::external; use yazi_proxy::{InputProxy, MgrProxy}; use yazi_shared::data::Data; @@ -107,15 +107,10 @@ impl Actor for SearchStop { handle.abort(); } - if !tab.cwd().is_search() { - succ!(); + if tab.cwd().is_search() { + act!(mgr:cd, cx, (tab.cwd().to_regular()?, CdSource::Escape))?; } - let rep = tab.history.remove_or(tab.cwd().to_regular()?); - drop(mem::replace(&mut tab.current, rep)); - - act!(mgr:hidden, cx)?; - act!(mgr:sort, cx)?; - act!(mgr:refresh, cx) + succ!(); } } diff --git a/yazi-actor/src/mgr/stash.rs b/yazi-actor/src/mgr/stash.rs new file mode 100644 index 00000000..eb1bd502 --- /dev/null +++ b/yazi-actor/src/mgr/stash.rs @@ -0,0 +1,31 @@ +use anyhow::Result; +use yazi_dds::spark::SparkKind; +use yazi_macro::succ; +use yazi_parser::mgr::StashOpt; +use yazi_shared::{Source, data::Data, url::AsUrl}; + +use crate::{Actor, Ctx}; + +pub struct Stash; + +impl Actor for Stash { + type Options = StashOpt; + + const NAME: &str = "stash"; + + fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + if opt.target.is_regular() { + cx.tab_mut().backstack.push(opt.target.as_url()); + } + + succ!() + } + + fn hook(cx: &Ctx, _opt: &Self::Options) -> Option { + match cx.source() { + Source::Relay => Some(SparkKind::RelayStash), + Source::Ind => Some(SparkKind::IndStash), + _ => None, + } + } +} diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index 9def6a38..06f3d7b5 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -49,6 +49,10 @@ impl From for yazi_shared::url::UrlBufCov { fn from(value: Url) -> Self { Self(value.inner) } } +impl From for yazi_shared::url::UrlCow<'_> { + fn from(value: Url) -> Self { value.inner.into() } +} + impl TryFrom<&[u8]> for Url { type Error = mlua::Error; diff --git a/yazi-config/src/which/sorting.rs b/yazi-config/src/which/sorting.rs index 2a4c475a..8c81be8b 100644 --- a/yazi-config/src/which/sorting.rs +++ b/yazi-config/src/which/sorting.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum SortBy { #[default] diff --git a/yazi-dds/src/ember/ember.rs b/yazi-dds/src/ember/ember.rs index b71d7d6a..477acb07 100644 --- a/yazi-dds/src/ember/ember.rs +++ b/yazi-dds/src/ember/ember.rs @@ -67,12 +67,10 @@ impl Ember<'static> { | "trash" | "delete" | "mount" - ) || kind.starts_with("emit-") - || kind.starts_with("emit-ind-") + ) || kind.starts_with("key-") || kind.starts_with("ind-") - || kind.starts_with("key-") + || kind.starts_with("emit-") || kind.starts_with("relay-") - || kind.starts_with("relay-ind-") { bail!("Cannot construct system event"); } diff --git a/yazi-dds/src/spark/kind.rs b/yazi-dds/src/spark/kind.rs index 6d37a9e8..3cefc874 100644 --- a/yazi-dds/src/spark/kind.rs +++ b/yazi-dds/src/spark/kind.rs @@ -2,13 +2,17 @@ use std::fmt::Display; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SparkKind { + IndStash, KeyQuit, + RelayStash, } impl AsRef for SparkKind { fn as_ref(&self) -> &str { match self { + Self::IndStash => "ind-stash", Self::KeyQuit => "key-quit", + Self::RelayStash => "relay-stash", } } } diff --git a/yazi-dds/src/spark/spark.rs b/yazi-dds/src/spark/spark.rs index 992f58c9..1eaf6e24 100644 --- a/yazi-dds/src/spark/spark.rs +++ b/yazi-dds/src/spark/spark.rs @@ -15,6 +15,8 @@ pub enum Spark<'a> { Close(yazi_parser::mgr::CloseOpt), Copy(yazi_parser::mgr::CopyOpt), Create(yazi_parser::mgr::CreateOpt), + Displace(yazi_parser::VoidOpt), + DisplaceDo(yazi_parser::mgr::DisplaceDoOpt), Download(yazi_parser::mgr::DownloadOpt), Enter(yazi_parser::VoidOpt), Escape(yazi_parser::mgr::EscapeOpt), @@ -53,6 +55,7 @@ pub enum Spark<'a> { Shell(yazi_parser::mgr::ShellOpt), Sort(yazi_parser::mgr::SortOpt), Spot(yazi_parser::mgr::SpotOpt), + Stash(yazi_parser::mgr::StashOpt), Suspend(yazi_parser::VoidOpt), TabClose(yazi_parser::mgr::TabCloseOpt), TabCreate(yazi_parser::mgr::TabCreateOpt), @@ -119,7 +122,9 @@ pub enum Spark<'a> { impl<'a> Spark<'a> { pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result { Ok(match kind { + SparkKind::IndStash => Self::Stash(<_>::from_lua(value, lua)?), SparkKind::KeyQuit => Self::Quit(<_>::from_lua(value, lua)?), + SparkKind::RelayStash => Self::Stash(<_>::from_lua(value, lua)?), }) } } @@ -138,6 +143,8 @@ impl<'a> IntoLua for Spark<'a> { Self::Close(b) => b.into_lua(lua), Self::Copy(b) => b.into_lua(lua), Self::Create(b) => b.into_lua(lua), + Self::Displace(b) => b.into_lua(lua), + Self::DisplaceDo(b) => b.into_lua(lua), Self::Download(b) => b.into_lua(lua), Self::Enter(b) => b.into_lua(lua), Self::Escape(b) => b.into_lua(lua), @@ -176,6 +183,7 @@ impl<'a> IntoLua for Spark<'a> { Self::Shell(b) => b.into_lua(lua), Self::Sort(b) => b.into_lua(lua), Self::Spot(b) => b.into_lua(lua), + Self::Stash(b) => b.into_lua(lua), Self::Suspend(b) => b.into_lua(lua), Self::TabClose(b) => b.into_lua(lua), Self::TabCreate(b) => b.into_lua(lua), @@ -282,6 +290,7 @@ try_from_spark!(mgr::CdOpt, mgr:cd); try_from_spark!(mgr::CloseOpt, mgr:close); try_from_spark!(mgr::CopyOpt, mgr:copy); try_from_spark!(mgr::CreateOpt, mgr:create); +try_from_spark!(mgr::DisplaceDoOpt, mgr:displace_do); try_from_spark!(mgr::DownloadOpt, mgr:download); try_from_spark!(mgr::EscapeOpt, mgr:escape); try_from_spark!(mgr::FilterOpt, mgr:filter, mgr:filter_do); @@ -306,6 +315,7 @@ try_from_spark!(mgr::SeekOpt, mgr:seek); try_from_spark!(mgr::ShellOpt, mgr:shell); try_from_spark!(mgr::SortOpt, mgr:sort); try_from_spark!(mgr::SpotOpt, mgr:spot); +try_from_spark!(mgr::StashOpt, mgr:stash); try_from_spark!(mgr::TabCloseOpt, mgr:tab_close); try_from_spark!(mgr::TabCreateOpt, mgr:tab_create); try_from_spark!(mgr::TabSwitchOpt, mgr:tab_switch); diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 60a8b2f2..70416d9b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -137,6 +137,7 @@ impl<'a> Executor<'a> { // VFS on!(download); on!(upload); + on!(displace_do); match cmd.name.as_ref() { // Help diff --git a/yazi-fs/src/path/expand.rs b/yazi-fs/src/path/expand.rs index 200f1665..4c5f2342 100644 --- a/yazi-fs/src/path/expand.rs +++ b/yazi-fs/src/path/expand.rs @@ -92,7 +92,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> { if url.has_base() { 0 } else { 2 }, if url.has_trail() { 0 } else { 2 }, ) - .expect("Failed to create Loc from drive letter") + .expect("Loc from drive letter") } else if let Ok(rest) = path.strip_prefix("~/") && let Some(home) = dirs::home_dir() && home.is_absolute() @@ -103,7 +103,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> { url.uri().components().count() + if url.has_base() { 0 } else { add }, url.urn().components().count() + if url.has_trail() { 0 } else { add }, ) - .expect("Failed to create Loc from home directory") + .expect("Loc from home directory") } else if !url.is_absolute() { let cwd = CWD.path(); LocBuf::::with( @@ -111,7 +111,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> { url.uri().components().count(), url.urn().components().count(), ) - .expect("Failed to create Loc from relative path") + .expect("Loc from relative path") } else { return url; }; diff --git a/yazi-parser/src/mgr/cd.rs b/yazi-parser/src/mgr/cd.rs index d26c3baa..ee83f606 100644 --- a/yazi-parser/src/mgr/cd.rs +++ b/yazi-parser/src/mgr/cd.rs @@ -1,4 +1,5 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::{Deserialize, Serialize}; use yazi_fs::path::expand_url; use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}}; @@ -46,7 +47,8 @@ impl IntoLua for CdOpt { } // --- Source -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] pub enum CdSource { Tab, Cd, @@ -55,9 +57,6 @@ pub enum CdSource { Leave, Forward, Back, -} - -impl CdSource { - #[inline] - pub fn big_jump(self) -> bool { self == Self::Cd || self == Self::Reveal } + Escape, + Displace, } diff --git a/yazi-parser/src/mgr/displace_do.rs b/yazi-parser/src/mgr/displace_do.rs new file mode 100644 index 00000000..49d25409 --- /dev/null +++ b/yazi-parser/src/mgr/displace_do.rs @@ -0,0 +1,29 @@ +use anyhow::bail; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_shared::{event::CmdCow, url::UrlBuf}; + +#[derive(Debug)] +pub struct DisplaceDoOpt { + pub to: UrlBuf, + pub from: UrlBuf, +} + +impl TryFrom for DisplaceDoOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + if let Some(opt) = c.take_any2("opt") { + opt + } else { + bail!("'opt' is required for DisplaceDoOpt"); + } + } +} + +impl FromLua for DisplaceDoOpt { + fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } +} + +impl IntoLua for DisplaceDoOpt { + fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } +} diff --git a/yazi-parser/src/mgr/mod.rs b/yazi-parser/src/mgr/mod.rs index caec442e..085ae19e 100644 --- a/yazi-parser/src/mgr/mod.rs +++ b/yazi-parser/src/mgr/mod.rs @@ -3,6 +3,7 @@ yazi_macro::mod_flat!( close copy create + displace_do download escape filter @@ -27,6 +28,7 @@ yazi_macro::mod_flat!( shell sort spot + stash tab_close tab_create tab_switch diff --git a/yazi-parser/src/mgr/stash.rs b/yazi-parser/src/mgr/stash.rs new file mode 100644 index 00000000..70ef52fe --- /dev/null +++ b/yazi-parser/src/mgr/stash.rs @@ -0,0 +1,44 @@ +use anyhow::bail; +use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Value}; +use serde::{Deserialize, Serialize}; +use yazi_binding::Url; +use yazi_shared::{event::CmdCow, url::UrlCow}; + +use crate::mgr::{CdOpt, CdSource}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct StashOpt { + pub target: UrlCow<'static>, + pub source: CdSource, +} + +impl TryFrom for StashOpt { + type Error = anyhow::Error; + + fn try_from(_: CmdCow) -> Result { bail!("unsupported") } +} + +impl From for StashOpt { + fn from(opt: CdOpt) -> Self { Self { target: opt.target, source: opt.source } } +} + +impl FromLua for StashOpt { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + let tbl = value.as_table().ok_or_else(|| "expected table".into_lua_err())?; + Ok(Self { + target: tbl.get::("target")?.into(), + source: lua.from_value(tbl.get("source")?)?, + }) + } +} + +impl IntoLua for StashOpt { + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua + .create_table_from([ + ("target", Url::new(self.target).into_lua(lua)?), + ("source", lua.to_value(&self.source)?), + ])? + .into_lua(lua) + } +} diff --git a/yazi-parser/src/void.rs b/yazi-parser/src/void.rs index a2fd45a1..2b9f25cf 100644 --- a/yazi-parser/src/void.rs +++ b/yazi-parser/src/void.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use mlua::{FromLua, IntoLua, Lua, Value}; use yazi_shared::event::CmdCow; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -13,9 +13,9 @@ impl From<()> for VoidOpt { } impl FromLua for VoidOpt { - fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn from_lua(_: Value, _: &Lua) -> mlua::Result { Ok(Self) } } impl IntoLua for VoidOpt { - fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn into_lua(self, lua: &Lua) -> mlua::Result { lua.create_table()?.into_lua(lua) } } diff --git a/yazi-plugin/preset/plugins/mime-local.lua b/yazi-plugin/preset/plugins/mime-local.lua index 3d8e8d18..fb50e2d2 100644 --- a/yazi-plugin/preset/plugins/mime-local.lua +++ b/yazi-plugin/preset/plugins/mime-local.lua @@ -44,7 +44,7 @@ function M:fetch(job) if file.cache then urls[i], paths[i] = tostring(file.url), tostring(file.cache) else - paths[i] = tostring(file.url) + paths[i] = tostring(file.url.path) end end diff --git a/yazi-proxy/src/mgr.rs b/yazi-proxy/src/mgr.rs index 1977d8c7..37826c26 100644 --- a/yazi-proxy/src/mgr.rs +++ b/yazi-proxy/src/mgr.rs @@ -1,22 +1,30 @@ use std::borrow::Cow; use yazi_macro::{emit, relay}; -use yazi_parser::mgr::{FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt}; -use yazi_shared::{SStr, url::UrlBuf}; +use yazi_parser::mgr::{DisplaceDoOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt}; +use yazi_shared::{Id, SStr, url::UrlBuf}; pub struct MgrProxy; impl MgrProxy { + pub fn arrow(step: impl Into) { + emit!(Call(relay!(mgr:arrow, [step.into()]))); + } + pub fn cd(target: &UrlBuf) { emit!(Call(relay!(mgr:cd, [target]).with("raw", true))); } - pub fn reveal(target: &UrlBuf) { - emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true))); + pub fn displace_do(tab: Id, opt: DisplaceDoOpt) { + emit!(Call(relay!(mgr:displace_do).with("tab", tab).with_any("opt", opt))); } - pub fn arrow(step: impl Into) { - emit!(Call(relay!(mgr:arrow, [step.into()]))); + pub fn filter_do(opt: FilterOpt) { + emit!(Call(relay!(mgr:filter_do).with_any("opt", opt))); + } + + pub fn find_do(opt: FindDoOpt) { + emit!(Call(relay!(mgr:find_do).with_any("opt", opt))); } pub fn open(opt: OpenOpt) { @@ -33,12 +41,8 @@ impl MgrProxy { )); } - pub fn find_do(opt: FindDoOpt) { - emit!(Call(relay!(mgr:find_do).with_any("opt", opt))); - } - - pub fn filter_do(opt: FilterOpt) { - emit!(Call(relay!(mgr:filter_do).with_any("opt", opt))); + pub fn reveal(target: &UrlBuf) { + emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true))); } pub fn search_do(opt: SearchOpt) { @@ -50,8 +54,8 @@ impl MgrProxy { )); } - pub fn watch() { - emit!(Call(relay!(mgr:watch))); + pub fn update_paged_by(page: usize, only_if: &UrlBuf) { + emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if))); } pub fn update_peeked(opt: UpdatePeekedOpt) { @@ -62,14 +66,14 @@ impl MgrProxy { emit!(Call(relay!(mgr:update_spotted).with_any("opt", opt))); } - pub fn update_paged_by(page: usize, only_if: &UrlBuf) { - emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if))); - } - pub fn upload(urls: I) where I: IntoIterator, { emit!(Call(relay!(mgr:upload).with_seq(urls))); } + + pub fn watch() { + emit!(Call(relay!(mgr:watch))); + } } diff --git a/yazi-shared/src/data/data.rs b/yazi-shared/src/data/data.rs index 6f77c30a..7fab095e 100644 --- a/yazi-shared/src/data/data.rs +++ b/yazi-shared/src/data/data.rs @@ -28,40 +28,6 @@ pub enum Data { Any(Box), } -impl Data { - pub fn as_str(&self) -> Option<&str> { - match self { - Self::String(s) => Some(s), - _ => None, - } - } - - pub fn into_string(self) -> Option { - match self { - Self::String(s) => Some(s), - _ => None, - } - } - - pub fn into_any(self) -> Option { - match self { - Self::Any(b) => b.downcast::().ok().map(|b| *b), - _ => None, - } - } - - // FIXME: find a better name - pub fn into_any2(self) -> Result { - if let Self::Any(b) = self - && let Ok(t) = b.downcast::() - { - Ok(*t) - } else { - bail!("Failed to downcast Data into {}", std::any::type_name::()) - } - } -} - impl From<()> for Data { fn from(_: ()) -> Self { Self::Nil } } @@ -201,13 +167,291 @@ impl<'a> TryFrom<&'a Data> for UrlCow<'a> { } } +impl<'a> TryFrom<&'a Data> for &'a [u8] { + type Error = anyhow::Error; + + fn try_from(value: &'a Data) -> Result { + match value { + Data::Bytes(b) => Ok(b), + _ => bail!("not bytes"), + } + } +} + impl PartialEq for Data { fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) } } +impl Data { + pub fn as_str(&self) -> Option<&str> { + match self { + Self::String(s) => Some(s), + _ => None, + } + } + + pub fn into_string(self) -> Option { + match self { + Self::String(s) => Some(s), + _ => None, + } + } + + pub fn into_any(self) -> Option { + match self { + Self::Any(b) => b.downcast::().ok().map(|b| *b), + _ => None, + } + } + + // FIXME: find a better name + pub fn into_any2(self) -> Result { + if let Self::Any(b) = self + && let Ok(t) = b.downcast::() + { + Ok(*t) + } else { + bail!("Failed to downcast Data into {}", std::any::type_name::()) + } + } +} + +impl<'de> serde::Deserializer<'de> for &Data { + type Error = serde::de::value::Error; + + fn deserialize_any(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("any not supported")) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_bool(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_i8(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_i16(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_i32(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_i64(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_u8(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_u16(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_u32(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_u64(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_f32(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_f64(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_char(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("not a char")) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_str(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_string(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("string not supported")) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_bytes(self.try_into().map_err(serde::de::Error::custom)?) + } + + fn deserialize_byte_buf(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("byte buf not supported")) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + match self { + Data::Nil => visitor.visit_none(), + _ => visitor.visit_some(self), + } + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_unit() + } + + fn deserialize_unit_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_unit() + } + + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("seq not supported")) + } + + fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("tuple not supported")) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + _visitor: V, + ) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("tuple struct not supported")) + } + + fn deserialize_map(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("map not supported")) + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + _visitor: V, + ) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("struct not supported")) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + _visitor: V, + ) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("enum not supported")) + } + + fn deserialize_identifier(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("identifier not supported")) + } + + fn deserialize_ignored_any(self, _visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + Err(serde::de::Error::custom("ignored any not supported")) + } +} + // --- Macros macro_rules! impl_into_integer { - ($t:ty, $name:ident) => { + ($t:ty) => { impl TryFrom<&Data> for $t { type Error = anyhow::Error; @@ -224,14 +468,14 @@ macro_rules! impl_into_integer { } macro_rules! impl_into_number { - ($t:ty, $name:ident) => { + ($t:ty) => { impl TryFrom<&Data> for $t { type Error = anyhow::Error; fn try_from(value: &Data) -> Result { Ok(match value { Data::Integer(i) if *i == (*i as $t as _) => *i as $t, - Data::Number(n) => <$t>::try_from(*n)?, + Data::Number(n) if *n == (*n as $t as _) => *n as $t, Data::String(s) => s.parse()?, Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t, _ => bail!("not a number"), @@ -241,10 +485,17 @@ macro_rules! impl_into_number { }; } -impl_into_integer!(usize, as_usize); -impl_into_integer!(isize, as_isize); -impl_into_integer!(i16, as_i16); -impl_into_integer!(i32, as_i32); -impl_into_integer!(crate::Id, as_id); +impl_into_integer!(i8); +impl_into_integer!(i16); +impl_into_integer!(i32); +impl_into_integer!(i64); +impl_into_integer!(isize); +impl_into_integer!(u8); +impl_into_integer!(u16); +impl_into_integer!(u32); +impl_into_integer!(u64); +impl_into_integer!(usize); +impl_into_integer!(crate::Id); -impl_into_number!(f64, as_f64); +impl_into_number!(f32); +impl_into_number!(f64); diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index e7ad0469..6d0c18d7 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -152,6 +152,14 @@ impl Cmd { self.take(0) } + pub fn take_second(&mut self) -> Result + where + T: TryFrom, + T::Error: Into, + { + self.take(1) + } + pub fn take_seq(&mut self) -> Vec where T: TryFrom, diff --git a/yazi-shared/src/event/cow.rs b/yazi-shared/src/event/cow.rs index 309b5782..5e510489 100644 --- a/yazi-shared/src/event/cow.rs +++ b/yazi-shared/src/event/cow.rs @@ -55,6 +55,18 @@ impl CmdCow { } } + pub fn take_second<'a, T>(&mut self) -> Result + where + T: TryFrom + TryFrom<&'a Data>, + >::Error: Into, + >::Error: Into, + { + match self { + Self::Owned(c) => c.take_second(), + Self::Borrowed(c) => c.second(), + } + } + pub fn take_seq<'a, T>(&mut self) -> Vec where T: TryFrom + TryFrom<&'a Data>, diff --git a/yazi-shared/src/loc/able.rs b/yazi-shared/src/loc/able.rs index 2421fdec..f25b9a3e 100644 --- a/yazi-shared/src/loc/able.rs +++ b/yazi-shared/src/loc/able.rs @@ -72,6 +72,8 @@ pub(super) trait LocAbleImpl<'p>: LocAble<'p> { fn len(self) -> usize { self.as_encoded_bytes().len() } + fn parent(self) -> Option; + fn strip_prefix<'a, T>(self, base: T) -> Option where T: AsStrandView<'a, Self::Strand<'a>>; @@ -99,6 +101,8 @@ impl<'p> LocAbleImpl<'p> for &'p std::path::Path { self.join(path.as_strand_view()) } + fn parent(self) -> Option { self.parent() } + fn strip_prefix<'a, T>(self, base: T) -> Option where T: AsStrandView<'a, Self::Strand<'a>>, @@ -129,6 +133,8 @@ impl<'p> LocAbleImpl<'p> for &'p typed_path::UnixPath { self.join(path.as_strand_view()) } + fn parent(self) -> Option { self.parent() } + fn strip_prefix<'a, T>(self, base: T) -> Option where T: AsStrandView<'a, Self::Strand<'a>>, diff --git a/yazi-shared/src/loc/buf.rs b/yazi-shared/src/loc/buf.rs index 029866a2..115ef646 100644 --- a/yazi-shared/src/loc/buf.rs +++ b/yazi-shared/src/loc/buf.rs @@ -182,7 +182,7 @@ where let old = self.inner.len(); self.mutate(|path| path.set_file_name(name)); - let new = self.len(); + let new = self.inner.len(); if new == old { return Ok(()); } @@ -215,6 +215,9 @@ where loc } + #[inline] + pub fn parent(&self) -> Option> { self.as_loc().parent() } + #[inline] fn mutate T>(&mut self, f: F) -> T { let mut inner = mem::take(&mut self.inner); diff --git a/yazi-shared/src/loc/loc.rs b/yazi-shared/src/loc/loc.rs index 6d9c27c6..59492801 100644 --- a/yazi-shared/src/loc/loc.rs +++ b/yazi-shared/src/loc/loc.rs @@ -73,6 +73,64 @@ impl<'p, P> Loc<'p, P> where P: LocAble<'p> + LocAbleImpl<'p>, { + #[inline] + pub fn as_inner(self) -> P { self.inner } + + #[inline] + pub fn as_loc(self) -> Self { self } + + pub fn bare(path: T) -> Self + where + T: AsPathView<'p, P>, + { + let path = path.as_path_view(); + let Some(name) = path.file_name() else { + let uri = path.strip_prefix(P::empty()).unwrap().len(); + return Self { inner: path, uri, urn: 0, _phantom: PhantomData }; + }; + + let name_len = name.len(); + let prefix_len = unsafe { + name.as_encoded_bytes().as_ptr().offset_from_unsigned(path.as_encoded_bytes().as_ptr()) + }; + + let bytes = &path.as_encoded_bytes()[..prefix_len + name_len]; + Self { + inner: unsafe { P::from_encoded_bytes_unchecked(bytes) }, + uri: bytes.len(), + urn: name_len, + _phantom: PhantomData, + } + } + + #[inline] + pub fn base(self) -> P { + unsafe { + P::from_encoded_bytes_unchecked( + self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.uri), + ) + } + } + + pub fn floated<'a, T, S>(path: T, base: S) -> Self + where + T: AsPathView<'p, P>, + S: AsStrandView<'a, P::Strand<'a>>, + { + let mut loc = Self::bare(path); + loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len(); + loc + } + + #[inline] + pub fn has_base(self) -> bool { self.inner.len() != self.uri } + + #[inline] + pub fn has_trail(self) -> bool { self.inner.len() != self.urn } + + #[inline] + pub fn is_empty(self) -> bool { self.inner.len() == 0 } + pub fn new<'a, T, S>(path: T, base: S, trail: S) -> Self where T: AsPathView<'p, P>, @@ -84,6 +142,67 @@ where loc } + #[inline] + pub fn parent(self) -> Option

{ + self.inner.parent().filter(|p| !p.as_encoded_bytes().is_empty()) + } + + pub fn saturated<'a, T>(path: T, kind: SchemeKind) -> Self + where + T: AsPathView<'p, P>, + { + match kind { + SchemeKind::Regular => Self::bare(path), + SchemeKind::Search => Self::zeroed(path), + SchemeKind::Archive => Self::zeroed(path), + SchemeKind::Sftp => Self::bare(path), + } + } + + #[inline] + pub fn trail(self) -> P { + unsafe { + P::from_encoded_bytes_unchecked( + self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.urn), + ) + } + } + + #[inline] + pub fn triple(self) -> (P, P, P) { + let len = self.inner.len(); + + let base = ..len - self.uri; + let rest = len - self.uri..len - self.urn; + let urn = len - self.urn..; + + unsafe { + ( + P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(base)), + P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(rest)), + P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(urn)), + ) + } + } + + #[inline] + pub fn uri(self) -> P { + unsafe { + P::from_encoded_bytes_unchecked( + self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.uri..), + ) + } + } + + #[inline] + pub fn urn(self) -> P { + unsafe { + P::from_encoded_bytes_unchecked( + self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.urn..), + ) + } + } + pub fn with(path: T, uri: usize, urn: usize) -> Result where T: AsPathView<'p, P>, @@ -116,30 +235,6 @@ where Ok(loc) } - pub fn bare(path: T) -> Self - where - T: AsPathView<'p, P>, - { - let path = path.as_path_view(); - let Some(name) = path.file_name() else { - let uri = path.strip_prefix(P::empty()).unwrap().len(); - return Self { inner: path, uri, urn: 0, _phantom: PhantomData }; - }; - - let name_len = name.len(); - let prefix_len = unsafe { - name.as_encoded_bytes().as_ptr().offset_from_unsigned(path.as_encoded_bytes().as_ptr()) - }; - - let bytes = &path.as_encoded_bytes()[..prefix_len + name_len]; - Self { - inner: unsafe { P::from_encoded_bytes_unchecked(bytes) }, - uri: bytes.len(), - urn: name_len, - _phantom: PhantomData, - } - } - pub fn zeroed(path: T) -> Self where T: AsPathView<'p, P>, @@ -148,94 +243,4 @@ where (loc.uri, loc.urn) = (0, 0); loc } - - pub fn floated<'a, T, S>(path: T, base: S) -> Self - where - T: AsPathView<'p, P>, - S: AsStrandView<'a, P::Strand<'a>>, - { - let mut loc = Self::bare(path); - loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len(); - loc - } - - pub fn saturated<'a, T>(path: T, kind: SchemeKind) -> Self - where - T: AsPathView<'p, P>, - { - match kind { - SchemeKind::Regular => Self::bare(path), - SchemeKind::Search => Self::zeroed(path), - SchemeKind::Archive => Self::zeroed(path), - SchemeKind::Sftp => Self::bare(path), - } - } - - #[inline] - pub fn as_loc(self) -> Self { self } - - #[inline] - pub fn as_inner(self) -> P { self.inner } - - #[inline] - pub fn is_empty(self) -> bool { self.inner.len() == 0 } - - #[inline] - pub fn uri(self) -> P { - unsafe { - P::from_encoded_bytes_unchecked( - self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.uri..), - ) - } - } - - #[inline] - pub fn urn(self) -> P { - unsafe { - P::from_encoded_bytes_unchecked( - self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.urn..), - ) - } - } - - #[inline] - pub fn base(self) -> P { - unsafe { - P::from_encoded_bytes_unchecked( - self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.uri), - ) - } - } - - #[inline] - pub fn has_base(self) -> bool { self.inner.len() != self.uri } - - #[inline] - pub fn trail(self) -> P { - unsafe { - P::from_encoded_bytes_unchecked( - self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.urn), - ) - } - } - - #[inline] - pub fn has_trail(self) -> bool { self.inner.len() != self.urn } - - #[inline] - pub fn triple(self) -> (P, P, P) { - let len = self.inner.len(); - - let base = ..len - self.uri; - let rest = len - self.uri..len - self.urn; - let urn = len - self.urn..; - - unsafe { - ( - P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(base)), - P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(rest)), - P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(urn)), - ) - } - } } diff --git a/yazi-shared/src/path/cow.rs b/yazi-shared/src/path/cow.rs index 3e1414b7..49b7ef4f 100644 --- a/yazi-shared/src/path/cow.rs +++ b/yazi-shared/src/path/cow.rs @@ -5,6 +5,7 @@ use anyhow::Result; use crate::path::{AsPath, PathBufDyn, PathDyn, PathKind}; // --- PathCow +#[derive(Debug)] pub enum PathCow<'a> { Borrowed(PathDyn<'a>), Owned(PathBufDyn), diff --git a/yazi-shared/src/path/path.rs b/yazi-shared/src/path/path.rs index 01c3cca6..0d33535f 100644 --- a/yazi-shared/src/path/path.rs +++ b/yazi-shared/src/path/path.rs @@ -139,8 +139,8 @@ impl<'p> PathDyn<'p> { pub fn parent(self) -> Option { Some(match self { - Self::Os(p) => Self::Os(p.parent()?), - Self::Unix(p) => Self::Unix(p.parent()?), + Self::Os(p) => Self::Os(p.parent().filter(|p| !p.as_os_str().is_empty())?), + Self::Unix(p) => Self::Unix(p.parent().filter(|p| !p.as_bytes().is_empty())?), }) } diff --git a/yazi-shared/src/source.rs b/yazi-shared/src/source.rs index 05d75447..f966af5a 100644 --- a/yazi-shared/src/source.rs +++ b/yazi-shared/src/source.rs @@ -6,16 +6,16 @@ pub enum Source { Unknown, Key, - Ind, - Emit, - EmitInd, - Relay, - RelayInd, + + Ind, } impl Source { #[inline] pub fn is_key(self) -> bool { self == Self::Key } + + #[inline] + pub fn is_ind(self) -> bool { self == Self::Ind } } diff --git a/yazi-shared/src/url/buf.rs b/yazi-shared/src/url/buf.rs index fbfabae9..b21688bc 100644 --- a/yazi-shared/src/url/buf.rs +++ b/yazi-shared/src/url/buf.rs @@ -96,6 +96,10 @@ impl PartialEq for UrlBuf { fn eq(&self, other: &Self) -> bool { self.as_url() == other.as_url() } } +impl PartialEq for &UrlBuf { + fn eq(&self, other: &UrlBuf) -> bool { self.as_url() == other.as_url() } +} + impl PartialEq> for UrlBuf { fn eq(&self, other: &Url) -> bool { self.as_url() == *other } } @@ -104,6 +108,14 @@ impl PartialEq> for &UrlBuf { fn eq(&self, other: &Url) -> bool { self.as_url() == *other } } +impl PartialEq> for UrlBuf { + fn eq(&self, other: &UrlCow) -> bool { self.as_url() == other.as_url() } +} + +impl PartialEq> for &UrlBuf { + fn eq(&self, other: &UrlCow) -> bool { self.as_url() == other.as_url() } +} + // --- Hash impl Hash for UrlBuf { fn hash(&self, state: &mut H) { self.as_url().hash(state) } @@ -293,7 +305,7 @@ mod tests { ("sftp://remote//", None), // Relative ("search://kw:2:2/a/b", Some("search://kw:1:1/a")), - ("search://kw:1:1/a", Some("search://kw/")), + ("search://kw:1:1/a", None), ("search://kw/", None), ];