refactor: make Lua bindings Send (#4027)

This commit is contained in:
sxyazi 2026-06-09 22:52:43 +08:00
parent f6113d3865
commit 9cfeb67db7
No known key found for this signature in database
56 changed files with 395 additions and 504 deletions

79
Cargo.lock generated
View file

@ -1935,9 +1935,9 @@ dependencies = [
[[package]] [[package]]
name = "imgref" name = "imgref"
version = "1.12.1" version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
@ -3337,9 +3337,8 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.30.0" version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc"
dependencies = [ dependencies = [
"instability", "instability",
"ratatui-core", "ratatui-core",
@ -3353,8 +3352,7 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-core" name = "ratatui-core"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"compact_str", "compact_str",
@ -3366,7 +3364,7 @@ dependencies = [
"lru", "lru",
"palette", "palette",
"serde", "serde",
"strum 0.28.0", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"unicode-segmentation", "unicode-segmentation",
"unicode-truncate", "unicode-truncate",
@ -3376,8 +3374,7 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-crossterm" name = "ratatui-crossterm"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossterm 0.28.1", "crossterm 0.28.1",
@ -3389,8 +3386,7 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-termion" name = "ratatui-termion"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "5c16cc35a9d9114e0b2bb4b22018b96ae7f5fe60e2595dc73e622b4e78624835"
dependencies = [ dependencies = [
"instability", "instability",
"ratatui-core", "ratatui-core",
@ -3400,8 +3396,7 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-termwiz" name = "ratatui-termwiz"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967"
dependencies = [ dependencies = [
"ratatui-core", "ratatui-core",
"termwiz", "termwiz",
@ -3409,19 +3404,18 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-widgets" name = "ratatui-widgets"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"hashbrown 0.16.1", "hashbrown 0.17.1",
"indoc", "indoc",
"instability", "instability",
"itertools", "itertools",
"line-clipping", "line-clipping",
"ratatui-core", "ratatui-core",
"serde", "serde",
"strum 0.27.2", "strum",
"time", "time",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
@ -4221,34 +4215,13 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros 0.27.2",
]
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.28.0" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
dependencies = [ dependencies = [
"strum_macros 0.28.0", "strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
] ]
[[package]] [[package]]
@ -5131,9 +5104,9 @@ dependencies = [
[[package]] [[package]]
name = "which" name = "which"
version = "8.0.2" version = "8.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" checksum = "c789537cf2f7f55be8e6192f92e464174ee55f91af622777f7f1ceb0dbccd03e"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -5674,7 +5647,7 @@ dependencies = [
"palette", "palette",
"quantette", "quantette",
"ratatui", "ratatui",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"yazi-config", "yazi-config",
@ -5695,8 +5668,10 @@ dependencies = [
"anyhow", "anyhow",
"futures", "futures",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"inventory",
"libc", "libc",
"mlua", "mlua",
"ordered-float 5.3.0",
"paste", "paste",
"ratatui", "ratatui",
"serde_json", "serde_json",
@ -5797,7 +5772,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"tokio", "tokio",
"toml", "toml",
"tracing", "tracing",
@ -5823,7 +5798,7 @@ dependencies = [
"ratatui", "ratatui",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"syntect", "syntect",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@ -5856,7 +5831,6 @@ dependencies = [
"indexmap 2.14.0", "indexmap 2.14.0",
"inventory", "inventory",
"mlua", "mlua",
"ordered-float 5.3.0",
"parking_lot", "parking_lot",
"paste", "paste",
"serde", "serde",
@ -5966,7 +5940,7 @@ dependencies = [
"regex", "regex",
"scopeguard", "scopeguard",
"serde", "serde",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"trash", "trash",
@ -5999,7 +5973,7 @@ dependencies = [
"ratatui", "ratatui",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"tokio", "tokio",
"yazi-binding", "yazi-binding",
"yazi-boot", "yazi-boot",
@ -6089,7 +6063,6 @@ dependencies = [
"tracing", "tracing",
"yazi-binding", "yazi-binding",
"yazi-config", "yazi-config",
"yazi-dds",
"yazi-fs", "yazi-fs",
"yazi-macro", "yazi-macro",
"yazi-shared", "yazi-shared",
@ -6111,7 +6084,7 @@ dependencies = [
"ordered-float 5.3.0", "ordered-float 5.3.0",
"parking_lot", "parking_lot",
"serde", "serde",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"yazi-binding", "yazi-binding",
@ -6156,7 +6129,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"typed-path", "typed-path",
@ -6199,7 +6172,7 @@ dependencies = [
"rustix 1.1.4", "rustix 1.1.4",
"serde", "serde",
"signal-hook 0.4.4", "signal-hook 0.4.4",
"strum 0.28.0", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"windows-sys 0.61.2", "windows-sys 0.61.2",

View file

@ -61,7 +61,7 @@ parking_lot = "0.12.5"
paste = "1.0.15" paste = "1.0.15"
percent-encoding = "2.3.2" percent-encoding = "2.3.2"
rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] }
ratatui = { version = "=0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } ratatui = { version = "0.30.1", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] }
regex = "1.12.3" regex = "1.12.3"
russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] } russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] }
scopeguard = "1.2.0" scopeguard = "1.2.0"
@ -91,3 +91,8 @@ module_inception = "allow"
option_map_unit_fn = "allow" option_map_unit_fn = "allow"
unit_arg = "allow" unit_arg = "allow"
use_self = "warn" use_self = "warn"
[patch.crates-io]
ratatui = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" }
ratatui-core = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-core", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" }
ratatui-widgets = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-widgets", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" }

View file

@ -28,6 +28,9 @@ rustPlatform.buildRustPackage (finalAttrs: {
cargoLock = { cargoLock = {
lockFile = "${src}/Cargo.lock"; lockFile = "${src}/Cargo.lock";
outputHashes = {
"ratatui-0.30.1" = "sha256-wkEOgwAhW0ObtPDlJmOOoY5qf9c/P79ktN4b43jtcGw=";
};
}; };
env = { env = {
@ -72,7 +75,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
hash = "sha256-kEVXejDg4ChFoMNBvKlwdFEyUuTcY2VuK9j0PdafKus="; hash = "sha256-kEVXejDg4ChFoMNBvKlwdFEyUuTcY2VuK9j0PdafKus=";
}; };
}; };
meta = { meta = {
description = "Blazing fast terminal file manager written in Rust, based on async I/O"; description = "Blazing fast terminal file manager written in Rust, based on async I/O";

View file

@ -1,6 +1,5 @@
use anyhow::Result; use anyhow::Result;
use yazi_binding::runtime_scope; use yazi_binding::{Sendable, runtime_scope};
use yazi_dds::Sendable;
use yazi_macro::succ; use yazi_macro::succ;
use yazi_parser::app::LuaForm; use yazi_parser::app::LuaForm;
use yazi_plugin::LUA; use yazi_plugin::LUA;

View file

@ -1,7 +1,7 @@
use std::{ops::Deref, ptr}; use std::{ops::Deref, ptr};
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value}; use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Range, Style, cached_field}; use yazi_binding::{Range, Style};
use yazi_config::THEME; use yazi_config::THEME;
use yazi_shared::{path::AsPath, url::UrlLike}; use yazi_shared::{path::AsPath, url::UrlLike};
@ -12,16 +12,6 @@ pub(super) struct File {
idx: usize, idx: usize,
folder: PtrCell<yazi_core::tab::Folder>, folder: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>, tab: PtrCell<yazi_core::tab::Tab>,
v_cha: Option<Value>,
v_url: Option<Value>,
v_link_to: Option<Value>,
v_name: Option<Value>,
v_path: Option<Value>,
v_cache: Option<Value>,
v_bare: Option<Value>,
} }
impl Deref for File { impl Deref for File {
@ -45,21 +35,7 @@ impl File {
Ok(match super::FILE_CACHE.borrow_mut().entry(PtrCell(&folder.files[idx])) { Ok(match super::FILE_CACHE.borrow_mut().entry(PtrCell(&folder.files[idx])) {
Entry::Occupied(oe) => oe.into_mut().clone(), Entry::Occupied(oe) => oe.into_mut().clone(),
Entry::Vacant(ve) => { Entry::Vacant(ve) => {
let ud = Lives::scoped_userdata(Self { let ud = Lives::scoped_userdata(Self { idx, folder: folder.into(), tab: tab.into() })?;
idx,
folder: folder.into(),
tab: tab.into(),
v_cha: None,
v_url: None,
v_link_to: None,
v_name: None,
v_path: None,
v_cache: None,
v_bare: None,
})?;
ve.insert(ud.clone()); ve.insert(ud.clone());
ud ud
} }
@ -73,7 +49,7 @@ impl File {
impl UserData for File { impl UserData for File {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
yazi_binding::impl_file_fields!(fields); yazi_binding::impl_file_fields!(fields);
cached_field!(fields, bare, |_, me| Ok(yazi_binding::File::new(&**me))); fields.add_cached_field("bare", |_, me| Ok(yazi_binding::File::new(&**me)));
fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered())); fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered()));

View file

@ -1,7 +1,7 @@
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
use yazi_binding::cached_field; use yazi_shim::mlua::UserDataFieldsExt;
use super::{File, Filter, Lives, PtrCell}; use super::{File, Filter, Lives, PtrCell};
@ -9,8 +9,6 @@ pub(super) struct Files {
window: Range<usize>, window: Range<usize>,
folder: PtrCell<yazi_core::tab::Folder>, folder: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>, tab: PtrCell<yazi_core::tab::Tab>,
v_filter: Option<Value>,
} }
impl Deref for Files { impl Deref for Files {
@ -25,13 +23,13 @@ impl Files {
folder: &yazi_core::tab::Folder, folder: &yazi_core::tab::Folder,
tab: &yazi_core::tab::Tab, tab: &yazi_core::tab::Tab,
) -> mlua::Result<AnyUserData> { ) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into(), v_filter: None }) Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into() })
} }
} }
impl UserData for Files { impl UserData for Files {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, filter, |_, me| me.filter().map(Filter::make).transpose()); fields.add_static_field("filter", |_, me| me.filter().map(Filter::make).transpose());
} }
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {

View file

@ -1,8 +1,9 @@
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
use mlua::{AnyUserData, UserData, UserDataFields, Value}; use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_binding::{FolderStage, Url, cached_field}; use yazi_binding::{FolderStage, Url};
use yazi_config::LAYOUT; use yazi_config::LAYOUT;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{File, Files, Lives, PtrCell}; use super::{File, Files, Lives, PtrCell};
@ -10,12 +11,6 @@ pub(super) struct Folder {
window: Range<usize>, window: Range<usize>,
inner: PtrCell<yazi_core::tab::Folder>, inner: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>, tab: PtrCell<yazi_core::tab::Tab>,
v_cwd: Option<Value>,
v_files: Option<Value>,
v_stage: Option<Value>,
v_window: Option<Value>,
v_hovered: Option<Value>,
} }
impl Deref for Folder { impl Deref for Folder {
@ -38,30 +33,20 @@ impl Folder {
} }
}; };
Lives::scoped_userdata(Self { Lives::scoped_userdata(Self { window, inner: inner.into(), tab: tab.into() })
window,
inner: inner.into(),
tab: tab.into(),
v_cwd: None,
v_files: None,
v_stage: None,
v_window: None,
v_hovered: None,
})
} }
} }
impl UserData for Folder { impl UserData for Folder {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, cwd, |_, me| Ok(Url::new(&me.url))); fields.add_cached_field("cwd", |_, me| Ok(Url::new(&me.url)));
cached_field!(fields, files, |_, me| Files::make(0..me.files.len(), me, &me.tab)); fields.add_static_field("files", |_, me| Files::make(0..me.files.len(), me, &me.tab));
cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage.clone()))); fields.add_cached_field("stage", |_, me| Ok(FolderStage::new(me.stage.clone())));
cached_field!(fields, window, |_, me| Files::make(me.window.clone(), me, &me.tab)); fields.add_static_field("window", |_, me| Files::make(me.window.clone(), me, &me.tab));
fields.add_field_method_get("offset", |_, me| Ok(me.offset)); fields.add_field_method_get("offset", |_, me| Ok(me.offset));
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
cached_field!(fields, hovered, |_, me| { fields.add_static_field("hovered", |_, me| {
me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose() me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose()
}); });
} }

