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 {
($fields:ident, $key:ident, $value:expr) => {
$fields.add_field_function_get(stringify!($key), |lua, ud| {
use mlua::IntoLua;
ud.borrow_mut_scoped::<Self, mlua::Result<mlua::Value>>(|me| {
match paste::paste! { &me.[<v_ $key>] } {
Some(v) => Ok(v.clone()),
None => {
let v: mlua::Result<_> = $value(lua, me);
let v = v?.into_lua(lua)?;
paste::paste! { me.[<v_ $key>] = Some(v.clone()) };
Ok(v)
}
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)
}
})?
});

View file

@ -62,7 +62,7 @@ impl Url {
impl FromLua for Url {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
Ok(match value {
Value::UserData(ud) => Self::new(ud.take::<Self>()?.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<F: UserDataFields<Self>>(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()));

View file

@ -32,7 +32,7 @@ impl Files {
impl UserData for Files {
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) {

View file

@ -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<F: UserDataFields<Self>>(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()
});
}

View file

@ -27,13 +27,13 @@ impl Preference {
impl UserData for Preference {
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_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));
}
}

View file

@ -28,7 +28,7 @@ impl Preview {
impl UserData for Preview {
fn add_fields<F: UserDataFields<Self>>(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| {

View file

@ -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<F: UserDataFields<Self>>(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<M: UserDataMethods<Self>>(methods: &mut M) {

View file

@ -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<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 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<F: UserDataFields<Self>>(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)));
}
}

View file

@ -58,7 +58,7 @@ impl Fetcher {
impl UserData for Fetcher {
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 {
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 {
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 {
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_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()))

View file

@ -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<F: mlua::UserDataFields<Self>>(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));
}
}