diff --git a/Cargo.lock b/Cargo.lock index 5e7d3cc5..e01e319c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2730,6 +2730,7 @@ dependencies = [ "ratatui", "serde", "serde_json", + "shell-words", "syntect", "tokio", "tokio-util", diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 351d7410..d8dfc91f 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -3,6 +3,7 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path use tokio::time::sleep; use tracing::debug; use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, popup::InputCfg, OPEN, PLUGIN}; +use yazi_plugin::ValueSendable; use yazi_scheduler::{Scheduler, TaskSummary}; use yazi_shared::{emit, event::Exec, fs::{File, Url}, term::Term, Layer, MIME_DIR}; @@ -145,9 +146,15 @@ impl Tasks { }); } - pub fn plugin_micro(&self, name: &str) { self.scheduler.plugin_micro(name.to_owned()); } + #[inline] + pub fn plugin_micro(&self, name: String, args: Vec) { + self.scheduler.plugin_micro(name, args); + } - pub fn plugin_macro(&self, name: &str) { self.scheduler.plugin_macro(name.to_owned()); } + #[inline] + pub fn plugin_macro(&self, name: String, args: Vec) { + self.scheduler.plugin_macro(name, args); + } pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap) { let mut single_tasks = Vec::with_capacity(paged.len()); diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 6331ae5f..4443d049 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -1,18 +1,21 @@ +use std::fmt::Display; + use mlua::{ExternalError, ExternalResult, IntoLua, Table, TableExt, Value, Variadic}; -use tracing::error; +use tracing::{error, warn}; use yazi_plugin::{LOADED, LUA}; use yazi_shared::{emit, event::Exec, Layer}; use crate::{app::App, lives::Lives}; impl App { - pub(crate) fn plugin(&mut self, opt: impl TryInto) { - let Ok(opt) = opt.try_into() else { - return; + pub(crate) fn plugin(&mut self, opt: impl TryInto) { + let opt = match opt.try_into() { + Ok(opt) => opt as yazi_plugin::Opt, + Err(e) => return warn!("{e}"), }; if !opt.sync { - return self.cx.tasks.plugin_micro(&opt.name); + return self.cx.tasks.plugin_micro(opt.name, opt.data.args); } if LOADED.read().contains_key(&opt.name) { @@ -26,9 +29,10 @@ impl App { }); } - pub(crate) fn plugin_do(&mut self, opt: impl TryInto) { - let Ok(opt) = opt.try_into() else { - return; + 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 args = Variadic::from_iter(opt.data.args.into_iter().filter_map(|v| v.into_lua(&LUA).ok())); @@ -37,14 +41,13 @@ impl App { Lives::scope(&self.cx, |_| { let mut plugin: Option = None; if let Some(b) = LOADED.read().get(&opt.name) { - match LUA.load(b).call(()) { + match LUA.load(b).call(args) { Ok(t) => plugin = Some(t), Err(e) => ret = Err(e), } } if let Some(plugin) = plugin { - ret = - if let Some(cb) = opt.data.cb { cb(plugin) } else { plugin.call_method("entry", args) }; + ret = if let Some(cb) = opt.data.cb { cb(plugin) } else { plugin.call_method("entry", ()) }; } }); diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 0235eb41..35bce019 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -24,6 +24,7 @@ parking_lot = "^0" ratatui = "^0" serde = "^1" serde_json = "^1" +shell-words = "^1" syntect = { version = "^5", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "^1", features = [ "parking_lot", "rt-multi-thread" ] } tokio-util = "^0" diff --git a/yazi-plugin/src/cast.rs b/yazi-plugin/src/cast.rs index 6388af4e..26d79359 100644 --- a/yazi-plugin/src/cast.rs +++ b/yazi-plugin/src/cast.rs @@ -22,6 +22,7 @@ pub fn cast_to_renderable(ud: AnyUserData) -> Option> } } +#[derive(Debug)] pub enum ValueSendable { Nil, Boolean(bool), @@ -96,7 +97,7 @@ impl ValueSendable { } } -#[derive(Hash, PartialEq, Eq)] +#[derive(Debug, Hash, PartialEq, Eq)] pub enum ValueSendableKey { Nil, Boolean(bool), diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index 572977b7..1fee4eb8 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -1,17 +1,18 @@ -use mlua::{ExternalError, ExternalResult, Table, TableExt}; +use mlua::{ExternalError, ExternalResult, IntoLua, Table, TableExt, Variadic}; use tokio::runtime::Handle; use super::slim_lua; -use crate::LOADED; +use crate::{ValueSendable, LOADED}; -pub async fn entry(name: &str) -> mlua::Result<()> { - LOADED.ensure(name).await.into_lua_err()?; +pub async fn entry(name: String, args: Vec) -> mlua::Result<()> { + LOADED.ensure(&name).await.into_lua_err()?; - let name = name.to_owned(); tokio::task::spawn_blocking(move || { let lua = slim_lua()?; + let args = Variadic::from_iter(args.into_iter().filter_map(|v| v.into_lua(&lua).ok())); + let plugin: Table = if let Some(b) = LOADED.read().get(&name) { - lua.load(b).call(())? + lua.load(b).call(args)? } else { return Err("unloaded plugin".into_lua_err()); }; diff --git a/yazi-plugin/src/opt.rs b/yazi-plugin/src/opt.rs index 1daa6f1c..503e5074 100644 --- a/yazi-plugin/src/opt.rs +++ b/yazi-plugin/src/opt.rs @@ -26,6 +26,15 @@ impl TryFrom for Opt { bail!("invalid plugin name"); }; - Ok(Self { name, sync: e.named.contains_key("sync"), data: e.take_data().unwrap_or_default() }) + let mut data: OptData = e.take_data().unwrap_or_default(); + + if let Some(args) = e.named.get("args") { + data.args = shell_words::split(args)? + .into_iter() + .map(|s| ValueSendable::String(s.into_bytes())) + .collect(); + } + + Ok(Self { name, sync: e.named.contains_key("sync"), data }) } } diff --git a/yazi-scheduler/src/plugin/op.rs b/yazi-scheduler/src/plugin/op.rs index 729021d9..84f3c8c9 100644 --- a/yazi-scheduler/src/plugin/op.rs +++ b/yazi-scheduler/src/plugin/op.rs @@ -1,3 +1,5 @@ +use yazi_plugin::ValueSendable; + #[derive(Debug)] pub enum PluginOp { Entry(PluginOpEntry), @@ -11,8 +13,9 @@ impl PluginOp { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PluginOpEntry { pub id: usize, pub name: String, + pub args: Vec, } diff --git a/yazi-scheduler/src/plugin/plugin.rs b/yazi-scheduler/src/plugin/plugin.rs index 28ebae3d..2d7f66bd 100644 --- a/yazi-scheduler/src/plugin/plugin.rs +++ b/yazi-scheduler/src/plugin/plugin.rs @@ -21,7 +21,7 @@ impl Plugin { pub async fn work(&self, op: PluginOp) -> Result<()> { match op { PluginOp::Entry(task) => { - isolate::entry(&task.name).await?; + isolate::entry(task.name, task.args).await?; } } Ok(()) @@ -30,7 +30,7 @@ impl Plugin { pub async fn micro(&self, task: PluginOpEntry) -> Result<()> { self.prog.send(TaskProg::New(task.id, 0))?; - if let Err(e) = isolate::entry(&task.name).await { + if let Err(e) = isolate::entry(task.name, task.args).await { self.fail(task.id, format!("Micro plugin failed:\n{e}"))?; return Err(e.into()); } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 510a141f..df7de96e 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -4,6 +4,7 @@ use futures::{future::BoxFuture, FutureExt}; use parking_lot::Mutex; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}}; use yazi_config::{open::Opener, plugin::PluginRule, TASKS}; +use yazi_plugin::ValueSendable; use yazi_shared::{emit, event::Exec, fs::{unique_path, Url}, Layer, Throttle}; use super::{Running, TaskProg, TaskStage}; @@ -284,23 +285,23 @@ impl Scheduler { ); } - pub fn plugin_micro(&self, name: String) { + pub fn plugin_micro(&self, name: String, args: Vec) { let id = self.running.lock().add(TaskKind::User, format!("Run micro plugin `{name}`")); let plugin = self.plugin.clone(); _ = self.micro.try_send( async move { - plugin.micro(PluginOpEntry { id, name }).await.ok(); + plugin.micro(PluginOpEntry { id, name, args }).await.ok(); } .boxed(), HIGH, ); } - pub fn plugin_macro(&self, name: String) { + pub fn plugin_macro(&self, name: String, args: Vec) { let id = self.running.lock().add(TaskKind::User, format!("Run macro plugin `{name}`")); - self.plugin.macro_(PluginOpEntry { id, name }).ok(); + self.plugin.macro_(PluginOpEntry { id, name, args }).ok(); } pub fn preload_paged(&self, rule: &PluginRule, targets: Vec<&yazi_shared::fs::File>) { diff --git a/yazi-shared/src/number.rs b/yazi-shared/src/number.rs index 5c939f13..70edd3bd 100644 --- a/yazi-shared/src/number.rs +++ b/yazi-shared/src/number.rs @@ -1,5 +1,6 @@ use std::hash::{Hash, Hasher}; +#[derive(Clone, Copy, Debug)] pub struct OrderedFloat(f64); impl OrderedFloat {