feat: allow to specify layer for keymap commands (#2399)

This commit is contained in:
三咲雅 · Misaki Masa 2025-02-26 03:01:17 +08:00 committed by GitHub
parent 235f6888d4
commit 953d567c63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 263 additions and 284 deletions

View file

@ -321,7 +321,7 @@ keymap = [
{ on = "<F1>", run = "help", desc = "Open help" }, { on = "<F1>", run = "help", desc = "Open help" },
] ]
[completion] [cmp]
keymap = [ keymap = [
{ on = "<C-c>", run = "close", desc = "Cancel completion" }, { on = "<C-c>", run = "close", desc = "Cancel completion" },

View file

@ -173,7 +173,7 @@ selected = { reversed = true }
# : Completion {{{ # : Completion {{{
[completion] [cmp]
border = { fg = "blue" } border = { fg = "blue" }
active = { reversed = true } active = { reversed = true }
inactive = {} inactive = {}

View file

@ -173,7 +173,7 @@ selected = { reversed = true }
# : Completion {{{ # : Completion {{{
[completion] [cmp]
border = { fg = "blue" } border = { fg = "blue" }
active = { reversed = true } active = { reversed = true }
inactive = {} inactive = {}

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock};
use regex::Regex; use regex::Regex;
use serde::Deserialize; use serde::Deserialize;
use yazi_shared::event::Cmd; use yazi_shared::{Layer, event::Cmd};
use super::Key; use super::Key;
@ -42,15 +42,25 @@ impl Chord {
pub fn desc_or_run(&self) -> Cow<str> { self.desc().unwrap_or_else(|| self.run().into()) } pub fn desc_or_run(&self) -> Cow<str> { self.desc().unwrap_or_else(|| self.run().into()) }
#[inline]
pub fn noop(&self) -> bool {
self.run.len() == 1 && self.run[0].name == "noop" && self.run[0].args.is_empty()
}
pub fn contains(&self, s: &str) -> bool { pub fn contains(&self, s: &str) -> bool {
let s = s.to_lowercase(); let s = s.to_lowercase();
self.desc().map(|d| d.to_lowercase().contains(&s)) == Some(true) self.desc().map(|d| d.to_lowercase().contains(&s)) == Some(true)
|| self.run().to_lowercase().contains(&s) || self.run().to_lowercase().contains(&s)
|| self.on().to_lowercase().contains(&s) || self.on().to_lowercase().contains(&s)
} }
#[inline]
pub(super) fn noop(&self) -> bool {
self.run.len() == 1 && self.run[0].name == "noop" && self.run[0].args.is_empty()
}
#[inline]
pub(super) fn with_layer(mut self, layer: Layer) -> Self {
for c in &mut self.run {
if c.layer == Default::default() {
c.layer = layer;
}
}
self
}
} }

View file

