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(())
}
}