From dc1718fcf0d9b018a24aa71ef6a8a87d8f02ca6c 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: Fri, 9 Feb 2024 16:46:43 +0800 Subject: [PATCH] feat: calling sync functions within the async plugin (#649) --- yazi-fm/src/app/commands/plugin.rs | 17 +++++--------- yazi-fm/src/lives/folder.rs | 14 ++++++++++- yazi-fm/src/lives/lives.rs | 10 ++++---- yazi-plugin/preset/ya.lua | 14 ++++------- yazi-plugin/src/cast.rs | 27 ++++++++++++++-------- yazi-plugin/src/isolate/entry.rs | 5 +++- yazi-plugin/src/isolate/isolate.rs | 1 + yazi-plugin/src/isolate/peek.rs | 5 ++-- yazi-plugin/src/isolate/seek.rs | 5 ++-- yazi-plugin/src/opt.rs | 7 +++--- yazi-plugin/src/utils/plugin.rs | 37 ++++++++++++++++++++++++++++-- yazi-plugin/src/utils/time.rs | 14 ++++++++++- 12 files changed, 106 insertions(+), 50 deletions(-) diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 34fdddf0..8e688edb 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use mlua::{ExternalError, ExternalResult, Table, TableExt}; +use mlua::{ExternalError, Table, TableExt}; use tracing::warn; use yazi_plugin::{LOADED, LUA}; use yazi_shared::{emit, event::Cmd, Layer}; @@ -35,7 +35,10 @@ impl App { Err(e) => return warn!("{e}"), }; - let result = Lives::scope(&self.cx, |_| { + _ = 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; @@ -48,18 +51,10 @@ impl App { }; if let Some(cb) = opt.data.cb { - cb(plugin) + cb(&LUA, plugin) } else { plugin.call_method("entry", opt.data.args) } }); - - let Some(tx) = opt.data.tx else { - return; - }; - - if let Ok(v) = result.and_then(|v| v.try_into().into_lua_err()) { - tx.send(v).ok(); - } } } diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index 011d8842..5e913261 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -1,6 +1,6 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, Lua, UserDataFields}; +use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods}; use yazi_config::LAYOUT; use yazi_plugin::{bindings::Cast, url::Url}; @@ -38,6 +38,7 @@ impl Folder { 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", |_, me| Files::make(me, 0..me.files.len())); + reg.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage)); reg.add_field_method_get("window", |_, me| Files::make(me, me.window.clone())); reg.add_field_method_get("offset", |_, me| Ok(me.offset)); @@ -47,6 +48,17 @@ impl Folder { }); })?; + lua.register_userdata_type::(|reg| { + reg.add_meta_method(MetaMethod::ToString, |lua, me, ()| { + use yazi_core::folder::FolderStage::{Failed, Loaded, Loading}; + lua.create_string(match me { + Loading => "loading", + Loaded => "loaded", + Failed => "failed", + }) + }); + })?; + Ok(()) } } diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 71677839..d078deb4 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -48,11 +48,11 @@ impl Lives { let ret = f(scope)?; LAYOUT.store(Arc::new(yazi_config::Layout { - header: *global.get::<_, Table>("Header")?.get::<_, RectRef>("area")?, - parent: *global.get::<_, Table>("Parent")?.get::<_, RectRef>("area")?, - current: *global.get::<_, Table>("Current")?.get::<_, RectRef>("area")?, - preview: *global.get::<_, Table>("Preview")?.get::<_, RectRef>("area")?, - status: *global.get::<_, Table>("Status")?.get::<_, RectRef>("area")?, + 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")?, })); SCOPE.drop(); diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index a7e3bebe..121a25f8 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -29,17 +29,13 @@ function ya.flat(t) end function ya.sync(f) - ya.SYNC_CALLS = ya.SYNC_CALLS + 1 - if ya.SYNC_BLOCK == ya.SYNC_CALLS then - ya.SYNC_ENTRY = f + YAZI_SYNC_BLOCKS = YAZI_SYNC_BLOCKS + 1 + if YAZI_SYNC_CALLS == YAZI_SYNC_BLOCKS then + YAZI_SYNC_ENTRY = f end - if ya.SYNC_ON then - return f - end - - local calls = ya.SYNC_CALLS - return function(...) return plugin_retrieve(YAZI_PLUGIN_NAME, calls, ...) end + local blocks = YAZI_SYNC_BLOCKS + return function(...) return ya.plugin_retrieve(YAZI_PLUGIN_NAME, blocks, ...) end end function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end diff --git a/yazi-plugin/src/cast.rs b/yazi-plugin/src/cast.rs index 26d79359..ab7d8da8 100644 --- a/yazi-plugin/src/cast.rs +++ b/yazi-plugin/src/cast.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; -use anyhow::bail; -use mlua::{AnyUserData, IntoLua, Lua, Value}; +use mlua::{AnyUserData, ExternalError, IntoLua, Lua, Value, Variadic}; use yazi_shared::OrderedFloat; use crate::elements::Renderable; @@ -33,13 +32,13 @@ pub enum ValueSendable { } impl<'a> TryFrom> for ValueSendable { - type Error = anyhow::Error; + type Error = mlua::Error; fn try_from(value: Value) -> Result { Ok(match value { Value::Nil => ValueSendable::Nil, Value::Boolean(b) => ValueSendable::Boolean(b), - Value::LightUserData(_) => bail!("light userdata is not supported"), + Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?, Value::Integer(n) => ValueSendable::Integer(n), Value::Number(n) => ValueSendable::Number(n), Value::String(s) => ValueSendable::String(s.as_bytes().to_vec()), @@ -51,10 +50,10 @@ impl<'a> TryFrom> for ValueSendable { } ValueSendable::Table(map) } - Value::Function(_) => bail!("function is not supported"), - Value::Thread(_) => bail!("thread is not supported"), - Value::UserData(_) => bail!("userdata is not supported"), - Value::Error(_) => bail!("error is not supported"), + Value::Function(_) => Err("function is not supported".into_lua_err())?, + Value::Thread(_) => Err("thread is not supported".into_lua_err())?, + Value::UserData(_) => Err("userdata is not supported".into_lua_err())?, + Value::Error(_) => Err("error is not supported".into_lua_err())?, }) } } @@ -79,6 +78,14 @@ impl<'lua> IntoLua<'lua> for ValueSendable { } impl ValueSendable { + pub fn try_from_variadic(values: Variadic) -> mlua::Result> { + let mut vec = Vec::with_capacity(values.len()); + for value in values { + vec.push(ValueSendable::try_from(value)?); + } + Ok(vec) + } + pub fn into_table_string(self) -> HashMap { let ValueSendable::Table(table) = self else { return Default::default(); @@ -107,7 +114,7 @@ pub enum ValueSendableKey { } impl TryInto for ValueSendable { - type Error = anyhow::Error; + type Error = mlua::Error; fn try_into(self) -> Result { Ok(match self { @@ -116,7 +123,7 @@ impl TryInto for ValueSendable { ValueSendable::Integer(n) => ValueSendableKey::Integer(n), ValueSendable::Number(n) => ValueSendableKey::Number(OrderedFloat::new(n)), ValueSendable::String(s) => ValueSendableKey::String(s), - ValueSendable::Table(_) => bail!("table is not supported"), + ValueSendable::Table(_) => Err("table is not supported".into_lua_err())?, }) } } diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index 73ddd006..0406f153 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -9,7 +9,10 @@ pub async fn entry(name: String, args: Vec) -> mlua::Result<()> { tokio::task::spawn_blocking(move || { let lua = slim_lua()?; - lua.globals().set("YAZI_PLUGIN_NAME", lua.create_string(&name)?)?; + let globals = lua.globals(); + + globals.raw_set("YAZI_PLUGIN_NAME", lua.create_string(&name)?)?; + globals.raw_set("YAZI_SYNC_BLOCKS", 0)?; let plugin: Table = if let Some(b) = LOADED.read().get(&name) { lua.load(b).call(())? diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 5f44a866..0bc6779e 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -14,6 +14,7 @@ pub fn slim_lua() -> mlua::Result { crate::process::install(&lua)?; crate::utils::install(&lua)?; crate::Config::new(&lua).install_preview()?; + lua.load(include_str!("../../preset/ya.lua")).exec()?; // Elements let ui = lua.create_table()?; diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 36303ff3..4fa1b516 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -57,15 +57,14 @@ pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> Cancellation pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) { let data = OptData { - args: vec![], - cb: Some(Box::new(move |plugin| { + cb: Some(Box::new(move |_, plugin| { plugin.set("file", File::cast(&LUA, file)?)?; plugin.set("skip", skip)?; plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?; plugin.set("window", Window::default())?; plugin.call_method("peek", ()) })), - tx: None, + ..Default::default() }; emit!(Call( Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data), diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs index 851022b8..65b2ef6d 100644 --- a/yazi-plugin/src/isolate/seek.rs +++ b/yazi-plugin/src/isolate/seek.rs @@ -6,13 +6,12 @@ use crate::{bindings::{Cast, File}, elements::Rect, OptData, LUA}; pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) { let data = OptData { - args: vec![], - cb: Some(Box::new(move |plugin| { + cb: Some(Box::new(move |_, plugin| { plugin.set("file", File::cast(&LUA, file)?)?; plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?; plugin.call_method("seek", units) })), - tx: None, + ..Default::default() }; emit!(Call( Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data), diff --git a/yazi-plugin/src/opt.rs b/yazi-plugin/src/opt.rs index 423513df..9e924a67 100644 --- a/yazi-plugin/src/opt.rs +++ b/yazi-plugin/src/opt.rs @@ -1,6 +1,5 @@ use anyhow::bail; -use mlua::{Table, Value}; -use tokio::sync::oneshot; +use mlua::{Lua, Table}; use yazi_shared::event::Cmd; use crate::ValueSendable; @@ -14,8 +13,8 @@ pub struct Opt { #[derive(Default)] pub struct OptData { pub args: Vec, - pub cb: Option mlua::Result + Send>>, - pub tx: Option>, + pub init: Option mlua::Result<()> + Send>>, + pub cb: Option mlua::Result<()> + Send>>, } impl TryFrom for Opt { diff --git a/yazi-plugin/src/utils/plugin.rs b/yazi-plugin/src/utils/plugin.rs index 88bf844d..247a0e85 100644 --- a/yazi-plugin/src/utils/plugin.rs +++ b/yazi-plugin/src/utils/plugin.rs @@ -1,13 +1,46 @@ -use mlua::{Lua, Table, Value, Variadic}; +use mlua::{ExternalError, Function, Lua, Table, Value, Variadic}; +use tokio::sync::oneshot; +use yazi_shared::{emit, event::Cmd, Layer}; use super::Utils; +use crate::{OptData, ValueSendable}; impl Utils { pub(super) fn plugin(lua: &Lua, ya: &Table) -> mlua::Result<()> { ya.set( "plugin_retrieve", lua.create_async_function( - |_, (_name, _calls, _args): (String, usize, Variadic)| async move { Ok(()) }, + |_, (name, blocks, args): (String, usize, Variadic)| async move { + let args = Variadic::from_iter(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(); + + let entry = globals.raw_get::<_, Function>("YAZI_SYNC_ENTRY")?; + globals.raw_set("YAZI_SYNC_ENTRY", Value::Nil)?; + + let value: ValueSendable = entry.call::<_, Value>(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), + Layer::App + )); + + rx.await + .map_err(|_| format!("Failed to execute sync block in `{name}` plugin").into_lua_err()) + }, )?, )?; diff --git a/yazi-plugin/src/utils/time.rs b/yazi-plugin/src/utils/time.rs index 7ea91cb9..fadbb18e 100644 --- a/yazi-plugin/src/utils/time.rs +++ b/yazi-plugin/src/utils/time.rs @@ -1,6 +1,6 @@ use std::time::{SystemTime, UNIX_EPOCH}; -use mlua::{Lua, Table}; +use mlua::{ExternalError, Lua, Table}; use super::Utils; @@ -13,6 +13,18 @@ impl Utils { })?, )?; + ya.set( + "sleep", + lua.create_async_function(|_, secs: f64| async move { + if secs < 0.0 { + return Err("negative sleep duration".into_lua_err()); + } + + tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await; + Ok(()) + })?, + )?; + Ok(()) } }