feat: new cx.which API to access the which component state (#3617)

This commit is contained in:
三咲雅 misaki masa 2026-01-27 01:29:14 +08:00 committed by GitHub
parent 06c665a086
commit 592e70a1c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 190 additions and 151 deletions

View file

@ -14,7 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
### Added ### Added
- New `ind-which-show` DDS event to change the which component behavior ([#3608]) - New `cx.which` API to access the which component state ([#3617])
- New `ind-which-activate` DDS event to change the which component behavior ([#3608])
### Fixed ### Fixed
@ -1622,3 +1623,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
[#3594]: https://github.com/sxyazi/yazi/pull/3594 [#3594]: https://github.com/sxyazi/yazi/pull/3594
[#3607]: https://github.com/sxyazi/yazi/pull/3607 [#3607]: https://github.com/sxyazi/yazi/pull/3607
[#3608]: https://github.com/sxyazi/yazi/pull/3608 [#3608]: https://github.com/sxyazi/yazi/pull/3608
[#3617]: https://github.com/sxyazi/yazi/pull/3617

View file

@ -18,9 +18,9 @@ impl Preflight {
Ok(Lives::scope(cx.core, || { Ok(Lives::scope(cx.core, || {
let mut body = opt.1.into_lua(&LUA)?; let mut body = opt.1.into_lua(&LUA)?;
for (id, cb) in handlers { for (id, cb) in handlers {
runtime_mut!(LUA)?.push(&id); let blocking = runtime_mut!(LUA)?.critical_push(&id, true);
let result = cb.call::<Value>(&body); let result = cb.call::<Value>(&body);
runtime_mut!(LUA)?.pop(); runtime_mut!(LUA)?.critical_pop(blocking);
match result { match result {
Ok(Value::Nil) => { Ok(Value::Nil) => {

View file

@ -1,11 +1,14 @@
use std::ops::Deref; use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields}; use mlua::{AnyUserData, UserData, UserDataFields, Value};
use yazi_binding::cached_field;
use super::{Lives, PtrCell}; use super::{Lives, PtrCell};
pub(super) struct Which { pub(super) struct Which {
inner: PtrCell<yazi_core::which::Which>, inner: PtrCell<yazi_core::which::Which>,
v_cands: Option<Value>,
} }
impl Deref for Which { impl Deref for Which {
@ -16,10 +19,18 @@ impl Deref for Which {
impl Which { impl Which {
pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result<AnyUserData> { pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() }) Lives::scoped_userdata(Self { inner: inner.into(), v_cands: None })
} }
} }
impl UserData for Which { impl UserData for Which {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {} fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
cached_field!(fields, cands, |lua, me| {
lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::ChordCow))
});
fields.add_field_method_get("active", |_, me| Ok(me.inner.active));
fields.add_field_method_get("silent", |_, me| Ok(me.inner.silent));
}
} }

View file

@ -87,12 +87,7 @@ impl UpdateFiles {
fn update_hovered(cx: &mut Ctx, op: FilesOp) -> Result<Data> { fn update_hovered(cx: &mut Ctx, op: FilesOp) -> Result<Data> {
let (id, url) = (cx.tab().id, op.cwd()); let (id, url) = (cx.tab().id, op.cwd());
let (_, folder) = cx let folder = cx.tab_mut().history.entry_ref(url).or_insert_with(|| Folder::from(url));
.tab_mut()
.history
.raw_entry_mut()
.from_key(url)
.or_insert_with(|| (url.clone(), Folder::from(url)));
if folder.update_pub(id, op) { if folder.update_pub(id, op) {
act!(mgr:peek, cx, true)?; act!(mgr:peek, cx, true)?;
@ -108,10 +103,8 @@ impl UpdateFiles {
tab tab
.history .history
.raw_entry_mut() .entry_ref(op.cwd())
.from_key(op.cwd()) .or_insert_with(|| Folder::from(op.cwd()))
.or_insert_with(|| (op.cwd().clone(), Folder::from(op.cwd())))
.1
.update_pub(tab.id, op); .update_pub(tab.id, op);
if leave { if leave {

View file

@ -2,17 +2,17 @@ use anyhow::Result;
use yazi_core::which::WhichSorter; use yazi_core::which::WhichSorter;
use yazi_dds::spark::SparkKind; use yazi_dds::spark::SparkKind;
use yazi_macro::{render, succ}; use yazi_macro::{render, succ};
use yazi_parser::which::ShowOpt; use yazi_parser::which::ActivateOpt;
use yazi_shared::{Source, data::Data}; use yazi_shared::{Source, data::Data};
use crate::{Actor, Ctx}; use crate::{Actor, Ctx};
pub struct Show; pub struct Activate;
impl Actor for Show { impl Actor for Activate {
type Options = ShowOpt; type Options = ActivateOpt;
const NAME: &str = "show"; const NAME: &str = "activate";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> { fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
opt.cands.retain(|c| c.on.len() > opt.times); opt.cands.retain(|c| c.on.len() > opt.times);
@ -26,14 +26,14 @@ impl Actor for Show {
which.times = opt.times; which.times = opt.times;
which.cands = opt.cands; which.cands = opt.cands;
which.visible = true; which.active = true;
which.silent = opt.silent; which.silent = opt.silent;
succ!(render!()); succ!(render!());
} }
fn hook(cx: &Ctx, _opt: &Self::Options) -> Option<SparkKind> { fn hook(cx: &Ctx, _opt: &Self::Options) -> Option<SparkKind> {
match cx.source() { match cx.source() {
Source::Unknown => Some(SparkKind::IndWhichShow), Source::Unknown => Some(SparkKind::IndWhichActivate),
_ => None, _ => None,
} }
} }

View file

@ -1 +1 @@
yazi_macro::mod_flat!(callback show); yazi_macro::mod_flat!(activate callback);

View file

@ -1,7 +1,7 @@
use mlua::UserData; use mlua::UserData;
use yazi_codegen::FromLuaOwned; use yazi_codegen::FromLuaOwned;
#[derive(FromLuaOwned)] #[derive(Clone, FromLuaOwned)]
pub struct ChordCow(pub yazi_config::keymap::ChordCow); pub struct ChordCow(pub yazi_config::keymap::ChordCow);
impl From<ChordCow> for yazi_config::keymap::ChordCow { impl From<ChordCow> for yazi_config::keymap::ChordCow {

View file

@ -66,8 +66,8 @@ impl UserData for Gauge {
Ok(ud) Ok(ud)
}); });
methods.add_function_mut("gauge_style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("gauge_style", |_, (ud, style): (AnyUserData, Style)| {
ud.borrow_mut::<Self>()?.gauge_style = Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.gauge_style = style.0;
Ok(ud) Ok(ud)
}); });
} }

View file

@ -127,20 +127,20 @@ impl UserData for Table {
Ok(ud) Ok(ud)
}); });
methods.add_function_mut("style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("style", |_, (ud, style): (AnyUserData, Style)| {
ud.borrow_mut::<Self>()?.style = Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.style = style.0;
Ok(ud) Ok(ud)
}); });
methods.add_function_mut("row_style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("row_style", |_, (ud, style): (AnyUserData, Style)| {
ud.borrow_mut::<Self>()?.row_highlight_style = Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.row_highlight_style = style.0;
Ok(ud) Ok(ud)
}); });
methods.add_function_mut("col_style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("col_style", |_, (ud, style): (AnyUserData, Style)| {
ud.borrow_mut::<Self>()?.column_highlight_style = Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.column_highlight_style = style.0;
Ok(ud) Ok(ud)
}); });
methods.add_function_mut("cell_style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("cell_style", |_, (ud, style): (AnyUserData, Style)| {
ud.borrow_mut::<Self>()?.cell_highlight_style = Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.cell_highlight_style = style.0;
Ok(ud) Ok(ud)
}); });
} }

View file

@ -71,8 +71,8 @@ macro_rules! impl_area_method {
#[macro_export] #[macro_export]
macro_rules! impl_style_method { macro_rules! impl_style_method {
($methods:ident, $($field:tt).+) => { ($methods:ident, $($field:tt).+) => {
$methods.add_function_mut("style", |_, (ud, value): (mlua::AnyUserData, mlua::Value)| { $methods.add_function_mut("style", |_, (ud, style): (mlua::AnyUserData, $crate::Style)| {
ud.borrow_mut::<Self>()?.$($field).+ = $crate::Style::try_from(value)?.0; ud.borrow_mut::<Self>()?.$($field).+ = style.0;
Ok(ud) Ok(ud)
}); });
}; };

View file

@ -1,6 +1,6 @@
use std::collections::VecDeque; use std::{collections::VecDeque, mem};
use hashbrown::HashMap; use hashbrown::{HashMap, hash_map::EntryRef};
use mlua::Function; use mlua::Function;
#[derive(Debug)] #[derive(Debug)]
@ -12,8 +12,7 @@ pub struct Runtime {
#[derive(Debug)] #[derive(Debug)]
struct RuntimeFrame { struct RuntimeFrame {
id: String, id: String,
calls: usize,
} }
impl Runtime { impl Runtime {
@ -21,40 +20,45 @@ impl Runtime {
pub fn new_isolate(id: &str) -> Self { pub fn new_isolate(id: &str) -> Self {
Self { Self {
frames: VecDeque::from([RuntimeFrame { id: id.to_owned(), calls: 0 }]), frames: VecDeque::from([RuntimeFrame { id: id.to_owned() }]),
blocks: <_>::default(), blocks: <_>::default(),
blocking: false, blocking: false,
} }
} }
pub fn push(&mut self, id: &str) { pub fn push(&mut self, id: &str) { self.frames.push_back(RuntimeFrame { id: id.to_owned() }); }
self.frames.push_back(RuntimeFrame { id: id.to_owned(), calls: 0 });
}
pub fn pop(&mut self) { self.frames.pop_back(); } pub fn pop(&mut self) { self.frames.pop_back(); }
pub fn critical_push(&mut self, id: &str, blocking: bool) -> bool {
self.push(id);
mem::replace(&mut self.blocking, blocking)
}
pub fn critical_pop(&mut self, blocking: bool) {
self.pop();
self.blocking = blocking;
}
pub fn current(&self) -> Option<&str> { self.frames.back().map(|f| f.id.as_str()) } pub fn current(&self) -> Option<&str> { self.frames.back().map(|f| f.id.as_str()) }
pub fn current_owned(&self) -> Option<String> { self.current().map(ToOwned::to_owned) } pub fn current_owned(&self) -> Option<String> { self.current().map(ToOwned::to_owned) }
pub fn next_block(&mut self) -> Option<usize> {
self.frames.back_mut().map(|f| {
f.calls += 1;
f.calls - 1
})
}
pub fn get_block(&self, id: &str, calls: usize) -> Option<Function> { pub fn get_block(&self, id: &str, calls: usize) -> Option<Function> {
self.blocks.get(id).and_then(|v| v.get(calls)).cloned() self.blocks.get(id).and_then(|v| v.get(calls)).cloned()
} }
pub fn put_block(&mut self, f: &Function) -> bool { pub fn put_block(&mut self, f: &Function) -> Option<usize> {
let Some(cur) = self.frames.back() else { return false }; let Some(cur) = self.frames.back() else { return None };
if let Some(v) = self.blocks.get_mut(&cur.id) { Some(match self.blocks.entry_ref(&cur.id) {
v.push(f.clone()); EntryRef::Occupied(mut oe) => {
} else { oe.get_mut().push(f.clone());
self.blocks.insert(cur.id.clone(), vec![f.clone()]); oe.get().len() - 1
} }
true EntryRef::Vacant(ve) => {
ve.insert(vec![f.clone()]);
0
}
})
} }
} }

View file

@ -1,4 +1,4 @@
use mlua::{AnyUserData, ExternalError, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value}; use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value};
use crate::SER_OPT; use crate::SER_OPT;
@ -7,7 +7,7 @@ pub struct Style(pub ratatui::style::Style);
impl Style { impl Style {
pub fn compose(lua: &Lua) -> mlua::Result<Value> { pub fn compose(lua: &Lua) -> mlua::Result<Value> {
let new = lua.create_function(|_, (_, value): (Table, Value)| Self::try_from(value))?; let new = lua.create_function(|_, (_, style): (Table, Self)| Ok(style))?;
let style = lua.create_table()?; let style = lua.create_table()?;
style.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; style.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?;
@ -16,10 +16,12 @@ impl Style {
} }
} }
impl TryFrom<Value> for Style { impl From<yazi_config::Style> for Style {
type Error = mlua::Error; fn from(value: yazi_config::Style) -> Self { Self(value.into()) }
}
fn try_from(value: Value) -> Result<Self, Self::Error> { impl FromLua for Style {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
Ok(Self(match value { Ok(Self(match value {
Value::Nil => Default::default(), Value::Nil => Default::default(),
Value::UserData(ud) => ud.borrow::<Self>()?.0, Value::UserData(ud) => ud.borrow::<Self>()?.0,
@ -28,10 +30,6 @@ impl TryFrom<Value> for Style {
} }
} }
impl From<yazi_config::Style> for Style {
fn from(value: yazi_config::Style) -> Self { Self(value.into()) }
}
impl UserData for Style { impl UserData for Style {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
crate::impl_style_shorthands!(methods, 0); crate::impl_style_shorthands!(methods, 0);
@ -39,11 +37,9 @@ impl UserData for Style {
methods methods
.add_method("raw", |lua, me, ()| lua.to_value_with(&yazi_config::Style::from(me.0), SER_OPT)); .add_method("raw", |lua, me, ()| lua.to_value_with(&yazi_config::Style::from(me.0), SER_OPT));
methods.add_function_mut("patch", |_, (ud, value): (AnyUserData, Value)| { methods.add_function_mut("patch", |_, (ud, style): (AnyUserData, Self)| {
{ let mut me = ud.borrow_mut::<Self>()?;
let mut me = ud.borrow_mut::<Self>()?; me.0 = me.0.patch(style.0);
me.0 = me.0.patch(Self::try_from(value)?.0);
}
Ok(ud) Ok(ud)
}) })
} }

View file

@ -46,7 +46,7 @@ impl Core {
} }
pub fn layer(&self) -> Layer { pub fn layer(&self) -> Layer {
if self.which.visible { if self.which.active {
Layer::Which Layer::Which
} else if self.cmp.visible { } else if self.cmp.visible {
Layer::Cmp Layer::Cmp

View file

@ -6,9 +6,9 @@ pub struct Which {
pub times: usize, pub times: usize,
pub cands: Vec<ChordCow>, pub cands: Vec<ChordCow>,
// Visibility // Active state
pub visible: bool, pub active: bool,
pub silent: bool, pub silent: bool,
} }
impl Which { impl Which {
@ -33,7 +33,7 @@ impl Which {
self.times = 0; self.times = 0;
self.cands.clear(); self.cands.clear();
self.visible = false; self.active = false;
self.silent = false; self.silent = false;
} }
} }

View file

@ -58,6 +58,9 @@ impl Sendable {
Some(t) if t == TypeId::of::<yazi_parser::mgr::UpdateYankedIter>() => { Some(t) if t == TypeId::of::<yazi_parser::mgr::UpdateYankedIter>() => {
Data::Any(Box::new(ud.take::<yazi_parser::mgr::UpdateYankedIter>()?.into_opt(lua)?)) Data::Any(Box::new(ud.take::<yazi_parser::mgr::UpdateYankedIter>()?.into_opt(lua)?))
} }
Some(t) if t == TypeId::of::<yazi_binding::ChordCow>() => {
Data::Any(Box::new(ud.take::<yazi_binding::ChordCow>()?))
}
_ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?, _ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?,
}, },
Value::Error(_) => Err("error is not supported".into_lua_err())?, Value::Error(_) => Err("error is not supported".into_lua_err())?,
@ -98,6 +101,7 @@ impl Sendable {
try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v));
try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua)); try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua));
try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua));
Err("unsupported DataAny included".into_lua_err())? Err("unsupported DataAny included".into_lua_err())?
} }
data => Self::data_to_value_ref(lua, &data)?, data => Self::data_to_value_ref(lua, &data)?,
@ -130,14 +134,19 @@ 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::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(a) => { Data::Any(b) => {
if let Some(t) = a.as_any().downcast_ref::<yazi_fs::FilesOp>() { macro_rules! try_cast {
lua.create_any_userdata(t.clone())?.into_lua(lua)? ($f:expr) => {
} else if let Some(t) = a.as_any().downcast_ref::<yazi_parser::mgr::UpdateYankedOpt>() { if let Some(v) = b.as_any().downcast_ref() {
t.clone().into_lua(lua)? return $f(Clone::clone(v))?.into_lua(lua);
} else { }
Err("unsupported DataAny included".into_lua_err())? };
} }
try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v));
try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua));
try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua));
Err("unsupported DataAny included".into_lua_err())?
} }
}) })
} }

View file

@ -10,8 +10,8 @@ pub enum SparkKind {
RelayStash, RelayStash,
// quit // quit
KeyQuit, KeyQuit,
// which:show // which:activate
IndWhichShow, IndWhichActivate,
} }
impl AsRef<str> for SparkKind { impl AsRef<str> for SparkKind {
@ -25,8 +25,8 @@ impl AsRef<str> for SparkKind {
Self::RelayStash => "relay-stash", Self::RelayStash => "relay-stash",
// quit // quit
Self::KeyQuit => "key-quit", Self::KeyQuit => "key-quit",
// which:show // which:activate
Self::IndWhichShow => "ind-which-show", Self::IndWhichActivate => "ind-which-activate",
} }
} }
} }

View file

@ -132,7 +132,7 @@ pub enum Spark<'a> {
// Which // Which
WhichCallback(yazi_parser::which::CallbackOpt), WhichCallback(yazi_parser::which::CallbackOpt),
WhichShow(yazi_parser::which::ShowOpt), WhichActivate(yazi_parser::which::ActivateOpt),
} }
impl<'a> Spark<'a> { impl<'a> Spark<'a> {
@ -148,8 +148,8 @@ impl<'a> Spark<'a> {
RelayStash => Self::Stash(<_>::from_lua(value, lua)?), RelayStash => Self::Stash(<_>::from_lua(value, lua)?),
// quit // quit
KeyQuit => Self::Quit(<_>::from_lua(value, lua)?), KeyQuit => Self::Quit(<_>::from_lua(value, lua)?),
// which:show // which:activate
IndWhichShow => Self::WhichShow(<_>::from_lua(value, lua)?), IndWhichActivate => Self::WhichActivate(<_>::from_lua(value, lua)?),
}) })
} }
} }
@ -285,7 +285,7 @@ impl<'a> IntoLua for Spark<'a> {
// Which // Which
Self::WhichCallback(b) => b.into_lua(lua), Self::WhichCallback(b) => b.into_lua(lua),
Self::WhichShow(b) => b.into_lua(lua), Self::WhichActivate(b) => b.into_lua(lua),
} }
} }
} }
@ -378,4 +378,4 @@ try_from_spark!(spot::CopyOpt, spot:copy);
try_from_spark!(tasks::ProcessOpenOpt, tasks:process_open); try_from_spark!(tasks::ProcessOpenOpt, tasks:process_open);
try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed); try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed);
try_from_spark!(which::CallbackOpt, which:callback); try_from_spark!(which::CallbackOpt, which:callback);
try_from_spark!(which::ShowOpt, which:show); try_from_spark!(which::ActivateOpt, which:activate);

View file

@ -29,11 +29,11 @@ impl App {
succ!(Lives::scope(&self.core, || { succ!(Lives::scope(&self.core, || {
let body = 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); let blocking = runtime_mut!(LUA)?.critical_push(&id, true);
if let Err(e) = cb.call::<()>(body.clone()) { if let Err(e) = cb.call::<()>(body.clone()) {
error!("Failed to run `{kind}` event handler in your `{id}` plugin: {e}"); error!("Failed to run `{kind}` event handler in your `{id}` plugin: {e}");
} }
runtime_mut!(LUA)?.pop(); runtime_mut!(LUA)?.critical_pop(blocking);
} }
Ok(()) Ok(())
})?); })?);

View file

@ -50,8 +50,8 @@ impl App {
succ!(self.core.tasks.plugin_entry(opt)); succ!(self.core.tasks.plugin_entry(opt));
} }
runtime_mut!(LUA)?.push(&opt.id); let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
defer! { _ = runtime_mut!(LUA).map(|mut r| r.pop()) } defer! { _ = runtime_mut!(LUA).map(|mut r| r.critical_pop(blocking)) }
let plugin = match LOADER.load_with(&LUA, &opt.id, chunk) { let plugin = match LOADER.load_with(&LUA, &opt.id, chunk) {
Ok(t) => t, Ok(t) => t,

View file

@ -340,7 +340,7 @@ impl<'a> Executor<'a> {
}; };
} }
on!(show); on!(activate);
on!(callback); on!(callback);
succ!(); succ!();

View file

@ -65,7 +65,7 @@ impl Widget for Root<'_> {
cmp::Cmp::new(self.core).render(area, buf); cmp::Cmp::new(self.core).render(area, buf);
} }
if self.core.which.visible { if self.core.which.active {
which::Which::new(self.core).render(area, buf); which::Which::new(self.core).render(area, buf);
} }
} }

View file

@ -43,7 +43,7 @@ impl<'a> Router<'a> {
if on.len() > 1 { if on.len() > 1 {
let cx = &mut Ctx::active(&mut self.app.core); let cx = &mut Ctx::active(&mut self.app.core);
act!(which:show, cx, (layer, key)).ok(); act!(which:activate, cx, (layer, key)).ok();
} else { } else {
emit!(Seq(ChordCow::from(chord).into_seq())); emit!(Seq(ChordCow::from(chord).into_seq()));
} }

View file

@ -1,27 +1,31 @@
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; use yazi_config::{KEYMAP, keymap::{ChordCow, Key}};
use yazi_shared::{Layer, event::CmdCow}; use yazi_shared::{Layer, event::CmdCow};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ShowOpt { pub struct ActivateOpt {
pub cands: Vec<ChordCow>, pub cands: Vec<ChordCow>,
pub silent: bool, pub silent: bool,
pub times: usize, pub times: usize,
} }
impl TryFrom<CmdCow> for ShowOpt { impl TryFrom<CmdCow> for ActivateOpt {
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> {
match c.take_any2("opt") { if let Some(opt) = c.take_any2("opt") {
Some(opt) => opt, return opt;
None => bail!("missing 'opt' argument"),
} }
Ok(Self {
cands: c.take_any_iter::<yazi_binding::ChordCow>().map(Into::into).collect(),
silent: c.bool("silent"),
times: c.get("times").unwrap_or(0),
})
} }
} }
impl From<(Layer, Key)> for ShowOpt { impl From<(Layer, Key)> for ActivateOpt {
fn from((layer, key): (Layer, Key)) -> Self { fn from((layer, key): (Layer, Key)) -> Self {
Self { Self {
cands: KEYMAP cands: KEYMAP
@ -36,13 +40,13 @@ impl From<(Layer, Key)> for ShowOpt {
} }
} }
impl FromLua for ShowOpt { impl FromLua for ActivateOpt {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> { fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
let Value::Table(t) = value else { let Value::Table(t) = value else {
return Err("expected a table".into_lua_err()); return Err("expected a table".into_lua_err());
}; };
Ok(ShowOpt { Ok(Self {
cands: t cands: t
.raw_get::<Table>("cands")? .raw_get::<Table>("cands")?
.sequence_values::<yazi_binding::ChordCow>() .sequence_values::<yazi_binding::ChordCow>()
@ -54,7 +58,7 @@ impl FromLua for ShowOpt {
} }
} }
impl IntoLua for ShowOpt { impl IntoLua for ActivateOpt {
#[rustfmt::skip] #[rustfmt::skip]
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua lua

View file

@ -1 +1 @@
yazi_macro::mod_flat!(callback show); yazi_macro::mod_flat!(activate callback);

View file

@ -6,7 +6,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
use yazi_binding::{elements::{Line, Pos, Text}, runtime}; use yazi_binding::{elements::{Line, Pos, Text}, runtime};
use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}}; use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}};
use yazi_macro::relay; use yazi_macro::relay;
use yazi_parser::which::ShowOpt; use yazi_parser::which::ActivateOpt;
use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy, WhichProxy}; use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy, WhichProxy};
use yazi_shared::Debounce; use yazi_shared::Debounce;
@ -37,7 +37,7 @@ impl Utils {
.collect::<mlua::Result<_>>()?; .collect::<mlua::Result<_>>()?;
drop(tx); drop(tx);
WhichProxy::show(ShowOpt { cands, times: 0, silent: t.raw_get("silent")? }); WhichProxy::activate(ActivateOpt { cands, times: 0, silent: t.raw_get("silent")? });
Ok(rx.recv().await.map(|idx| idx + 1)) Ok(rx.recv().await.map(|idx| idx + 1))
}) })

View file

@ -1,3 +1,5 @@
use std::mem;
use anyhow::Context; use anyhow::Context;
use futures::future::join_all; use futures::future::join_all;
use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic};
@ -12,39 +14,34 @@ use super::Utils;
use crate::{bindings::{MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx}, loader::LOADER}; use crate::{bindings::{MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx}, loader::LOADER};
impl Utils { impl Utils {
pub(super) fn sync(lua: &Lua, isolate: bool) -> mlua::Result<Function> { pub(super) fn sync(lua: &Lua) -> mlua::Result<Function> {
if isolate { lua.create_function(|lua, f: Function| {
lua.create_function(|lua, ()| { let Some(block) = runtime_mut!(lua)?.put_block(&f) else {
let Some(block) = runtime_mut!(lua)?.next_block() else { return Err("`ya.sync()` must be called in a plugin").into_lua_err();
return Err("`ya.sync()` must be called in a plugin").into_lua_err(); };
};
lua.create_async_function(move |lua, args: MultiValue| async move { lua.create_async_function(move |lua, mut args: MultiValue| {
let Some(cur) = runtime!(lua)?.current_owned() else { let f = f.clone();
async move {
let rt = runtime!(lua)?;
let (Some(cur), blocking) = (rt.current_owned(), rt.blocking) else {
return Err("`ya.sync()` block must be used within a plugin").into_lua_err(); return Err("`ya.sync()` block must be used within a plugin").into_lua_err();
}; };
drop(rt);
Self::retrieve(&lua, &cur, block, args) if blocking {
.await args.push_front(Value::Table(LOADER.try_load(&lua, &cur)?));
.and_then(|data| Sendable::list_to_values(&lua, data)) f.call::<MultiValue>(args)
.with_context(|| format!("Failed to execute sync block-{block} in `{cur}` plugin")) } else {
.into_lua_err() Self::retrieve(&lua, &cur, block, args)
}) .await
}) .and_then(|data| Sendable::list_to_values(&lua, data))
} else { .with_context(|| format!("Failed to execute sync block-{block} in `{cur}` plugin"))
lua.create_function(|lua, f: Function| { .into_lua_err()
let mut rt = runtime_mut!(lua)?; }
if !rt.put_block(&f) {
return Err("`ya.sync()` must be called in a plugin").into_lua_err();
} }
let cur = rt.current_owned().unwrap();
lua.create_function(move |lua, mut args: MultiValue| {
args.push_front(Value::Table(LOADER.try_load(lua, &cur)?));
f.call::<MultiValue>(args)
})
}) })
} })
} }
pub(super) fn r#async(lua: &Lua, isolate: bool) -> mlua::Result<Function> { pub(super) fn r#async(lua: &Lua, isolate: bool) -> mlua::Result<Function> {
@ -55,9 +52,21 @@ impl Utils {
} else { } else {
lua.create_function(|lua, (f, args): (Function, MultiValue)| { lua.create_function(|lua, (f, args): (Function, MultiValue)| {
let name = runtime!(lua)?.current_owned(); let name = runtime!(lua)?.current_owned();
let lua = lua.clone();
Ok(Handle::AsyncFn(LOCAL_SET.spawn_local(async move { Ok(Handle::AsyncFn(LOCAL_SET.spawn_local(async move {
let blocking = mem::replace(&mut runtime_mut!(lua)?.blocking, false);
if let Some(s) = &name {
runtime_mut!(lua)?.push(s); // make `current_owned` always return Some to eliminate the check
}
let result = f.call_async::<MultiValue>(args).await; let result = f.call_async::<MultiValue>(args).await;
runtime_mut!(lua)?.blocking = blocking;
if name.is_some() {
runtime_mut!(lua)?.pop();
}
if let Err(ref e) = result { if let Err(ref e) = result {
match name { match name {
Some(s) => tracing::error!("Failed to execute async block in `{s}` plugin: {e}"), Some(s) => tracing::error!("Failed to execute async block in `{s}` plugin: {e}"),

View file

@ -50,7 +50,7 @@ pub fn compose(
b"spot_widgets" => Utils::spot_widgets(lua)?, b"spot_widgets" => Utils::spot_widgets(lua)?,
// Sync // Sync
b"sync" => Utils::sync(lua, isolate)?, b"sync" => Utils::sync(lua)?,
b"async" => Utils::r#async(lua, isolate)?, b"async" => Utils::r#async(lua, isolate)?,
b"chan" => Utils::chan(lua)?, b"chan" => Utils::chan(lua)?,
b"join" => Utils::join(lua)?, b"join" => Utils::join(lua)?,

View file

@ -1,10 +1,10 @@
use yazi_macro::{emit, relay}; use yazi_macro::{emit, relay};
use yazi_parser::which::ShowOpt; use yazi_parser::which::ActivateOpt;
pub struct WhichProxy; pub struct WhichProxy;
impl WhichProxy { impl WhichProxy {
pub fn show(opt: ShowOpt) { pub fn activate(opt: ActivateOpt) {
emit!(Call(relay!(which:show).with_any("opt", opt))); emit!(Call(relay!(which:activate).with_any("opt", opt)));
} }
} }

View file

@ -1,4 +1,4 @@
use hashbrown::{HashMap, hash_map::RawEntryMut}; use hashbrown::{HashMap, hash_map::Entry};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_parser::app::TaskSummary; use yazi_parser::app::TaskSummary;
@ -24,8 +24,8 @@ impl Ongoing {
} }
pub(super) fn cancel(&mut self, id: Id) -> Option<TaskIn> { pub(super) fn cancel(&mut self, id: Id) -> Option<TaskIn> {
match self.inner.raw_entry_mut().from_key(&id) { match self.inner.entry(id) {
RawEntryMut::Occupied(mut oe) => { Entry::Occupied(mut oe) => {
let task = oe.get_mut(); let task = oe.get_mut();
task.done.complete(false); task.done.complete(false);
@ -35,7 +35,7 @@ impl Ongoing {
oe.remove(); oe.remove();
} }
RawEntryMut::Vacant(_) => {} Entry::Vacant(_) => {}
} }
None None
} }

View file

@ -185,6 +185,10 @@ impl Cmd {
self.args.remove(&name.into()).map(Data::into_any2) self.args.remove(&name.into()).map(Data::into_any2)
} }
pub fn take_any_iter<T: 'static>(&mut self) -> impl Iterator<Item = T> {
(0..self.len()).filter_map(|i| self.args.remove(&DataKey::from(i))?.into_any())
}
// Parse // Parse
pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>> pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>>
where where

View file

@ -1,4 +1,4 @@
use std::ops::Deref; use std::{iter, ops::Deref};
use anyhow::Result; use anyhow::Result;
@ -92,4 +92,11 @@ impl CmdCow {
Self::Borrowed(_) => None, Self::Borrowed(_) => None,
} }
} }
pub fn take_any_iter<'a, T: 'static>(&'a mut self) -> Box<dyn Iterator<Item = T> + 'a> {
match self {
Self::Owned(c) => Box::new(c.take_any_iter()),
Self::Borrowed(_) => Box::new(iter::empty()),
}
}
} }