diff --git a/Cargo.lock b/Cargo.lock index 04c4d0ac..5b169516 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,9 +588,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.59" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -1365,9 +1365,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" -version = "0.14.0-rc.29" +version = "0.14.0-rc.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84043d573efd4ac9d2d125817979a379204bf7e328b25a4a30487e8d100e618" +checksum = "7d7a0bfd012613a7bcfe02cbfccf2b846e9ef9e1bccb641c48d461253cfb034d" dependencies = [ "base16ct", "crypto-bigint", @@ -1793,9 +1793,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", @@ -1851,6 +1851,17 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" dependencies = [ "allocator-api2", "equivalent", @@ -2010,12 +2021,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -2279,9 +2290,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "libc", ] @@ -3176,7 +3187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64", - "indexmap 2.13.1", + "indexmap 2.14.0", "quick-xml", "serde", "time", @@ -4103,7 +4114,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.1", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -4667,9 +4678,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.51.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -4723,7 +4734,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime", @@ -5153,7 +5164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.1", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -5166,7 +5177,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap 2.13.1", + "indexmap 2.14.0", "semver", ] @@ -5662,7 +5673,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.1", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -5693,7 +5704,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap 2.13.1", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -5712,7 +5723,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.1", + "indexmap 2.14.0", "log", "semver", "serde", @@ -5745,7 +5756,7 @@ dependencies = [ "crossterm 0.29.0", "either", "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libc", "mlua", "paste", @@ -5809,7 +5820,7 @@ dependencies = [ "anyhow", "crossterm 0.29.0", "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libc", "mlua", "paste", @@ -5840,7 +5851,7 @@ dependencies = [ "clap_complete_fig", "clap_complete_nushell", "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "regex", "vergen-gitcl", "yazi-adapter", @@ -5869,7 +5880,7 @@ dependencies = [ "clap_complete_fig", "clap_complete_nushell", "crossterm 0.29.0", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_json", "tokio", @@ -5900,8 +5911,8 @@ dependencies = [ "bitflags 2.11.0", "crossterm 0.29.0", "globset", - "hashbrown 0.16.1", - "indexmap 2.13.1", + "hashbrown 0.17.0", + "indexmap 2.14.0", "ratatui", "regex", "serde", @@ -5923,8 +5934,8 @@ dependencies = [ "anyhow", "crossterm 0.29.0", "dyn-clone", - "hashbrown 0.16.1", - "indexmap 2.13.1", + "hashbrown 0.17.0", + "indexmap 2.14.0", "mlua", "parking_lot", "ratatui", @@ -5959,8 +5970,8 @@ name = "yazi-dds" version = "26.2.2" dependencies = [ "anyhow", - "hashbrown 0.16.1", - "indexmap 2.13.1", + "hashbrown 0.17.0", + "indexmap 2.14.0", "mlua", "ordered-float 5.3.0", "parking_lot", @@ -6060,7 +6071,7 @@ dependencies = [ "dirs", "either", "foldhash 0.2.0", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libc", "objc2", "parking_lot", @@ -6097,7 +6108,7 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "crossterm 0.29.0", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "mlua", "paste", "ratatui", @@ -6172,6 +6183,7 @@ dependencies = [ "yazi-macro", "yazi-scheduler", "yazi-shared", + "yazi-shim", "yazi-widgets", ] @@ -6180,7 +6192,7 @@ name = "yazi-runner" version = "26.2.2" dependencies = [ "anyhow", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "mlua", "parking_lot", "thiserror 2.0.18", @@ -6203,7 +6215,7 @@ dependencies = [ "anyhow", "async-priority-channel", "foldhash 0.2.0", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libc", "lru", "mlua", @@ -6245,7 +6257,7 @@ dependencies = [ "dyn-clone", "foldhash 0.2.0", "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libc", "memchr", "ordered-float 5.3.0", @@ -6311,7 +6323,7 @@ dependencies = [ "deadpool", "either", "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "parking_lot", "russh", "tokio", @@ -6329,7 +6341,7 @@ name = "yazi-watcher" version = "26.2.2" dependencies = [ "futures", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "notify", "parking_lot", "percent-encoding", diff --git a/Cargo.toml b/Cargo.toml index d5b697fd..6acc8042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,8 +50,8 @@ either = { version = "1.15.0" } foldhash = "0.2.0" futures = "0.3.32" globset = "0.4.18" -hashbrown = { version = "0.16.1", features = [ "serde" ] } -indexmap = { version = "2.13.1", features = [ "serde" ] } +hashbrown = { version = "0.17.0", features = [ "serde" ] } +indexmap = { version = "2.14.0", features = [ "serde" ] } libc = "0.2.184" lru = "0.16.3" mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] } @@ -71,7 +71,7 @@ serde_with = "3.18.0" strum = { version = "0.28.0", features = [ "derive" ] } syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } thiserror = "2.0.18" -tokio = { version = "1.51.0", features = [ "full" ] } +tokio = { version = "1.51.1", features = [ "full" ] } tokio-stream = "0.1.18" tokio-util = "0.7.18" toml = { version = "1.1.2" } diff --git a/yazi-actor/src/app/bootstrap.rs b/yazi-actor/src/app/bootstrap.rs index 312476d0..cf45d003 100644 --- a/yazi-actor/src/app/bootstrap.rs +++ b/yazi-actor/src/app/bootstrap.rs @@ -1,8 +1,9 @@ use anyhow::Result; use yazi_actor::Ctx; use yazi_boot::BOOT; +use yazi_core::mgr::CdSource; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike}; use crate::Actor; diff --git a/yazi-actor/src/mgr/back.rs b/yazi-actor/src/mgr/back.rs index 1f604819..33bd2a6f 100644 --- a/yazi-actor/src/mgr/back.rs +++ b/yazi-actor/src/mgr/back.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::mgr::CdSource; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs index e52a1143..78cc959a 100644 --- a/yazi-actor/src/mgr/cd.rs +++ b/yazi-actor/src/mgr/cd.rs @@ -4,6 +4,7 @@ use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; +use yazi_core::mgr::CdSource; use yazi_dds::Pubsub; use yazi_fs::{File, FilesOp, path::{clean_url, expand_url}}; use yazi_macro::{act, err, render, succ}; @@ -26,14 +27,15 @@ impl Actor for Cd { act!(mgr:escape_visual, cx)?; if form.interactive { return Self::cd_interactive(cx); - } - - let tab = cx.tab_mut(); - if form.target == *tab.cwd() { + } else if form.target == *cx.cwd() { succ!(); } + // Stash first so it's possible to access the original cwd in hooks + act!(mgr:stash, cx, &form).ok(); + // Take parent to history + let tab = cx.tab_mut(); if let Some(t) = tab.parent.take() { tab.history.insert(t.url.clone(), t); } @@ -54,7 +56,6 @@ impl Actor for Cd { act!(mgr:sort, cx).ok(); act!(mgr:hover, cx)?; act!(mgr:refresh, cx)?; - act!(mgr:stash, cx, form).ok(); act!(app:title, cx).ok(); succ!(render!()); } @@ -77,7 +78,7 @@ impl Cd { let Ok(file) = File::new(&url).await else { return }; if file.is_dir() { - return MgrProxy::cd(&url); + return MgrProxy::cd(&url, CdSource::Cd); } if let Some(p) = url.parent() { diff --git a/yazi-actor/src/mgr/displace_do.rs b/yazi-actor/src/mgr/displace_do.rs index 172629f7..dc18cbc3 100644 --- a/yazi-actor/src/mgr/displace_do.rs +++ b/yazi-actor/src/mgr/displace_do.rs @@ -1,7 +1,8 @@ use anyhow::{Result, bail}; +use yazi_core::mgr::CdSource; use yazi_fs::FilesOp; use yazi_macro::{act, succ}; -use yazi_parser::mgr::{CdSource, DisplaceDoForm}; +use yazi_parser::mgr::DisplaceDoForm; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/enter.rs b/yazi-actor/src/mgr/enter.rs index 54c9aead..f0647a9a 100644 --- a/yazi-actor/src/mgr/enter.rs +++ b/yazi-actor/src/mgr/enter.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::mgr::CdSource; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/follow.rs b/yazi-actor/src/mgr/follow.rs index b713524b..50d9e113 100644 --- a/yazi-actor/src/mgr/follow.rs +++ b/yazi-actor/src/mgr/follow.rs @@ -1,7 +1,8 @@ use anyhow::Result; +use yazi_core::mgr::CdSource; use yazi_fs::path::clean_url; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/forward.rs b/yazi-actor/src/mgr/forward.rs index e226f7a7..719d7b12 100644 --- a/yazi-actor/src/mgr/forward.rs +++ b/yazi-actor/src/mgr/forward.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::mgr::CdSource; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/leave.rs b/yazi-actor/src/mgr/leave.rs index a3b9b7b8..db1f18ba 100644 --- a/yazi-actor/src/mgr/leave.rs +++ b/yazi-actor/src/mgr/leave.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::mgr::CdSource; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::CdSource}; +use yazi_parser::VoidForm; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs index b8e45af5..1e5106d7 100644 --- a/yazi-actor/src/mgr/search.rs +++ b/yazi-actor/src/mgr/search.rs @@ -4,10 +4,10 @@ use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; -use yazi_core::mgr::SearchVia; +use yazi_core::mgr::{CdSource, SearchVia}; use yazi_fs::{FilesOp, cha::Cha}; use yazi_macro::{act, succ}; -use yazi_parser::{VoidForm, mgr::{CdSource, SearchForm}}; +use yazi_parser::{VoidForm, mgr::SearchForm}; use yazi_plugin::external; use yazi_proxy::{InputProxy, MgrProxy}; use yazi_scheduler::NotifyProxy; @@ -85,7 +85,7 @@ impl Actor for SearchDo { let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500)); pin!(rx); - let ((), ticket) = (MgrProxy::cd(&cwd), FilesOp::prepare(&cwd)); + let ((), ticket) = (MgrProxy::cd(&cwd, CdSource::Search), FilesOp::prepare(&cwd)); while let Some(chunk) = rx.next().await { FilesOp::Part(cwd.clone(), chunk, ticket).emit(); } diff --git a/yazi-actor/src/mgr/tab_create.rs b/yazi-actor/src/mgr/tab_create.rs index 349578c3..b506dbbb 100644 --- a/yazi-actor/src/mgr/tab_create.rs +++ b/yazi-actor/src/mgr/tab_create.rs @@ -1,7 +1,7 @@ use anyhow::Result; -use yazi_core::tab::Tab; +use yazi_core::{mgr::CdSource, tab::Tab}; use yazi_macro::{act, render, succ}; -use yazi_parser::mgr::{CdSource, TabCreateForm}; +use yazi_parser::mgr::TabCreateForm; use yazi_scheduler::NotifyProxy; use yazi_shared::{data::Data, url::UrlLike}; @@ -25,8 +25,8 @@ impl Actor for TabCreate { } let mut tab = Tab::default(); - let (cd, url) = if let Some(wd) = form.url { - (true, wd.into_owned()) + let (cd, url) = if let Some(target) = form.target { + (true, target) } else if let Some(h) = cx.hovered() { tab.pref = cx.tab().pref.clone(); (false, h.url.clone()) diff --git a/yazi-actor/src/mgr/update_files.rs b/yazi-actor/src/mgr/update_files.rs index d710f1aa..97b77ad5 100644 --- a/yazi-actor/src/mgr/update_files.rs +++ b/yazi-actor/src/mgr/update_files.rs @@ -3,7 +3,7 @@ use yazi_core::tab::Folder; use yazi_fs::FilesOp; use yazi_macro::{act, render, succ}; use yazi_parser::mgr::UpdateFilesForm; -use yazi_shared::{data::Data, url::UrlLike}; +use yazi_shared::{data::Data, url::{UrlLike, UrlMapExt}}; use yazi_watcher::local::LINKED; use crate::{Actor, Ctx}; @@ -87,7 +87,7 @@ impl UpdateFiles { fn update_hovered(cx: &mut Ctx, op: FilesOp) -> Result { let (id, url) = (cx.tab().id, op.cwd()); - let folder = cx.tab_mut().history.entry_ref(url).or_insert_with(|| Folder::from(url)); + let folder = cx.tab_mut().history.get_or_insert_with(url, |u| Folder::from(u)); if folder.update_pub(id, op) { act!(mgr:peek, cx, true)?; @@ -101,15 +101,11 @@ impl UpdateFiles { |(p, n)| matches!(op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(&n)), ); - tab - .history - .entry_ref(op.cwd()) - .or_insert_with(|| Folder::from(op.cwd())) - .update_pub(tab.id, op); - + tab.history.get_or_insert_with(op.cwd(), |u| Folder::from(u)).update_pub(tab.id, op); if leave { act!(mgr:leave, cx)?; } + succ!(); } } diff --git a/yazi-binding/src/runtime.rs b/yazi-binding/src/runtime.rs index a8c6ce43..7e4f4d4c 100644 --- a/yazi-binding/src/runtime.rs +++ b/yazi-binding/src/runtime.rs @@ -1,7 +1,7 @@ use std::{collections::VecDeque, mem}; use anyhow::{Context, Result}; -use hashbrown::{HashMap, hash_map::EntryRef}; +use hashbrown::HashMap; use mlua::Function; #[derive(Debug, Default)] @@ -49,15 +49,9 @@ impl Runtime { pub fn put_block(&mut self, f: &Function) -> Option { let cur = self.frames.back().filter(|f| f.id != "init")?; - Some(match self.blocks.entry_ref(&cur.id) { - EntryRef::Occupied(mut oe) => { - oe.get_mut().push(f.clone()); - oe.get().len() - 1 - } - EntryRef::Vacant(ve) => { - ve.insert(vec![f.clone()]); - 0 - } - }) + let blocks = self.blocks.entry_ref(&cur.id).or_default(); + + blocks.push(f.clone()); + Some(blocks.len() - 1) } } diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index 8c036e56..7b9bca2a 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -6,6 +6,7 @@ use serde::Deserialize; use yazi_shared::{Layer, Source, event::Action}; use super::Key; +use crate::Platform; static RE: OnceLock = OnceLock::new(); @@ -16,7 +17,8 @@ pub struct Chord { #[serde(deserialize_with = "super::deserialize_run")] pub run: Vec, pub desc: Option, - pub r#for: Option, + #[serde(default)] + pub r#for: Platform, } impl PartialEq for Chord { diff --git a/yazi-config/src/keymap/cow.rs b/yazi-config/src/keymap/cow.rs index ba415af4..87a7deeb 100644 --- a/yazi-config/src/keymap/cow.rs +++ b/yazi-config/src/keymap/cow.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use yazi_shared::event::ActionCow; use super::Chord; +use crate::Platform; #[derive(Clone, Debug)] pub enum ChordCow { @@ -31,7 +32,7 @@ impl Deref for ChordCow { impl Default for ChordCow { fn default() -> Self { - const C: &Chord = &Chord { on: vec![], run: vec![], desc: None, r#for: None }; + const C: &Chord = &Chord { on: vec![], run: vec![], desc: None, r#for: Platform::All }; Self::Borrowed(C) } } diff --git a/yazi-config/src/keymap/rules.rs b/yazi-config/src/keymap/rules.rs index 1fcf8897..778242a7 100644 --- a/yazi-config/src/keymap/rules.rs +++ b/yazi-config/src/keymap/rules.rs @@ -7,7 +7,7 @@ use yazi_codegen::DeserializeOver2; use yazi_shared::Layer; use super::Chord; -use crate::{Preset, check_for, keymap::Key}; +use crate::{Preset, keymap::Key}; #[derive(Default, Deserialize, DeserializeOver2)] pub struct KeymapRules { @@ -39,9 +39,8 @@ impl KeymapRules { self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))), self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))), ) - .map(|mut chord| (chord.r#for.take(), chord)) - .filter(|(r#for, chord)| !chord.noop() && check_for(r#for.as_deref())) - .map(|(_, chord)| chord.reshape(layer)) + .filter(|chord| !chord.noop() && chord.r#for.matches()) + .map(|chord| chord.reshape(layer)) .collect::>()?; Ok(Self { keymap, ..Default::default() }) diff --git a/yazi-config/src/opener/opener.rs b/yazi-config/src/opener/opener.rs index f6b5702e..f2b09e8c 100644 --- a/yazi-config/src/opener/opener.rs +++ b/yazi-config/src/opener/opener.rs @@ -8,7 +8,6 @@ use toml::{Spanned, de::DeTable}; use yazi_codegen::DeserializeOver; use super::OpenerRule; -use crate::check_for; #[derive(Debug, Deserialize, DeserializeOver)] pub struct Opener(HashMap>); @@ -47,9 +46,8 @@ impl Opener { for rules in self.0.values_mut() { *rules = mem::take(rules) .into_iter() - .map(|mut r| (r.r#for.take(), r)) - .filter(|(r#for, _)| check_for(r#for.as_deref())) - .map(|(_, r)| r.reshape()) + .filter(|r| r.r#for.matches()) + .map(|r| r.reshape()) .collect::>>()? .into_iter() .collect(); diff --git a/yazi-config/src/opener/rule.rs b/yazi-config/src/opener/rule.rs index 673c44e1..3b176ed1 100644 --- a/yazi-config/src/opener/rule.rs +++ b/yazi-config/src/opener/rule.rs @@ -2,6 +2,8 @@ use anyhow::{Result, bail}; use serde::Deserialize; use yazi_fs::Splatter; +use crate::Platform; + #[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OpenerRule { pub run: String, @@ -11,7 +13,8 @@ pub struct OpenerRule { pub orphan: bool, #[serde(default)] pub desc: String, - pub r#for: Option, + #[serde(default)] + pub r#for: Platform, #[serde(skip)] pub spread: bool, } diff --git a/yazi-config/src/platform.rs b/yazi-config/src/platform.rs index e1855d9e..25c5305e 100644 --- a/yazi-config/src/platform.rs +++ b/yazi-config/src/platform.rs @@ -1,9 +1,26 @@ -#[inline] -pub(crate) fn check_for(r#for: Option<&str>) -> bool { - match r#for.as_ref().map(|s| s.as_ref()) { - Some("unix") if cfg!(unix) => true, - Some(os) if os == std::env::consts::OS => true, - Some(_) => false, - None => true, +use serde::Deserialize; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[serde(rename_all = "kebab-case")] +pub enum Platform { + #[default] + All, + Linux, + Macos, + Windows, + Android, + Unix, +} + +impl Platform { + pub(crate) fn matches(self) -> bool { + match self { + Self::All => true, + Self::Linux => cfg!(target_os = "linux"), + Self::Macos => cfg!(target_os = "macos"), + Self::Windows => cfg!(windows), + Self::Android => cfg!(target_os = "android"), + Self::Unix => cfg!(unix), + } } } diff --git a/yazi-core/src/mgr/cd.rs b/yazi-core/src/mgr/cd.rs new file mode 100644 index 00000000..e291b27d --- /dev/null +++ b/yazi-core/src/mgr/cd.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; +use strum::IntoStaticStr; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, IntoStaticStr, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +#[strum(serialize_all = "kebab-case")] +pub enum CdSource { + #[default] + Cd, + Reveal, + + Enter, + Leave, + + Follow, + Search, + Escape, + + Forward, + Back, + + Tab, + Displace, +} diff --git a/yazi-core/src/mgr/mod.rs b/yazi-core/src/mgr/mod.rs index 8a89291d..b1816933 100644 --- a/yazi-core/src/mgr/mod.rs +++ b/yazi-core/src/mgr/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(batcher displace filter find mgr mimetype open search tabs yanked); +yazi_macro::mod_flat!(batcher cd displace filter find mgr mimetype open search tabs yanked); diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index 2676e0ac..105ba9e4 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use hashbrown::HashMap; use indexmap::IndexMap; use yazi_fs::FilesOp; -use yazi_shared::{timestamp_us, url::{Url, UrlBuf, UrlBufCov, UrlCov}}; +use yazi_shared::{timestamp_us, url::{Url, UrlBuf, UrlBufCov, UrlCov, UrlCovMapExt}}; #[derive(Default)] pub struct Selected { @@ -66,7 +66,7 @@ impl Selected { self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64))); for u in parents { - *self.parents.entry_ref(&UrlCov::new(u)).or_default() += self.inner.len() - len; + *self.parents.get_or_insert_default(UrlCov::new(u)) += self.inner.len() - len; } urls.len() } diff --git a/yazi-core/src/tasks/option.rs b/yazi-core/src/tasks/option.rs index 5b63001a..30269059 100644 --- a/yazi-core/src/tasks/option.rs +++ b/yazi-core/src/tasks/option.rs @@ -1,6 +1,39 @@ -use yazi_scheduler::plugin::PluginInEntry; +use std::borrow::Cow; + +use yazi_scheduler::{TaskIn, plugin::PluginInEntry}; +use yazi_shared::{Id, SStr}; #[derive(Clone, Debug)] pub enum TaskOpt { Plugin(PluginInEntry), } + +impl TaskIn for TaskOpt { + type Prog = (); + + fn id(&self) -> Id { + match self { + Self::Plugin(r#in) => r#in.id(), + } + } + + fn set_id(&mut self, id: Id) -> &mut Self { + match self { + Self::Plugin(r#in) => _ = r#in.set_id(id), + } + self + } + + fn title(&self) -> Cow<'_, str> { + match self { + Self::Plugin(r#in) => r#in.title(), + } + } + + fn set_title(&mut self, title: impl Into) -> &mut Self { + match self { + Self::Plugin(r#in) => _ = r#in.set_title(title), + } + self + } +} diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 897d86da..7fec4ad6 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -101,6 +101,7 @@ impl<'a> Executor<'a> { on!(forward); on!(reveal); on!(follow); + on!(stash); // Toggle on!(toggle); diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index 3e8fe3bf..779f2ae7 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -2,7 +2,7 @@ use std::path::Path; use hashbrown::{HashMap, HashSet}; use yazi_macro::relay; -use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike}}; +use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike, UrlMapExt}}; use super::File; use crate::{cha::Cha, error::Error}; @@ -52,15 +52,15 @@ impl FilesOp { } pub fn rename(map: HashMap) { - let mut parents: HashMap<_, (HashSet<_>, HashMap<_, _>)> = Default::default(); + let mut parents: HashMap, HashMap<_, _>)> = Default::default(); for (o, n) in map { let Some(o_p) = o.parent() else { continue }; let Some(n_p) = n.url.parent() else { continue }; if o_p == n_p { - parents.entry_ref(&o_p).or_default().1.insert(o.urn().into(), n); + parents.get_or_insert_default(o_p).1.insert(o.urn().into(), n); } else { - parents.entry_ref(&o_p).or_default().0.insert(o.urn().into()); - parents.entry_ref(&n_p).or_default().1.insert(n.urn().into(), n); + parents.get_or_insert_default(o_p).0.insert(o.urn().into()); + parents.get_or_insert_default(n_p).1.insert(n.urn().into(), n); } } for (p, (o, n)) in parents { diff --git a/yazi-parser/src/mgr/cd.rs b/yazi-parser/src/mgr/cd.rs index 6d3ac772..5cd3160d 100644 --- a/yazi-parser/src/mgr/cd.rs +++ b/yazi-parser/src/mgr/cd.rs @@ -1,41 +1,46 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; +use yazi_core::mgr::CdSource; use yazi_fs::path::{clean_url, expand_url}; use yazi_shared::{event::ActionCow, url::{Url, UrlBuf}}; use yazi_vfs::provider; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct CdForm { + #[serde(alias = "0")] pub target: UrlBuf, + #[serde(default)] pub interactive: bool, + #[serde(default)] + pub raw: bool, + #[serde(default)] pub source: CdSource, } -impl From for CdForm { - fn from(mut a: ActionCow) -> Self { - let mut target = a.take_first().unwrap_or_default(); +impl TryFrom for CdForm { + type Error = anyhow::Error; - if !a.bool("raw") { - target = expand_url(target); + fn try_from(a: ActionCow) -> Result { + let mut me: Self = a.deserialize()?; + + if !me.raw { + me.target = expand_url(me.target).into_owned(); } - if let Some(u) = provider::try_absolute(&target) + if let Some(u) = provider::try_absolute(&me.target) && u.is_owned() { - target = u.into_static(); + me.target = u.into_owned(); } - Self { - target: clean_url(target), - interactive: a.bool("interactive"), - source: CdSource::Cd, - } + me.target = clean_url(me.target); + Ok(me) } } impl From<(UrlBuf, CdSource)> for CdForm { fn from((target, source): (UrlBuf, CdSource)) -> Self { - Self { target, interactive: false, source } + Self { target, interactive: false, raw: false, source } } } @@ -50,19 +55,3 @@ impl FromLua for CdForm { impl IntoLua for CdForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// --- Source -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "kebab-case")] -pub enum CdSource { - Tab, - Cd, - Reveal, - Enter, - Leave, - Follow, - Forward, - Back, - Escape, - Displace, -} diff --git a/yazi-parser/src/mgr/copy.rs b/yazi-parser/src/mgr/copy.rs index 030331a9..65773881 100644 --- a/yazi-parser/src/mgr/copy.rs +++ b/yazi-parser/src/mgr/copy.rs @@ -5,21 +5,20 @@ use serde::Deserialize; use strum::EnumString; use yazi_shared::{SStr, event::ActionCow, strand::AsStrand}; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct CopyForm { + #[serde(alias = "0")] pub r#type: SStr, + #[serde(default)] pub separator: CopySeparator, + #[serde(default)] pub hovered: bool, } -impl From for CopyForm { - fn from(mut a: ActionCow) -> Self { - Self { - r#type: a.take_first().unwrap_or_default(), - separator: a.str("separator").parse().unwrap_or_default(), - hovered: a.bool("hovered"), - } - } +impl TryFrom for CopyForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for CopyForm { diff --git a/yazi-parser/src/mgr/create.rs b/yazi-parser/src/mgr/create.rs index 5b38885e..bf8dc1ab 100644 --- a/yazi-parser/src/mgr/create.rs +++ b/yazi-parser/src/mgr/create.rs @@ -1,14 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct CreateForm { + #[serde(default)] pub dir: bool, + #[serde(default)] pub force: bool, } -impl From for CreateForm { - fn from(a: ActionCow) -> Self { Self { dir: a.bool("dir"), force: a.bool("force") } } +impl TryFrom for CreateForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for CreateForm { diff --git a/yazi-parser/src/mgr/hardlink.rs b/yazi-parser/src/mgr/hardlink.rs index 857a609f..22742d1e 100644 --- a/yazi-parser/src/mgr/hardlink.rs +++ b/yazi-parser/src/mgr/hardlink.rs @@ -1,14 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct HardlinkForm { + #[serde(default)] pub force: bool, + #[serde(default)] pub follow: bool, } -impl From for HardlinkForm { - fn from(a: ActionCow) -> Self { Self { force: a.bool("force"), follow: a.bool("follow") } } +impl TryFrom for HardlinkForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for HardlinkForm { diff --git a/yazi-parser/src/mgr/link.rs b/yazi-parser/src/mgr/link.rs index e1c39f5c..4569e339 100644 --- a/yazi-parser/src/mgr/link.rs +++ b/yazi-parser/src/mgr/link.rs @@ -1,14 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct LinkForm { + #[serde(default)] pub relative: bool, + #[serde(default)] pub force: bool, } -impl From for LinkForm { - fn from(a: ActionCow) -> Self { Self { relative: a.bool("relative"), force: a.bool("force") } } +impl TryFrom for LinkForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for LinkForm { diff --git a/yazi-parser/src/mgr/paste.rs b/yazi-parser/src/mgr/paste.rs index 2eecc4fe..033ec337 100644 --- a/yazi-parser/src/mgr/paste.rs +++ b/yazi-parser/src/mgr/paste.rs @@ -1,14 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct PasteForm { + #[serde(default)] pub force: bool, + #[serde(default)] pub follow: bool, } -impl From for PasteForm { - fn from(a: ActionCow) -> Self { Self { force: a.bool("force"), follow: a.bool("follow") } } +impl TryFrom for PasteForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for PasteForm { diff --git a/yazi-parser/src/mgr/reveal.rs b/yazi-parser/src/mgr/reveal.rs index b19172c9..133f9896 100644 --- a/yazi-parser/src/mgr/reveal.rs +++ b/yazi-parser/src/mgr/reveal.rs @@ -1,41 +1,53 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; +use yazi_core::mgr::CdSource; use yazi_fs::path::{clean_url, expand_url}; use yazi_shared::{event::ActionCow, url::UrlBuf}; use yazi_vfs::provider; -use crate::mgr::CdSource; - -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct RevealForm { + #[serde(alias = "0")] pub target: UrlBuf, + #[serde(default)] + pub raw: bool, + #[serde(default = "default_source")] pub source: CdSource, + #[serde(alias = "no-dummy", default)] pub no_dummy: bool, } -impl From for RevealForm { - fn from(mut a: ActionCow) -> Self { - let mut target = a.take_first().unwrap_or_default(); +impl TryFrom for RevealForm { + type Error = anyhow::Error; - if !a.bool("raw") { - target = expand_url(target); + fn try_from(a: ActionCow) -> Result { + let mut me: Self = a.deserialize()?; + + if !me.raw { + me.target = expand_url(me.target).into_owned(); } - if let Some(u) = provider::try_absolute(&target) + if let Some(u) = provider::try_absolute(&me.target) && u.is_owned() { - target = u.into_static(); + me.target = u.into_owned(); } - Self { target: clean_url(target), source: CdSource::Reveal, no_dummy: a.bool("no-dummy") } + me.target = clean_url(me.target); + Ok(me) } } impl From for RevealForm { - fn from(target: UrlBuf) -> Self { Self { target, source: CdSource::Reveal, no_dummy: false } } + fn from(target: UrlBuf) -> Self { + Self { target, raw: false, source: CdSource::Reveal, no_dummy: false } + } } impl From<(UrlBuf, CdSource)> for RevealForm { - fn from((target, source): (UrlBuf, CdSource)) -> Self { Self { target, source, no_dummy: false } } + fn from((target, source): (UrlBuf, CdSource)) -> Self { + Self { target, raw: false, source, no_dummy: false } + } } impl FromLua for RevealForm { @@ -45,3 +57,5 @@ impl FromLua for RevealForm { impl IntoLua for RevealForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } + +fn default_source() -> CdSource { CdSource::Reveal } diff --git a/yazi-parser/src/mgr/seek.rs b/yazi-parser/src/mgr/seek.rs index e9e49548..c80b0253 100644 --- a/yazi-parser/src/mgr/seek.rs +++ b/yazi-parser/src/mgr/seek.rs @@ -1,13 +1,17 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct SeekForm { + #[serde(alias = "0")] pub units: i16, } -impl From for SeekForm { - fn from(a: ActionCow) -> Self { Self { units: a.first().unwrap_or(0) } } +impl TryFrom for SeekForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for SeekForm { diff --git a/yazi-parser/src/mgr/spot.rs b/yazi-parser/src/mgr/spot.rs index 08fb39e3..39f6e473 100644 --- a/yazi-parser/src/mgr/spot.rs +++ b/yazi-parser/src/mgr/spot.rs @@ -1,13 +1,16 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug, Default)] +#[derive(Debug, Default, Deserialize)] pub struct SpotOpt { pub skip: Option, } -impl From for SpotOpt { - fn from(a: ActionCow) -> Self { Self { skip: a.get("skip").ok() } } +impl TryFrom for SpotOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl From for SpotOpt { diff --git a/yazi-parser/src/mgr/stash.rs b/yazi-parser/src/mgr/stash.rs index 8d725809..de8e843a 100644 --- a/yazi-parser/src/mgr/stash.rs +++ b/yazi-parser/src/mgr/stash.rs @@ -1,13 +1,14 @@ -use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Value}; use serde::{Deserialize, Serialize}; use yazi_binding::{SER_OPT, Url}; +use yazi_core::mgr::CdSource; use yazi_shared::{event::ActionCow, url::UrlBuf}; -use crate::mgr::{CdForm, CdSource}; +use crate::mgr::CdForm; #[derive(Debug, Deserialize, Serialize)] pub struct StashForm { + #[serde(alias = "0")] pub target: UrlBuf, pub source: CdSource, } @@ -15,11 +16,11 @@ pub struct StashForm { impl TryFrom for StashForm { type Error = anyhow::Error; - fn try_from(_: ActionCow) -> Result { bail!("unsupported") } + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } -impl From for StashForm { - fn from(form: CdForm) -> Self { Self { target: form.target, source: form.source } } +impl From<&CdForm> for StashForm { + fn from(form: &CdForm) -> Self { Self { target: form.target.clone(), source: form.source } } } impl FromLua for StashForm { diff --git a/yazi-parser/src/mgr/tab_close.rs b/yazi-parser/src/mgr/tab_close.rs index 20e5e868..bb47e595 100644 --- a/yazi-parser/src/mgr/tab_close.rs +++ b/yazi-parser/src/mgr/tab_close.rs @@ -1,13 +1,17 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct TabCloseForm { + #[serde(alias = "0", default)] pub idx: usize, } -impl From for TabCloseForm { - fn from(a: ActionCow) -> Self { Self { idx: a.first().unwrap_or(0) } } +impl TryFrom for TabCloseForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl From for TabCloseForm { diff --git a/yazi-parser/src/mgr/tab_create.rs b/yazi-parser/src/mgr/tab_create.rs index 0270af04..20c7090f 100644 --- a/yazi-parser/src/mgr/tab_create.rs +++ b/yazi-parser/src/mgr/tab_create.rs @@ -1,35 +1,45 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_boot::BOOT; use yazi_fs::path::{clean_url, expand_url}; -use yazi_shared::{event::ActionCow, url::UrlCow}; +use yazi_shared::{event::ActionCow, url::UrlBuf}; use yazi_vfs::provider; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct TabCreateForm { - pub url: Option>, + #[serde(alias = "0")] + pub target: Option, + #[serde(default)] + pub current: bool, + #[serde(default)] + pub raw: bool, } -impl From for TabCreateForm { - fn from(mut a: ActionCow) -> Self { - if a.bool("current") { - return Self { url: None }; +impl TryFrom for TabCreateForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + let mut me: Self = a.deserialize()?; + + if me.current { + me.target = None; + } else if me.target.is_none() { + me.target = Some(BOOT.cwds[0].clone()); + } else if let Some(mut target) = me.target { + if !me.raw { + target = expand_url(target).into_owned(); + } + + if let Some(u) = provider::try_absolute(&target) + && u.is_owned() + { + target = u.into_owned(); + } + + me.target = Some(clean_url(target)); } - let Ok(mut url) = a.take_first() else { - return Self { url: Some(UrlCow::from(&BOOT.cwds[0])) }; - }; - - if !a.bool("raw") { - url = expand_url(url); - } - - if let Some(u) = provider::try_absolute(&url) - && u.is_owned() - { - url = u.into_static(); - } - - Self { url: Some(clean_url(url).into()) } + Ok(me) } } diff --git a/yazi-parser/src/mgr/tab_switch.rs b/yazi-parser/src/mgr/tab_switch.rs index 2075505e..74b33e8c 100644 --- a/yazi-parser/src/mgr/tab_switch.rs +++ b/yazi-parser/src/mgr/tab_switch.rs @@ -1,16 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct TabSwitchForm { + #[serde(alias = "0")] pub step: isize, + #[serde(default)] pub relative: bool, } -impl From for TabSwitchForm { - fn from(a: ActionCow) -> Self { - Self { step: a.first().unwrap_or(0), relative: a.bool("relative") } - } +impl TryFrom for TabSwitchForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for TabSwitchForm { diff --git a/yazi-parser/src/mgr/update_peeked.rs b/yazi-parser/src/mgr/update_peeked.rs index 5ce7201b..0928c546 100644 --- a/yazi-parser/src/mgr/update_peeked.rs +++ b/yazi-parser/src/mgr/update_peeked.rs @@ -1,4 +1,4 @@ -use anyhow::bail; +use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_core::tab::PreviewLock; use yazi_shared::event::ActionCow; @@ -12,15 +12,9 @@ impl TryFrom for UpdatePeekedForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - let Some(lock) = a.take_any("lock") else { - bail!("Invalid 'lock' in UpdatePeekedForm"); - }; - - Ok(Self { lock }) + Ok(Self { + lock: a.take_any("lock").ok_or_else(|| anyhow!("Invalid 'lock' in UpdatePeekedForm"))?, + }) } } diff --git a/yazi-parser/src/mgr/update_spotted.rs b/yazi-parser/src/mgr/update_spotted.rs index e25ceb50..0619d489 100644 --- a/yazi-parser/src/mgr/update_spotted.rs +++ b/yazi-parser/src/mgr/update_spotted.rs @@ -1,4 +1,4 @@ -use anyhow::bail; +use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_core::spot::SpotLock; use yazi_shared::event::ActionCow; @@ -12,11 +12,9 @@ impl TryFrom for UpdateSpottedForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - let Some(lock) = a.take_any("lock") else { - bail!("Invalid 'lock' in UpdateSpottedForm"); - }; - - Ok(Self { lock }) + Ok(Self { + lock: a.take_any("lock").ok_or_else(|| anyhow!("Invalid 'lock' in UpdateSpottedForm"))?, + }) } } diff --git a/yazi-parser/src/mgr/update_yanked.rs b/yazi-parser/src/mgr/update_yanked.rs index 23b13ac2..68a14acb 100644 --- a/yazi-parser/src/mgr/update_yanked.rs +++ b/yazi-parser/src/mgr/update_yanked.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use anyhow::bail; +use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; use yazi_shared::event::ActionCow; @@ -18,11 +18,7 @@ impl TryFrom for UpdateYankedForm<'_> { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - let Some(state) = a.take_any("state") else { - bail!("Invalid 'state' in UpdateYankedForm"); - }; - - Ok(Self(state)) + a.take_any(0).map(Self).ok_or_else(|| anyhow!("Invalid payload in UpdateYankedForm")) } } diff --git a/yazi-parser/src/mgr/visual_mode.rs b/yazi-parser/src/mgr/visual_mode.rs index cbcc30dc..b6783ad0 100644 --- a/yazi-parser/src/mgr/visual_mode.rs +++ b/yazi-parser/src/mgr/visual_mode.rs @@ -1,13 +1,17 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct VisualModeForm { + #[serde(default)] pub unset: bool, } -impl From for VisualModeForm { - fn from(a: ActionCow) -> Self { Self { unset: a.bool("unset") } } +impl TryFrom for VisualModeForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for VisualModeForm { diff --git a/yazi-parser/src/mgr/yank.rs b/yazi-parser/src/mgr/yank.rs index 956d1025..a1e2565d 100644 --- a/yazi-parser/src/mgr/yank.rs +++ b/yazi-parser/src/mgr/yank.rs @@ -1,13 +1,17 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct YankForm { + #[serde(default)] pub cut: bool, } -impl From for YankForm { - fn from(a: ActionCow) -> Self { Self { cut: a.bool("cut") } } +impl TryFrom for YankForm { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { Ok(a.deserialize()?) } } impl FromLua for YankForm { diff --git a/yazi-plugin/preset/plugins/extract.lua b/yazi-plugin/preset/plugins/extract.lua index 0b7c2aa7..952890d4 100644 --- a/yazi-plugin/preset/plugins/extract.lua +++ b/yazi-plugin/preset/plugins/extract.lua @@ -6,12 +6,12 @@ function M:setup() ps.sub_remote("extract", function(args) ya.async(function() for i, arg in ipairs(args) do - ya.task("plugin", { + local in_ = { self._id, args = { arg, "", noisy = #args == 1 }, - title = "Extract " .. arg, track = i == 1, - }):spawn() + } + ya.task("plugin", in_):name("Extract " .. arg):spawn() end end) end) diff --git a/yazi-plugin/preset/plugins/session.lua b/yazi-plugin/preset/plugins/session.lua index b35e5261..1e1b2485 100644 --- a/yazi-plugin/preset/plugins/session.lua +++ b/yazi-plugin/preset/plugins/session.lua @@ -1,6 +1,6 @@ local function setup(_, opts) if opts.sync_yanked then - ps.sub_remote("@yank", function(state) ya.emit("update_yanked", { state = state }) end) + ps.sub_remote("@yank", function(state) ya.emit("update_yanked", { state }) end) end end diff --git a/yazi-plugin/src/tasks/option.rs b/yazi-plugin/src/tasks/option.rs index c6fccf7f..18a863c4 100644 --- a/yazi-plugin/src/tasks/option.rs +++ b/yazi-plugin/src/tasks/option.rs @@ -1,5 +1,6 @@ -use mlua::{UserData, UserDataMethods}; +use mlua::{AnyUserData, UserData, UserDataMethods}; use yazi_proxy::TasksProxy; +use yazi_scheduler::TaskIn; use crate::tasks::Task; @@ -8,6 +9,10 @@ pub(crate) struct TaskOpt(pub(crate) yazi_core::tasks::TaskOpt); impl UserData for TaskOpt { fn add_methods>(methods: &mut M) { + methods.add_function_mut("name", |_, (ud, name): (AnyUserData, mlua::String)| { + ud.borrow_mut::()?.0.set_title(name.to_string_lossy()); + Ok(ud) + }); methods.add_async_method_once("spawn", |_, me, ()| async move { Ok(Task { id: TasksProxy::spawn(me.0).await? }) }); diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 4cf02f00..2432abbe 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -3,7 +3,7 @@ use std::{str::FromStr, time::Duration}; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value}; use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_binding::{InputRx, elements::{Line, Pos, Text}, runtime}; -use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}}; +use yazi_config::{Platform, keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}}; use yazi_core::notify::MessageOpt; use yazi_macro::relay; use yazi_proxy::{ConfirmProxy, InputProxy, NotifyProxy, WhichProxy}; @@ -28,7 +28,7 @@ impl Utils { on: Self::parse_keys(cand.raw_get("on")?)?, run: vec![relay!(which:callback, [i + 1])], desc: cand.raw_get("desc").ok(), - r#for: None, + r#for: Platform::All, })) }) .collect::>()?; diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 35aba306..a38baf2f 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -18,6 +18,7 @@ yazi-core = { path = "../yazi-core", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-shim = { path = "../yazi-shim", version = "26.2.2" } yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies diff --git a/yazi-proxy/src/mgr.rs b/yazi-proxy/src/mgr.rs index 82aebadd..5253218c 100644 --- a/yazi-proxy/src/mgr.rs +++ b/yazi-proxy/src/mgr.rs @@ -1,6 +1,7 @@ -use yazi_core::{mgr::{DisplaceOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt}, spot::SpotLock}; +use yazi_core::{mgr::{CdSource, DisplaceOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt}, spot::SpotLock}; use yazi_macro::{emit, relay}; use yazi_shared::{Id, SStr, url::UrlBuf}; +use yazi_shim::strum::IntoStr; pub struct MgrProxy; @@ -9,8 +10,10 @@ impl MgrProxy { emit!(Call(relay!(mgr:arrow, [step.into()]))); } - pub fn cd(target: impl Into) { - emit!(Call(relay!(mgr:cd, [target.into()]).with("raw", true))); + pub fn cd(target: impl Into, source: CdSource) { + emit!(Call( + relay!(mgr:cd, [target.into()]).with("raw", true).with("source", source.into_str()) + )); } pub fn displace_do(tab: Id, opt: DisplaceOpt) { diff --git a/yazi-scheduler/src/in.rs b/yazi-scheduler/src/in.rs index e602f0b3..b463e70e 100644 --- a/yazi-scheduler/src/in.rs +++ b/yazi-scheduler/src/in.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; -use yazi_shared::Id; +use yazi_shared::{Id, SStr}; -pub(crate) trait TaskIn { +pub trait TaskIn { type Prog; fn id(&self) -> Id; @@ -10,4 +10,6 @@ pub(crate) trait TaskIn { fn set_id(&mut self, id: Id) -> &mut Self; fn title(&self) -> Cow<'_, str>; + + fn set_title(&mut self, _title: impl Into) -> &mut Self { self } } diff --git a/yazi-scheduler/src/plugin/in.rs b/yazi-scheduler/src/plugin/in.rs index 4bbeeebf..0d0dc423 100644 --- a/yazi-scheduler/src/plugin/in.rs +++ b/yazi-scheduler/src/plugin/in.rs @@ -65,6 +65,11 @@ impl TaskIn for PluginInEntry { Cow::Borrowed(&self.title) } } + + fn set_title(&mut self, title: impl Into) -> &mut Self { + self.title = title.into(); + self + } } impl PluginInEntry { @@ -80,11 +85,10 @@ impl FromLua for PluginInEntry { }; Ok(Self { - id: Id::ZERO, plugin: t.raw_get::(1)?.into(), - args: Sendable::table_to_args(lua, t.raw_get("args")?)?, - title: t.raw_get::>("title")?.unwrap_or_default().into(), - track: t.raw_get("track")?, + args: Sendable::table_to_args(lua, t.raw_get("args")?)?, + track: t.raw_get("track")?, + ..Default::default() }) } } diff --git a/yazi-shared/src/url/cov.rs b/yazi-shared/src/url/cov.rs index 38b3f504..7b77c24b 100644 --- a/yazi-shared/src/url/cov.rs +++ b/yazi-shared/src/url/cov.rs @@ -82,6 +82,10 @@ impl From> for UrlBufCov { fn from(value: Url<'_>) -> Self { Self(value.to_owned()) } } +impl From> for UrlBufCov { + fn from(value: UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) } +} + impl From<&UrlCov<'_>> for UrlBufCov { fn from(value: &UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) } } diff --git a/yazi-shared/src/url/cow.rs b/yazi-shared/src/url/cow.rs index b1de5301..323e34fb 100644 --- a/yazi-shared/src/url/cow.rs +++ b/yazi-shared/src/url/cow.rs @@ -261,21 +261,6 @@ impl<'a> UrlCow<'a> { pub fn into_path(self) -> PathCow<'a> { self.into_pair().1 } - pub fn into_static(self) -> UrlCow<'static> { - match self { - UrlCow::Regular(loc) => UrlCow::Regular(loc.into_owned().into()), - UrlCow::Search { loc, domain } => { - UrlCow::Search { loc: loc.into_owned().into(), domain: domain.into_owned().into() } - } - UrlCow::Archive { loc, domain } => { - UrlCow::Archive { loc: loc.into_owned().into(), domain: domain.into_owned().into() } - } - UrlCow::Sftp { loc, domain } => { - UrlCow::Sftp { loc: loc.into_owned().into(), domain: domain.into_owned().into() } - } - } - } - pub fn to_owned(&self) -> UrlBuf { self.as_url().into() } } diff --git a/yazi-shared/src/url/traits.rs b/yazi-shared/src/url/traits.rs index 70ab0c29..bb5d69fe 100644 --- a/yazi-shared/src/url/traits.rs +++ b/yazi-shared/src/url/traits.rs @@ -1,6 +1,8 @@ -use std::path::{Path, PathBuf}; +use std::{hash::BuildHasher, path::{Path, PathBuf}}; -use crate::{loc::Loc, url::{Url, UrlBuf, UrlCow}}; +use hashbrown::{HashMap, hash_map::EntryRef}; + +use crate::{loc::Loc, url::{Url, UrlBuf, UrlBufCov, UrlCov, UrlCow}}; // --- AsUrl pub trait AsUrl { @@ -86,3 +88,63 @@ where { fn from(value: &'a mut T) -> Self { value.as_url() } } + +// --- UrlMapExt +pub trait UrlMapExt { + fn get_or_insert_default(&mut self, url: U) -> &mut V + where + U: AsUrl, + V: Default; + + fn get_or_insert_with(&mut self, url: U, default: F) -> &mut V + where + U: AsUrl, + F: FnOnce(Url<'_>) -> V; +} + +impl UrlMapExt for HashMap +where + S: BuildHasher, +{ + fn get_or_insert_default(&mut self, url: U) -> &mut V + where + U: AsUrl, + V: Default, + { + self.get_or_insert_with(url, |_| Default::default()) + } + + fn get_or_insert_with(&mut self, url: U, default: F) -> &mut V + where + U: AsUrl, + F: FnOnce(Url<'_>) -> V, + { + let url = url.as_url(); + match self.entry_ref(&url) { + EntryRef::Occupied(oe) => oe.into_mut(), + EntryRef::Vacant(ve) => ve.insert_with_key(url.into(), default(url)), + } + } +} + +// --- UrlCovMapExt +pub trait UrlCovMapExt { + fn get_or_insert_default(&mut self, url: UrlCov<'_>) -> &mut V + where + V: Default; +} + +impl UrlCovMapExt for HashMap +where + S: BuildHasher, +{ + fn get_or_insert_default(&mut self, url: UrlCov<'_>) -> &mut V + where + V: Default, + { + match self.entry_ref(&url) { + EntryRef::Occupied(oe) => oe.into_mut(), + EntryRef::Vacant(ve) => ve.insert_with_key(url.into(), Default::default()), + } + } +}