@ -10,21 +10,21 @@ use crate::{Preset, keymap::Key};
#[derive(Debug)] #[derive(Debug)]
pub struct Keymap { pub struct Keymap {
pub mgr: Vec<Chord>, pub mgr: Vec<Chord>,
pub tasks: Vec<Chord>, pub tasks: Vec<Chord>,
pub spot: Vec<Chord>, pub spot: Vec<Chord>,
pub pick: Vec<Chord>, pub pick: Vec<Chord>,
pub input: Vec<Chord>, pub input: Vec<Chord>,
pub confirm: Vec<Chord>, pub confirm: Vec<Chord>,
pub help: Vec<Chord>, pub help: Vec<Chord>,
pub completion: Vec<Chord>, pub cmp: Vec<Chord>,
} }
impl Keymap { impl Keymap {
#[inline] #[inline]
pub fn get(&self, layer: Layer) -> &[Chord] { pub fn get(&self, layer: Layer) -> &[Chord] {
match layer { match layer {
Layer::App => unreachable!(), Layer::App => &[],
Layer::Mgr => &self.mgr, Layer::Mgr => &self.mgr,
Layer::Tasks => &self.tasks, Layer::Tasks => &self.tasks,
Layer::Spot => &self.spot, Layer::Spot => &self.spot,
@ -32,8 +32,8 @@ impl Keymap {
Layer::Input => &self.input, Layer::Input => &self.input,
Layer::Confirm => &self.confirm, Layer::Confirm => &self.confirm,
Layer::Help => &self.help, Layer::Help => &self.help,
Layer::Completion => &self.completion, Layer::Cmp => &self.cmp,
Layer::Which => unreachable!(), Layer::Which => &[],
} }
} }
} }
@ -54,14 +54,14 @@ impl<'de> Deserialize<'de> for Keymap {
#[derive(Deserialize)] #[derive(Deserialize)]
struct Shadow { struct Shadow {
#[serde(rename = "manager")] #[serde(rename = "manager")]
mgr: Inner, // TODO: remove serde(rename) mgr: Inner, // TODO: remove serde(rename)
tasks: Inner, tasks: Inner,
spot: Inner, spot: Inner,
pick: Inner, pick: Inner,
input: Inner, input: Inner,
confirm: Inner, confirm: Inner,
help: Inner, help: Inner,
completion: Inner, cmp: Inner,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct Inner { struct Inner {
@ -72,7 +72,7 @@ impl<'de> Deserialize<'de> for Keymap {
append_keymap: IndexSet<Chord>, append_keymap: IndexSet<Chord>,
} }
fn mix(a: IndexSet<Chord>, b: IndexSet<Chord>, c: IndexSet<Chord>) -> Vec<Chord> { fn mix(l: Layer, a: IndexSet<Chord>, b: IndexSet<Chord>, c: IndexSet<Chord>) -> Vec<Chord> {
#[inline] #[inline]
fn on(Chord { on, .. }: &Chord) -> [Key; 2] { fn on(Chord { on, .. }: &Chord) -> [Key; 2] {
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()] [on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
@ -87,27 +87,28 @@ impl<'de> Deserialize<'de> for Keymap {
c.into_iter().filter(|v| !b_seen.contains(&on(v))), c.into_iter().filter(|v| !b_seen.contains(&on(v))),
) )
.filter(|chord| !chord.noop()) .filter(|chord| !chord.noop())
.map(|chord| chord.with_layer(l))
.collect() .collect()
} }
let shadow = Shadow::deserialize(deserializer)?; let shadow = Shadow::deserialize(deserializer)?;
Ok(Self { Ok(Self {
#[rustfmt::skip] #[rustfmt::skip]
mgr: mix(shadow.mgr.prepend_keymap, shadow.mgr.keymap, shadow.mgr.append_keymap), mgr: mix(Layer::Mgr, shadow.mgr.prepend_keymap, shadow.mgr.keymap, shadow.mgr.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
tasks: mix(shadow.tasks.prepend_keymap, shadow.tasks.keymap, shadow.tasks.append_keymap), tasks: mix(Layer::Tasks, shadow.tasks.prepend_keymap, shadow.tasks.keymap, shadow.tasks.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
spot: mix(shadow.spot.prepend_keymap, shadow.spot.keymap, shadow.spot.append_keymap), spot: mix(Layer::Spot, shadow.spot.prepend_keymap, shadow.spot.keymap, shadow.spot.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
pick: mix(shadow.pick.prepend_keymap, shadow.pick.keymap, shadow.pick.append_keymap), pick: mix(Layer::Pick, shadow.pick.prepend_keymap, shadow.pick.keymap, shadow.pick.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
input: mix(shadow.input.prepend_keymap, shadow.input.keymap, shadow.input.append_keymap), input: mix(Layer::Input, shadow.input.prepend_keymap, shadow.input.keymap, shadow.input.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
confirm: mix(shadow.confirm.prepend_keymap, shadow.confirm.keymap, shadow.confirm.append_keymap), confirm: mix(Layer::Confirm, shadow.confirm.prepend_keymap, shadow.confirm.keymap, shadow.confirm.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
help: mix(shadow.help.prepend_keymap, shadow.help.keymap, shadow.help.append_keymap), help: mix(Layer::Help, shadow.help.prepend_keymap, shadow.help.keymap, shadow.help.append_keymap),
#[rustfmt::skip] #[rustfmt::skip]
completion: mix(shadow.completion.prepend_keymap, shadow.completion.keymap, shadow.completion.append_keymap), cmp: mix(Layer::Cmp, shadow.cmp.prepend_keymap, shadow.cmp.keymap, shadow.cmp.append_keymap),
}) })
} }
} }

View file

@ -9,20 +9,20 @@ use super::{Filetype, Flavor, Icons};
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Theme { pub struct Theme {
pub flavor: Flavor, pub flavor: Flavor,
#[serde(rename = "manager")] #[serde(rename = "manager")]
pub mgr: Mgr, // TODO: Remove `serde(rename)` pub mgr: Mgr, // TODO: Remove `serde(rename)`
pub mode: Mode, pub mode: Mode,
pub status: Status, pub status: Status,
pub which: Which, pub which: Which,
pub confirm: Confirm, pub confirm: Confirm,
pub spot: Spot, pub spot: Spot,
pub notify: Notify, pub notify: Notify,
pub pick: Pick, pub pick: Pick,
pub input: Input, pub input: Input,
pub completion: Completion, pub cmp: Cmp,
pub tasks: Tasks, pub tasks: Tasks,
pub help: Help, pub help: Help,
// File-specific styles // File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
@ -178,7 +178,7 @@ pub struct Input {
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Completion { pub struct Cmp {
pub border: Style, pub border: Style,
pub active: Style, pub active: Style,
pub inactive: Style, pub inactive: Style,

View file

@ -1,7 +1,7 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
#[derive(Default)] #[derive(Default)]
pub struct Completion { pub struct Cmp {
pub(super) caches: HashMap<PathBuf, Vec<String>>, pub(super) caches: HashMap<PathBuf, Vec<String>>,
pub(super) cands: Vec<String>, pub(super) cands: Vec<String>,
pub(super) offset: usize, pub(super) offset: usize,
@ -11,7 +11,7 @@ pub struct Completion {
pub visible: bool, pub visible: bool,
} }
impl Completion { impl Cmp {
// --- Cands // --- Cands
#[inline] #[inline]
pub fn window(&self) -> &[String] { pub fn window(&self) -> &[String] {

View file

@ -1,7 +1,7 @@
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::event::{CmdCow, Data}; use yazi_shared::event::{CmdCow, Data};
use crate::completion::Completion; use crate::cmp::Cmp;
struct Opt { struct Opt {
step: isize, step: isize,
@ -11,7 +11,7 @@ impl From<CmdCow> for Opt {
fn from(c: CmdCow) -> Self { Self { step: c.first().and_then(Data::as_isize).unwrap_or(0) } } fn from(c: CmdCow) -> Self { Self { step: c.first().and_then(Data::as_isize).unwrap_or(0) } }
} }
impl Completion { impl Cmp {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn arrow(&mut self, opt: Opt) { pub fn arrow(&mut self, opt: Opt) {
if opt.step > 0 { if opt.step > 0 {

View file

@ -2,7 +2,7 @@ use yazi_macro::render;
use yazi_proxy::InputProxy; use yazi_proxy::InputProxy;
use yazi_shared::event::CmdCow; use yazi_shared::event::CmdCow;
use crate::completion::Completion; use crate::cmp::Cmp;
struct Opt { struct Opt {
submit: bool, submit: bool,
@ -15,7 +15,7 @@ impl From<bool> for Opt {
fn from(submit: bool) -> Self { Self { submit } } fn from(submit: bool) -> Self { Self { submit } }
} }
impl Completion { impl Cmp {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn close(&mut self, opt: Opt) { pub fn close(&mut self, opt: Opt) {
if let Some(s) = self.selected().filter(|_| opt.submit) { if let Some(s) = self.selected().filter(|_| opt.submit) {

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, mem, ops::ControlFlow, path::PathBuf};
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::event::{Cmd, CmdCow, Data}; use yazi_shared::event::{Cmd, CmdCow, Data};
use crate::completion::Completion; use crate::cmp::Cmp;
const LIMIT: usize = 30; const LIMIT: usize = 30;
@ -29,7 +29,7 @@ impl From<Cmd> for Opt {
fn from(c: Cmd) -> Self { Self::from(CmdCow::from(c)) } fn from(c: Cmd) -> Self { Self::from(CmdCow::from(c)) }
} }
impl Completion { impl Cmp {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn show(&mut self, opt: Opt) { pub fn show(&mut self, opt: Opt) {
if self.ticket != opt.ticket { if self.ticket != opt.ticket {

View file

@ -3,9 +3,9 @@ use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR_STR, PathBuf}};
use tokio::fs; use tokio::fs;
use yazi_fs::{CWD, expand_path}; use yazi_fs::{CWD, expand_path};
use yazi_macro::{emit, render}; use yazi_macro::{emit, render};
use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}}; use yazi_shared::event::{Cmd, CmdCow, Data};
use crate::completion::Completion; use crate::cmp::Cmp;
struct Opt { struct Opt {
word: Cow<'static, str>, word: Cow<'static, str>,
@ -21,7 +21,7 @@ impl From<CmdCow> for Opt {
} }
} }
impl Completion { impl Cmp {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn trigger(&mut self, opt: Opt) { pub fn trigger(&mut self, opt: Opt) {
if opt.ticket < self.ticket { if opt.ticket < self.ticket {
@ -55,12 +55,11 @@ impl Completion {
if !cache.is_empty() { if !cache.is_empty() {
emit!(Call( emit!(Call(
Cmd::new("show") Cmd::new("cmp:show")
.with_any("cache", cache) .with_any("cache", cache)
.with_any("cache-name", parent) .with_any("cache-name", parent)
.with("word", word) .with("word", word)
.with("ticket", ticket), .with("ticket", ticket)
Layer::Completion
)); ));
} }
@ -95,7 +94,7 @@ mod tests {
use super::*; use super::*;
fn compare(s: &str, parent: &str, child: &str) -> bool { fn compare(s: &str, parent: &str, child: &str) -> bool {
let (p, c) = Completion::split_path(s).unwrap(); let (p, c) = Cmp::split_path(s).unwrap();
let p = p.strip_prefix(yazi_fs::CWD.load().as_ref()).unwrap_or(&p); let p = p.strip_prefix(yazi_fs::CWD.load().as_ref()).unwrap_or(&p);
p == Path::new(parent) && c == child p == Path::new(parent) && c == child
} }

3
yazi-core/src/cmp/mod.rs Normal file
View file

@ -0,0 +1,3 @@
yazi_macro::mod_pub!(commands);
yazi_macro::mod_flat!(cmp);

View file

@ -1,3 +0,0 @@
yazi_macro::mod_pub!(commands);
yazi_macro::mod_flat!(completion);

View file

@ -1,5 +1,5 @@
use yazi_macro::render; use yazi_macro::render;
use yazi_proxy::CompletionProxy; use yazi_proxy::CmpProxy;
use yazi_shared::{errors::InputError, event::CmdCow}; use yazi_shared::{errors::InputError, event::CmdCow};
use crate::input::Input; use crate::input::Input;
@ -19,7 +19,7 @@ impl Input {
#[yazi_codegen::command] #[yazi_codegen::command]
pub fn close(&mut self, opt: Opt) { pub fn close(&mut self, opt: Opt) {
if self.completion { if self.completion {
CompletionProxy::close(); CmpProxy::close();
} }
if let Some(cb) = self.callback.take() { if let Some(cb) = self.callback.take() {

View file

@ -1,5 +1,5 @@
use yazi_macro::render; use yazi_macro::render;
use yazi_proxy::CompletionProxy; use yazi_proxy::CmpProxy;
use yazi_shared::event::CmdCow; use yazi_shared::event::CmdCow;
use crate::input::{Input, InputMode, op::InputOp}; use crate::input::{Input, InputMode, op::InputOp};
@ -29,7 +29,7 @@ impl Input {
self.move_(-1); self.move_(-1);
if self.completion { if self.completion {
CompletionProxy::close(); CmpProxy::close();
} }
} }
InputMode::Replace => { InputMode::Replace => {

View file

@ -6,7 +6,7 @@
clippy::unit_arg clippy::unit_arg
)] )]
yazi_macro::mod_pub!(completion confirm help input mgr notify pick spot tab tasks which); yazi_macro::mod_pub!(cmp confirm help input mgr notify pick spot tab tasks which);
pub fn init() { pub fn init() {
mgr::WATCHED.with(<_>::default); mgr::WATCHED.with(<_>::default);

View file

@ -1,7 +1,7 @@
use std::time::Instant; use std::time::Instant;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
use crate::notify::{Message, Notify}; use crate::notify::{Message, Notify};
@ -14,7 +14,7 @@ impl Notify {
if self.messages.iter().all(|m| m != &msg) { if self.messages.iter().all(|m| m != &msg) {
self.messages.push(msg); self.messages.push(msg);
emit!(Call(Cmd::args("update_notify", &[0]), Layer::App)); emit!(Call(Cmd::args("app:update_notify", &[0])));
} }
} }
} }

View file

@ -2,7 +2,7 @@ use std::time::Duration;
use ratatui::layout::Rect; use ratatui::layout::Rect;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}}; use yazi_shared::event::{Cmd, CmdCow, Data};
use crate::notify::Notify; use crate::notify::Notify;
@ -69,7 +69,7 @@ impl Notify {
self.tick_handle = Some(tokio::spawn(async move { self.tick_handle = Some(tokio::spawn(async move {
tokio::time::sleep(interval).await; tokio::time::sleep(interval).await;
emit!(Call(Cmd::args("update_notify", &[interval.as_secs_f64()]), Layer::App)); emit!(Call(Cmd::args("app:update_notify", &[interval.as_secs_f64()])));
})); }));
} }
} }

View file

@ -6,7 +6,7 @@ use yazi_config::popup::InputCfg;
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::expand_path; use yazi_fs::expand_path;
use yazi_macro::render; use yazi_macro::render;
use yazi_proxy::{CompletionProxy, InputProxy, MgrProxy, TabProxy}; use yazi_proxy::{CmpProxy, InputProxy, MgrProxy, TabProxy};
use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url}; use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url};
use crate::tab::Tab; use crate::tab::Tab;
@ -98,7 +98,7 @@ impl Tab {
} }
} }
Err(InputError::Completed(before, ticket)) => { Err(InputError::Completed(before, ticket)) => {
CompletionProxy::trigger(&before, ticket); CmpProxy::trigger(&before, ticket);
} }
_ => break, _ => break,
} }

View file

@ -6,7 +6,7 @@ use yazi_config::popup::InputCfg;
use yazi_fs::FilterCase; use yazi_fs::FilterCase;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_proxy::InputProxy; use yazi_proxy::InputProxy;
use yazi_shared::{Debounce, Layer, errors::InputError, event::{Cmd, CmdCow}}; use yazi_shared::{Debounce, errors::InputError, event::{Cmd, CmdCow}};
use crate::tab::Tab; use crate::tab::Tab;
@ -41,11 +41,10 @@ impl Tab {
let (Ok(s) | Err(InputError::Typed(s))) = result else { continue }; let (Ok(s) | Err(InputError::Typed(s))) = result else { continue };
emit!(Call( emit!(Call(
Cmd::args("filter_do", &[s]) Cmd::args("mgr:filter_do", &[s])
.with_bool("smart", opt.case == FilterCase::Smart) .with_bool("smart", opt.case == FilterCase::Smart)
.with_bool("insensitive", opt.case == FilterCase::Insensitive) .with_bool("insensitive", opt.case == FilterCase::Insensitive)
.with_bool("done", done), .with_bool("done", done)
Layer::Mgr
)); ));
} }
}); });

View file

@ -6,7 +6,7 @@ use yazi_config::popup::InputCfg;
use yazi_fs::FilterCase; use yazi_fs::FilterCase;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_proxy::InputProxy; use yazi_proxy::InputProxy;
use yazi_shared::{Debounce, Layer, errors::InputError, event::{Cmd, CmdCow}}; use yazi_shared::{Debounce, errors::InputError, event::{Cmd, CmdCow}};
use crate::tab::Tab; use crate::tab::Tab;
@ -33,11 +33,10 @@ impl Tab {
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( emit!(Call(
Cmd::args("find_do", &[s]) Cmd::args("mgr:find_do", &[s])
.with_bool("previous", opt.prev) .with_bool("previous", opt.prev)
.with_bool("smart", opt.case == FilterCase::Smart) .with_bool("smart", opt.case == FilterCase::Smart)
.with_bool("insensitive", opt.case == FilterCase::Insensitive), .with_bool("insensitive", opt.case == FilterCase::Insensitive)
Layer::Mgr
)); ));
} }
}); });

View file

@ -5,7 +5,7 @@ use tokio::{task::JoinHandle, time::sleep};
use yazi_adapter::Dimension; use yazi_adapter::Dimension;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_scheduler::{Ongoing, Scheduler, TaskSummary}; use yazi_scheduler::{Ongoing, Scheduler, TaskSummary};
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress}; use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT, TasksProgress};
@ -32,7 +32,7 @@ impl Tasks {
let new = TasksProgress::from(&*ongoing.lock()); let new = TasksProgress::from(&*ongoing.lock());
if last != new { if last != new {
last = new; last = new;
emit!(Call(Cmd::new("update_progress").with_any("progress", new), Layer::App)); emit!(Call(Cmd::new("app:update_progress").with_any("progress", new)));
} }
} }
}); });

View file

@ -1,5 +1,3 @@
use std::str::FromStr;
use yazi_config::{KEYMAP, keymap::{Chord, Key}}; use yazi_config::{KEYMAP, keymap::{Chord, Key}};
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::{Layer, event::CmdCow}; use yazi_shared::{Layer, event::CmdCow};
@ -8,7 +6,6 @@ use crate::which::{Which, WhichSorter};
pub struct Opt { pub struct Opt {
cands: Vec<Chord>, cands: Vec<Chord>,
layer: Layer,
silent: bool, silent: bool,
} }
@ -16,11 +13,7 @@ impl TryFrom<CmdCow> for Opt {
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> {
Ok(Self { Ok(Self { cands: c.take_any("candidates").unwrap_or_default(), silent: c.bool("silent") })
cands: c.take_any("candidates").unwrap_or_default(),
layer: Layer::from_str(&c.take_str("layer").unwrap_or_default())?,
silent: c.bool("silent"),
})
} }
} }
@ -34,7 +27,6 @@ impl Which {
return; return;
} }
self.layer = opt.layer;
self.times = 0; self.times = 0;
self.cands = opt.cands.into_iter().map(|c| c.into()).collect(); self.cands = opt.cands.into_iter().map(|c| c.into()).collect();
@ -44,7 +36,6 @@ impl Which {
} }
pub fn show_with(&mut self, key: Key, layer: Layer) { pub fn show_with(&mut self, key: Key, layer: Layer) {
self.layer = layer;
self.times = 1; self.times = 1;
self.cands = KEYMAP self.cands = KEYMAP
.get(layer) .get(layer)

View file

@ -1,12 +1,10 @@
use yazi_config::keymap::{ChordCow, Key}; use yazi_config::keymap::{ChordCow, Key};
use yazi_macro::{emit, render_and}; use yazi_macro::{emit, render_and};
use yazi_shared::Layer;
#[derive(Default)] #[derive(Default)]
pub struct Which { pub struct Which {
pub(super) layer: Layer, pub times: usize,
pub times: usize, pub cands: Vec<ChordCow>,
pub cands: Vec<ChordCow>,
// Visibility // Visibility
pub visible: bool, pub visible: bool,
@ -21,10 +19,10 @@ impl Which {
if self.cands.is_empty() { if self.cands.is_empty() {
self.reset(); self.reset();
} else if self.cands.len() == 1 { } else if self.cands.len() == 1 {
emit!(Seq(self.cands.remove(0).into_seq(), self.layer)); emit!(Seq(self.cands.remove(0).into_seq()));
self.reset(); self.reset();
} else if let Some(i) = self.cands.iter().position(|c| c.on.len() == self.times) { } else if let Some(i) = self.cands.iter().position(|c| c.on.len() == self.times) {
emit!(Seq(self.cands.remove(i).into_seq(), self.layer)); emit!(Seq(self.cands.remove(i).into_seq()));
self.reset(); self.reset();
} }

View file

@ -3,7 +3,7 @@ use std::{fmt::Display, io::Write, str::FromStr};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use yazi_boot::BOOT; use yazi_boot::BOOT;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
use crate::{ID, body::Body}; use crate::{ID, body::Body};
@ -47,7 +47,7 @@ impl<'a> Payload<'a> {
impl Payload<'static> { impl Payload<'static> {
pub(super) fn emit(self) { pub(super) fn emit(self) {
self.try_flush(); self.try_flush();
emit!(Call(Cmd::new("accept_payload").with_any("payload", self), Layer::App)); emit!(Call(Cmd::new("app:accept_payload").with_any("payload", self)));
} }
} }

View file

@ -5,7 +5,7 @@ use crossterm::event::KeyEvent;
use yazi_config::keymap::Key; use yazi_config::keymap::Key;
use yazi_core::input::InputMode; use yazi_core::input::InputMode;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::{CmdCow, Event, NEED_RENDER}}; use yazi_shared::event::{CmdCow, Event, NEED_RENDER};
use crate::{Ctx, Executor, Router, Signals, Term, lives::Lives}; use crate::{Ctx, Executor, Router, Signals, Term, lives::Lives};
@ -53,8 +53,8 @@ impl App {
#[inline] #[inline]
fn dispatch(&mut self, event: Event) -> Result<()> { fn dispatch(&mut self, event: Event) -> Result<()> {
match event { match event {
Event::Call(cmd, layer) => self.dispatch_call(cmd, layer), Event::Call(cmd) => self.dispatch_call(cmd),
Event::Seq(cmds, layer) => self.dispatch_seq(cmds, layer), Event::Seq(cmds) => self.dispatch_seq(cmds),
Event::Render => self.dispatch_render(), Event::Render => self.dispatch_render(),
Event::Key(key) => self.dispatch_key(key), Event::Key(key) => self.dispatch_key(key),
Event::Mouse(mouse) => self.mouse(mouse), Event::Mouse(mouse) => self.mouse(mouse),
@ -66,17 +66,15 @@ impl App {
} }
#[inline] #[inline]
fn dispatch_call(&mut self, cmd: CmdCow, layer: Layer) { fn dispatch_call(&mut self, cmd: CmdCow) { Executor::new(self).execute(cmd); }
Executor::new(self).execute(cmd, layer);
}
#[inline] #[inline]
fn dispatch_seq(&mut self, mut cmds: Vec<CmdCow>, layer: Layer) { fn dispatch_seq(&mut self, mut cmds: Vec<CmdCow>) {
if let Some(last) = cmds.pop() { if let Some(last) = cmds.pop() {
Executor::new(self).execute(last, layer); Executor::new(self).execute(last);
} }
if !cmds.is_empty() { if !cmds.is_empty() {
emit!(Seq(cmds, layer)); emit!(Seq(cmds));
} }
} }

View file

@ -6,34 +6,31 @@ use yazi_config::{THEME, popup::{Offset, Position}};
use crate::Ctx; use crate::Ctx;
pub(crate) struct Completion<'a> { pub(crate) struct Cmp<'a> {
cx: &'a Ctx, cx: &'a Ctx,
} }
impl<'a> Completion<'a> { impl<'a> Cmp<'a> {
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
} }
impl Widget for Completion<'_> { impl Widget for Cmp<'_> {
fn render(self, rect: Rect, buf: &mut Buffer) { fn render(self, rect: Rect, buf: &mut Buffer) {
let items: Vec<_> = self let items: Vec<_> = self
.cx .cx
.completion .cmp
.window() .window()
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, x)| { .map(|(i, x)| {
let icon = if x.ends_with(MAIN_SEPARATOR) { let icon =
&THEME.completion.icon_folder if x.ends_with(MAIN_SEPARATOR) { &THEME.cmp.icon_folder } else { &THEME.cmp.icon_file };
} else {
&THEME.completion.icon_file
};
let mut item = ListItem::new(format!(" {icon} {x}")); let mut item = ListItem::new(format!(" {icon} {x}"));
if i == self.cx.completion.rel_cursor() { if i == self.cx.cmp.rel_cursor() {
item = item.style(THEME.completion.active); item = item.style(THEME.cmp.active);
} else { } else {
item = item.style(THEME.completion.inactive); item = item.style(THEME.cmp.inactive);
} }
item item
@ -57,9 +54,7 @@ impl Widget for Completion<'_> {
yazi_plugin::elements::Clear::default().render(area, buf); yazi_plugin::elements::Clear::default().render(area, buf);
List::new(items) List::new(items)
.block( .block(Block::bordered().border_type(BorderType::Rounded).border_style(THEME.cmp.border))
Block::bordered().border_type(BorderType::Rounded).border_style(THEME.completion.border),
)
.render(area, buf); .render(area, buf);
} }
} }

1
yazi-fm/src/cmp/mod.rs Normal file
View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(cmp);

View file

@ -1 +0,0 @@
yazi_macro::mod_flat!(completion);

View file

@ -1,31 +1,31 @@
use ratatui::layout::Rect; use ratatui::layout::Rect;
use yazi_core::{completion::Completion, confirm::Confirm, help::Help, input::Input, mgr::Mgr, notify::Notify, pick::Pick, tab::{Folder, Tab}, tasks::Tasks, which::Which}; use yazi_core::{cmp::Cmp, confirm::Confirm, help::Help, input::Input, mgr::Mgr, notify::Notify, pick::Pick, tab::{Folder, Tab}, tasks::Tasks, which::Which};
use yazi_shared::Layer; use yazi_shared::Layer;
pub struct Ctx { pub struct Ctx {
pub mgr: Mgr, pub mgr: Mgr,
pub tasks: Tasks, pub tasks: Tasks,
pub pick: Pick, pub pick: Pick,
pub input: Input, pub input: Input,
pub confirm: Confirm, pub confirm: Confirm,
pub help: Help, pub help: Help,
pub completion: Completion, pub cmp: Cmp,
pub which: Which, pub which: Which,
pub notify: Notify, pub notify: Notify,
} }
impl Ctx { impl Ctx {
pub fn make() -> Self { pub fn make() -> Self {
Self { Self {
mgr: Mgr::make(), mgr: Mgr::make(),
tasks: Tasks::serve(), tasks: Tasks::serve(),
pick: Default::default(), pick: Default::default(),
input: Default::default(), input: Default::default(),
confirm: Default::default(), confirm: Default::default(),
help: Default::default(), help: Default::default(),
completion: Default::default(), cmp: Default::default(),
which: Default::default(), which: Default::default(),
notify: Default::default(), notify: Default::default(),
} }
} }
@ -45,8 +45,8 @@ impl Ctx {
pub fn layer(&self) -> Layer { pub fn layer(&self) -> Layer {
if self.which.visible { if self.which.visible {
Layer::Which Layer::Which
} else if self.completion.visible { } else if self.cmp.visible {
Layer::Completion Layer::Cmp
} else if self.help.visible { } else if self.help.visible {
Layer::Help Layer::Help
} else if self.confirm.visible { } else if self.confirm.visible {

View file

@ -12,8 +12,8 @@ impl<'a> Executor<'a> {
pub(super) fn new(app: &'a mut App) -> Self { Self { app } } pub(super) fn new(app: &'a mut App) -> Self { Self { app } }
#[inline] #[inline]
pub(super) fn execute(&mut self, cmd: CmdCow, layer: Layer) { pub(super) fn execute(&mut self, cmd: CmdCow) {
match layer { match cmd.layer {
Layer::App => self.app(cmd), Layer::App => self.app(cmd),
Layer::Mgr => self.mgr(cmd), Layer::Mgr => self.mgr(cmd),
Layer::Tasks => self.tasks(cmd), Layer::Tasks => self.tasks(cmd),
@ -22,7 +22,7 @@ impl<'a> Executor<'a> {
Layer::Input => self.input(cmd), Layer::Input => self.input(cmd),
Layer::Confirm => self.confirm(cmd), Layer::Confirm => self.confirm(cmd),
Layer::Help => self.help(cmd), Layer::Help => self.help(cmd),
Layer::Completion => self.completion(cmd), Layer::Cmp => self.cmp(cmd),
Layer::Which => self.which(cmd), Layer::Which => self.which(cmd),
} }
} }
@ -249,7 +249,7 @@ impl<'a> Executor<'a> {
if cmd.name.as_str() == "complete" { if cmd.name.as_str() == "complete" {
return if cmd.bool("trigger") { return if cmd.bool("trigger") {
self.app.cx.completion.trigger(cmd) self.app.cx.cmp.trigger(cmd)
} else { } else {
self.app.cx.input.complete(cmd) self.app.cx.input.complete(cmd)
}; };
@ -319,11 +319,11 @@ impl<'a> Executor<'a> {
} }
} }
fn completion(&mut self, cmd: CmdCow) { fn cmp(&mut self, cmd: CmdCow) {
macro_rules! on { macro_rules! on {
($name:ident) => { ($name:ident) => {
if cmd.name == stringify!($name) { if cmd.name == stringify!($name) {
return self.app.cx.completion.$name(cmd); return self.app.cx.cmp.$name(cmd);
} }
}; };
} }
@ -336,7 +336,7 @@ impl<'a> Executor<'a> {
match cmd.name.as_str() { match cmd.name.as_str() {
"close_input" => self.app.cx.input.close(cmd), "close_input" => self.app.cx.input.close(cmd),
// Help // Help
"help" => self.app.cx.help.toggle(Layer::Completion), "help" => self.app.cx.help.toggle(Layer::Cmp),
// Plugin // Plugin
"plugin" => self.app.plugin(cmd), "plugin" => self.app.plugin(cmd),
_ => {} _ => {}

View file

@ -4,7 +4,7 @@
#[global_allocator] #[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
yazi_macro::mod_pub!(app completion confirm help input lives mgr notify pick spot tasks which); yazi_macro::mod_pub!(app cmp confirm help input lives mgr notify pick spot tasks which);
yazi_macro::mod_flat!(context executor logs panic root router signals term); yazi_macro::mod_flat!(context executor logs panic root router signals term);

View file

@ -3,7 +3,7 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use tracing::error; use tracing::error;
use yazi_plugin::{LUA, elements::render_once}; use yazi_plugin::{LUA, elements::render_once};
use super::{completion, confirm, help, input, mgr, pick, spot, tasks, which}; use super::{cmp, confirm, help, input, mgr, pick, spot, tasks, which};
use crate::Ctx; use crate::Ctx;
pub(super) struct Root<'a> { pub(super) struct Root<'a> {
@ -60,8 +60,8 @@ impl Widget for Root<'_> {
help::Help::new(self.cx).render(area, buf); help::Help::new(self.cx).render(area, buf);
} }
if self.cx.completion.visible { if self.cx.cmp.visible {
completion::Completion::new(self.cx).render(area, buf); cmp::Cmp::new(self.cx).render(area, buf);
} }
if self.cx.which.visible { if self.cx.which.visible {

View file

@ -30,7 +30,7 @@ impl<'a> Router<'a> {
L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => { L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => {
self.matches(layer, key) self.matches(layer, key)
} }
L::Completion => self.matches(L::Completion, key) || self.matches(L::Input, key), L::Cmp => self.matches(L::Cmp, key) || self.matches(L::Input, key),
L::Which => cx.which.type_(key), L::Which => cx.which.type_(key),
} }
} }
@ -45,7 +45,7 @@ impl<'a> Router<'a> {
if on.len() > 1 { if on.len() > 1 {
self.app.cx.which.show_with(key, layer); self.app.cx.which.show_with(key, layer);
} else { } else {
emit!(Seq(ChordCow::from(chord).into_seq(), layer)); emit!(Seq(ChordCow::from(chord).into_seq()));
} }
return true; return true;
} }

View file

@ -1,6 +1,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use yazi_shared::{Id, Ids, Layer, event::Cmd, url::{Url, UrnBuf}}; use yazi_shared::{Id, Ids, event::Cmd, url::{Url, UrnBuf}};
use super::{Cha, File}; use super::{Cha, File};
@ -39,11 +39,8 @@ impl FilesOp {
#[inline] #[inline]
pub fn emit(self) { pub fn emit(self) {
yazi_shared::event::Event::Call( yazi_shared::event::Event::Call(Cmd::new("mgr:update_files").with_any("op", self).into())
Cmd::new("update_files").with_any("op", self).into(), .emit();
Layer::Mgr,
)
.emit();
} }
pub fn prepare(cwd: &Url) -> Id { pub fn prepare(cwd: &Url) -> Id {

View file

@ -3,11 +3,11 @@ macro_rules! emit {
(Quit($opt:expr)) => { (Quit($opt:expr)) => {
yazi_shared::event::Event::Quit($opt).emit(); yazi_shared::event::Event::Quit($opt).emit();
}; };
(Call($cmd:expr, $layer:expr)) => { (Call($cmd:expr)) => {
yazi_shared::event::Event::Call(yazi_shared::event::CmdCow::from($cmd), $layer).emit(); yazi_shared::event::Event::Call(yazi_shared::event::CmdCow::from($cmd)).emit();
}; };
(Seq($cmds:expr, $layer:expr)) => { (Seq($cmds:expr)) => {
yazi_shared::event::Event::Seq($cmds, $layer).emit(); yazi_shared::event::Event::Seq($cmds).emit();
}; };
($event:ident) => { ($event:ident) => {
yazi_shared::event::Event::$event.emit(); yazi_shared::event::Event::$event.emit();

View file

@ -40,21 +40,21 @@ impl Utils {
pub(super) fn app_emit(lua: &Lua) -> mlua::Result<Function> { pub(super) fn app_emit(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (name, args): (String, Table)| { lua.create_function(|_, (name, args): (String, Table)| {
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::App)); emit!(Call(Cmd { name, args: Sendable::table_to_args(args)?, layer: Layer::App }));
Ok(()) Ok(())
}) })
} }
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(|_, (name, args): (String, Table)| {
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::Mgr)); emit!(Call(Cmd { name, args: Sendable::table_to_args(args)?, layer: Layer::Mgr }));
Ok(()) Ok(())
}) })
} }
pub(super) fn input_emit(lua: &Lua) -> mlua::Result<Function> { pub(super) fn input_emit(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (name, args): (String, Table)| { lua.create_function(|_, (name, args): (String, Table)| {
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)? }, Layer::Input)); emit!(Call(Cmd { name, args: Sendable::table_to_args(args)?, layer: Layer::Input }));
Ok(()) Ok(())
}) })
} }

View file

@ -6,7 +6,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
use yazi_config::{keymap::{Chord, Key}, popup::{ConfirmCfg, InputCfg}}; use yazi_config::{keymap::{Chord, Key}, popup::{ConfirmCfg, InputCfg}};
use yazi_macro::emit; use yazi_macro::emit;
use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy}; use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy};
use yazi_shared::{Debounce, Layer, event::Cmd}; use yazi_shared::{Debounce, event::Cmd};
use super::Utils; use super::Utils;
use crate::{bindings::InputRx, elements::{Line, Pos, Text}}; use crate::{bindings::InputRx, elements::{Line, Pos, Text}};
@ -21,18 +21,16 @@ impl Utils {
let cand = cand?; let cand = cand?;
cands.push(Chord { cands.push(Chord {
on: Self::parse_keys(cand.raw_get("on")?)?, on: Self::parse_keys(cand.raw_get("on")?)?,
run: vec![Cmd::args("callback", &[i]).with_any("tx", tx.clone())], run: vec![Cmd::args("which:callback", &[i]).with_any("tx", tx.clone())],
desc: cand.raw_get("desc").ok(), desc: cand.raw_get("desc").ok(),
}); });
} }
drop(tx); drop(tx);
emit!(Call( emit!(Call(
Cmd::new("show") Cmd::new("which:show")
.with("layer", Layer::Which)
.with_any("candidates", cands) .with_any("candidates", cands)
.with_bool("silent", t.raw_get("silent").unwrap_or_default()), .with_bool("silent", t.raw_get("silent").unwrap_or_default())
Layer::Which
)); ));
Ok(rx.recv().await.map(|idx| idx + 1)) Ok(rx.recv().await.map(|idx| idx + 1))

View file

@ -1,7 +1,7 @@
use mlua::{AnyUserData, Function, IntoLuaMulti, Lua, Table, Value}; use mlua::{AnyUserData, Function, IntoLuaMulti, Lua, Table, Value};
use yazi_config::{PREVIEW, preview::PreviewWrap}; use yazi_config::{PREVIEW, preview::PreviewWrap};
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, errors::PeekError, event::Cmd}; use yazi_shared::{errors::PeekError, event::Cmd};
use super::Utils; use super::Utils;
use crate::{elements::{Area, Rect, Renderable, Text, WRAP, WRAP_NO}, external::Highlighter, file::FileRef}; use crate::{elements::{Area, Rect, Renderable, Text, WRAP, WRAP_NO}, external::Highlighter, file::FileRef};
@ -54,7 +54,7 @@ impl Utils {
wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO },
})]; })];
emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock)));
(Value::Nil, Value::Nil).into_lua_multi(&lua) (Value::Nil, Value::Nil).into_lua_multi(&lua)
}) })
} }
@ -64,7 +64,7 @@ impl Utils {
let mut lock = PreviewLock::try_from(t)?; let mut lock = PreviewLock::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("update_peeked").with_any("lock", lock), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock)));
Ok(()) Ok(())
}) })
} }

