perf!: cacheable iterator (#2997)

This commit is contained in:
三咲雅 misaki masa 2025-07-18 07:48:00 +08:00 committed by GitHub
parent 5f8a1496fe
commit 0fd74ae44c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 414 additions and 303 deletions

2
Cargo.lock generated
View file

@ -3670,6 +3670,7 @@ dependencies = [
"yazi-boot", "yazi-boot",
"yazi-fs", "yazi-fs",
"yazi-macro", "yazi-macro",
"yazi-parser",
"yazi-shared", "yazi-shared",
] ]
@ -3772,7 +3773,6 @@ dependencies = [
"yazi-binding", "yazi-binding",
"yazi-boot", "yazi-boot",
"yazi-config", "yazi-config",
"yazi-dds",
"yazi-fs", "yazi-fs",
"yazi-macro", "yazi-macro",
"yazi-shared", "yazi-shared",

View file

@ -3,9 +3,10 @@ use std::{ffi::OsString, mem, path::{MAIN_SEPARATOR_STR, Path, PathBuf}};
use anyhow::Result; use anyhow::Result;
use tokio::fs; use tokio::fs;
use yazi_fs::{CWD, expand_path}; use yazi_fs::{CWD, expand_path};
use yazi_macro::{act, emit, render, succ}; use yazi_macro::{act, render, succ};
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt}; use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
use yazi_shared::{event::{Cmd, Data}, natsort}; use yazi_proxy::CmpProxy;
use yazi_shared::{event::Data, natsort};
use crate::{Actor, Ctx}; use crate::{Actor, Ctx};
@ -55,13 +56,7 @@ impl Actor for Trigger {
cache.sort_unstable_by(|a, b| { cache.sort_unstable_by(|a, b| {
natsort(a.name.as_encoded_bytes(), b.name.as_encoded_bytes(), false) natsort(a.name.as_encoded_bytes(), b.name.as_encoded_bytes(), false)
}); });
emit!(Call( CmpProxy::show(ShowOpt { cache, cache_name: parent, word: word.into(), ticket });
Cmd::new("cmp:show")
.with_any("cache", cache)
.with_any("cache-name", parent)
.with("word", word)
.with("ticket", ticket)
));
} }
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())

View file

@ -4,11 +4,10 @@ use anyhow::Result;
use tokio::pin; use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_fs::FilterCase; use yazi_macro::succ;
use yazi_macro::{emit, succ};
use yazi_parser::tab::FilterOpt; use yazi_parser::tab::FilterOpt;
use yazi_proxy::InputProxy; use yazi_proxy::{InputProxy, MgrProxy};
use yazi_shared::{Debounce, errors::InputError, event::{Cmd, Data}}; use yazi_shared::{Debounce, errors::InputError, event::Data};
use crate::{Actor, Ctx}; use crate::{Actor, Ctx};
@ -30,12 +29,7 @@ impl Actor for Filter {
let done = result.is_ok(); let done = result.is_ok();
let (Ok(s) | Err(InputError::Typed(s))) = result else { continue }; let (Ok(s) | Err(InputError::Typed(s))) = result else { continue };
emit!(Call( MgrProxy::filter_do(FilterOpt { query: s.into(), case: opt.case, done });
Cmd::args("mgr:filter_do", [s])
.with("smart", opt.case == FilterCase::Smart)
.with("insensitive", opt.case == FilterCase::Insensitive)
.with("done", done)
));
} }
}); });
succ!(); succ!();

View file

@ -4,11 +4,10 @@ use anyhow::Result;
use tokio::pin; use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_fs::FilterCase; use yazi_macro::succ;
use yazi_macro::{emit, succ}; use yazi_parser::tab::{FindDoOpt, FindOpt};
use yazi_parser::tab::FindOpt; use yazi_proxy::{InputProxy, MgrProxy};
use yazi_proxy::InputProxy; use yazi_shared::{Debounce, errors::InputError, event::Data};
use yazi_shared::{Debounce, errors::InputError, event::{Cmd, Data}};
use crate::{Actor, Ctx}; use crate::{Actor, Ctx};
@ -27,12 +26,7 @@ impl Actor for Find {
pin!(rx); pin!(rx);
while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await { while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await {
emit!(Call( MgrProxy::find_do(FindDoOpt { query: s.into(), prev: opt.prev, case: opt.case });
Cmd::args("mgr:find_do", [s])
.with("previous", opt.prev)
.with("smart", opt.case == FilterCase::Smart)
.with("insensitive", opt.case == FilterCase::Insensitive)
));
} }
}); });
succ!(); succ!();

View file

@ -9,7 +9,7 @@ use crate::{Actor, Ctx};
pub struct UpdateYanked; pub struct UpdateYanked;
impl Actor for UpdateYanked { impl Actor for UpdateYanked {
type Options = UpdateYankedOpt; type Options = UpdateYankedOpt<'static>;
const NAME: &'static str = "update_yanked"; const NAME: &'static str = "update_yanked";
@ -18,7 +18,7 @@ impl Actor for UpdateYanked {
succ!(); succ!();
} }
cx.mgr.yanked = Yanked::new(opt.cut, opt.urls); cx.mgr.yanked = Yanked::new(opt.cut, opt.urls.into_owned());
succ!(render!()); succ!(render!());
} }
} }

68
yazi-binding/src/iter.rs Normal file
View file

@ -0,0 +1,68 @@
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, IntoLuaMulti, Lua, MetaMethod, UserData, UserDataMethods, UserDataRefMut, Value};
pub struct Iter<I: Iterator<Item = T>, T> {
iter: I,
len: Option<usize>,
count: usize,
cache: Vec<Value>,
}
impl<I, T> Iter<I, T>
where
I: Iterator<Item = T> + 'static,
T: IntoLua + 'static,
{
#[inline]
pub fn new(iter: I, len: Option<usize>) -> Self { Self { iter, len, count: 0, cache: vec![] } }
}
impl<I, T> Iter<I, T>
where
I: Iterator<Item = T> + 'static,
T: FromLua + 'static,
{
#[inline]
pub fn into_iter(self, lua: &Lua) -> impl Iterator<Item = mlua::Result<T>> {
self
.cache
.into_iter()
.map(|cached| T::from_lua(cached, lua))
.chain(self.iter.map(|rest| Ok(rest)))
}
}
impl<I, T> UserData for Iter<I, T>
where
I: Iterator<Item = T> + 'static,
T: IntoLua + 'static,
{
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Len, |_, me, ()| {
if let Some(len) = me.len {
Ok(len)
} else {
Err(format!("Length is unknown for {}", std::any::type_name::<Self>()).into_lua_err())
}
});
methods.add_meta_function(MetaMethod::Pairs, |lua, ud: AnyUserData| {
let iter = lua.create_function(|lua, mut me: UserDataRefMut<Self>| {
if let Some(next) = me.cache.get(me.count).cloned() {
me.count += 1;
(me.count, next).into_lua_multi(lua)
} else if let Some(next) = me.iter.next() {
let value = next.into_lua(lua)?;
me.cache.push(value.clone());
me.count += 1;
(me.count, value).into_lua_multi(lua)
} else {
().into_lua_multi(lua)
}
})?;
ud.borrow_mut::<Self>()?.count = 0;
Ok((iter, ud))
});
}
}

View file

@ -4,4 +4,4 @@ mod macros;
yazi_macro::mod_pub!(elements); yazi_macro::mod_pub!(elements);
yazi_macro::mod_flat!(cha color composer error file icon id permit runtime stage style url urn); yazi_macro::mod_flat!(cha color composer error file icon id iter permit runtime stage style url urn utils);

10
yazi-binding/src/utils.rs Normal file
View file

@ -0,0 +1,10 @@
use mlua::{IntoLua, Lua, Table, Value};
pub fn get_metatable(lua: &Lua, value: impl IntoLua) -> mlua::Result<Table> {
let (_, mt): (Value, Table) = unsafe {
lua.exec_raw(value.into_lua(lua)?, |state| {
mlua::ffi::lua_getmetatable(state, -1);
})
}?;
Ok(mt)
}

View file

@ -3,10 +3,9 @@ use std::{sync::Arc, time::Duration};
use parking_lot::Mutex; use parking_lot::Mutex;
use tokio::{task::JoinHandle, time::sleep}; use tokio::{task::JoinHandle, time::sleep};
use yazi_adapter::Dimension; use yazi_adapter::Dimension;
use yazi_macro::emit;
use yazi_parser::app::TasksProgress; use yazi_parser::app::TasksProgress;
use yazi_proxy::AppProxy;
use yazi_scheduler::{Ongoing, Scheduler, TaskSummary}; use yazi_scheduler::{Ongoing, Scheduler, TaskSummary};
use yazi_shared::event::Cmd;
use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT}; use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT};
@ -33,7 +32,7 @@ impl Tasks {
let new = ongoing.lock().progress(); let new = ongoing.lock().progress();
if last != new { if last != new {
last = new; last = new;
emit!(Call(Cmd::new("app:update_progress").with_any("progress", new))); AppProxy::update_progress(new);
} }
} }
}); });

View file

@ -17,6 +17,7 @@ yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" } yazi-boot = { path = "../yazi-boot", version = "25.6.11" }
yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-fs = { path = "../yazi-fs", version = "25.6.11" }
yazi-macro = { path = "../yazi-macro", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
yazi-parser = { path = "../yazi-parser", version = "25.6.11" }
yazi-shared = { path = "../yazi-shared", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
# External dependencies # External dependencies

View file

@ -0,0 +1,7 @@
use yazi_shared::Source;
pub struct BeforeQuit {
// FIXME
// pub opts: BeforeQuitOpt,
pub source: Source,
}

View file

@ -47,9 +47,9 @@ impl Body<'static> {
}) })
} }
pub fn from_lua(kind: &str, value: Value) -> mlua::Result<Self> { pub fn from_lua(lua: &Lua, kind: &str, value: Value) -> mlua::Result<Self> {
Self::validate(kind).into_lua_err()?; Self::validate(kind).into_lua_err()?;
BodyCustom::from_lua(kind, value) BodyCustom::from_lua(lua, kind, value)
} }
pub fn validate(kind: &str) -> Result<()> { pub fn validate(kind: &str) -> Result<()> {

View file

@ -41,6 +41,7 @@ impl IntoLua for BodyBulk<'static> {
} }
// --- Iterator // --- Iterator
// TODO: use `yazi_binding::Iter` instead
pub struct BodyBulkIter { pub struct BodyBulkIter {
pub inner: hash_map::IntoIter<Cow<'static, Url>, Cow<'static, Url>>, pub inner: hash_map::IntoIter<Cow<'static, Url>, Cow<'static, Url>>,
} }

View file

@ -16,8 +16,8 @@ impl BodyCustom {
Ok(Self { kind: kind.to_owned(), data: serde_json::from_str(data)? }.into()) Ok(Self { kind: kind.to_owned(), data: serde_json::from_str(data)? }.into())
} }
pub fn from_lua(kind: &str, data: Value) -> mlua::Result<Body<'static>> { pub fn from_lua(lua: &Lua, kind: &str, data: Value) -> mlua::Result<Body<'static>> {
Ok(Self { kind: kind.to_owned(), data: Sendable::value_to_data(data)? }.into()) Ok(Self { kind: kind.to_owned(), data: Sendable::value_to_data(lua, data)? }.into())
} }
} }

View file

@ -1,5 +1,5 @@
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
yazi_macro::mod_flat!( yazi_macro::mod_flat!(
body bulk bye cd custom delete hey hi hover load mount r#move rename tab trash yank before_quit body bulk bye cd custom delete hey hi hover load mount r#move rename tab trash yank
); );

View file

@ -1,28 +1,24 @@
use std::{borrow::Cow, collections::HashSet}; use std::{borrow::Cow, collections::HashSet};
use mlua::{IntoLua, Lua, MetaMethod, UserData, Value}; use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yazi_parser::mgr::UpdateYankedOpt;
use yazi_shared::url::Url; use yazi_shared::url::Url;
use super::Body; use super::Body;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct BodyYank<'a> { pub struct BodyYank<'a>(UpdateYankedOpt<'a>);
pub cut: bool,
pub urls: Cow<'a, HashSet<Url>>,
#[serde(skip)]
dummy: bool,
}
impl<'a> BodyYank<'a> { impl<'a> BodyYank<'a> {
pub fn borrowed(cut: bool, urls: &'a HashSet<Url>) -> Body<'a> { pub fn borrowed(cut: bool, urls: &'a HashSet<Url>) -> Body<'a> {
Self { cut, urls: Cow::Borrowed(urls), dummy: false }.into() Self(UpdateYankedOpt { cut, urls: Cow::Borrowed(urls) }).into()
} }
} }
impl BodyYank<'static> { impl BodyYank<'static> {
pub fn owned(cut: bool, _: &HashSet<Url>) -> Body<'static> { pub fn owned(cut: bool, _: &HashSet<Url>) -> Body<'static> {
Self { cut, urls: Default::default(), dummy: true }.into() Self(UpdateYankedOpt { cut, urls: Default::default() }).into()
} }
} }
@ -31,36 +27,5 @@ impl<'a> From<BodyYank<'a>> for Body<'a> {
} }
impl IntoLua for BodyYank<'static> { impl IntoLua for BodyYank<'static> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { self.0.into_lua(lua) }
if let Some(Cow::Owned(urls)) = Some(self.urls).filter(|_| !self.dummy) {
BodyYankIter { cut: self.cut, urls: urls.into_iter().collect() }.into_lua(lua)
} else {
lua.create_table()?.into_lua(lua)
}
}
}
// --- Iterator
#[derive(Clone)]
pub struct BodyYankIter {
pub cut: bool,
pub urls: Vec<Url>,
}
impl UserData for BodyYankIter {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("cut", |_, me| Ok(me.cut));
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.urls.len()));
methods.add_meta_method(MetaMethod::Index, |_, me, idx: usize| {
Ok(if idx > me.urls.len() || idx == 0 {
None
} else {
Some(yazi_binding::Url::new(me.urls[idx - 1].clone()))
})
});
}
} }

View file

@ -7,7 +7,7 @@ use yazi_shared::{event::{Data, DataKey}, replace_cow};
pub struct Sendable; pub struct Sendable;
impl Sendable { impl Sendable {
pub fn value_to_data(value: Value) -> mlua::Result<Data> { pub fn value_to_data(lua: &Lua, value: Value) -> mlua::Result<Data> {
Ok(match value { Ok(match value {
Value::Nil => Data::Nil, Value::Nil => Data::Nil,
Value::Boolean(b) => Data::Boolean(b), Value::Boolean(b) => Data::Boolean(b),
@ -30,7 +30,7 @@ impl Sendable {
if k == DataKey::Integer(i) { if k == DataKey::Integer(i) {
i += 1; i += 1;
} }
map.insert(k, Self::value_to_data(v)?); map.insert(k, Self::value_to_data(lua, v)?);
} }
if map.len() == i as usize - 1 { if map.len() == i as usize - 1 {
@ -54,8 +54,8 @@ impl Sendable {
Some(t) if t == TypeId::of::<yazi_fs::FilesOp>() => { Some(t) if t == TypeId::of::<yazi_fs::FilesOp>() => {
Data::Any(Box::new(ud.take::<yazi_fs::FilesOp>()?)) Data::Any(Box::new(ud.take::<yazi_fs::FilesOp>()?))
} }
Some(t) if t == TypeId::of::<super::body::BodyYankIter>() => { Some(t) if t == TypeId::of::<yazi_parser::mgr::UpdateYankedIter>() => {
Data::Any(Box::new(ud.take::<super::body::BodyYankIter>()?)) Data::Any(Box::new(ud.take::<yazi_parser::mgr::UpdateYankedIter>()?.into_opt(lua)?))
} }
_ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?, _ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?,
}, },
@ -83,13 +83,15 @@ impl Sendable {
} }
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?,
Data::Urn(u) => yazi_binding::Urn::new(u).into_lua(lua)?, Data::Urn(u) => yazi_binding::Urn::new(u).into_lua(lua)?,
Data::Any(a) => Value::UserData(if a.is::<yazi_fs::FilesOp>() { Data::Any(a) => {
lua.create_any_userdata(*a.downcast::<yazi_fs::FilesOp>().unwrap())? if a.is::<yazi_fs::FilesOp>() {
} else if a.is::<super::body::BodyYankIter>() { lua.create_any_userdata(*a.downcast::<yazi_fs::FilesOp>().unwrap())?.into_lua(lua)?
lua.create_userdata(*a.downcast::<super::body::BodyYankIter>().unwrap())? } else if a.is::<yazi_parser::mgr::UpdateYankedOpt>() {
} else { a.downcast::<yazi_parser::mgr::UpdateYankedOpt>().unwrap().into_lua(lua)?
Err("unsupported Data::Any included".into_lua_err())? } else {
}), Err("unsupported Data::Any included".into_lua_err())?
}
}
data => Self::data_to_value_ref(lua, &data)?, data => Self::data_to_value_ref(lua, &data)?,
}) })
} }
@ -120,28 +122,30 @@ impl Sendable {
Data::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?, Data::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?,
Data::Urn(u) => yazi_binding::Urn::new(u.clone()).into_lua(lua)?, Data::Urn(u) => yazi_binding::Urn::new(u.clone()).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(a) => Value::UserData(if let Some(t) = a.downcast_ref::<yazi_fs::FilesOp>() { Data::Any(a) => {
lua.create_any_userdata(t.clone())? if let Some(t) = a.downcast_ref::<yazi_fs::FilesOp>() {
} else if let Some(t) = a.downcast_ref::<super::body::BodyYankIter>() { lua.create_any_userdata(t.clone())?.into_lua(lua)?
lua.create_userdata(t.clone())? } else if let Some(t) = a.downcast_ref::<yazi_parser::mgr::UpdateYankedOpt>() {
} else { t.clone().into_lua(lua)?
Err("unsupported Data::Any included".into_lua_err())? } else {
}), Err("unsupported Data::Any included".into_lua_err())?
}
}
}) })
} }
pub fn table_to_args(t: Table) -> mlua::Result<HashMap<DataKey, Data>> { pub fn table_to_args(lua: &Lua, t: Table) -> mlua::Result<HashMap<DataKey, Data>> {
let mut args = HashMap::with_capacity(t.raw_len()); let mut args = HashMap::with_capacity(t.raw_len());
for pair in t.pairs::<Value, Value>() { for pair in t.pairs::<Value, Value>() {
let (k, v) = pair?; let (k, v) = pair?;
match k { match k {
Value::Integer(i) if i > 0 => { Value::Integer(i) if i > 0 => {
args.insert(DataKey::Integer(i - 1), Self::value_to_data(v)?); args.insert(DataKey::Integer(i - 1), Self::value_to_data(lua, v)?);
} }
Value::String(s) => { Value::String(s) => {
args.insert( args.insert(
DataKey::String(Cow::Owned(s.to_str()?.replace('_', "-"))), DataKey::String(Cow::Owned(s.to_str()?.replace('_', "-"))),
Self::value_to_data(v)?, Self::value_to_data(lua, v)?,
); );
} }
_ => return Err("invalid key in Cmd".into_lua_err()), _ => return Err("invalid key in Cmd".into_lua_err()),
@ -182,8 +186,8 @@ impl Sendable {
data.into_iter().map(|d| Self::data_to_value(lua, d)).collect() data.into_iter().map(|d| Self::data_to_value(lua, d)).collect()
} }
pub fn values_to_list(values: MultiValue) -> mlua::Result<Vec<Data>> { pub fn values_to_list(lua: &Lua, values: MultiValue) -> mlua::Result<Vec<Data>> {
values.into_iter().map(Self::value_to_data).collect() values.into_iter().map(|v| Self::value_to_data(lua, v)).collect()
} }
} }

View file

@ -1,19 +1,22 @@
use anyhow::Result; use anyhow::{Result, bail};
use mlua::IntoLua; use mlua::IntoLua;
use tracing::error; use tracing::error;
use yazi_binding::runtime_mut; use yazi_binding::runtime_mut;
use yazi_dds::{LOCAL, REMOTE}; use yazi_dds::{LOCAL, Payload, REMOTE};
use yazi_macro::succ; use yazi_macro::succ;
use yazi_parser::app::AcceptPayload;
use yazi_plugin::LUA; use yazi_plugin::LUA;
use yazi_shared::event::Data; use yazi_shared::event::{CmdCow, Data};
use crate::{app::App, lives::Lives}; use crate::{app::App, lives::Lives};
impl App { impl App {
pub(crate) fn accept_payload(&mut self, opt: AcceptPayload) -> Result<Data> { pub(crate) fn accept_payload(&mut self, mut c: CmdCow) -> Result<Data> {
let kind = opt.payload.body.kind().to_owned(); let Some(payload) = c.take_any2::<Payload>("payload").transpose()? else {
let lock = if opt.payload.receiver == 0 || opt.payload.receiver != opt.payload.sender { bail!("'payload' is required for accept_payload");
};
let kind = payload.body.kind().to_owned();
let lock = if payload.receiver == 0 || payload.receiver != payload.sender {
REMOTE.read() REMOTE.read()
} else { } else {
LOCAL.read() LOCAL.read()
@ -23,7 +26,7 @@ impl App {
drop(lock); drop(lock);
succ!(Lives::scope(&self.core, || { succ!(Lives::scope(&self.core, || {
let body = opt.payload.body.into_lua(&LUA)?; let body = payload.body.into_lua(&LUA)?;
for (id, cb) in handlers { for (id, cb) in handlers {
runtime_mut!(LUA)?.push(&id); runtime_mut!(LUA)?.push(&id);
if let Err(e) = cb.call::<()>(body.clone()) { if let Err(e) = cb.call::<()>(body.clone()) {

View file

@ -1,27 +0,0 @@
use mlua::{AnyUserData, UserData};
use super::Lives;
pub(super) struct Iter<I: Iterator<Item = T>, T> {
inner: I,
count: usize,
}
impl<I: Iterator<Item = T> + 'static, T: 'static> Iter<I, T> {
#[inline]
pub(super) fn make(inner: I) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner, count: 0 })
}
}
impl<I: Iterator<Item = T>, T> Iterator for Iter<I, T> {
type Item = (usize, T);
fn next(&mut self) -> Option<Self::Item> {
let next = self.inner.next()?;
self.count += 1;
Some((self.count, next))
}
}
impl<I: Iterator<Item = T>, T> UserData for Iter<I, T> {}

View file

@ -1,3 +1,3 @@
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
yazi_macro::mod_flat!(core file files filter finder folder iter lives mode preference preview ptr selected tab tabs tasks yanked); yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs tasks yanked);

View file

@ -1,45 +1,20 @@
use std::ops::Deref; use indexmap::IndexMap;
use mlua::AnyUserData;
use indexmap::{IndexMap, map::Keys}; use super::Lives;
use mlua::{AnyUserData, IntoLuaMulti, MetaMethod, UserData, UserDataMethods, UserDataRefMut}; use crate::lives::PtrCell;
use yazi_binding::Url;
use super::{Iter, Lives, PtrCell};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(super) struct Selected { pub(super) struct Selected;
inner: PtrCell<IndexMap<yazi_shared::url::Url, u64>>,
}
impl Deref for Selected {
type Target = IndexMap<yazi_shared::url::Url, u64>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Selected { impl Selected {
#[inline] #[inline]
pub(super) fn make(inner: &IndexMap<yazi_shared::url::Url, u64>) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &IndexMap<yazi_shared::url::Url, u64>) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() }) let inner = PtrCell::from(inner);
}
} Lives::scoped_userdata(yazi_binding::Iter::new(
inner.as_static().keys().cloned().map(yazi_binding::Url::new),
impl UserData for Selected { Some(inner.len()),
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) { ))
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len()));
methods.add_meta_method(MetaMethod::Pairs, |lua, me, ()| {
let iter = lua.create_function(
|lua, mut iter: UserDataRefMut<Iter<Keys<yazi_shared::url::Url, u64>, _>>| {
if let Some(next) = iter.next() {
(next.0, Url::new(next.1.clone())).into_lua_multi(lua)
} else {
().into_lua_multi(lua)
}
},
)?;
Ok((iter, Iter::make(me.inner.as_static().keys())))
});
} }
} }

View file

@ -1,12 +1,13 @@
use std::{collections::hash_set, ops::Deref}; use std::ops::Deref;
use mlua::{AnyUserData, IntoLuaMulti, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRefMut}; use mlua::{AnyUserData, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods};
use yazi_binding::Url; use yazi_binding::{Iter, get_metatable};
use super::{Iter, Lives, PtrCell}; use super::{Lives, PtrCell};
pub(super) struct Yanked { pub(super) struct Yanked {
inner: PtrCell<yazi_core::mgr::Yanked>, inner: PtrCell<yazi_core::mgr::Yanked>,
iter: AnyUserData,
} }
impl Deref for Yanked { impl Deref for Yanked {
@ -18,7 +19,15 @@ impl Deref for Yanked {
impl Yanked { impl Yanked {
#[inline] #[inline]
pub(super) fn make(inner: &yazi_core::mgr::Yanked) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::mgr::Yanked) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() }) let inner = PtrCell::from(inner);
Lives::scoped_userdata(Self {
inner,
iter: Lives::scoped_userdata(Iter::new(
inner.as_static().iter().cloned().map(yazi_binding::Url::new),
Some(inner.len()),
))?,
})
} }
} }
@ -30,18 +39,9 @@ impl UserData for Yanked {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len())); methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len()));
methods.add_meta_method(MetaMethod::Pairs, |lua, me, ()| { methods.add_meta_function(MetaMethod::Pairs, |lua, ud: AnyUserData| {
let iter = lua.create_function( let me = ud.borrow::<Self>()?;
|lua, mut iter: UserDataRefMut<Iter<hash_set::Iter<yazi_shared::url::Url>, _>>| { get_metatable(lua, &me.iter)?.call_function::<MultiValue>(MetaMethod::Pairs.name(), ud)
if let Some(next) = iter.next() {
(next.0, Url::new(next.1.clone())).into_lua_multi(lua)
} else {
().into_lua_multi(lua)
}
},
)?;
Ok((iter, Iter::make(me.inner.as_static().iter())))
}); });
} }
} }