View file

@ -1,18 +1,12 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, Value}; use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_binding::cached_field; use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr};
use yazi_shim::strum::IntoStr;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
pub(super) struct Preference { pub(super) struct Preference {
inner: PtrCell<yazi_core::tab::Preference>, inner: PtrCell<yazi_core::tab::Preference>,
v_name: Option<Value>,
v_linemode: Option<Value>,
v_sort_by: Option<Value>,
} }
impl Deref for Preference { impl Deref for Preference {
@ -23,26 +17,19 @@ impl Deref for Preference {
impl Preference { impl Preference {
pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { Lives::scoped_userdata(Self { inner: inner.into() })
inner: inner.into(),
v_name: None,
v_linemode: None,
v_sort_by: None,
})
} }
} }
impl UserData for Preference { impl UserData for Preference {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
// Display // Display
cached_field!(fields, name, |lua, me| lua.create_string(&me.name)); fields.add_cached_field("name", |lua, me| lua.create_string(&me.name));
cached_field!(fields, linemode, |lua, me| lua.create_string(&*me.linemode)); fields.add_cached_field("linemode", |lua, me| lua.create_string(&*me.linemode));
fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
// Sorting // Sorting
cached_field!(fields, sort_by, |_, me| Ok(me.sort_by.into_str())); fields.add_cached_field("sort_by", |_, me| Ok(me.sort_by.into_str()));
fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive));
fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse)); fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse));
fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first)); fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first));

View file

@ -1,15 +1,13 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, Value}; use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_binding::cached_field;
use yazi_config::LAYOUT; use yazi_config::LAYOUT;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Folder, Lives, PtrCell}; use super::{Folder, Lives, PtrCell};
pub(super) struct Preview { pub(super) struct Preview {
tab: PtrCell<yazi_core::tab::Tab>, tab: PtrCell<yazi_core::tab::Tab>,
v_folder: Option<Value>,
} }
impl Deref for Preview { impl Deref for Preview {
@ -20,14 +18,14 @@ impl Deref for Preview {
impl Preview { impl Preview {
pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> { pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { tab: tab.into(), v_folder: None }) Lives::scoped_userdata(Self { tab: tab.into() })
} }
} }
impl UserData for Preview { impl UserData for Preview {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("skip", |_, me| Ok(me.skip)); fields.add_field_method_get("skip", |_, me| Ok(me.skip));
cached_field!(fields, folder, |_, me| { fields.add_static_field("folder", |_, me| {
me.tab me.tab
.hovered_folder() .hovered_folder()
.map(|f| { .map(|f| {

View file

@ -1,21 +1,13 @@
use std::{borrow::Cow, ops::Deref}; use std::{borrow::Cow, ops::Deref};
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value}; use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Id, UrlRef, cached_field}; use yazi_binding::{Id, UrlRef};
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected}; use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};
pub(super) struct Tab { pub(super) struct Tab {
inner: PtrCell<yazi_core::tab::Tab>, inner: PtrCell<yazi_core::tab::Tab>,
v_name: Option<Value>,
v_mode: Option<Value>,
v_pref: Option<Value>,
v_current: Option<Value>,
v_parent: Option<Value>,
v_selected: Option<Value>,
v_preview: Option<Value>,
v_finder: Option<Value>,
} }
impl Deref for Tab { impl Deref for Tab {
@ -26,42 +18,29 @@ impl Deref for Tab {
impl Tab { impl Tab {
pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { Lives::scoped_userdata(Self { inner: inner.into() })
inner: inner.into(),
v_name: None,
v_mode: None,
v_pref: None,
v_current: None,
v_parent: None,
v_selected: None,
v_preview: None,
v_finder: None,
})
} }
} }
impl UserData for Tab { impl UserData for Tab {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, name, |lua, me| { fields.add_cached_field("name", |lua, me| match me.name() {
match me.name() { Cow::Borrowed(s) => lua.create_string(s),
Cow::Borrowed(s) => lua.create_string(s), Cow::Owned(s) => lua.create_external_string(s),
Cow::Owned(s) => lua.create_external_string(s),
}
}); });
cached_field!(fields, mode, |_, me| Mode::make(&me.mode)); fields.add_static_field("mode", |_, me| Mode::make(&me.mode));
cached_field!(fields, pref, |_, me| Preference::make(&me.pref)); fields.add_static_field("pref", |_, me| Preference::make(&me.pref));
cached_field!(fields, current, |_, me| Folder::make(None, &me.current, me)); fields.add_static_field("current", |_, me| Folder::make(None, &me.current, me));
cached_field!(fields, parent, |_, me| { fields.add_static_field("parent", |_, me| {
me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose()
}); });
cached_field!(fields, selected, |_, me| Selected::make(&me.selected)); fields.add_static_field("selected", |_, me| Selected::make(&me.selected));
cached_field!(fields, preview, |_, me| Preview::make(me)); fields.add_static_field("preview", |_, me| Preview::make(me));
cached_field!(fields, finder, |_, me| me.finder.as_ref().map(Finder::make).transpose()); fields.add_static_field("finder", |_, me| me.finder.as_ref().map(Finder::make).transpose());
} }
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {

View file

@ -1,16 +1,14 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
use yazi_binding::{SER_OPT, cached_field}; use yazi_binding::SER_OPT;
use yazi_scheduler::Progress; use yazi_scheduler::Progress;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
pub(super) struct TaskSnap { pub(super) struct TaskSnap {
inner: PtrCell<yazi_scheduler::TaskSnap>, inner: PtrCell<yazi_scheduler::TaskSnap>,
v_title: Option<Value>,
v_prog: Option<Value>,
} }
impl Deref for TaskSnap { impl Deref for TaskSnap {
@ -21,14 +19,14 @@ impl Deref for TaskSnap {
impl TaskSnap { impl TaskSnap {
pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_title: None, v_prog: None }) Lives::scoped_userdata(Self { inner: inner.into() })
} }
} }
impl UserData for TaskSnap { impl UserData for TaskSnap {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, title, |lua, me| lua.create_string(&me.title)); fields.add_cached_field("title", |lua, me| lua.create_string(&me.title));
cached_field!(fields, prog, |lua, me| lua.to_value_with(&me.prog, SER_OPT)); fields.add_cached_field("prog", |lua, me| lua.to_value_with(&me.prog, SER_OPT));
fields.add_field_method_get("running", |_, me| Ok(me.prog.running())); fields.add_field_method_get("running", |_, me| Ok(me.prog.running()));
fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked())); fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked()));

View file

@ -1,17 +1,14 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
use yazi_binding::{SER_OPT, cached_field}; use yazi_binding::SER_OPT;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
use crate::lives::{Behavior, TaskSnap}; use crate::lives::{Behavior, TaskSnap};
pub(super) struct Tasks { pub(super) struct Tasks {
inner: PtrCell<yazi_core::tasks::Tasks>, inner: PtrCell<yazi_core::tasks::Tasks>,
v_behavior: Option<Value>,
v_snaps: Option<Value>,
v_summary: Option<Value>,
} }
impl Deref for Tasks { impl Deref for Tasks {
@ -22,13 +19,7 @@ impl Deref for Tasks {
impl Tasks { impl Tasks {
pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { Lives::scoped_userdata(Self { inner: inner.into() })
inner: inner.into(),
v_behavior: None,
v_snaps: None,
v_summary: None,
})
} }
} }
@ -36,9 +27,9 @@ impl UserData for Tasks {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
cached_field!(fields, behavior, |_, me| Behavior::make(&me.scheduler.behavior)); fields.add_static_field("behavior", |_, me| Behavior::make(&me.scheduler.behavior));
cached_field!(fields, snaps, |lua, me| { fields.add_static_field("snaps", |lua, me| {
let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?; let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?;
for snap in &me.snaps { for snap in &me.snaps {
tbl.raw_push(TaskSnap::make(snap)?)?; tbl.raw_push(TaskSnap::make(snap)?)?;
@ -46,6 +37,6 @@ impl UserData for Tasks {
Ok(tbl) Ok(tbl)
}); });
cached_field!(fields, summary, |lua, me| lua.to_value_with(&me.summary, SER_OPT)); fields.add_cached_field("summary", |lua, me| lua.to_value_with(&me.summary, SER_OPT));
} }
} }

View file

@ -1,15 +1,12 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, Value}; use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_binding::cached_field; use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
pub(super) struct Which { pub(super) struct Which {
inner: PtrCell<yazi_core::which::Which>, inner: PtrCell<yazi_core::which::Which>,
v_tx: Option<Value>,
v_cands: Option<Value>,
} }
impl Deref for Which { impl Deref for Which {
@ -20,15 +17,15 @@ impl Deref for Which {
impl Which { impl Which {
pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_tx: None, v_cands: None }) Lives::scoped_userdata(Self { inner: inner.into() })
} }
} }
impl UserData for Which { impl UserData for Which {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, tx, |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx)));
fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
cached_field!(fields, cands, |lua, me| { fields.add_cached_field("cands", |lua, me| {
lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow)) lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow))
}); });

View file

@ -34,7 +34,9 @@ ansi-to-tui = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
hashbrown = { workspace = true } hashbrown = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true } mlua = { workspace = true }
ordered-float = { workspace = true }
paste = { workspace = true } paste = { workspace = true }
ratatui = { workspace = true } ratatui = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }

View file

@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter, cached_field}; use crate::{FileRef, Id, Iter};
#[derive(Clone)] #[derive(Clone)]
pub struct Fetcher { pub struct Fetcher {
inner: Arc<yazi_config::plugin::Fetcher>, inner: Arc<yazi_config::plugin::Fetcher>,
v_name: Option<Value>,
} }
impl Deref for Fetcher { impl Deref for Fetcher {
@ -20,7 +19,7 @@ impl Deref for Fetcher {
impl Fetcher { impl Fetcher {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Fetcher>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::plugin::Fetcher>>) -> Self {
Self { inner: inner.into(), v_name: None } Self { inner: inner.into() }
} }
} }
@ -28,7 +27,7 @@ impl UserData for Fetcher {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
} }
} }

View file

@ -2,13 +2,12 @@ use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter, cached_field}; use crate::{FileRef, Id, Iter};
pub struct OpenRule { pub struct OpenRule {
inner: Arc<yazi_config::open::OpenRule>, inner: Arc<yazi_config::open::OpenRule>,
v_use: Option<Value>,
} }
impl Deref for OpenRule { impl Deref for OpenRule {
@ -23,7 +22,7 @@ impl From<OpenRule> for Arc<yazi_config::open::OpenRule> {
impl OpenRule { impl OpenRule {
pub fn new(inner: impl Into<Arc<yazi_config::open::OpenRule>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::open::OpenRule>>) -> Self {
Self { inner: inner.into(), v_use: None } Self { inner: inner.into() }
} }
} }
@ -36,7 +35,7 @@ impl FromLua for OpenRule {
impl UserData for OpenRule { impl UserData for OpenRule {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, use, |lua, me| { fields.add_cached_field("use", |lua, me| {
lua.create_sequence_from(me.r#use.iter().map(|s| s.as_str())) lua.create_sequence_from(me.r#use.iter().map(|s| s.as_str()))
}); });
} }

View file

@ -1,14 +1,12 @@
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{Id, Iter, cached_field}; use crate::{Id, Iter};
pub struct OpenerRule { pub struct OpenerRule {
inner: Arc<yazi_config::opener::OpenerRule>, inner: Arc<yazi_config::opener::OpenerRule>,
v_run: Option<Value>,
v_desc: Option<Value>,
} }
impl Deref for OpenerRule { impl Deref for OpenerRule {
@ -23,7 +21,7 @@ impl From<OpenerRule> for Arc<yazi_config::opener::OpenerRule> {
impl OpenerRule { impl OpenerRule {
pub fn new(inner: impl Into<Arc<yazi_config::opener::OpenerRule>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::opener::OpenerRule>>) -> Self {
Self { inner: inner.into(), v_run: None, v_desc: None } Self { inner: inner.into() }
} }
} }
@ -39,10 +37,10 @@ impl FromLua for OpenerRule {
impl UserData for OpenerRule { impl UserData for OpenerRule {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, run, |lua, me| lua.create_string(&*me.run)); fields.add_cached_field("run", |lua, me| lua.create_string(&*me.run));
fields.add_field_method_get("block", |_, me| Ok(me.block)); fields.add_field_method_get("block", |_, me| Ok(me.block));
fields.add_field_method_get("orphan", |_, me| Ok(me.orphan)); fields.add_field_method_get("orphan", |_, me| Ok(me.orphan));
cached_field!(fields, desc, |lua, me| lua.create_string(&*me.desc)); fields.add_cached_field("desc", |lua, me| lua.create_string(&*me.desc));
} }
} }

View file

@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter, cached_field}; use crate::{FileRef, Id, Iter};
#[derive(Clone)] #[derive(Clone)]
pub struct Preloader { pub struct Preloader {
inner: Arc<yazi_config::plugin::Preloader>, inner: Arc<yazi_config::plugin::Preloader>,
v_name: Option<Value>,
} }
impl Deref for Preloader { impl Deref for Preloader {
@ -20,7 +19,7 @@ impl Deref for Preloader {
impl Preloader { impl Preloader {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Preloader>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::plugin::Preloader>>) -> Self {
Self { inner: inner.into(), v_name: None } Self { inner: inner.into() }
} }
} }
@ -28,7 +27,7 @@ impl UserData for Preloader {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
} }
} }

View file

@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter, cached_field}; use crate::{FileRef, Id, Iter};
#[derive(Clone)] #[derive(Clone)]
pub struct Previewer { pub struct Previewer {
inner: Arc<yazi_config::plugin::Previewer>, inner: Arc<yazi_config::plugin::Previewer>,
v_name: Option<Value>,
} }
impl Deref for Previewer { impl Deref for Previewer {
@ -24,7 +23,7 @@ impl From<Previewer> for Arc<yazi_config::plugin::Previewer> {
impl Previewer { impl Previewer {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Previewer>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::plugin::Previewer>>) -> Self {
Self { inner: inner.into(), v_name: None } Self { inner: inner.into() }
} }
} }
@ -38,7 +37,7 @@ impl UserData for Previewer {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
} }
} }

View file

@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter, cached_field}; use crate::{FileRef, Id, Iter};
#[derive(Clone)] #[derive(Clone)]
pub struct Spotter { pub struct Spotter {
inner: Arc<yazi_config::plugin::Spotter>, inner: Arc<yazi_config::plugin::Spotter>,
v_name: Option<Value>,
} }
impl Deref for Spotter { impl Deref for Spotter {
@ -20,7 +19,7 @@ impl Deref for Spotter {
impl Spotter { impl Spotter {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Spotter>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::plugin::Spotter>>) -> Self {
Self { inner: inner.into(), v_name: None } Self { inner: inner.into() }
} }
} }
@ -28,7 +27,7 @@ impl UserData for Spotter {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
} }
} }

View file

@ -1,16 +1,11 @@
use std::{mem, ops::Deref}; use std::{mem, ops::Deref};
use mlua::{UserData, UserDataFields, Value}; use mlua::{IntoLua, UserData, UserDataFields, Value};
use yazi_shim::strum::IntoStr; use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr};
use yazi_term::event::{DndDropArrive, DndEvent as Inner}; use yazi_term::event::{DndDropArrive, DndEvent as Inner};
use crate::{cached_field, cached_field_mut};
pub struct DndEvent { pub struct DndEvent {
inner: Inner, inner: Inner,
v_mimes: Option<Value>,
v_data: Option<mlua::Result<Value>>,
} }
impl Deref for DndEvent { impl Deref for DndEvent {
@ -20,7 +15,7 @@ impl Deref for DndEvent {
} }
impl From<Inner> for DndEvent { impl From<Inner> for DndEvent {
fn from(inner: Inner) -> Self { Self { inner, v_mimes: None, v_data: None } } fn from(inner: Inner) -> Self { Self { inner } }
} }
impl UserData for DndEvent { impl UserData for DndEvent {
@ -35,7 +30,7 @@ impl UserData for DndEvent {
fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str))); fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str)));
cached_field!(fields, mimes, |lua, me| { fields.add_cached_field("mimes", |lua, me| {
if let Some(mimes) = me.inner.mimes() { if let Some(mimes) = me.inner.mimes() {
lua.create_sequence_from(mimes.iter())?.into_lua(lua) lua.create_sequence_from(mimes.iter())?.into_lua(lua)
} else { } else {
@ -43,13 +38,11 @@ impl UserData for DndEvent {
} }
}); });
cached_field_mut!(fields, data, |lua, me| { fields.add_cached_field_mut("data", |lua, me| match &mut me.inner {
match &mut me.inner { Inner::DropArrive(DndDropArrive { data, .. }) => {
Inner::DropArrive(DndDropArrive { data, .. }) => { lua.create_external_string(mem::take(data))?.into_lua(lua)
lua.create_external_string(mem::take(data))?.into_lua(lua)
}
_ => Ok(Value::Nil),
} }
_ => Ok(Value::Nil),
}); });
} }
} }

View file

@ -1,6 +1,9 @@
use std::ops::Deref; use std::{mem, ops::{Deref, DerefMut}};
use mlua::UserData; use mlua::{UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::event::Cmd;
pub struct Action { pub struct Action {
inner: yazi_shared::event::Action, inner: yazi_shared::event::Action,
@ -12,8 +15,16 @@ impl Deref for Action {
fn deref(&self) -> &Self::Target { &self.inner } fn deref(&self) -> &Self::Target { &self.inner }
} }
impl DerefMut for Action {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl Action { impl Action {
pub fn new(inner: impl Into<yazi_shared::event::Action>) -> Self { Self { inner: inner.into() } } pub fn new(inner: impl Into<yazi_shared::event::Action>) -> Self { Self { inner: inner.into() } }
} }
impl UserData for Action {} impl UserData for Action {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field_mut("cmd", |_, me| Ok(Cmd::new(mem::take(&mut me.cmd))));
}
}

View file

@ -1,6 +1,9 @@
use std::ops::Deref; use std::{mem, ops::{Deref, DerefMut}};
use mlua::UserData; use mlua::{UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::Sendable;
pub struct Cmd { pub struct Cmd {
inner: yazi_shared::event::Cmd, inner: yazi_shared::event::Cmd,
@ -12,8 +15,20 @@ impl Deref for Cmd {
fn deref(&self) -> &Self::Target { &self.inner } fn deref(&self) -> &Self::Target { &self.inner }
} }
impl DerefMut for Cmd {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl Cmd { impl Cmd {
pub fn new(inner: impl Into<yazi_shared::event::Cmd>) -> Self { Self { inner: inner.into() } } pub fn new(inner: impl Into<yazi_shared::event::Cmd>) -> Self { Self { inner: inner.into() } }
} }
impl UserData for Cmd {} impl UserData for Cmd {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field_mut("name", |_, me| Ok(mem::take(&mut me.name)));
fields.add_cached_field_mut("args", |lua, me| {
Sendable::args_to_table(lua, mem::take(&mut me.args))
});
}
}

View file

@ -11,14 +11,6 @@ const EXPECTED: &str = "expected a table, File, or fs::File";
#[derive(Clone)] #[derive(Clone)]
pub struct File { pub struct File {
pub(crate) inner: yazi_fs::File, pub(crate) inner: yazi_fs::File,
v_cha: Option<Value>,
v_url: Option<Value>,
v_link_to: Option<Value>,
v_name: Option<Value>,
v_path: Option<Value>,
v_cache: Option<Value>,
} }
impl Deref for File { impl Deref for File {
@ -32,18 +24,7 @@ impl From<File> for yazi_fs::File {
} }
impl File { impl File {
pub fn new(inner: impl Into<yazi_fs::File>) -> Self { pub fn new(inner: impl Into<yazi_fs::File>) -> Self { Self { inner: inner.into() } }
Self {
inner: inner.into(),
v_cha: None,
v_url: None,
v_link_to: None,
v_name: None,
v_path: None,
v_cache: None,
}
}
pub fn install(lua: &Lua) -> mlua::Result<()> { pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set("File", lua.create_function(|_, file: Self| Ok(file))?) lua.globals().raw_set("File", lua.create_function(|_, file: Self| Ok(file))?)

View file

@ -1,14 +1,12 @@
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{Id, Iter, cached_field, event::Action, keymap::Key}; use crate::{Id, Iter, event::Action, keymap::Key};
pub struct Chord { pub struct Chord {
inner: Arc<yazi_config::keymap::Chord>, inner: Arc<yazi_config::keymap::Chord>,
v_on: Option<Value>,
v_run: Option<Value>,
} }
impl Deref for Chord { impl Deref for Chord {
@ -23,7 +21,7 @@ impl From<Chord> for Arc<yazi_config::keymap::Chord> {
impl Chord { impl Chord {
pub fn new(inner: impl Into<Arc<yazi_config::keymap::Chord>>) -> Self { pub fn new(inner: impl Into<Arc<yazi_config::keymap::Chord>>) -> Self {
Self { inner: inner.into(), v_on: None, v_run: None } Self { inner: inner.into() }
} }
} }
@ -37,10 +35,14 @@ impl UserData for Chord {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id))); fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id)));
cached_field!(fields, on, |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); fields
.add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key)));
cached_field!(fields, run, |lua, me| lua fields.add_cached_field("run", |lua, me| {
.create_sequence_from(me.run.iter().cloned().map(Action::new))); lua.create_sequence_from(me.run.iter().cloned().map(Action::new))
});
fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc));
} }
} }

View file

@ -1,16 +1,15 @@
use std::ops::Deref; use std::ops::Deref;
use anyhow::bail; use anyhow::bail;
use mlua::{UserData, UserDataFields, Value}; use mlua::{UserData, UserDataFields};
use yazi_config::KEYMAP; use yazi_config::KEYMAP;
use yazi_shared::Layer; use yazi_shared::Layer;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{cached_field, keymap::Chords}; use crate::keymap::Chords;
pub struct KeymapSection { pub struct KeymapSection {
inner: &'static yazi_config::keymap::KeymapSection, inner: &'static yazi_config::keymap::KeymapSection,
v_rules: Option<Value>,
} }
impl Deref for KeymapSection { impl Deref for KeymapSection {
@ -37,12 +36,12 @@ impl TryFrom<Layer> for KeymapSection {
Layer::Notify => bail!("invalid layer"), Layer::Notify => bail!("invalid layer"),
}; };
Ok(Self { inner, v_rules: None }) Ok(Self { inner })
} }
} }
impl UserData for KeymapSection { impl UserData for KeymapSection {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, rules, |_, me| Ok(Chords::new(me.inner))); fields.add_cached_field("rules", |_, me| Ok(Chords::new(me.inner)));
} }
} }

View file

@ -2,4 +2,4 @@ mod macros;
yazi_macro::mod_pub!(config elements event keymap process theme); yazi_macro::mod_pub!(config elements event keymap process theme);
yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector sendable stage style tty url utils);

View file

@ -43,48 +43,6 @@ macro_rules! deprecate {
}}; }};
} }
#[macro_export]
macro_rules! cached_field {
($fields:ident, $key:ident, $value:expr) => {
$fields.add_field_function_get(stringify!($key), |lua, ud| {
use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData};
ud.borrow_mut_scoped::<Self, Result<Value>>(|me| match paste::paste! { &me.[<v_ $key>] } {
Some(v) if !v.is_userdata() => Ok(v.clone()),
Some(v @ UserData(ud)) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => {
Ok(v.clone())
}
_ => {
let v = ($value as fn(&Lua, &Self) -> Result<_>)(lua, me)?.into_lua(lua)?;
paste::paste! { me.[<v_ $key>] = Some(v.clone()) };
Ok(v)
}
})?
});
};
}
#[macro_export]
macro_rules! cached_field_mut {
($fields:ident, $key:ident, $value:expr) => {
$fields.add_field_function_get(stringify!($key), |lua, ud| {
use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData};
ud.borrow_mut_scoped::<Self, Result<Value>>(|me| match paste::paste! { &me.[<v_ $key>] } {
Some(Ok(v)) if !v.is_userdata() => Ok(v.clone()),
Some(Ok(v @ UserData(ud))) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => {
Ok(v.clone())
}
Some(Err(e)) => Err(e.clone()),
_ => {
let v =
($value as fn(&Lua, &mut Self) -> Result<_>)(lua, me).and_then(|v| v.into_lua(lua));
paste::paste! { me.[<v_ $key>] = Some(v.clone()) };
v
}
})?
});
};
}
#[macro_export] #[macro_export]
macro_rules! impl_area_method { macro_rules! impl_area_method {
($methods:ident) => { ($methods:ident) => {
@ -245,19 +203,21 @@ macro_rules! impl_style_shorthands {
#[macro_export] #[macro_export]
macro_rules! impl_file_fields { macro_rules! impl_file_fields {
($fields:ident) => { ($fields:ident) => {
$crate::cached_field!($fields, cha, |_, me| Ok($crate::Cha(me.cha))); use yazi_shim::mlua::UserDataFieldsExt;
$crate::cached_field!($fields, url, |_, me| Ok($crate::Url::new(me.url_owned())));
$crate::cached_field!($fields, link_to, |_, me| Ok(me.link_to.as_ref().map($crate::Path::new)));
$crate::cached_field!($fields, name, |lua, me| { $fields.add_cached_field("cha", |_, me| Ok($crate::Cha(me.cha)));
$fields.add_cached_field("url", |_, me| Ok($crate::Url::new(me.url_owned())));
$fields.add_cached_field("link_to", |_, me| Ok(me.link_to.as_ref().map($crate::Path::new)));
$fields.add_cached_field("name", |lua, me| {
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
$crate::cached_field!($fields, path, |_, me| { $fields.add_cached_field("path", |_, me| {
use yazi_fs::FsUrl; use yazi_fs::FsUrl;
use yazi_shared::url::AsUrl; use yazi_shared::url::AsUrl;
Ok($crate::Path::new(me.url.as_url().unified_path())) Ok($crate::Path::new(me.url.as_url().unified_path()))
}); });
$crate::cached_field!($fields, cache, |_, me| { $fields.add_cached_field("cache", |_, me| {
use yazi_fs::FsUrl; use yazi_fs::FsUrl;
Ok(me.url.cache().map($crate::Path::new)) Ok(me.url.cache().map($crate::Path::new))
}); });

View file

@ -3,19 +3,13 @@ use std::ops::Deref;
use mlua::{ExternalError, ExternalResult, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; use mlua::{ExternalError, ExternalResult, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
use yazi_codegen::FromLuaOwned; use yazi_codegen::FromLuaOwned;
use yazi_shared::{path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, Strand, StrandCow}}; use yazi_shared::{path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, Strand, StrandCow}};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::cached_field;
pub type PathRef = UserDataRef<Path>; pub type PathRef = UserDataRef<Path>;
#[derive(FromLuaOwned)] #[derive(FromLuaOwned)]
pub struct Path { pub struct Path {
inner: PathBufDyn, inner: PathBufDyn,
v_ext: Option<Value>,
v_name: Option<Value>,
v_parent: Option<Value>,
v_stem: Option<Value>,
} }
impl Deref for Path { impl Deref for Path {
@ -37,16 +31,7 @@ impl AsStrand for &Path {
} }
impl Path { impl Path {
pub fn new(path: impl Into<PathBufDyn>) -> Self { pub fn new(path: impl Into<PathBufDyn>) -> Self { Self { inner: path.into() } }
Self {
inner: path.into(),
v_ext: None,
v_name: None,
v_parent: None,
v_stem: None,
}
}
pub fn install(lua: &Lua) -> mlua::Result<()> { pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set( lua.globals().raw_set(
@ -110,14 +95,14 @@ impl Path {
impl UserData for Path { impl UserData for Path {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, ext, |lua, me| { fields.add_cached_field("ext", |lua, me| {
me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
cached_field!(fields, name, |lua, me| { fields.add_cached_field("name", |lua, me| {
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new))); fields.add_cached_field("parent", |_, me| Ok(me.parent().map(Self::new)));
cached_field!(fields, stem, |lua, me| { fields.add_cached_field("stem", |lua, me| {
me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });

View file

@ -1,31 +1,25 @@
use std::mem; use std::mem;
use mlua::{UserData, UserDataFields, Value}; use mlua::{UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use super::Status; use super::Status;
use crate::{cached_field, cached_field_mut};
pub struct Output { pub struct Output {
inner: std::process::Output, inner: std::process::Output,
v_status: Option<Value>,
v_stdout: Option<mlua::Result<Value>>,
v_stderr: Option<mlua::Result<Value>>,
} }
impl Output { impl Output {
pub fn new(inner: std::process::Output) -> Self { pub fn new(inner: std::process::Output) -> Self { Self { inner } }
Self { inner, v_status: None, v_stdout: None, v_stderr: None }
}
} }
impl UserData for Output { impl UserData for Output {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, status, |_, me| Ok(Status::new(me.inner.status))); fields.add_cached_field("status", |_, me| Ok(Status::new(me.inner.status)));
cached_field_mut!(fields, stdout, |lua, me| { fields.add_cached_field_mut("stdout", |lua, me| {
lua.create_external_string(mem::take(&mut me.inner.stdout)) lua.create_external_string(mem::take(&mut me.inner.stdout))
}); });
cached_field_mut!(fields, stderr, |lua, me| { fields.add_cached_field_mut("stderr", |lua, me| {
lua.create_external_string(mem::take(&mut me.inner.stderr)) lua.create_external_string(mem::take(&mut me.inner.stderr))
}); });
} }

View file

@ -1,17 +1,14 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{UserData, UserDataFields, Value}; use mlua::{UserData, UserDataFields};
use yazi_fs::FsScheme; use yazi_fs::FsScheme;
use yazi_shared::scheme::SchemeLike; use yazi_shared::scheme::SchemeLike;
use yazi_shim::strum::IntoStr; use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr};
use crate::{Path, cached_field}; use crate::Path;
pub struct Scheme { pub struct Scheme {
inner: yazi_shared::scheme::Scheme, inner: yazi_shared::scheme::Scheme,
v_kind: Option<Value>,
v_cache: Option<Value>,
} }
impl Deref for Scheme { impl Deref for Scheme {
@ -22,14 +19,14 @@ impl Deref for Scheme {
impl Scheme { impl Scheme {
pub fn new(scheme: impl Into<yazi_shared::scheme::Scheme>) -> Self { pub fn new(scheme: impl Into<yazi_shared::scheme::Scheme>) -> Self {
Self { inner: scheme.into(), v_kind: None, v_cache: None } Self { inner: scheme.into() }
} }
} }
impl UserData for Scheme { impl UserData for Scheme {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, kind, |_, me| Ok(me.kind().into_str())); fields.add_cached_field("kind", |_, me| Ok(me.kind().into_str()));
cached_field!(fields, cache, |_, me| Ok(me.cache().map(Path::new))); fields.add_cached_field("cache", |_, me| Ok(me.cache().map(Path::new)));
fields.add_field_method_get("is_virtual", |_, me| Ok(me.is_virtual())); fields.add_field_method_get("is_virtual", |_, me| Ok(me.is_virtual()));
} }

View file

@ -5,6 +5,8 @@ use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow}; use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow};
use crate::{Id, Path, Url};
pub struct Sendable; pub struct Sendable;
impl Sendable { impl Sendable {
@ -40,14 +42,14 @@ impl Sendable {
}); });
} }
Value::UserData(ud) => match ud.type_id() { Value::UserData(ud) => match ud.type_id() {
Some(t) if t == TypeId::of::<yazi_binding::Url>() => { Some(t) if t == TypeId::of::<Url>() => {
return Ok(Data::Url(ud.take::<yazi_binding::Url>()?.into())); return Ok(Data::Url(ud.take::<Url>()?.into()));
} }
Some(t) if t == TypeId::of::<yazi_binding::Path>() => { Some(t) if t == TypeId::of::<Path>() => {
return Ok(Data::Path(ud.take::<yazi_binding::Path>()?.into())); return Ok(Data::Path(ud.take::<Path>()?.into()));
} }
Some(t) if t == TypeId::of::<yazi_binding::Id>() => { Some(t) if t == TypeId::of::<Id>() => {
return Ok(Data::Id(**ud.borrow::<yazi_binding::Id>()?)); return Ok(Data::Id(**ud.borrow::<Id>()?));
} }
Some(t) if t == TypeId::of::<AnyData>() => return Ok(Data::Any(ud.take::<AnyData>()?.0)), Some(t) if t == TypeId::of::<AnyData>() => return Ok(Data::Any(ud.take::<AnyData>()?.0)),
_ => {} _ => {}
@ -87,8 +89,8 @@ impl Sendable {
} }
Value::Table(tbl) Value::Table(tbl)
} }
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Url(u) => Url::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Path(u) => Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_external_string(b)?), Data::Bytes(b) => Value::String(lua.create_external_string(b)?),
Data::Any(a) => a.into_lua(lua)?, Data::Any(a) => a.into_lua(lua)?,
_ => Self::data_to_value_ref(lua, &data)?, _ => Self::data_to_value_ref(lua, &data)?,
@ -117,9 +119,9 @@ impl Sendable {
} }
Value::Table(tbl) Value::Table(tbl)
} }
Data::Id(i) => yazi_binding::Id(*i).into_lua(lua)?, Data::Id(i) => Id(*i).into_lua(lua)?,
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Url(u) => Url::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Path(u) => Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(a) => a.to_lua(lua)?, Data::Any(a) => a.to_lua(lua)?,
}) })
@ -201,15 +203,9 @@ impl Sendable {
Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Function(_) => Err("function is not supported".into_lua_err())?,
Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
Value::UserData(ud) => match ud.type_id() { Value::UserData(ud) => match ud.type_id() {
Some(t) if t == TypeId::of::<yazi_binding::Url>() => { Some(t) if t == TypeId::of::<Url>() => DataKey::Url(ud.take::<Url>()?.into()),
DataKey::Url(ud.take::<yazi_binding::Url>()?.into()) Some(t) if t == TypeId::of::<Path>() => DataKey::Path(ud.take::<Path>()?.into()),
} Some(t) if t == TypeId::of::<Id>() => DataKey::Id(**ud.borrow::<Id>()?),
Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
DataKey::Path(ud.take::<yazi_binding::Path>()?.into())
}
Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
DataKey::Id(**ud.borrow::<yazi_binding::Id>()?)
}
_ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?, _ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?,
}, },
Value::Error(_) => Err("error is not supported".into_lua_err())?, Value::Error(_) => Err("error is not supported".into_lua_err())?,
@ -220,8 +216,8 @@ impl Sendable {
fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> { fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> {
match key { match key {
DataKey::String(Cow::Owned(s)) => lua.create_external_string(s).map(Value::String), DataKey::String(Cow::Owned(s)) => lua.create_external_string(s).map(Value::String),
DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua), DataKey::Url(u) => Url::new(u).into_lua(lua),
DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua), DataKey::Path(u) => Path::new(u).into_lua(lua),
DataKey::Bytes(b) => lua.create_external_string(b).map(Value::String), DataKey::Bytes(b) => lua.create_external_string(b).map(Value::String),
_ => Self::key_to_value_ref(lua, &key), _ => Self::key_to_value_ref(lua, &key),
} }
@ -234,9 +230,9 @@ impl Sendable {
DataKey::Integer(i) => Value::Integer(*i), DataKey::Integer(i) => Value::Integer(*i),
DataKey::Number(n) => Value::Number(n.0), DataKey::Number(n) => Value::Number(n.0),
DataKey::String(s) => Value::String(lua.create_string(&**s)?), DataKey::String(s) => Value::String(lua.create_string(&**s)?),
DataKey::Id(i) => yazi_binding::Id(*i).into_lua(lua)?, DataKey::Id(i) => Id(*i).into_lua(lua)?,
DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, DataKey::Url(u) => Url::new(u).into_lua(lua)?,
DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, DataKey::Path(u) => Path::new(u).into_lua(lua)?,
DataKey::Bytes(b) => Value::String(lua.create_string(b)?), DataKey::Bytes(b) => Value::String(lua.create_string(b)?),
}) })
} }

View file

@ -4,8 +4,9 @@ use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, MetaMethod,
use yazi_codegen::FromLuaOwned; use yazi_codegen::FromLuaOwned;
use yazi_fs::{FsHash64, FsHash128, FsUrl}; use yazi_fs::{FsHash64, FsHash128, FsUrl};
use yazi_shared::{path::{PathLike, StripPrefixError}, scheme::{SchemeCow, SchemeLike}, strand::{StrandLike, ToStrand}, url::{AsUrl, UrlCow, UrlLike}}; use yazi_shared::{path::{PathLike, StripPrefixError}, scheme::{SchemeCow, SchemeLike}, strand::{StrandLike, ToStrand}, url::{AsUrl, UrlCow, UrlLike}};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{Path, Scheme, cached_field}; use crate::{Path, Scheme};
pub type UrlRef = UserDataRef<Url>; pub type UrlRef = UserDataRef<Url>;
@ -14,19 +15,6 @@ const EXPECTED: &str = "expected a string, Url, or Path";
#[derive(FromLuaOwned)] #[derive(FromLuaOwned)]
pub struct Url { pub struct Url {
inner: yazi_shared::url::UrlBuf, inner: yazi_shared::url::UrlBuf,
v_path: Option<Value>,
v_name: Option<Value>,
v_stem: Option<Value>,
v_ext: Option<Value>,
v_urn: Option<Value>,
v_base: Option<Value>,
v_parent: Option<Value>,
v_scheme: Option<Value>,
v_domain: Option<Value>,
v_cache: Option<Value>,
} }
impl Deref for Url { impl Deref for Url {
@ -62,24 +50,7 @@ impl TryFrom<&[u8]> for Url {
} }
impl Url { impl Url {
pub fn new(url: impl Into<yazi_shared::url::UrlBuf>) -> Self { pub fn new(url: impl Into<yazi_shared::url::UrlBuf>) -> Self { Self { inner: url.into() } }
Self {
inner: url.into(),
v_path: None,
v_name: None,
v_stem: None,
v_ext: None,
v_urn: None,
v_base: None,
v_parent: None,
v_scheme: None,
v_domain: None,
v_cache: None,
}
}
pub fn install(lua: &Lua) -> mlua::Result<()> { pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set( lua.globals().raw_set(
@ -171,28 +142,28 @@ impl Url {
impl UserData for Url { impl UserData for Url {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, path, |_, me| Ok(Path::new(me.loc()))); fields.add_cached_field("path", |_, me| Ok(Path::new(me.loc())));
cached_field!(fields, name, |lua, me| { fields.add_cached_field("name", |lua, me| {
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
cached_field!(fields, stem, |lua, me| { fields.add_cached_field("stem", |lua, me| {
me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
cached_field!(fields, ext, |lua, me| { fields.add_cached_field("ext", |lua, me| {
me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose() me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose()
}); });
cached_field!(fields, urn, |_, me| Ok(Path::new(me.urn()))); fields.add_cached_field("urn", |_, me| Ok(Path::new(me.urn())));
cached_field!(fields, base, |_, me| { fields.add_cached_field("base", |_, me| {
Ok(Some(me.base()).filter(|u| !u.loc().is_empty()).map(Self::new)) Ok(Some(me.base()).filter(|u| !u.loc().is_empty()).map(Self::new))
}); });
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new))); fields.add_cached_field("parent", |_, me| Ok(me.parent().map(Self::new)));
cached_field!(fields, scheme, |_, me| Ok(Scheme::new(me.scheme()))); fields.add_cached_field("scheme", |_, me| Ok(Scheme::new(me.scheme())));
cached_field!(fields, domain, |lua, me| { fields.add_cached_field("domain", |lua, me| {
me.scheme().domain().map(|s| lua.create_string(s)).transpose() me.scheme().domain().map(|s| lua.create_string(s)).transpose()
}); });
cached_field!(fields, cache, |_, me| Ok(me.cache().map(Path::new))); fields.add_cached_field("cache", |_, me| Ok(me.cache().map(Path::new)));
fields.add_field_method_get("is_regular", |_, me| Ok(me.is_regular())); fields.add_field_method_get("is_regular", |_, me| Ok(me.is_regular()));
fields.add_field_method_get("is_search", |_, me| Ok(me.is_search())); fields.add_field_method_get("is_search", |_, me| Ok(me.is_search()));

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, hash::{Hash, Hasher}, sync::{Arc, OnceLock}};
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Deserializer, de}; use serde::{Deserialize, Deserializer, de};
use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany};
use yazi_shared::{Id, Layer, Source, event::Action}; use yazi_shared::{Id, Layer, event::{Actions, deserialize_actions}};
use super::{Key, ids::chord_id}; use super::{Key, ids::chord_id};
use crate::{Mixable, Platform, keymap::Chords}; use crate::{Mixable, Platform, keymap::Chords};
@ -16,8 +16,8 @@ pub struct Chord<const L: u8 = { Layer::Null as u8 }> {
pub id: Id, pub id: Id,
#[serde(deserialize_with = "deserialize_on")] #[serde(deserialize_with = "deserialize_on")]
pub on: Vec<Key>, pub on: Vec<Key>,
#[serde(deserialize_with = "deserialize_run::<L, _>")] #[serde(deserialize_with = "deserialize_actions::<L, _>")]
pub run: Vec<Action>, pub run: Actions,
#[serde(default)] #[serde(default)]
pub desc: String, pub desc: String,
#[serde(default)] #[serde(default)]
@ -95,26 +95,6 @@ where
Ok(keys) Ok(keys)
} }
fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result<Vec<Action>, D::Error>
where
D: Deserializer<'de>,
{
let Some(layer) = Layer::from_repr(L) else {
return Err(de::Error::custom(format!("invalid keymap layer const: {L}")));
};
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
for action in &mut actions {
action.source = Source::Key;
if action.layer == Layer::Null {
action.layer = layer;
}
}
Ok(actions)
}
// --- Matcher // --- Matcher
#[derive(Default)] #[derive(Default)]
pub struct ChordMatcher { pub struct ChordMatcher {

View file

@ -1 +1 @@
yazi_macro::mod_flat!(chord chords cow ids key keymap section); yazi_macro::mod_flat!(chord chord_cow chords ids key keymap section);

View file

@ -31,7 +31,6 @@ hashbrown = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
inventory = { workspace = true } inventory = { workspace = true }
mlua = { workspace = true } mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }
paste = { workspace = true } paste = { workspace = true }
serde = { workspace = true } serde = { workspace = true }

View file

@ -1,9 +1,9 @@
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use serde::Serialize; use serde::Serialize;
use yazi_binding::Sendable;
use yazi_shared::data::Data; use yazi_shared::data::Data;
use super::Ember; use super::Ember;
use crate::Sendable;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EmberCustom { pub struct EmberCustom {

View file

@ -1,6 +1,6 @@
yazi_macro::mod_pub!(ember); yazi_macro::mod_pub!(ember);
yazi_macro::mod_flat!(client payload pubsub pump sendable server state stream); yazi_macro::mod_flat!(client payload pubsub pump server state stream);
pub fn init() { pub fn init() {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); let (tx, rx) = tokio::sync::mpsc::unbounded_channel();

View file

@ -20,9 +20,9 @@ yazi-term = { path = "../yazi-term", version = "26.5.6" }
yazi-tty = { path = "../yazi-tty", version = "26.5.9" } yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
# External dependencies # External dependencies
anyhow = { workspace = true } anyhow = { workspace = true }
either = { workspace = true } either = { workspace = true }
ratatui = { workspace = true } ratatui = { workspace = true }
scopeguard = { workspace = true } scopeguard = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }

View file

@ -1,6 +1,6 @@
use mlua::{ExternalError, Function, Lua, Table}; use mlua::{ExternalError, Function, Lua, Table};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use yazi_dds::Sendable; use yazi_binding::Sendable;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, Source, event::Action}; use yazi_shared::{Layer, Source, event::Action};

View file

@ -27,7 +27,7 @@ impl Utils {
Ok(ChordCow::Owned(Chord { Ok(ChordCow::Owned(Chord {
id: yazi_config::keymap::chord_id(), id: yazi_config::keymap::chord_id(),
on: Self::parse_keys(cand.raw_get("on")?)?, on: Self::parse_keys(cand.raw_get("on")?)?,
run: vec![relay!(which:callback, [i + 1])], run: relay!(which:callback, [i + 1]).into(),
desc: cand.raw_get("desc").unwrap_or_default(), desc: cand.raw_get("desc").unwrap_or_default(),
r#for: Platform::All, r#for: Platform::All,
})) }))

View file

@ -2,9 +2,8 @@ use anyhow::Context;
use futures::future::join_all; use futures::future::join_all;
use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, runtime, runtime_mut}; use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, Sendable, runtime, runtime_mut};
use yazi_core::{AppProxy, app::PluginOpt}; use yazi_core::{AppProxy, app::PluginOpt};
use yazi_dds::Sendable;
use yazi_runner::loader::LOADER; use yazi_runner::loader::LOADER;
use yazi_shared::{LOCAL_SET, data::Data}; use yazi_shared::{LOCAL_SET, data::Data};

View file

@ -19,7 +19,6 @@ vendored-lua = [ "mlua/vendored" ]
[dependencies] [dependencies]
yazi-binding = { path = "../yazi-binding", version = "26.5.6" } yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-config = { path = "../yazi-config", version = "26.5.6" }
yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" }

View file

@ -1,6 +1,6 @@
use hashbrown::HashMap; use hashbrown::HashMap;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use yazi_dds::Sendable; use yazi_binding::Sendable;
use yazi_shared::{Id, SStr, data::{Data, DataKey}}; use yazi_shared::{Id, SStr, data::{Data, DataKey}};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]

View file

@ -1,8 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use yazi_binding::File; use yazi_binding::{File, Sendable};
use yazi_dds::Sendable;
pub struct FetchJob { pub struct FetchJob {
pub fetcher: Arc<yazi_config::plugin::Fetcher>, pub fetcher: Arc<yazi_config::plugin::Fetcher>,

View file

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use yazi_binding::{File, elements::Rect}; use yazi_binding::{File, Sendable, elements::Rect};
use yazi_config::LAYOUT; use yazi_config::LAYOUT;
use yazi_dds::Sendable;
pub struct PreloadJob { pub struct PreloadJob {
pub preloader: Arc<yazi_config::plugin::Preloader>, pub preloader: Arc<yazi_config::plugin::Preloader>,

View file

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use yazi_binding::{File, elements::Rect}; use yazi_binding::{File, Sendable, elements::Rect};
use yazi_config::LAYOUT; use yazi_config::LAYOUT;
use yazi_dds::Sendable;
use yazi_shared::pool::Symbol; use yazi_shared::pool::Symbol;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -4,9 +4,8 @@ use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmS
use tokio::{runtime::Handle, select}; use tokio::{runtime::Handle, select};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tracing::error; use tracing::error;
use yazi_binding::{File, Id}; use yazi_binding::{File, Id, Sendable};
use yazi_config::plugin::Spotter; use yazi_config::plugin::Spotter;
use yazi_dds::Sendable;
use yazi_shared::{Ids, pool::Symbol}; use yazi_shared::{Ids, pool::Symbol};
use crate::{Runner, loader::LOADER}; use crate::{Runner, loader::LOADER};

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use hashbrown::HashMap; use hashbrown::HashMap;
use mlua::{ExternalError, FromLua, Lua, Value}; use mlua::{ExternalError, FromLua, Lua, Value};
use yazi_dds::Sendable; use yazi_binding::Sendable;
use yazi_runner::entry::EntryJob; use yazi_runner::entry::EntryJob;
use yazi_shared::{Id, SStr, data::{Data, DataKey}}; use yazi_shared::{Id, SStr, data::{Data, DataKey}};

View file

@ -0,0 +1,57 @@
use std::{ops::{Deref, DerefMut}, slice, vec};
use serde::{Deserializer, de};
use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany};
use crate::{Layer, Source, event::Action};
#[derive(Clone, Debug, Default)]
pub struct Actions(Vec<Action>);
impl Deref for Actions {
type Target = Vec<Action>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Actions {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl From<Action> for Actions {
fn from(value: Action) -> Self { Self(vec![value]) }
}
impl IntoIterator for Actions {
type IntoIter = vec::IntoIter<Action>;
type Item = Action;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl<'a> IntoIterator for &'a Actions {
type IntoIter = slice::Iter<'a, Action>;
type Item = &'a Action;
fn into_iter(self) -> Self::IntoIter { self.0.iter() }
}
pub fn deserialize_actions<'de, const L: u8, D>(deserializer: D) -> Result<Actions, D::Error>
where
D: Deserializer<'de>,
{
let Some(layer) = Layer::from_repr(L) else {
return Err(de::Error::custom(format!("invalid keymap layer const: {L}")));
};
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
for action in &mut actions {
action.source = Source::Key;
if action.layer == Layer::Null {
action.layer = layer;
}
}
Ok(Actions(actions))
}

View file

@ -1,4 +1,4 @@
yazi_macro::mod_flat!(action cmd cow de de_owned event); yazi_macro::mod_flat!(action action_cow actions cmd de de_owned event);
pub type Replier = tokio::sync::mpsc::UnboundedSender<anyhow::Result<crate::data::Data>>; pub type Replier = tokio::sync::mpsc::UnboundedSender<anyhow::Result<crate::data::Data>>;

View file

@ -0,0 +1,9 @@
use mlua::{Error::UserDataDestructed, Value};
pub(super) fn is_alive(v: &Value) -> bool {
if let Value::UserData(ud) = v {
!matches!(ud.borrow::<()>(), Err(UserDataDestructed))
} else {
true
}
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(sequence string traits); yazi_macro::mod_flat!(cache sequence string traits);

View file

@ -1,8 +1,8 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use mlua::{ExternalError, FromLua, Lua, Table, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use crate::{mlua::SequenceIter, toml::DeserializeOverWith}; use crate::{mlua::{SequenceIter, is_alive}, toml::DeserializeOverWith};
// --- DeserializeOverLua // --- DeserializeOverLua
pub trait DeserializeOverLua: DeserializeOverWith { pub trait DeserializeOverLua: DeserializeOverWith {
@ -29,3 +29,67 @@ impl LuaTableExt for Table {
} }
} }
} }
// --- UserDataFieldsExt
pub trait UserDataFieldsExt<S: UserData + 'static>: UserDataFields<S> {
fn add_cached_field<R: IntoLua + 'static>(
&mut self,
key: &'static str,
compute: fn(&Lua, &S) -> mlua::Result<R>,
) -> &mut Self {
self.add_field_function_get(key, move |lua, ud| {
match ud.named_user_value::<Option<Value>>(key)? {
Some(v) if is_alive(&v) => Ok(v),
_ => {
let v = compute(lua, &*ud.borrow::<S>()?)?.into_lua(lua)?;
ud.set_named_user_value(key, &v)?;
Ok(v)
}
}
});
self
}
fn add_cached_field_mut<R: IntoLua + 'static>(
&mut self,
key: &'static str,
compute: fn(&Lua, &mut S) -> mlua::Result<R>,
) -> &mut Self {
self.add_field_function_get(key, move |lua, ud| {
match ud.named_user_value::<Option<Value>>(key)? {
Some(Value::Error(e)) => Err(*e),
Some(v) if is_alive(&v) => Ok(v),
_ => {
let v = match compute(lua, &mut *ud.borrow_mut::<S>()?).and_then(|r| r.into_lua(lua)) {
Ok(v) => v,
Err(e) => Value::Error(Box::new(e)),
};
ud.set_named_user_value(key, &v)?;
Ok(v)
}
}
});
self
}
fn add_static_field<R: IntoLua + 'static>(
&mut self,
key: &'static str,
compute: fn(&Lua, &S) -> mlua::Result<R>,
) -> &mut Self {
self.add_field_function_get(key, move |lua, ud| {
match ud.named_user_value::<Option<Value>>(key)? {
Some(v) => Ok(v),
_ => {
let v = compute(lua, &*ud.borrow::<S>()?)?.into_lua(lua)?;
ud.set_named_user_value(key, &v)?;
Ok(v)
}
}
});
self
}
}
impl<S: UserData + 'static, F: UserDataFields<S>> UserDataFieldsExt<S> for F {}