feat: support passing arguments to plugin (#587)

This commit is contained in:
三咲雅 · Misaki Masa 2024-01-27 15:20:16 +08:00 committed by GitHub
parent f31f78b063
commit c325c332de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 56 additions and 28 deletions

1
Cargo.lock generated
View file

@ -2730,6 +2730,7 @@ dependencies = [
"ratatui",
"serde",
"serde_json",
"shell-words",
"syntect",
"tokio",
"tokio-util",

View file

@ -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<ValueSendable>) {
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<ValueSendable>) {
self.scheduler.plugin_macro(name, args);
}
pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap<Url, String>) {
let mut single_tasks = Vec::with_capacity(paged.len());

View file

@ -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<yazi_plugin::Opt>) {
let Ok(opt) = opt.try_into() else {
return;
pub(crate) fn plugin(&mut self, opt: impl TryInto<yazi_plugin::Opt, Error = impl Display>) {
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<yazi_plugin::Opt>) {
let Ok(opt) = opt.try_into() else {
return;
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<yazi_plugin::Opt, Error = impl Display>) {
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<Table> = 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", ()) };
}
});

View file

@ -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"

View file

@ -22,6 +22,7 @@ pub fn cast_to_renderable(ud: AnyUserData) -> Option<Box<dyn Renderable + Send>>
}
}
#[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),

View file

@ -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<ValueSendable>) -> 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());
};

View file

@ -26,6 +26,15 @@ impl TryFrom<Exec> 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 })
}
}

View file

@ -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<ValueSendable>,
}

View file

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

View file

@ -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<ValueSendable>) {
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<ValueSendable>) {
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>) {

View file

@ -1,5 +1,6 @@
use std::hash::{Hash, Hasher};
#[derive(Clone, Copy, Debug)]
pub struct OrderedFloat(f64);
impl OrderedFloat {