mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: expand the types supported by the event system (#923)
This commit is contained in:
parent
2975b999bf
commit
09bc9aa371
99 changed files with 605 additions and 502 deletions
|
|
@ -15,9 +15,7 @@ pub struct Control {
|
|||
|
||||
impl Control {
|
||||
#[inline]
|
||||
pub fn to_seq(&self) -> VecDeque<Cmd> {
|
||||
self.run.iter().map(|e| e.clone_without_data()).collect()
|
||||
}
|
||||
pub fn to_seq(&self) -> VecDeque<Cmd> { self.run.iter().map(|c| c.shallow_clone()).collect() }
|
||||
}
|
||||
|
||||
impl Control {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::fmt;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use serde::{de::{self, Visitor}, Deserializer};
|
||||
use yazi_shared::event::Cmd;
|
||||
use yazi_shared::event::{Cmd, Data};
|
||||
|
||||
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
|
||||
where
|
||||
|
|
@ -10,21 +10,28 @@ where
|
|||
{
|
||||
struct RunVisitor;
|
||||
|
||||
#[allow(clippy::explicit_counter_loop)]
|
||||
fn parse(s: &str) -> Result<Cmd> {
|
||||
let s = shell_words::split(s)?;
|
||||
if s.is_empty() {
|
||||
bail!("`run` cannot be empty");
|
||||
}
|
||||
let mut args = shell_words::split(s)?;
|
||||
let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() };
|
||||
|
||||
let mut cmd = Cmd { name: s[0].clone(), ..Default::default() };
|
||||
for arg in s.into_iter().skip(1) {
|
||||
if arg.starts_with("--") {
|
||||
let mut arg = arg.splitn(2, '=');
|
||||
let key = arg.next().unwrap().trim_start_matches('-');
|
||||
let val = arg.next().unwrap_or("").to_string();
|
||||
cmd.named.insert(key.to_string(), val);
|
||||
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.push(arg);
|
||||
cmd.args.insert(key, Data::Boolean(true));
|
||||
}
|
||||
}
|
||||
Ok(cmd)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.named.contains_key("submit") } }
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.get_bool("submit") } }
|
||||
}
|
||||
|
||||
impl Completion {
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
cache: mem::take(&mut c.args),
|
||||
cache_name: c.take_name("cache-name").unwrap_or_default(),
|
||||
word: c.take_name("word").unwrap_or_default(),
|
||||
ticket: c.take_name("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||
cache: c.take_any("cache").unwrap_or_default(),
|
||||
cache_name: c.take_str("cache-name").unwrap_or_default(),
|
||||
word: c.take_str("word").unwrap_or_default(),
|
||||
ticket: c.take_str("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ impl Completion {
|
|||
}
|
||||
|
||||
if !opt.cache.is_empty() {
|
||||
self.caches.insert(opt.cache_name.to_owned(), opt.cache.clone());
|
||||
self.caches.insert(opt.cache_name.to_owned(), opt.cache);
|
||||
}
|
||||
let Some(cache) = self.caches.get(&opt.cache_name) else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
word: c.take_first().unwrap_or_default(),
|
||||
ticket: c.take_name("ticket").and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
word: c.take_first_str().unwrap_or_default(),
|
||||
ticket: c.take_str("ticket").and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,8 @@ impl Completion {
|
|||
|
||||
if !cache.is_empty() {
|
||||
emit!(Call(
|
||||
Cmd::args("show", cache)
|
||||
Cmd::new("show")
|
||||
.with_any("cache", cache)
|
||||
.with("cache-name", parent)
|
||||
.with("word", child)
|
||||
.with("ticket", ticket),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub enum FilterCase {
|
|||
|
||||
impl From<&Cmd> for FilterCase {
|
||||
fn from(c: &Cmd) -> Self {
|
||||
match (c.named.contains_key("smart"), c.named.contains_key("insensitive")) {
|
||||
match (c.get_bool("smart"), c.get_bool("insensitive")) {
|
||||
(true, _) => Self::Smart,
|
||||
(_, false) => Self::Sensitive,
|
||||
(_, true) => Self::Insensitive,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
impl From<isize> for Opt {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { under: c.named.contains_key("under") } }
|
||||
fn from(c: Cmd) -> Self { Self { under: c.get_bool("under") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(under: bool) -> Self { Self { under } }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.named.contains_key("submit") } }
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.get_bool("submit") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(submit: bool) -> Self { Self { submit } }
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
word: c.take_first().unwrap_or_default(),
|
||||
ticket: c.take_name("ticket").and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
word: c.take_first_str().unwrap_or_default(),
|
||||
ticket: c.take_str("ticket").and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
Self { cut: c.named.contains_key("cut"), insert: c.named.contains_key("insert") }
|
||||
}
|
||||
fn from(c: Cmd) -> Self { Self { cut: c.get_bool("cut"), insert: c.get_bool("insert") } }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { end_of_word: c.named.contains_key("end-of-word") } }
|
||||
fn from(c: Cmd) -> Self { Self { end_of_word: c.get_bool("end-of-word") } }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { append: c.named.contains_key("append") } }
|
||||
fn from(c: Cmd) -> Self { Self { append: c.get_bool("append") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(append: bool) -> Self { Self { append } }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self { Self { kind: c.take_first().unwrap_or_default() } }
|
||||
fn from(mut c: Cmd) -> Self { Self { kind: c.take_first_str().unwrap_or_default() } }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
in_operating: c.named.contains_key("in-operating"),
|
||||
step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
in_operating: c.get_bool("in-operating"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { before: c.named.contains_key("before") } }
|
||||
fn from(c: Cmd) -> Self { Self { before: c.get_bool("before") } }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,24 @@
|
|||
use yazi_proxy::options::InputOpt;
|
||||
use yazi_shared::render;
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_shared::{event::Cmd, render, InputError};
|
||||
|
||||
use crate::input::Input;
|
||||
|
||||
pub struct Opt {
|
||||
cfg: InputCfg,
|
||||
tx: mpsc::UnboundedSender<Result<String, InputError>>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { cfg: c.take_any("cfg").ok_or(())?, tx: c.take_any("tx").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn show(&mut self, opt: impl TryInto<InputOpt>) {
|
||||
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else { return };
|
||||
|
||||
self.close(false);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { force: c.named.contains_key("force") } }
|
||||
fn from(c: Cmd) -> Self { Self { force: c.get_bool("force") } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self { Self { url: c.take_first().map(Url::from) } }
|
||||
fn from(mut c: Cmd) -> Self { Self { url: c.take_first_str().map(Url::from) } }
|
||||
}
|
||||
impl From<Option<Url>> for Opt {
|
||||
fn from(url: Option<Url>) -> Self { Self { url } }
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
Self { relative: c.named.contains_key("relative"), force: c.named.contains_key("force") }
|
||||
}
|
||||
fn from(c: Cmd) -> Self { Self { relative: c.get_bool("relative"), force: c.get_bool("force") } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
Self {
|
||||
interactive: c.named.contains_key("interactive"),
|
||||
hovered: c.named.contains_key("hovered"),
|
||||
}
|
||||
Self { interactive: c.get_bool("interactive"), hovered: c.get_bool("hovered") }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
Self { force: c.named.contains_key("force"), follow: c.named.contains_key("follow") }
|
||||
}
|
||||
fn from(c: Cmd) -> Self { Self { force: c.get_bool("force"), follow: c.get_bool("follow") } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
skip: c.take_first().and_then(|s| s.parse().ok()),
|
||||
force: c.named.contains_key("force"),
|
||||
only_if: c.take_name("only-if").map(Url::from),
|
||||
upper_bound: c.named.contains_key("upper-bound"),
|
||||
skip: c.take_first_str().and_then(|s| s.parse().ok()),
|
||||
force: c.get_bool("force"),
|
||||
only_if: c.take_str("only-if").map(Url::from),
|
||||
upper_bound: c.get_bool("upper-bound"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl From<()> for Opt {
|
|||
fn from(_: ()) -> Self { Self::default() }
|
||||
}
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { no_cwd_file: c.named.contains_key("no-cwd-file") } }
|
||||
fn from(c: Cmd) -> Self { Self { no_cwd_file: c.get_bool("no-cwd-file") } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
force: c.named.contains_key("force"),
|
||||
permanently: c.named.contains_key("permanently"),
|
||||
targets: c.take_data().unwrap_or_default(),
|
||||
force: c.get_bool("force"),
|
||||
permanently: c.get_bool("permanently"),
|
||||
targets: c.take_any("targets").unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
force: c.named.contains_key("force"),
|
||||
empty: c.take_name("empty").unwrap_or_default(),
|
||||
cursor: c.take_name("cursor").unwrap_or_default(),
|
||||
force: c.get_bool("force"),
|
||||
empty: c.take_str("empty").unwrap_or_default(),
|
||||
cursor: c.take_str("cursor").unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { units: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { units: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { idx: c.take_first().and_then(|i| i.parse().ok()).unwrap_or(0) }
|
||||
Self { idx: c.take_first_str().and_then(|i| i.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
if c.named.contains_key("current") {
|
||||
if c.get_bool("current") {
|
||||
Self { url: Default::default(), current: true }
|
||||
} else {
|
||||
Self {
|
||||
url: c.take_first().map_or_else(|| Url::from(&BOOT.cwd), Url::from),
|
||||
url: c.take_first_str().map_or_else(|| Url::from(&BOOT.cwd), Url::from),
|
||||
current: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
relative: c.named.contains_key("relative"),
|
||||
step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
relative: c.get_bool("relative"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ pub struct Opt {
|
|||
impl TryFrom<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { Ok(Self { op: c.take_data().ok_or(())? }) }
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { op: c.take_any("op").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::{event::Cmd, fs::Url, render};
|
||||
|
||||
use crate::{manager::{Manager, LINKED}, tasks::Tasks};
|
||||
|
||||
pub struct Opt {
|
||||
data: ValueSendable,
|
||||
updates: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { data: c.take_data().ok_or(())? })
|
||||
Ok(Self { updates: c.take_data("updates").ok_or(())?.into_table_string() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -25,8 +24,7 @@ impl Manager {
|
|||
|
||||
let linked = LINKED.read();
|
||||
let updates = opt
|
||||
.data
|
||||
.into_table_string()
|
||||
.updates
|
||||
.into_iter()
|
||||
.map(|(url, mime)| (Url::from(url), mime))
|
||||
.filter(|(url, mime)| self.mimetype.get(url) != Some(mime))
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
page: c.take_first().and_then(|s| s.parse().ok()),
|
||||
only_if: c.take_name("only-if").map(Url::from),
|
||||
page: c.take_first_str().and_then(|s| s.parse().ok()),
|
||||
only_if: c.take_str("only-if").map(Url::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { cut: c.named.contains_key("cut") } }
|
||||
fn from(c: Cmd) -> Self { Self { cut: c.get_bool("cut") } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl TryFrom<Cmd> for Opt {
|
|||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
let interval = c.take_first().and_then(|s| s.parse::<f64>().ok()).ok_or(())?;
|
||||
let interval = c.take_first_str().and_then(|s| s.parse::<f64>().ok()).ok_or(())?;
|
||||
if interval < 0.0 {
|
||||
return Err(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.named.contains_key("submit") } }
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.get_bool("submit") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(submit: bool) -> Self { Self { submit } }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,24 @@
|
|||
use yazi_proxy::options::SelectOpt;
|
||||
use yazi_shared::render;
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::popup::SelectCfg;
|
||||
use yazi_shared::{event::Cmd, render};
|
||||
|
||||
use crate::select::Select;
|
||||
|
||||
pub struct Opt {
|
||||
cfg: SelectCfg,
|
||||
tx: oneshot::Sender<anyhow::Result<usize>>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { cfg: c.take_any("cfg").ok_or(())?, tx: c.take_any("tx").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub fn show(&mut self, opt: impl TryInto<SelectOpt>) {
|
||||
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or_default() }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or_default() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
let mut target = Url::from(c.take_first().unwrap_or_default());
|
||||
let mut target = Url::from(c.take_first_str().unwrap_or_default());
|
||||
if target.is_regular() {
|
||||
target.set_path(expand_path(&target))
|
||||
}
|
||||
|
||||
Self { target, interactive: c.named.contains_key("interactive") }
|
||||
Self { target, interactive: c.get_bool("interactive") }
|
||||
}
|
||||
}
|
||||
impl From<Url> for Opt {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self { Self { type_: c.take_first().unwrap_or_default() } }
|
||||
fn from(mut c: Cmd) -> Self { Self { type_: c.take_first_str().unwrap_or_default() } }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,16 @@ bitflags! {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
c.named.iter().fold(Opt::empty(), |acc, (k, _)| match k.as_str() {
|
||||
"all" => Self::all(),
|
||||
"find" => acc | Self::FIND,
|
||||
"visual" => acc | Self::VISUAL,
|
||||
"select" => acc | Self::SELECT,
|
||||
"filter" => acc | Self::FILTER,
|
||||
"search" => acc | Self::SEARCH,
|
||||
_ => acc,
|
||||
c.args.iter().fold(Opt::empty(), |acc, (k, v)| {
|
||||
match (k.as_str(), v.as_bool().unwrap_or(false)) {
|
||||
("all", true) => Self::all(),
|
||||
("find", true) => acc | Self::FIND,
|
||||
("visual", true) => acc | Self::VISUAL,
|
||||
("select", true) => acc | Self::SELECT,
|
||||
("filter", true) => acc | Self::FILTER,
|
||||
("search", true) => acc | Self::SEARCH,
|
||||
_ => acc,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
query: c.take_first().unwrap_or_default(),
|
||||
query: c.take_first_str().unwrap_or_default(),
|
||||
case: FilterCase::from(&c),
|
||||
done: c.named.contains_key("done"),
|
||||
done: c.get_bool("done"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
query: c.take_first(),
|
||||
prev: c.named.contains_key("previous"),
|
||||
case: FilterCase::from(&c),
|
||||
}
|
||||
Self { query: c.take_first_str(), prev: c.get_bool("previous"), case: FilterCase::from(&c) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +25,7 @@ pub struct ArrowOpt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for ArrowOpt {
|
||||
fn from(c: Cmd) -> Self { Self { prev: c.named.contains_key("previous") } }
|
||||
fn from(c: Cmd) -> Self { Self { prev: c.get_bool("previous") } }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use yazi_shared::event::Cmd;
|
|||
use crate::tab::Tab;
|
||||
|
||||
impl Tab {
|
||||
pub fn hidden(&mut self, c: Cmd) {
|
||||
self.conf.show_hidden = match c.args.first().map(|s| s.as_str()) {
|
||||
pub fn hidden(&mut self, mut c: Cmd) {
|
||||
self.conf.show_hidden = match c.take_first_str().as_deref() {
|
||||
Some("show") => true,
|
||||
Some("hide") => false,
|
||||
_ => !self.conf.show_hidden,
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ pub enum OptType {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
type_: match c.args.first().map(|s| s.as_str()) {
|
||||
type_: match c.take_first_str().as_deref() {
|
||||
Some("fzf") => OptType::Fzf,
|
||||
Some("zoxide") => OptType::Zoxide,
|
||||
_ => OptType::None,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::tab::Tab;
|
|||
impl Tab {
|
||||
pub fn linemode(&mut self, mut c: Cmd) {
|
||||
render!(self.conf.patch(|new| {
|
||||
let Some(mode) = c.take_first() else {
|
||||
let Some(mode) = c.take_first_str() else {
|
||||
return;
|
||||
};
|
||||
if !mode.is_empty() && mode.len() <= 20 {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ impl TryFrom<Cmd> for Opt {
|
|||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { lock: c.take_data().ok_or(())? })
|
||||
Ok(Self { lock: c.take_any("lock").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
let mut target = Url::from(c.take_first().unwrap_or_default());
|
||||
let mut target = Url::from(c.take_first_str().unwrap_or_default());
|
||||
if target.is_regular() {
|
||||
target.set_path(expand_path(&target))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self { Self { type_: c.take_first().unwrap_or_default().into() } }
|
||||
fn from(mut c: Cmd) -> Self { Self { type_: c.take_first_str().unwrap_or_default().into() } }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ pub struct Opt<'a> {
|
|||
impl<'a> From<Cmd> for Opt<'a> {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
url: c.take_name("url").map(|s| Cow::Owned(Url::from(s))),
|
||||
state: match c.named.get("state").map(|s| s.as_str()) {
|
||||
url: c.take_str("url").map(|s| Cow::Owned(Url::from(s))),
|
||||
state: match c.take_str("state").as_deref() {
|
||||
Some("true") => Some(true),
|
||||
Some("false") => Some(false),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
state: match c.named.get("state").map(|s| s.as_str()) {
|
||||
state: match c.take_str("state").as_deref() {
|
||||
Some("true") => Some(true),
|
||||
Some("false") => Some(false),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
run: c.take_first().unwrap_or_default(),
|
||||
block: c.named.contains_key("block"),
|
||||
orphan: c.named.contains_key("orphan"),
|
||||
confirm: c.named.contains_key("confirm"),
|
||||
run: c.take_first_str().unwrap_or_default(),
|
||||
block: c.get_bool("block"),
|
||||
orphan: c.get_bool("orphan"),
|
||||
confirm: c.get_bool("confirm"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ use yazi_shared::event::Cmd;
|
|||
use crate::{tab::Tab, tasks::Tasks};
|
||||
|
||||
impl Tab {
|
||||
pub fn sort(&mut self, c: Cmd, tasks: &Tasks) {
|
||||
if let Some(by) = c.args.first() {
|
||||
self.conf.sort_by = SortBy::from_str(by).unwrap_or_default();
|
||||
pub fn sort(&mut self, mut c: Cmd, tasks: &Tasks) {
|
||||
if let Some(by) = c.take_first_str() {
|
||||
self.conf.sort_by = SortBy::from_str(&by).unwrap_or_default();
|
||||
}
|
||||
self.conf.sort_sensitive = c.named.contains_key("sensitive");
|
||||
self.conf.sort_reverse = c.named.contains_key("reverse");
|
||||
self.conf.sort_dir_first = c.named.contains_key("dir-first");
|
||||
self.conf.sort_sensitive = c.get_bool("sensitive");
|
||||
self.conf.sort_reverse = c.get_bool("reverse");
|
||||
self.conf.sort_dir_first = c.get_bool("dir-first");
|
||||
|
||||
self.apply_files_attrs();
|
||||
ManagerProxy::update_paged();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { unset: c.named.contains_key("unset") } }
|
||||
fn from(c: Cmd) -> Self { Self { unset: c.get_bool("unset") } }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self { step: c.take_first().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
Self { step: c.take_first_str().and_then(|s| s.parse().ok()).unwrap_or(0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
use super::Tasks;
|
||||
|
||||
impl Tasks {
|
||||
#[inline]
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<ValueSendable>) {
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
|
||||
self.scheduler.plugin_micro(name, args);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<ValueSendable>) {
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
|
||||
self.scheduler.plugin_macro(name, args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl Tasks {
|
|||
let new = TasksProgress::from(&*ongoing.lock());
|
||||
if last != new {
|
||||
last = new;
|
||||
emit!(Call(Cmd::new("update_progress").with_data(new), Layer::App));
|
||||
emit!(Call(Cmd::new("update_progress").with_any("progress", new), Layer::App));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ impl TryFrom<Cmd> for Opt {
|
|||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
tx: c.take_data().ok_or(())?,
|
||||
idx: c.take_first().and_then(|s| s.parse().ok()).ok_or(())?,
|
||||
tx: c.take_any("tx").ok_or(())?,
|
||||
idx: c.take_first_str().and_then(|s| s.parse().ok()).ok_or(())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ impl TryFrom<Cmd> for Opt {
|
|||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
cands: c.take_data().unwrap_or_default(),
|
||||
layer: Layer::from_str(&c.take_name("layer").unwrap_or_default())?,
|
||||
silent: c.named.contains_key("silent"),
|
||||
cands: c.take_any("candidates").unwrap_or_default(),
|
||||
layer: Layer::from_str(&c.take_str("layer").unwrap_or_default())?,
|
||||
silent: c.get_bool("silent"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,25 @@
|
|||
use mlua::{IntoLua, Lua, Value};
|
||||
use serde::Serialize;
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
use super::Body;
|
||||
use crate::ValueSendable;
|
||||
use crate::Sendable;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BodyCustom {
|
||||
pub kind: String,
|
||||
pub value: ValueSendable,
|
||||
pub kind: String,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
impl BodyCustom {
|
||||
#[inline]
|
||||
pub fn from_str(kind: &str, value: &str) -> anyhow::Result<Body<'static>> {
|
||||
Ok(Self { kind: kind.to_owned(), value: serde_json::from_str(value)? }.into())
|
||||
pub fn from_str(kind: &str, data: &str) -> anyhow::Result<Body<'static>> {
|
||||
Ok(Self { kind: kind.to_owned(), data: serde_json::from_str(data)? }.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_lua(kind: &str, value: Value) -> mlua::Result<Body<'static>> {
|
||||
Ok(Self { kind: kind.to_owned(), value: value.try_into()? }.into())
|
||||
pub fn from_lua(kind: &str, data: Value) -> mlua::Result<Body<'static>> {
|
||||
Ok(Self { kind: kind.to_owned(), data: Sendable::value_to_data(data)? }.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,11 +28,11 @@ impl From<BodyCustom> for Body<'_> {
|
|||
}
|
||||
|
||||
impl IntoLua<'_> for BodyCustom {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { self.value.into_lua(lua) }
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { Sendable::data_to_value(lua, self.data) }
|
||||
}
|
||||
|
||||
impl Serialize for BodyCustom {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serde::Serialize::serialize(&self.value, serializer)
|
||||
serde::Serialize::serialize(&self.data, serializer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl Payload<'static> {
|
|||
|
||||
pub(super) fn emit(self) {
|
||||
self.try_flush();
|
||||
emit!(Call(Cmd::new("accept_payload").with_data(self), Layer::App));
|
||||
emit!(Call(Cmd::new("accept_payload").with_any("payload", self), Layer::App));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,129 +1,122 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{ExternalError, IntoLua, Lua, Value, Variadic};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_shared::OrderedFloat;
|
||||
use mlua::{ExternalError, Lua, Table, Value, Variadic};
|
||||
use yazi_shared::{event::{Data, DataKey}, OrderedFloat};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ValueSendable {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(f64),
|
||||
String(String),
|
||||
Table(HashMap<ValueSendableKey, ValueSendable>),
|
||||
}
|
||||
pub struct Sendable;
|
||||
|
||||
impl ValueSendable {
|
||||
pub fn try_from_variadic(values: Variadic<Value>) -> mlua::Result<Vec<Self>> {
|
||||
impl Sendable {
|
||||
pub fn value_to_data(value: Value) -> mlua::Result<Data> {
|
||||
Ok(match value {
|
||||
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::Number(n) => Data::Number(n),
|
||||
Value::String(s) => Data::String(s.to_str()?.to_owned()),
|
||||
Value::Table(t) => {
|
||||
let mut map = HashMap::with_capacity(t.raw_len());
|
||||
for result in t.pairs::<Value, Value>() {
|
||||
let (k, v) = result?;
|
||||
map.insert(Self::value_to_key(k)?, Self::value_to_data(v)?);
|
||||
}
|
||||
Data::Table(map)
|
||||
}
|
||||
Value::Function(_) => Err("function is not supported".into_lua_err())?,
|
||||
Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(t) = ud.take::<yazi_shared::fs::Url>() {
|
||||
Data::Url(t)
|
||||
} else if let Ok(t) = ud.take::<super::body::BodyYankIter>() {
|
||||
Data::Any(Box::new(t))
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
Value::Error(_) => Err("error is not supported".into_lua_err())?,
|
||||
})
|
||||
}
|
||||
|
||||
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::Table(t) => {
|
||||
let seq_len = t.keys().filter(|&k| !k.is_numeric()).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)?)?;
|
||||
}
|
||||
Value::Table(table)
|
||||
}
|
||||
Data::Url(v) => Value::UserData(lua.create_any_userdata(v)?),
|
||||
Data::Any(v) => {
|
||||
if let Ok(t) = v.downcast::<super::body::BodyYankIter>() {
|
||||
Value::UserData(lua.create_userdata(*t)?)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn vec_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)?);
|
||||
}
|
||||
lua.create_sequence_from(vec)
|
||||
}
|
||||
|
||||
pub fn vec_to_variadic(lua: &Lua, data: Vec<Data>) -> mlua::Result<Variadic<Value>> {
|
||||
let mut vec = Vec::with_capacity(data.len());
|
||||
for v in data {
|
||||
vec.push(Self::data_to_value(lua, v)?);
|
||||
}
|
||||
Ok(Variadic::from_iter(vec))
|
||||
}
|
||||
|
||||
pub fn variadic_to_vec(values: Variadic<Value>) -> mlua::Result<Vec<Data>> {
|
||||
let mut vec = Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
vec.push(Self::try_from(value)?);
|
||||
vec.push(Self::value_to_data(value)?);
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
pub fn into_table_string(self) -> HashMap<String, String> {
|
||||
let Self::Table(table) = self else {
|
||||
return Default::default();
|
||||
};
|
||||
|
||||
let mut map = HashMap::with_capacity(table.len());
|
||||
for pair in table {
|
||||
if let (ValueSendableKey::String(k), Self::String(v)) = pair {
|
||||
map.insert(k, v);
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Value<'a>> for ValueSendable {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
fn value_to_key(value: Value) -> mlua::Result<DataKey> {
|
||||
Ok(match value {
|
||||
Value::Nil => Self::Nil,
|
||||
Value::Boolean(b) => Self::Boolean(b),
|
||||
Value::Nil => DataKey::Nil,
|
||||
Value::Boolean(v) => DataKey::Boolean(v),
|
||||
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
|
||||
Value::Integer(n) => Self::Integer(n),
|
||||
Value::Number(n) => Self::Number(n),
|
||||
Value::String(s) => Self::String(s.to_str()?.to_owned()),
|
||||
Value::Table(t) => {
|
||||
let mut map = HashMap::with_capacity(t.len().map(|l| l as usize)?);
|
||||
for result in t.pairs::<Value, Value>() {
|
||||
let (k, v) = result?;
|
||||
map.insert(Self::try_from(k)?.try_into()?, v.try_into()?);
|
||||
}
|
||||
Self::Table(map)
|
||||
}
|
||||
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::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())?,
|
||||
Value::UserData(_) => Err("userdata is not supported".into_lua_err())?,
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(t) = ud.take::<yazi_shared::fs::Url>() {
|
||||
DataKey::Url(t)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
Value::Error(_) => Err("error is not supported".into_lua_err())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua<'_> for ValueSendable {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
match self {
|
||||
Self::Nil => Ok(Value::Nil),
|
||||
Self::Boolean(v) => Ok(Value::Boolean(v)),
|
||||
Self::Integer(v) => Ok(Value::Integer(v)),
|
||||
Self::Number(v) => Ok(Value::Number(v)),
|
||||
Self::String(v) => Ok(Value::String(lua.create_string(v)?)),
|
||||
Self::Table(v) => {
|
||||
let seq_len = v.keys().filter(|&k| !k.is_numeric()).count();
|
||||
let table = lua.create_table_with_capacity(seq_len, v.len() - seq_len)?;
|
||||
for (k, v) in v {
|
||||
table.raw_set(k, v)?;
|
||||
}
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ValueSendableKey {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(OrderedFloat),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl ValueSendableKey {
|
||||
#[inline]
|
||||
fn is_numeric(&self) -> bool { matches!(self, Self::Integer(_) | Self::Number(_)) }
|
||||
}
|
||||
|
||||
impl TryInto<ValueSendableKey> for ValueSendable {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_into(self) -> Result<ValueSendableKey, Self::Error> {
|
||||
Ok(match self {
|
||||
Self::Nil => ValueSendableKey::Nil,
|
||||
Self::Boolean(v) => ValueSendableKey::Boolean(v),
|
||||
Self::Integer(v) => ValueSendableKey::Integer(v),
|
||||
Self::Number(v) => ValueSendableKey::Number(OrderedFloat::new(v)),
|
||||
Self::String(v) => ValueSendableKey::String(v),
|
||||
Self::Table(_) => Err("table is not supported".into_lua_err())?,
|
||||
fn key_to_value(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)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua<'_> for ValueSendableKey {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
match self {
|
||||
Self::Nil => Ok(Value::Nil),
|
||||
Self::Boolean(k) => Ok(Value::Boolean(k)),
|
||||
Self::Integer(k) => Ok(Value::Integer(k)),
|
||||
Self::Number(k) => Ok(Value::Number(k.get())),
|
||||
Self::String(k) => Ok(Value::String(lua.create_string(k)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{app::App, lives::Lives};
|
|||
|
||||
impl App {
|
||||
pub(crate) fn accept_payload(&mut self, mut cmd: Cmd) {
|
||||
let Some(payload) = cmd.take_data::<Payload>() else {
|
||||
let Some(payload) = cmd.take_any::<Payload>("payload") else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use std::fmt::Display;
|
|||
use mlua::TableExt;
|
||||
use scopeguard::defer;
|
||||
use tracing::warn;
|
||||
use yazi_plugin::{loader::LOADER, OptData, RtRef, LUA};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_plugin::{loader::LOADER, RtRef, LUA};
|
||||
use yazi_shared::{emit, event::Cmd, Layer};
|
||||
|
||||
use crate::{app::App, lives::Lives};
|
||||
|
|
@ -16,7 +17,7 @@ impl App {
|
|||
};
|
||||
|
||||
if !opt.sync {
|
||||
return self.cx.tasks.plugin_micro(opt.name, opt.data.args);
|
||||
return self.cx.tasks.plugin_micro(opt.name, opt.args);
|
||||
}
|
||||
|
||||
if LOADER.read().contains_key(&opt.name) {
|
||||
|
|
@ -25,14 +26,15 @@ impl App {
|
|||
|
||||
tokio::spawn(async move {
|
||||
if LOADER.ensure(&opt.name).await.is_ok() {
|
||||
Self::_plugin_do(opt.name, opt.data);
|
||||
Self::_plugin_do(opt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _plugin_do(name: String, data: OptData) {
|
||||
emit!(Call(Cmd::args("plugin_do", vec![name]).with_data(data), Layer::App));
|
||||
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>) {
|
||||
|
|
@ -53,10 +55,10 @@ impl App {
|
|||
};
|
||||
|
||||
_ = Lives::scope(&self.cx, |_| {
|
||||
if let Some(cb) = opt.data.cb {
|
||||
if let Some(cb) = opt.cb {
|
||||
cb(&LUA, plugin)
|
||||
} else {
|
||||
plugin.call_method("entry", opt.data.args)
|
||||
plugin.call_method("entry", Sendable::vec_to_table(&LUA, opt.args)?)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub struct Opt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self { Self { tx: c.take_data() } }
|
||||
fn from(mut c: Cmd) -> Self { Self { tx: c.take_any("tx") } }
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl TryFrom<Cmd> for Opt {
|
|||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { progress: c.take_data().ok_or(())? })
|
||||
Ok(Self { progress: c.take_any("progress").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ impl<'a> Executor<'a> {
|
|||
on!(forward);
|
||||
|
||||
if cmd.name.as_str() == "complete" {
|
||||
return if cmd.named.contains_key("trigger") {
|
||||
return if cmd.get_bool("trigger") {
|
||||
self.app.cx.completion.trigger(cmd)
|
||||
} else {
|
||||
self.app.cx.input.complete(cmd)
|
||||
|
|
|
|||
|
|
@ -22,18 +22,18 @@ function M:preload()
|
|||
return 0
|
||||
end
|
||||
|
||||
local mimes, last = {}, ya.time()
|
||||
local updates, last = {}, ya.time()
|
||||
local flush = function(force)
|
||||
if not force and ya.time() - last < 0.3 then
|
||||
return
|
||||
end
|
||||
if next(mimes) then
|
||||
ya.manager_emit("update_mimetype", {}, mimes)
|
||||
mimes, last = {}, ya.time()
|
||||
if next(updates) then
|
||||
ya.manager_emit("update_mimetype", { updates = updates })
|
||||
updates, last = {}, ya.time()
|
||||
end
|
||||
end
|
||||
|
||||
local i, j, mime = 1, 0, nil
|
||||
local i, j, valid = 1, 0, nil
|
||||
repeat
|
||||
local line, event = child:read_line_with { timeout = 300 }
|
||||
if event == 3 then
|
||||
|
|
@ -43,11 +43,11 @@ function M:preload()
|
|||
break
|
||||
end
|
||||
|
||||
mime = match_mimetype(line)
|
||||
if mime and string.find(line, mime, 1, true) ~= 1 then
|
||||
valid = match_mimetype(line)
|
||||
if valid and string.find(line, valid, 1, true) ~= 1 then
|
||||
goto continue
|
||||
elseif mime then
|
||||
j, mimes[urls[i]] = j + 1, mime
|
||||
elseif valid then
|
||||
j, updates[urls[i]] = j + 1, valid
|
||||
flush(false)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use mlua::{ExternalError, ExternalResult, Table, TableExt};
|
||||
use tokio::runtime::Handle;
|
||||
use yazi_dds::ValueSendable;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::loader::LOADER;
|
||||
|
||||
pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> {
|
||||
pub async fn entry(name: String, args: Vec<Data>) -> mlua::Result<()> {
|
||||
LOADER.ensure(&name).await.into_lua_err()?;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
|
|
@ -16,7 +17,8 @@ pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> {
|
|||
return Err("unloaded plugin".into_lua_err());
|
||||
};
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("entry", args))
|
||||
Handle::current()
|
||||
.block_on(plugin.call_async_method("entry", Sendable::vec_to_table(&lua, args)))
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use yazi_config::LAYOUT;
|
|||
use yazi_shared::{emit, event::Cmd, Layer};
|
||||
|
||||
use super::slim_lua;
|
||||
use crate::{bindings::{Cast, File, Window}, elements::Rect, loader::LOADER, OptData, LUA};
|
||||
use crate::{bindings::{Cast, File, Window}, elements::Rect, loader::LOADER, Opt, OptCallback, LUA};
|
||||
|
||||
pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> CancellationToken {
|
||||
let ct = CancellationToken::new();
|
||||
|
|
@ -56,18 +56,16 @@ pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> Cancellation
|
|||
}
|
||||
|
||||
pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) {
|
||||
let data = OptData {
|
||||
cb: Some(Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||
plugin.raw_set("window", Window::default())?;
|
||||
plugin.call_method("peek", ())
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
emit!(Call(
|
||||
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
||||
Layer::App
|
||||
));
|
||||
let cb: OptCallback = Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||
plugin.raw_set("window", Window::default())?;
|
||||
plugin.call_method("peek", ())
|
||||
});
|
||||
|
||||
let cmd: Cmd =
|
||||
Opt { name: cmd.name.to_owned(), sync: true, cb: Some(cb), ..Default::default() }.into();
|
||||
|
||||
emit!(Call(cmd.with_name("plugin"), Layer::App));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,17 @@ use mlua::TableExt;
|
|||
use yazi_config::LAYOUT;
|
||||
use yazi_shared::{emit, event::Cmd, Layer};
|
||||
|
||||
use crate::{bindings::{Cast, File}, elements::Rect, OptData, LUA};
|
||||
use crate::{bindings::{Cast, File}, elements::Rect, Opt, OptCallback, LUA};
|
||||
|
||||
pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) {
|
||||
let data = OptData {
|
||||
cb: Some(Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||
plugin.call_method("seek", units)
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
emit!(Call(
|
||||
Cmd::args("plugin", vec![cmd.name.to_owned()]).with_bool("sync", true).with_data(data),
|
||||
Layer::App
|
||||
));
|
||||
let cb: OptCallback = Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||
plugin.call_method("seek", units)
|
||||
});
|
||||
|
||||
let cmd: Cmd =
|
||||
Opt { name: cmd.name.to_owned(), sync: true, cb: Some(cb), ..Default::default() }.into();
|
||||
|
||||
emit!(Call(cmd.with_name("plugin"), Layer::App));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,43 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{Lua, Table};
|
||||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::event::Cmd;
|
||||
use yazi_shared::event::{Cmd, Data};
|
||||
|
||||
pub(super) type OptCallback = Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Opt {
|
||||
pub name: String,
|
||||
pub sync: bool,
|
||||
pub data: OptData,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OptData {
|
||||
pub args: Vec<ValueSendable>,
|
||||
pub cb: Option<Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>>,
|
||||
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(name) = c.take_first().filter(|s| !s.is_empty()) else {
|
||||
let Some(name) = c.take_first_str().filter(|s| !s.is_empty()) else {
|
||||
bail!("plugin name cannot be empty");
|
||||
};
|
||||
|
||||
let mut data: OptData = c.take_data().unwrap_or_default();
|
||||
let args = if let Some(s) = c.get_str("args") {
|
||||
shell_words::split(s)?.into_iter().map(Data::String).collect()
|
||||
} else {
|
||||
c.take_any::<Vec<Data>>("args").unwrap_or_default()
|
||||
};
|
||||
|
||||
if let Some(args) = c.named.get("args") {
|
||||
data.args = shell_words::split(args)?.into_iter().map(ValueSendable::String).collect();
|
||||
}
|
||||
|
||||
Ok(Self { name, sync: c.named.contains_key("sync"), data })
|
||||
Ok(Self { name, sync: c.get_bool("sync"), args, cb: c.take_any("callback") })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Opt> for Cmd {
|
||||
fn from(value: Opt) -> Self {
|
||||
let mut cmd =
|
||||
Cmd::args("", vec![value.name]).with_bool("sync", value.sync).with_any("args", value.args);
|
||||
|
||||
if let Some(cb) = value.cb {
|
||||
cmd = cmd.with_any("callback", cb);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,27 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{ExternalError, Lua, Table, Value};
|
||||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::{emit, event::Cmd, render, Layer};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::{emit, event::{Cmd, Data}, render, Layer};
|
||||
|
||||
use super::Utils;
|
||||
|
||||
impl Utils {
|
||||
fn parse_args(t: Table) -> mlua::Result<(Vec<String>, HashMap<String, String>)> {
|
||||
let mut args = vec![];
|
||||
let mut named = HashMap::new();
|
||||
for result in t.pairs::<Value, Value>() {
|
||||
let (k, v) = result?;
|
||||
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(_) => {
|
||||
args.push(match v {
|
||||
Value::Integer(i) => i.to_string(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::String(s) => s.to_string_lossy().into_owned(),
|
||||
_ => return Err("invalid value in cmd".into_lua_err()),
|
||||
});
|
||||
Value::Integer(i) if i > 0 => {
|
||||
args.insert((i - 1).to_string(), Sendable::value_to_data(v)?);
|
||||
}
|
||||
Value::String(s) => {
|
||||
let v = match v {
|
||||
Value::Boolean(b) if b => String::new(),
|
||||
Value::Boolean(b) if !b => continue,
|
||||
Value::Integer(i) => i.to_string(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::String(s) => s.to_string_lossy().into_owned(),
|
||||
_ => return Err("invalid value in cmd".into_lua_err()),
|
||||
};
|
||||
named.insert(s.to_str()?.replace('_', "-"), v);
|
||||
args.insert(s.to_str()?.replace('_', "-"), Sendable::value_to_data(v)?);
|
||||
}
|
||||
_ => return Err("invalid key in cmd".into_lua_err()),
|
||||
}
|
||||
}
|
||||
Ok((args, named))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn create_cmd(name: String, table: Table, data: Option<Value>) -> mlua::Result<Cmd> {
|
||||
let (args, named) = Self::parse_args(table)?;
|
||||
let mut cmd = Cmd { name, args, named, ..Default::default() };
|
||||
|
||||
if let Some(data) = data.and_then(|v| ValueSendable::try_from(v).ok()) {
|
||||
cmd = cmd.with_data(data);
|
||||
}
|
||||
Ok(cmd)
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
pub(super) fn call(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
||||
|
|
@ -60,16 +35,16 @@ impl Utils {
|
|||
|
||||
ya.raw_set(
|
||||
"app_emit",
|
||||
lua.create_function(|_, (name, table, data): (String, Table, Option<Value>)| {
|
||||
emit!(Call(Self::create_cmd(name, table, data)?, Layer::App));
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::App));
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
||||
ya.raw_set(
|
||||
"manager_emit",
|
||||
lua.create_function(|_, (name, table, data): (String, Table, Option<Value>)| {
|
||||
emit!(Call(Self::create_cmd(name, table, data)?, Layer::Manager));
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Manager));
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ impl Utils {
|
|||
let cand = cand?;
|
||||
cands.push(Control {
|
||||
on: Self::parse_keys(cand.raw_get("on")?)?,
|
||||
run: vec![Cmd::args("callback", vec![i.to_string()]).with_data(tx.clone())],
|
||||
run: vec![Cmd::args("callback", vec![i.to_string()]).with_any("tx", tx.clone())],
|
||||
desc: cand.raw_get("desc").ok(),
|
||||
});
|
||||
}
|
||||
|
|
@ -46,8 +46,8 @@ impl Utils {
|
|||
emit!(Call(
|
||||
Cmd::new("show")
|
||||
.with("layer", Layer::Which)
|
||||
.with_bool("silent", t.raw_get("silent").unwrap_or_default())
|
||||
.with_data(cands),
|
||||
.with_any("candidates", cands)
|
||||
.with_bool("silent", t.raw_get("silent").unwrap_or_default()),
|
||||
Layer::Which
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ impl Utils {
|
|||
};
|
||||
lock.data = vec![Box::new(Paragraph { area: *area, text, ..Default::default() })];
|
||||
|
||||
emit!(Call(Cmd::new("preview").with_data(lock), Layer::Manager));
|
||||
emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager));
|
||||
(true, Value::Nil).into_lua_multi(lua)
|
||||
})?,
|
||||
)?;
|
||||
|
|
@ -67,7 +67,7 @@ impl Utils {
|
|||
..Default::default()
|
||||
})];
|
||||
|
||||
emit!(Call(Cmd::new("preview").with_data(lock), Layer::Manager));
|
||||
emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager));
|
||||
(true, Value::Nil).into_lua_multi(lua)
|
||||
})?,
|
||||
)?;
|
||||
|
|
@ -78,7 +78,7 @@ impl Utils {
|
|||
let mut lock = PreviewLock::try_from(t)?;
|
||||
lock.data = widgets.into_iter().filter_map(cast_to_renderable).collect();
|
||||
|
||||
emit!(Call(Cmd::new("preview").with_data(lock), Layer::Manager));
|
||||
emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager));
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use mlua::{ExternalError, ExternalResult, Function, IntoLua, Lua, Table, Value, Variadic};
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::{emit, event::Cmd, Layer};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::{emit, event::{Cmd, Data}, Layer};
|
||||
|
||||
use super::Utils;
|
||||
use crate::{loader::LOADER, runtime::RtRef, OptData};
|
||||
use crate::{loader::LOADER, runtime::RtRef, OptCallback};
|
||||
|
||||
impl Utils {
|
||||
pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> {
|
||||
|
|
@ -37,7 +37,7 @@ impl Utils {
|
|||
return Err("`ya.sync()` must be called in a plugin").into_lua_err();
|
||||
};
|
||||
|
||||
Self::retrieve(cur, block, args).await
|
||||
Sendable::vec_to_variadic(lua, Self::retrieve(cur, block, args).await?)
|
||||
})
|
||||
})?,
|
||||
)?;
|
||||
|
|
@ -49,39 +49,37 @@ impl Utils {
|
|||
name: String,
|
||||
calls: usize,
|
||||
args: Variadic<Value<'_>>,
|
||||
) -> mlua::Result<mlua::Variadic<ValueSendable>> {
|
||||
let args = ValueSendable::try_from_variadic(args)?;
|
||||
let (tx, rx) = oneshot::channel::<Vec<ValueSendable>>();
|
||||
) -> mlua::Result<Vec<Data>> {
|
||||
let args = Sendable::variadic_to_vec(args)?;
|
||||
let (tx, rx) = oneshot::channel::<Vec<Data>>();
|
||||
|
||||
let data = OptData {
|
||||
cb: Some({
|
||||
let name = name.clone();
|
||||
Box::new(move |lua, plugin| {
|
||||
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.get_block(&name, calls) else {
|
||||
return Err("sync block not found".into_lua_err());
|
||||
};
|
||||
let callback: OptCallback = {
|
||||
let name = name.clone();
|
||||
Box::new(move |lua, plugin| {
|
||||
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.get_block(&name, calls) else {
|
||||
return Err("sync block not found".into_lua_err());
|
||||
};
|
||||
|
||||
let mut self_args = Vec::with_capacity(args.len() + 1);
|
||||
self_args.push(Value::Table(plugin));
|
||||
for arg in args {
|
||||
self_args.push(arg.into_lua(lua)?);
|
||||
}
|
||||
let mut self_args = Vec::with_capacity(args.len() + 1);
|
||||
self_args.push(Value::Table(plugin));
|
||||
for arg in args {
|
||||
self_args.push(Sendable::data_to_value(lua, arg)?);
|
||||
}
|
||||
|
||||
let values =
|
||||
ValueSendable::try_from_variadic(block.call(Variadic::from_iter(self_args))?)?;
|
||||
tx.send(values).map_err(|_| "send failed".into_lua_err())
|
||||
})
|
||||
}),
|
||||
..Default::default()
|
||||
let values = Sendable::variadic_to_vec(block.call(Variadic::from_iter(self_args))?)?;
|
||||
tx.send(values).map_err(|_| "send failed".into_lua_err())
|
||||
})
|
||||
};
|
||||
|
||||
emit!(Call(
|
||||
Cmd::args("plugin", vec![name.clone()]).with_bool("sync", true).with_data(data),
|
||||
Cmd::args("plugin", vec![name.clone()])
|
||||
.with_bool("sync", true)
|
||||
.with_any("callback", callback),
|
||||
Layer::App
|
||||
));
|
||||
|
||||
Ok(Variadic::from_iter(rx.await.map_err(|_| {
|
||||
rx.await.map_err(|_| {
|
||||
format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err()
|
||||
})?))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ impl AppProxy {
|
|||
#[inline]
|
||||
pub async fn stop() {
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
emit!(Call(Cmd::new("stop").with_data(tx), Layer::App));
|
||||
emit!(Call(Cmd::new("stop").with_any("tx", tx), Layer::App));
|
||||
rx.await.ok();
|
||||
}
|
||||
|
||||
|
|
@ -22,13 +22,13 @@ impl AppProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn notify(opt: NotifyOpt) {
|
||||
emit!(Call(Cmd::new("notify").with_data(opt), Layer::App));
|
||||
emit!(Call(Cmd::new("notify").with_any("option", opt), Layer::App));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn notify_warn(title: &str, content: &str) {
|
||||
emit!(Call(
|
||||
Cmd::new("notify").with_data(NotifyOpt {
|
||||
Cmd::new("notify").with_any("option", NotifyOpt {
|
||||
title: title.to_owned(),
|
||||
content: content.to_owned(),
|
||||
level: NotifyLevel::Warn,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@ use tokio::sync::mpsc;
|
|||
use yazi_config::popup::InputCfg;
|
||||
use yazi_shared::{emit, event::Cmd, InputError, Layer};
|
||||
|
||||
use crate::options::InputOpt;
|
||||
|
||||
pub struct InputProxy;
|
||||
|
||||
impl InputProxy {
|
||||
#[inline]
|
||||
pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
emit!(Call(Cmd::new("show").with_data(InputOpt { cfg, tx }), Layer::Input));
|
||||
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Input));
|
||||
rx
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ impl ManagerProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn open_do(opt: OpenDoOpt) {
|
||||
emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager));
|
||||
emit!(Call(Cmd::new("open_do").with_any("option", opt), Layer::Manager));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove_do(targets: Vec<Url>, permanently: bool) {
|
||||
emit!(Call(
|
||||
Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets),
|
||||
Cmd::new("remove_do").with_bool("permanently", permanently).with_any("targets", targets),
|
||||
Layer::Manager
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
use tokio::sync::mpsc;
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_shared::{event::Cmd, InputError};
|
||||
|
||||
pub struct InputOpt {
|
||||
pub cfg: InputCfg,
|
||||
pub tx: mpsc::UnboundedSender<Result<String, InputError>>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for InputOpt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
|
||||
}
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
mod input;
|
||||
mod notify;
|
||||
mod open;
|
||||
mod process;
|
||||
mod select;
|
||||
|
||||
pub use input::*;
|
||||
pub use notify::*;
|
||||
pub use open::*;
|
||||
pub use process::*;
|
||||
pub use select::*;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub struct NotifyOpt {
|
|||
impl TryFrom<Cmd> for NotifyOpt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_any("option").ok_or(()) }
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<mlua::Table<'a>> for NotifyOpt {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub struct OpenDoOpt {
|
|||
}
|
||||
|
||||
impl From<Cmd> for OpenDoOpt {
|
||||
fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() }
|
||||
fn from(mut c: Cmd) -> Self { c.take_any("option").unwrap_or_default() }
|
||||
}
|
||||
|
||||
// --- Open with
|
||||
|
|
@ -24,5 +24,5 @@ pub struct OpenWithOpt {
|
|||
impl TryFrom<Cmd> for OpenWithOpt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_any("option").ok_or(()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@ pub struct ProcessExecOpt {
|
|||
impl TryFrom<Cmd> for ProcessExecOpt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_any("option").ok_or(()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
use tokio::sync::oneshot;
|
||||
use yazi_config::popup::SelectCfg;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
pub struct SelectOpt {
|
||||
pub cfg: SelectCfg,
|
||||
pub tx: oneshot::Sender<anyhow::Result<usize>>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for SelectOpt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
|
||||
}
|
||||
|
|
@ -2,15 +2,13 @@ use tokio::sync::oneshot;
|
|||
use yazi_config::popup::SelectCfg;
|
||||
use yazi_shared::{emit, event::Cmd, Layer};
|
||||
|
||||
use crate::options::SelectOpt;
|
||||
|
||||
pub struct SelectProxy;
|
||||
|
||||
impl SelectProxy {
|
||||
#[inline]
|
||||
pub async fn show(cfg: SelectCfg) -> anyhow::Result<usize> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
emit!(Call(Cmd::new("show").with_data(SelectOpt { cfg, tx }), Layer::Select));
|
||||
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Select));
|
||||
rx.await?
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,14 +11,17 @@ pub struct TasksProxy;
|
|||
impl TasksProxy {
|
||||
#[inline]
|
||||
pub fn open_with(targets: Vec<Url>, opener: Cow<'static, Opener>) {
|
||||
emit!(Call(Cmd::new("open_with").with_data(OpenWithOpt { targets, opener }), Layer::Tasks));
|
||||
emit!(Call(
|
||||
Cmd::new("open_with").with_any("option", OpenWithOpt { targets, opener }),
|
||||
Layer::Tasks
|
||||
));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn process_exec(args: Vec<OsString>, opener: Cow<'static, Opener>) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
emit!(Call(
|
||||
Cmd::new("process_exec").with_data(ProcessExecOpt { args, opener, done: tx }),
|
||||
Cmd::new("process_exec").with_any("option", ProcessExecOpt { args, opener, done: tx }),
|
||||
Layer::Tasks
|
||||
));
|
||||
rx.await.ok();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, collections::VecDeque, fs::Metadata, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::{future::BoxFuture, FutureExt};
|
||||
use tokio::{fs, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
||||
use tracing::warn;
|
||||
|
|
@ -51,9 +51,10 @@ impl File {
|
|||
if task.retry < TASKS.bizarre_retry
|
||||
&& matches!(e.raw_os_error(), Some(1) | Some(93)) =>
|
||||
{
|
||||
self.log(task.id, format!("Paste task retry: {:?}", task))?;
|
||||
task.retry += 1;
|
||||
return Ok(self.macro_.send(FileOp::Paste(task).into(), LOW).await?);
|
||||
self.log(task.id, format!("Paste task retry: {:?}", task))?;
|
||||
self.queue(FileOp::Paste(task), LOW).await?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
|
|
@ -147,9 +148,9 @@ impl File {
|
|||
self.prog.send(TaskProg::New(id, meta.len()))?;
|
||||
|
||||
if meta.is_file() {
|
||||
self.macro_.send(FileOp::Paste(task).into(), LOW).await?;
|
||||
self.queue(FileOp::Paste(task), LOW).await?;
|
||||
} else if meta.is_symlink() {
|
||||
self.macro_.send(FileOp::Link(task.to_link(meta)).into(), NORMAL).await?;
|
||||
self.queue(FileOp::Link(task.to_link(meta)), NORMAL).await?;
|
||||
}
|
||||
return self.succ(id);
|
||||
}
|
||||
|
|
@ -193,9 +194,9 @@ impl File {
|
|||
self.prog.send(TaskProg::New(task.id, meta.len()))?;
|
||||
|
||||
if meta.is_file() {
|
||||
self.macro_.send(FileOp::Paste(task.clone()).into(), LOW).await?;
|
||||
self.queue(FileOp::Paste(task.clone()), LOW).await?;
|
||||
} else if meta.is_symlink() {
|
||||
self.macro_.send(FileOp::Link(task.to_link(meta)).into(), NORMAL).await?;
|
||||
self.queue(FileOp::Link(task.to_link(meta)), NORMAL).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -209,7 +210,7 @@ impl File {
|
|||
}
|
||||
|
||||
self.prog.send(TaskProg::New(id, task.meta.as_ref().unwrap().len()))?;
|
||||
self.macro_.send(FileOp::Link(task).into(), NORMAL).await?;
|
||||
self.queue(FileOp::Link(task), NORMAL).await?;
|
||||
self.succ(id)
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +220,7 @@ impl File {
|
|||
let id = task.id;
|
||||
task.length = meta.len();
|
||||
self.prog.send(TaskProg::New(id, meta.len()))?;
|
||||
self.macro_.send(FileOp::Delete(task).into(), NORMAL).await?;
|
||||
self.queue(FileOp::Delete(task), NORMAL).await?;
|
||||
return self.succ(id);
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +239,7 @@ impl File {
|
|||
task.target = Url::from(entry.path());
|
||||
task.length = meta.len();
|
||||
self.prog.send(TaskProg::New(task.id, meta.len()))?;
|
||||
self.macro_.send(FileOp::Delete(task.clone()).into(), NORMAL).await?;
|
||||
self.queue(FileOp::Delete(task.clone()), NORMAL).await?;
|
||||
}
|
||||
}
|
||||
self.succ(task.id)
|
||||
|
|
@ -249,7 +250,7 @@ impl File {
|
|||
task.length = calculate_size(&task.target).await;
|
||||
|
||||
self.prog.send(TaskProg::New(id, task.length))?;
|
||||
self.macro_.send(FileOp::Trash(task).into(), LOW).await?;
|
||||
self.queue(FileOp::Trash(task), LOW).await?;
|
||||
self.succ(id)
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +297,11 @@ impl File {
|
|||
fn log(&self, id: usize, line: String) -> Result<()> {
|
||||
Ok(self.prog.send(TaskProg::Log(id, line))?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn queue(&self, op: impl Into<TaskOp>, priority: u8) -> Result<()> {
|
||||
self.macro_.send(op.into(), priority).await.map_err(|_| anyhow!("Failed to send task"))
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOpPaste {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_dds::ValueSendable;
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PluginOp {
|
||||
|
|
@ -17,5 +17,5 @@ impl PluginOp {
|
|||
pub struct PluginOpEntry {
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub args: Vec<ValueSendable>,
|
||||
pub args: Vec<Data>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_plugin::isolate;
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ impl Plugin {
|
|||
let id = task.id;
|
||||
|
||||
self.prog.send(TaskProg::New(id, 0))?;
|
||||
self.macro_.try_send(PluginOp::Entry(task).into(), HIGH)?;
|
||||
self.queue(PluginOp::Entry(task), HIGH)?;
|
||||
self.succ(id)
|
||||
}
|
||||
}
|
||||
|
|
@ -56,4 +56,9 @@ impl Plugin {
|
|||
fn fail(&self, id: usize, reason: String) -> Result<()> {
|
||||
Ok(self.prog.send(TaskProg::Fail(id, reason))?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self, op: impl Into<TaskOp>, priority: u8) -> Result<()> {
|
||||
self.macro_.try_send(op.into(), priority).map_err(|_| anyhow!("Failed to send task"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use parking_lot::RwLock;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::error;
|
||||
|
|
@ -73,8 +73,8 @@ impl Preload {
|
|||
self.prog.send(TaskProg::New(id, 0))?;
|
||||
|
||||
match task.plugin.prio {
|
||||
Priority::Low => self.macro_.send(PreloadOp::Rule(task).into(), NORMAL).await?,
|
||||
Priority::Normal => self.macro_.send(PreloadOp::Rule(task).into(), HIGH).await?,
|
||||
Priority::Low => self.queue(PreloadOp::Rule(task), NORMAL).await?,
|
||||
Priority::Normal => self.queue(PreloadOp::Rule(task), HIGH).await?,
|
||||
Priority::High => self.work(PreloadOp::Rule(task)).await?,
|
||||
}
|
||||
self.succ(id)
|
||||
|
|
@ -97,4 +97,9 @@ impl Preload {
|
|||
fn fail(&self, id: usize, reason: String) -> Result<()> {
|
||||
Ok(self.prog.send(TaskProg::Fail(id, reason))?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn queue(&self, op: impl Into<TaskOp>, priority: u8) -> Result<()> {
|
||||
self.macro_.send(op.into(), priority).await.map_err(|_| anyhow!("Failed to send task"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use futures::{future::BoxFuture, FutureExt};
|
|||
use parking_lot::Mutex;
|
||||
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle};
|
||||
use yazi_config::{open::Opener, plugin::PluginRule, TASKS};
|
||||
use yazi_dds::{Pump, ValueSendable};
|
||||
use yazi_shared::{fs::{unique_path, Url}, Throttle};
|
||||
use yazi_dds::Pump;
|
||||
use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle};
|
||||
|
||||
use super::{Ongoing, TaskProg, TaskStage};
|
||||
use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL};
|
||||
|
|
@ -182,7 +182,7 @@ impl Scheduler {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<ValueSendable>) {
|
||||
pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{name}`"));
|
||||
|
||||
let plugin = self.plugin.clone();
|
||||
|
|
@ -195,7 +195,7 @@ impl Scheduler {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<ValueSendable>) {
|
||||
pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
|
||||
let id = self.ongoing.lock().add(TaskKind::User, format!("Run macro plugin `{name}`"));
|
||||
|
||||
self.plugin.macro_(PluginOpEntry { id, name, args }).ok();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{any::Any, collections::HashMap, fmt::{self, Display}, mem};
|
||||
use std::{any::Any, collections::HashMap, fmt::{self, Display}};
|
||||
|
||||
use super::Data;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Cmd {
|
||||
pub name: String,
|
||||
pub args: Vec<String>,
|
||||
pub named: HashMap<String, String>,
|
||||
pub data: Option<Box<dyn Any + Send>>,
|
||||
pub name: String,
|
||||
pub args: HashMap<String, Data>,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
|
|
@ -14,49 +14,70 @@ impl Cmd {
|
|||
|
||||
#[inline]
|
||||
pub fn args(name: &str, args: Vec<String>) -> Self {
|
||||
Self { name: name.to_owned(), args, ..Default::default() }
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
args: args.into_iter().enumerate().map(|(i, s)| (i.to_string(), Data::String(s))).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with(mut self, name: impl ToString, value: impl ToString) -> Self {
|
||||
self.named.insert(name.to_string(), value.to_string());
|
||||
self.args.insert(name.to_string(), Data::String(value.to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_bool(mut self, name: impl ToString, state: bool) -> Self {
|
||||
if state {
|
||||
self.named.insert(name.to_string(), Default::default());
|
||||
}
|
||||
self.args.insert(name.to_string(), Data::Boolean(state));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_data(mut self, data: impl Any + Send) -> Self {
|
||||
self.data = Some(Box::new(data));
|
||||
pub fn with_any(mut self, name: impl ToString, data: impl Any + Send) -> Self {
|
||||
self.args.insert(name.to_string(), Data::Any(Box::new(data)));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_data<T: 'static>(&mut self) -> Option<T> {
|
||||
self.data.take().and_then(|d| d.downcast::<T>().ok()).map(|d| *d)
|
||||
pub fn with_name(mut self, name: impl ToString) -> Self {
|
||||
self.name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_first(&mut self) -> Option<String> {
|
||||
if self.args.is_empty() { None } else { Some(mem::take(&mut self.args[0])) }
|
||||
pub fn get_str(&self, name: &str) -> Option<&str> { self.args.get(name).and_then(Data::as_str) }
|
||||
|
||||
#[inline]
|
||||
pub fn get_bool(&self, name: &str) -> bool {
|
||||
self.args.get(name).and_then(Data::as_bool).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_name(&mut self, name: &str) -> Option<String> { self.named.remove(name) }
|
||||
pub fn take_data(&mut self, name: &str) -> Option<Data> { self.args.remove(name) }
|
||||
|
||||
#[inline]
|
||||
pub fn clone_without_data(&self) -> Self {
|
||||
pub fn take_str(&mut self, name: &str) -> Option<String> {
|
||||
if let Some(Data::String(s)) = self.args.remove(name) { Some(s) } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_first_str(&mut self) -> Option<String> {
|
||||
if let Some(Data::String(s)) = self.args.remove("0") { Some(s) } else { None }
|
||||
}
|
||||
|
||||
#[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 shallow_clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
args: self.args.clone(),
|
||||
named: self.named.clone(),
|
||||
data: None,
|
||||
name: self.name.clone(),
|
||||
args: self
|
||||
.args
|
||||
.iter()
|
||||
.filter_map(|(k, v)| v.shallow_clone().map(|v| (k.clone(), v)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,13 +85,15 @@ impl Cmd {
|
|||
impl Display for Cmd {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)?;
|
||||
if !self.args.is_empty() {
|
||||
write!(f, " {}", self.args.join(" "))?;
|
||||
}
|
||||
for (k, v) in &self.named {
|
||||
write!(f, " --{k}")?;
|
||||
if !v.is_empty() {
|
||||
write!(f, "={v}")?;
|
||||
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}")?;
|
||||
}
|
||||
} 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}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
96
yazi-shared/src/event/data.rs
Normal file
96
yazi-shared/src/event/data.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use std::{any::Any, collections::HashMap};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{fs::Url, OrderedFloat};
|
||||
|
||||
// --- Data
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Data {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(f64),
|
||||
String(String),
|
||||
Table(HashMap<DataKey, Data>),
|
||||
#[serde(skip)]
|
||||
Url(Url),
|
||||
#[serde(skip)]
|
||||
Any(Box<dyn Any + Send>),
|
||||
}
|
||||
|
||||
impl Data {
|
||||
#[inline]
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
match self {
|
||||
Self::Boolean(b) => Some(*b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_any<T: 'static>(&self) -> Option<&T> {
|
||||
match self {
|
||||
Self::Any(b) => b.downcast_ref::<T>(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_any<T: 'static>(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Any(b) => b.downcast::<T>().ok().map(|b| *b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_table_string(self) -> HashMap<String, String> {
|
||||
let Self::Table(table) = self else {
|
||||
return Default::default();
|
||||
};
|
||||
|
||||
let mut map = HashMap::with_capacity(table.len());
|
||||
for pair in table {
|
||||
if let (DataKey::String(k), Self::String(v)) = pair {
|
||||
map.insert(k, v);
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shallow_clone(&self) -> Option<Self> {
|
||||
match self {
|
||||
Self::Boolean(b) => Some(Self::Boolean(*b)),
|
||||
Self::String(s) => Some(Self::String(s.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Key
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DataKey {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(OrderedFloat),
|
||||
String(String),
|
||||
#[serde(skip)]
|
||||
Url(Url),
|
||||
}
|
||||
|
||||
impl DataKey {
|
||||
#[inline]
|
||||
pub fn is_numeric(&self) -> bool { matches!(self, Self::Integer(_) | Self::Number(_)) }
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
mod cmd;
|
||||
mod data;
|
||||
mod event;
|
||||
mod render;
|
||||
|
||||
pub use cmd::*;
|
||||
pub use data::*;
|
||||
pub use event::*;
|
||||
pub use render::*;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl FilesOp {
|
|||
|
||||
#[inline]
|
||||
pub fn emit(self) {
|
||||
emit!(Call(Cmd::new("update_files").with_data(self), Layer::Manager));
|
||||
emit!(Call(Cmd::new("update_files").with_any("op", self), Layer::Manager));
|
||||
}
|
||||
|
||||
pub fn prepare(url: &Url) -> u64 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue