mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-23 07:41:03 +00:00
feat!: support passing arguments to previewer/preloader/spotter/fetcher (#1966)
This commit is contained in:
parent
37292adfde
commit
d72f90356b
55 changed files with 703 additions and 458 deletions
40
Cargo.lock
generated
40
Cargo.lock
generated
|
|
@ -1008,9 +1008,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
|
|
@ -1340,9 +1340,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
|
|
@ -1396,9 +1396,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.164"
|
||||
version = "0.2.166"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||
checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
|
|
@ -2430,9 +2430,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
|
|
@ -2733,9 +2733,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
|
|
@ -2756,9 +2756,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2767,9 +2767,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
|
|
@ -2962,9 +2962,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
|||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "9.0.1"
|
||||
version = "9.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349ed9e45296a581f455bc18039878f409992999bc1d5da12a6800eb18c8752f"
|
||||
checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
|
|
@ -2976,9 +2976,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "vergen-gitcl"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a3a7f91caabecefc3c249fd864b11d4abe315c166fbdb568964421bccfd2b7a"
|
||||
checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
|
|
@ -2990,9 +2990,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "vergen-lib"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229eaddb0050920816cf051e619affaf18caa3dd512de8de5839ccbc8e53abb0"
|
||||
checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ crossterm = { version = "0.28.1", features = [ "event-stream" ] }
|
|||
dirs = "5.0.1"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.15"
|
||||
libc = "0.2.164"
|
||||
libc = "0.2.166"
|
||||
md-5 = "0.10.6"
|
||||
mlua = { version = "0.10.1", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] }
|
||||
parking_lot = "0.12.3"
|
||||
|
|
@ -32,6 +32,6 @@ shell-words = "1.1.0"
|
|||
tokio = { version = "1.41.1", features = [ "full" ] }
|
||||
tokio-stream = "0.1.16"
|
||||
tokio-util = "0.7.12"
|
||||
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_debug" ] }
|
||||
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
|
||||
unicode-width = "0.2.0"
|
||||
uzers = "0.12.1"
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265
|
|||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images (#1786) |
|
||||
| [Mintty](https://github.com/mintty/mintty) (Git Bash) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ clap = { workspace = true }
|
|||
clap_complete = "4.5.38"
|
||||
clap_complete_fig = "4.5.2"
|
||||
clap_complete_nushell = "4.5.4"
|
||||
vergen-gitcl = { version = "1.0.1", features = [ "build", "rustc" ] }
|
||||
vergen-gitcl = { version = "1.0.2", features = [ "build", "rustc" ] }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ clap_complete = "4.5.38"
|
|||
clap_complete_fig = "4.5.2"
|
||||
clap_complete_nushell = "4.5.4"
|
||||
serde_json = { workspace = true }
|
||||
vergen-gitcl = { version = "1.0.1", features = [ "build" ] }
|
||||
vergen-gitcl = { version = "1.0.2", features = [ "build" ] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::VecDeque, ops::Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
use yazi_shared::event::CmdCow;
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ impl Default for ChordCow {
|
|||
}
|
||||
|
||||
impl ChordCow {
|
||||
pub fn into_seq(self) -> VecDeque<CmdCow> {
|
||||
pub fn into_seq(self) -> Vec<CmdCow> {
|
||||
match self {
|
||||
Self::Owned(c) => c.run.into_iter().map(|c| c.into()).collect(),
|
||||
Self::Borrowed(c) => c.run.iter().map(|c| c.into()).collect(),
|
||||
Self::Owned(c) => c.run.into_iter().rev().map(|c| c.into()).collect(),
|
||||
Self::Borrowed(c) => c.run.iter().rev().map(|c| c.into()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,16 +28,3 @@ impl Fetcher {
|
|||
|| self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetcherProps {
|
||||
pub id: u8,
|
||||
pub name: &'static str,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl From<&'static Fetcher> for FetcherProps {
|
||||
fn from(fetcher: &'static Fetcher) -> Self {
|
||||
Self { id: fetcher.idx, name: &fetcher.run.name, prio: fetcher.prio }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,16 +26,3 @@ impl Preloader {
|
|||
|| self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PreloaderProps {
|
||||
pub id: u8,
|
||||
pub name: &'static str,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl From<&'static Preloader> for PreloaderProps {
|
||||
fn from(preloader: &'static Preloader) -> Self {
|
||||
Self { id: preloader.idx, name: &preloader.run.name, prio: preloader.prio }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use yazi_fs::Folder;
|
|||
use yazi_macro::emit;
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_proxy::{ManagerProxy, TasksProxy, options::OpenDoOpt};
|
||||
use yazi_shared::{MIME_DIR, event::{CmdCow, EventQuit}, fs::{File, Url}};
|
||||
use yazi_shared::{MIME_DIR, event::{Cmd, CmdCow, EventQuit}, fs::{File, Url}};
|
||||
|
||||
use crate::{manager::Manager, tasks::Tasks};
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
done.extend(files.iter().map(|f| (f.url_owned(), String::new())));
|
||||
if let Err(e) = isolate::fetch("mime", files).await {
|
||||
if let Err(e) = isolate::fetch(Cmd::new("mime").into(), files).await {
|
||||
error!("Fetch `mime` failed in opening: {e}");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use tracing::error;
|
||||
use yazi_macro::render;
|
||||
|
|
@ -7,7 +7,7 @@ use yazi_shared::{event::CmdCow, fs::Url};
|
|||
use crate::{manager::{LINKED, Manager}, tasks::Tasks};
|
||||
|
||||
pub struct Opt {
|
||||
updates: HashMap<String, String>,
|
||||
updates: HashMap<Cow<'static, str>, String>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for Opt {
|
||||
|
|
@ -28,7 +28,7 @@ impl Manager {
|
|||
let updates = opt
|
||||
.updates
|
||||
.into_iter()
|
||||
.map(|(url, mime)| (Url::from(url), mime))
|
||||
.map(|(url, mime)| (Url::from(url.into_owned()), mime))
|
||||
.filter(|(url, mime)| self.mimetype.get(url) != Some(mime))
|
||||
.fold(HashMap::new(), |mut map, (u, m)| {
|
||||
for u in linked.from_file(&u) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use tracing::error;
|
|||
use yazi_fs::{Files, Folder};
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_proxy::WATCHER;
|
||||
use yazi_shared::{RoCell, fs::{Cha, File, FilesOp, Url, realname_unchecked}};
|
||||
use yazi_shared::{RoCell, event::Cmd, fs::{Cha, File, FilesOp, Url, realname_unchecked}};
|
||||
|
||||
use super::Linked;
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ impl Watcher {
|
|||
}
|
||||
|
||||
FilesOp::mutate(ops);
|
||||
if let Err(e) = isolate::fetch("mime", reload).await {
|
||||
if let Err(e) = isolate::fetch(Cmd::new("mime").into(), reload).await {
|
||||
error!("Fetch `mime` failed in watcher: {e}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ bitflags! {
|
|||
impl From<CmdCow> for Opt {
|
||||
fn from(c: CmdCow) -> Self {
|
||||
c.args.iter().fold(Opt::empty(), |acc, (k, v)| {
|
||||
match (k.as_str(), v.as_bool().unwrap_or(false)) {
|
||||
match (k.as_str().unwrap_or(""), v.as_bool().unwrap_or(false)) {
|
||||
("all", true) => Self::all(),
|
||||
("find", true) => acc | Self::FIND,
|
||||
("visual", true) => acc | Self::VISUAL,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
use yazi_shared::event::Data;
|
||||
use yazi_proxy::options::PluginOpt;
|
||||
|
||||
use super::Tasks;
|
||||
|
||||
impl Tasks {
|
||||
#[inline]
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
|
||||
self.scheduler.plugin_micro(name, args);
|
||||
}
|
||||
pub fn plugin_micro(&self, opt: PluginOpt) { self.scheduler.plugin_micro(opt); }
|
||||
|
||||
#[inline]
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
|
||||
self.scheduler.plugin_macro(name, args);
|
||||
}
|
||||
pub fn plugin_macro(&self, opt: PluginOpt) { self.scheduler.plugin_macro(opt); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ impl Tasks {
|
|||
let mime = if f.is_dir() { MIME_DIR } else { mimetype.get(&f.url).unwrap_or_default() };
|
||||
let factors = |s: &str| match s {
|
||||
"mime" => !mime.is_empty(),
|
||||
"dummy" => f.cha.is_dummy(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ tokio-util = { workspace = true }
|
|||
tracing = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gitcl = { version = "1.0.1", features = [ "build" ] }
|
||||
vergen-gitcl = { version = "1.0.2", features = [ "build" ] }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
uzers = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ impl IntoLua for BodyYank<'static> {
|
|||
}
|
||||
|
||||
// --- Iterator
|
||||
#[derive(Clone)]
|
||||
pub struct BodyYankIter {
|
||||
pub cut: bool,
|
||||
pub urls: Vec<Url>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use mlua::{ExternalError, Lua, MultiValue, Table, Value};
|
||||
use yazi_shared::{OrderedFloat, event::{Data, DataKey}};
|
||||
use yazi_shared::{OrderedFloat, event::{Data, DataKey}, replace_cow};
|
||||
|
||||
pub struct Sendable;
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ impl Sendable {
|
|||
Value::Nil => Data::Nil,
|
||||
Value::Boolean(b) => Data::Boolean(b),
|
||||
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
|
||||
Value::Integer(n) => Data::Integer(n),
|
||||
Value::Integer(i) => Data::Integer(i),
|
||||
Value::Number(n) => Data::Number(n),
|
||||
Value::String(s) => Data::String(s.to_str()?.to_owned()),
|
||||
Value::Table(t) => {
|
||||
|
|
@ -50,37 +50,112 @@ impl Sendable {
|
|||
|
||||
pub fn data_to_value(lua: &Lua, data: Data) -> mlua::Result<Value> {
|
||||
Ok(match data {
|
||||
Data::Nil => Value::Nil,
|
||||
Data::Boolean(v) => Value::Boolean(v),
|
||||
Data::Integer(v) => Value::Integer(v),
|
||||
Data::Number(v) => Value::Number(v),
|
||||
Data::String(v) => Value::String(lua.create_string(v)?),
|
||||
Data::List(v) => Value::Table(Self::list_to_table(lua, v)?),
|
||||
Data::Dict(t) => {
|
||||
let seq_len = t.keys().filter(|&k| !k.is_integer()).count();
|
||||
let table = lua.create_table_with_capacity(seq_len, t.len() - seq_len)?;
|
||||
for (k, v) in t {
|
||||
table.raw_set(Self::key_to_value(lua, k)?, Self::data_to_value(lua, v)?)?;
|
||||
Data::List(l) => {
|
||||
let mut vec = Vec::with_capacity(l.len());
|
||||
for v in l.into_iter() {
|
||||
vec.push(Self::data_to_value(lua, v)?);
|
||||
}
|
||||
Value::Table(table)
|
||||
Value::Table(lua.create_sequence_from(vec)?)
|
||||
}
|
||||
Data::Url(v) => Value::UserData(lua.create_any_userdata(v)?),
|
||||
Data::Any(v) => {
|
||||
if let Ok(t) = v.downcast::<super::body::BodyYankIter>() {
|
||||
Data::Dict(d) => {
|
||||
let seq_len = d.keys().filter(|&k| k.is_integer()).count();
|
||||
let tbl = lua.create_table_with_capacity(seq_len, d.len() - seq_len)?;
|
||||
for (k, v) in d {
|
||||
tbl.raw_set(Self::key_to_value(lua, k)?, Self::data_to_value(lua, v)?)?;
|
||||
}
|
||||
Value::Table(tbl)
|
||||
}
|
||||
Data::Url(u) => Value::UserData(lua.create_any_userdata(u)?),
|
||||
Data::Any(a) => {
|
||||
if let Ok(t) = a.downcast::<super::body::BodyYankIter>() {
|
||||
Value::UserData(lua.create_userdata(*t)?)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
data => Self::data_to_value_ref(lua, &data)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn data_to_value_ref(lua: &Lua, data: &Data) -> mlua::Result<Value> {
|
||||
Ok(match data {
|
||||
Data::Nil => Value::Nil,
|
||||
Data::Boolean(b) => Value::Boolean(*b),
|
||||
Data::Integer(i) => Value::Integer(*i),
|
||||
Data::Number(n) => Value::Number(*n),
|
||||
Data::String(s) => Value::String(lua.create_string(s)?),
|
||||
Data::List(l) => {
|
||||
let mut vec = Vec::with_capacity(l.len());
|
||||
for v in l {
|
||||
vec.push(Self::data_to_value_ref(lua, v)?);
|
||||
}
|
||||
Value::Table(lua.create_sequence_from(vec)?)
|
||||
}
|
||||
Data::Dict(d) => {
|
||||
let seq_len = d.keys().filter(|&k| k.is_integer()).count();
|
||||
let tbl = lua.create_table_with_capacity(seq_len, d.len() - seq_len)?;
|
||||
for (k, v) in d {
|
||||
tbl.raw_set(Self::key_to_value_ref(lua, k)?, Self::data_to_value_ref(lua, v)?)?;
|
||||
}
|
||||
Value::Table(tbl)
|
||||
}
|
||||
Data::Url(u) => Value::UserData(lua.create_any_userdata(u.clone())?),
|
||||
Data::Any(a) => {
|
||||
if let Some(t) = a.downcast_ref::<super::body::BodyYankIter>() {
|
||||
Value::UserData(lua.create_userdata(t.clone())?)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_to_table(lua: &Lua, data: Vec<Data>) -> mlua::Result<Table> {
|
||||
let mut vec = Vec::with_capacity(data.len());
|
||||
for v in data.into_iter() {
|
||||
vec.push(Self::data_to_value(lua, v)?);
|
||||
pub fn table_to_args(t: Table) -> mlua::Result<HashMap<DataKey, Data>> {
|
||||
let mut args = HashMap::with_capacity(t.raw_len());
|
||||
for pair in t.pairs::<Value, Value>() {
|
||||
let (k, v) = pair?;
|
||||
match k {
|
||||
Value::Integer(i) if i > 0 => {
|
||||
args.insert(DataKey::Integer(i - 1), Self::value_to_data(v)?);
|
||||
}
|
||||
Value::String(s) => {
|
||||
args.insert(
|
||||
DataKey::String(Cow::Owned(s.to_str()?.replace('_', "-"))),
|
||||
Self::value_to_data(v)?,
|
||||
);
|
||||
}
|
||||
_ => return Err("invalid key in Cmd".into_lua_err()),
|
||||
}
|
||||
}
|
||||
lua.create_sequence_from(vec)
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
pub fn args_to_table(lua: &Lua, args: HashMap<DataKey, Data>) -> mlua::Result<Table> {
|
||||
let seq_len = args.keys().filter(|&k| k.is_integer()).count();
|
||||
let tbl = lua.create_table_with_capacity(seq_len, args.len() - seq_len)?;
|
||||
for (k, v) in args {
|
||||
match k {
|
||||
DataKey::Integer(i) => tbl.raw_set(i + 1, Self::data_to_value(lua, v)?),
|
||||
DataKey::String(s) => tbl.raw_set(replace_cow(&s, "-", "_"), Self::data_to_value(lua, v)?),
|
||||
_ => Err("invalid key in Data".into_lua_err()),
|
||||
}?;
|
||||
}
|
||||
Ok(tbl)
|
||||
}
|
||||
|
||||
pub fn args_to_table_ref(lua: &Lua, args: &HashMap<DataKey, Data>) -> mlua::Result<Table> {
|
||||
let seq_len = args.keys().filter(|&k| k.is_integer()).count();
|
||||
let tbl = lua.create_table_with_capacity(seq_len, args.len() - seq_len)?;
|
||||
for (k, v) in args {
|
||||
match k {
|
||||
DataKey::Integer(i) => tbl.raw_set(i + 1, Self::data_to_value_ref(lua, v)?),
|
||||
DataKey::String(s) => {
|
||||
tbl.raw_set(replace_cow(s, "-", "_"), Self::data_to_value_ref(lua, v)?)
|
||||
}
|
||||
_ => Err("invalid key in Data".into_lua_err()),
|
||||
}?;
|
||||
}
|
||||
Ok(tbl)
|
||||
}
|
||||
|
||||
pub fn list_to_values(lua: &Lua, data: Vec<Data>) -> mlua::Result<MultiValue> {
|
||||
|
|
@ -91,14 +166,16 @@ impl Sendable {
|
|||
Ok(MultiValue::from_vec(vec))
|
||||
}
|
||||
|
||||
pub fn values_to_vec(values: MultiValue) -> mlua::Result<Vec<Data>> {
|
||||
pub fn values_to_list(values: MultiValue) -> mlua::Result<Vec<Data>> {
|
||||
let mut vec = Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
vec.push(Self::value_to_data(value)?);
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sendable {
|
||||
fn value_to_key(value: Value) -> mlua::Result<DataKey> {
|
||||
Ok(match value {
|
||||
Value::Nil => DataKey::Nil,
|
||||
|
|
@ -106,7 +183,7 @@ impl Sendable {
|
|||
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
|
||||
Value::Integer(v) => DataKey::Integer(v),
|
||||
Value::Number(v) => DataKey::Number(OrderedFloat::new(v)),
|
||||
Value::String(v) => DataKey::String(v.to_str()?.to_owned()),
|
||||
Value::String(v) => DataKey::String(Cow::Owned(v.to_str()?.to_owned())),
|
||||
Value::Table(_) => Err("table is not supported".into_lua_err())?,
|
||||
Value::Function(_) => Err("function is not supported".into_lua_err())?,
|
||||
Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
|
||||
|
|
@ -123,13 +200,20 @@ impl Sendable {
|
|||
}
|
||||
|
||||
fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> {
|
||||
Ok(match key {
|
||||
DataKey::Url(u) => Value::UserData(lua.create_any_userdata(u)?),
|
||||
key => Self::key_to_value_ref(lua, &key)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn key_to_value_ref(lua: &Lua, key: &DataKey) -> mlua::Result<Value> {
|
||||
Ok(match key {
|
||||
DataKey::Nil => Value::Nil,
|
||||
DataKey::Boolean(k) => Value::Boolean(k),
|
||||
DataKey::Integer(k) => Value::Integer(k),
|
||||
DataKey::Number(k) => Value::Number(k.get()),
|
||||
DataKey::String(k) => Value::String(lua.create_string(k)?),
|
||||
DataKey::Url(k) => Value::UserData(lua.create_any_userdata(k)?),
|
||||
DataKey::Boolean(b) => Value::Boolean(*b),
|
||||
DataKey::Integer(i) => Value::Integer(*i),
|
||||
DataKey::Number(n) => Value::Number(n.get()),
|
||||
DataKey::String(s) => Value::String(lua.create_string(s.as_ref())?),
|
||||
DataKey::Url(u) => Value::UserData(lua.create_any_userdata(u.clone())?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::VecDeque, sync::atomic::Ordering};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::event::KeyEvent;
|
||||
|
|
@ -71,9 +71,9 @@ impl App {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn dispatch_seq(&mut self, mut cmds: VecDeque<CmdCow>, layer: Layer) {
|
||||
if let Some(cmd) = cmds.pop_front() {
|
||||
Executor::new(self).execute(cmd, layer);
|
||||
fn dispatch_seq(&mut self, mut cmds: Vec<CmdCow>, layer: Layer) {
|
||||
if let Some(last) = cmds.pop() {
|
||||
Executor::new(self).execute(last, layer);
|
||||
}
|
||||
if !cmds.is_empty() {
|
||||
emit!(Seq(cmds, layer));
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl App {
|
|||
}
|
||||
|
||||
if opt.mode == PluginMode::Async {
|
||||
return self.cx.tasks.plugin_micro(opt.id.into_owned(), opt.args);
|
||||
return self.cx.tasks.plugin_micro(opt);
|
||||
} else if opt.mode == PluginMode::Sync && hits {
|
||||
return self.plugin_do(opt);
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ impl App {
|
|||
};
|
||||
|
||||
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
||||
return self.cx.tasks.plugin_micro(opt.id.into_owned(), opt.args);
|
||||
return self.cx.tasks.plugin_micro(opt);
|
||||
}
|
||||
|
||||
match LUA.named_registry_value::<RtRef>("rt") {
|
||||
|
|
@ -66,7 +66,12 @@ impl App {
|
|||
if let Some(cb) = opt.cb {
|
||||
cb(&LUA, plugin)
|
||||
} else {
|
||||
plugin.call_method("entry", Sendable::list_to_table(&LUA, opt.args)?)
|
||||
let job = LUA.create_table_from([("args", Sendable::args_to_table(&LUA, opt.args)?)])?;
|
||||
|
||||
// TODO: remove this
|
||||
yazi_plugin::isolate::install_entry_warn(&LUA, &job, opt._old_args).ok();
|
||||
|
||||
plugin.call_method("entry", job)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl<'a> Input<'a> {
|
|||
bail!("Highlighting is disabled");
|
||||
}
|
||||
|
||||
let (theme, syntaxes) = futures::executor::block_on(Highlighter::init());
|
||||
let (theme, syntaxes) = Highlighter::init();
|
||||
if let Some(syntax) = syntaxes.find_syntax_by_name("Bourne Again Shell (bash)") {
|
||||
let mut h = HighlightLines::new(syntax, theme);
|
||||
let regions = h.highlight_line(self.cx.input.value(), syntaxes)?;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local limit = self.area.h
|
||||
function M:peek(job)
|
||||
local limit = job.area.h
|
||||
local paths, sizes = {}, {}
|
||||
|
||||
local files, bound, code = self.list_files({ "-p", tostring(self.file.url) }, self.skip, limit)
|
||||
local files, bound, code = self.list_files({ "-p", tostring(job.file.url) }, job.skip, limit)
|
||||
if code ~= 0 then
|
||||
return ya.preview_widgets(self, {
|
||||
return ya.preview_widgets(job, {
|
||||
ui.Text(
|
||||
code == 2 and "File list in this archive is encrypted"
|
||||
or "Failed to start both `7z` and `7zz`. Do you have 7-zip installed?"
|
||||
):area(self.area),
|
||||
):area(job.area),
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -33,17 +33,17 @@ function M:peek()
|
|||
end
|
||||
end
|
||||
|
||||
if self.skip > 0 and bound < self.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, bound - limit), only_if = self.file.url, upper_bound = true })
|
||||
if job.skip > 0 and bound < job.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true })
|
||||
else
|
||||
ya.preview_widgets(self, {
|
||||
ui.Text(paths):area(self.area),
|
||||
ui.Text(sizes):area(self.area):align(ui.Text.RIGHT),
|
||||
ya.preview_widgets(job, {
|
||||
ui.Text(paths):area(job.area),
|
||||
ui.Text(sizes):area(job.area):align(ui.Text.RIGHT),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(units) require("code").seek(self, units) end
|
||||
function M:seek(job) require("code"):seek(job) end
|
||||
|
||||
function M.spawn_7z(args)
|
||||
local last_err = nil
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local err, bound = ya.preview_code(self)
|
||||
function M:peek(job)
|
||||
local err, bound = ya.preview_code(job)
|
||||
if bound then
|
||||
ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
|
||||
ya.manager_emit("peek", { bound, only_if = job.file.url, upper_bound = true })
|
||||
elseif err and not err:find("cancelled", 1, true) then
|
||||
ya.preview_widgets(self, {
|
||||
ui.Text(err):area(self.area):reverse(),
|
||||
ya.preview_widgets(job, {
|
||||
ui.Text(err):area(job.area):reverse(),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(units)
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if not h or h.url ~= self.file.url then
|
||||
if not h or h.url ~= job.file.url then
|
||||
return
|
||||
end
|
||||
|
||||
local step = math.floor(units * self.area.h / 10)
|
||||
step = step == 0 and ya.clamp(-1, units, 1) or step
|
||||
local step = math.floor(job.units * job.area.h / 10)
|
||||
step = step == 0 and ya.clamp(-1, job.units, 1) or step
|
||||
|
||||
ya.manager_emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + step),
|
||||
only_if = self.file.url,
|
||||
only_if = job.file.url,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
local M = {}
|
||||
|
||||
function M:msg(s) ya.preview_widgets(self, { ui.Text(s):area(self.area):reverse():wrap(ui.Text.WRAP) }) end
|
||||
function M:msg(job, s) ya.preview_widgets(job, { ui.Text(s):area(job.area):reverse():wrap(ui.Text.WRAP) }) end
|
||||
|
||||
function M:peek()
|
||||
local path = tostring(self.file.url)
|
||||
function M:peek(job)
|
||||
local path = tostring(job.file.url)
|
||||
if path:sub(1, 6) ~= "/proc/" then
|
||||
return self:msg("Empty file")
|
||||
return self:msg(job, "Empty file")
|
||||
end
|
||||
|
||||
local limit = self.area.h
|
||||
local limit = job.area.h
|
||||
local i, lines = 0, {}
|
||||
local ok, err = pcall(function()
|
||||
for line in io.lines(path) do
|
||||
i = i + 1
|
||||
if i > self.skip + limit then
|
||||
if i > job.skip + limit then
|
||||
break
|
||||
elseif i > self.skip then
|
||||
elseif i > job.skip then
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
self:msg(err)
|
||||
elseif self.skip > 0 and i < self.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true })
|
||||
self:msg(job, err)
|
||||
elseif job.skip > 0 and i < job.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
|
||||
else
|
||||
ya.preview_widgets(self, { ui.Text(lines):area(self.area) })
|
||||
ya.preview_widgets(job, { ui.Text(lines):area(job.area) })
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(units) require("code").seek(self, units) end
|
||||
function M:seek(job) require("code"):seek(job) end
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -11,17 +11,19 @@ function M:setup()
|
|||
end)
|
||||
end
|
||||
|
||||
function M:entry(args)
|
||||
if not args[1] then
|
||||
function M:entry(job)
|
||||
local from = job.args[1] and Url(job.args[1])
|
||||
local to = job.args[2] ~= "" and Url(job.args[2]) or nil
|
||||
if not from then
|
||||
fail("No URL provided")
|
||||
end
|
||||
|
||||
local from, to, pwd = Url(args[1]), args[2] ~= "" and Url(args[2]) or nil, ""
|
||||
local pwd = ""
|
||||
while true do
|
||||
if not M:try_with(from, pwd, to) then
|
||||
break
|
||||
elseif args[3] ~= "--noisy" then
|
||||
fail("'%s' is password-protected, please extract it individually and enter the password", args[1])
|
||||
elseif not job.args.noisy then
|
||||
fail("'%s' is password-protected, please extract it individually and enter the password", from)
|
||||
end
|
||||
|
||||
local value, event = ya.input {
|
||||
|
|
@ -78,7 +80,7 @@ function M:tidy(from, to, tmp)
|
|||
|
||||
local only = #outs == 1
|
||||
if only and not outs[1].cha.is_dir and require("archive").is_tar(outs[1].url) then
|
||||
self:entry { tostring(outs[1].url), tostring(to) }
|
||||
self:entry { args = { tostring(outs[1].url), tostring(to) } }
|
||||
fs.remove("file", outs[1].url)
|
||||
fs.remove("dir", tmp)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
function M:peek(job)
|
||||
local cmd = os.getenv("YAZI_FILE_ONE") or "file"
|
||||
local output, err = Command(cmd):args({ "-bL", "--", tostring(self.file.url) }):stdout(Command.PIPED):output()
|
||||
local output, err = Command(cmd):args({ "-bL", "--", tostring(job.file.url) }):stdout(Command.PIPED):output()
|
||||
|
||||
local text
|
||||
if output then
|
||||
|
|
@ -11,7 +11,7 @@ function M:peek()
|
|||
text = ui.Text(string.format("Failed to start `%s`, error: %s", cmd, err))
|
||||
end
|
||||
|
||||
ya.preview_widgets(self, { text:area(self.area):wrap(ui.Text.WRAP) })
|
||||
ya.preview_widgets(job, { text:area(job.area):wrap(ui.Text.WRAP) })
|
||||
end
|
||||
|
||||
function M:seek() end
|
||||
|
|
@ -33,7 +33,7 @@ function M:spot_base(job)
|
|||
local url, cha = job.file.url, job.file.cha
|
||||
local spotter = PLUGIN.spotter(url, job.mime)
|
||||
local previewer = PLUGIN.previewer(url, job.mime)
|
||||
local fetchers = PLUGIN.fetchers(url, job.mime)
|
||||
local fetchers = PLUGIN.fetchers(job.file, job.mime)
|
||||
local preloaders = PLUGIN.preloaders(url, job.mime)
|
||||
|
||||
for i, v in ipairs(fetchers) do
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
function M:peek(job)
|
||||
local folder = cx.active.preview.folder
|
||||
if not folder or folder.cwd ~= self.file.url then
|
||||
if not folder or folder.cwd ~= job.file.url then
|
||||
return
|
||||
end
|
||||
|
||||
local bound = math.max(0, #folder.files - self.area.h)
|
||||
if self.skip > bound then
|
||||
return ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
|
||||
local bound = math.max(0, #folder.files - job.area.h)
|
||||
if job.skip > bound then
|
||||
return ya.manager_emit("peek", { bound, only_if = job.file.url, upper_bound = true })
|
||||
end
|
||||
|
||||
if #folder.files == 0 then
|
||||
return ya.preview_widgets(self, {
|
||||
ui.Text(folder.stage.is_loading and "Loading..." or "No items"):area(self.area):align(ui.Text.CENTER),
|
||||
return ya.preview_widgets(job, {
|
||||
ui.Text(folder.stage.is_loading and "Loading..." or "No items"):area(job.area):align(ui.Text.CENTER),
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -22,20 +22,20 @@ function M:peek()
|
|||
entities[#entities + 1] = Entity:new(f):redraw()
|
||||
end
|
||||
|
||||
ya.preview_widgets(self, {
|
||||
ui.List(entities):area(self.area),
|
||||
table.unpack(Marker:new(self.area, folder):redraw()),
|
||||
ya.preview_widgets(job, {
|
||||
ui.List(entities):area(job.area),
|
||||
table.unpack(Marker:new(job.area, folder):redraw()),
|
||||
})
|
||||
end
|
||||
|
||||
function M:seek(units)
|
||||
function M:seek(job)
|
||||
local folder = cx.active.preview.folder
|
||||
if folder and folder.cwd == self.file.url then
|
||||
local step = math.floor(units * self.area.h / 10)
|
||||
local bound = math.max(0, #folder.files - self.area.h)
|
||||
if folder and folder.cwd == job.file.url then
|
||||
local step = math.floor(job.units * job.area.h / 10)
|
||||
local bound = math.max(0, #folder.files - job.area.h)
|
||||
ya.manager_emit("peek", {
|
||||
ya.clamp(0, cx.active.preview.skip + step, bound),
|
||||
only_if = self.file.url,
|
||||
only_if = job.file.url,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,21 +2,21 @@ local TEXT = "ABCDEFGHIJKLM\nNOPQRSTUVWXYZ\nabcdefghijklm\nnopqrstuvwxyz\n123456
|
|||
|
||||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local start, cache = os.clock(), ya.file_cache(self)
|
||||
if not cache or self:preload() ~= 1 then
|
||||
function M:peek(job)
|
||||
local start, cache = os.clock(), ya.file_cache(job)
|
||||
if not cache or self:preload(job) ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.image_show(cache, self.area)
|
||||
ya.preview_widgets(self, {})
|
||||
ya.image_show(cache, job.area)
|
||||
ya.preview_widgets(job, {})
|
||||
end
|
||||
|
||||
function M:seek() end
|
||||
|
||||
function M:preload()
|
||||
local cache = ya.file_cache(self)
|
||||
function M:preload(job)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
|
|
@ -27,7 +27,7 @@ function M:preload()
|
|||
"-gravity",
|
||||
"center",
|
||||
"-font",
|
||||
tostring(self.file.url),
|
||||
tostring(job.file.url),
|
||||
"-pointsize",
|
||||
"64",
|
||||
"xc:white",
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local start, url = os.clock(), ya.file_cache(self)
|
||||
function M:peek(job)
|
||||
local start, url = os.clock(), ya.file_cache(job)
|
||||
if not url or not fs.cha(url) then
|
||||
url = self.file.url
|
||||
url = job.file.url
|
||||
end
|
||||
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.image_show(url, self.area)
|
||||
ya.preview_widgets(self, {})
|
||||
ya.image_show(url, job.area)
|
||||
ya.preview_widgets(job, {})
|
||||
end
|
||||
|
||||
function M:seek() end
|
||||
|
||||
function M:preload()
|
||||
local cache = ya.file_cache(self)
|
||||
function M:preload(job)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
|
||||
return ya.image_precache(self.file.url, cache) and 1 or 2
|
||||
return ya.image_precache(job.file.url, cache) and 1 or 2
|
||||
end
|
||||
|
||||
function M:spot(job)
|
||||
|
|
|
|||
|
|
@ -1,46 +1,46 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
function M:peek(job)
|
||||
local child = Command("jq")
|
||||
:args({
|
||||
"-C",
|
||||
"--tab",
|
||||
".",
|
||||
tostring(self.file.url),
|
||||
tostring(job.file.url),
|
||||
})
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:spawn()
|
||||
|
||||
if not child then
|
||||
return require("code").peek(self)
|
||||
return require("code"):peek(job)
|
||||
end
|
||||
|
||||
local limit = self.area.h
|
||||
local limit = job.area.h
|
||||
local i, lines = 0, ""
|
||||
repeat
|
||||
local next, event = child:read_line()
|
||||
if event == 1 then
|
||||
return require("code").peek(self)
|
||||
return require("code"):peek(job)
|
||||
elseif event ~= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
if i > self.skip then
|
||||
if i > job.skip then
|
||||
lines = lines .. next
|
||||
end
|
||||
until i >= self.skip + limit
|
||||
until i >= job.skip + limit
|
||||
|
||||
child:start_kill()
|
||||
if self.skip > 0 and i < self.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true })
|
||||
if job.skip > 0 and i < job.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
|
||||
else
|
||||
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
|
||||
ya.preview_widgets(self, { ui.Text.parse(lines):area(self.area) })
|
||||
ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) })
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(units) require("code").seek(self, units) end
|
||||
function M:seek(job) require("code"):seek(job) end
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local start, cache = os.clock(), ya.file_cache(self)
|
||||
if not cache or self:preload() ~= 1 then
|
||||
function M:peek(job)
|
||||
local start, cache = os.clock(), ya.file_cache(job)
|
||||
if not cache or self:preload(job) ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.image_show(cache, self.area)
|
||||
ya.preview_widgets(self, {})
|
||||
ya.image_show(cache, job.area)
|
||||
ya.preview_widgets(job, {})
|
||||
end
|
||||
|
||||
function M:seek() end
|
||||
|
||||
function M:preload()
|
||||
local cache = ya.file_cache(self)
|
||||
function M:preload(job)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
|
|
@ -22,7 +22,7 @@ function M:preload()
|
|||
local status, err = Command("magick"):args({
|
||||
"-density",
|
||||
"200",
|
||||
tostring(self.file.url),
|
||||
tostring(job.file.url),
|
||||
"-flatten",
|
||||
"-resize",
|
||||
string.format("%dx%d^", PREVIEW.max_width, PREVIEW.max_height),
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local start, cache = os.clock(), ya.file_cache(self)
|
||||
if not cache or self:preload() ~= 1 then
|
||||
function M:peek(job)
|
||||
local start, cache = os.clock(), ya.file_cache(job)
|
||||
if not cache or self:preload(job) ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.image_show(cache, self.area)
|
||||
ya.preview_widgets(self, {})
|
||||
ya.image_show(cache, job.area)
|
||||
ya.preview_widgets(job, {})
|
||||
end
|
||||
|
||||
function M:seek(units)
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == self.file.url then
|
||||
local step = ya.clamp(-1, units, 1)
|
||||
ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = self.file.url })
|
||||
if h and h.url == job.file.url then
|
||||
local step = ya.clamp(-1, job.units, 1)
|
||||
ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = job.file.url })
|
||||
end
|
||||
end
|
||||
|
||||
function M:preload()
|
||||
local cache = ya.file_cache(self)
|
||||
function M:preload(job)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
|
||||
local output = Command("pdftoppm")
|
||||
:args({ "-singlefile", "-jpeg", "-jpegopt", "quality=75", "-f", tostring(self.skip + 1), tostring(self.file.url) })
|
||||
:args({ "-singlefile", "-jpeg", "-jpegopt", "quality=75", "-f", tostring(job.skip + 1), tostring(job.file.url) })
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:output()
|
||||
|
|
@ -35,8 +35,8 @@ function M:preload()
|
|||
return 0
|
||||
elseif not output.status.success then
|
||||
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
|
||||
if self.skip > 0 and pages > 0 then
|
||||
ya.manager_emit("peek", { math.max(0, pages - 1), only_if = self.file.url, upper_bound = true })
|
||||
if job.skip > 0 and pages > 0 then
|
||||
ya.manager_emit("peek", { math.max(0, pages - 1), only_if = job.file.url, upper_bound = true })
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local start, cache = os.clock(), ya.file_cache(self)
|
||||
if not cache or self:preload() ~= 1 then
|
||||
function M:peek(job)
|
||||
local start, cache = os.clock(), ya.file_cache(job)
|
||||
if not cache or self:preload(job) ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.image_show(cache, self.area)
|
||||
ya.preview_widgets(self, {})
|
||||
ya.image_show(cache, job.area)
|
||||
ya.preview_widgets(job, {})
|
||||
end
|
||||
|
||||
function M:seek(units)
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == self.file.url then
|
||||
if h and h.url == job.file.url then
|
||||
ya.manager_emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + units),
|
||||
only_if = self.file.url,
|
||||
math.max(0, cx.active.preview.skip + job.units),
|
||||
only_if = job.file.url,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:preload()
|
||||
local percent = 5 + self.skip
|
||||
function M:preload(job)
|
||||
local percent = 5 + job.skip
|
||||
if percent > 95 then
|
||||
ya.manager_emit("peek", { 90, only_if = self.file.url, upper_bound = true })
|
||||
ya.manager_emit("peek", { 90, only_if = job.file.url, upper_bound = true })
|
||||
return 2
|
||||
end
|
||||
|
||||
local cache = ya.file_cache(self)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache then
|
||||
return 1
|
||||
end
|
||||
|
|
@ -38,7 +38,7 @@ function M:preload()
|
|||
return 1
|
||||
end
|
||||
|
||||
local meta, err = self.list_meta(self.file.url, "format=duration")
|
||||
local meta, err = self.list_meta(job.file.url, "format=duration")
|
||||
if not meta then
|
||||
ya.err(tostring(err))
|
||||
return 0
|
||||
|
|
@ -53,7 +53,7 @@ function M:preload()
|
|||
"-v", "quiet", "-hwaccel", "auto",
|
||||
"-skip_frame", "nokey", "-ss", ss,
|
||||
"-an", "-sn", "-dn",
|
||||
"-i", tostring(self.file.url),
|
||||
"-i", tostring(job.file.url),
|
||||
"-vframes", 1,
|
||||
"-q:v", qv,
|
||||
"-vf", string.format("scale=%d:-2:flags=fast_bilinear", PREVIEW.max_width),
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
use mlua::{Function, Lua, UserData};
|
||||
use yazi_config::PLUGIN;
|
||||
|
||||
use crate::url::UrlRef;
|
||||
use crate::{file::FileRef, url::UrlRef};
|
||||
|
||||
pub(super) struct Plugin;
|
||||
|
||||
impl Plugin {
|
||||
pub(super) fn fetchers(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| {
|
||||
lua.create_function(|lua, (file, mime): (FileRef, mlua::String)| {
|
||||
let factors = |s: &str| match s {
|
||||
"mime" => !mime.as_bytes().is_empty(),
|
||||
"dummy" => file.cha.is_dummy(),
|
||||
_ => false,
|
||||
};
|
||||
lua.create_sequence_from(PLUGIN.fetchers(&url, &mime.to_str()?, factors).map(Fetcher))
|
||||
lua.create_sequence_from(PLUGIN.fetchers(&file.url, &mime.to_str()?, factors).map(Fetcher))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ use mlua::{FromLua, Lua, MetaMethod, MultiValue, Table, Value};
|
|||
use super::Rect;
|
||||
use crate::RtRef;
|
||||
|
||||
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
#[inline]
|
||||
fn warn_deprecated(id: Option<&str>) {
|
||||
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) {
|
||||
let id = match id {
|
||||
Some(id) => format!("`{id}.yazi` plugin"),
|
||||
|
|
|
|||
29
yazi-plugin/src/external/highlighter.rs
vendored
29
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -1,14 +1,14 @@
|
|||
use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}};
|
||||
use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}, sync::OnceLock};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use ratatui::{layout::Size, text::{Line, Span, Text}};
|
||||
use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}};
|
||||
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}, sync::OnceCell};
|
||||
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}};
|
||||
use yazi_config::{PREVIEW, THEME, preview::PreviewWrap};
|
||||
use yazi_shared::{Ids, errors::PeekError, replace_to_printable};
|
||||
|
||||
static INCR: Ids = Ids::new();
|
||||
static SYNTECT: OnceCell<(Theme, SyntaxSet)> = OnceCell::const_new();
|
||||
static SYNTECT: OnceLock<(Theme, SyntaxSet)> = OnceLock::new();
|
||||
|
||||
pub struct Highlighter {
|
||||
path: PathBuf,
|
||||
|
|
@ -18,21 +18,19 @@ impl Highlighter {
|
|||
#[inline]
|
||||
pub fn new(path: &Path) -> Self { Self { path: path.to_owned() } }
|
||||
|
||||
pub async fn init() -> (&'static Theme, &'static SyntaxSet) {
|
||||
pub fn init() -> (&'static Theme, &'static SyntaxSet) {
|
||||
let f = || {
|
||||
tokio::task::spawn_blocking(|| {
|
||||
let theme = std::fs::File::open(&THEME.manager.syntect_theme)
|
||||
.map_err(LoadingError::Io)
|
||||
.and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f)))
|
||||
.or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme())));
|
||||
let theme = std::fs::File::open(&THEME.manager.syntect_theme)
|
||||
.map_err(LoadingError::Io)
|
||||
.and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f)))
|
||||
.or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme())));
|
||||
|
||||
let syntaxes = dumps::from_uncompressed_data(yazi_prebuild::syntaxes());
|
||||
let syntaxes = dumps::from_uncompressed_data(yazi_prebuild::syntaxes());
|
||||
|
||||
(theme.unwrap(), syntaxes.unwrap())
|
||||
})
|
||||
(theme.unwrap(), syntaxes.unwrap())
|
||||
};
|
||||
|
||||
let (theme, syntaxes) = SYNTECT.get_or_try_init(f).await.unwrap();
|
||||
let (theme, syntaxes) = SYNTECT.get_or_init(f);
|
||||
(theme, syntaxes)
|
||||
}
|
||||
|
||||
|
|
@ -103,10 +101,11 @@ impl Highlighter {
|
|||
syntax: &'static SyntaxReference,
|
||||
) -> Result<Text<'static>, PeekError> {
|
||||
let ticket = INCR.current();
|
||||
let (theme, syntaxes) = Self::init().await;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let (theme, syntaxes) = Self::init();
|
||||
let mut h = HighlightLines::new(syntax, theme);
|
||||
|
||||
for line in before {
|
||||
if ticket != INCR.current() {
|
||||
return Err("Highlighting cancelled".into());
|
||||
|
|
@ -131,7 +130,7 @@ impl Highlighter {
|
|||
}
|
||||
|
||||
async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
|
||||
let (_, syntaxes) = Self::init().await;
|
||||
let (_, syntaxes) = Self::init();
|
||||
let name = path.file_name().map(|n| n.to_string_lossy()).unwrap_or_default();
|
||||
if let Some(s) = syntaxes.find_syntax_by_extension(&name) {
|
||||
return Ok(s);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,75 @@
|
|||
use mlua::{ExternalError, ExternalResult, ObjectLike, Table};
|
||||
use std::time::Duration;
|
||||
|
||||
use mlua::{ExternalError, ExternalResult, Lua, MetaMethod, ObjectLike, Table, Value};
|
||||
use tokio::runtime::Handle;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_proxy::options::PluginOpt;
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::loader::LOADER;
|
||||
use crate::{RtRef, loader::LOADER};
|
||||
|
||||
pub async fn entry(name: String, args: Vec<Data>) -> mlua::Result<()> {
|
||||
LOADER.ensure(&name).await.into_lua_err()?;
|
||||
pub async fn entry(opt: PluginOpt) -> mlua::Result<()> {
|
||||
LOADER.ensure(&opt.id).await.into_lua_err()?;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let lua = slim_lua(&name)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||
let lua = slim_lua(&opt.id)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(opt.id.as_ref()) {
|
||||
lua.load(b.as_bytes()).set_name(opt.id.as_ref()).call(())?
|
||||
} else {
|
||||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
|
||||
Handle::current()
|
||||
.block_on(plugin.call_async_method("entry", Sendable::list_to_table(&lua, args)))
|
||||
let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?;
|
||||
|
||||
// TODO: remove this
|
||||
install_entry_warn(&lua, &job, opt._old_args).ok();
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("entry", job))
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn warn_deprecated(id: Option<&str>) {
|
||||
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) {
|
||||
let id = match id {
|
||||
Some(id) => format!("`{id}.yazi` plugin"),
|
||||
None => "`init.lua` config".to_owned(),
|
||||
};
|
||||
let s = "The first parameter of `entry()` has been replaced by the new `job` instead of the previous `args`.
|
||||
|
||||
Please use `job.args` instead `args` to access the arguments in your {id}.
|
||||
|
||||
See #1966 for details: https://github.com/sxyazi/yazi/pull/1966";
|
||||
yazi_proxy::AppProxy::notify(yazi_proxy::options::NotifyOpt {
|
||||
title: "Deprecated API".to_owned(),
|
||||
content: s.replace("{id}", &id),
|
||||
level: yazi_proxy::options::NotifyLevel::Warn,
|
||||
timeout: Duration::from_secs(20),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_entry_warn(lua: &Lua, job: &Table, old_args: Vec<Data>) -> mlua::Result<()> {
|
||||
let mt = lua.create_table_from([(
|
||||
MetaMethod::Index.name(),
|
||||
lua.create_function(|lua, (ts, key): (Table, mlua::String)| {
|
||||
if key.as_bytes().first().is_some_and(|&b| b.is_ascii_digit()) {
|
||||
warn_deprecated(lua.named_registry_value::<RtRef>("rt")?.current());
|
||||
lua
|
||||
.load(mlua::chunk! {
|
||||
return rawget($ts, "__unsafe_args")[tonumber($key)]
|
||||
})
|
||||
.call::<Value>(())
|
||||
} else {
|
||||
ts.raw_get::<Value>(key)
|
||||
}
|
||||
})?,
|
||||
)])?;
|
||||
job.set_metatable(Some(mt));
|
||||
job.raw_set("__unsafe_args", Sendable::data_to_value(lua, Data::List(old_args))?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
use mlua::{ExternalError, ExternalResult, IntoLua, ObjectLike, Table};
|
||||
use tokio::runtime::Handle;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::CmdCow;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER};
|
||||
|
||||
pub async fn fetch(name: &str, files: Vec<yazi_shared::fs::File>) -> mlua::Result<u8> {
|
||||
pub async fn fetch(cmd: CmdCow, files: Vec<yazi_shared::fs::File>) -> mlua::Result<u8> {
|
||||
if files.is_empty() {
|
||||
return Ok(1);
|
||||
}
|
||||
LOADER.ensure(name).await.into_lua_err()?;
|
||||
LOADER.ensure(&cmd.name).await.into_lua_err()?;
|
||||
|
||||
let name = name.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let lua = slim_lua(&name)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||
let lua = slim_lua(&cmd.name)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
|
||||
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
|
||||
} else {
|
||||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
|
|
@ -31,6 +32,7 @@ pub async fn fetch(name: &str, files: Vec<yazi_shared::fs::File>) -> mlua::Resul
|
|||
"fetch",
|
||||
lua.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?),
|
||||
("files", files.into_lua(&lua)?),
|
||||
])?,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, time::Duration};
|
||||
|
||||
use mlua::{ExternalError, ExternalResult, HookTriggers, ObjectLike, Table, VmState};
|
||||
use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, Table, VmState};
|
||||
use tokio::{runtime::Handle, select};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::{LUA, bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER};
|
||||
use crate::{RtRef, bindings::Cast, elements::Rect, file::File, loader::LOADER};
|
||||
|
||||
pub fn peek(
|
||||
cmd: &Cmd,
|
||||
cmd: &'static Cmd,
|
||||
file: yazi_shared::fs::File,
|
||||
mime: Cow<'static, str>,
|
||||
skip: usize,
|
||||
|
|
@ -20,12 +21,11 @@ pub fn peek(
|
|||
let ct = CancellationToken::new();
|
||||
let (ct1, ct2) = (ct.clone(), ct.clone());
|
||||
|
||||
let name = cmd.name.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let future = async {
|
||||
LOADER.ensure(&name).await.into_lua_err()?;
|
||||
LOADER.ensure(&cmd.name).await.into_lua_err()?;
|
||||
|
||||
let lua = slim_lua(&name)?;
|
||||
let lua = slim_lua(&cmd.name)?;
|
||||
lua.set_hook(
|
||||
HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000),
|
||||
move |_, dbg| {
|
||||
|
|
@ -37,20 +37,24 @@ pub fn peek(
|
|||
},
|
||||
);
|
||||
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
|
||||
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
|
||||
} else {
|
||||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
plugin.raw_set("file", File::cast(&lua, file)?)?;
|
||||
plugin.raw_set("mime", mime)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
// TODO: remove this as it's not safe in async context,
|
||||
// there may be race conditions between the `window` and `area`
|
||||
plugin.raw_set("window", Window::get())?;
|
||||
|
||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", ()).await }
|
||||
let job = lua.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?),
|
||||
("file", File::cast(&lua, file)?.into_lua(&lua)?),
|
||||
("mime", mime.into_lua(&lua)?),
|
||||
("skip", skip.into_lua(&lua)?),
|
||||
])?;
|
||||
|
||||
// TODO: remove this
|
||||
install_warn_mt(&lua, &plugin, job.clone()).ok();
|
||||
|
||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", job).await }
|
||||
};
|
||||
|
||||
let result = Handle::current().block_on(async {
|
||||
|
|
@ -70,17 +74,73 @@ pub fn peek(
|
|||
ct
|
||||
}
|
||||
|
||||
pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>, skip: usize) {
|
||||
let cb: PluginCallback = Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("mime", mime)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
// TODO: remove this as it's not safe in async context,
|
||||
// there may be race conditions between the `window` and `area`
|
||||
plugin.raw_set("window", Window::get())?;
|
||||
plugin.call_method("peek", ())
|
||||
pub fn peek_sync(
|
||||
cmd: &'static Cmd,
|
||||
file: yazi_shared::fs::File,
|
||||
mime: Cow<'static, str>,
|
||||
skip: usize,
|
||||
) {
|
||||
let cb: PluginCallback = Box::new(move |lua, plugin| {
|
||||
let job = lua.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
|
||||
("args", Sendable::args_to_table_ref(lua, &cmd.args)?.into_lua(lua)?),
|
||||
("file", File::cast(lua, file)?.into_lua(lua)?),
|
||||
("mime", mime.into_lua(lua)?),
|
||||
("skip", skip.into_lua(lua)?),
|
||||
])?;
|
||||
|
||||
// TODO: remove this
|
||||
install_warn_mt(lua, &plugin, job.clone()).ok();
|
||||
|
||||
plugin.call_method("peek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||
}
|
||||
|
||||
pub(super) fn install_warn_mt(lua: &Lua, plugin: &Table, job: Table) -> mlua::Result<()> {
|
||||
let mt = lua.create_table_from([(
|
||||
MetaMethod::Index.name(),
|
||||
lua.create_function(|lua, (ts, key): (Table, mlua::String)| {
|
||||
let b = key.as_bytes() == b"file"
|
||||
|| key.as_bytes() == b"mime"
|
||||
|| key.as_bytes() == b"skip"
|
||||
|| key.as_bytes() == b"area";
|
||||
if b {
|
||||
warn_deprecated(lua.named_registry_value::<RtRef>("rt")?.current());
|
||||
}
|
||||
lua
|
||||
.load(mlua::chunk! {
|
||||
if $b then
|
||||
return rawget($ts, "__unsafe_job")[$key]
|
||||
else
|
||||
return rawget($ts, $key)
|
||||
end
|
||||
})
|
||||
.call::<MultiValue>(())
|
||||
})?,
|
||||
)])?;
|
||||
plugin.set_metatable(Some(mt));
|
||||
plugin.raw_set("__unsafe_job", job.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn warn_deprecated(id: Option<&str>) {
|
||||
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) {
|
||||
let id = match id {
|
||||
Some(id) => format!("`{id}.yazi` plugin"),
|
||||
None => "`init.lua` config".to_owned(),
|
||||
};
|
||||
let s = "The file list for peek() and preload() method has been moved from `self` to the first parameter `job` of the method to avoid conflicts with the plugin's own attributes.
|
||||
|
||||
Please use the new `job` parameter in your {id} instead of `self`. See #1966 for details: https://github.com/sxyazi/yazi/pull/1966";
|
||||
yazi_proxy::AppProxy::notify(yazi_proxy::options::NotifyOpt {
|
||||
title: "Deprecated API".to_owned(),
|
||||
content: s.replace("{id}", &id),
|
||||
level: yazi_proxy::options::NotifyLevel::Warn,
|
||||
timeout: Duration::from_secs(20),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,34 @@
|
|||
use mlua::{ExternalError, ExternalResult, ObjectLike, Table};
|
||||
use mlua::{ExternalError, ExternalResult, IntoLua, ObjectLike, Table};
|
||||
use tokio::runtime::Handle;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER};
|
||||
|
||||
pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result<u8> {
|
||||
LOADER.ensure(name).await.into_lua_err()?;
|
||||
pub async fn preload(cmd: &'static Cmd, file: yazi_shared::fs::File) -> mlua::Result<u8> {
|
||||
LOADER.ensure(&cmd.name).await.into_lua_err()?;
|
||||
|
||||
let name = name.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let lua = slim_lua(&name)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||
let lua = slim_lua(&cmd.name)?;
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
|
||||
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
|
||||
} else {
|
||||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
|
||||
plugin.raw_set("skip", 0)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.raw_set("file", File::cast(&lua, file)?)?;
|
||||
let job = lua.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?),
|
||||
("file", File::cast(&lua, file)?.into_lua(&lua)?),
|
||||
("skip", 0.into_lua(&lua)?),
|
||||
])?;
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("preload", ()))
|
||||
// TODO: remove this
|
||||
super::install_warn_mt(&lua, &plugin, job.clone()).ok();
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("preload", job))
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
use mlua::ObjectLike;
|
||||
use mlua::{IntoLua, ObjectLike};
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::{LUA, bindings::Cast, elements::Rect, file::File};
|
||||
use crate::{bindings::Cast, elements::Rect, file::File};
|
||||
|
||||
pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) {
|
||||
let cb: PluginCallback = Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.call_method("seek", units)
|
||||
let cb: PluginCallback = Box::new(move |lua, plugin| {
|
||||
let job = lua.create_table_from([
|
||||
("file", File::cast(lua, file)?.into_lua(lua)?),
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
|
||||
("units", units.into_lua(lua)?),
|
||||
])?;
|
||||
|
||||
plugin.call_method("seek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, Tab
|
|||
use tokio::{runtime::Handle, select};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::{bindings::Cast, file::File, loader::LOADER};
|
||||
|
||||
pub fn spot(
|
||||
cmd: &Cmd,
|
||||
cmd: &'static Cmd,
|
||||
file: yazi_shared::fs::File,
|
||||
mime: Cow<'static, str>,
|
||||
skip: usize,
|
||||
|
|
@ -18,12 +19,11 @@ pub fn spot(
|
|||
let ct = CancellationToken::new();
|
||||
let (ct1, ct2) = (ct.clone(), ct.clone());
|
||||
|
||||
let name = cmd.name.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let future = async {
|
||||
LOADER.ensure(&name).await.into_lua_err()?;
|
||||
LOADER.ensure(&cmd.name).await.into_lua_err()?;
|
||||
|
||||
let lua = slim_lua(&name)?;
|
||||
let lua = slim_lua(&cmd.name)?;
|
||||
lua.set_hook(
|
||||
HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000),
|
||||
move |_, dbg| {
|
||||
|
|
@ -35,18 +35,19 @@ pub fn spot(
|
|||
},
|
||||
);
|
||||
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&name) {
|
||||
lua.load(b.as_bytes()).set_name(name).call(())?
|
||||
let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
|
||||
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
|
||||
} else {
|
||||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
|
||||
let args = lua.create_table_from([
|
||||
let job = lua.create_table_from([
|
||||
("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?),
|
||||
("file", File::cast(&lua, file)?.into_lua(&lua)?),
|
||||
("mime", mime.into_lua(&lua)?),
|
||||
("skip", skip.into_lua(&lua)?),
|
||||
])?;
|
||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", args).await }
|
||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", job).await }
|
||||
};
|
||||
|
||||
let result = Handle::current().block_on(async {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{ExternalError, Function, Lua, ObjectLike, Table, Value};
|
||||
use mlua::{Function, Lua, ObjectLike, Table};
|
||||
use tracing::error;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_macro::{emit, render};
|
||||
use yazi_shared::{Layer, event::{Cmd, Data}};
|
||||
use yazi_shared::{Layer, event::Cmd};
|
||||
|
||||
use super::Utils;
|
||||
|
||||
|
|
@ -42,39 +40,22 @@ impl Utils {
|
|||
|
||||
pub(super) fn app_emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::App));
|
||||
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::App));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn manager_emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Manager));
|
||||
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::Manager));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn input_emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Input));
|
||||
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::Input));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_args(t: Table) -> mlua::Result<HashMap<String, Data>> {
|
||||
let mut args = HashMap::with_capacity(t.raw_len());
|
||||
for pair in t.pairs::<Value, Value>() {
|
||||
let (k, v) = pair?;
|
||||
match k {
|
||||
Value::Integer(i) if i > 0 => {
|
||||
args.insert((i - 1).to_string(), Sendable::value_to_data(v)?);
|
||||
}
|
||||
Value::String(s) => {
|
||||
args.insert(s.to_str()?.replace('_', "-"), Sendable::value_to_data(v)?);
|
||||
}
|
||||
_ => return Err("invalid key in cmd".into_lua_err()),
|
||||
}
|
||||
}
|
||||
Ok(args)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,15 @@ impl TryFrom<Table> for PreviewLock {
|
|||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(t: Table) -> Result<Self, Self::Error> {
|
||||
let file: FileRef = t.raw_get("file")?;
|
||||
// TODO: use `raw_get` instead of `get`
|
||||
let file: FileRef = t.get("file")?;
|
||||
Ok(Self {
|
||||
url: file.url_owned(),
|
||||
cha: file.cha,
|
||||
mime: t.raw_get("mime")?,
|
||||
mime: t.get("mime")?,
|
||||
|
||||
skip: t.raw_get("skip")?,
|
||||
area: t.raw_get("area")?,
|
||||
skip: t.get("skip")?,
|
||||
area: t.get("area")?,
|
||||
data: Default::default(),
|
||||
})
|
||||
}
|
||||
|
|
@ -36,7 +37,7 @@ impl TryFrom<Table> for PreviewLock {
|
|||
impl Utils {
|
||||
pub(super) fn preview_code(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_async_function(|lua, t: Table| async move {
|
||||
let area: Area = t.raw_get("area")?;
|
||||
let area: Area = t.get("area")?; // TODO: use `raw_get` instead of `get`
|
||||
let mut lock = PreviewLock::try_from(t)?;
|
||||
|
||||
let inner = match Highlighter::new(&lock.url).highlight(lock.skip, area.size()).await {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl Utils {
|
|||
}
|
||||
|
||||
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_list(args)?;
|
||||
let (tx, rx) = oneshot::channel::<Vec<Data>>();
|
||||
|
||||
let callback: PluginCallback = {
|
||||
|
|
@ -55,7 +55,7 @@ impl Utils {
|
|||
.chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d)))
|
||||
.collect::<mlua::Result<_>>()?;
|
||||
|
||||
let values = Sendable::values_to_vec(block.call(MultiValue::from_vec(args))?)?;
|
||||
let values = Sendable::values_to_list(block.call(MultiValue::from_vec(args))?)?;
|
||||
tx.send(values).map_err(|_| "send failed".into_lua_err())
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, collections::HashMap, fmt::Debug};
|
||||
|
||||
use anyhow::bail;
|
||||
use mlua::{Lua, Table};
|
||||
use yazi_shared::event::{CmdCow, Data};
|
||||
use yazi_shared::event::{Cmd, CmdCow, Data, DataKey};
|
||||
|
||||
pub type PluginCallback = Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PluginOpt {
|
||||
pub id: Cow<'static, str>,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub mode: PluginMode,
|
||||
pub args: Vec<Data>,
|
||||
pub cb: Option<PluginCallback>,
|
||||
|
||||
// TODO: remove this
|
||||
pub _old_args: Vec<Data>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for PluginOpt {
|
||||
|
|
@ -26,10 +29,13 @@ impl TryFrom<CmdCow> for PluginOpt {
|
|||
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()
|
||||
let (args, _old_args) = if let Some(s) = c.str("args") {
|
||||
(
|
||||
Cmd::parse_args(shell_words::split(s)?.into_iter())?,
|
||||
shell_words::split(s)?.into_iter().map(Data::String).collect(),
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
(Default::default(), Default::default())
|
||||
};
|
||||
|
||||
let mut mode = c.str("mode").map(Into::into).unwrap_or_default();
|
||||
|
|
@ -46,7 +52,18 @@ Please add `--- @sync entry` metadata at the head of your `{id}` plugin instead.
|
|||
});
|
||||
}
|
||||
|
||||
Ok(Self { id, mode, args, cb: c.take_any("callback") })
|
||||
Ok(Self { id, args, mode, cb: c.take_any("callback"), _old_args })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PluginOpt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PluginOpt")
|
||||
.field("id", &self.id)
|
||||
.field("args", &self.args)
|
||||
.field("mode", &self.mode)
|
||||
.field("cb", &self.cb.is_some())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_shared::event::Data;
|
||||
use yazi_proxy::options::PluginOpt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PluginOp {
|
||||
|
|
@ -15,8 +15,6 @@ impl PluginOp {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginOpEntry {
|
||||
pub id: usize,
|
||||
// TODO: remove these fields and use `CmdCow` instead
|
||||
pub name: String,
|
||||
pub args: Vec<Data>,
|
||||
pub id: usize,
|
||||
pub opt: PluginOpt,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl Plugin {
|
|||
pub async fn work(&self, op: PluginOp) -> Result<()> {
|
||||
match op {
|
||||
PluginOp::Entry(task) => {
|
||||
isolate::entry(task.name, task.args).await?;
|
||||
isolate::entry(task.opt).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, task.args).await {
|
||||
if let Err(e) = isolate::entry(task.opt).await {
|
||||
self.fail(task.id, format!("Micro plugin failed:\n{e}"))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use yazi_config::plugin::{FetcherProps, PreloaderProps};
|
||||
use yazi_config::plugin::{Fetcher, Preloader};
|
||||
use yazi_shared::{Throttle, fs::Url};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -20,17 +20,17 @@ impl PreworkOp {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct PreworkOpFetch {
|
||||
pub id: usize,
|
||||
pub plugin: FetcherProps,
|
||||
pub plugin: &'static Fetcher,
|
||||
pub targets: Vec<yazi_shared::fs::File>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PreworkOpLoad {
|
||||
pub id: usize,
|
||||
pub plugin: PreloaderProps,
|
||||
pub plugin: &'static Preloader,
|
||||
pub target: yazi_shared::fs::File,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use tokio::sync::mpsc;
|
|||
use tracing::error;
|
||||
use yazi_config::Priority;
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_shared::fs::{FilesOp, Url, calculate_size};
|
||||
use yazi_shared::{event::CmdCow, fs::{FilesOp, Url, calculate_size}};
|
||||
|
||||
use super::{PreworkOp, PreworkOpFetch, PreworkOpLoad, PreworkOpSize};
|
||||
use crate::{HIGH, NORMAL, TaskOp, TaskProg};
|
||||
|
|
@ -31,13 +31,13 @@ impl Prework {
|
|||
match op {
|
||||
PreworkOp::Fetch(task) => {
|
||||
let urls: Vec<_> = task.targets.iter().map(|f| f.url_owned()).collect();
|
||||
let result = isolate::fetch(task.plugin.name, task.targets).await;
|
||||
let result = isolate::fetch(CmdCow::from(&task.plugin.run), task.targets).await;
|
||||
if let Err(e) = result {
|
||||
self.fail(
|
||||
task.id,
|
||||
format!(
|
||||
"Failed to run fetcher `{}` with:\n{}\n\nError message:\n{e}",
|
||||
task.plugin.name,
|
||||
task.plugin.run.name,
|
||||
urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n")
|
||||
),
|
||||
)?;
|
||||
|
|
@ -48,36 +48,36 @@ impl Prework {
|
|||
if code & 1 == 0 {
|
||||
error!(
|
||||
"Returned {code} when running fetcher `{}` with:\n{}",
|
||||
task.plugin.name,
|
||||
task.plugin.run.name,
|
||||
urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n")
|
||||
);
|
||||
}
|
||||
if code & 2 != 0 {
|
||||
let mut loaded = self.loaded.lock();
|
||||
for url in urls {
|
||||
loaded.get_mut(&url).map(|x| *x &= !(1 << task.plugin.id));
|
||||
loaded.get_mut(&url).map(|x| *x &= !(1 << task.plugin.idx));
|
||||
}
|
||||
}
|
||||
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
||||
}
|
||||
PreworkOp::Load(task) => {
|
||||
let url = task.target.url_owned();
|
||||
let result = isolate::preload(task.plugin.name, task.target).await;
|
||||
let result = isolate::preload(&task.plugin.run, task.target).await;
|
||||
if let Err(e) = result {
|
||||
self.fail(
|
||||
task.id,
|
||||
format!("Failed to run preloader `{}` with `{url}`:\n{e}", task.plugin.name),
|
||||
format!("Failed to run preloader `{}` with `{url}`:\n{e}", task.plugin.run.name),
|
||||
)?;
|
||||
return Err(e.into());
|
||||
};
|
||||
|
||||
let code = result.unwrap();
|
||||
if code & 1 == 0 {
|
||||
error!("Returned {code} when running preloader `{}` with `{url}`", task.plugin.name);
|
||||
error!("Returned {code} when running preloader `{}` with `{url}`", task.plugin.run.name);
|
||||
}
|
||||
if code & 2 != 0 {
|
||||
let mut loaded = self.loaded.lock();
|
||||
loaded.get_mut(&url).map(|x| *x &= !(1 << task.plugin.id));
|
||||
loaded.get_mut(&url).map(|x| *x &= !(1 << task.plugin.idx));
|
||||
}
|
||||
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use parking_lot::Mutex;
|
|||
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle};
|
||||
use yazi_config::{TASKS, open::Opener, plugin::{Fetcher, Preloader}};
|
||||
use yazi_dds::Pump;
|
||||
use yazi_proxy::ManagerProxy;
|
||||
use yazi_shared::{Throttle, event::Data, fs::{Url, must_be_dir, remove_dir_clean, unique_name}};
|
||||
use yazi_proxy::{ManagerProxy, options::PluginOpt};
|
||||
use yazi_shared::{Throttle, fs::{Url, must_be_dir, remove_dir_clean, unique_name}};
|
||||
|
||||
use super::{Ongoing, TaskProg, TaskStage};
|
||||
use crate::{HIGH, LOW, NORMAL, TaskKind, TaskOp, file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}};
|
||||
|
|
@ -206,21 +206,17 @@ impl Scheduler {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{name}`"));
|
||||
pub fn plugin_micro(&self, opt: PluginOpt) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{}`", opt.id));
|
||||
|
||||
let plugin = self.plugin.clone();
|
||||
self.send_micro(
|
||||
id,
|
||||
NORMAL,
|
||||
async move { plugin.micro(PluginOpEntry { id, name, args }).await },
|
||||
);
|
||||
self.send_micro(id, NORMAL, async move { plugin.micro(PluginOpEntry { id, opt }).await });
|
||||
}
|
||||
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run macro plugin `{name}`"));
|
||||
pub fn plugin_macro(&self, opt: PluginOpt) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run macro plugin `{}`", opt.id));
|
||||
|
||||
self.plugin.macro_(PluginOpEntry { id, name, args }).ok();
|
||||
self.plugin.macro_(PluginOpEntry { id, opt }).ok();
|
||||
}
|
||||
|
||||
pub fn fetch_paged(&self, fetcher: &'static Fetcher, targets: Vec<yazi_shared::fs::File>) {
|
||||
|
|
@ -229,10 +225,9 @@ impl Scheduler {
|
|||
format!("Run fetcher `{}` with {} target(s)", fetcher.run.name, targets.len()),
|
||||
);
|
||||
|
||||
let plugin = fetcher.into();
|
||||
let prework = self.prework.clone();
|
||||
self.send_micro(id, NORMAL, async move {
|
||||
prework.fetch(PreworkOpFetch { id, plugin, targets }).await
|
||||
prework.fetch(PreworkOpFetch { id, plugin: fetcher, targets }).await
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -240,14 +235,11 @@ impl Scheduler {
|
|||
let id =
|
||||
self.ongoing.lock().add(TaskKind::Preload, format!("Run preloader `{}`", preloader.run.name));
|
||||
|
||||
let plugin = preloader.into();
|
||||
let target = target.clone();
|
||||
let prework = self.prework.clone();
|
||||
self.send_micro(
|
||||
id,
|
||||
NORMAL,
|
||||
async move { prework.load(PreworkOpLoad { id, plugin, target }).await },
|
||||
);
|
||||
self.send_micro(id, NORMAL, async move {
|
||||
prework.load(PreworkOpLoad { id, plugin: preloader, target }).await
|
||||
});
|
||||
}
|
||||
|
||||
pub fn prework_size(&self, targets: Vec<&Url>) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use core::str;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub const MIME_DIR: &str = "inode/directory";
|
||||
|
||||
|
|
@ -28,6 +29,37 @@ pub fn strip_trailing_newline(mut s: String) -> String {
|
|||
s
|
||||
}
|
||||
|
||||
pub fn replace_cow<'a>(s: &'a str, from: &str, to: &str) -> Cow<'a, str> {
|
||||
replace_cow_impl(s, s.match_indices(from), to)
|
||||
}
|
||||
|
||||
pub fn replacen_cow<'a>(s: &'a str, from: &str, to: &str, n: usize) -> Cow<'a, str> {
|
||||
replace_cow_impl(s, s.match_indices(from).take(n), to)
|
||||
}
|
||||
|
||||
fn replace_cow_impl<'s>(
|
||||
src: &'s str,
|
||||
mut indices: impl Iterator<Item = (usize, &'s str)>,
|
||||
to: &str,
|
||||
) -> Cow<'s, str> {
|
||||
let Some((first_idx, first_sub)) = indices.next() else {
|
||||
return Cow::Borrowed(src);
|
||||
};
|
||||
|
||||
let mut result = Cow::Owned(String::with_capacity(src.len()));
|
||||
result += unsafe { src.get_unchecked(..first_idx) };
|
||||
result.to_mut().push_str(to);
|
||||
|
||||
let mut last = first_idx + first_sub.len();
|
||||
for (idx, sub) in indices {
|
||||
result += unsafe { src.get_unchecked(last..idx) };
|
||||
result.to_mut().push_str(to);
|
||||
last = idx + sub.len();
|
||||
}
|
||||
|
||||
result + unsafe { src.get_unchecked(last..) }
|
||||
}
|
||||
|
||||
pub fn replace_to_printable(s: &[String], tab_size: u8) -> String {
|
||||
let mut buf = Vec::new();
|
||||
buf.try_reserve_exact(s.iter().map(|s| s.len()).sum::<usize>() | 15).unwrap_or_else(|_| panic!());
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use std::{any::Any, collections::HashMap, fmt::{self, Display}, mem, str::FromStr};
|
||||
use std::{any::Any, borrow::Cow, collections::HashMap, fmt::{self, Display}, mem, str::FromStr};
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::{Result, bail};
|
||||
use serde::{Deserialize, de};
|
||||
|
||||
use super::Data;
|
||||
use super::{Data, DataKey};
|
||||
use crate::fs::Url;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Cmd {
|
||||
pub name: String,
|
||||
pub args: HashMap<String, Data>,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
|
|
@ -23,68 +23,74 @@ impl Cmd {
|
|||
args: args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| (i.to_string(), Data::String(s.to_string())))
|
||||
.map(|(i, s)| (DataKey::Integer(i as i64), Data::String(s.to_string())))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// --- With
|
||||
#[inline]
|
||||
pub fn with(mut self, name: impl ToString, value: impl ToString) -> Self {
|
||||
self.args.insert(name.to_string(), Data::String(value.to_string()));
|
||||
pub fn with(mut self, name: impl Into<DataKey>, value: impl ToString) -> Self {
|
||||
self.args.insert(name.into(), Data::String(value.to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_opt(mut self, name: impl ToString, value: Option<impl ToString>) -> Self {
|
||||
pub fn with_opt(mut self, name: impl Into<DataKey>, value: Option<impl ToString>) -> Self {
|
||||
if let Some(v) = value {
|
||||
self.args.insert(name.to_string(), Data::String(v.to_string()));
|
||||
self.args.insert(name.into(), Data::String(v.to_string()));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_bool(mut self, name: impl ToString, state: bool) -> Self {
|
||||
self.args.insert(name.to_string(), Data::Boolean(state));
|
||||
pub fn with_bool(mut self, name: impl Into<DataKey>, state: bool) -> Self {
|
||||
self.args.insert(name.into(), Data::Boolean(state));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_any(mut self, name: impl ToString, data: impl Any + Send + Sync) -> Self {
|
||||
self.args.insert(name.to_string(), Data::Any(Box::new(data)));
|
||||
pub fn with_any(mut self, name: impl Into<DataKey>, data: impl Any + Send + Sync) -> Self {
|
||||
self.args.insert(name.into(), Data::Any(Box::new(data)));
|
||||
self
|
||||
}
|
||||
|
||||
// --- Get
|
||||
#[inline]
|
||||
pub fn get(&self, name: &str) -> Option<&Data> { self.args.get(name) }
|
||||
pub fn get(&self, name: impl Into<DataKey>) -> Option<&Data> { self.args.get(&name.into()) }
|
||||
|
||||
#[inline]
|
||||
pub fn str(&self, name: &str) -> Option<&str> { self.get(name).and_then(Data::as_str) }
|
||||
pub fn str(&self, name: impl Into<DataKey>) -> Option<&str> {
|
||||
self.get(name).and_then(Data::as_str)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bool(&self, name: &str) -> bool { self.maybe_bool(name).unwrap_or(false) }
|
||||
pub fn bool(&self, name: impl Into<DataKey>) -> bool { self.maybe_bool(name).unwrap_or(false) }
|
||||
|
||||
#[inline]
|
||||
pub fn maybe_bool(&self, name: &str) -> Option<bool> { self.get(name).and_then(Data::as_bool) }
|
||||
pub fn maybe_bool(&self, name: impl Into<DataKey>) -> Option<bool> {
|
||||
self.get(name).and_then(Data::as_bool)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn first(&self) -> Option<&Data> { self.get("0") }
|
||||
pub fn first(&self) -> Option<&Data> { self.get(0) }
|
||||
|
||||
#[inline]
|
||||
pub fn first_str(&self) -> Option<&str> { self.str("0") }
|
||||
pub fn first_str(&self) -> Option<&str> { self.str(0) }
|
||||
|
||||
// --- Take
|
||||
#[inline]
|
||||
pub fn take(&mut self, name: &str) -> Option<Data> { self.args.remove(name) }
|
||||
pub fn take(&mut self, name: impl Into<DataKey>) -> Option<Data> {
|
||||
self.args.remove(&name.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_str(&mut self, name: &str) -> Option<String> {
|
||||
pub fn take_str(&mut self, name: impl Into<DataKey>) -> Option<String> {
|
||||
if let Some(Data::String(s)) = self.take(name) { Some(s) } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_first(&mut self) -> Option<Data> { self.take("0") }
|
||||
pub fn take_first(&mut self) -> Option<Data> { self.take(0) }
|
||||
|
||||
#[inline]
|
||||
pub fn take_first_str(&mut self) -> Option<String> {
|
||||
|
|
@ -95,8 +101,35 @@ impl Cmd {
|
|||
pub fn take_first_url(&mut self) -> Option<Url> { self.take_first()?.into_url() }
|
||||
|
||||
#[inline]
|
||||
pub fn take_any<T: 'static>(&mut self, name: &str) -> Option<T> {
|
||||
self.args.remove(name).and_then(|d| d.into_any())
|
||||
pub fn take_any<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<T> {
|
||||
self.args.remove(&name.into()).and_then(|d| d.into_any())
|
||||
}
|
||||
|
||||
// Parse
|
||||
pub fn parse_args(words: impl Iterator<Item = String>) -> Result<HashMap<DataKey, Data>> {
|
||||
let mut i = 0i64;
|
||||
words
|
||||
.into_iter()
|
||||
.map(|word| {
|
||||
let Some(arg) = word.strip_prefix("--") else {
|
||||
i += 1;
|
||||
return Ok((DataKey::Integer(i - 1), Data::String(word)));
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
let Some(key) = parts.next().map(|s| s.to_owned()) else {
|
||||
bail!("invalid argument: {arg}");
|
||||
};
|
||||
|
||||
let val = if let Some(val) = parts.next() {
|
||||
Data::String(val.to_owned())
|
||||
} else {
|
||||
Data::Boolean(true)
|
||||
};
|
||||
|
||||
Ok((DataKey::String(Cow::Owned(key)), val))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,14 +137,20 @@ impl Display for Cmd {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)?;
|
||||
for (k, v) in &self.args {
|
||||
if k.as_bytes().first().is_some_and(|b| b.is_ascii_digit()) {
|
||||
if let Some(s) = v.as_str() {
|
||||
write!(f, " {s}")?;
|
||||
match k {
|
||||
DataKey::Integer(_) => {
|
||||
if let Some(s) = v.as_str() {
|
||||
write!(f, " {s}")?;
|
||||
}
|
||||
}
|
||||
} else if v.as_bool().is_some_and(|b| b) {
|
||||
write!(f, " --{k}")?;
|
||||
} else if let Some(s) = v.as_str() {
|
||||
write!(f, " --{k}={s}")?;
|
||||
DataKey::String(k) => {
|
||||
if v.as_bool().is_some_and(|b| b) {
|
||||
write!(f, " --{k}")?;
|
||||
} else if let Some(s) = v.as_str() {
|
||||
write!(f, " --{k}={s}")?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -128,27 +167,7 @@ impl FromStr for Cmd {
|
|||
bail!("command name cannot be empty");
|
||||
}
|
||||
|
||||
let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() };
|
||||
let mut i = 0usize;
|
||||
for arg in args.into_iter().skip(1) {
|
||||
let Some(arg) = arg.strip_prefix("--") else {
|
||||
cmd.args.insert(i.to_string(), Data::String(arg));
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
let Some(key) = parts.next().map(|s| s.to_owned()) else {
|
||||
bail!("invalid argument: {arg}");
|
||||
};
|
||||
|
||||
if let Some(val) = parts.next() {
|
||||
cmd.args.insert(key, Data::String(val.to_owned()));
|
||||
} else {
|
||||
cmd.args.insert(key, Data::Boolean(true));
|
||||
}
|
||||
}
|
||||
Ok(cmd)
|
||||
Ok(Cmd { name: mem::take(&mut args[0]), args: Cmd::parse_args(args.into_iter().skip(1))? })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use super::{Cmd, Data};
|
||||
use super::{Cmd, Data, DataKey};
|
||||
use crate::fs::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -30,7 +30,7 @@ impl Deref for CmdCow {
|
|||
|
||||
impl CmdCow {
|
||||
#[inline]
|
||||
pub fn try_take(&mut self, name: &str) -> Option<Data> {
|
||||
pub fn try_take(&mut self, name: impl Into<DataKey>) -> Option<Data> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take(name),
|
||||
Self::Borrowed(_) => None,
|
||||
|
|
@ -38,7 +38,7 @@ impl CmdCow {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_str(&mut self, name: &str) -> Option<Cow<'static, str>> {
|
||||
pub fn take_str(&mut self, name: impl Into<DataKey>) -> Option<Cow<'static, str>> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_str(name).map(Cow::Owned),
|
||||
Self::Borrowed(c) => c.str(name).map(Cow::Borrowed),
|
||||
|
|
@ -46,7 +46,7 @@ impl CmdCow {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_url(&mut self, name: &str) -> Option<Url> {
|
||||
pub fn take_url(&mut self, name: impl Into<DataKey>) -> Option<Url> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take(name).and_then(Data::into_url),
|
||||
Self::Borrowed(c) => c.get(name).and_then(Data::to_url),
|
||||
|
|
@ -69,7 +69,7 @@ impl CmdCow {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_any<T: 'static>(&mut self, name: &str) -> Option<T> {
|
||||
pub fn take_any<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<T> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_any(name),
|
||||
Self::Borrowed(_) => None,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{any::Any, collections::HashMap};
|
||||
use std::{any::Any, borrow::Cow, collections::HashMap};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ impl Data {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn into_dict_string(self) -> HashMap<String, String> {
|
||||
pub fn into_dict_string(self) -> HashMap<Cow<'static, str>, String> {
|
||||
let Self::Dict(dict) = self else {
|
||||
return Default::default();
|
||||
};
|
||||
|
|
@ -89,7 +89,7 @@ pub enum DataKey {
|
|||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(OrderedFloat),
|
||||
String(String),
|
||||
String(Cow<'static, str>),
|
||||
#[serde(skip_deserializing)]
|
||||
Url(Url),
|
||||
}
|
||||
|
|
@ -97,6 +97,26 @@ pub enum DataKey {
|
|||
impl DataKey {
|
||||
#[inline]
|
||||
pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(_)) }
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for DataKey {
|
||||
fn from(value: usize) -> Self { Self::Integer(value as i64) }
|
||||
}
|
||||
|
||||
impl From<&'static str> for DataKey {
|
||||
fn from(value: &'static str) -> Self { Self::String(Cow::Borrowed(value)) }
|
||||
}
|
||||
|
||||
impl From<String> for DataKey {
|
||||
fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
|
||||
}
|
||||
|
||||
// --- Macros
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::VecDeque, ffi::OsString};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use crossterm::event::{KeyEvent, MouseEvent};
|
||||
use tokio::sync::mpsc;
|
||||
|
|
@ -12,7 +12,7 @@ static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
|||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Call(CmdCow, Layer),
|
||||
Seq(VecDeque<CmdCow>, Layer),
|
||||
Seq(Vec<CmdCow>, Layer),
|
||||
Render,
|
||||
Key(KeyEvent),
|
||||
Mouse(MouseEvent),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue