From d954784643316a5e9cd5f688fe3050e3511fee1d 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, 22 Feb 2024 07:01:23 +0800 Subject: [PATCH] perf: cache loaded plugins (#710) --- yazi-fm/src/app/commands/plugin.rs | 30 +++++++++++-------------- yazi-fm/src/lives/lives.rs | 14 ++++++------ yazi-plugin/preset/setup.lua | 9 ++++++++ yazi-plugin/preset/state.lua | 25 --------------------- yazi-plugin/preset/ya.lua | 17 +++++++------- yazi-plugin/src/isolate/entry.rs | 2 +- yazi-plugin/src/loader.rs | 21 +++++++++++++++++ yazi-plugin/src/opt.rs | 1 - yazi-plugin/src/plugin.rs | 7 +----- yazi-plugin/src/utils/plugin.rs | 36 ++++++++++++++++-------------- 10 files changed, 79 insertions(+), 83 deletions(-) create mode 100644 yazi-plugin/preset/setup.lua delete mode 100644 yazi-plugin/preset/state.lua diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 8e688edb..89eeaee5 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -1,8 +1,8 @@ use std::fmt::Display; -use mlua::{ExternalError, Table, TableExt}; +use mlua::TableExt; use tracing::warn; -use yazi_plugin::{LOADED, LUA}; +use yazi_plugin::{OptData, LOADED, LUA}; use yazi_shared::{emit, event::Cmd, Layer}; use crate::{app::App, lives::Lives}; @@ -24,32 +24,28 @@ impl App { tokio::spawn(async move { if LOADED.ensure(&opt.name).await.is_ok() { - emit!(Call(Cmd::args("plugin_do", vec![opt.name]).with_data(opt.data), Layer::App)); + Self::_plugin_do(opt.name, opt.data); } }); } + #[inline] + pub(crate) fn _plugin_do(name: String, data: OptData) { + emit!(Call(Cmd::args("plugin_do", vec![name]).with_data(data), Layer::App)); + } + pub(crate) fn plugin_do(&mut self, opt: impl TryInto) { let opt = match opt.try_into() { Ok(opt) => opt as yazi_plugin::Opt, Err(e) => return warn!("{e}"), }; + let plugin = match LOADED.load(&opt.name) { + Ok(plugin) => plugin, + Err(e) => return warn!("{e}"), + }; + _ = Lives::scope(&self.cx, |_| { - if let Some(init) = opt.data.init { - init(&LUA)?; - } - LUA.globals().set("YAZI_PLUGIN_NAME", LUA.create_string(&opt.name)?)?; - - let mut plugin: Option = None; - if let Some(b) = LOADED.read().get(&opt.name) { - plugin = LUA.load(b).call(())?; - } - - let Some(plugin) = plugin else { - return Err("plugin not found".into_lua_err()); - }; - if let Some(cb) = opt.data.cb { cb(&LUA, plugin) } else { diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 3b45c761..171ade83 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -37,8 +37,8 @@ impl Lives { SCOPE.init(unsafe { mem::transmute(scope) }); LUA.set_named_registry_value("cx", scope.create_any_userdata_ref(cx)?)?; - let global = LUA.globals(); - global.set( + let globals = LUA.globals(); + globals.set( "cx", LUA.create_table_from([ ("active", super::Tab::make(cx.manager.active())?), @@ -51,11 +51,11 @@ impl Lives { let ret = f(scope)?; LAYOUT.store(Arc::new(yazi_config::Layout { - header: *global.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?, - parent: *global.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?, - current: *global.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?, - preview: *global.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?, - status: *global.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?, + header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?, + parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?, + current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?, + preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?, + status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?, })); SCOPE.drop(); diff --git a/yazi-plugin/preset/setup.lua b/yazi-plugin/preset/setup.lua new file mode 100644 index 00000000..66597d00 --- /dev/null +++ b/yazi-plugin/preset/setup.lua @@ -0,0 +1,9 @@ +package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path + +local _require = require +require = function(name) + YAZI_PLUGIN_NAME, YAZI_SYNC_CALLS = name, 0 + return _require(name) +end + +YAZI_SYNC_BLOCKS = {} diff --git a/yazi-plugin/preset/state.lua b/yazi-plugin/preset/state.lua deleted file mode 100644 index 8d0e4d59..00000000 --- a/yazi-plugin/preset/state.lua +++ /dev/null @@ -1,25 +0,0 @@ -local cache = {} -local sub_mt = { - __index = function(target, k) - local bucket = rawget(target, "__yazi_bucket") - return cache[bucket] and cache[bucket][k] - end, - __newindex = function(target, k, v) - local bucket = rawget(target, "__yazi_bucket") - cache[bucket] = cache[bucket] or {} - cache[bucket][k] = v - end, -} - -state = setmetatable({}, { - __index = function(_, k) - local bucket = YAZI_PLUGIN_NAME - return cache[bucket] and cache[bucket][k] - end, - __newindex = function(_, k, v) - local bucket = YAZI_PLUGIN_NAME - cache[bucket] = cache[bucket] or {} - cache[bucket][k] = v - end, - __call = function(_, name) return setmetatable({ __yazi_bucket = name or YAZI_PLUGIN_NAME }, sub_mt) end, -}) diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 97ffb545..0b1cd810 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -29,17 +29,16 @@ function ya.flat(t) end function ya.sync(f) - if ya.SYNC_ON then - return function(...) f(...) end + YAZI_SYNC_CALLS = YAZI_SYNC_CALLS + 1 + + local name, calls = YAZI_PLUGIN_NAME, YAZI_SYNC_CALLS + if not YAZI_SYNC_BLOCKS then + return function(...) return ya.plugin_retrieve(name, calls, ...) end end - YAZI_SYNC_BLOCKS = YAZI_SYNC_BLOCKS + 1 - if YAZI_SYNC_CALLS == YAZI_SYNC_BLOCKS then - YAZI_SYNC_ENTRY = f - end - - local blocks = YAZI_SYNC_BLOCKS - return function(...) return ya.plugin_retrieve(YAZI_PLUGIN_NAME, blocks, ...) end + YAZI_SYNC_BLOCKS[name] = YAZI_SYNC_BLOCKS[name] or {} + YAZI_SYNC_BLOCKS[name][calls] = f + return function(...) return f(package.loaded[name], ...) end end function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index 0406f153..7ac0a7f9 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -12,7 +12,7 @@ pub async fn entry(name: String, args: Vec) -> mlua::Result<()> { let globals = lua.globals(); globals.raw_set("YAZI_PLUGIN_NAME", lua.create_string(&name)?)?; - globals.raw_set("YAZI_SYNC_BLOCKS", 0)?; + globals.raw_set("YAZI_SYNC_CALLS", 0)?; let plugin: Table = if let Some(b) = LOADED.read().get(&name) { lua.load(b).call(())? diff --git a/yazi-plugin/src/loader.rs b/yazi-plugin/src/loader.rs index e3529563..123e55bd 100644 --- a/yazi-plugin/src/loader.rs +++ b/yazi-plugin/src/loader.rs @@ -1,11 +1,14 @@ use std::{borrow::Cow, collections::BTreeMap, ops::Deref}; use anyhow::{bail, Result}; +use mlua::{ExternalError, Table}; use parking_lot::RwLock; use tokio::fs; use yazi_config::BOOT; use yazi_shared::RoCell; +use crate::LUA; + pub static LOADED: RoCell = RoCell::new(); #[derive(Default)] @@ -42,6 +45,24 @@ impl Loader { self.loaded.write().insert(name.to_owned(), b.into_owned()); Ok(()) } + + pub fn load(&self, name: &str) -> mlua::Result
{ + let globals = LUA.globals(); + let loaded: Table = globals.raw_get::<_, Table>("package")?.raw_get("loaded")?; + if let Ok(t) = loaded.raw_get::<_, Table>(name) { + return Ok(t); + } + + globals.raw_set("YAZI_PLUGIN_NAME", LUA.create_string(name)?)?; + globals.raw_set("YAZI_SYNC_CALLS", 0)?; + let t: Table = match self.read().get(name) { + Some(b) => LUA.load(b).call(())?, + None => Err(format!("plugin `{name}` not found").into_lua_err())?, + }; + + loaded.raw_set(name, t.clone())?; + Ok(t) + } } impl Deref for Loader { diff --git a/yazi-plugin/src/opt.rs b/yazi-plugin/src/opt.rs index 9e924a67..e7854097 100644 --- a/yazi-plugin/src/opt.rs +++ b/yazi-plugin/src/opt.rs @@ -13,7 +13,6 @@ pub struct Opt { #[derive(Default)] pub struct OptData { pub args: Vec, - pub init: Option mlua::Result<()> + Send>>, pub cb: Option mlua::Result<()> + Send>>, } diff --git a/yazi-plugin/src/plugin.rs b/yazi-plugin/src/plugin.rs index 89cecdc7..0a73c2f0 100644 --- a/yazi-plugin/src/plugin.rs +++ b/yazi-plugin/src/plugin.rs @@ -14,7 +14,6 @@ pub fn init() { // Base lua.load(include_str!("../preset/inspect/inspect.lua")).exec()?; - 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)?; @@ -36,11 +35,7 @@ pub fn init() { } fn stage_2(lua: &Lua) { - let setup = br#" -ya.SYNC_ON = true -package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path -"#; - lua.load(setup as &[u8]).exec().unwrap(); + lua.load(include_str!("../preset/setup.lua")).exec().unwrap(); if let Ok(b) = std::fs::read(BOOT.config_dir.join("init.lua")) { lua.load(b).exec().unwrap(); diff --git a/yazi-plugin/src/utils/plugin.rs b/yazi-plugin/src/utils/plugin.rs index 247a0e85..13e90694 100644 --- a/yazi-plugin/src/utils/plugin.rs +++ b/yazi-plugin/src/utils/plugin.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, Function, Lua, Table, Value, Variadic}; +use mlua::{ExternalError, Function, IntoLua, Lua, Table, Value, Variadic}; use tokio::sync::oneshot; use yazi_shared::{emit, event::Cmd, Layer}; @@ -10,31 +10,33 @@ impl Utils { ya.set( "plugin_retrieve", lua.create_async_function( - |_, (name, blocks, args): (String, usize, Variadic)| async move { - let args = Variadic::from_iter(ValueSendable::try_from_variadic(args)?); + |_, (name, calls, args): (String, usize, Variadic)| async move { + let args = ValueSendable::try_from_variadic(args)?; let (tx, rx) = oneshot::channel::(); let data = OptData { - init: Some(Box::new(move |lua| { - let globals = lua.globals(); - globals.raw_set("YAZI_SYNC_BLOCKS", 0)?; - globals.raw_set("YAZI_SYNC_CALLS", blocks)?; - Ok(()) - })), - cb: Some(Box::new(move |lua, _| { - let globals = lua.globals(); + cb: Some({ + let name = name.clone(); + Box::new(move |lua, plugin| { + let blocks = lua.globals().raw_get::<_, Table>("YAZI_SYNC_BLOCKS")?; + let block = blocks.raw_get::<_, Table>(name)?.raw_get::<_, Function>(calls)?; - let entry = globals.raw_get::<_, Function>("YAZI_SYNC_ENTRY")?; - globals.raw_set("YAZI_SYNC_ENTRY", Value::Nil)?; + let mut self_args = Vec::with_capacity(args.len() + 1); + self_args.push(Value::Table(plugin)); + for arg in args { + self_args.push(arg.into_lua(lua)?); + } - let value: ValueSendable = entry.call::<_, Value>(args)?.try_into()?; - tx.send(value).map_err(|_| "send failed".into_lua_err()) - })), + let value: ValueSendable = + block.call::<_, Value>(Variadic::from_iter(self_args))?.try_into()?; + tx.send(value).map_err(|_| "send failed".into_lua_err()) + }) + }), ..Default::default() }; emit!(Call( - Cmd::args("plugin", vec![name.to_owned()]).with_bool("sync", true).with_data(data), + Cmd::args("plugin", vec![name.clone()]).with_bool("sync", true).with_data(data), Layer::App ));