From 510cb14ab6e348029763ec4c77a5b6a8c4b38caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Wed, 16 Apr 2025 20:08:12 +0800 Subject: [PATCH] feat: copy-on-write support for field caching (#2629) --- yazi-binding/src/macros.rs | 20 ++++++++++---------- yazi-binding/src/url.rs | 14 +++++++------- yazi-fm/src/lives/files.rs | 2 +- yazi-fm/src/lives/folder.rs | 12 ++++++------ yazi-fm/src/lives/preference.rs | 4 ++-- yazi-fm/src/lives/preview.rs | 2 +- yazi-fm/src/lives/tab.rs | 18 +++++++++--------- yazi-fm/src/lives/tasks.rs | 4 ++-- yazi-plugin/src/bindings/icon.rs | 6 +++--- yazi-plugin/src/config/plugin.rs | 8 ++++---- yazi-plugin/src/macros.rs | 12 ++++-------- yazi-plugin/src/process/output.rs | 8 ++++---- 12 files changed, 53 insertions(+), 57 deletions(-) diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 90c65098..8495d63d 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -2,16 +2,16 @@ macro_rules! cached_field { ($fields:ident, $key:ident, $value:expr) => { $fields.add_field_function_get(stringify!($key), |lua, ud| { - use mlua::IntoLua; - ud.borrow_mut_scoped::>(|me| { - match paste::paste! { &me.[] } { - Some(v) => Ok(v.clone()), - None => { - let v: mlua::Result<_> = $value(lua, me); - let v = v?.into_lua(lua)?; - paste::paste! { me.[] = Some(v.clone()) }; - Ok(v) - } + use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData}; + ud.borrow_mut_scoped::>(|me| match paste::paste! { &me.[] } { + 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.[] = Some(v.clone()) }; + Ok(v) } })? }); diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index 176f29c1..81ae6e2a 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -62,7 +62,7 @@ impl Url { impl FromLua for Url { fn from_lua(value: Value, _: &Lua) -> mlua::Result { Ok(match value { - Value::UserData(ud) => Self::new(ud.take::()?.inner), + Value::UserData(ud) => ud.take()?, _ => Err("Expected a Url".into_lua_err())?, }) } @@ -70,23 +70,23 @@ impl FromLua for Url { impl UserData for Url { fn add_fields>(fields: &mut F) { - cached_field!(fields, name, |lua: &Lua, me: &Self| { + cached_field!(fields, name, |lua, me| { Some(me.name()) .filter(|&s| !s.is_empty()) .map(|s| lua.create_string(s.as_encoded_bytes())) .transpose() }); - cached_field!(fields, stem, |lua: &Lua, me: &Self| { + cached_field!(fields, stem, |lua, me| { me.file_stem().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() }); - cached_field!(fields, ext, |lua: &Lua, me: &Self| { + cached_field!(fields, ext, |lua, me| { me.extension().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() }); - cached_field!(fields, parent, |_, me: &Self| Ok(me.parent_url().map(Self::new))); - cached_field!(fields, base, |_, me: &Self| { + cached_field!(fields, parent, |_, me| Ok(me.parent_url().map(Self::new))); + cached_field!(fields, base, |_, me| { Ok(if me.base().as_os_str().is_empty() { None } else { Some(Self::new(me.base())) }) }); - cached_field!(fields, frag, |lua: &Lua, me: &Self| lua.create_string(me.frag())); + cached_field!(fields, frag, |lua, me| lua.create_string(me.frag())); fields.add_field_method_get("is_regular", |_, me| Ok(me.is_regular())); fields.add_field_method_get("is_search", |_, me| Ok(me.is_search())); diff --git a/yazi-fm/src/lives/files.rs b/yazi-fm/src/lives/files.rs index 2f2ba645..f6114bac 100644 --- a/yazi-fm/src/lives/files.rs +++ b/yazi-fm/src/lives/files.rs @@ -32,7 +32,7 @@ impl Files { impl UserData for Files { fn add_fields>(fields: &mut F) { - cached_field!(fields, filter, |_, me: &Self| me.filter().map(Filter::make).transpose()); + cached_field!(fields, filter, |_, me| me.filter().map(Filter::make).transpose()); } fn add_methods>(methods: &mut M) { diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index 22939195..9ce52196 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -1,6 +1,6 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, Lua, UserData, UserDataFields, Value}; +use mlua::{AnyUserData, UserData, UserDataFields, Value}; use yazi_binding::{FolderStage, Url, cached_field}; use yazi_config::LAYOUT; @@ -55,14 +55,14 @@ impl Folder { impl UserData for Folder { fn add_fields>(fields: &mut F) { - cached_field!(fields, cwd, |_, me: &Self| Ok(Url::new(me.url.to_owned()))); - cached_field!(fields, files, |_, me: &Self| Files::make(0..me.files.len(), me, &me.tab)); - cached_field!(fields, stage, |_: &Lua, me: &Self| Ok(FolderStage::new(me.stage))); - cached_field!(fields, window, |_, me: &Self| Files::make(me.window.clone(), me, &me.tab)); + cached_field!(fields, cwd, |_, me| Ok(Url::new(me.url.to_owned()))); + cached_field!(fields, files, |_, me| Files::make(0..me.files.len(), me, &me.tab)); + cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage))); + cached_field!(fields, 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("cursor", |_, me| Ok(me.cursor)); - cached_field!(fields, hovered, |_, me: &Self| { + cached_field!(fields, hovered, |_, me| { me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose() }); } diff --git a/yazi-fm/src/lives/preference.rs b/yazi-fm/src/lives/preference.rs index d12c0f7d..4e5c4e0a 100644 --- a/yazi-fm/src/lives/preference.rs +++ b/yazi-fm/src/lives/preference.rs @@ -27,13 +27,13 @@ impl Preference { impl UserData for Preference { fn add_fields>(fields: &mut F) { - cached_field!(fields, sort_by, |_, me: &Self| Ok(me.sort_by.to_string())); + cached_field!(fields, sort_by, |_, me| Ok(me.sort_by.to_string())); 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_dir_first", |_, me| Ok(me.sort_dir_first)); fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit)); - cached_field!(fields, linemode, |_, me: &Self| Ok(me.linemode.to_string())); + cached_field!(fields, linemode, |_, me| Ok(me.linemode.to_string())); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); } } diff --git a/yazi-fm/src/lives/preview.rs b/yazi-fm/src/lives/preview.rs index e629b197..7e618458 100644 --- a/yazi-fm/src/lives/preview.rs +++ b/yazi-fm/src/lives/preview.rs @@ -28,7 +28,7 @@ impl Preview { impl UserData for Preview { fn add_fields>(fields: &mut F) { fields.add_field_method_get("skip", |_, me| Ok(me.skip)); - cached_field!(fields, folder, |_, me: &Self| { + cached_field!(fields, folder, |_, me| { me.tab .hovered_folder() .map(|f| { diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index 6bc147bd..067b9a50 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use mlua::{AnyUserData, Lua, UserData, UserDataFields, UserDataMethods, Value}; +use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value}; use yazi_binding::{UrlRef, cached_field}; use yazi_plugin::Id; @@ -46,21 +46,21 @@ impl Tab { impl UserData for Tab { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua: &Lua, me: &Self| { + cached_field!(fields, name, |lua, me| { lua.create_string(me.current.url.name().as_encoded_bytes()) }); - cached_field!(fields, mode, |_, me: &Self| Mode::make(&me.mode)); - cached_field!(fields, pref, |_, me: &Self| Preference::make(&me.pref)); - cached_field!(fields, current, |_, me: &Self| Folder::make(None, &me.current, me)); - cached_field!(fields, parent, |_, me: &Self| { + cached_field!(fields, mode, |_, me| Mode::make(&me.mode)); + cached_field!(fields, pref, |_, me| Preference::make(&me.pref)); + cached_field!(fields, current, |_, me| Folder::make(None, &me.current, me)); + cached_field!(fields, parent, |_, me| { me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() }); - cached_field!(fields, selected, |_, me: &Self| Selected::make(&me.selected)); + cached_field!(fields, selected, |_, me| Selected::make(&me.selected)); - cached_field!(fields, preview, |_, me: &Self| Preview::make(me)); - cached_field!(fields, finder, |_, me: &Self| me.finder.as_ref().map(Finder::make).transpose()); + cached_field!(fields, preview, |_, me| Preview::make(me)); + cached_field!(fields, finder, |_, me| me.finder.as_ref().map(Finder::make).transpose()); } fn add_methods>(methods: &mut M) { diff --git a/yazi-fm/src/lives/tasks.rs b/yazi-fm/src/lives/tasks.rs index 2a85aa2a..a79a59e4 100644 --- a/yazi-fm/src/lives/tasks.rs +++ b/yazi-fm/src/lives/tasks.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use mlua::{AnyUserData, Lua, LuaSerdeExt, UserData, UserDataFields, Value}; +use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; use yazi_binding::cached_field; use super::{Lives, PtrCell}; @@ -26,6 +26,6 @@ impl Tasks { impl UserData for Tasks { fn add_fields>(fields: &mut F) { - cached_field!(fields, progress, |lua: &Lua, me: &Self| lua.to_value(&me.progress)); + cached_field!(fields, progress, |lua, me| lua.to_value(&me.progress)); } } diff --git a/yazi-plugin/src/bindings/icon.rs b/yazi-plugin/src/bindings/icon.rs index d27ddc03..eff21620 100644 --- a/yazi-plugin/src/bindings/icon.rs +++ b/yazi-plugin/src/bindings/icon.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use mlua::{Lua, UserData, UserDataFields, Value}; +use mlua::{UserData, UserDataFields, Value}; use yazi_binding::cached_field; use crate::elements::Style; @@ -26,7 +26,7 @@ impl From<&'static yazi_shared::theme::Icon> for Icon { impl UserData for Icon { fn add_fields>(fields: &mut F) { - cached_field!(fields, text, |lua: &Lua, me: &Self| lua.create_string(&me.text)); - cached_field!(fields, style, |_, me: &Self| Ok(Style::from(me.style))); + cached_field!(fields, text, |lua, me| lua.create_string(&me.text)); + cached_field!(fields, style, |_, me| Ok(Style::from(me.style))); } } diff --git a/yazi-plugin/src/config/plugin.rs b/yazi-plugin/src/config/plugin.rs index ec8b19ea..e0979673 100644 --- a/yazi-plugin/src/config/plugin.rs +++ b/yazi-plugin/src/config/plugin.rs @@ -58,7 +58,7 @@ impl Fetcher { impl UserData for Fetcher { fn add_fields>(fields: &mut F) { - cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); + cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name)); } } @@ -75,7 +75,7 @@ impl Spotter { impl UserData for Spotter { fn add_fields>(fields: &mut F) { - cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); + cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name)); } } @@ -92,7 +92,7 @@ impl Preloader { impl UserData for Preloader { fn add_fields>(fields: &mut F) { - cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); + cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name)); } } @@ -109,6 +109,6 @@ impl Previewer { impl UserData for Previewer { fn add_fields>(fields: &mut F) { - cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); + cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name)); } } diff --git a/yazi-plugin/src/macros.rs b/yazi-plugin/src/macros.rs index e6d284b7..1c554fca 100644 --- a/yazi-plugin/src/macros.rs +++ b/yazi-plugin/src/macros.rs @@ -81,17 +81,13 @@ macro_rules! impl_style_shorthands { #[macro_export] macro_rules! impl_file_fields { ($fields:ident) => { - yazi_binding::cached_field!($fields, cha, |_, me: &Self| Ok($crate::bindings::Cha::from( - me.cha - ))); - yazi_binding::cached_field!($fields, url, |_, me: &Self| Ok(yazi_binding::Url::new( - me.url_owned() - ))); - yazi_binding::cached_field!($fields, link_to, |_, me: &Self| Ok( + yazi_binding::cached_field!($fields, cha, |_, me| Ok($crate::bindings::Cha::from(me.cha))); + yazi_binding::cached_field!($fields, url, |_, me| Ok(yazi_binding::Url::new(me.url_owned()))); + yazi_binding::cached_field!($fields, link_to, |_, me| Ok( me.link_to.clone().map(yazi_binding::Url::new) )); - yazi_binding::cached_field!($fields, name, |lua: &mlua::Lua, me: &Self| { + yazi_binding::cached_field!($fields, name, |lua, me| { Some(me.name()) .filter(|s| !s.is_empty()) .map(|s| lua.create_string(s.as_encoded_bytes())) diff --git a/yazi-plugin/src/process/output.rs b/yazi-plugin/src/process/output.rs index 8b1a5af0..7b250634 100644 --- a/yazi-plugin/src/process/output.rs +++ b/yazi-plugin/src/process/output.rs @@ -1,4 +1,4 @@ -use mlua::{Lua, UserData, Value}; +use mlua::{UserData, Value}; use yazi_binding::cached_field; use super::Status; @@ -19,8 +19,8 @@ impl Output { impl UserData for Output { fn add_fields>(fields: &mut F) { - cached_field!(fields, status, |_, me: &Self| Ok(Status::new(me.inner.status))); - cached_field!(fields, stdout, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stdout)); - cached_field!(fields, stderr, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stderr)); + cached_field!(fields, status, |_, me| Ok(Status::new(me.inner.status))); + cached_field!(fields, stdout, |lua, me| lua.create_string(&me.inner.stdout)); + cached_field!(fields, stderr, |lua, me| lua.create_string(&me.inner.stderr)); } }