feat: copy-on-write support for field caching (#2629)

This commit is contained in:
三咲雅 · Misaki Masa 2025-04-16 20:08:12 +08:00 committed by GitHub
parent 56d6e4a71e
commit 510cb14ab6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 53 additions and 57 deletions

View file

@ -2,16 +2,16 @@
macro_rules! cached_field { macro_rules! cached_field {
($fields:ident, $key:ident, $value:expr) => { ($fields:ident, $key:ident, $value:expr) => {
$fields.add_field_function_get(stringify!($key), |lua, ud| { $fields.add_field_function_get(stringify!($key), |lua, ud| {
use mlua::IntoLua; use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData};
ud.borrow_mut_scoped::<Self, mlua::Result<mlua::Value>>(|me| { ud.borrow_mut_scoped::<Self, Result<Value>>(|me| match paste::paste! { &me.[<v_ $key>] } {
match paste::paste! { &me.[<v_ $key>] } { Some(v) if !v.is_userdata() => Ok(v.clone()),
Some(v) => Ok(v.clone()), Some(v @ UserData(ud)) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => {
None => { Ok(v.clone())
let v: mlua::Result<_> = $value(lua, me); }
let v = v?.into_lua(lua)?; _ => {
paste::paste! { me.[<v_ $key>] = Some(v.clone()) }; let v = ($value as fn(&Lua, &Self) -> Result<_>)(lua, me)?.into_lua(lua)?;
Ok(v) paste::paste! { me.[<v_ $key>] = Some(v.clone()) };
} Ok(v)
} }
})? })?
}); });

View file

@ -62,7 +62,7 @@ impl Url {
impl FromLua for Url { impl FromLua for Url {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> { fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
Ok(match value { Ok(match value {
Value::UserData(ud) => Self::new(ud.take::<Self>()?.inner), Value::UserData(ud) => ud.take()?,
_ => Err("Expected a Url".into_lua_err())?, _ => Err("Expected a Url".into_lua_err())?,
}) })
} }
@ -70,23 +70,23 @@ impl FromLua for 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, name, |lua: &Lua, me: &Self| { cached_field!(fields, name, |lua, me| {
Some(me.name()) Some(me.name())
.filter(|&s| !s.is_empty()) .filter(|&s| !s.is_empty())
.map(|s| lua.create_string(s.as_encoded_bytes())) .map(|s| lua.create_string(s.as_encoded_bytes()))
.transpose() .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() 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() 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, parent, |_, me| Ok(me.parent_url().map(Self::new)));
cached_field!(fields, base, |_, me: &Self| { cached_field!(fields, base, |_, me| {
Ok(if me.base().as_os_str().is_empty() { None } else { Some(Self::new(me.base())) }) 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_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

@ -32,7 +32,7 @@ impl Files {
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: &Self| me.filter().map(Filter::make).transpose()); cached_field!(fields, 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,6 +1,6 @@
use std::ops::{Deref, Range}; 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_binding::{FolderStage, Url, cached_field};
use yazi_config::LAYOUT; use yazi_config::LAYOUT;
@ -55,14 +55,14 @@ impl Folder {
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: &Self| Ok(Url::new(me.url.to_owned()))); cached_field!(fields, cwd, |_, me| 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, files, |_, me| Files::make(0..me.files.len(), me, &me.tab));
cached_field!(fields, stage, |_: &Lua, me: &Self| Ok(FolderStage::new(me.stage))); cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage)));
cached_field!(fields, window, |_, me: &Self| Files::make(me.window.clone(), me, &me.tab)); 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("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: &Self| { cached_field!(fields, 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

@ -27,13 +27,13 @@ impl Preference {
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) {
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_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));
fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit)); 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)); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
} }
} }

View file

@ -28,7 +28,7 @@ impl Preview {
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: &Self| { cached_field!(fields, folder, |_, me| {
me.tab me.tab
.hovered_folder() .hovered_folder()
.map(|f| { .map(|f| {

View file

@ -1,6 +1,6 @@
use std::ops::Deref; 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_binding::{UrlRef, cached_field};
use yazi_plugin::Id; use yazi_plugin::Id;
@ -46,21 +46,21 @@ impl Tab {
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: &Lua, me: &Self| { cached_field!(fields, name, |lua, me| {
lua.create_string(me.current.url.name().as_encoded_bytes()) lua.create_string(me.current.url.name().as_encoded_bytes())
}); });
cached_field!(fields, mode, |_, me: &Self| Mode::make(&me.mode)); cached_field!(fields, mode, |_, me| Mode::make(&me.mode));
cached_field!(fields, pref, |_, me: &Self| Preference::make(&me.pref)); cached_field!(fields, pref, |_, me| Preference::make(&me.pref));
cached_field!(fields, current, |_, me: &Self| Folder::make(None, &me.current, me)); cached_field!(fields, current, |_, me| Folder::make(None, &me.current, me));
cached_field!(fields, parent, |_, me: &Self| { cached_field!(fields, 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: &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, preview, |_, me| Preview::make(me));
cached_field!(fields, finder, |_, me: &Self| me.finder.as_ref().map(Finder::make).transpose()); cached_field!(fields, 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,6 +1,6 @@
use std::ops::Deref; 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 yazi_binding::cached_field;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
@ -26,6 +26,6 @@ impl Tasks {
impl UserData for Tasks { impl UserData for Tasks {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(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));
} }
} }

View file

@ -1,6 +1,6 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{Lua, UserData, UserDataFields, Value}; use mlua::{UserData, UserDataFields, Value};
use yazi_binding::cached_field; use yazi_binding::cached_field;
use crate::elements::Style; use crate::elements::Style;
@ -26,7 +26,7 @@ impl From<&'static yazi_shared::theme::Icon> for Icon {
impl UserData for Icon { impl UserData for Icon {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, text, |lua: &Lua, me: &Self| lua.create_string(&me.text)); cached_field!(fields, text, |lua, me| lua.create_string(&me.text));
cached_field!(fields, style, |_, me: &Self| Ok(Style::from(me.style))); cached_field!(fields, style, |_, me| Ok(Style::from(me.style)));
} }
} }

View file

@ -58,7 +58,7 @@ impl Fetcher {
impl UserData for Fetcher { impl UserData for Fetcher {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(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 { impl UserData for Spotter {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(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 { impl UserData for Preloader {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(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 { impl UserData for Previewer {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(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));
} }
} }

View file

@ -81,17 +81,13 @@ macro_rules! impl_style_shorthands {
#[macro_export] #[macro_export]
macro_rules! impl_file_fields { macro_rules! impl_file_fields {
($fields:ident) => { ($fields:ident) => {
yazi_binding::cached_field!($fields, cha, |_, me: &Self| Ok($crate::bindings::Cha::from( yazi_binding::cached_field!($fields, cha, |_, me| Ok($crate::bindings::Cha::from(me.cha)));
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(
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(
me.link_to.clone().map(yazi_binding::Url::new) 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()) Some(me.name())
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.map(|s| lua.create_string(s.as_encoded_bytes())) .map(|s| lua.create_string(s.as_encoded_bytes()))

View file

@ -1,4 +1,4 @@
use mlua::{Lua, UserData, Value}; use mlua::{UserData, Value};
use yazi_binding::cached_field; use yazi_binding::cached_field;
use super::Status; use super::Status;
@ -19,8 +19,8 @@ impl Output {
impl UserData for Output { impl UserData for Output {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, status, |_, me: &Self| Ok(Status::new(me.inner.status))); cached_field!(fields, status, |_, me| Ok(Status::new(me.inner.status)));
cached_field!(fields, stdout, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stdout)); cached_field!(fields, stdout, |lua, me| lua.create_string(&me.inner.stdout));
cached_field!(fields, stderr, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stderr)); cached_field!(fields, stderr, |lua, me| lua.create_string(&me.inner.stderr));
} }
} }