View file

@ -41,7 +41,7 @@ impl Display for Filter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.raw) } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.raw) }
} }
#[derive(Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum FilterCase { pub enum FilterCase {
Smart, Smart,
#[default] #[default]

View file

@ -12,7 +12,6 @@ repository = "https://github.com/sxyazi/yazi"
yazi-binding = { path = "../yazi-binding", version = "25.6.11" } yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" } yazi-boot = { path = "../yazi-boot", version = "25.6.11" }
yazi-config = { path = "../yazi-config", version = "25.6.11" } yazi-config = { path = "../yazi-config", version = "25.6.11" }
yazi-dds = { path = "../yazi-dds", version = "25.6.11" }
yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-fs = { path = "../yazi-fs", version = "25.6.11" }
yazi-macro = { path = "../yazi-macro", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
yazi-shared = { path = "../yazi-shared", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" }

View file

@ -1,19 +0,0 @@
use anyhow::bail;
use yazi_dds::Payload;
use yazi_shared::event::CmdCow;
pub struct AcceptPayload {
pub payload: Payload<'static>,
}
impl TryFrom<CmdCow> for AcceptPayload {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(payload) = c.take_any("payload") else {
bail!("Invalid 'payload' in AcceptPayload");
};
Ok(Self { payload })
}
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(accept_payload deprecate mouse notify plugin stop update_progress); yazi_macro::mod_flat!(deprecate mouse notify plugin stop update_progress);

View file

@ -1,6 +1,6 @@
use std::{ffi::OsString, path::{MAIN_SEPARATOR_STR, PathBuf}}; use std::{ffi::OsString, path::{MAIN_SEPARATOR_STR, PathBuf}};
use yazi_shared::{Id, SStr, event::{Cmd, CmdCow}}; use yazi_shared::{Id, SStr, event::CmdCow};
#[derive(Default)] #[derive(Default)]
pub struct ShowOpt { pub struct ShowOpt {
@ -10,21 +10,23 @@ pub struct ShowOpt {
pub ticket: Id, pub ticket: Id,
} }
impl From<CmdCow> for ShowOpt { impl TryFrom<CmdCow> for ShowOpt {
fn from(mut c: CmdCow) -> Self { type Error = anyhow::Error;
Self {
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
Ok(Self {
cache: c.take_any("cache").unwrap_or_default(), cache: c.take_any("cache").unwrap_or_default(),
cache_name: c.take_any("cache-name").unwrap_or_default(), cache_name: c.take_any("cache-name").unwrap_or_default(),
word: c.take_str("word").unwrap_or_default(), word: c.take_str("word").unwrap_or_default(),
ticket: c.id("ticket").unwrap_or_default(), ticket: c.id("ticket").unwrap_or_default(),
} })
} }
} }
impl From<Cmd> for ShowOpt {
fn from(c: Cmd) -> Self { Self::from(CmdCow::from(c)) }
}
// --- Item // --- Item
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CmpItem { pub struct CmpItem {

View file

@ -1,6 +1,8 @@
use mlua::{IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize};
use yazi_shared::event::{CmdCow, Data, EventQuit}; use yazi_shared::event::{CmdCow, Data, EventQuit};
#[derive(Default)] #[derive(Default, Serialize, Deserialize)]
pub struct QuitOpt { pub struct QuitOpt {
pub code: i32, pub code: i32,
pub no_cwd_file: bool, pub no_cwd_file: bool,
@ -20,3 +22,7 @@ impl From<QuitOpt> for EventQuit {
EventQuit { code: value.code, no_cwd_file: value.no_cwd_file, ..Default::default() } EventQuit { code: value.code, no_cwd_file: value.no_cwd_file, ..Default::default() }
} }
} }
impl IntoLua for QuitOpt {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value(&self) }
}

View file

@ -1,23 +1,76 @@
use std::collections::HashSet; use std::{borrow::Cow, collections::HashSet};
use anyhow::bail; use anyhow::bail;
use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value};
use serde::{Deserialize, Serialize};
use yazi_binding::get_metatable;
use yazi_shared::{event::CmdCow, url::Url}; use yazi_shared::{event::CmdCow, url::Url};
#[derive(Default)] type Iter = yazi_binding::Iter<
pub struct UpdateYankedOpt { std::iter::Map<std::collections::hash_set::IntoIter<Url>, fn(Url) -> yazi_binding::Url>,
yazi_binding::Url,
>;
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct UpdateYankedOpt<'a> {
pub cut: bool, pub cut: bool,
pub urls: HashSet<Url>, pub urls: Cow<'a, HashSet<Url>>,
} }
impl TryFrom<CmdCow> for UpdateYankedOpt { impl TryFrom<CmdCow> for UpdateYankedOpt<'_> {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> { fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
// TODO: remove `BodyYankIter` if let Some(opt) = c.take_any2("opt") {
if let Some(iter) = c.take_any::<yazi_dds::body::BodyYankIter>("urls") { opt
Ok(Self { urls: iter.urls.into_iter().collect(), cut: iter.cut })
} else { } else {
bail!("Invalid 'urls' argument in UpdateYankedOpt"); bail!("'opt' is required for UpdateYankedOpt");
} }
} }
} }
impl IntoLua for UpdateYankedOpt<'static> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let len = self.urls.len();
let iter = Iter::new(self.urls.into_owned().into_iter().map(yazi_binding::Url::new), Some(len));
UpdateYankedIter { cut: self.cut, len, inner: lua.create_userdata(iter)? }.into_lua(lua)
}
}
// --- Iter
pub struct UpdateYankedIter {
cut: bool,
len: usize,
inner: AnyUserData,
}
impl UpdateYankedIter {
pub fn into_opt(self, lua: &Lua) -> mlua::Result<UpdateYankedOpt<'static>> {
Ok(UpdateYankedOpt {
cut: self.cut,
urls: Cow::Owned(
self
.inner
.take::<Iter>()?
.into_iter(lua)
.map(|result| result.map(Into::into))
.collect::<mlua::Result<_>>()?,
),
})
}
}
impl UserData for UpdateYankedIter {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("cut", |_, me| Ok(me.cut));
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len));
methods.add_meta_function(MetaMethod::Pairs, |lua, ud: AnyUserData| {
let me = ud.borrow::<Self>()?;
get_metatable(lua, &me.inner)?.call_function::<MultiValue>(MetaMethod::Pairs.name(), ud)
});
}
}

