mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: support passing arguments to plugin (#587)
This commit is contained in:
parent
f31f78b063
commit
c325c332de
11 changed files with 56 additions and 28 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2730,6 +2730,7 @@ dependencies = [
|
|||
"ratatui",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shell-words",
|
||||
"syntect",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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", ()) };
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OrderedFloat(f64);
|
||||
|
||||
impl OrderedFloat {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue