mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat!: deprecate --sync option for the plugin command (#1891)
This commit is contained in:
parent
6001f3c619
commit
baf062f3b1
21 changed files with 258 additions and 156 deletions
|
|
@ -4,56 +4,63 @@ use mlua::TableExt;
|
||||||
use scopeguard::defer;
|
use scopeguard::defer;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use yazi_dds::Sendable;
|
use yazi_dds::Sendable;
|
||||||
use yazi_macro::emit;
|
|
||||||
use yazi_plugin::{LUA, RtRef, loader::LOADER};
|
use yazi_plugin::{LUA, RtRef, loader::LOADER};
|
||||||
use yazi_shared::{Layer, event::Cmd};
|
use yazi_proxy::{AppProxy, options::{PluginMode, PluginOpt}};
|
||||||
|
|
||||||
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, Error = impl Display>) {
|
pub(crate) fn plugin(&mut self, opt: impl TryInto<PluginOpt, Error = impl Display>) {
|
||||||
let opt = match opt.try_into() {
|
let mut opt = match opt.try_into() {
|
||||||
Ok(opt) => opt as yazi_plugin::Opt,
|
Ok(opt) => opt as PluginOpt,
|
||||||
Err(e) => return warn!("{e}"),
|
Err(e) => return warn!("{e}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !opt.sync {
|
let mut hits = false;
|
||||||
return self.cx.tasks.plugin_micro(opt.id, opt.args);
|
if let Some(chunk) = LOADER.read().get(&opt.id) {
|
||||||
|
hits = true;
|
||||||
|
opt.mode = opt.mode.auto_then(chunk.sync_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if LOADER.read().contains_key(&opt.id) {
|
if opt.mode == PluginMode::Async {
|
||||||
|
return self.cx.tasks.plugin_micro(opt.id, opt.args);
|
||||||
|
} else if opt.mode == PluginMode::Sync && hits {
|
||||||
return self.plugin_do(opt);
|
return self.plugin_do(opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if LOADER.ensure(&opt.id).await.is_ok() {
|
if LOADER.ensure(&opt.id).await.is_ok() {
|
||||||
Self::_plugin_do(opt);
|
AppProxy::plugin_do(opt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<PluginOpt, Error = impl Display>) {
|
||||||
pub(crate) fn _plugin_do(opt: yazi_plugin::Opt) {
|
|
||||||
let cmd: Cmd = opt.into();
|
|
||||||
emit!(Call(cmd.with_name("plugin_do"), Layer::App));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<yazi_plugin::Opt, Error = impl Display>) {
|
|
||||||
let opt = match opt.try_into() {
|
let opt = match opt.try_into() {
|
||||||
Ok(opt) => opt as yazi_plugin::Opt,
|
Ok(opt) => opt as PluginOpt,
|
||||||
Err(e) => return warn!("{e}"),
|
Err(e) => return warn!("{e}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let loader = LOADER.read();
|
||||||
|
let Some(chunk) = loader.get(&opt.id) else {
|
||||||
|
return warn!("plugin `{}` not found", opt.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
||||||
|
return self.cx.tasks.plugin_micro(opt.id, opt.args);
|
||||||
|
}
|
||||||
|
|
||||||
match LUA.named_registry_value::<RtRef>("rt") {
|
match LUA.named_registry_value::<RtRef>("rt") {
|
||||||
Ok(mut r) => r.push(&opt.id),
|
Ok(mut r) => r.push(&opt.id),
|
||||||
Err(e) => return warn!("{e}"),
|
Err(e) => return warn!("{e}"),
|
||||||
}
|
}
|
||||||
defer! { _ = LUA.named_registry_value::<RtRef>("rt").map(|mut r| r.pop()) }
|
defer! { _ = LUA.named_registry_value::<RtRef>("rt").map(|mut r| r.pop()) }
|
||||||
|
|
||||||
let plugin = match LOADER.load(&LUA, &opt.id) {
|
let plugin = match LOADER.load_with(&LUA, &opt.id, chunk) {
|
||||||
Ok(plugin) => plugin,
|
Ok(plugin) => plugin,
|
||||||
Err(e) => return warn!("{e}"),
|
Err(e) => return warn!("{e}"),
|
||||||
};
|
};
|
||||||
|
drop(loader);
|
||||||
|
|
||||||
_ = Lives::scope(&self.cx, |_| {
|
_ = Lives::scope(&self.cx, |_| {
|
||||||
if let Some(cb) = opt.cb {
|
if let Some(cb) = opt.cb {
|
||||||
|
|
|
||||||
|
|
@ -141,13 +141,13 @@ impl<'a> Executor<'a> {
|
||||||
on!(TABS, switch);
|
on!(TABS, switch);
|
||||||
on!(TABS, swap);
|
on!(TABS, swap);
|
||||||
|
|
||||||
match cmd.name.as_bytes() {
|
match cmd.name.as_str() {
|
||||||
// Tasks
|
// Tasks
|
||||||
b"tasks_show" => self.app.cx.tasks.toggle(()),
|
"tasks_show" => self.app.cx.tasks.toggle(()),
|
||||||
// Help
|
// Help
|
||||||
b"help" => self.app.cx.help.toggle(Layer::Manager),
|
"help" => self.app.cx.help.toggle(Layer::Manager),
|
||||||
// Plugin
|
// Plugin
|
||||||
b"plugin" => self.app.plugin(cmd),
|
"plugin" => self.app.plugin(cmd),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
yazi-plugin/src/external/highlighter.rs
vendored
2
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -35,7 +35,7 @@ impl Highlighter {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (theme, syntaxes) = SYNTECT.get_or_init(|| fut).await;
|
let (theme, syntaxes) = SYNTECT.get_or_init(|| fut).await;
|
||||||
(&theme, &syntaxes)
|
(theme, syntaxes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub async fn entry(name: String, args: Vec<Data>) -> mlua::Result<()> {
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua(&name)?;
|
let lua = slim_lua(&name)?;
|
||||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||||
lua.load(b.as_ref()).set_name(name).call(())?
|
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||||
} else {
|
} else {
|
||||||
return Err("unloaded plugin".into_lua_err());
|
return Err("unloaded plugin".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub async fn fetch(name: &str, files: Vec<yazi_shared::fs::File>) -> mlua::Resul
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua(&name)?;
|
let lua = slim_lua(&name)?;
|
||||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||||
lua.load(b.as_ref()).set_name(name).call(())?
|
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||||
} else {
|
} else {
|
||||||
return Err("unloaded plugin".into_lua_err());
|
return Err("unloaded plugin".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use tokio::{runtime::Handle, select};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use yazi_config::LAYOUT;
|
use yazi_config::LAYOUT;
|
||||||
use yazi_macro::emit;
|
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
|
||||||
use yazi_shared::{Layer, event::Cmd};
|
use yazi_shared::event::Cmd;
|
||||||
|
|
||||||
use super::slim_lua;
|
use super::slim_lua;
|
||||||
use crate::{LUA, Opt, OptCallback, bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER};
|
use crate::{LUA, bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER};
|
||||||
|
|
||||||
pub fn peek(
|
pub fn peek(
|
||||||
cmd: &Cmd,
|
cmd: &Cmd,
|
||||||
|
|
@ -34,7 +34,7 @@ pub fn peek(
|
||||||
);
|
);
|
||||||
|
|
||||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||||
lua.load(b.as_ref()).set_name(name).call(())?
|
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||||
} else {
|
} else {
|
||||||
return Err("unloaded plugin".into_lua_err());
|
return Err("unloaded plugin".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
@ -65,7 +65,7 @@ pub fn peek(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>, skip: usize) {
|
pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>, skip: usize) {
|
||||||
let cb: OptCallback = Box::new(move |_, plugin| {
|
let cb: PluginCallback = Box::new(move |_, plugin| {
|
||||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||||
plugin.raw_set("_mime", mime)?;
|
plugin.raw_set("_mime", mime)?;
|
||||||
plugin.raw_set("skip", skip)?;
|
plugin.raw_set("skip", skip)?;
|
||||||
|
|
@ -74,8 +74,5 @@ pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>
|
||||||
plugin.call_method("peek", ())
|
plugin.call_method("peek", ())
|
||||||
});
|
});
|
||||||
|
|
||||||
let cmd: Cmd =
|
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||||
Opt { id: cmd.name.to_owned(), sync: true, cb: Some(cb), ..Default::default() }.into();
|
|
||||||
|
|
||||||
emit!(Call(cmd.with_name("plugin"), Layer::App));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result<u8
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua(&name)?;
|
let lua = slim_lua(&name)?;
|
||||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||||
lua.load(b.as_ref()).set_name(name).call(())?
|
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||||
} else {
|
} else {
|
||||||
return Err("unloaded plugin".into_lua_err());
|
return Err("unloaded plugin".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
use mlua::TableExt;
|
use mlua::TableExt;
|
||||||
use yazi_config::LAYOUT;
|
use yazi_config::LAYOUT;
|
||||||
use yazi_macro::emit;
|
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
|
||||||
use yazi_shared::{Layer, event::Cmd};
|
use yazi_shared::event::Cmd;
|
||||||
|
|
||||||
use crate::{LUA, Opt, OptCallback, bindings::Cast, elements::Rect, file::File};
|
use crate::{LUA, bindings::Cast, elements::Rect, file::File};
|
||||||
|
|
||||||
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 cb: OptCallback = Box::new(move |_, plugin| {
|
let cb: PluginCallback = Box::new(move |_, plugin| {
|
||||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||||
plugin.call_method("seek", units)
|
plugin.call_method("seek", units)
|
||||||
});
|
});
|
||||||
|
|
||||||
let cmd: Cmd =
|
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||||
Opt { id: cmd.name.to_owned(), sync: true, cb: Some(cb), ..Default::default() }.into();
|
|
||||||
|
|
||||||
emit!(Call(cmd.with_name("plugin"), Layer::App));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ yazi_macro::mod_pub!(
|
||||||
bindings, cha, elements, external, file, fs, isolate, loader, process, pubsub, url, utils
|
bindings, cha, elements, external, file, fs, isolate, loader, process, pubsub, url, utils
|
||||||
);
|
);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(cast clipboard config lua opt runtime);
|
yazi_macro::mod_flat!(cast clipboard config lua runtime);
|
||||||
|
|
||||||
pub fn init() -> anyhow::Result<()> {
|
pub fn init() -> anyhow::Result<()> {
|
||||||
CLIPBOARD.with(<_>::default);
|
CLIPBOARD.with(<_>::default);
|
||||||
|
|
|
||||||
43
yazi-plugin/src/loader/chunk.rs
Normal file
43
yazi-plugin/src/loader/chunk.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub struct Chunk {
|
||||||
|
pub bytes: Cow<'static, [u8]>,
|
||||||
|
pub sync_entry: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
#[inline]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] { &self.bytes }
|
||||||
|
|
||||||
|
fn analyze(&mut self) {
|
||||||
|
for line in self.bytes.split(|&b| b == b'\n') {
|
||||||
|
let Some(rest) = line.strip_prefix(b"---") else { break };
|
||||||
|
|
||||||
|
let rest = rest.trim_ascii();
|
||||||
|
let Some(pos) = rest.iter().position(|&b| b == b' ' || b == b'\t') else { break };
|
||||||
|
|
||||||
|
match (rest[..pos].trim_ascii(), rest[pos..].trim_ascii()) {
|
||||||
|
(b"@sync", b"entry") => self.sync_entry = true,
|
||||||
|
(_, []) => break,
|
||||||
|
(b, _) if b.strip_prefix(b"@").unwrap_or(b"").is_empty() => break,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Cow<'static, [u8]>> for Chunk {
|
||||||
|
fn from(b: Cow<'static, [u8]>) -> Self {
|
||||||
|
let mut chunk = Self { bytes: b, sync_entry: false };
|
||||||
|
chunk.analyze();
|
||||||
|
chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static [u8]> for Chunk {
|
||||||
|
fn from(b: &'static [u8]) -> Self { Self::from(Cow::Borrowed(b)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Chunk {
|
||||||
|
fn from(b: Vec<u8>) -> Self { Self::from(Cow::Owned(b)) }
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, collections::HashMap, ops::Deref};
|
use std::{collections::HashMap, ops::Deref};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use mlua::{ExternalError, Lua, Table};
|
use mlua::{ExternalError, Lua, Table};
|
||||||
|
|
@ -8,11 +8,45 @@ use yazi_boot::BOOT;
|
||||||
use yazi_macro::plugin_preset as preset;
|
use yazi_macro::plugin_preset as preset;
|
||||||
use yazi_shared::RoCell;
|
use yazi_shared::RoCell;
|
||||||
|
|
||||||
|
use super::Chunk;
|
||||||
|
|
||||||
pub static LOADER: RoCell<Loader> = RoCell::new();
|
pub static LOADER: RoCell<Loader> = RoCell::new();
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Loader {
|
pub struct Loader {
|
||||||
cache: RwLock<HashMap<String, Cow<'static, [u8]>>>,
|
cache: RwLock<HashMap<String, Chunk>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Loader {
|
||||||
|
type Target = RwLock<HashMap<String, Chunk>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target { &self.cache }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Loader {
|
||||||
|
fn default() -> Self {
|
||||||
|
let cache = HashMap::from_iter([
|
||||||
|
("archive".to_owned(), preset!("plugins/archive").into()),
|
||||||
|
("code".to_owned(), preset!("plugins/code").into()),
|
||||||
|
("dds".to_owned(), preset!("plugins/dds").into()),
|
||||||
|
("empty".to_owned(), preset!("plugins/empty").into()),
|
||||||
|
("extract".to_owned(), preset!("plugins/extract").into()),
|
||||||
|
("file".to_owned(), preset!("plugins/file").into()),
|
||||||
|
("folder".to_owned(), preset!("plugins/folder").into()),
|
||||||
|
("font".to_owned(), preset!("plugins/font").into()),
|
||||||
|
("fzf".to_owned(), preset!("plugins/fzf").into()),
|
||||||
|
("image".to_owned(), preset!("plugins/image").into()),
|
||||||
|
("json".to_owned(), preset!("plugins/json").into()),
|
||||||
|
("magick".to_owned(), preset!("plugins/magick").into()),
|
||||||
|
("mime".to_owned(), preset!("plugins/mime").into()),
|
||||||
|
("noop".to_owned(), preset!("plugins/noop").into()),
|
||||||
|
("pdf".to_owned(), preset!("plugins/pdf").into()),
|
||||||
|
("session".to_owned(), preset!("plugins/session").into()),
|
||||||
|
("video".to_owned(), preset!("plugins/video").into()),
|
||||||
|
("zoxide".to_owned(), preset!("plugins/zoxide").into()),
|
||||||
|
]);
|
||||||
|
Self { cache: RwLock::new(cache) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loader {
|
impl Loader {
|
||||||
|
|
@ -21,36 +55,11 @@ impl Loader {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let preset = match name {
|
let p = BOOT.plugin_dir.join(format!("{name}.yazi/init.lua"));
|
||||||
"archive" => preset!("plugins/archive"),
|
let chunk =
|
||||||
"code" => preset!("plugins/code"),
|
fs::read(&p).await.with_context(|| format!("failed to load plugin from {p:?}"))?.into();
|
||||||
"dds" => preset!("plugins/dds"),
|
|
||||||
"empty" => preset!("plugins/empty"),
|
|
||||||
"extract" => preset!("plugins/extract"),
|
|
||||||
"file" => preset!("plugins/file"),
|
|
||||||
"folder" => preset!("plugins/folder"),
|
|
||||||
"font" => preset!("plugins/font"),
|
|
||||||
"fzf" => preset!("plugins/fzf"),
|
|
||||||
"image" => preset!("plugins/image"),
|
|
||||||
"json" => preset!("plugins/json"),
|
|
||||||
"magick" => preset!("plugins/magick"),
|
|
||||||
"mime" => preset!("plugins/mime"),
|
|
||||||
"noop" => preset!("plugins/noop"),
|
|
||||||
"pdf" => preset!("plugins/pdf"),
|
|
||||||
"session" => preset!("plugins/session"),
|
|
||||||
"video" => preset!("plugins/video"),
|
|
||||||
"zoxide" => preset!("plugins/zoxide"),
|
|
||||||
_ => Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = if preset.is_empty() {
|
self.cache.write().insert(name.to_owned(), chunk);
|
||||||
let p = BOOT.plugin_dir.join(format!("{name}.yazi/init.lua"));
|
|
||||||
Cow::Owned(fs::read(&p).await.with_context(|| format!("failed to load plugin from {p:?}"))?)
|
|
||||||
} else {
|
|
||||||
preset.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.cache.write().insert(name.to_owned(), b);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +70,7 @@ impl Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
let t: Table = match self.read().get(id) {
|
let t: Table = match self.read().get(id) {
|
||||||
Some(b) => lua.load(b.as_ref()).set_name(id).call(())?,
|
Some(c) => lua.load(c.as_bytes()).set_name(id).call(())?,
|
||||||
None => Err(format!("plugin `{id}` not found").into_lua_err())?,
|
None => Err(format!("plugin `{id}` not found").into_lua_err())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -69,11 +78,22 @@ impl Loader {
|
||||||
loaded.raw_set(id, t.clone())?;
|
loaded.raw_set(id, t.clone())?;
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Loader {
|
pub fn try_load<'a>(&self, lua: &'a Lua, id: &str) -> mlua::Result<Table<'a>> {
|
||||||
type Target = RwLock<HashMap<String, Cow<'static, [u8]>>>;
|
let loaded: Table = lua.globals().raw_get::<_, Table>("package")?.raw_get("loaded")?;
|
||||||
|
loaded.raw_get(id)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn load_with<'a>(&self, lua: &'a Lua, id: &str, chunk: &Chunk) -> mlua::Result<Table<'a>> {
|
||||||
fn deref(&self) -> &Self::Target { &self.cache }
|
let loaded: Table = lua.globals().raw_get::<_, Table>("package")?.raw_get("loaded")?;
|
||||||
|
if let Ok(t) = loaded.raw_get::<_, Table>(id) {
|
||||||
|
return Ok(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
let t: Table = lua.load(chunk.as_bytes()).set_name(id).call(())?;
|
||||||
|
t.raw_set("_id", lua.create_string(id)?)?;
|
||||||
|
|
||||||
|
loaded.raw_set(id, t.clone())?;
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
yazi_macro::mod_flat!(loader require);
|
yazi_macro::mod_flat!(chunk loader require);
|
||||||
|
|
||||||
pub(super) fn init() { LOADER.with(<_>::default); }
|
pub(super) fn init() { LOADER.with(<_>::default); }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,18 +103,18 @@ impl Require {
|
||||||
mut args: MultiValue<'a>,
|
mut args: MultiValue<'a>,
|
||||||
) -> mlua::Result<(Table<'a>, MultiValue<'a>)> {
|
) -> mlua::Result<(Table<'a>, MultiValue<'a>)> {
|
||||||
let Some(front) = args.pop_front() else {
|
let Some(front) = args.pop_front() else {
|
||||||
return Ok((LOADER.load(lua, id)?, args));
|
return Ok((LOADER.try_load(lua, id)?, args));
|
||||||
};
|
};
|
||||||
let Value::Table(tbl) = front else {
|
let Value::Table(tbl) = front else {
|
||||||
args.push_front(front);
|
args.push_front(front);
|
||||||
return Ok((LOADER.load(lua, id)?, args));
|
return Ok((LOADER.try_load(lua, id)?, args));
|
||||||
};
|
};
|
||||||
Ok(if let Ok(mod_) = tbl.raw_get::<_, Table>("__mod") {
|
Ok(if let Ok(mod_) = tbl.raw_get::<_, Table>("__mod") {
|
||||||
args.push_front(Value::Table(mod_.clone()));
|
args.push_front(Value::Table(mod_.clone()));
|
||||||
(mod_, args)
|
(mod_, args)
|
||||||
} else {
|
} else {
|
||||||
args.push_front(Value::Table(tbl));
|
args.push_front(Value::Table(tbl));
|
||||||
(LOADER.load(lua, id)?, args)
|
(LOADER.try_load(lua, id)?, args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
use anyhow::bail;
|
|
||||||
use mlua::{Lua, Table};
|
|
||||||
use yazi_shared::event::{Cmd, Data};
|
|
||||||
|
|
||||||
pub(super) type OptCallback = Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Opt {
|
|
||||||
pub id: String,
|
|
||||||
pub sync: bool,
|
|
||||||
pub args: Vec<Data>,
|
|
||||||
pub cb: Option<OptCallback>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Cmd> for Opt {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
|
||||||
let Some(id) = c.take_first_str().filter(|s| !s.is_empty()) else {
|
|
||||||
bail!("plugin id cannot be empty");
|
|
||||||
};
|
|
||||||
|
|
||||||
let args = if let Some(s) = c.str("args") {
|
|
||||||
shell_words::split(s)?.into_iter().map(Data::String).collect()
|
|
||||||
} else {
|
|
||||||
c.take_any::<Vec<Data>>("args").unwrap_or_default()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { id, sync: c.bool("sync"), args, cb: c.take_any("callback") })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Opt> for Cmd {
|
|
||||||
fn from(value: Opt) -> Self {
|
|
||||||
let mut cmd =
|
|
||||||
Cmd::args("", &[value.id]).with_bool("sync", value.sync).with_any("args", value.args);
|
|
||||||
|
|
||||||
if let Some(cb) = value.cb {
|
|
||||||
cmd = cmd.with_any("callback", cb);
|
|
||||||
}
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use mlua::{ExternalError, ExternalResult, Function, Lua, MultiValue, Table, Value};
|
use mlua::{ExternalError, ExternalResult, Function, Lua, MultiValue, Table, Value};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use yazi_dds::Sendable;
|
use yazi_dds::Sendable;
|
||||||
use yazi_macro::emit;
|
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
|
||||||
use yazi_shared::{Layer, event::{Cmd, Data}};
|
use yazi_shared::event::Data;
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::{OptCallback, loader::LOADER, runtime::RtRef};
|
use crate::{loader::LOADER, runtime::RtRef};
|
||||||
|
|
||||||
impl Utils {
|
impl Utils {
|
||||||
pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> {
|
pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> {
|
||||||
|
|
@ -19,7 +19,7 @@ impl Utils {
|
||||||
|
|
||||||
let cur = rt.current().unwrap().to_owned();
|
let cur = rt.current().unwrap().to_owned();
|
||||||
lua.create_function(move |lua, mut args: MultiValue| {
|
lua.create_function(move |lua, mut args: MultiValue| {
|
||||||
args.push_front(Value::Table(LOADER.load(lua, &cur)?));
|
args.push_front(Value::Table(LOADER.try_load(lua, &cur)?));
|
||||||
f.call::<_, MultiValue>(args)
|
f.call::<_, MultiValue>(args)
|
||||||
})
|
})
|
||||||
})?,
|
})?,
|
||||||
|
|
@ -49,14 +49,14 @@ impl Utils {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn retrieve(name: &str, calls: usize, args: MultiValue<'_>) -> mlua::Result<Vec<Data>> {
|
async fn retrieve(id: &str, calls: usize, args: MultiValue<'_>) -> mlua::Result<Vec<Data>> {
|
||||||
let args = Sendable::values_to_vec(args)?;
|
let args = Sendable::values_to_vec(args)?;
|
||||||
let (tx, rx) = oneshot::channel::<Vec<Data>>();
|
let (tx, rx) = oneshot::channel::<Vec<Data>>();
|
||||||
|
|
||||||
let callback: OptCallback = {
|
let callback: PluginCallback = {
|
||||||
let name = name.to_owned();
|
let id = id.to_owned();
|
||||||
Box::new(move |lua, plugin| {
|
Box::new(move |lua, plugin| {
|
||||||
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.get_block(&name, calls) else {
|
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.get_block(&id, calls) else {
|
||||||
return Err("sync block not found".into_lua_err());
|
return Err("sync block not found".into_lua_err());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,13 +70,9 @@ impl Utils {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
emit!(Call(
|
AppProxy::plugin(PluginOpt::new_callback(id, callback));
|
||||||
Cmd::args("plugin", &[name]).with_bool("sync", true).with_any("callback", callback),
|
|
||||||
Layer::App
|
|
||||||
));
|
|
||||||
|
|
||||||
rx.await.map_err(|_| {
|
rx.await
|
||||||
format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err()
|
.map_err(|_| format!("Failed to execute sync block-{calls} in `{id}` plugin").into_lua_err())
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use tokio::sync::oneshot;
|
||||||
use yazi_macro::emit;
|
use yazi_macro::emit;
|
||||||
use yazi_shared::{Layer, event::Cmd};
|
use yazi_shared::{Layer, event::Cmd};
|
||||||
|
|
||||||
use crate::options::{NotifyLevel, NotifyOpt};
|
use crate::options::{NotifyLevel, NotifyOpt, PluginOpt};
|
||||||
|
|
||||||
pub struct AppProxy;
|
pub struct AppProxy;
|
||||||
|
|
||||||
|
|
@ -45,4 +45,14 @@ impl AppProxy {
|
||||||
timeout: Duration::from_secs(10),
|
timeout: Duration::from_secs(10),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn plugin(opt: PluginOpt) {
|
||||||
|
emit!(Call(Cmd::new("plugin").with_any("opt", opt), Layer::App));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn plugin_do(opt: PluginOpt) {
|
||||||
|
emit!(Call(Cmd::new("plugin_do").with_any("opt", opt), Layer::App));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(notify open process search);
|
yazi_macro::mod_flat!(notify open plugin process search);
|
||||||
|
|
|
||||||
83
yazi-proxy/src/options/plugin.rs
Normal file
83
yazi-proxy/src/options/plugin.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
use anyhow::bail;
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
use yazi_shared::event::{Cmd, Data};
|
||||||
|
|
||||||
|
pub type PluginCallback = Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PluginOpt {
|
||||||
|
pub id: String,
|
||||||
|
pub mode: PluginMode,
|
||||||
|
pub args: Vec<Data>,
|
||||||
|
pub cb: Option<PluginCallback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Cmd> for PluginOpt {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||||
|
if let Some(opt) = c.take_any("opt") {
|
||||||
|
return Ok(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(id) = c.take_first_str().filter(|s| !s.is_empty()) else {
|
||||||
|
bail!("plugin id cannot be empty");
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = if let Some(s) = c.str("args") {
|
||||||
|
shell_words::split(s)?.into_iter().map(Data::String).collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mode = c.str("mode").map(Into::into).unwrap_or_default();
|
||||||
|
if c.bool("sync") {
|
||||||
|
mode = PluginMode::Sync;
|
||||||
|
let s = "The `--sync` option for the `plugin` command has been deprecated in Yazi v0.4.
|
||||||
|
|
||||||
|
Please add `--- @sync entry` metadata at the head of your `{id}` plugin instead. See #1891 for details: https://github.com/sxyazi/yazi/pull/1891";
|
||||||
|
crate::AppProxy::notify(crate::options::NotifyOpt {
|
||||||
|
title: "Deprecated API".to_owned(),
|
||||||
|
content: s.replace("{id}", &id),
|
||||||
|
level: crate::options::NotifyLevel::Warn,
|
||||||
|
timeout: std::time::Duration::from_secs(20),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { id, mode, args, cb: c.take_any("callback") })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginOpt {
|
||||||
|
pub fn new_callback(id: &str, cb: PluginCallback) -> Self {
|
||||||
|
Self { id: id.to_owned(), mode: PluginMode::Sync, cb: Some(cb), ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Mode
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum PluginMode {
|
||||||
|
#[default]
|
||||||
|
Auto,
|
||||||
|
Sync,
|
||||||
|
Async,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for PluginMode {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
match s {
|
||||||
|
"sync" => Self::Sync,
|
||||||
|
"async" => Self::Async,
|
||||||
|
_ => Self::Auto,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginMode {
|
||||||
|
pub fn auto_then(self, sync: bool) -> Self {
|
||||||
|
if self != Self::Auto {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if sync { Self::Sync } else { Self::Async }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ impl Plugin {
|
||||||
|
|
||||||
if let Err(e) = isolate::entry(task.name, task.args).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 Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,7 @@ pub fn replace_to_printable(s: &[String], tab_size: u8) -> String {
|
||||||
match b {
|
match b {
|
||||||
b'\n' => buf.push(b'\n'),
|
b'\n' => buf.push(b'\n'),
|
||||||
b'\t' => {
|
b'\t' => {
|
||||||
for _ in 0..tab_size {
|
buf.extend((0..tab_size).map(|_| b' '));
|
||||||
buf.push(b' ');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
b'\0'..=b'\x1F' => {
|
b'\0'..=b'\x1F' => {
|
||||||
buf.push(b'^');
|
buf.push(b'^');
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,6 @@ impl Cmd {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn with_name(mut self, name: impl ToString) -> Self {
|
|
||||||
self.name = name.to_string();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Get
|
// --- Get
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self, name: &str) -> Option<&Data> { self.args.get(name) }
|
pub fn get(&self, name: &str) -> Option<&Data> { self.args.get(name) }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue