From c12a735f682cb4aad4f53a2bdf29aea1bf1ad1b7 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: Thu, 8 Feb 2024 12:14:01 +0800 Subject: [PATCH] perf: cheaper sync context initialization (#643) --- yazi-fm/src/lives/active.rs | 10 +- yazi-fm/src/lives/file.rs | 122 +++++++++++++++++ yazi-fm/src/lives/files.rs | 43 ++++++ yazi-fm/src/lives/folder.rs | 204 ++++------------------------- yazi-fm/src/lives/lives.rs | 2 + yazi-fm/src/lives/mod.rs | 13 +- yazi-fm/src/lives/tabs.rs | 22 ++-- yazi-fm/src/lives/tasks.rs | 2 +- yazi-plugin/src/bindings/file.rs | 9 +- yazi-plugin/src/isolate/isolate.rs | 2 +- yazi-plugin/src/plugin.rs | 1 + 11 files changed, 219 insertions(+), 211 deletions(-) create mode 100644 yazi-fm/src/lives/file.rs create mode 100644 yazi-fm/src/lives/files.rs diff --git a/yazi-fm/src/lives/active.rs b/yazi-fm/src/lives/active.rs index 0ba37501..09785287 100644 --- a/yazi-fm/src/lives/active.rs +++ b/yazi-fm/src/lives/active.rs @@ -1,9 +1,8 @@ use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods, Value}; -use yazi_config::LAYOUT; use super::Folder; -pub struct Active<'a, 'b> { +pub(super) struct Active<'a, 'b> { scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::tab::Tab, @@ -50,9 +49,9 @@ impl<'a, 'b> Active<'a, 'b> { ud.set_named_user_value("conf", self.scope.create_any_userdata_ref(&self.inner.conf)?)?; ud.set_named_user_value( "parent", - self.inner.parent.as_ref().and_then(|p| Folder::new(self.scope, p).make(None).ok()), + self.inner.parent.as_ref().and_then(|p| Folder::make(self.scope, p).ok()), )?; - ud.set_named_user_value("current", Folder::new(self.scope, &self.inner.current).make(None)?)?; + ud.set_named_user_value("current", Folder::make(self.scope, &self.inner.current)?)?; ud.set_named_user_value("preview", self.preview(self.inner)?)?; Ok(ud) @@ -60,7 +59,6 @@ impl<'a, 'b> Active<'a, 'b> { fn preview(&self, tab: &'a yazi_core::tab::Tab) -> mlua::Result> { let inner = &tab.preview; - let window = Some((inner.skip, LAYOUT.load().preview.height as usize)); let ud = self.scope.create_any_userdata_ref(inner)?; ud.set_named_user_value( @@ -70,7 +68,7 @@ impl<'a, 'b> Active<'a, 'b> { .hovered() .filter(|&f| f.is_dir()) .and_then(|f| tab.history(&f.url)) - .and_then(|f| Folder::new(self.scope, f).make(window).ok()), + .and_then(|f| Folder::make(self.scope, f).ok()), )?; Ok(ud) diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs new file mode 100644 index 00000000..806f8af4 --- /dev/null +++ b/yazi-fm/src/lives/file.rs @@ -0,0 +1,122 @@ +use std::ops::Deref; + +use mlua::{IntoLua, Lua, UserDataFields, UserDataMethods}; +use yazi_config::THEME; +use yazi_plugin::{bindings::{Cast, Cha, Icon, Range}, elements::Style, url::Url}; +use yazi_shared::MIME_DIR; + +use super::CtxRef; + +pub(super) struct File { + idx: usize, + folder: *const yazi_core::folder::Folder, +} + +impl Deref for File { + type Target = yazi_shared::fs::File; + + fn deref(&self) -> &Self::Target { &self.folder().files[self.idx] } +} + +impl File { + #[inline] + pub(super) fn new(idx: usize, folder: &yazi_core::folder::Folder) -> Self { Self { idx, folder } } + + pub(super) fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|reg| { + reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone())); + reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha)); + reg.add_field_method_get("link_to", |lua, me| { + me.link_to.as_ref().cloned().map(|u| Url::cast(lua, u)).transpose() + }); + + reg.add_field_method_get("name", |lua, me| { + me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose() + }); + reg.add_method("size", |_, me, ()| { + Ok(if me.is_dir() { me.folder().files.sizes.get(&me.url).copied() } else { Some(me.len) }) + }); + reg.add_method("mime", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + Ok(cx.manager.mimetype.get(&me.url).cloned()) + }); + reg.add_method("prefix", |lua, me, ()| { + if !me.folder().cwd.is_search() { + return Ok(None); + } + + let mut p = me.url.strip_prefix(&me.folder().cwd).unwrap_or(&me.url).components(); + p.next_back(); + Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() + }); + reg.add_method("icon", |lua, me, ()| { + THEME + .icons + .iter() + .find(|&x| x.name.match_path(&me.url, me.is_dir())) + .map(|x| Icon::cast(lua, x)) + .transpose() + }); + reg.add_method("style", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + let mime = + if me.is_dir() { Some(MIME_DIR) } else { cx.manager.mimetype.get(&me.url).map(|x| &**x) }; + + Ok(THEME.filetypes.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style))) + }); + reg.add_method("is_hovered", |_, me, ()| { + Ok(matches!(me.folder().hovered(), Some(f) if f.url == me.url)) + }); + reg.add_method("is_yanked", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + Ok(if !cx.manager.yanked.1.contains(&me.url) { + 0u8 + } else if cx.manager.yanked.0 { + 2u8 + } else { + 1u8 + }) + }); + reg.add_method("is_selected", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + let selected = me.folder().files.is_selected(&me.url); + + Ok(if !cx.manager.active().mode.is_visual() { + selected + } else { + cx.manager.active().mode.pending(me.idx, selected) + }) + }); + reg.add_method("found", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + let Some(finder) = &cx.manager.active().finder else { + return Ok(None); + }; + + let Some(idx) = finder.matched_idx(&me.url) else { + return Ok(None); + }; + + Some(lua.create_sequence_from([idx.into_lua(lua)?, finder.matched().len().into_lua(lua)?])) + .transpose() + }); + reg.add_method("highlights", |lua, me, ()| { + let cx = lua.named_registry_value::("cx")?; + let Some(finder) = &cx.manager.active().finder else { + return Ok(None); + }; + + let Some(h) = me.name().and_then(|n| finder.filter.highlighted(n)) else { + return Ok(None); + }; + + Ok(Some(h.into_iter().map(Range::from).collect::>())) + }); + })?; + + Ok(()) + } + + #[inline] + fn folder(&self) -> &yazi_core::folder::Folder { unsafe { &*self.folder } } +} diff --git a/yazi-fm/src/lives/files.rs b/yazi-fm/src/lives/files.rs new file mode 100644 index 00000000..50e4c781 --- /dev/null +++ b/yazi-fm/src/lives/files.rs @@ -0,0 +1,43 @@ +use std::ops::{Deref, Range}; + +use mlua::{Lua, MetaMethod, UserDataMethods}; + +use super::File; + +pub(super) struct Files { + folder: *const yazi_core::folder::Folder, + window: Range, +} + +impl Deref for Files { + type Target = yazi_core::folder::Files; + + fn deref(&self) -> &Self::Target { &self.folder().files } +} + +impl Files { + #[inline] + pub(super) fn new(folder: &yazi_core::folder::Folder, window: Range) -> Self { + Self { folder, window } + } + + pub(super) fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|reg| { + reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.window.end - me.window.start)); + + reg.add_meta_method(MetaMethod::Index, |lua, me, mut key: usize| { + key += me.window.start; + if key > me.window.end || key == 0 { + Ok(None) + } else { + Some(lua.create_any_userdata(File::new(key - 1, me.folder()))).transpose() + } + }); + })?; + + Ok(()) + } + + #[inline] + fn folder(&self) -> &yazi_core::folder::Folder { unsafe { &*self.folder } } +} diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index 8fa7e1cf..92a2dd65 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -1,150 +1,29 @@ -use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, Value}; -use yazi_config::{LAYOUT, THEME}; -use yazi_plugin::{bindings::{Cast, File, Icon, Range}, elements::Style, url::Url}; -use yazi_shared::MIME_DIR; +use mlua::{AnyUserData, Lua, UserDataFields}; +use yazi_config::LAYOUT; +use yazi_plugin::{bindings::Cast, url::Url}; -use super::{CtxRef, FolderRef}; +use super::{File, Files}; -pub struct Folder<'a, 'b> { - scope: &'b mlua::Scope<'a, 'a>, +pub(super) struct Folder; - inner: &'a yazi_core::folder::Folder, -} - -impl<'a, 'b> Folder<'a, 'b> { +impl Folder { pub(super) fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.cwd.clone())); + reg.add_field_method_get("files", |lua, me| { + lua.create_any_userdata(Files::new(me, 0..me.files.len())) + }); + reg.add_field_method_get("window", |lua, me| { + lua.create_any_userdata(Files::new( + me, + me.offset..me.files.len().min(me.offset + LAYOUT.load().preview.height as usize), + )) + }); + reg.add_field_method_get("offset", |_, me| Ok(me.offset)); reg.add_field_method_get("cursor", |_, me| Ok(me.cursor)); - - reg.add_field_function_get("window", |_, me| me.named_user_value::("window")); - reg.add_field_function_get("files", |_, me| me.named_user_value::("files")); - reg.add_field_function_get("hovered", |_, me| me.named_user_value::("hovered")); - })?; - - lua.register_userdata_type::(|reg| { - reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len())); - - reg.add_meta_function(MetaMethod::Pairs, |lua, me: AnyUserData| { - let iter = lua.create_function(|lua, (me, i): (AnyUserData, usize)| { - let files = me.borrow::()?; - let i = i + 1; - Ok(if i > files.len() { - mlua::Variadic::new() - } else { - mlua::Variadic::from_iter([ - i.into_lua(lua)?, - File::cast(lua, files[i - 1].clone())?.into_lua(lua)?, - ]) - }) - })?; - Ok((iter, me, 0)) - }); - })?; - - File::register(lua, |reg| { - reg.add_function("size", |_, me: AnyUserData| { - let file = me.borrow::()?; - if !file.is_dir() { - return Ok(Some(file.len)); - } - - let folder = me.named_user_value::("folder")?; - Ok(folder.files.sizes.get(&file.url).copied()) - }); - reg.add_function("mime", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let file = me.borrow::()?; - Ok(cx.manager.mimetype.get(&file.url).cloned()) - }); - reg.add_function("prefix", |lua, me: AnyUserData| { - let folder = me.named_user_value::("folder")?; - if !folder.cwd.is_search() { - return Ok(None); - } - - let file = me.borrow::()?; - let mut p = file.url.strip_prefix(&folder.cwd).unwrap_or(&file.url).components(); - p.next_back(); - Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() - }); - reg.add_method("icon", |lua, me, ()| { - THEME - .icons - .iter() - .find(|&x| x.name.match_path(&me.url, me.is_dir())) - .map(|x| Icon::cast(lua, x)) - .transpose() - }); - reg.add_function("style", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let file = me.borrow::()?; - - let mime = if file.is_dir() { - Some(MIME_DIR) - } else { - cx.manager.mimetype.get(&file.url).map(|x| &**x) - }; - - Ok(THEME.filetypes.iter().find(|&x| x.matches(&file, mime)).map(|x| Style::from(x.style))) - }); - reg.add_function("is_hovered", |_, me: AnyUserData| { - let folder = me.named_user_value::("folder")?; - let file = me.borrow::()?; - Ok(matches!(folder.hovered(), Some(f) if f.url == file.url)) - }); - reg.add_function("is_yanked", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let file = me.borrow::()?; - Ok(if !cx.manager.yanked.1.contains(&file.url) { - 0u8 - } else if cx.manager.yanked.0 { - 2u8 - } else { - 1u8 - }) - }); - reg.add_function("is_selected", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let folder = me.named_user_value::("folder")?; - let file = me.borrow::()?; - - let selected = folder.files.is_selected(&file.url); - Ok(if !cx.manager.active().mode.is_visual() { - selected - } else { - let pos: isize = me.named_user_value("pos")?; - cx.manager.active().mode.pending((folder.offset as isize + pos) as usize, selected) - }) - }); - reg.add_function("found", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let Some(finder) = &cx.manager.active().finder else { - return Ok(None); - }; - - let file = me.borrow::()?; - if let Some(idx) = finder.matched_idx(&file.url) { - return Some( - lua.create_sequence_from([idx.into_lua(lua)?, finder.matched().len().into_lua(lua)?]), - ) - .transpose(); - } - Ok(None) - }); - reg.add_function("highlights", |lua, me: AnyUserData| { - let cx = lua.named_registry_value::("cx")?; - let Some(finder) = &cx.manager.active().finder else { - return Ok(None); - }; - - let file = me.borrow::()?; - let Some(h) = file.name().and_then(|n| finder.filter.highlighted(n)) else { - return Ok(None); - }; - - Ok(Some(h.into_iter().map(Range::from).collect::>())) + reg.add_field_method_get("hovered", |lua, me| { + me.hovered().map(|_| lua.create_any_userdata(File::new(me.cursor, me))).transpose() }); })?; @@ -152,45 +31,12 @@ impl<'a, 'b> Folder<'a, 'b> { } } -impl<'a, 'b> Folder<'a, 'b> { - pub(crate) fn new(scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::folder::Folder) -> Self { - Self { scope, inner } - } - - pub(crate) fn make(&self, window: Option<(usize, usize)>) -> mlua::Result> { - let window = - window.unwrap_or_else(|| (self.inner.offset, LAYOUT.load().preview.height as usize)); - - let ud = self.scope.create_any_userdata_ref(self.inner)?; - ud.set_named_user_value( - "window", - self - .inner - .files - .iter() - .skip(window.0) - .take(window.1) - .enumerate() - .filter_map(|(i, f)| self.file(i as isize, f).ok()) - .collect::>(), - )?; - ud.set_named_user_value("files", self.scope.create_any_userdata_ref(&self.inner.files)?)?; - ud.set_named_user_value( - "hovered", - self - .inner - .hovered() - .and_then(|h| self.file(self.inner.cursor as isize - window.0 as isize, h).ok()), - )?; - - Ok(ud) - } - - fn file(&self, pos: isize, inner: &'a yazi_shared::fs::File) -> mlua::Result> { - let ud = self.scope.create_any_userdata_ref(inner)?; - ud.set_named_user_value("pos", pos)?; - ud.set_named_user_value("folder", self.scope.create_any_userdata_ref(self.inner)?)?; - - Ok(ud) +impl Folder { + #[inline] + pub(crate) fn make<'a>( + scope: &mlua::Scope<'a, 'a>, + inner: &'a yazi_core::folder::Folder, + ) -> mlua::Result> { + scope.create_any_userdata_ref(inner) } } diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 4b64f572..b174bd82 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -12,6 +12,8 @@ pub(crate) struct Lives; impl Lives { pub(crate) fn register() -> mlua::Result<()> { super::Active::register(&LUA)?; + super::File::register(&LUA)?; + super::Files::register(&LUA)?; super::Folder::register(&LUA)?; super::Tabs::register(&LUA)?; super::Tasks::register(&LUA)?; diff --git a/yazi-fm/src/lives/mod.rs b/yazi-fm/src/lives/mod.rs index 2dcc8e18..4690ffb0 100644 --- a/yazi-fm/src/lives/mod.rs +++ b/yazi-fm/src/lives/mod.rs @@ -1,16 +1,19 @@ #![allow(clippy::module_inception)] mod active; +mod file; +mod files; mod folder; mod lives; mod tabs; mod tasks; -pub(super) use active::*; -pub(super) use folder::*; +use active::*; +use file::*; +use files::*; +use folder::*; pub(super) use lives::*; -pub(super) use tabs::*; -pub(super) use tasks::*; +use tabs::*; +use tasks::*; type CtxRef<'lua> = mlua::UserDataRef<'lua, crate::Ctx>; -type FolderRef<'lua> = mlua::UserDataRef<'lua, yazi_core::folder::Folder>; diff --git a/yazi-fm/src/lives/tabs.rs b/yazi-fm/src/lives/tabs.rs index 776412a4..3aa7f360 100644 --- a/yazi-fm/src/lives/tabs.rs +++ b/yazi-fm/src/lives/tabs.rs @@ -1,6 +1,10 @@ +// TODO: unsafe + use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods, Value}; -pub struct Tabs<'a, 'b> { +use super::Folder; + +pub(super) struct Tabs<'a, 'b> { scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::manager::Tabs, @@ -60,18 +64,12 @@ impl<'a, 'b> Tabs<'a, 'b> { fn tab(&self, inner: &'a yazi_core::tab::Tab) -> mlua::Result> { let ud = self.scope.create_any_userdata_ref(inner)?; - ud.set_named_user_value("parent", inner.parent.as_ref().and_then(|p| self.folder(p).ok()))?; - ud.set_named_user_value("current", self.folder(&inner.current)?)?; + ud.set_named_user_value( + "parent", + inner.parent.as_ref().and_then(|p| Folder::make(self.scope, p).ok()), + )?; - Ok(ud) - } - - pub(crate) fn folder( - &self, - inner: &'a yazi_core::folder::Folder, - ) -> mlua::Result> { - let ud = self.scope.create_any_userdata_ref(inner)?; - ud.set_named_user_value("files", self.scope.create_any_userdata_ref(&inner.files)?)?; + ud.set_named_user_value("current", Folder::make(self.scope, &inner.current)?)?; Ok(ud) } diff --git a/yazi-fm/src/lives/tasks.rs b/yazi-fm/src/lives/tasks.rs index 44ed5108..5f5befbd 100644 --- a/yazi-fm/src/lives/tasks.rs +++ b/yazi-fm/src/lives/tasks.rs @@ -1,6 +1,6 @@ use mlua::{AnyUserData, Lua, LuaSerdeExt, UserDataFields}; -pub struct Tasks<'a, 'b> { +pub(super) struct Tasks<'a, 'b> { scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::tasks::Tasks, diff --git a/yazi-plugin/src/bindings/file.rs b/yazi-plugin/src/bindings/file.rs index 79f285b6..9f714658 100644 --- a/yazi-plugin/src/bindings/file.rs +++ b/yazi-plugin/src/bindings/file.rs @@ -1,4 +1,4 @@ -use mlua::{AnyUserData, Lua, UserDataFields, UserDataRef, UserDataRegistry}; +use mlua::{AnyUserData, Lua, UserDataFields, UserDataRef}; use super::{Cast, Cha}; use crate::url::Url; @@ -8,10 +8,7 @@ pub type FileRef<'lua> = UserDataRef<'lua, yazi_shared::fs::File>; pub struct File; impl File { - pub fn register( - lua: &Lua, - f: impl FnOnce(&mut UserDataRegistry), - ) -> mlua::Result<()> { + pub fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone())); reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha)); @@ -23,8 +20,6 @@ impl File { reg.add_field_method_get("name", |lua, me| { me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose() }); - - f(reg); }) } } diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index ad966f5d..5f44a866 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -7,7 +7,7 @@ pub fn slim_lua() -> mlua::Result { // Base bindings::Cha::register(&lua)?; - bindings::File::register(&lua, |_| {})?; + bindings::File::register(&lua)?; crate::url::pour(&lua)?; crate::fs::install(&lua)?; diff --git a/yazi-plugin/src/plugin.rs b/yazi-plugin/src/plugin.rs index ec4cc732..89cecdc7 100644 --- a/yazi-plugin/src/plugin.rs +++ b/yazi-plugin/src/plugin.rs @@ -17,6 +17,7 @@ pub fn init() { lua.load(include_str!("../preset/state.lua")).exec()?; lua.load(include_str!("../preset/ya.lua")).exec()?; crate::bindings::Cha::register(lua)?; + crate::bindings::File::register(lua)?; crate::bindings::Icon::register(lua)?; crate::elements::pour(lua)?; crate::url::pour(lua)?;