View file

@ -1,7 +1,7 @@
use mlua::{AnyUserData, Function, Lua, Table}; use mlua::{AnyUserData, Function, Lua, Table};
use yazi_config::THEME; use yazi_config::THEME;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
use super::Utils; use super::Utils;
use crate::{elements::Renderable, file::FileRef}; use crate::{elements::Renderable, file::FileRef};
@ -80,7 +80,7 @@ impl Utils {
}), }),
Renderable::Table(table), Renderable::Table(table),
]; ];
emit!(Call(Cmd::new("update_spotted").with_any("lock", lock), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_spotted").with_any("lock", lock)));
Ok(()) Ok(())
}) })
@ -91,7 +91,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("update_spotted").with_any("lock", lock), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_spotted").with_any("lock", lock)));
Ok(()) Ok(())
}) })
} }

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_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
use crate::options::{NotifyLevel, NotifyOpt, PluginOpt}; use crate::options::{NotifyLevel, NotifyOpt, PluginOpt};
@ -12,18 +12,18 @@ impl AppProxy {
#[inline] #[inline]
pub async fn stop() { pub async fn stop() {
let (tx, rx) = oneshot::channel::<()>(); let (tx, rx) = oneshot::channel::<()>();
emit!(Call(Cmd::new("stop").with_any("tx", tx), Layer::App)); emit!(Call(Cmd::new("app:stop").with_any("tx", tx)));
rx.await.ok(); rx.await.ok();
} }
#[inline] #[inline]
pub fn resume() { pub fn resume() {
emit!(Call(Cmd::new("resume"), Layer::App)); emit!(Call(Cmd::new("app:resume")));
} }
#[inline] #[inline]
pub fn notify(opt: NotifyOpt) { pub fn notify(opt: NotifyOpt) {
emit!(Call(Cmd::new("notify").with_any("option", opt), Layer::App)); emit!(Call(Cmd::new("app:notify").with_any("option", opt)));
} }
#[inline] #[inline]
@ -48,11 +48,11 @@ impl AppProxy {
#[inline] #[inline]
pub fn plugin(opt: PluginOpt) { pub fn plugin(opt: PluginOpt) {
emit!(Call(Cmd::new("plugin").with_any("opt", opt), Layer::App)); emit!(Call(Cmd::new("app:plugin").with_any("opt", opt)));
} }
#[inline] #[inline]
pub fn plugin_do(opt: PluginOpt) { pub fn plugin_do(opt: PluginOpt) {
emit!(Call(Cmd::new("plugin_do").with_any("opt", opt), Layer::App)); emit!(Call(Cmd::new("app:plugin_do").with_any("opt", opt)));
} }
} }

16
yazi-proxy/src/cmp.rs Normal file
View file

@ -0,0 +1,16 @@
use yazi_macro::emit;
use yazi_shared::event::Cmd;
pub struct CmpProxy;
impl CmpProxy {
#[inline]
pub fn close() {
emit!(Call(Cmd::new("cmp:close")));
}
#[inline]
pub fn trigger(word: &str, ticket: usize) {
emit!(Call(Cmd::args("cmp:trigger", &[word]).with("ticket", ticket)));
}
}

View file

@ -1,16 +0,0 @@
use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd};
pub struct CompletionProxy;
impl CompletionProxy {
#[inline]
pub fn close() {
emit!(Call(Cmd::new("close"), Layer::Completion));
}
#[inline]
pub fn trigger(word: &str, ticket: usize) {
emit!(Call(Cmd::args("trigger", &[word]).with("ticket", ticket), Layer::Completion));
}
}

View file

@ -1,7 +1,7 @@
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_config::popup::ConfirmCfg; use yazi_config::popup::ConfirmCfg;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
pub struct ConfirmProxy; pub struct ConfirmProxy;
@ -12,7 +12,7 @@ impl ConfirmProxy {
#[inline] #[inline]
pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver<bool> { pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver<bool> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Confirm)); emit!(Call(Cmd::new("confirm:show").with_any("tx", tx).with_any("cfg", cfg)));
rx rx
} }
} }

View file

@ -1,7 +1,7 @@
use tokio::sync::mpsc; use tokio::sync::mpsc;
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, errors::InputError, event::Cmd}; use yazi_shared::{errors::InputError, event::Cmd};
pub struct InputProxy; pub struct InputProxy;
@ -9,12 +9,12 @@ impl InputProxy {
#[inline] #[inline]
pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> { pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> {
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Input)); emit!(Call(Cmd::new("input:show").with_any("tx", tx).with_any("cfg", cfg)));
rx rx
} }
#[inline] #[inline]
pub fn complete(word: &str, ticket: usize) { pub fn complete(word: &str, ticket: usize) {
emit!(Call(Cmd::args("complete", &[word]).with("ticket", ticket), Layer::Input)); emit!(Call(Cmd::args("input:complete", &[word]).with("ticket", ticket)));
} }
} }

View file

@ -2,6 +2,6 @@ mod macros;
yazi_macro::mod_pub!(options); yazi_macro::mod_pub!(options);
yazi_macro::mod_flat!(app completion confirm input mgr pick semaphore tab tasks); yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tab tasks);
pub fn init() { crate::init_semaphore(); } pub fn init() { crate::init_semaphore(); }

View file

@ -1,5 +1,5 @@
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Id, Layer, event::Cmd, url::Url}; use yazi_shared::{Id, event::Cmd, url::Url};
use crate::options::OpenDoOpt; use crate::options::OpenDoOpt;
@ -8,55 +8,48 @@ pub struct MgrProxy;
impl MgrProxy { impl MgrProxy {
#[inline] #[inline]
pub fn spot(skip: Option<usize>) { pub fn spot(skip: Option<usize>) {
emit!(Call(Cmd::new("spot").with_opt("skip", skip), Layer::Mgr)); emit!(Call(Cmd::new("mgr:spot").with_opt("skip", skip)));
} }
#[inline] #[inline]
pub fn peek(force: bool) { pub fn peek(force: bool) {
emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Mgr)); emit!(Call(Cmd::new("mgr:peek").with_bool("force", force)));
} }
#[inline] #[inline]
pub fn hover(url: Option<Url>, tab: Id) { pub fn hover(url: Option<Url>, tab: Id) {
emit!(Call( emit!(Call(Cmd::args("mgr:hover", &url.map_or_else(Vec::new, |u| vec![u])).with("tab", tab)));
Cmd::args("hover", &url.map_or_else(Vec::new, |u| vec![u])).with("tab", tab),
Layer::Mgr
));
} }
#[inline] #[inline]
pub fn refresh() { pub fn refresh() {
emit!(Call(Cmd::new("refresh"), Layer::Mgr)); emit!(Call(Cmd::new("mgr:refresh")));
} }
#[inline] #[inline]
pub fn open_do(opt: OpenDoOpt) { pub fn open_do(opt: OpenDoOpt) {
emit!(Call(Cmd::new("open_do").with_any("option", opt), Layer::Mgr)); emit!(Call(Cmd::new("mgr:open_do").with_any("option", opt)));
} }
#[inline] #[inline]
pub fn remove_do(targets: Vec<Url>, permanently: bool) { pub fn remove_do(targets: Vec<Url>, permanently: bool) {
emit!(Call( emit!(Call(
Cmd::new("remove_do").with_bool("permanently", permanently).with_any("targets", targets), Cmd::new("mgr:remove_do").with_bool("permanently", permanently).with_any("targets", targets)
Layer::Mgr
)); ));
} }
#[inline] #[inline]
pub fn update_tasks(url: &Url) { pub fn update_tasks(url: &Url) {
emit!(Call(Cmd::new("update_tasks").with_any("urls", vec![url.clone()]), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_tasks").with_any("urls", vec![url.clone()])));
} }
#[inline] #[inline]
pub fn update_paged() { pub fn update_paged() {
emit!(Call(Cmd::new("update_paged"), Layer::Mgr)); emit!(Call(Cmd::new("mgr:update_paged")));
} }
#[inline] #[inline]
pub fn update_paged_by(page: usize, only_if: &Url) { pub fn update_paged_by(page: usize, only_if: &Url) {
emit!(Call( emit!(Call(Cmd::args("mgr:update_paged", &[page]).with_any("only-if", only_if.clone())));
Cmd::args("update_paged", &[page]).with_any("only-if", only_if.clone()),
Layer::Mgr
));
} }
} }

View file

@ -1,7 +1,7 @@
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_config::popup::PickCfg; use yazi_config::popup::PickCfg;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::event::Cmd;
pub struct PickProxy; pub struct PickProxy;
@ -9,7 +9,7 @@ impl PickProxy {
#[inline] #[inline]
pub async fn show(cfg: PickCfg) -> anyhow::Result<usize> { pub async fn show(cfg: PickCfg) -> anyhow::Result<usize> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Pick)); emit!(Call(Cmd::new("pick:show").with_any("tx", tx).with_any("cfg", cfg)));
rx.await? rx.await?
} }
} }

View file

@ -1,5 +1,5 @@
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd, url::Url}; use yazi_shared::{event::Cmd, url::Url};
use crate::options::SearchOpt; use crate::options::SearchOpt;
@ -8,25 +8,24 @@ pub struct TabProxy;
impl TabProxy { impl TabProxy {
#[inline] #[inline]
pub fn cd(target: &Url) { pub fn cd(target: &Url) {
emit!(Call(Cmd::args("cd", &[target]), Layer::Mgr)); emit!(Call(Cmd::args("mgr:cd", &[target])));
} }
#[inline] #[inline]
pub fn reveal(target: &Url) { pub fn reveal(target: &Url) {
emit!(Call(Cmd::args("reveal", &[target]), Layer::Mgr)); emit!(Call(Cmd::args("mgr:reveal", &[target])));
} }
#[inline] #[inline]
pub fn arrow(step: isize) { pub fn arrow(step: isize) {
emit!(Call(Cmd::args("arrow", &[step]), Layer::Mgr)); emit!(Call(Cmd::args("mgr:arrow", &[step])));
} }
#[inline] #[inline]
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
Cmd::args("search_do", &[opt.subject]).with("via", opt.via).with("args", opt.args_raw), Cmd::args("mgr:search_do", &[opt.subject]).with("via", opt.via).with("args", opt.args_raw)
Layer::Mgr
)); ));
} }
} }

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsString};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_config::open::Opener; use yazi_config::open::Opener;
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd, url::Url}; use yazi_shared::{event::Cmd, url::Url};
use crate::options::{OpenWithOpt, ProcessExecOpt}; use crate::options::{OpenWithOpt, ProcessExecOpt};
@ -12,24 +12,22 @@ pub struct TasksProxy;
impl TasksProxy { impl TasksProxy {
#[inline] #[inline]
pub fn open_with(opener: Cow<'static, Opener>, cwd: Url, targets: Vec<Url>) { pub fn open_with(opener: Cow<'static, Opener>, cwd: Url, targets: Vec<Url>) {
emit!(Call( emit!(Call(Cmd::new("tasks:open_with").with_any("option", OpenWithOpt {
Cmd::new("open_with").with_any("option", OpenWithOpt { opener, cwd, targets }), opener,
Layer::Tasks cwd,
)); targets
})));
} }
#[inline] #[inline]
pub async fn process_exec(opener: Cow<'static, Opener>, cwd: Url, args: Vec<OsString>) { pub async fn process_exec(opener: Cow<'static, Opener>, cwd: Url, args: Vec<OsString>) {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
emit!(Call( emit!(Call(Cmd::new("tasks:process_exec").with_any("option", ProcessExecOpt {
Cmd::new("process_exec").with_any("option", ProcessExecOpt { cwd,
cwd, opener,
opener, args,
args, done: Some(tx)
done: Some(tx) })));
}),
Layer::Tasks
));
rx.await.ok(); rx.await.ok();
} }
} }

View file

@ -1,31 +1,36 @@
use std::{any::Any, borrow::Cow, collections::HashMap, fmt::{self, Display}, mem, str::FromStr}; use std::{any::Any, borrow::Cow, collections::HashMap, fmt::{self, Display}, str::FromStr};
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use serde::{Deserialize, de}; use serde::{Deserialize, de};
use super::{Data, DataKey}; use super::{Data, DataKey};
use crate::url::Url; use crate::{Layer, url::Url};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Cmd { pub struct Cmd {
pub name: String, pub name: String,
pub args: HashMap<DataKey, Data>, pub args: HashMap<DataKey, Data>,
pub layer: Layer,
} }
impl Cmd { impl Cmd {
#[inline] pub fn new(s: &str) -> Self {
pub fn new(name: &str) -> Self { Self { name: name.to_owned(), ..Default::default() } } let (layer, name) = match s.split_once(':') {
Some((l, n)) => (Layer::from_str(l).unwrap_or_default(), n),
None => (Layer::default(), s),
};
Self { name: name.to_owned(), args: Default::default(), layer }
}
#[inline]
pub fn args(name: &str, args: &[impl ToString]) -> Self { pub fn args(name: &str, args: &[impl ToString]) -> Self {
Self { let mut me = Self::new(name);
name: name.to_owned(), me.args = args
args: args .iter()
.iter() .enumerate()
.enumerate() .map(|(i, s)| (DataKey::Integer(i as i64), Data::String(s.to_string())))
.map(|(i, s)| (DataKey::Integer(i as i64), Data::String(s.to_string()))) .collect();
.collect(), me
}
} }
// --- With // --- With
@ -170,15 +175,14 @@ impl FromStr for Cmd {
type Err = anyhow::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut args = crate::shell::split_unix(s)?; let args = crate::shell::split_unix(s)?;
if args.is_empty() || args[0].is_empty() { if args.is_empty() || args[0].is_empty() {
bail!("command name cannot be empty"); bail!("command name cannot be empty");
} }
Ok(Cmd { let mut me = Self::new(&args[0]);
name: mem::take(&mut args[0]), me.args = Cmd::parse_args(args.into_iter().skip(1), true)?;
args: Cmd::parse_args(args.into_iter().skip(1), true)?, Ok(me)
})
} }
} }

View file

@ -4,15 +4,15 @@ use crossterm::event::{KeyEvent, MouseEvent};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use super::CmdCow; use super::CmdCow;
use crate::{Layer, RoCell}; use crate::RoCell;
static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new(); static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new();
static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new(); static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
Call(CmdCow, Layer), Call(CmdCow),
Seq(Vec<CmdCow>, Layer), Seq(Vec<CmdCow>),
Render, Render,
Key(KeyEvent), Key(KeyEvent),
Mouse(MouseEvent), Mouse(MouseEvent),

View file

@ -14,7 +14,7 @@ pub enum Layer {
Input, Input,
Confirm, Confirm,
Help, Help,
Completion, Cmp,
Which, Which,
} }
@ -29,7 +29,7 @@ impl Display for Layer {
Self::Input => "input", Self::Input => "input",
Self::Confirm => "confirm", Self::Confirm => "confirm",
Self::Help => "help", Self::Help => "help",
Self::Completion => "completion", Self::Cmp => "cmp",
Self::Which => "which", Self::Which => "which",
}) })
} }