View file

@ -8,12 +8,18 @@ pub struct FilterOpt {
pub done: bool, pub done: bool,
} }
impl From<CmdCow> for FilterOpt { impl TryFrom<CmdCow> for FilterOpt {
fn from(mut c: CmdCow) -> Self { type Error = anyhow::Error;
Self {
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
Ok(Self {
query: c.take_first_str().unwrap_or_default(), query: c.take_first_str().unwrap_or_default(),
case: FilterCase::from(&*c), case: FilterCase::from(&*c),
done: c.bool("done"), done: c.bool("done"),
} })
} }
} }

View file

@ -1,21 +0,0 @@
use anyhow::bail;
use yazi_fs::FilterCase;
use yazi_shared::{SStr, event::CmdCow};
pub struct FindDoOpt {
pub query: SStr,
pub prev: bool,
pub case: FilterCase,
}
impl TryFrom<CmdCow> for FindDoOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(query) = c.take_first_str() else {
bail!("'query' is required for FindOpt");
};
Ok(Self { query, prev: c.bool("previous"), case: FilterCase::from(&*c) })
}
}

View file

@ -1 +1,25 @@
use anyhow::bail;
use yazi_fs::FilterCase;
use yazi_shared::{SStr, event::CmdCow};
pub struct FindDoOpt {
pub query: SStr,
pub prev: bool,
pub case: FilterCase,
}
impl TryFrom<CmdCow> for FindDoOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
let Some(query) = c.take_first_str() else {
bail!("'query' is required for FindDoOpt");
};
Ok(Self { query, prev: c.bool("previous"), case: FilterCase::from(&*c) })
}
}

View file

@ -5,7 +5,6 @@ yazi_macro::mod_flat!(
enter enter
escape escape
filter filter
filter_do
find find
find_arrow find_arrow
find_do find_do

View file

@ -11,6 +11,10 @@ impl TryFrom<CmdCow> for UpdatePeekedOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> { fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
let Some(lock) = c.take_any("lock") else { let Some(lock) = c.take_any("lock") else {
bail!("Invalid 'lock' argument in UpdatePeekedOpt"); bail!("Invalid 'lock' argument in UpdatePeekedOpt");
}; };

View file

@ -11,6 +11,10 @@ impl TryFrom<CmdCow> for UpdateSpottedOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> { fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
let Some(lock) = c.take_any("lock") else { let Some(lock) = c.take_any("lock") else {
bail!("Invalid 'lock' argument in UpdateSpottedOpt"); bail!("Invalid 'lock' argument in UpdateSpottedOpt");
}; };

View file

@ -10,6 +10,10 @@ impl TryFrom<CmdCow> for ShowOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> { fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
if let Some(opt) = c.take_any2("opt") {
return opt;
}
Ok(Self { cands: c.take_any("candidates").unwrap_or_default(), silent: c.bool("silent") }) Ok(Self { cands: c.take_any("candidates").unwrap_or_default(), silent: c.bool("silent") })
} }
} }

View file

@ -1,6 +1,6 @@
local function setup(_, opts) local function setup(_, opts)
if opts.sync_yanked then if opts.sync_yanked then
ps.sub_remote("@yank", function(body) ya.emit("update_yanked", { cut = body.cut, urls = body }) end) ps.sub_remote("@yank", function(opt) ya.emit("update_yanked", { opt = opt }) end)
end end
end end

View file

@ -6,14 +6,15 @@ pub struct Pubsub;
impl Pubsub { impl Pubsub {
pub(super) fn r#pub(lua: &Lua) -> mlua::Result<Function> { pub(super) fn r#pub(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (kind, value): (mlua::String, Value)| { lua.create_function(|lua, (kind, value): (mlua::String, Value)| {
yazi_dds::Pubsub::r#pub(Body::from_lua(&kind.to_str()?, value)?).into_lua_err() yazi_dds::Pubsub::r#pub(Body::from_lua(lua, &kind.to_str()?, value)?).into_lua_err()
}) })
} }
pub(super) fn pub_to(lua: &Lua) -> mlua::Result<Function> { pub(super) fn pub_to(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (receiver, kind, value): (Id, mlua::String, Value)| { lua.create_function(|lua, (receiver, kind, value): (Id, mlua::String, Value)| {
yazi_dds::Pubsub::pub_to(*receiver, Body::from_lua(&kind.to_str()?, value)?).into_lua_err() yazi_dds::Pubsub::pub_to(*receiver, Body::from_lua(lua, &kind.to_str()?, value)?)
.into_lua_err()
}) })
} }

View file

@ -17,18 +17,18 @@ impl Utils {
} }
pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> { pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (name, args): (String, Table)| { lua.create_function(|lua, (name, args): (String, Table)| {
let mut cmd = Cmd::new_or(name, Layer::Mgr)?; let mut cmd = Cmd::new_or(name, Layer::Mgr)?;
cmd.args = Sendable::table_to_args(args)?; cmd.args = Sendable::table_to_args(lua, args)?;
Ok(emit!(Call(cmd))) Ok(emit!(Call(cmd)))
}) })
} }
pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result<Function> { pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (name, args): (String, Table)| { lua.create_function(|lua, (name, args): (String, Table)| {
emit!(Call(Cmd { emit!(Call(Cmd {
name: name.into(), name: name.into(),
args: Sendable::table_to_args(args)?, args: Sendable::table_to_args(lua, args)?,
layer: Layer::Mgr, layer: Layer::Mgr,
})); }));
Ok(()) Ok(())

View file

@ -5,8 +5,8 @@ use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_stream::wrappers::UnboundedReceiverStream;
use yazi_binding::{deprecate, elements::{Line, Pos, Text}}; use yazi_binding::{deprecate, elements::{Line, Pos, Text}};
use yazi_config::{keymap::{Chord, Key}, popup::{ConfirmCfg, InputCfg}}; use yazi_config::{keymap::{Chord, Key}, popup::{ConfirmCfg, InputCfg}};
use yazi_macro::emit; use yazi_parser::which::ShowOpt;
use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy}; use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy, WhichProxy};
use yazi_shared::{Debounce, event::Cmd}; use yazi_shared::{Debounce, event::Cmd};
use super::Utils; use super::Utils;
@ -29,11 +29,7 @@ impl Utils {
} }
drop(tx); drop(tx);
emit!(Call( WhichProxy::show(ShowOpt { cands, silent: t.raw_get("silent").unwrap_or_default() });
Cmd::new("which:show")
.with_any("candidates", cands)
.with("silent", t.raw_get::<bool>("silent").unwrap_or_default())
));
Ok(rx.recv().await.map(|idx| idx + 1)) Ok(rx.recv().await.map(|idx| idx + 1))
}) })

View file

@ -1,9 +1,9 @@
use mlua::{AnyUserData, ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; use mlua::{AnyUserData, ExternalError, Function, IntoLuaMulti, Lua, Table, Value};
use yazi_binding::{Error, elements::{Area, Renderable, Text}}; use yazi_binding::{Error, elements::{Area, Renderable, Text}};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_macro::emit; use yazi_parser::tab::{PreviewLock, UpdatePeekedOpt};
use yazi_parser::tab::PreviewLock; use yazi_proxy::MgrProxy;
use yazi_shared::{errors::PeekError, event::Cmd}; use yazi_shared::errors::PeekError;
use super::Utils; use super::Utils;
use crate::external::Highlighter; use crate::external::Highlighter;
@ -29,7 +29,7 @@ impl Utils {
scroll: Default::default(), scroll: Default::default(),
})]; })];
emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock))); MgrProxy::update_peeked(UpdatePeekedOpt { lock });
().into_lua_multi(&lua) ().into_lua_multi(&lua)
}) })
} }
@ -56,7 +56,7 @@ impl Utils {
_ => Err("preview widget must be a renderable element or a table of them".into_lua_err())?, _ => Err("preview widget must be a renderable element or a table of them".into_lua_err())?,
}; };
emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock))); MgrProxy::update_peeked(UpdatePeekedOpt { lock });
Ok(()) Ok(())
}) })
} }

View file

@ -1,9 +1,8 @@
use mlua::{AnyUserData, Function, Lua, Table}; use mlua::{AnyUserData, Function, Lua, Table};
use yazi_binding::elements::{Edge, Renderable}; use yazi_binding::elements::{Edge, Renderable};
use yazi_config::THEME; use yazi_config::THEME;
use yazi_macro::emit; use yazi_parser::tab::{SpotLock, UpdateSpottedOpt};
use yazi_parser::tab::SpotLock; use yazi_proxy::MgrProxy;
use yazi_shared::event::Cmd;
use super::Utils; use super::Utils;
@ -32,7 +31,7 @@ impl Utils {
}), }),
Renderable::Table(Box::new(table)), Renderable::Table(Box::new(table)),
]; ];
emit!(Call(Cmd::new("mgr:update_spotted").with_any("lock", lock))); MgrProxy::update_spotted(UpdateSpottedOpt { lock });
Ok(()) Ok(())
}) })
@ -43,7 +42,7 @@ impl Utils {
let mut lock = SpotLock::try_from(t)?; let mut lock = SpotLock::try_from(t)?;
lock.data = widgets.into_iter().map(Renderable::try_from).collect::<mlua::Result<_>>()?; lock.data = widgets.into_iter().map(Renderable::try_from).collect::<mlua::Result<_>>()?;
emit!(Call(Cmd::new("mgr:update_spotted").with_any("lock", lock))); MgrProxy::update_spotted(UpdateSpottedOpt { lock });
Ok(()) Ok(())
}) })
} }

View file

@ -22,7 +22,7 @@ impl Utils {
let Some(cur) = runtime!(lua)?.current_owned() else { let Some(cur) = runtime!(lua)?.current_owned() else {
return Err("block spawned by `ya.sync()` must be called in a plugin").into_lua_err(); return Err("block spawned by `ya.sync()` must be called in a plugin").into_lua_err();
}; };
Sendable::list_to_values(&lua, Self::retrieve(cur, block, args).await?) Sendable::list_to_values(&lua, Self::retrieve(&lua, cur, block, args).await?)
}) })
}) })
} else { } else {
@ -79,8 +79,13 @@ impl Utils {
lua.create_async_function(|_lua, _futs: MultiValue| async move { Ok(()) }) lua.create_async_function(|_lua, _futs: MultiValue| async move { Ok(()) })
} }
async fn retrieve(id: String, calls: usize, args: MultiValue) -> mlua::Result<Vec<Data>> { async fn retrieve(
let args = Sendable::values_to_list(args)?; lua: &Lua,
id: String,
calls: usize,
args: MultiValue,
) -> mlua::Result<Vec<Data>> {
let args = Sendable::values_to_list(lua, args)?;
let (tx, rx) = oneshot::channel::<Vec<Data>>(); let (tx, rx) = oneshot::channel::<Vec<Data>>();
let callback: PluginCallback = { let callback: PluginCallback = {
@ -95,7 +100,7 @@ impl Utils {
.chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d))) .chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d)))
.collect::<mlua::Result<MultiValue>>()?; .collect::<mlua::Result<MultiValue>>()?;
let values = Sendable::values_to_list(block.call(args)?)?; let values = Sendable::values_to_list(lua, block.call(args)?)?;
tx.send(values).map_err(|_| "send failed".into_lua_err()) tx.send(values).map_err(|_| "send failed".into_lua_err())
}) })
}; };

View file

@ -2,7 +2,7 @@ use std::time::Duration;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt}; use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt, TasksProgress};
use yazi_shared::event::Cmd; use yazi_shared::event::Cmd;
pub struct AppProxy; pub struct AppProxy;
@ -51,4 +51,8 @@ impl AppProxy {
pub fn plugin_do(opt: PluginOpt) { pub fn plugin_do(opt: PluginOpt) {
emit!(Call(Cmd::new("app:plugin_do").with_any("opt", opt))); emit!(Call(Cmd::new("app:plugin_do").with_any("opt", opt)));
} }
pub fn update_progress(progress: TasksProgress) {
emit!(Call(Cmd::new("app:update_progress").with_any("progress", progress)));
}
} }

