mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: calling sync functions within the async plugin (#649)
This commit is contained in:
parent
ed46456f8c
commit
dc1718fcf0
12 changed files with 106 additions and 50 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use mlua::{ExternalError, ExternalResult, Table, TableExt};
|
use mlua::{ExternalError, Table, TableExt};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use yazi_plugin::{LOADED, LUA};
|
use yazi_plugin::{LOADED, LUA};
|
||||||
use yazi_shared::{emit, event::Cmd, Layer};
|
use yazi_shared::{emit, event::Cmd, Layer};
|
||||||
|
|
@ -35,7 +35,10 @@ impl App {
|
||||||
Err(e) => return warn!("{e}"),
|
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)?)?;
|
LUA.globals().set("YAZI_PLUGIN_NAME", LUA.create_string(&opt.name)?)?;
|
||||||
|
|
||||||
let mut plugin: Option<Table> = None;
|
let mut plugin: Option<Table> = None;
|
||||||
|
|
@ -48,18 +51,10 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cb) = opt.data.cb {
|
if let Some(cb) = opt.data.cb {
|
||||||
cb(plugin)
|
cb(&LUA, plugin)
|
||||||
} else {
|
} else {
|
||||||
plugin.call_method("entry", opt.data.args)
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::{Deref, Range};
|
||||||
|
|
||||||
use mlua::{AnyUserData, Lua, UserDataFields};
|
use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods};
|
||||||
use yazi_config::LAYOUT;
|
use yazi_config::LAYOUT;
|
||||||
use yazi_plugin::{bindings::Cast, url::Url};
|
use yazi_plugin::{bindings::Cast, url::Url};
|
||||||
|
|
||||||
|
|
@ -38,6 +38,7 @@ impl Folder {
|
||||||
lua.register_userdata_type::<Self>(|reg| {
|
lua.register_userdata_type::<Self>(|reg| {
|
||||||
reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.cwd.clone()));
|
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("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("window", |_, me| Files::make(me, me.window.clone()));
|
||||||
|
|
||||||
reg.add_field_method_get("offset", |_, me| Ok(me.offset));
|
reg.add_field_method_get("offset", |_, me| Ok(me.offset));
|
||||||
|
|
@ -47,6 +48,17 @@ impl Folder {
|
||||||
});
|
});
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
lua.register_userdata_type::<yazi_core::folder::FolderStage>(|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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,11 @@ impl Lives {
|
||||||
let ret = f(scope)?;
|
let ret = f(scope)?;
|
||||||
|
|
||||||
LAYOUT.store(Arc::new(yazi_config::Layout {
|
LAYOUT.store(Arc::new(yazi_config::Layout {
|
||||||
header: *global.get::<_, Table>("Header")?.get::<_, RectRef>("area")?,
|
header: *global.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?,
|
||||||
parent: *global.get::<_, Table>("Parent")?.get::<_, RectRef>("area")?,
|
parent: *global.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?,
|
||||||
current: *global.get::<_, Table>("Current")?.get::<_, RectRef>("area")?,
|
current: *global.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?,
|
||||||
preview: *global.get::<_, Table>("Preview")?.get::<_, RectRef>("area")?,
|
preview: *global.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?,
|
||||||
status: *global.get::<_, Table>("Status")?.get::<_, RectRef>("area")?,
|
status: *global.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
SCOPE.drop();
|
SCOPE.drop();
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,13 @@ function ya.flat(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ya.sync(f)
|
function ya.sync(f)
|
||||||
ya.SYNC_CALLS = ya.SYNC_CALLS + 1
|
YAZI_SYNC_BLOCKS = YAZI_SYNC_BLOCKS + 1
|
||||||
if ya.SYNC_BLOCK == ya.SYNC_CALLS then
|
if YAZI_SYNC_CALLS == YAZI_SYNC_BLOCKS then
|
||||||
ya.SYNC_ENTRY = f
|
YAZI_SYNC_ENTRY = f
|
||||||
end
|
end
|
||||||
|
|
||||||
if ya.SYNC_ON then
|
local blocks = YAZI_SYNC_BLOCKS
|
||||||
return f
|
return function(...) return ya.plugin_retrieve(YAZI_PLUGIN_NAME, blocks, ...) end
|
||||||
end
|
|
||||||
|
|
||||||
local calls = ya.SYNC_CALLS
|
|
||||||
return function(...) return plugin_retrieve(YAZI_PLUGIN_NAME, calls, ...) end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end
|
function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::bail;
|
use mlua::{AnyUserData, ExternalError, IntoLua, Lua, Value, Variadic};
|
||||||
use mlua::{AnyUserData, IntoLua, Lua, Value};
|
|
||||||
use yazi_shared::OrderedFloat;
|
use yazi_shared::OrderedFloat;
|
||||||
|
|
||||||
use crate::elements::Renderable;
|
use crate::elements::Renderable;
|
||||||
|
|
@ -33,13 +32,13 @@ pub enum ValueSendable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<Value<'a>> for ValueSendable {
|
impl<'a> TryFrom<Value<'a>> for ValueSendable {
|
||||||
type Error = anyhow::Error;
|
type Error = mlua::Error;
|
||||||
|
|
||||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Value::Nil => ValueSendable::Nil,
|
Value::Nil => ValueSendable::Nil,
|
||||||
Value::Boolean(b) => ValueSendable::Boolean(b),
|
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::Integer(n) => ValueSendable::Integer(n),
|
||||||
Value::Number(n) => ValueSendable::Number(n),
|
Value::Number(n) => ValueSendable::Number(n),
|
||||||
Value::String(s) => ValueSendable::String(s.as_bytes().to_vec()),
|
Value::String(s) => ValueSendable::String(s.as_bytes().to_vec()),
|
||||||
|
|
@ -51,10 +50,10 @@ impl<'a> TryFrom<Value<'a>> for ValueSendable {
|
||||||
}
|
}
|
||||||
ValueSendable::Table(map)
|
ValueSendable::Table(map)
|
||||||
}
|
}
|
||||||
Value::Function(_) => bail!("function is not supported"),
|
Value::Function(_) => Err("function is not supported".into_lua_err())?,
|
||||||
Value::Thread(_) => bail!("thread is not supported"),
|
Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
|
||||||
Value::UserData(_) => bail!("userdata is not supported"),
|
Value::UserData(_) => Err("userdata is not supported".into_lua_err())?,
|
||||||
Value::Error(_) => bail!("error is not supported"),
|
Value::Error(_) => Err("error is not supported".into_lua_err())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,6 +78,14 @@ impl<'lua> IntoLua<'lua> for ValueSendable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueSendable {
|
impl ValueSendable {
|
||||||
|
pub fn try_from_variadic(values: Variadic<Value>) -> mlua::Result<Vec<ValueSendable>> {
|
||||||
|
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<String, String> {
|
pub fn into_table_string(self) -> HashMap<String, String> {
|
||||||
let ValueSendable::Table(table) = self else {
|
let ValueSendable::Table(table) = self else {
|
||||||
return Default::default();
|
return Default::default();
|
||||||
|
|
@ -107,7 +114,7 @@ pub enum ValueSendableKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<ValueSendableKey> for ValueSendable {
|
impl TryInto<ValueSendableKey> for ValueSendable {
|
||||||
type Error = anyhow::Error;
|
type Error = mlua::Error;
|
||||||
|
|
||||||
fn try_into(self) -> Result<ValueSendableKey, Self::Error> {
|
fn try_into(self) -> Result<ValueSendableKey, Self::Error> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
|
|
@ -116,7 +123,7 @@ impl TryInto<ValueSendableKey> for ValueSendable {
|
||||||
ValueSendable::Integer(n) => ValueSendableKey::Integer(n),
|
ValueSendable::Integer(n) => ValueSendableKey::Integer(n),
|
||||||
ValueSendable::Number(n) => ValueSendableKey::Number(OrderedFloat::new(n)),
|
ValueSendable::Number(n) => ValueSendableKey::Number(OrderedFloat::new(n)),
|
||||||
ValueSendable::String(s) => ValueSendableKey::String(s),
|
ValueSendable::String(s) => ValueSendableKey::String(s),
|
||||||
ValueSendable::Table(_) => bail!("table is not supported"),
|
ValueSendable::Table(_) => Err("table is not supported".into_lua_err())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> {
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua()?;
|
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) {
|
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
||||||
lua.load(b).call(())?
|
lua.load(b).call(())?
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ pub fn slim_lua() -> mlua::Result<Lua> {
|
||||||
crate::process::install(&lua)?;
|
crate::process::install(&lua)?;
|
||||||
crate::utils::install(&lua)?;
|
crate::utils::install(&lua)?;
|
||||||
crate::Config::new(&lua).install_preview()?;
|
crate::Config::new(&lua).install_preview()?;
|
||||||
|
lua.load(include_str!("../../preset/ya.lua")).exec()?;
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
let ui = lua.create_table()?;
|
let ui = lua.create_table()?;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) {
|
||||||
let data = OptData {
|
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("file", File::cast(&LUA, file)?)?;
|
||||||
plugin.set("skip", skip)?;
|
plugin.set("skip", skip)?;
|
||||||
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||||
plugin.set("window", Window::default())?;
|
plugin.set("window", Window::default())?;
|
||||||
plugin.call_method("peek", ())
|
plugin.call_method("peek", ())
|
||||||
})),
|
})),
|
||||||
tx: None,
|
..Default::default()
|
||||||
};
|
};
|
||||||
emit!(Call(
|
emit!(Call(
|
||||||
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) {
|
||||||
let data = OptData {
|
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("file", File::cast(&LUA, file)?)?;
|
||||||
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||||
plugin.call_method("seek", units)
|
plugin.call_method("seek", units)
|
||||||
})),
|
})),
|
||||||
tx: None,
|
..Default::default()
|
||||||
};
|
};
|
||||||
emit!(Call(
|
emit!(Call(
|
||||||
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use mlua::{Table, Value};
|
use mlua::{Lua, Table};
|
||||||
use tokio::sync::oneshot;
|
|
||||||
use yazi_shared::event::Cmd;
|
use yazi_shared::event::Cmd;
|
||||||
|
|
||||||
use crate::ValueSendable;
|
use crate::ValueSendable;
|
||||||
|
|
@ -14,8 +13,8 @@ pub struct Opt {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct OptData {
|
pub struct OptData {
|
||||||
pub args: Vec<ValueSendable>,
|
pub args: Vec<ValueSendable>,
|
||||||
pub cb: Option<Box<dyn FnOnce(Table) -> mlua::Result<Value> + Send>>,
|
pub init: Option<Box<dyn FnOnce(&Lua) -> mlua::Result<()> + Send>>,
|
||||||
pub tx: Option<oneshot::Sender<ValueSendable>>,
|
pub cb: Option<Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Cmd> for Opt {
|
impl TryFrom<Cmd> for Opt {
|
||||||
|
|
|
||||||
|
|
@ -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 super::Utils;
|
||||||
|
use crate::{OptData, ValueSendable};
|
||||||
|
|
||||||
impl Utils {
|
impl Utils {
|
||||||
pub(super) fn plugin(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
pub(super) fn plugin(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
||||||
ya.set(
|
ya.set(
|
||||||
"plugin_retrieve",
|
"plugin_retrieve",
|
||||||
lua.create_async_function(
|
lua.create_async_function(
|
||||||
|_, (_name, _calls, _args): (String, usize, Variadic<Value>)| async move { Ok(()) },
|
|_, (name, blocks, args): (String, usize, Variadic<Value>)| async move {
|
||||||
|
let args = Variadic::from_iter(ValueSendable::try_from_variadic(args)?);
|
||||||
|
let (tx, rx) = oneshot::channel::<ValueSendable>();
|
||||||
|
|
||||||
|
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())
|
||||||
|
},
|
||||||
)?,
|
)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use mlua::{Lua, Table};
|
use mlua::{ExternalError, Lua, Table};
|
||||||
|
|
||||||
use super::Utils;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue