From c240602046e372959312380963d451904f512766 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: Sat, 30 Mar 2024 22:15:05 +0800 Subject: [PATCH] perf: port `require()` and `ya.sync()` to Rust to avoid plugin information initialization process (#853) --- cspell.json | 2 +- yazi-dds/src/body/bulk.rs | 4 +- yazi-dds/src/body/tabs.rs | 6 +- yazi-dds/src/body/yank.rs | 4 +- yazi-fm/src/app/commands/plugin.rs | 9 ++- yazi-plugin/preset/setup.lua | 10 --- yazi-plugin/preset/ya.lua | 13 ---- yazi-plugin/src/isolate/entry.rs | 2 +- yazi-plugin/src/isolate/isolate.rs | 13 ++-- yazi-plugin/src/isolate/peek.rs | 2 +- yazi-plugin/src/isolate/preload.rs | 2 +- yazi-plugin/src/lib.rs | 10 ++- yazi-plugin/src/{ => loader}/loader.rs | 40 ++++-------- yazi-plugin/src/loader/mod.rs | 15 +++++ yazi-plugin/src/loader/require.rs | 66 +++++++++++++++++++ yazi-plugin/src/lua.rs | 19 +++--- yazi-plugin/src/pubsub/mod.rs | 6 ++ yazi-plugin/src/pubsub/pubsub.rs | 22 +++---- yazi-plugin/src/runtime.rs | 52 +++++++++++++++ yazi-plugin/src/utils/mod.rs | 2 +- yazi-plugin/src/utils/plugin.rs | 53 ---------------- yazi-plugin/src/utils/sync.rs | 87 ++++++++++++++++++++++++++ yazi-plugin/src/utils/utils.rs | 23 ++++++- 23 files changed, 310 insertions(+), 152 deletions(-) rename yazi-plugin/src/{ => loader}/loader.rs (54%) create mode 100644 yazi-plugin/src/loader/mod.rs create mode 100644 yazi-plugin/src/loader/require.rs create mode 100644 yazi-plugin/src/runtime.rs delete mode 100644 yazi-plugin/src/utils/plugin.rs create mode 100644 yazi-plugin/src/utils/sync.rs diff --git a/cspell.json b/cspell.json index b3d88cb7..3469e3d8 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub"]} \ No newline at end of file +{"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers"],"language":"en","flagWords":[]} \ No newline at end of file diff --git a/yazi-dds/src/body/bulk.rs b/yazi-dds/src/body/bulk.rs index c15ccd3d..d762842b 100644 --- a/yazi-dds/src/body/bulk.rs +++ b/yazi-dds/src/body/bulk.rs @@ -48,11 +48,11 @@ pub struct BodyBulkIter { } impl UserData for BodyBulkIter { - fn add_fields<'a, F: mlua::UserDataFields<'a, Self>>(fields: &mut F) { + fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("tab", |_, me| Ok(me.tab)); } - fn add_methods<'a, M: mlua::UserDataMethods<'a, Self>>(methods: &mut M) { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.inner.len())); methods.add_meta_function(MetaMethod::Pairs, |lua, me: AnyUserData| { diff --git a/yazi-dds/src/body/tabs.rs b/yazi-dds/src/body/tabs.rs index e6d7a762..97b619aa 100644 --- a/yazi-dds/src/body/tabs.rs +++ b/yazi-dds/src/body/tabs.rs @@ -53,7 +53,7 @@ pub struct BodyTabsItem<'a> { } impl UserData for BodyTabsItem<'static> { - fn add_fields<'a, F: mlua::UserDataFields<'a, Self>>(fields: &mut F) { + fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("url", |lua, me| lua.create_any_userdata(me.url.clone())); } } @@ -69,11 +69,11 @@ impl From> for BodyTabsIter { } impl UserData for BodyTabsIter { - fn add_fields<'a, F: mlua::UserDataFields<'a, Self>>(fields: &mut F) { + fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); } - fn add_methods<'a, M: mlua::UserDataMethods<'a, Self>>(methods: &mut M) { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.items.len())); methods.add_meta_method(MetaMethod::Index, |_, me, idx: usize| { diff --git a/yazi-dds/src/body/yank.rs b/yazi-dds/src/body/yank.rs index bacb6434..5bcc012d 100644 --- a/yazi-dds/src/body/yank.rs +++ b/yazi-dds/src/body/yank.rs @@ -54,11 +54,11 @@ impl From> for BodyYankIter { } impl UserData for BodyYankIter { - fn add_fields<'a, F: mlua::UserDataFields<'a, Self>>(fields: &mut F) { + fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("is_cut", |_, me| Ok(me.cut)); } - fn add_methods<'a, M: mlua::UserDataMethods<'a, Self>>(methods: &mut M) { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.urls.len())); methods.add_meta_method(MetaMethod::Index, |lua, me, idx: usize| { diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 1d30440d..a71e08e4 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use mlua::TableExt; use tracing::warn; -use yazi_plugin::{OptData, LOADER, LUA}; +use yazi_plugin::{loader::LOADER, OptData, RtRef, LUA}; use yazi_shared::{emit, event::Cmd, Defer, Layer}; use crate::{app::App, lives::Lives}; @@ -40,9 +40,12 @@ impl App { Err(e) => return warn!("{e}"), }; - LOADER.set_running(Some(&opt.name)); - let _defer = Defer::new(|| LOADER.set_running(None)); + match LUA.named_registry_value::("rt") { + Ok(mut r) => r.swap(&opt.name), + Err(e) => return warn!("{e}"), + } + let _defer = Defer::new(|| LUA.named_registry_value::("rt").map(|mut r| r.reset())); let plugin = match LOADER.load(&opt.name) { Ok(plugin) => plugin, Err(e) => return warn!("{e}"), diff --git a/yazi-plugin/preset/setup.lua b/yazi-plugin/preset/setup.lua index 77b2a8a8..e67aebbe 100644 --- a/yazi-plugin/preset/setup.lua +++ b/yazi-plugin/preset/setup.lua @@ -1,11 +1 @@ package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path - -local _require = require -require = function(name) - YAZI_PLUGIN_NAME, YAZI_SYNC_CALLS = name, 0 - local mod = _require(name) - mod._name = name - return mod -end - -YAZI_SYNC_BLOCKS = {} diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 0b1cd810..8713c0a1 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -28,19 +28,6 @@ function ya.flat(t) return r end -function ya.sync(f) - 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[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 function ya.readable_size(size) diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index eb8d6e5b..7b776170 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -3,7 +3,7 @@ use tokio::runtime::Handle; use yazi_dds::ValueSendable; use super::slim_lua; -use crate::LOADER; +use crate::loader::LOADER; pub async fn entry(name: String, args: Vec) -> mlua::Result<()> { LOADER.ensure(&name).await.into_lua_err()?; diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index e0e0de17..933cff57 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -1,9 +1,10 @@ use mlua::Lua; -use crate::{bindings, elements}; +use crate::{bindings, elements, runtime::Runtime}; pub fn slim_lua(name: &str) -> mlua::Result { let lua = Lua::new(); + lua.set_named_registry_value("rt", Runtime::new(name))?; // Base bindings::Cha::register(&lua)?; @@ -12,7 +13,7 @@ pub fn slim_lua(name: &str) -> mlua::Result { crate::fs::install(&lua)?; crate::process::install(&lua)?; - crate::utils::install(&lua)?; + crate::utils::install_isolate(&lua)?; crate::Config::new(&lua).install_preview()?; lua.load(include_str!("../../preset/ya.lua")).exec()?; @@ -24,12 +25,6 @@ pub fn slim_lua(name: &str) -> mlua::Result { elements::Rect::install(&lua, &ui)?; elements::Span::install(&lua, &ui)?; - { - let globals = lua.globals(); - globals.raw_set("ui", ui)?; - globals.raw_set("YAZI_PLUGIN_NAME", lua.create_string(name)?)?; - globals.raw_set("YAZI_SYNC_CALLS", 0)?; - } - + lua.globals().raw_set("ui", ui)?; Ok(lua) } diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 76a7b13f..aba27316 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -6,7 +6,7 @@ use yazi_config::LAYOUT; use yazi_shared::{emit, event::Cmd, Layer}; use super::slim_lua; -use crate::{bindings::{Cast, File, Window}, elements::Rect, OptData, LOADER, LUA}; +use crate::{bindings::{Cast, File, Window}, elements::Rect, loader::LOADER, OptData, LUA}; pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> CancellationToken { let ct = CancellationToken::new(); diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index e94fbdfc..19668597 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -3,7 +3,7 @@ use tokio::runtime::Handle; use yazi_config::LAYOUT; use super::slim_lua; -use crate::{bindings::{Cast, File}, elements::Rect, LOADER}; +use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; pub async fn preload( name: &str, diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index a1069c2c..9cac546d 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -7,18 +7,22 @@ pub mod elements; pub mod external; pub mod fs; pub mod isolate; -mod loader; +pub mod loader; mod lua; mod opt; pub mod process; pub mod pubsub; +mod runtime; pub mod url; pub mod utils; pub use cast::*; pub use config::*; -pub use loader::*; pub use lua::*; pub use opt::*; +pub use runtime::*; -pub fn init() { crate::init_lua(); } +pub fn init() { + crate::init_lua(); + crate::loader::init(); +} diff --git a/yazi-plugin/src/loader.rs b/yazi-plugin/src/loader/loader.rs similarity index 54% rename from yazi-plugin/src/loader.rs rename to yazi-plugin/src/loader/loader.rs index 4f0d77d0..a18e394b 100644 --- a/yazi-plugin/src/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap, ops::Deref, sync::Arc}; +use std::{borrow::Cow, collections::HashMap, ops::Deref}; use anyhow::{bail, Result}; use mlua::{ExternalError, Table}; @@ -11,20 +11,12 @@ use crate::LUA; pub static LOADER: RoCell = RoCell::new(); -pub(super) static RUNNING: RoCell> = RoCell::new(); - #[derive(Default)] pub struct Loader { cache: RwLock>>, } impl Loader { - #[inline] - pub(super) fn init() { - LOADER.with(Default::default); - RUNNING.with(Default::default); - } - pub async fn ensure(&self, name: &str) -> Result<()> { if self.cache.read().contains_key(name) { return Ok(()); @@ -33,16 +25,16 @@ impl Loader { let path = BOOT.plugin_dir.join(format!("{name}.yazi/init.lua")); let b = fs::read(path).await.map(|v| v.into()).or_else(|_| { Ok(Cow::from(match name { - "archive" => include_bytes!("../preset/plugins/archive.lua") as &[u8], - "code" => include_bytes!("../preset/plugins/code.lua"), - "file" => include_bytes!("../preset/plugins/file.lua"), - "folder" => include_bytes!("../preset/plugins/folder.lua"), - "image" => include_bytes!("../preset/plugins/image.lua"), - "json" => include_bytes!("../preset/plugins/json.lua"), - "mime" => include_bytes!("../preset/plugins/mime.lua"), - "noop" => include_bytes!("../preset/plugins/noop.lua"), - "pdf" => include_bytes!("../preset/plugins/pdf.lua"), - "video" => include_bytes!("../preset/plugins/video.lua"), + "archive" => include_bytes!("../../preset/plugins/archive.lua") as &[_], + "code" => include_bytes!("../../preset/plugins/code.lua"), + "file" => include_bytes!("../../preset/plugins/file.lua"), + "folder" => include_bytes!("../../preset/plugins/folder.lua"), + "image" => include_bytes!("../../preset/plugins/image.lua"), + "json" => include_bytes!("../../preset/plugins/json.lua"), + "mime" => include_bytes!("../../preset/plugins/mime.lua"), + "noop" => include_bytes!("../../preset/plugins/noop.lua"), + "pdf" => include_bytes!("../../preset/plugins/pdf.lua"), + "video" => include_bytes!("../../preset/plugins/video.lua"), _ => bail!("plugin not found: {name}"), })) })?; @@ -58,8 +50,6 @@ impl Loader { 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())?, @@ -69,14 +59,6 @@ impl Loader { loaded.raw_set(name, t.clone())?; Ok(t) } - - pub fn set_running(&self, name: Option<&str>) { - if let Some(s) = name { - RUNNING.store(Some(Arc::new(s.to_owned()))); - } else { - RUNNING.store(None); - } - } } impl Deref for Loader { diff --git a/yazi-plugin/src/loader/mod.rs b/yazi-plugin/src/loader/mod.rs new file mode 100644 index 00000000..3dcbbf8c --- /dev/null +++ b/yazi-plugin/src/loader/mod.rs @@ -0,0 +1,15 @@ +#![allow(clippy::module_inception)] + +mod loader; +mod require; + +pub use loader::*; +use require::*; + +pub(super) fn init() { LOADER.with(Default::default); } + +pub(super) fn install(lua: &'static mlua::Lua) -> mlua::Result<()> { + Require::install(lua)?; + + Ok(()) +} diff --git a/yazi-plugin/src/loader/require.rs b/yazi-plugin/src/loader/require.rs new file mode 100644 index 00000000..2144bd0b --- /dev/null +++ b/yazi-plugin/src/loader/require.rs @@ -0,0 +1,66 @@ +use mlua::{ExternalResult, Function, IntoLua, Lua, MetaMethod, Table, TableExt, UserData, Value, Variadic}; + +use crate::RtRef; + +pub(super) struct Require; + +impl Require { + pub(super) fn install(lua: &'static Lua) -> mlua::Result<()> { + let globals = lua.globals(); + + let require = globals.raw_get::<_, Function>("require")?; + globals.raw_set( + "require", + lua.create_function(move |lua, name: mlua::String| { + lua.named_registry_value::("rt")?.swap(name.to_str()?); + let module: Table = require.call(&name)?; + lua.named_registry_value::("rt")?.reset(); + + module.raw_set("_name", &name)?; + Self::create_mt(lua, name, module) + })?, + )?; + + Ok(()) + } + + fn create_mt( + lua: &'static Lua, + name: mlua::String<'static>, + module: Table<'static>, + ) -> mlua::Result> { + let ts = + lua.create_table_from([("name", name.into_lua(lua)?), ("module", module.into_lua(lua)?)])?; + + let mt = lua.create_table()?; + mt.raw_set( + "__index", + lua.create_function(|_, (ts, key): (Table, mlua::String)| { + if key.to_str()? == "setup" { + Ok(RequireSetup { name: ts.raw_get("name")?, module: ts.raw_get("module")? }) + } else { + Err("Only `require():setup()` is supported").into_lua_err() + } + })?, + )?; + + ts.set_metatable(Some(mt)); + Ok(ts) + } +} + +pub(super) struct RequireSetup { + name: mlua::String<'static>, + module: Table<'static>, +} + +impl UserData for RequireSetup { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(MetaMethod::Call, |lua, me, args: Variadic| { + lua.named_registry_value::("rt")?.swap(me.name.to_str()?); + let result = me.module.call_method::<_, Variadic>("setup", args); + lua.named_registry_value::("rt")?.reset(); + result + }); + } +} diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index 937c6eb7..0715d198 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -3,28 +3,31 @@ use mlua::Lua; use yazi_boot::BOOT; use yazi_shared::RoCell; +use crate::runtime::Runtime; + pub static LUA: RoCell = RoCell::new(); pub(super) fn init_lua() { LUA.init(Lua::new()); - stage_1(&LUA).expect("failed to initialize Lua"); - stage_2(&LUA); + stage_1(&LUA).expect("Lua setup failed"); + stage_2(&LUA).expect("Lua runtime failed"); } fn stage_1(lua: &'static Lua) -> Result<()> { - crate::Loader::init(); crate::Config::new(lua).install_boot()?.install_manager()?.install_theme()?; crate::utils::init(); crate::utils::install(lua)?; // Base + lua.set_named_registry_value("rt", Runtime::default())?; 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::pubsub::Pubsub::install(lua)?; + crate::loader::install(lua)?; + crate::pubsub::install(lua)?; crate::url::pour(lua)?; // Components @@ -41,10 +44,12 @@ fn stage_1(lua: &'static Lua) -> Result<()> { Ok(()) } -fn stage_2(lua: &'static Lua) { - lua.load(include_str!("../preset/setup.lua")).exec().unwrap(); +fn stage_2(lua: &'static Lua) -> mlua::Result<()> { + lua.load(include_str!("../preset/setup.lua")).exec()?; if let Ok(b) = std::fs::read(BOOT.config_dir.join("init.lua")) { - lua.load(b).exec().unwrap(); + lua.load(b).exec()?; } + + Ok(()) } diff --git a/yazi-plugin/src/pubsub/mod.rs b/yazi-plugin/src/pubsub/mod.rs index e84be56d..6baa14bc 100644 --- a/yazi-plugin/src/pubsub/mod.rs +++ b/yazi-plugin/src/pubsub/mod.rs @@ -3,3 +3,9 @@ mod pubsub; pub use pubsub::*; + +pub(super) fn install(lua: &'static mlua::Lua) -> mlua::Result<()> { + Pubsub::install(lua)?; + + Ok(()) +} diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 7c960eef..a0f4fbf7 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -1,12 +1,12 @@ use mlua::{ExternalResult, Function, Lua, Value}; use yazi_dds::body::Body; -use crate::RUNNING; +use crate::runtime::RtRef; pub struct Pubsub; impl Pubsub { - pub fn install(lua: &'static Lua) -> mlua::Result<()> { + pub(super) fn install(lua: &'static Lua) -> mlua::Result<()> { let ps = lua.create_table()?; ps.raw_set( @@ -42,11 +42,11 @@ impl Pubsub { ps.raw_set( "sub", - lua.create_function(|_, (kind, f): (mlua::String, Function)| { - let Some(name) = &*RUNNING.load() else { + lua.create_function(|lua, (kind, f): (mlua::String, Function)| { + let Some(ref cur) = lua.named_registry_value::("rt")?.current else { return Err("`sub()` must be called in a sync plugin").into_lua_err(); }; - if !yazi_dds::Pubsub::sub(name, kind.to_str()?, f) { + if !yazi_dds::Pubsub::sub(cur, kind.to_str()?, f) { return Err("`sub()` called twice").into_lua_err(); } Ok(()) @@ -56,10 +56,10 @@ impl Pubsub { ps.raw_set( "sub_remote", lua.create_function(|_, (kind, f): (mlua::String, Function)| { - let Some(name) = &*RUNNING.load() else { + let Some(ref cur) = lua.named_registry_value::("rt")?.current else { return Err("`sub_remote()` must be called in a sync plugin").into_lua_err(); }; - if !yazi_dds::Pubsub::sub_remote(name, kind.to_str()?, f) { + if !yazi_dds::Pubsub::sub_remote(cur, kind.to_str()?, f) { return Err("`sub_remote()` called twice").into_lua_err(); } Ok(()) @@ -69,8 +69,8 @@ impl Pubsub { ps.raw_set( "unsub", lua.create_function(|_, kind: mlua::String| { - if let Some(name) = &*RUNNING.load() { - Ok(yazi_dds::Pubsub::unsub(name, kind.to_str()?)) + if let Some(ref cur) = lua.named_registry_value::("rt")?.current { + Ok(yazi_dds::Pubsub::unsub(cur, kind.to_str()?)) } else { Err("`unsub()` must be called in a sync plugin").into_lua_err() } @@ -80,8 +80,8 @@ impl Pubsub { ps.raw_set( "unsub_remote", lua.create_function(|_, kind: mlua::String| { - if let Some(name) = &*RUNNING.load() { - Ok(yazi_dds::Pubsub::unsub_remote(name, kind.to_str()?)) + if let Some(ref cur) = lua.named_registry_value::("rt")?.current { + Ok(yazi_dds::Pubsub::unsub_remote(cur, kind.to_str()?)) } else { Err("`unsub_remote()` must be called in a sync plugin").into_lua_err() } diff --git a/yazi-plugin/src/runtime.rs b/yazi-plugin/src/runtime.rs new file mode 100644 index 00000000..75741610 --- /dev/null +++ b/yazi-plugin/src/runtime.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +use mlua::{Function, UserData}; + +#[derive(Default)] +pub struct Runtime { + pub current: Option, + pub calls: usize, + pub blocks: HashMap>>, +} + +pub type RtRef<'lua> = mlua::UserDataRefMut<'lua, Runtime>; + +impl Runtime { + pub fn new(current: &str) -> Self { + Self { current: Some(current.to_owned()), ..Default::default() } + } + + pub fn swap(&mut self, name: &str) { + self.current = Some(name.to_owned()); + self.calls = 0; + } + + pub fn reset(&mut self) { + self.current = None; + self.calls = 0; + } + + pub fn next_block(&mut self) -> usize { + self.calls += 1; + self.calls - 1 + } + + pub fn get_block(&self, name: &str, calls: usize) -> Option> { + self.blocks.get(name).and_then(|v| v.get(calls)).cloned() + } + + pub fn push_block(&mut self, f: Function<'static>) -> bool { + let Some(ref cur) = self.current else { + return false; + }; + + if let Some(vec) = self.blocks.get_mut(cur) { + vec.push(f); + } else { + self.blocks.insert(cur.clone(), vec![f]); + } + true + } +} + +impl UserData for Runtime {} diff --git a/yazi-plugin/src/utils/mod.rs b/yazi-plugin/src/utils/mod.rs index e75695eb..87107001 100644 --- a/yazi-plugin/src/utils/mod.rs +++ b/yazi-plugin/src/utils/mod.rs @@ -6,8 +6,8 @@ mod call; mod image; mod layer; mod log; -mod plugin; mod preview; +mod sync; mod target; mod text; mod time; diff --git a/yazi-plugin/src/utils/plugin.rs b/yazi-plugin/src/utils/plugin.rs deleted file mode 100644 index 276d8018..00000000 --- a/yazi-plugin/src/utils/plugin.rs +++ /dev/null @@ -1,53 +0,0 @@ -use mlua::{ExternalError, Function, IntoLua, Lua, Table, Value, Variadic}; -use tokio::sync::oneshot; -use yazi_dds::ValueSendable; -use yazi_shared::{emit, event::Cmd, Layer}; - -use super::Utils; -use crate::OptData; - -impl Utils { - pub(super) fn plugin(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "plugin_retrieve", - lua.create_async_function( - |_, (name, calls, args): (String, usize, Variadic)| async move { - let args = ValueSendable::try_from_variadic(args)?; - let (tx, rx) = oneshot::channel::>(); - - let data = OptData { - 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 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 values = - ValueSendable::try_from_variadic(block.call(Variadic::from_iter(self_args))?)?; - tx.send(values).map_err(|_| "send failed".into_lua_err()) - }) - }), - ..Default::default() - }; - - emit!(Call( - Cmd::args("plugin", vec![name.clone()]).with_bool("sync", true).with_data(data), - Layer::App - )); - - Ok(Variadic::from_iter(rx.await.map_err(|_| { - format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err() - })?)) - }, - )?, - )?; - - Ok(()) - } -} diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs new file mode 100644 index 00000000..55fd6b90 --- /dev/null +++ b/yazi-plugin/src/utils/sync.rs @@ -0,0 +1,87 @@ +use mlua::{ExternalError, ExternalResult, Function, IntoLua, Lua, Table, Value, Variadic}; +use tokio::sync::oneshot; +use yazi_dds::ValueSendable; +use yazi_shared::{emit, event::Cmd, Layer}; + +use super::Utils; +use crate::{loader::LOADER, runtime::RtRef, OptData}; + +impl Utils { + pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> { + ya.raw_set( + "sync", + lua.create_function(|lua, f: Function<'static>| { + let mut rt = lua.named_registry_value::("rt")?; + if !rt.push_block(f.clone()) { + return Err("`ya.sync()` must be called in a plugin").into_lua_err(); + } + + let cur = rt.current.clone().unwrap(); + lua.create_function(move |lua, mut args: Variadic| { + args.insert(0, LOADER.load(&cur)?.into_lua(lua)?); + f.call::<_, Variadic>(args) + }) + })?, + )?; + + Ok(()) + } + + pub(super) fn sync_isolate(lua: &Lua, ya: &Table) -> mlua::Result<()> { + ya.raw_set( + "sync", + lua.create_function(|lua, ()| { + let block = lua.named_registry_value::("rt")?.next_block(); + lua.create_async_function(move |lua, args: Variadic| async move { + let Some(cur) = lua.named_registry_value::("rt")?.current.clone() else { + return Err("`ya.sync()` must be called in a plugin").into_lua_err(); + }; + + Self::retrieve(cur, block, args).await + }) + })?, + )?; + + Ok(()) + } + + async fn retrieve( + name: String, + calls: usize, + args: Variadic>, + ) -> mlua::Result> { + let args = ValueSendable::try_from_variadic(args)?; + let (tx, rx) = oneshot::channel::>(); + + let data = OptData { + cb: Some({ + let name = name.clone(); + Box::new(move |lua, plugin| { + let Some(block) = lua.named_registry_value::("rt")?.get_block(&name, calls) else { + return Err("sync block not found".into_lua_err()); + }; + + 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 values = + ValueSendable::try_from_variadic(block.call(Variadic::from_iter(self_args))?)?; + tx.send(values).map_err(|_| "send failed".into_lua_err()) + }) + }), + ..Default::default() + }; + + emit!(Call( + Cmd::args("plugin", vec![name.clone()]).with_bool("sync", true).with_data(data), + Layer::App + )); + + Ok(Variadic::from_iter(rx.await.map_err(|_| { + format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err() + })?)) + } +} diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index f9285215..6183f0b8 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -6,7 +6,7 @@ pub(super) static HOSTNAME_CACHE: std::sync::OnceLock> = std::syn pub(super) struct Utils; -pub fn install(lua: &mlua::Lua) -> mlua::Result<()> { +pub fn install(lua: &'static mlua::Lua) -> mlua::Result<()> { let ya = lua.create_table()?; Utils::app(lua, &ya)?; @@ -15,8 +15,27 @@ pub fn install(lua: &mlua::Lua) -> mlua::Result<()> { Utils::image(lua, &ya)?; Utils::layer(lua, &ya)?; Utils::log(lua, &ya)?; - Utils::plugin(lua, &ya)?; Utils::preview(lua, &ya)?; + Utils::sync(lua, &ya)?; + Utils::target(lua, &ya)?; + Utils::text(lua, &ya)?; + Utils::time(lua, &ya)?; + Utils::user(lua, &ya)?; + + lua.globals().raw_set("ya", ya) +} + +pub fn install_isolate(lua: &mlua::Lua) -> mlua::Result<()> { + let ya = lua.create_table()?; + + Utils::app(lua, &ya)?; + Utils::cache(lua, &ya)?; + Utils::call(lua, &ya)?; + Utils::image(lua, &ya)?; + Utils::layer(lua, &ya)?; + Utils::log(lua, &ya)?; + Utils::preview(lua, &ya)?; + Utils::sync_isolate(lua, &ya)?; Utils::target(lua, &ya)?; Utils::text(lua, &ya)?; Utils::time(lua, &ya)?;