From 92880b844b921ec49d5eeae8a56010a944ff6080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 26 Jan 2026 07:36:35 +0800 Subject: [PATCH] refactor: make command data cloneable (#3613) --- Cargo.lock | 8 ++++ Cargo.toml | 1 + yazi-actor/src/confirm/close.rs | 5 +-- yazi-actor/src/confirm/show.rs | 2 +- yazi-actor/src/lives/core.rs | 3 ++ yazi-actor/src/lives/mod.rs | 2 +- yazi-actor/src/lives/which.rs | 25 ++++++++++++ yazi-actor/src/mgr/displace.rs | 5 ++- yazi-actor/src/mgr/displace_do.rs | 2 +- yazi-actor/src/mgr/open_do.rs | 2 +- yazi-actor/src/mgr/quit.rs | 8 ++-- yazi-actor/src/pick/close.rs | 4 +- yazi-actor/src/which/callback.rs | 2 +- yazi-config/src/keymap/chord.rs | 2 +- yazi-config/src/keymap/cow.rs | 2 +- yazi-config/src/popup/options.rs | 6 +-- yazi-core/src/confirm/confirm.rs | 6 +-- yazi-core/src/pick/pick.rs | 5 +-- yazi-dds/src/client.rs | 2 +- yazi-dds/src/ember/bulk.rs | 2 +- yazi-dds/src/ember/bye.rs | 2 +- yazi-dds/src/ember/cd.rs | 2 +- yazi-dds/src/ember/custom.rs | 2 +- yazi-dds/src/ember/delete.rs | 2 +- yazi-dds/src/ember/duplicate.rs | 2 +- yazi-dds/src/ember/ember.rs | 2 +- yazi-dds/src/ember/hey.rs | 2 +- yazi-dds/src/ember/hi.rs | 2 +- yazi-dds/src/ember/hover.rs | 2 +- yazi-dds/src/ember/load.rs | 2 +- yazi-dds/src/ember/mount.rs | 2 +- yazi-dds/src/ember/move.rs | 2 +- yazi-dds/src/ember/rename.rs | 2 +- yazi-dds/src/ember/tab.rs | 2 +- yazi-dds/src/ember/trash.rs | 2 +- yazi-dds/src/ember/yank.rs | 2 +- yazi-dds/src/payload.rs | 2 +- yazi-dds/src/sendable.rs | 27 ++++++++----- yazi-fm/src/app/commands/plugin.rs | 2 +- yazi-fm/src/app/commands/resume.rs | 6 +-- yazi-fm/src/app/commands/stop.rs | 2 +- yazi-fm/src/signals.rs | 22 +++++----- yazi-parser/Cargo.toml | 1 + yazi-parser/src/app/notify.rs | 1 + yazi-parser/src/app/plugin.rs | 53 +++++++++++++++---------- yazi-parser/src/app/resume.rs | 18 ++++++--- yazi-parser/src/app/stop.rs | 18 ++++++--- yazi-parser/src/arrow.rs | 2 +- yazi-parser/src/cmp/show.rs | 2 +- yazi-parser/src/confirm/show.rs | 15 ++++--- yazi-parser/src/input/show.rs | 4 +- yazi-parser/src/mgr/displace_do.rs | 6 +-- yazi-parser/src/mgr/filter.rs | 2 +- yazi-parser/src/mgr/find_do.rs | 4 +- yazi-parser/src/mgr/open.rs | 2 +- yazi-parser/src/mgr/open_do.rs | 4 +- yazi-parser/src/mgr/search.rs | 2 +- yazi-parser/src/mgr/update_files.rs | 2 +- yazi-parser/src/mgr/update_mimes.rs | 2 +- yazi-parser/src/mgr/update_peeked.rs | 6 +-- yazi-parser/src/mgr/update_spotted.rs | 6 +-- yazi-parser/src/mgr/update_yanked.rs | 2 +- yazi-parser/src/notify/tick.rs | 2 +- yazi-parser/src/pick/show.rs | 8 ++-- yazi-parser/src/tasks/process_open.rs | 2 +- yazi-parser/src/tasks/update_succeed.rs | 2 +- yazi-parser/src/which/callback.rs | 6 +-- yazi-parser/src/which/show.rs | 2 +- yazi-plugin/src/isolate/peek.rs | 8 ++-- yazi-plugin/src/isolate/seek.rs | 8 ++-- yazi-plugin/src/utils/layer.rs | 2 +- yazi-plugin/src/utils/sync.rs | 34 ++++++++-------- yazi-proxy/src/app.rs | 14 +++---- yazi-proxy/src/confirm.rs | 12 +++--- yazi-proxy/src/pick.rs | 8 ++-- yazi-shared/Cargo.toml | 1 + yazi-shared/src/data/any.rs | 28 +++++++++++++ yazi-shared/src/data/data.rs | 12 +++--- yazi-shared/src/data/key.rs | 2 +- yazi-shared/src/data/mod.rs | 2 +- yazi-shared/src/event/cmd.rs | 8 ++-- 81 files changed, 297 insertions(+), 203 deletions(-) create mode 100644 yazi-actor/src/lives/which.rs create mode 100644 yazi-shared/src/data/any.rs diff --git a/Cargo.lock b/Cargo.lock index 19715034..57172e35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,6 +1242,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "ecdsa" version = "0.16.9" @@ -5726,6 +5732,7 @@ dependencies = [ "anyhow", "bitflags 2.10.0", "crossterm 0.29.0", + "dyn-clone", "hashbrown", "mlua", "ordered-float 5.1.0", @@ -5844,6 +5851,7 @@ version = "26.1.22" dependencies = [ "anyhow", "crossterm 0.29.0", + "dyn-clone", "foldhash", "futures", "hashbrown", diff --git a/Cargo.toml b/Cargo.toml index 5a6168ae..8a133b3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ clap = { version = "4.5.54", features = [ "derive" ] } core-foundation-sys = "0.8.7" crossterm = { version = "0.29.0", features = [ "event-stream" ] } dirs = "6.0.0" +dyn-clone = "1.0.20" foldhash = "0.2.0" futures = "0.3.31" globset = "0.4.18" diff --git a/yazi-actor/src/confirm/close.rs b/yazi-actor/src/confirm/close.rs index 7bc18533..315dd335 100644 --- a/yazi-actor/src/confirm/close.rs +++ b/yazi-actor/src/confirm/close.rs @@ -13,10 +13,7 @@ impl Actor for Close { const NAME: &str = "close"; fn act(cx: &mut Ctx, opt: Self::Options) -> Result { - if let Some(cb) = cx.confirm.callback.take() { - _ = cb.send(opt.submit); - } - + cx.confirm.token.complete(opt.submit); cx.confirm.visible = false; succ!(render!()); } diff --git a/yazi-actor/src/confirm/show.rs b/yazi-actor/src/confirm/show.rs index 71be83d9..d4d5be25 100644 --- a/yazi-actor/src/confirm/show.rs +++ b/yazi-actor/src/confirm/show.rs @@ -23,7 +23,7 @@ impl Actor for Show { confirm.position = opt.cfg.position; confirm.offset = 0; - confirm.callback = Some(opt.tx); + confirm.token = opt.token; confirm.visible = true; succ!(render!()); diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index a0341054..d30a1391 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -13,6 +13,7 @@ pub(super) struct Core { c_tasks: Option, c_yanked: Option, c_layer: Option, + c_which: Option, } impl Deref for Core { @@ -31,6 +32,7 @@ impl Core { c_tasks: None, c_yanked: None, c_layer: None, + c_which: None, }) } } @@ -58,6 +60,7 @@ impl UserData for Core { b"layer" => { reuse!(layer, Ok::<_, mlua::Error>(yazi_plugin::bindings::Layer::from(me.layer()))) } + b"which" => reuse!(which, super::Which::make(&me.which)), _ => Value::Nil, }) }); diff --git a/yazi-actor/src/lives/mod.rs b/yazi-actor/src/lives/mod.rs index 843fa013..d6c82311 100644 --- a/yazi-actor/src/lives/mod.rs +++ b/yazi-actor/src/lives/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks yanked); +yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks which yanked); diff --git a/yazi-actor/src/lives/which.rs b/yazi-actor/src/lives/which.rs new file mode 100644 index 00000000..ec622e0c --- /dev/null +++ b/yazi-actor/src/lives/which.rs @@ -0,0 +1,25 @@ +use std::ops::Deref; + +use mlua::{AnyUserData, UserData, UserDataFields}; + +use super::{Lives, PtrCell}; + +pub(super) struct Which { + inner: PtrCell, +} + +impl Deref for Which { + type Target = yazi_core::which::Which; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Which { + pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result { + Lives::scoped_userdata(Self { inner: inner.into() }) + } +} + +impl UserData for Which { + fn add_fields>(fields: &mut F) {} +} diff --git a/yazi-actor/src/mgr/displace.rs b/yazi-actor/src/mgr/displace.rs index ef039798..6469ea73 100644 --- a/yazi-actor/src/mgr/displace.rs +++ b/yazi-actor/src/mgr/displace.rs @@ -22,7 +22,10 @@ impl Actor for Displace { let tab = cx.tab().id; let from = cx.cwd().to_owned(); tokio::spawn(async move { - MgrProxy::displace_do(tab, DisplaceDoOpt { to: provider::canonicalize(&from).await, from }); + MgrProxy::displace_do(tab, DisplaceDoOpt { + to: provider::canonicalize(&from).await.map_err(Into::into), + from, + }); }); succ!(); diff --git a/yazi-actor/src/mgr/displace_do.rs b/yazi-actor/src/mgr/displace_do.rs index aa707ed2..3564c61c 100644 --- a/yazi-actor/src/mgr/displace_do.rs +++ b/yazi-actor/src/mgr/displace_do.rs @@ -20,7 +20,7 @@ impl Actor for DisplaceDo { let to = match opt.to { Ok(url) => url, - Err(e) => return act!(mgr:update_files, cx, FilesOp::IOErr(opt.from, e.into())), + Err(e) => return act!(mgr:update_files, cx, FilesOp::IOErr(opt.from, e)), }; if !to.is_absolute() { diff --git a/yazi-actor/src/mgr/open_do.rs b/yazi-actor/src/mgr/open_do.rs index 73130368..0a7cd1a7 100644 --- a/yazi-actor/src/mgr/open_do.rs +++ b/yazi-actor/src/mgr/open_do.rs @@ -41,7 +41,7 @@ impl Actor for OpenDo { let urls: Vec<_> = [UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { - if let Ok(choice) = pick.await { + if let Some(choice) = pick.await { TasksProxy::open_shell_compat(ProcessOpenOpt { cwd: opt.cwd, cmd: openers[choice].run.clone().into(), diff --git a/yazi-actor/src/mgr/quit.rs b/yazi-actor/src/mgr/quit.rs index 3014c7ef..37cb7428 100644 --- a/yazi-actor/src/mgr/quit.rs +++ b/yazi-actor/src/mgr/quit.rs @@ -33,7 +33,7 @@ impl Actor for Quit { tokio::spawn(async move { let mut i = 0; - let mut rx = ConfirmProxy::show_rx(ConfirmCfg::quit(left, left_names)); + let token = ConfirmProxy::show_sync(ConfirmCfg::quit(left, left_names)); loop { select! { _ = time::sleep(Duration::from_millis(50)) => { @@ -44,8 +44,8 @@ impl Actor for Quit { return; } } - b = &mut rx => { - if b.unwrap_or(false) { + b = token.future() => { + if b { emit!(Quit(event)); } return; @@ -53,7 +53,7 @@ impl Actor for Quit { } } - if rx.await.unwrap_or(false) { + if token.future().await { emit!(Quit(event)); } }); diff --git a/yazi-actor/src/pick/close.rs b/yazi-actor/src/pick/close.rs index 2962ab57..02596f6b 100644 --- a/yazi-actor/src/pick/close.rs +++ b/yazi-actor/src/pick/close.rs @@ -1,4 +1,4 @@ -use anyhow::{Result, anyhow}; +use anyhow::Result; use yazi_macro::{render, succ}; use yazi_parser::pick::CloseOpt; use yazi_shared::data::Data; @@ -15,7 +15,7 @@ impl Actor for Close { fn act(cx: &mut Ctx, opt: Self::Options) -> Result { let pick = &mut cx.pick; if let Some(cb) = pick.callback.take() { - _ = cb.send(if opt.submit { Ok(pick.cursor) } else { Err(anyhow!("canceled")) }); + _ = cb.send(if opt.submit { Some(pick.cursor) } else { None }); } pick.cursor = 0; diff --git a/yazi-actor/src/which/callback.rs b/yazi-actor/src/which/callback.rs index 7a672f65..0e0d6f7d 100644 --- a/yazi-actor/src/which/callback.rs +++ b/yazi-actor/src/which/callback.rs @@ -13,7 +13,7 @@ impl Actor for Callback { const NAME: &str = "callback"; fn act(_: &mut Ctx, opt: Self::Options) -> Result { - opt.tx.try_send(opt.idx)?; + opt.tx.send(opt.idx)?; succ!(); } } diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index 80923bd7..1d35909a 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -9,7 +9,7 @@ use super::Key; static RE: OnceLock = OnceLock::new(); -#[derive(Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize)] pub struct Chord { #[serde(deserialize_with = "super::deserialize_on")] pub on: Vec, diff --git a/yazi-config/src/keymap/cow.rs b/yazi-config/src/keymap/cow.rs index d30e25b2..78b71022 100644 --- a/yazi-config/src/keymap/cow.rs +++ b/yazi-config/src/keymap/cow.rs @@ -4,7 +4,7 @@ use yazi_shared::event::CmdCow; use super::Chord; -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum ChordCow { Owned(Chord), Borrowed(&'static Chord), diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index 55a41be1..25ab69e0 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -4,7 +4,7 @@ use yazi_shared::{scheme::Encode as EncodeScheme, strand::ToStrand, url::{Url, U use super::{Offset, Position}; use crate::YAZI; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct InputCfg { pub title: String, pub value: String, @@ -15,14 +15,14 @@ pub struct InputCfg { pub completion: bool, } -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct PickCfg { pub title: String, pub items: Vec, pub position: Position, } -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ConfirmCfg { pub position: Position, pub title: Line<'static>, diff --git a/yazi-core/src/confirm/confirm.rs b/yazi-core/src/confirm/confirm.rs index 0fb62fe2..a0d24343 100644 --- a/yazi-core/src/confirm/confirm.rs +++ b/yazi-core/src/confirm/confirm.rs @@ -1,6 +1,6 @@ use ratatui::{text::Line, widgets::Paragraph}; -use tokio::sync::oneshot::Sender; use yazi_config::popup::Position; +use yazi_shared::CompletionToken; #[derive(Default)] pub struct Confirm { @@ -11,6 +11,6 @@ pub struct Confirm { pub position: Position, pub offset: usize, - pub callback: Option>, - pub visible: bool, + pub token: CompletionToken, + pub visible: bool, } diff --git a/yazi-core/src/pick/pick.rs b/yazi-core/src/pick/pick.rs index 15102d4b..6808b14c 100644 --- a/yazi-core/src/pick/pick.rs +++ b/yazi-core/src/pick/pick.rs @@ -1,5 +1,4 @@ -use anyhow::Result; -use tokio::sync::oneshot::Sender; +use tokio::sync::mpsc::UnboundedSender; use yazi_config::{YAZI, popup::Position}; use yazi_widgets::Scrollable; @@ -11,7 +10,7 @@ pub struct Pick { pub offset: usize, pub cursor: usize, - pub callback: Option>>, + pub callback: Option>>, pub visible: bool, } diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index 20ec57d2..84f40b13 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -24,7 +24,7 @@ pub struct Client { pub(super) abilities: HashSet, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct Peer { pub(super) abilities: HashSet, } diff --git a/yazi-dds/src/ember/bulk.rs b/yazi-dds/src/ember/bulk.rs index 53343f8e..93650d14 100644 --- a/yazi-dds/src/ember/bulk.rs +++ b/yazi-dds/src/ember/bulk.rs @@ -5,7 +5,7 @@ use yazi_shared::url::{Url, UrlCow}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberBulk<'a> { pub changes: HashMap, UrlCow<'a>>, } diff --git a/yazi-dds/src/ember/bye.rs b/yazi-dds/src/ember/bye.rs index 2b16edd8..6f2440dd 100644 --- a/yazi-dds/src/ember/bye.rs +++ b/yazi-dds/src/ember/bye.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberBye; impl EmberBye { diff --git a/yazi-dds/src/ember/cd.rs b/yazi-dds/src/ember/cd.rs index d8219eab..246cad4e 100644 --- a/yazi-dds/src/ember/cd.rs +++ b/yazi-dds/src/ember/cd.rs @@ -6,7 +6,7 @@ use yazi_shared::{Id, url::UrlBuf}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberCd<'a> { pub tab: Id, pub url: Cow<'a, UrlBuf>, diff --git a/yazi-dds/src/ember/custom.rs b/yazi-dds/src/ember/custom.rs index 4b05f30a..8f3a8f64 100644 --- a/yazi-dds/src/ember/custom.rs +++ b/yazi-dds/src/ember/custom.rs @@ -5,7 +5,7 @@ use yazi_shared::data::Data; use super::Ember; use crate::Sendable; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct EmberCustom { pub kind: String, pub data: Data, diff --git a/yazi-dds/src/ember/delete.rs b/yazi-dds/src/ember/delete.rs index c0a9ff25..8ceb8443 100644 --- a/yazi-dds/src/ember/delete.rs +++ b/yazi-dds/src/ember/delete.rs @@ -6,7 +6,7 @@ use yazi_shared::url::UrlBuf; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberDelete<'a> { pub urls: Cow<'a, Vec>, } diff --git a/yazi-dds/src/ember/duplicate.rs b/yazi-dds/src/ember/duplicate.rs index 2c41b83f..cb024124 100644 --- a/yazi-dds/src/ember/duplicate.rs +++ b/yazi-dds/src/ember/duplicate.rs @@ -6,7 +6,7 @@ use yazi_shared::url::UrlBuf; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberDuplicate<'a> { pub items: Cow<'a, Vec>, } diff --git a/yazi-dds/src/ember/ember.rs b/yazi-dds/src/ember/ember.rs index b7e07860..230e73dd 100644 --- a/yazi-dds/src/ember/ember.rs +++ b/yazi-dds/src/ember/ember.rs @@ -5,7 +5,7 @@ use yazi_shared::Id; use super::{EmberBulk, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank}; use crate::Payload; -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum Ember<'a> { Hi(EmberHi<'a>), Hey(EmberHey), diff --git a/yazi-dds/src/ember/hey.rs b/yazi-dds/src/ember/hey.rs index 850fdb46..a0b591b7 100644 --- a/yazi-dds/src/ember/hey.rs +++ b/yazi-dds/src/ember/hey.rs @@ -7,7 +7,7 @@ use super::{Ember, EmberHi}; use crate::Peer; /// Server handshake -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberHey { pub peers: HashMap, pub version: SStr, diff --git a/yazi-dds/src/ember/hi.rs b/yazi-dds/src/ember/hi.rs index 5ebe4534..baa102f4 100644 --- a/yazi-dds/src/ember/hi.rs +++ b/yazi-dds/src/ember/hi.rs @@ -8,7 +8,7 @@ use yazi_shared::SStr; use super::Ember; /// Client handshake -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberHi<'a> { /// Kinds of events the client can handle pub abilities: HashSet>, diff --git a/yazi-dds/src/ember/hover.rs b/yazi-dds/src/ember/hover.rs index ea09cbd9..dcee7f12 100644 --- a/yazi-dds/src/ember/hover.rs +++ b/yazi-dds/src/ember/hover.rs @@ -6,7 +6,7 @@ use yazi_shared::{Id, url::UrlBuf}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberHover<'a> { pub tab: Id, pub url: Option>, diff --git a/yazi-dds/src/ember/load.rs b/yazi-dds/src/ember/load.rs index 86bf4fb4..84ef2576 100644 --- a/yazi-dds/src/ember/load.rs +++ b/yazi-dds/src/ember/load.rs @@ -7,7 +7,7 @@ use yazi_shared::{Id, url::UrlBuf}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberLoad<'a> { pub tab: Id, pub url: Cow<'a, UrlBuf>, diff --git a/yazi-dds/src/ember/mount.rs b/yazi-dds/src/ember/mount.rs index 6e061299..834ae7a2 100644 --- a/yazi-dds/src/ember/mount.rs +++ b/yazi-dds/src/ember/mount.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberMount; impl EmberMount { diff --git a/yazi-dds/src/ember/move.rs b/yazi-dds/src/ember/move.rs index 49db53e5..363836cb 100644 --- a/yazi-dds/src/ember/move.rs +++ b/yazi-dds/src/ember/move.rs @@ -6,7 +6,7 @@ use yazi_shared::url::UrlBuf; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberMove<'a> { pub items: Cow<'a, Vec>, } diff --git a/yazi-dds/src/ember/rename.rs b/yazi-dds/src/ember/rename.rs index 85810ef7..a86cbf2f 100644 --- a/yazi-dds/src/ember/rename.rs +++ b/yazi-dds/src/ember/rename.rs @@ -6,7 +6,7 @@ use yazi_shared::{Id, url::UrlBuf}; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberRename<'a> { pub tab: Id, pub from: Cow<'a, UrlBuf>, diff --git a/yazi-dds/src/ember/tab.rs b/yazi-dds/src/ember/tab.rs index 23064421..371023c7 100644 --- a/yazi-dds/src/ember/tab.rs +++ b/yazi-dds/src/ember/tab.rs @@ -4,7 +4,7 @@ use yazi_shared::Id; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberTab { pub id: Id, } diff --git a/yazi-dds/src/ember/trash.rs b/yazi-dds/src/ember/trash.rs index 020dbaad..766d9b19 100644 --- a/yazi-dds/src/ember/trash.rs +++ b/yazi-dds/src/ember/trash.rs @@ -6,7 +6,7 @@ use yazi_shared::url::UrlBuf; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberTrash<'a> { pub urls: Cow<'a, Vec>, } diff --git a/yazi-dds/src/ember/yank.rs b/yazi-dds/src/ember/yank.rs index 7ea3a894..5360830e 100644 --- a/yazi-dds/src/ember/yank.rs +++ b/yazi-dds/src/ember/yank.rs @@ -8,7 +8,7 @@ use yazi_shared::url::UrlBufCov; use super::Ember; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct EmberYank<'a>(UpdateYankedOpt<'a>); impl<'a> EmberYank<'a> { diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index caedd655..49edbf05 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -7,7 +7,7 @@ use yazi_shared::Id; use crate::{ID, ember::Ember}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Payload<'a> { pub receiver: Id, pub sender: Id, diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs index 0fa95ff3..49598cfa 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-dds/src/sendable.rs @@ -84,14 +84,21 @@ impl Sendable { } Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, - Data::Any(a) => { - if a.is::() { - lua.create_any_userdata(*a.downcast::().unwrap())?.into_lua(lua)? - } else if a.is::() { - a.downcast::().unwrap().into_lua(lua)? - } else { - Err("unsupported Data::Any included".into_lua_err())? + Data::Any(b) => { + let mut b = b.into_any(); + macro_rules! try_cast { + ($f:expr) => { + match b.downcast() { + Ok(v) => return $f(*v)?.into_lua(lua), + #[allow(unused_assignments)] + Err(e) => b = e, + } + }; } + + try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); + try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua)); + Err("unsupported DataAny included".into_lua_err())? } data => Self::data_to_value_ref(lua, &data)?, }) @@ -124,12 +131,12 @@ impl Sendable { Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Any(a) => { - if let Some(t) = a.downcast_ref::() { + if let Some(t) = a.as_any().downcast_ref::() { lua.create_any_userdata(t.clone())?.into_lua(lua)? - } else if let Some(t) = a.downcast_ref::() { + } else if let Some(t) = a.as_any().downcast_ref::() { t.clone().into_lua(lua)? } else { - Err("unsupported Data::Any included".into_lua_err())? + Err("unsupported DataAny included".into_lua_err())? } } }) diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index d3023b41..d15d45d5 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -60,7 +60,7 @@ impl App { drop(loader); let result = Lives::scope(&self.core, || { - if let Some(cb) = opt.cb { + if let Some(cb) = opt.callback { cb(&LUA, plugin) } else { let job = LUA.create_table_from([("args", Sendable::args_to_table(&LUA, opt.args)?)])?; diff --git a/yazi-fm/src/app/commands/resume.rs b/yazi-fm/src/app/commands/resume.rs index 1df67288..fef0404f 100644 --- a/yazi-fm/src/app/commands/resume.rs +++ b/yazi-fm/src/app/commands/resume.rs @@ -1,12 +1,12 @@ use anyhow::Result; use yazi_macro::{act, render, succ}; -use yazi_parser::VoidOpt; +use yazi_parser::app::ResumeOpt; use yazi_shared::data::Data; use crate::{Term, app::App}; impl App { - pub(crate) fn resume(&mut self, _: VoidOpt) -> Result { + pub(crate) fn resume(&mut self, opt: ResumeOpt) -> Result { self.core.active_mut().preview.reset(); self.term = Some(Term::start().unwrap()); @@ -14,7 +14,7 @@ impl App { // We need to trigger a resize, and render the UI based on the resized area. act!(resize, self)?; - self.signals.resume(None); + self.signals.resume(opt.token); succ!(render!()); } diff --git a/yazi-fm/src/app/commands/stop.rs b/yazi-fm/src/app/commands/stop.rs index 4868639b..2456e619 100644 --- a/yazi-fm/src/app/commands/stop.rs +++ b/yazi-fm/src/app/commands/stop.rs @@ -14,7 +14,7 @@ impl App { // while the app is being suspended. self.term = None; - self.signals.stop(opt.tx); + self.signals.stop(opt.token); succ!(); } diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index f30ccbe1..6d733246 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -1,12 +1,12 @@ use anyhow::Result; use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind}; use futures::StreamExt; -use tokio::{select, sync::{mpsc, oneshot}}; +use tokio::{select, sync::mpsc}; use yazi_config::YAZI; -use yazi_shared::event::Event; +use yazi_shared::{CompletionToken, event::Event}; pub(super) struct Signals { - tx: mpsc::UnboundedSender<(bool, Option>)>, + tx: mpsc::UnboundedSender<(bool, CompletionToken)>, } impl Signals { @@ -17,11 +17,9 @@ impl Signals { Ok(Self { tx }) } - pub(super) fn stop(&mut self, cb: Option>) { self.tx.send((false, cb)).ok(); } + pub(super) fn stop(&mut self, token: CompletionToken) { self.tx.send((false, token)).ok(); } - pub(super) fn resume(&mut self, cb: Option>) { - self.tx.send((true, cb)).ok(); - } + pub(super) fn resume(&mut self, token: CompletionToken) { self.tx.send((true, token)).ok(); } #[cfg(unix)] fn handle_sys(n: libc::c_int) -> bool { @@ -71,7 +69,7 @@ impl Signals { } } - fn spawn(mut rx: mpsc::UnboundedReceiver<(bool, Option>)>) -> Result<()> { + fn spawn(mut rx: mpsc::UnboundedReceiver<(bool, CompletionToken)>) -> Result<()> { #[cfg(unix)] use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP}; @@ -96,9 +94,9 @@ impl Signals { if let Some(t) = &mut term { select! { biased; - Some((state, mut callback)) = rx.recv() => { + Some((state, token)) = rx.recv() => { term = term.filter(|_| state); - callback.take().map(|cb| cb.send(())); + token.complete(true); }, Some(n) = sys.next() => if !Self::handle_sys(n) { return }, Some(Ok(e)) = t.next() => Self::handle_term(e) @@ -106,9 +104,9 @@ impl Signals { } else { select! { biased; - Some((state, mut callback)) = rx.recv() => { + Some((state, token)) = rx.recv() => { term = state.then(EventStream::new); - callback.take().map(|cb| cb.send(())); + token.complete(true); }, Some(n) = sys.next() => if !Self::handle_sys(n) { return }, } diff --git a/yazi-parser/Cargo.toml b/yazi-parser/Cargo.toml index b38d5914..381575f2 100644 --- a/yazi-parser/Cargo.toml +++ b/yazi-parser/Cargo.toml @@ -28,6 +28,7 @@ yazi-vfs = { path = "../yazi-vfs", version = "26.1.22" } anyhow = { workspace = true } bitflags = { workspace = true } crossterm = { workspace = true } +dyn-clone = { workspace = true } hashbrown = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } diff --git a/yazi-parser/src/app/notify.rs b/yazi-parser/src/app/notify.rs index 20278846..027652d1 100644 --- a/yazi-parser/src/app/notify.rs +++ b/yazi-parser/src/app/notify.rs @@ -6,6 +6,7 @@ use serde::Deserialize; use yazi_config::{Style, THEME}; use yazi_shared::event::CmdCow; +#[derive(Clone)] pub struct NotifyOpt { pub title: String, pub content: String, diff --git a/yazi-parser/src/app/plugin.rs b/yazi-parser/src/app/plugin.rs index 5232ea8f..30184201 100644 --- a/yazi-parser/src/app/plugin.rs +++ b/yazi-parser/src/app/plugin.rs @@ -1,19 +1,18 @@ -use std::{borrow::Cow, fmt::Debug, str::FromStr}; +use std::{borrow::Cow, fmt::{self, Debug}, str::FromStr}; use anyhow::bail; +use dyn_clone::DynClone; use hashbrown::HashMap; use mlua::{Lua, Table}; use serde::Deserialize; use yazi_shared::{SStr, data::{Data, DataKey}, event::{Cmd, CmdCow}}; -pub type PluginCallback = Box mlua::Result<()> + Send + Sync>; - -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct PluginOpt { - pub id: SStr, - pub args: HashMap, - pub mode: PluginMode, - pub cb: Option, + pub id: SStr, + pub args: HashMap, + pub mode: PluginMode, + pub callback: Option>, } impl TryFrom for PluginOpt { @@ -36,27 +35,16 @@ impl TryFrom for PluginOpt { }; let mode = c.str("mode").parse().unwrap_or_default(); - Ok(Self { id: Self::normalize_id(id), args, mode, cb: c.take_any("callback") }) - } -} - -impl Debug for PluginOpt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PluginOpt") - .field("id", &self.id) - .field("args", &self.args) - .field("mode", &self.mode) - .field("cb", &self.cb.is_some()) - .finish() + Ok(Self { id: Self::normalize_id(id), args, mode, callback: c.take_any("callback") }) } } impl PluginOpt { - pub fn new_callback(id: impl Into, cb: PluginCallback) -> Self { + pub fn new_callback(id: impl Into, f: impl PluginCallback) -> Self { Self { id: Self::normalize_id(id.into()), mode: PluginMode::Sync, - cb: Some(cb), + callback: Some(Box::new(f)), ..Default::default() } } @@ -98,3 +86,24 @@ impl PluginMode { if sync { Self::Sync } else { Self::Async } } } + +// --- Callback +pub trait PluginCallback: + FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static +{ +} + +impl PluginCallback for T where + T: FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static +{ +} + +impl Clone for Box { + fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } +} + +impl fmt::Debug for dyn PluginCallback { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PluginCallback").finish_non_exhaustive() + } +} diff --git a/yazi-parser/src/app/resume.rs b/yazi-parser/src/app/resume.rs index a7154a90..52e77c35 100644 --- a/yazi-parser/src/app/resume.rs +++ b/yazi-parser/src/app/resume.rs @@ -1,10 +1,18 @@ -use tokio::sync::oneshot; -use yazi_shared::event::CmdCow; +use anyhow::bail; +use yazi_shared::{CompletionToken, event::CmdCow}; pub struct ResumeOpt { - pub tx: Option>, + pub token: CompletionToken, } -impl From for ResumeOpt { - fn from(mut c: CmdCow) -> Self { Self { tx: c.take_any("tx") } } +impl TryFrom for ResumeOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + let Some(token) = c.take_any("token") else { + bail!("Invalid 'token' in ResumeOpt"); + }; + + Ok(Self { token }) + } } diff --git a/yazi-parser/src/app/stop.rs b/yazi-parser/src/app/stop.rs index 5ccc91d3..683ddf8d 100644 --- a/yazi-parser/src/app/stop.rs +++ b/yazi-parser/src/app/stop.rs @@ -1,10 +1,18 @@ -use tokio::sync::oneshot; -use yazi_shared::event::CmdCow; +use anyhow::bail; +use yazi_shared::{CompletionToken, event::CmdCow}; pub struct StopOpt { - pub tx: Option>, + pub token: CompletionToken, } -impl From for StopOpt { - fn from(mut c: CmdCow) -> Self { Self { tx: c.take_any("tx") } } +impl TryFrom for StopOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + let Some(token) = c.take_any("token") else { + bail!("Invalid 'token' in StopOpt"); + }; + + Ok(Self { token }) + } } diff --git a/yazi-parser/src/arrow.rs b/yazi-parser/src/arrow.rs index 9aa9627d..4abbefcf 100644 --- a/yazi-parser/src/arrow.rs +++ b/yazi-parser/src/arrow.rs @@ -14,7 +14,7 @@ impl TryFrom for ArrowOpt { fn try_from(c: CmdCow) -> Result { let Ok(step) = c.first() else { - bail!("'step' is required for ArrowOpt"); + bail!("Invalid 'step' in ArrowOpt"); }; Ok(Self { step }) diff --git a/yazi-parser/src/cmp/show.rs b/yazi-parser/src/cmp/show.rs index 8984c6c9..ad599f6e 100644 --- a/yazi-parser/src/cmp/show.rs +++ b/yazi-parser/src/cmp/show.rs @@ -4,7 +4,7 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_shared::{Id, event::CmdCow, path::PathBufDyn, strand::{StrandBuf, StrandLike}, url::UrlBuf}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ShowOpt { pub cache: Vec, pub cache_name: UrlBuf, diff --git a/yazi-parser/src/confirm/show.rs b/yazi-parser/src/confirm/show.rs index e34b093f..5df2d9f4 100644 --- a/yazi-parser/src/confirm/show.rs +++ b/yazi-parser/src/confirm/show.rs @@ -1,13 +1,12 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use tokio::sync::oneshot; use yazi_config::popup::ConfirmCfg; -use yazi_shared::event::CmdCow; +use yazi_shared::{CompletionToken, event::CmdCow}; #[derive(Debug)] pub struct ShowOpt { - pub cfg: ConfirmCfg, - pub tx: oneshot::Sender, + pub cfg: ConfirmCfg, + pub token: CompletionToken, } impl TryFrom for ShowOpt { @@ -15,14 +14,14 @@ impl TryFrom for ShowOpt { fn try_from(mut c: CmdCow) -> Result { let Some(cfg) = c.take_any("cfg") else { - bail!("Invalid 'cfg' argument in ShowOpt"); + bail!("Invalid 'cfg' in ShowOpt"); }; - let Some(tx) = c.take_any("tx") else { - bail!("Invalid 'tx' argument in ShowOpt"); + let Some(token) = c.take_any("token") else { + bail!("Invalid 'token' in ShowOpt"); }; - Ok(Self { cfg, tx }) + Ok(Self { cfg, token }) } } diff --git a/yazi-parser/src/input/show.rs b/yazi-parser/src/input/show.rs index 398cd2fc..bfd7d668 100644 --- a/yazi-parser/src/input/show.rs +++ b/yazi-parser/src/input/show.rs @@ -15,11 +15,11 @@ impl TryFrom for ShowOpt { fn try_from(mut c: CmdCow) -> Result { let Some(cfg) = c.take_any("cfg") else { - bail!("Invalid 'cfg' argument in ShowOpt"); + bail!("Invalid 'cfg' in ShowOpt"); }; let Some(tx) = c.take_any("tx") else { - bail!("Invalid 'tx' argument in ShowOpt"); + bail!("Invalid 'tx' in ShowOpt"); }; Ok(Self { cfg, tx }) diff --git a/yazi-parser/src/mgr/displace_do.rs b/yazi-parser/src/mgr/displace_do.rs index 56716945..afc766c9 100644 --- a/yazi-parser/src/mgr/displace_do.rs +++ b/yazi-parser/src/mgr/displace_do.rs @@ -2,9 +2,9 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_shared::{event::CmdCow, url::UrlBuf}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DisplaceDoOpt { - pub to: std::io::Result, + pub to: Result, pub from: UrlBuf, } @@ -15,7 +15,7 @@ impl TryFrom for DisplaceDoOpt { if let Some(opt) = c.take_any2("opt") { opt } else { - bail!("'opt' is required for DisplaceDoOpt"); + bail!("Invalid 'opt' in DisplaceDoOpt"); } } } diff --git a/yazi-parser/src/mgr/filter.rs b/yazi-parser/src/mgr/filter.rs index a6f693a3..62170989 100644 --- a/yazi-parser/src/mgr/filter.rs +++ b/yazi-parser/src/mgr/filter.rs @@ -2,7 +2,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_fs::FilterCase; use yazi_shared::{SStr, event::CmdCow}; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct FilterOpt { pub query: SStr, pub case: FilterCase, diff --git a/yazi-parser/src/mgr/find_do.rs b/yazi-parser/src/mgr/find_do.rs index 28438523..a3705104 100644 --- a/yazi-parser/src/mgr/find_do.rs +++ b/yazi-parser/src/mgr/find_do.rs @@ -3,7 +3,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_fs::FilterCase; use yazi_shared::{SStr, event::CmdCow}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct FindDoOpt { pub query: SStr, pub prev: bool, @@ -19,7 +19,7 @@ impl TryFrom for FindDoOpt { } let Ok(query) = c.take_first() else { - bail!("'query' is required for FindDoOpt"); + bail!("Invalid 'query' in FindDoOpt"); }; Ok(Self { query, prev: c.bool("previous"), case: FilterCase::from(&*c) }) diff --git a/yazi-parser/src/mgr/open.rs b/yazi-parser/src/mgr/open.rs index 763cf52a..dc2b29ce 100644 --- a/yazi-parser/src/mgr/open.rs +++ b/yazi-parser/src/mgr/open.rs @@ -1,7 +1,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_shared::{event::CmdCow, url::UrlCow}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct OpenOpt { pub cwd: Option>, pub targets: Vec>, diff --git a/yazi-parser/src/mgr/open_do.rs b/yazi-parser/src/mgr/open_do.rs index fa8d8c4c..287403fe 100644 --- a/yazi-parser/src/mgr/open_do.rs +++ b/yazi-parser/src/mgr/open_do.rs @@ -2,7 +2,7 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_shared::{event::CmdCow, url::UrlCow}; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct OpenDoOpt { pub cwd: UrlCow<'static>, pub targets: Vec>, @@ -16,7 +16,7 @@ impl TryFrom for OpenDoOpt { if let Some(opt) = c.take_any2("opt") { opt } else { - bail!("'opt' is required for OpenDoOpt"); + bail!("Invalid 'opt' in OpenDoOpt"); } } } diff --git a/yazi-parser/src/mgr/search.rs b/yazi-parser/src/mgr/search.rs index 10459077..8ce91bd4 100644 --- a/yazi-parser/src/mgr/search.rs +++ b/yazi-parser/src/mgr/search.rs @@ -25,7 +25,7 @@ impl TryFrom for SearchOpt { }; let Ok(args) = yazi_shared::shell::unix::split(c.str("args"), false) else { - bail!("Invalid 'args' argument in SearchOpt"); + bail!("Invalid 'args' in SearchOpt"); }; Ok(Self { diff --git a/yazi-parser/src/mgr/update_files.rs b/yazi-parser/src/mgr/update_files.rs index 0402f841..6f274379 100644 --- a/yazi-parser/src/mgr/update_files.rs +++ b/yazi-parser/src/mgr/update_files.rs @@ -13,7 +13,7 @@ impl TryFrom for UpdateFilesOpt { fn try_from(mut c: CmdCow) -> Result { let Some(op) = c.take_any("op") else { - bail!("Invalid 'op' argument in UpdateFilesOpt"); + bail!("Invalid 'op' in UpdateFilesOpt"); }; Ok(Self { op }) diff --git a/yazi-parser/src/mgr/update_mimes.rs b/yazi-parser/src/mgr/update_mimes.rs index 7758e8e7..4a47c18f 100644 --- a/yazi-parser/src/mgr/update_mimes.rs +++ b/yazi-parser/src/mgr/update_mimes.rs @@ -13,7 +13,7 @@ impl TryFrom for UpdateMimesOpt { fn try_from(mut c: CmdCow) -> Result { let Ok(updates) = c.take("updates") else { - bail!("Invalid 'updates' argument in UpdateMimesOpt"); + bail!("Invalid 'updates' in UpdateMimesOpt"); }; Ok(Self { updates }) diff --git a/yazi-parser/src/mgr/update_peeked.rs b/yazi-parser/src/mgr/update_peeked.rs index 57f494f3..4af594e0 100644 --- a/yazi-parser/src/mgr/update_peeked.rs +++ b/yazi-parser/src/mgr/update_peeked.rs @@ -3,7 +3,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use yazi_binding::{FileRef, elements::{Rect, Renderable}}; use yazi_shared::event::CmdCow; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct UpdatePeekedOpt { pub lock: PreviewLock, } @@ -17,7 +17,7 @@ impl TryFrom for UpdatePeekedOpt { } let Some(lock) = c.take_any("lock") else { - bail!("Invalid 'lock' argument in UpdatePeekedOpt"); + bail!("Invalid 'lock' in UpdatePeekedOpt"); }; Ok(Self { lock }) @@ -33,7 +33,7 @@ impl IntoLua for UpdatePeekedOpt { } // --- Lock -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct PreviewLock { pub url: yazi_shared::url::UrlBuf, pub cha: yazi_fs::cha::Cha, diff --git a/yazi-parser/src/mgr/update_spotted.rs b/yazi-parser/src/mgr/update_spotted.rs index 457fc7e2..39bb0156 100644 --- a/yazi-parser/src/mgr/update_spotted.rs +++ b/yazi-parser/src/mgr/update_spotted.rs @@ -3,7 +3,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use yazi_binding::{FileRef, elements::Renderable}; use yazi_shared::{Id, event::CmdCow}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct UpdateSpottedOpt { pub lock: SpotLock, } @@ -17,7 +17,7 @@ impl TryFrom for UpdateSpottedOpt { } let Some(lock) = c.take_any("lock") else { - bail!("Invalid 'lock' argument in UpdateSpottedOpt"); + bail!("Invalid 'lock' in UpdateSpottedOpt"); }; Ok(Self { lock }) @@ -33,7 +33,7 @@ impl IntoLua for UpdateSpottedOpt { } // --- Lock -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct SpotLock { pub url: yazi_shared::url::UrlBuf, pub cha: yazi_fs::cha::Cha, diff --git a/yazi-parser/src/mgr/update_yanked.rs b/yazi-parser/src/mgr/update_yanked.rs index 41288723..d835a508 100644 --- a/yazi-parser/src/mgr/update_yanked.rs +++ b/yazi-parser/src/mgr/update_yanked.rs @@ -25,7 +25,7 @@ impl TryFrom for UpdateYankedOpt<'_> { if let Some(opt) = c.take_any2("opt") { opt } else { - bail!("'opt' is required for UpdateYankedOpt"); + bail!("Invalid 'opt' in UpdateYankedOpt"); } } } diff --git a/yazi-parser/src/notify/tick.rs b/yazi-parser/src/notify/tick.rs index ade39d53..491bc958 100644 --- a/yazi-parser/src/notify/tick.rs +++ b/yazi-parser/src/notify/tick.rs @@ -14,7 +14,7 @@ impl TryFrom for TickOpt { fn try_from(c: CmdCow) -> Result { let Ok(interval) = c.first() else { - bail!("Invalid 'interval' argument in TickOpt"); + bail!("Invalid 'interval' in TickOpt"); }; if interval < 0.0 { diff --git a/yazi-parser/src/pick/show.rs b/yazi-parser/src/pick/show.rs index 15aa2576..543789aa 100644 --- a/yazi-parser/src/pick/show.rs +++ b/yazi-parser/src/pick/show.rs @@ -1,13 +1,13 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use tokio::sync::oneshot; +use tokio::sync::mpsc; use yazi_config::popup::PickCfg; use yazi_shared::event::CmdCow; #[derive(Debug)] pub struct ShowOpt { pub cfg: PickCfg, - pub tx: oneshot::Sender>, + pub tx: mpsc::UnboundedSender>, } impl TryFrom for ShowOpt { @@ -15,11 +15,11 @@ impl TryFrom for ShowOpt { fn try_from(mut c: CmdCow) -> Result { let Some(cfg) = c.take_any("cfg") else { - bail!("Missing 'cfg' argument in ShowOpt"); + bail!("Invalid 'cfg' in ShowOpt"); }; let Some(tx) = c.take_any("tx") else { - bail!("Missing 'tx' argument in ShowOpt"); + bail!("Invalid 'tx' in ShowOpt"); }; Ok(Self { cfg, tx }) diff --git a/yazi-parser/src/tasks/process_open.rs b/yazi-parser/src/tasks/process_open.rs index 554a6a50..e7d5e28c 100644 --- a/yazi-parser/src/tasks/process_open.rs +++ b/yazi-parser/src/tasks/process_open.rs @@ -5,7 +5,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_shared::{CompletionToken, event::CmdCow, url::UrlCow}; // --- Exec -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ProcessOpenOpt { pub cwd: UrlCow<'static>, pub cmd: OsString, diff --git a/yazi-parser/src/tasks/update_succeed.rs b/yazi-parser/src/tasks/update_succeed.rs index 382923f0..b9187a83 100644 --- a/yazi-parser/src/tasks/update_succeed.rs +++ b/yazi-parser/src/tasks/update_succeed.rs @@ -12,7 +12,7 @@ impl TryFrom for UpdateSucceedOpt { fn try_from(mut c: CmdCow) -> Result { let Some(urls) = c.take_any("urls") else { - bail!("Invalid 'urls' argument in UpdateSucceedOpt"); + bail!("Invalid 'urls' in UpdateSucceedOpt"); }; Ok(Self { urls }) diff --git a/yazi-parser/src/which/callback.rs b/yazi-parser/src/which/callback.rs index 61f5a4e4..26078a7c 100644 --- a/yazi-parser/src/which/callback.rs +++ b/yazi-parser/src/which/callback.rs @@ -5,7 +5,7 @@ use yazi_shared::event::CmdCow; #[derive(Debug)] pub struct CallbackOpt { - pub tx: mpsc::Sender, + pub tx: mpsc::UnboundedSender, pub idx: usize, } @@ -14,11 +14,11 @@ impl TryFrom for CallbackOpt { fn try_from(mut c: CmdCow) -> Result { let Some(tx) = c.take_any("tx") else { - bail!("Invalid 'tx' argument in CallbackOpt"); + bail!("Invalid 'tx' in CallbackOpt"); }; let Ok(idx) = c.first() else { - bail!("Invalid 'idx' argument in CallbackOpt"); + bail!("Invalid 'idx' in CallbackOpt"); }; Ok(Self { tx, idx }) diff --git a/yazi-parser/src/which/show.rs b/yazi-parser/src/which/show.rs index 8e1039f5..dbc847ef 100644 --- a/yazi-parser/src/which/show.rs +++ b/yazi-parser/src/which/show.rs @@ -3,7 +3,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; use yazi_shared::{Layer, event::CmdCow}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ShowOpt { pub cands: Vec, pub silent: bool, diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index bc3b2cd6..0697a0ee 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -1,11 +1,11 @@ -use mlua::{ExternalError, HookTriggers, IntoLua, ObjectLike, VmState}; +use mlua::{ExternalError, HookTriggers, IntoLua, Lua, ObjectLike, Table, VmState}; use tokio::{runtime::Handle, select}; use tokio_util::sync::CancellationToken; use tracing::error; use yazi_binding::{Error, File, elements::{Rect, Renderable}}; use yazi_config::LAYOUT; use yazi_dds::Sendable; -use yazi_parser::{app::{PluginCallback, PluginOpt}, mgr::{PreviewLock, UpdatePeekedOpt}}; +use yazi_parser::{app::PluginOpt, mgr::{PreviewLock, UpdatePeekedOpt}}; use yazi_proxy::{AppProxy, MgrProxy}; use yazi_shared::{event::Cmd, pool::Symbol}; @@ -52,7 +52,7 @@ pub fn peek( } fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Symbol, skip: usize) { - let cb: PluginCallback = Box::new(move |lua, plugin| { + let cb = move |lua: &Lua, plugin: Table| { let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), ("args", Sendable::args_to_table_ref(lua, &cmd.args)?.into_lua(lua)?), @@ -62,7 +62,7 @@ fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Symbol, skip: us ])?; plugin.call_method("peek", job) - }); + }; AppProxy::plugin(PluginOpt::new_callback(&*cmd.name, cb)); } diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs index 9085b82d..0762711f 100644 --- a/yazi-plugin/src/isolate/seek.rs +++ b/yazi-plugin/src/isolate/seek.rs @@ -1,12 +1,12 @@ -use mlua::{IntoLua, ObjectLike}; +use mlua::{IntoLua, Lua, ObjectLike, Table}; use yazi_binding::{File, elements::Rect}; use yazi_config::LAYOUT; -use yazi_parser::app::{PluginCallback, PluginOpt}; +use yazi_parser::app::PluginOpt; use yazi_proxy::AppProxy; use yazi_shared::event::Cmd; pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) { - let cb: PluginCallback = Box::new(move |lua, plugin| { + let cb = move |lua: &Lua, plugin: Table| { let job = lua.create_table_from([ ("file", File::new(file).into_lua(lua)?), ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), @@ -14,7 +14,7 @@ pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) { ])?; plugin.call_method("seek", job) - }); + }; AppProxy::plugin(PluginOpt::new_callback(&*cmd.name, cb)); } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 129402bd..2a199643 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -20,7 +20,7 @@ impl Utils { return Err("Cannot call `ya.which()` while main thread is blocked".into_lua_err()); } - let (tx, mut rx) = mpsc::channel::(1); + let (tx, mut rx) = mpsc::unbounded_channel::(); let cands: Vec<_> = t .raw_get::("cands")? .sequence_values::
() diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index 238aeabd..00f9f132 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -1,10 +1,10 @@ use anyhow::Context; use futures::future::join_all; -use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Value, Variadic}; -use tokio::sync::oneshot; +use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; +use tokio::sync::mpsc; use yazi_binding::{Handle, runtime, runtime_mut}; use yazi_dds::Sendable; -use yazi_parser::app::{PluginCallback, PluginOpt}; +use yazi_parser::app::PluginOpt; use yazi_proxy::AppProxy; use yazi_shared::{LOCAL_SET, data::Data}; @@ -115,27 +115,25 @@ impl Utils { args: MultiValue, ) -> mlua::Result> { let args = Sendable::values_to_list(lua, args)?; - let (tx, rx) = oneshot::channel::>(); + let (tx, mut rx) = mpsc::channel::>(1); - let callback: PluginCallback = { - let id = id.to_owned(); - Box::new(move |lua, plugin| { - let Some(block) = runtime!(lua)?.get_block(&id, calls) else { - return Err("sync block not found".into_lua_err()); - }; + let id_ = id.to_owned(); + let callback = move |lua: &Lua, plugin: Table| { + let Some(block) = runtime!(lua)?.get_block(&id_, calls) else { + return Err("sync block not found".into_lua_err()); + }; - let args = [Ok(Value::Table(plugin))] - .into_iter() - .chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d))) - .collect::>()?; + let args = [Ok(Value::Table(plugin))] + .into_iter() + .chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d))) + .collect::>()?; - let values = Sendable::values_to_list(lua, block.call(args)?)?; - tx.send(values).map_err(|_| "send failed".into_lua_err()) - }) + let values = Sendable::values_to_list(lua, block.call(args)?)?; + tx.try_send(values).map_err(|_| "send failed".into_lua_err()) }; AppProxy::plugin(PluginOpt::new_callback(id.to_owned(), callback)); - rx.await.into_lua_err() + rx.recv().await.ok_or("recv failed").into_lua_err() } } diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs index 7a15807a..92442ba1 100644 --- a/yazi-proxy/src/app.rs +++ b/yazi-proxy/src/app.rs @@ -1,22 +1,22 @@ use std::time::Duration; -use tokio::sync::oneshot; use yazi_macro::{emit, relay}; use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt, TaskSummary}; +use yazi_shared::CompletionToken; pub struct AppProxy; impl AppProxy { pub async fn stop() { - let (tx, rx) = oneshot::channel::<()>(); - emit!(Call(relay!(app:stop).with_any("tx", tx))); - rx.await.ok(); + let token = CompletionToken::default(); + emit!(Call(relay!(app:stop).with_any("token", token.clone()))); + token.future().await; } pub async fn resume() { - let (tx, rx) = oneshot::channel::<()>(); - emit!(Call(relay!(app:resume).with_any("tx", tx))); - rx.await.ok(); + let token = CompletionToken::default(); + emit!(Call(relay!(app:resume).with_any("token", token.clone()))); + token.future().await; } pub fn notify(opt: NotifyOpt) { diff --git a/yazi-proxy/src/confirm.rs b/yazi-proxy/src/confirm.rs index bd9309bc..8dee81fa 100644 --- a/yazi-proxy/src/confirm.rs +++ b/yazi-proxy/src/confirm.rs @@ -1,15 +1,15 @@ -use tokio::sync::oneshot; use yazi_config::popup::ConfirmCfg; use yazi_macro::{emit, relay}; +use yazi_shared::CompletionToken; pub struct ConfirmProxy; impl ConfirmProxy { - pub async fn show(cfg: ConfirmCfg) -> bool { Self::show_rx(cfg).await.unwrap_or(false) } + pub async fn show(cfg: ConfirmCfg) -> bool { Self::show_sync(cfg).future().await } - pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver { - let (tx, rx) = oneshot::channel(); - emit!(Call(relay!(confirm:show).with_any("tx", tx).with_any("cfg", cfg))); - rx + pub fn show_sync(cfg: ConfirmCfg) -> CompletionToken { + let token = CompletionToken::default(); + emit!(Call(relay!(confirm:show).with_any("cfg", cfg).with_any("token", token.clone()))); + token } } diff --git a/yazi-proxy/src/pick.rs b/yazi-proxy/src/pick.rs index 1293fc2b..b3e2d550 100644 --- a/yazi-proxy/src/pick.rs +++ b/yazi-proxy/src/pick.rs @@ -1,13 +1,13 @@ -use tokio::sync::oneshot; +use tokio::sync::mpsc; use yazi_config::popup::PickCfg; use yazi_macro::{emit, relay}; pub struct PickProxy; impl PickProxy { - pub async fn show(cfg: PickCfg) -> anyhow::Result { - let (tx, rx) = oneshot::channel(); + pub async fn show(cfg: PickCfg) -> Option { + let (tx, mut rx) = mpsc::unbounded_channel::>(); emit!(Call(relay!(pick:show).with_any("tx", tx).with_any("cfg", cfg))); - rx.await? + rx.recv().await? } } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index c8eaa70f..d0b19181 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -18,6 +18,7 @@ yazi-macro = { path = "../yazi-macro", version = "26.1.22" } # External dependencies anyhow = { workspace = true } crossterm = { workspace = true } +dyn-clone = { workspace = true } foldhash = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } diff --git a/yazi-shared/src/data/any.rs b/yazi-shared/src/data/any.rs new file mode 100644 index 00000000..406d9a40 --- /dev/null +++ b/yazi-shared/src/data/any.rs @@ -0,0 +1,28 @@ +use std::{any::Any, fmt}; + +use dyn_clone::DynClone; + +pub trait DataAny: Any + Send + Sync + DynClone { + fn as_any(&self) -> &dyn Any; + + fn into_any(self: Box) -> Box; +} + +impl DataAny for T +where + T: Any + Send + Sync + DynClone, +{ + fn as_any(&self) -> &dyn Any { self } + + fn into_any(self: Box) -> Box { self } +} + +impl Clone for Box { + fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } +} + +impl fmt::Debug for dyn DataAny { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DataAny").finish_non_exhaustive() + } +} diff --git a/yazi-shared/src/data/data.rs b/yazi-shared/src/data/data.rs index 7fab095e..2b23d11e 100644 --- a/yazi-shared/src/data/data.rs +++ b/yazi-shared/src/data/data.rs @@ -1,13 +1,13 @@ -use std::{any::Any, borrow::Cow}; +use std::borrow::Cow; use anyhow::{Result, bail}; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use crate::{Id, SStr, data::DataKey, path::PathBufDyn, url::{UrlBuf, UrlCow}}; +use crate::{Id, SStr, data::{DataAny, DataKey}, path::PathBufDyn, url::{UrlBuf, UrlCow}}; // --- Data -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum Data { Nil, @@ -25,7 +25,7 @@ pub enum Data { #[serde(skip)] Bytes(Vec), #[serde(skip)] - Any(Box), + Any(Box), } impl From<()> for Data { @@ -199,7 +199,7 @@ impl Data { pub fn into_any(self) -> Option { match self { - Self::Any(b) => b.downcast::().ok().map(|b| *b), + Self::Any(b) => b.into_any().downcast::().ok().map(|b| *b), _ => None, } } @@ -207,7 +207,7 @@ impl Data { // FIXME: find a better name pub fn into_any2(self) -> Result { if let Self::Any(b) = self - && let Ok(t) = b.downcast::() + && let Ok(t) = b.into_any().downcast::() { Ok(*t) } else { diff --git a/yazi-shared/src/data/key.rs b/yazi-shared/src/data/key.rs index 61b6c5e1..b66db94e 100644 --- a/yazi-shared/src/data/key.rs +++ b/yazi-shared/src/data/key.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize, de}; use crate::{Id, SStr, path::PathBufDyn, url::{UrlBuf, UrlCow}}; -#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[serde(untagged)] pub enum DataKey { Nil, diff --git a/yazi-shared/src/data/mod.rs b/yazi-shared/src/data/mod.rs index ea06534c..67f01151 100644 --- a/yazi-shared/src/data/mod.rs +++ b/yazi-shared/src/data/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(data key); +yazi_macro::mod_flat!(any data key); diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index 9b7222b0..37bf044f 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -1,12 +1,12 @@ -use std::{any::Any, borrow::Cow, fmt::{self, Display}, mem, str::FromStr}; +use std::{borrow::Cow, fmt::{self, Display}, mem, str::FromStr}; use anyhow::{Result, anyhow, bail}; use hashbrown::HashMap; use serde::{Deserialize, de}; -use crate::{Layer, SStr, Source, data::{Data, DataKey}}; +use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}}; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct Cmd { pub name: SStr, pub args: HashMap, @@ -76,7 +76,7 @@ impl Cmd { self } - pub fn with_any(mut self, name: impl Into, data: impl Any + Send + Sync) -> Self { + pub fn with_any(mut self, name: impl Into, data: impl DataAny) -> Self { self.args.insert(name.into(), Data::Any(Box::new(data))); self }