View file

@ -1,9 +1,14 @@
use yazi_macro::emit; use yazi_macro::emit;
use yazi_parser::cmp::ShowOpt;
use yazi_shared::{Id, event::Cmd}; use yazi_shared::{Id, event::Cmd};
pub struct CmpProxy; pub struct CmpProxy;
impl CmpProxy { impl CmpProxy {
pub fn show(opt: ShowOpt) {
emit!(Call(Cmd::new("cmp:show").with_any("opt", opt)));
}
pub fn trigger(word: &str, ticket: Id) { pub fn trigger(word: &str, ticket: Id) {
emit!(Call(Cmd::args("cmp:trigger", [word]).with("ticket", ticket))); emit!(Call(Cmd::args("cmp:trigger", [word]).with("ticket", ticket)));
} }

View file

@ -1,5 +1,5 @@
mod macros; mod macros;
yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tasks); yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tasks which);
pub fn init() { crate::init_semaphore(); } pub fn init() { crate::init_semaphore(); }

View file

@ -1,7 +1,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_parser::mgr::{OpenDoOpt, SearchOpt}; use yazi_parser::{mgr::{OpenDoOpt, SearchOpt}, tab::{FilterOpt, FindDoOpt, UpdatePeekedOpt, UpdateSpottedOpt}};
use yazi_shared::{SStr, event::Cmd, url::Url}; use yazi_shared::{SStr, event::Cmd, url::Url};
pub struct MgrProxy; pub struct MgrProxy;
@ -29,6 +29,14 @@ impl MgrProxy {
)); ));
} }
pub fn find_do(opt: FindDoOpt) {
emit!(Call(Cmd::new("mgr:find_do").with_any("opt", opt)));
}
pub fn filter_do(opt: FilterOpt) {
emit!(Call(Cmd::new("mgr:filter_do").with_any("opt", opt)));
}
pub fn search_do(opt: SearchOpt) { pub fn search_do(opt: SearchOpt) {
emit!(Call( emit!(Call(
// TODO: use second positional argument instead of `args` parameter // TODO: use second positional argument instead of `args` parameter
@ -38,6 +46,14 @@ impl MgrProxy {
)); ));
} }
pub fn update_peeked(opt: UpdatePeekedOpt) {
emit!(Call(Cmd::new("mgr:update_peeked").with_any("opt", opt)));
}
pub fn update_spotted(opt: UpdateSpottedOpt) {
emit!(Call(Cmd::new("mgr:update_spotted").with_any("opt", opt)));
}
pub fn update_tasks(url: &Url) { pub fn update_tasks(url: &Url) {
emit!(Call(Cmd::new("mgr:update_tasks").with_any("urls", vec![url.clone()]))); emit!(Call(Cmd::new("mgr:update_tasks").with_any("urls", vec![url.clone()])));
} }

11
yazi-proxy/src/which.rs Normal file
View file

@ -0,0 +1,11 @@
use yazi_macro::emit;
use yazi_parser::which::ShowOpt;
use yazi_shared::event::Cmd;
pub struct WhichProxy;
impl WhichProxy {
pub fn show(opt: ShowOpt) {
emit!(Call(Cmd::new("which:show").with_any("opt", opt)));
}
}

View file

@ -85,20 +85,16 @@ impl Cmd {
pub fn get(&self, name: impl Into<DataKey>) -> Option<&Data> { self.args.get(&name.into()) } pub fn get(&self, name: impl Into<DataKey>) -> Option<&Data> { self.args.get(&name.into()) }
#[inline] #[inline]
pub fn str(&self, name: impl Into<DataKey>) -> Option<&str> { pub fn str(&self, name: impl Into<DataKey>) -> Option<&str> { self.get(name)?.as_str() }
self.get(name).and_then(Data::as_str)
}
#[inline] #[inline]
pub fn bool(&self, name: impl Into<DataKey>) -> 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] #[inline]
pub fn maybe_bool(&self, name: impl Into<DataKey>) -> Option<bool> { pub fn maybe_bool(&self, name: impl Into<DataKey>) -> Option<bool> { self.get(name)?.as_bool() }
self.get(name).and_then(Data::as_bool)
}
#[inline] #[inline]
pub fn id(&self, name: impl Into<DataKey>) -> Option<Id> { self.get(name).and_then(Data::as_id) } pub fn id(&self, name: impl Into<DataKey>) -> Option<Id> { self.get(name)?.as_id() }
#[inline] #[inline]
pub fn first(&self) -> Option<&Data> { self.get(0) } pub fn first(&self) -> Option<&Data> { self.get(0) }
@ -136,7 +132,12 @@ impl Cmd {
#[inline] #[inline]
pub fn take_any<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<T> { 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()) self.args.remove(&name.into())?.into_any()
}
#[inline]
pub fn take_any2<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<Result<T>> {
self.args.remove(&name.into()).map(Data::into_any2)
} }
// Parse // Parse

View file

@ -1,5 +1,7 @@
use std::{borrow::Cow, ops::Deref}; use std::{borrow::Cow, ops::Deref};
use anyhow::Result;
use super::{Cmd, Data, DataKey}; use super::{Cmd, Data, DataKey};
use crate::{SStr, url::Url}; use crate::{SStr, url::Url};
@ -75,4 +77,12 @@ impl CmdCow {
Self::Borrowed(_) => None, Self::Borrowed(_) => None,
} }
} }
#[inline]
pub fn take_any2<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<Result<T>> {
match self {
Self::Owned(c) => c.take_any2(name),
Self::Borrowed(_) => None,
}
}
} }

View file

@ -1,5 +1,6 @@
use std::{any::Any, borrow::Cow, collections::HashMap}; use std::{any::Any, borrow::Cow, collections::HashMap};
use anyhow::{Result, bail};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize, de}; use serde::{Deserialize, Serialize, de};
@ -80,6 +81,18 @@ impl Data {
} }
} }
// FIXME: find a better name
#[inline]
pub fn into_any2<T: 'static>(self) -> Result<T> {
if let Self::Any(b) = self
&& let Ok(t) = b.downcast::<T>()
{
Ok(*t)
} else {
bail!("Failed to downcast Data into {}", std::any::type_name::<T>())
}
}
#[inline] #[inline]
pub fn to_url(&self) -> Option<Url> { pub fn to_url(&self) -> Option<Url> {
match self { match self {