mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41: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",
|
"ratatui",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"shell-words",
|
||||||
"syntect",
|
"syntect",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, popup::InputCfg, OPEN, PLUGIN};
|
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_scheduler::{Scheduler, TaskSummary};
|
||||||
use yazi_shared::{emit, event::Exec, fs::{File, Url}, term::Term, Layer, MIME_DIR};
|
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>) {
|
pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap<Url, String>) {
|
||||||
let mut single_tasks = Vec::with_capacity(paged.len());
|
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 mlua::{ExternalError, ExternalResult, IntoLua, Table, TableExt, Value, Variadic};
|
||||||
use tracing::error;
|
use tracing::{error, warn};
|
||||||
use yazi_plugin::{LOADED, LUA};
|
use yazi_plugin::{LOADED, LUA};
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, Layer};
|
||||||
|
|
||||||
use crate::{app::App, lives::Lives};
|
use crate::{app::App, lives::Lives};
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(crate) fn plugin(&mut self, opt: impl TryInto<yazi_plugin::Opt>) {
|
pub(crate) fn plugin(&mut self, opt: impl TryInto<yazi_plugin::Opt, Error = impl Display>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let opt = match opt.try_into() {
|
||||||
return;
|
Ok(opt) => opt as yazi_plugin::Opt,
|
||||||
|
Err(e) => return warn!("{e}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !opt.sync {
|
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) {
|
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>) {
|
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<yazi_plugin::Opt, Error = impl Display>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let opt = match opt.try_into() {
|
||||||
return;
|
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()));
|
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, |_| {
|
Lives::scope(&self.cx, |_| {
|
||||||
let mut plugin: Option<Table> = None;
|
let mut plugin: Option<Table> = None;
|
||||||
if let Some(b) = LOADED.read().get(&opt.name) {
|
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),
|
Ok(t) => plugin = Some(t),
|
||||||
Err(e) => ret = Err(e),
|
Err(e) => ret = Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(plugin) = plugin {
|
if let Some(plugin) = plugin {
|
||||||
ret =
|
ret = if let Some(cb) = opt.data.cb { cb(plugin) } else { plugin.call_method("entry", ()) };
|
||||||
if let Some(cb) = opt.data.cb { cb(plugin) } else { plugin.call_method("entry", args) };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ parking_lot = "^0"
|
||||||
ratatui = "^0"
|
ratatui = "^0"
|
||||||
serde = "^1"
|
serde = "^1"
|
||||||
serde_json = "^1"
|
serde_json = "^1"
|
||||||
|
shell-words = "^1"
|
||||||
syntect = { version = "^5", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
syntect = { version = "^5", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
||||||
tokio = { version = "^1", features = [ "parking_lot", "rt-multi-thread" ] }
|
tokio = { version = "^1", features = [ "parking_lot", "rt-multi-thread" ] }
|
||||||
tokio-util = "^0"
|
tokio-util = "^0"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ pub fn cast_to_renderable(ud: AnyUserData) -> Option<Box<dyn Renderable + Send>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ValueSendable {
|
pub enum ValueSendable {
|
||||||
Nil,
|
Nil,
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
|
@ -96,7 +97,7 @@ impl ValueSendable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum ValueSendableKey {
|
pub enum ValueSendableKey {
|
||||||
Nil,
|
Nil,
|
||||||
Boolean(bool),
|
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 tokio::runtime::Handle;
|
||||||
|
|
||||||
use super::slim_lua;
|
use super::slim_lua;
|
||||||
use crate::LOADED;
|
use crate::{ValueSendable, LOADED};
|
||||||
|
|
||||||
pub async fn entry(name: &str) -> mlua::Result<()> {
|
pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> {
|
||||||
LOADED.ensure(name).await.into_lua_err()?;
|
LOADED.ensure(&name).await.into_lua_err()?;
|
||||||
|
|
||||||
let name = name.to_owned();
|
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua()?;
|
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) {
|
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
||||||
lua.load(b).call(())?
|
lua.load(b).call(args)?
|
||||||
} else {
|
} else {
|
||||||
return Err("unloaded plugin".into_lua_err());
|
return Err("unloaded plugin".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,15 @@ impl TryFrom<Exec> for Opt {
|
||||||
bail!("invalid plugin name");
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum PluginOp {
|
pub enum PluginOp {
|
||||||
Entry(PluginOpEntry),
|
Entry(PluginOpEntry),
|
||||||
|
|
@ -11,8 +13,9 @@ impl PluginOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PluginOpEntry {
|
pub struct PluginOpEntry {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub args: Vec<ValueSendable>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl Plugin {
|
||||||
pub async fn work(&self, op: PluginOp) -> Result<()> {
|
pub async fn work(&self, op: PluginOp) -> Result<()> {
|
||||||
match op {
|
match op {
|
||||||
PluginOp::Entry(task) => {
|
PluginOp::Entry(task) => {
|
||||||
isolate::entry(&task.name).await?;
|
isolate::entry(task.name, task.args).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -30,7 +30,7 @@ impl Plugin {
|
||||||
pub async fn micro(&self, task: PluginOpEntry) -> Result<()> {
|
pub async fn micro(&self, task: PluginOpEntry) -> Result<()> {
|
||||||
self.prog.send(TaskProg::New(task.id, 0))?;
|
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}"))?;
|
self.fail(task.id, format!("Micro plugin failed:\n{e}"))?;
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use futures::{future::BoxFuture, FutureExt};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}};
|
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}};
|
||||||
use yazi_config::{open::Opener, plugin::PluginRule, TASKS};
|
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 yazi_shared::{emit, event::Exec, fs::{unique_path, Url}, Layer, Throttle};
|
||||||
|
|
||||||
use super::{Running, TaskProg, TaskStage};
|
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 id = self.running.lock().add(TaskKind::User, format!("Run micro plugin `{name}`"));
|
||||||
|
|
||||||
let plugin = self.plugin.clone();
|
let plugin = self.plugin.clone();
|
||||||
_ = self.micro.try_send(
|
_ = self.micro.try_send(
|
||||||
async move {
|
async move {
|
||||||
plugin.micro(PluginOpEntry { id, name }).await.ok();
|
plugin.micro(PluginOpEntry { id, name, args }).await.ok();
|
||||||
}
|
}
|
||||||
.boxed(),
|
.boxed(),
|
||||||
HIGH,
|
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}`"));
|
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>) {
|
pub fn preload_paged(&self, rule: &PluginRule, targets: Vec<&yazi_shared::fs::File>) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct OrderedFloat(f64);
|
pub struct OrderedFloat(f64);
|
||||||
|
|
||||||
impl OrderedFloat {
|
impl OrderedFloat {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue