feat: key-* DDS events to allow canceling user key commands (#3005)

This commit is contained in:
三咲雅 misaki masa 2025-07-20 14:11:57 +08:00 committed by GitHub
parent c28d2e8180
commit 74bd98031e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
148 changed files with 893 additions and 465 deletions

29
Cargo.lock generated
View file

@ -352,9 +352,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.29"
version = "1.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
dependencies = [
"jobserver",
"libc",
@ -460,9 +460,9 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "clipboard-win"
version = "5.4.0"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
"error-code",
]
@ -1193,9 +1193,9 @@ dependencies = [
[[package]]
name = "instability"
version = "0.3.7"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
dependencies = [
"darling",
"indoc",
@ -1335,9 +1335,9 @@ dependencies = [
[[package]]
name = "libredox"
version = "0.1.4"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
dependencies = [
"bitflags 2.9.1",
"libc",
@ -2344,9 +2344,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.140"
version = "1.0.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
dependencies = [
"itoa",
"memchr",
@ -3489,13 +3489,17 @@ version = "25.6.11"
dependencies = [
"anyhow",
"crossterm 0.29.0",
"foldhash",
"futures",
"indexmap",
"libc",
"mlua",
"paste",
"scopeguard",
"tokio",
"tokio-stream",
"tracing",
"yazi-binding",
"yazi-boot",
"yazi-config",
"yazi-core",
@ -3696,9 +3700,7 @@ dependencies = [
"better-panic",
"crossterm 0.29.0",
"fdlimit",
"foldhash",
"futures",
"indexmap",
"libc",
"mlua",
"paste",
@ -3829,8 +3831,6 @@ name = "yazi-proxy"
version = "25.6.11"
dependencies = [
"anyhow",
"mlua",
"serde",
"tokio",
"yazi-config",
"yazi-macro",
@ -3868,7 +3868,6 @@ name = "yazi-shared"
version = "25.6.11"
dependencies = [
"anyhow",
"bitflags 2.9.1",
"crossterm 0.29.0",
"foldhash",
"futures",

View file

@ -43,7 +43,7 @@ ratatui = { version = "0.29.0", features = [ "unstable-rendered-line
regex = "1.11.1"
scopeguard = "1.2.0"
serde = { version = "1.0.219", features = [ "derive" ] }
serde_json = "1.0.140"
serde_json = "1.0.141"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
tokio = { version = "1.46.1", features = [ "full" ] }
tokio-stream = "0.1.17"

View file

@ -1 +1 @@
{"version":"0.2","language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg"]}
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash"],"version":"0.2","flagWords":[],"language":"en"}

View file

@ -8,7 +8,12 @@ description = "Yazi actor model"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" }
yazi-config = { path = "../yazi-config", version = "25.6.11" }
yazi-core = { path = "../yazi-core", version = "25.6.11" }
@ -25,7 +30,10 @@ yazi-widgets = { path = "../yazi-widgets", version = "25.6.11" }
# External dependencies
anyhow = { workspace = true }
crossterm = { workspace = true }
foldhash = { workspace = true }
futures = { workspace = true }
indexmap = { workspace = true }
mlua = { workspace = true }
paste = { workspace = true }
scopeguard = { workspace = true }
tokio = { workspace = true }

View file

@ -9,4 +9,6 @@ pub trait Actor {
const NAME: &str;
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data>;
fn hook(_cx: &Ctx, _opt: &Self::Options) -> Option<&'static str> { None }
}

View file

@ -3,11 +3,13 @@ use std::ops::{Deref, DerefMut};
use anyhow::{Result, anyhow};
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks};
use yazi_fs::File;
use yazi_shared::{event::Cmd, url::Url};
use yazi_shared::{Source, event::Cmd, url::Url};
pub struct Ctx<'a> {
pub core: &'a mut Core,
pub tab: usize,
pub core: &'a mut Core,
pub tab: usize,
pub level: usize,
pub source: Source,
}
impl Deref for Ctx<'_> {
@ -36,15 +38,23 @@ impl<'a> Ctx<'a> {
core.mgr.tabs.cursor
};
Ok(Self { core, tab })
Ok(Self { core, tab, level: 0, source: cmd.source })
}
#[inline]
pub fn renew<'b>(cx: &'a mut Ctx<'b>) -> Ctx<'a> {
let tab = cx.core.mgr.tabs.cursor;
Self { core: cx.core, tab, level: cx.level, source: cx.source }
}
#[inline]
pub fn active(core: &'a mut Core) -> Self {
let tab = core.mgr.tabs.cursor;
Self { core, tab }
Self { core, tab, level: 0, source: Source::Unknown }
}
}
impl<'a> Ctx<'a> {
#[inline]
pub fn tabs(&self) -> &Tabs { &self.mgr.tabs }
@ -80,4 +90,15 @@ impl<'a> Ctx<'a> {
#[inline]
pub fn tasks(&self) -> &Tasks { &self.tasks }
#[inline]
pub fn source(&self) -> Source {
use Source::*;
match self.source {
Key if self.level != 1 => Ind,
Emit if self.level != 1 => EmitInd,
Relay if self.level != 1 => RelayInd,
s => s,
}
}
}

View file

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

View file

@ -0,0 +1,48 @@
use std::marker::PhantomData;
use anyhow::Result;
use mlua::{IntoLua, Value};
use tracing::error;
use yazi_binding::runtime_mut;
use yazi_dds::{LOCAL, body::Body};
use yazi_macro::succ;
use yazi_plugin::LUA;
use yazi_shared::event::Data;
use crate::{Actor, Ctx, lives::Lives};
pub struct Preflight<'a> {
_lifetime: PhantomData<&'a ()>,
}
impl<'a> Actor for Preflight<'a> {
type Options = (&'static str, Body<'a>);
const NAME: &'static str = "preflight";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let kind = opt.0;
let Some(handlers) = LOCAL.read().get(kind).filter(|&m| !m.is_empty()).cloned() else {
succ!(false)
};
succ!(Lives::scope(cx.core, || {
let body = opt.1.into_lua(&LUA)?;
for (id, cb) in handlers {
runtime_mut!(LUA)?.push(&id);
let result = cb.call::<Value>(body.clone());
runtime_mut!(LUA)?.pop();
match result {
Ok(Value::Boolean(true)) => return Ok(true),
Ok(Value::Nil | Value::Boolean(false)) => {}
Ok(v) => {
error!("Unexpected return type from `{kind}` event handler in `{id}` plugin: {v:?}")
}
Err(e) => error!("Failed to run `{kind}` event handler in `{id}` plugin: {e}"),
}
}
Ok(false)
})?)
}
}

View file

@ -2,6 +2,6 @@
extern crate self as yazi_actor;
yazi_macro::mod_pub!(cmp confirm help input mgr pick spot tasks which);
yazi_macro::mod_pub!(cmp confirm core help input lives mgr pick spot tasks which);
yazi_macro::mod_flat!(actor context);

View file

@ -13,13 +13,10 @@ static TO_DESTROY: RoCell<RefCell<Vec<AnyUserData>>> = RoCell::new_const(RefCell
pub(super) static FILE_CACHE: RoCell<RefCell<HashMap<PtrCell<yazi_fs::File>, AnyUserData>>> =
RoCell::new();
pub(crate) struct Lives;
pub struct Lives;
impl Lives {
pub(crate) fn scope<T>(
core: &yazi_core::Core,
f: impl FnOnce() -> mlua::Result<T>,
) -> mlua::Result<T> {
pub fn scope<T>(core: &yazi_core::Core, f: impl FnOnce() -> mlua::Result<T>) -> mlua::Result<T> {
FILE_CACHE.init(Default::default());
defer! { FILE_CACHE.drop(); }

View file

@ -59,6 +59,10 @@ impl Actor for Quit {
});
succ!();
}
fn hook(cx: &Ctx, _opt: &Self::Options) -> Option<&'static str> {
Some("key-quit").filter(|_| cx.source().is_key())
}
}
impl Quit {

View file

@ -6,7 +6,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_fs::{FilesOp, cha::Cha};
use yazi_macro::{act, succ};
use yazi_parser::mgr::{SearchOpt, SearchOptVia};
use yazi_parser::{VoidOpt, mgr::{SearchOpt, SearchOptVia}};
use yazi_plugin::external;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_shared::event::Data;
@ -97,7 +97,7 @@ impl Actor for SearchDo {
pub struct SearchStop;
impl Actor for SearchStop {
type Options = ();
type Options = VoidOpt;
const NAME: &str = "search_stop";

View file

@ -1,26 +1,24 @@
use std::str::FromStr;
use anyhow::Result;
use yazi_fs::SortBy;
use yazi_macro::{act, succ};
use yazi_shared::event::{CmdCow, Data};
use yazi_parser::mgr::SortOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
pub struct Sort;
impl Actor for Sort {
type Options = CmdCow;
type Options = SortOpt;
const NAME: &str = "sort";
fn act(cx: &mut Ctx, c: Self::Options) -> Result<Data> {
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mut new = cx.tab().pref.clone();
new.sort_by = c.first_str().and_then(|s| SortBy::from_str(s).ok()).unwrap_or(new.sort_by);
new.sort_reverse = c.maybe_bool("reverse").unwrap_or(new.sort_reverse);
new.sort_dir_first = c.maybe_bool("dir-first").unwrap_or(new.sort_dir_first);
new.sort_sensitive = c.maybe_bool("sensitive").unwrap_or(new.sort_sensitive);
new.sort_translit = c.maybe_bool("translit").unwrap_or(new.sort_translit);
new.sort_by = opt.by.unwrap_or(new.sort_by);
new.sort_reverse = opt.reverse.unwrap_or(new.sort_reverse);
new.sort_dir_first = opt.dir_first.unwrap_or(new.sort_dir_first);
new.sort_sensitive = opt.sensitive.unwrap_or(new.sort_sensitive);
new.sort_translit = opt.translit.unwrap_or(new.sort_translit);
if new == cx.tab().pref {
succ!();

View file

@ -27,7 +27,7 @@ impl Actor for TabClose {
tabs.set_idx(usize::min(tabs.cursor + 1, tabs.len() - 1));
}
let cx = &mut Ctx::active(cx.core);
let cx = &mut Ctx::renew(cx);
act!(mgr:refresh, cx)?;
act!(mgr:peek, cx, true)?;

View file

@ -41,7 +41,7 @@ impl Actor for TabCreate {
tabs.items.insert(tabs.cursor + 1, tab);
tabs.set_idx(tabs.cursor + 1);
let cx = &mut Ctx::active(cx.core);
let cx = &mut Ctx::renew(cx);
if cd {
act!(mgr:cd, cx, (url, CdSource::Tab))?;
} else {

View file

@ -23,7 +23,7 @@ impl Actor for TabSwap {
tabs.items.swap(tabs.cursor, new);
tabs.set_idx(new);
let cx = &mut Ctx::active(cx.core);
let cx = &mut Ctx::renew(cx);
act!(mgr:refresh, cx)?;
act!(mgr:peek, cx, true)?;

View file

@ -25,7 +25,7 @@ impl Actor for TabSwitch {
}
tabs.set_idx(idx);
let cx = &mut Ctx::active(cx.core);
let cx = &mut Ctx::renew(cx);
act!(mgr:refresh, cx)?;
act!(mgr:peek, cx, true)?;

View file

@ -7,7 +7,7 @@ use yazi_shared::env_exists;
use crate::{Emulator, SHOWN, TMUX, drivers};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Adapter {
Kgp,
KgpOld,

View file

@ -3,7 +3,7 @@ use yazi_shared::env_exists;
use crate::Mux;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Brand {
Kitty,
Konsole,

View file

@ -1,6 +1,6 @@
use crate::Adapter;
#[derive(Debug, Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct Unknown {
pub kgp: bool,
pub sixel: bool,

View file

@ -8,6 +8,10 @@ description = "Yazi Lua bindings"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-adapter = { path = "../yazi-adapter", version = "25.6.11" }
yazi-config = { path = "../yazi-config", version = "25.6.11" }

View file

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

View file

@ -24,7 +24,7 @@ macro_rules! deprecate {
None => "`init.lua` config",
};
yazi_macro::emit!(Call(
yazi_shared::event::Cmd::new("app:deprecate").with("content", format!($tt, id))
yazi_macro::relay!(app:deprecate).with("content", format!($tt, id))
));
}
}};

View file

@ -1,7 +0,0 @@
use mlua::{IntoLua, Lua, Value};
pub struct Source(pub yazi_shared::Source);
impl IntoLua for Source {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { self.0.bits().into_lua(lua) }
}

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock};
use anyhow::Result;
use regex::Regex;
use serde::Deserialize;
use yazi_shared::{Layer, event::Cmd};
use yazi_shared::{Layer, Source, event::Cmd};
use super::Key;
@ -60,6 +60,7 @@ impl Chord {
impl Chord {
pub(super) fn reshape(mut self, layer: Layer) -> Result<Self> {
for cmd in &mut self.run {
cmd.source = Source::Key;
if cmd.layer == Default::default() {
cmd.layer = layer;
}

View file

@ -3,7 +3,7 @@ use std::{fmt::{Display, Write}, str::FromStr};
use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Key {
pub code: KeyCode,
pub shift: bool,

View file

@ -1,6 +1,6 @@
use ratatui::layout::Rect;
#[derive(Clone, Copy, Default, PartialEq, Eq)]
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub struct Layout {
pub current: Rect,
pub preview: Rect,

View file

@ -4,7 +4,7 @@ use crossterm::event::MouseEventKind;
use serde::{Deserialize, Serialize};
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(try_from = "Vec<String>", into = "Vec<String>")]
pub struct MouseEvents: u8 {
const CLICK = 0b00001;

View file

@ -1,7 +1,7 @@
use anyhow::bail;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(try_from = "[u16; 3]")]
pub struct MgrRatio {
pub parent: u16,

View file

@ -1,7 +1,7 @@
use anyhow::{Result, bail};
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct OpenerRule {
pub run: String,
#[serde(default)]

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum PreviewWrap {
No,

View file

@ -1,6 +1,6 @@
use serde::Deserialize;
#[derive(Default, Clone, Copy, Debug, Deserialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Priority {
Low = 0,

View file

@ -1,72 +0,0 @@
use anyhow::bail;
use crate::body::Body;
macro_rules! unsupported {
[ $( $layer:ident : $name:ident $(<$lt:lifetime>)? $(,)? )+ ] => {
$(
paste::paste! {
unsupported!(yazi_parser::$layer::[<$name:camel Opt>] $(<$lt>)?);
}
)+
};
[ $( $path:path $(,)? )+ ] => {
$(
impl TryFrom<&$path> for Body<'static> {
type Error = anyhow::Error;
fn try_from(_: &$path) -> Result<Self, Self::Error> {
bail!("unsupported");
}
}
)+
};
}
unsupported!(
mgr:cd
mgr:close
mgr:copy
mgr:create
mgr:escape
mgr:filter
mgr:find
mgr:find_arrow
mgr:find_do
mgr:forward
mgr:hardlink
mgr:hidden
mgr:linemode
mgr:link
mgr:open
mgr:open_do
mgr:paste
mgr:peek
mgr:remove
mgr:rename
mgr:reveal
mgr:search
mgr:seek
mgr:shell
mgr:spot
mgr:tab_close
mgr:tab_create
mgr:tab_switch
mgr:toggle
mgr:toggle_all
mgr:update_files
mgr:update_mimes
mgr:update_paged
mgr:update_peeked
mgr:update_spotted
mgr:update_tasks
mgr:update_yanked<'_>
mgr:visual_mode
mgr:yank
);
unsupported!(
yazi_parser::ArrowOpt
yazi_parser::VoidOpt
yazi_shared::event::CmdCow
);

View file

@ -1,36 +0,0 @@
use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize};
use yazi_parser::mgr::QuitOpt;
use yazi_shared::Source;
use crate::{body::Body, local_or_err};
#[derive(Debug, Serialize, Deserialize)]
pub struct BeforeQuitBody {
pub opts: QuitOpt,
pub source: Source,
}
impl IntoLua for BeforeQuitBody {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([
("opts", self.opts.into_lua(lua)?),
("source", yazi_binding::Source(self.source).into_lua(lua)?),
])?
.into_lua(lua)
}
}
impl TryFrom<&QuitOpt> for Body<'static> {
type Error = anyhow::Error;
fn try_from(value: &QuitOpt) -> Result<Self, Self::Error> {
local_or_err!("before-quit");
Ok(Body::BeforeQuit(BeforeQuitBody {
opts: value.clone(),
source: Source::default(), // FIXME
}))
}
}

View file

@ -1,13 +1,11 @@
use anyhow::{Result, bail};
use mlua::{ExternalResult, IntoLua, Lua, Value};
use serde::Serialize;
use yazi_shared::Id;
use super::{BeforeQuitBody, BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank};
use super::{BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank};
use crate::Payload;
#[derive(Debug, Serialize)]
#[serde(untagged)]
#[derive(Debug)]
pub enum Body<'a> {
Hi(BodyHi<'a>),
Hey(BodyHey),
@ -23,8 +21,74 @@ pub enum Body<'a> {
Trash(BodyTrash<'a>),
Delete(BodyDelete<'a>),
Mount(BodyMount),
BeforeQuit(BeforeQuitBody),
Custom(BodyCustom),
// Manager key events
KeyArrow(&'a yazi_parser::ArrowOpt),
KeyBack(&'a yazi_parser::VoidOpt),
KeyBulkRename(&'a yazi_parser::VoidOpt),
KeyCd(&'a yazi_parser::mgr::CdOpt),
KeyClose(&'a yazi_parser::mgr::CloseOpt),
KeyCopy(&'a yazi_parser::mgr::CopyOpt),
KeyCreate(&'a yazi_parser::mgr::CreateOpt),
KeyEnter(&'a yazi_parser::VoidOpt),
KeyEscape(&'a yazi_parser::mgr::EscapeOpt),
KeyEscapeFilter(&'a yazi_parser::VoidOpt),
KeyEscapeFind(&'a yazi_parser::VoidOpt),
KeyEscapeSearch(&'a yazi_parser::VoidOpt),
KeyEscapeSelect(&'a yazi_parser::VoidOpt),
KeyEscapeVisual(&'a yazi_parser::VoidOpt),
KeyFilter(&'a yazi_parser::mgr::FilterOpt),
KeyFilterDo(&'a yazi_parser::mgr::FilterOpt),
KeyFind(&'a yazi_parser::mgr::FindOpt),
KeyFindArrow(&'a yazi_parser::mgr::FindArrowOpt),
KeyFindDo(&'a yazi_parser::mgr::FindDoOpt),
KeyFollow(&'a yazi_parser::VoidOpt),
KeyForward(&'a yazi_parser::VoidOpt),
KeyHardlink(&'a yazi_parser::mgr::HardlinkOpt),
KeyHidden(&'a yazi_parser::mgr::HiddenOpt),
KeyHover(&'a yazi_parser::mgr::HoverOpt),
KeyHoverDo(&'a yazi_parser::mgr::HoverDoOpt),
KeyLeave(&'a yazi_parser::VoidOpt),
KeyLinemode(&'a yazi_parser::mgr::LinemodeOpt),
KeyLink(&'a yazi_parser::mgr::LinkOpt),
KeyOpen(&'a yazi_parser::mgr::OpenOpt),
KeyOpenDo(&'a yazi_parser::mgr::OpenDoOpt),
KeyPaste(&'a yazi_parser::mgr::PasteOpt),
KeyPeek(&'a yazi_parser::mgr::PeekOpt),
KeyQuit(&'a yazi_parser::mgr::QuitOpt),
KeyRefresh(&'a yazi_parser::VoidOpt),
KeyRemove(&'a yazi_parser::mgr::RemoveOpt),
KeyRemoveDo(&'a yazi_parser::mgr::RemoveOpt),
KeyRename(&'a yazi_parser::mgr::RenameOpt),
KeyReveal(&'a yazi_parser::mgr::RevealOpt),
KeySearch(&'a yazi_parser::mgr::SearchOpt),
KeySearchDo(&'a yazi_parser::mgr::SearchOpt),
KeySearchStop(&'a yazi_parser::VoidOpt),
KeySeek(&'a yazi_parser::mgr::SeekOpt),
KeyShell(&'a yazi_parser::mgr::ShellOpt),
KeySort(&'a yazi_parser::mgr::SortOpt),
KeySpot(&'a yazi_parser::mgr::SpotOpt),
KeySuspend(&'a yazi_parser::VoidOpt),
KeyTabClose(&'a yazi_parser::mgr::TabCloseOpt),
KeyTabCreate(&'a yazi_parser::mgr::TabCreateOpt),
KeyTabSwap(&'a yazi_parser::ArrowOpt),
KeyTabSwitch(&'a yazi_parser::mgr::TabSwitchOpt),
KeyToggle(&'a yazi_parser::mgr::ToggleOpt),
KeyToggleAll(&'a yazi_parser::mgr::ToggleAllOpt),
KeyUnyank(&'a yazi_parser::VoidOpt),
KeyUpdateFiles(&'a yazi_parser::mgr::UpdateFilesOpt),
KeyUpdateMimes(&'a yazi_parser::mgr::UpdateMimesOpt),
KeyUpdatePaged(&'a yazi_parser::mgr::UpdatePagedOpt),
KeyUpdatePeeked(&'a yazi_parser::mgr::UpdatePeekedOpt),
KeyUpdateSpotted(&'a yazi_parser::mgr::UpdateSpottedOpt),
KeyUpdateTasks(&'a yazi_parser::mgr::UpdateTasksOpt),
KeyUpdateYanked(&'a yazi_parser::mgr::UpdateYankedOpt<'a>),
KeyVisualMode(&'a yazi_parser::mgr::VisualModeOpt),
KeyWatch(&'a yazi_parser::VoidOpt),
KeyYank(&'a yazi_parser::mgr::YankOpt),
// Void
Void(&'a yazi_parser::VoidOpt),
}
impl Body<'static> {
@ -44,7 +108,6 @@ impl Body<'static> {
"trash" => Self::Trash(serde_json::from_str(body)?),
"delete" => Self::Delete(serde_json::from_str(body)?),
"mount" => Self::Mount(serde_json::from_str(body)?),
"before-quit" => Self::BeforeQuit(serde_json::from_str(body)?),
_ => BodyCustom::from_str(kind, body)?,
})
}
@ -71,8 +134,13 @@ impl Body<'static> {
| "trash"
| "delete"
| "mount"
| "before-quit"
) {
) || kind.starts_with("emit-")
|| kind.starts_with("emit-ind-")
|| kind.starts_with("ind-")
|| kind.starts_with("key-")
|| kind.starts_with("relay-")
|| kind.starts_with("relay-ind-")
{
bail!("Cannot construct system event");
}
@ -106,8 +174,74 @@ impl<'a> Body<'a> {
Self::Trash(_) => "trash",
Self::Delete(_) => "delete",
Self::Mount(_) => "mount",
Self::BeforeQuit(_) => "before-quit",
Self::Custom(b) => b.kind.as_str(),
// Manager key events
Self::KeyArrow(_) => "key-arrow",
Self::KeyBack(_) => "key-back",
Self::KeyBulkRename(_) => "key-bulk-rename",
Self::KeyCd(_) => "key-cd",
Self::KeyClose(_) => "key-close",
Self::KeyCopy(_) => "key-copy",
Self::KeyCreate(_) => "key-create",
Self::KeyEnter(_) => "key-enter",
Self::KeyEscape(_) => "key-escape",
Self::KeyEscapeFilter(_) => "key-escape-filter",
Self::KeyEscapeFind(_) => "key-escape-find",
Self::KeyEscapeSearch(_) => "key-escape-search",
Self::KeyEscapeSelect(_) => "key-escape-select",
Self::KeyEscapeVisual(_) => "key-escape-visual",
Self::KeyFilter(_) => "key-filter",
Self::KeyFilterDo(_) => "key-filter-do",
Self::KeyFind(_) => "key-find",
Self::KeyFindArrow(_) => "key-find-arrow",
Self::KeyFindDo(_) => "key-find-do",
Self::KeyFollow(_) => "key-follow",
Self::KeyForward(_) => "key-forward",
Self::KeyHardlink(_) => "key-hardlink",
Self::KeyHidden(_) => "key-hidden",
Self::KeyHover(_) => "key-hover",
Self::KeyHoverDo(_) => "key-hover-do",
Self::KeyLeave(_) => "key-leave",
Self::KeyLinemode(_) => "key-linemode",
Self::KeyLink(_) => "key-link",
Self::KeyOpen(_) => "key-open",
Self::KeyOpenDo(_) => "key-open-do",
Self::KeyPaste(_) => "key-paste",
Self::KeyPeek(_) => "key-peek",
Self::KeyQuit(_) => "key-quit",
Self::KeyRefresh(_) => "key-refresh",
Self::KeyRemove(_) => "key-remove",
Self::KeyRemoveDo(_) => "key-remove-do",
Self::KeyRename(_) => "key-rename",
Self::KeyReveal(_) => "key-reveal",
Self::KeySearch(_) => "key-search",
Self::KeySearchDo(_) => "key-search-do",
Self::KeySearchStop(_) => "key-search-stop",
Self::KeySeek(_) => "key-seek",
Self::KeyShell(_) => "key-shell",
Self::KeySort(_) => "key-sort",
Self::KeySpot(_) => "key-spot",
Self::KeySuspend(_) => "key-suspend",
Self::KeyTabClose(_) => "key-tab-close",
Self::KeyTabCreate(_) => "key-tab-create",
Self::KeyTabSwap(_) => "key-tab-swap",
Self::KeyTabSwitch(_) => "key-tab-switch",
Self::KeyToggle(_) => "key-toggle",
Self::KeyToggleAll(_) => "key-toggle-all",
Self::KeyUnyank(_) => "key-unyank",
Self::KeyUpdateFiles(_) => "key-update-files",
Self::KeyUpdateMimes(_) => "key-update-mimes",
Self::KeyUpdatePaged(_) => "key-update-paged",
Self::KeyUpdatePeeked(_) => "key-update-peeked",
Self::KeyUpdateSpotted(_) => "key-update-spotted",
Self::KeyUpdateTasks(_) => "key-update-tasks",
Self::KeyUpdateYanked(_) => "key-update-yanked",
Self::KeyVisualMode(_) => "key-visual-mode",
Self::KeyWatch(_) => "key-watch",
Self::KeyYank(_) => "key-yank",
// Void
Self::Void(_) => "void",
}
}
@ -120,7 +254,7 @@ impl<'a> Body<'a> {
pub fn with_sender(self, sender: Id) -> Payload<'a> { Payload::new(self).with_sender(sender) }
}
impl IntoLua for Body<'static> {
impl<'a> IntoLua for Body<'a> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
match self {
Self::Hi(b) => b.into_lua(lua),
@ -137,8 +271,74 @@ impl IntoLua for Body<'static> {
Self::Trash(b) => b.into_lua(lua),
Self::Delete(b) => b.into_lua(lua),
Self::Mount(b) => b.into_lua(lua),
Self::BeforeQuit(b) => b.into_lua(lua),
Self::Custom(b) => b.into_lua(lua),
// Manager key events
Self::KeyArrow(b) => b.into_lua(lua),
Self::KeyBack(b) => b.into_lua(lua),
Self::KeyBulkRename(b) => b.into_lua(lua),
Self::KeyCd(b) => b.into_lua(lua),
Self::KeyClose(b) => b.into_lua(lua),
Self::KeyCopy(b) => b.into_lua(lua),
Self::KeyCreate(b) => b.into_lua(lua),
Self::KeyEnter(b) => b.into_lua(lua),
Self::KeyEscape(b) => b.into_lua(lua),
Self::KeyEscapeFilter(b) => b.into_lua(lua),
Self::KeyEscapeFind(b) => b.into_lua(lua),
Self::KeyEscapeSearch(b) => b.into_lua(lua),
Self::KeyEscapeSelect(b) => b.into_lua(lua),
Self::KeyEscapeVisual(b) => b.into_lua(lua),
Self::KeyFilter(b) => b.into_lua(lua),
Self::KeyFilterDo(b) => b.into_lua(lua),
Self::KeyFind(b) => b.into_lua(lua),
Self::KeyFindArrow(b) => b.into_lua(lua),
Self::KeyFindDo(b) => b.into_lua(lua),
Self::KeyFollow(b) => b.into_lua(lua),
Self::KeyForward(b) => b.into_lua(lua),
Self::KeyHardlink(b) => b.into_lua(lua),
Self::KeyHidden(b) => b.into_lua(lua),
Self::KeyHover(b) => b.into_lua(lua),
Self::KeyHoverDo(b) => b.into_lua(lua),
Self::KeyLeave(b) => b.into_lua(lua),
Self::KeyLinemode(b) => b.into_lua(lua),
Self::KeyLink(b) => b.into_lua(lua),
Self::KeyOpen(b) => b.into_lua(lua),
Self::KeyOpenDo(b) => b.into_lua(lua),
Self::KeyPaste(b) => b.into_lua(lua),
Self::KeyPeek(b) => b.into_lua(lua),
Self::KeyQuit(b) => b.into_lua(lua),
Self::KeyRefresh(b) => b.into_lua(lua),
Self::KeyRemove(b) => b.into_lua(lua),
Self::KeyRemoveDo(b) => b.into_lua(lua),
Self::KeyRename(b) => b.into_lua(lua),
Self::KeyReveal(b) => b.into_lua(lua),
Self::KeySearch(b) => b.into_lua(lua),
Self::KeySearchDo(b) => b.into_lua(lua),
Self::KeySearchStop(b) => b.into_lua(lua),
Self::KeySeek(b) => b.into_lua(lua),
Self::KeyShell(b) => b.into_lua(lua),
Self::KeySort(b) => b.into_lua(lua),
Self::KeySpot(b) => b.into_lua(lua),
Self::KeySuspend(b) => b.into_lua(lua),
Self::KeyTabClose(b) => b.into_lua(lua),
Self::KeyTabCreate(b) => b.into_lua(lua),
Self::KeyTabSwap(b) => b.into_lua(lua),
Self::KeyTabSwitch(b) => b.into_lua(lua),
Self::KeyToggle(b) => b.into_lua(lua),
Self::KeyToggleAll(b) => b.into_lua(lua),
Self::KeyUnyank(b) => b.into_lua(lua),
Self::KeyUpdateFiles(b) => b.into_lua(lua),
Self::KeyUpdateMimes(b) => b.into_lua(lua),
Self::KeyUpdatePaged(b) => b.into_lua(lua),
Self::KeyUpdatePeeked(b) => b.into_lua(lua),
Self::KeyUpdateSpotted(b) => b.into_lua(lua),
Self::KeyUpdateTasks(b) => b.into_lua(lua),
Self::KeyUpdateYanked(b) => b.into_lua(lua),
Self::KeyVisualMode(b) => b.into_lua(lua),
Self::KeyWatch(b) => b.into_lua(lua),
Self::KeyYank(b) => b.into_lua(lua),
// Void
Self::Void(b) => b.into_lua(lua),
}
}
}

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, collections::{HashMap, hash_map}};
use std::{borrow::Cow, collections::HashMap};
use mlua::{AnyUserData, IntoLua, IntoLuaMulti, Lua, MetaMethod, UserData, UserDataRefMut, Value};
use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize};
use yazi_shared::url::Url;
@ -34,32 +34,15 @@ impl<'a> From<BodyBulk<'a>> for Body<'a> {
fn from(value: BodyBulk<'a>) -> Self { Self::Bulk(value) }
}
impl IntoLua for BodyBulk<'static> {
impl IntoLua for BodyBulk<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
BodyBulkIter { inner: self.changes.into_iter() }.into_lua(lua)
}
}
// --- Iterator
// TODO: use `yazi_binding::Iter` instead
pub struct BodyBulkIter {
pub inner: hash_map::IntoIter<Cow<'static, Url>, Cow<'static, Url>>,
}
impl UserData for BodyBulkIter {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.inner.len()));
methods.add_meta_function(MetaMethod::Pairs, |lua, me: AnyUserData| {
let iter = lua.create_function(|lua, mut me: UserDataRefMut<Self>| {
if let Some((Cow::Owned(from), Cow::Owned(to))) = me.inner.next() {
(yazi_binding::Url::new(from), yazi_binding::Url::new(to)).into_lua_multi(lua)
} else {
().into_lua_multi(lua)
}
})?;
Ok((iter, me))
});
lua
.create_table_from(
self
.changes
.into_iter()
.map(|(from, to)| (yazi_binding::Url::new(from), yazi_binding::Url::new(to))),
)?
.into_lua(lua)
}
}

View file

@ -30,7 +30,7 @@ impl<'a> From<BodyCd<'a>> for Body<'a> {
fn from(value: BodyCd<'a>) -> Self { Self::Cd(value) }
}
impl IntoLua for BodyCd<'static> {
impl IntoLua for BodyCd<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([

View file

@ -23,7 +23,7 @@ impl<'a> From<BodyDelete<'a>> for Body<'a> {
fn from(value: BodyDelete<'a>) -> Self { Self::Delete(value) }
}
impl IntoLua for BodyDelete<'static> {
impl IntoLua for BodyDelete<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let urls =
lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?;

View file

@ -26,7 +26,7 @@ impl<'a> From<BodyHover<'a>> for Body<'a> {
fn from(value: BodyHover<'a>) -> Self { Self::Hover(value) }
}
impl IntoLua for BodyHover<'static> {
impl IntoLua for BodyHover<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([

View file

@ -30,7 +30,7 @@ impl<'a> From<BodyLoad<'a>> for Body<'a> {
fn from(value: BodyLoad<'a>) -> Self { Self::Load(value) }
}
impl IntoLua for BodyLoad<'static> {
impl IntoLua for BodyLoad<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([

View file

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

View file

@ -27,7 +27,7 @@ impl<'a> From<BodyMove<'a>> for Body<'a> {
fn from(value: BodyMove<'a>) -> Self { Self::Move(value) }
}
impl IntoLua for BodyMove<'static> {
impl IntoLua for BodyMove<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua.create_table_from([("items", self.items.into_owned())])?.into_lua(lua)
}

View file

@ -29,7 +29,7 @@ impl<'a> From<BodyRename<'a>> for Body<'a> {
fn from(value: BodyRename<'a>) -> Self { Self::Rename(value) }
}
impl IntoLua for BodyRename<'static> {
impl IntoLua for BodyRename<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([

View file

@ -23,7 +23,7 @@ impl<'a> From<BodyTrash<'a>> for Body<'a> {
fn from(value: BodyTrash<'a>) -> Self { Self::Trash(value) }
}
impl IntoLua for BodyTrash<'static> {
impl IntoLua for BodyTrash<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let urls =
lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?;

View file

@ -26,6 +26,6 @@ impl<'a> From<BodyYank<'a>> for Body<'a> {
fn from(value: BodyYank<'a>) -> Self { Self::Yank(value) }
}
impl IntoLua for BodyYank<'static> {
impl IntoLua for BodyYank<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { self.0.into_lua(lua) }
}

View file

@ -1,8 +1,14 @@
#[macro_export]
macro_rules! local_or_err {
($name:literal) => {
if !$crate::LOCAL.read().contains_key($name) {
anyhow::bail!("No local event handler found");
macro_rules! body {
(mgr: $name:ident, $body:expr) => {
paste::paste! {
$crate::body::Body::[<Key $name:camel>]($body)
}
};
($layer:ident : $name:ident, $body:expr) => {
$crate::body::Body::Void(&yazi_parser::VoidOpt)
// paste::paste! {
// $crate::body::Body::[<Key $layer:camel $name:camel>]($body)
// }
};
}

View file

@ -2,8 +2,8 @@ use std::{fmt::Display, io::Write, str::FromStr};
use anyhow::{Result, anyhow};
use yazi_boot::BOOT;
use yazi_macro::emit;
use yazi_shared::{Id, event::Cmd};
use yazi_macro::{emit, relay};
use yazi_shared::Id;
use crate::{ID, body::Body};
@ -47,7 +47,7 @@ impl<'a> Payload<'a> {
impl Payload<'static> {
pub(super) fn emit(self) -> Result<()> {
self.try_flush()?;
emit!(Call(Cmd::new("app:accept_payload").with_any("payload", self)));
emit!(Call(relay!(app:accept_payload).with_any("payload", self)));
Ok(())
}
}
@ -93,8 +93,8 @@ impl Display for Payload<'_> {
Body::Trash(b) => serde_json::to_string(b),
Body::Delete(b) => serde_json::to_string(b),
Body::Mount(b) => serde_json::to_string(b),
Body::BeforeQuit(b) => serde_json::to_string(b),
Body::Custom(b) => serde_json::to_string(b),
_ => return Err(std::fmt::Error),
};
if let Ok(s) = result {

View file

@ -44,9 +44,7 @@ anyhow = { workspace = true }
better-panic = "0.3.0"
crossterm = { workspace = true }
fdlimit = "0.3.0"
foldhash = { workspace = true }
futures = { workspace = true }
indexmap = { workspace = true }
mlua = { workspace = true }
paste = { workspace = true }
ratatui = { workspace = true }

View file

@ -21,7 +21,6 @@ impl App {
let mut app = Self { core: Core::make(), term: Some(term), signals };
act!(bootstrap, app)?;
act!(render, app)?;
let mut events = Vec::with_capacity(50);
let (mut timeout, mut last_render) = (None, Instant::now());

View file

@ -1,14 +1,14 @@
use anyhow::{Result, bail};
use mlua::{IntoLua, Value};
use mlua::IntoLua;
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_binding::runtime_mut;
use yazi_core::Core;
use yazi_dds::{LOCAL, Payload, REMOTE, body::Body};
use yazi_dds::{LOCAL, Payload, REMOTE};
use yazi_macro::succ;
use yazi_plugin::LUA;
use yazi_shared::event::{CmdCow, Data};
use crate::{app::App, lives::Lives};
use crate::app::App;
impl App {
pub(crate) fn accept_payload(&self, mut c: CmdCow) -> Result<Data> {
@ -38,32 +38,4 @@ impl App {
Ok(())
})?);
}
// FIXME: find a better name
pub(crate) fn accept_payload2(core: &Core, body: Body<'static>) -> Result<Data> {
let kind = body.kind();
let Some(handlers) = LOCAL.read().get(kind).filter(|&m| !m.is_empty()).cloned() else {
succ!(false)
};
let kind = kind.to_owned();
succ!(Lives::scope(core, || {
let body = body.into_lua(&LUA)?;
for (id, cb) in handlers {
runtime_mut!(LUA)?.push(&id);
let result = cb.call::<Value>(body.clone());
runtime_mut!(LUA)?.pop();
match result {
Ok(Value::Boolean(true)) => return Ok(true),
Ok(Value::Nil | Value::Boolean(false)) => {}
Ok(v) => {
error!("Unexpected return type from `{kind}` event handler in `{id}` plugin: {v:?}")
}
Err(e) => error!("Failed to run `{kind}` event handler in `{id}` plugin: {e}"),
}
}
Ok(false)
})?);
}
}

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_boot::BOOT;
use yazi_macro::{act, succ};
use yazi_macro::act;
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::{event::Data, url::Url};
@ -15,7 +15,9 @@ impl App {
tabs.push(Default::default());
}
let cx = &mut Ctx { core: &mut self.core, tab: i };
let cx = &mut Ctx::active(&mut self.core);
cx.tab = i;
if file.is_empty() {
act!(mgr:cd, cx, (Url::from(&BOOT.cwds[i]), CdSource::Tab))?;
} else {
@ -23,6 +25,6 @@ impl App {
}
}
succ!();
act!(render, self)
}
}

View file

@ -2,13 +2,14 @@ use anyhow::Result;
use crossterm::event::MouseEventKind;
use mlua::{ObjectLike, Table};
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_config::YAZI;
use yazi_macro::succ;
use yazi_parser::app::MouseOpt;
use yazi_plugin::LUA;
use yazi_shared::event::Data;
use crate::{app::App, lives::Lives};
use crate::app::App;
impl App {
pub fn mouse(&mut self, opt: MouseOpt) -> Result<Data> {

View file

@ -2,6 +2,7 @@ use anyhow::Result;
use mlua::ObjectLike;
use scopeguard::defer;
use tracing::{error, warn};
use yazi_actor::lives::Lives;
use yazi_binding::runtime_mut;
use yazi_dds::Sendable;
use yazi_macro::succ;
@ -10,7 +11,7 @@ use yazi_plugin::{LUA, loader::{LOADER, Loader}};
use yazi_proxy::AppProxy;
use yazi_shared::event::Data;
use crate::{app::App, lives::Lives};
use crate::app::App;
impl App {
pub(crate) fn plugin(&mut self, mut opt: PluginOpt) -> Result<Data> {

View file

@ -2,12 +2,13 @@ use anyhow::Result;
use mlua::Value;
use ratatui::layout::Position;
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_config::LAYOUT;
use yazi_macro::{render, succ};
use yazi_parser::VoidOpt;
use yazi_shared::event::Data;
use crate::{Root, app::App, lives::Lives};
use crate::{Root, app::App};
impl App {
pub fn reflow(&mut self, _: VoidOpt) -> Result<Data> {

View file

@ -3,14 +3,14 @@ use std::sync::atomic::{AtomicU8, Ordering};
use anyhow::Result;
use crossterm::{cursor::{MoveTo, SetCursorStyle, Show}, execute, queue, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}};
use ratatui::{CompletedFrame, backend::{Backend, CrosstermBackend}, buffer::Buffer, layout::Position};
use yazi_actor::Ctx;
use yazi_actor::{Ctx, lives::Lives};
use yazi_binding::elements::COLLISION;
use yazi_macro::{act, succ};
use yazi_parser::VoidOpt;
use yazi_shared::event::{Data, NEED_RENDER};
use yazi_term::tty::TTY;
use crate::{app::App, lives::Lives, root::Root};
use crate::{app::App, root::Root};
impl App {
pub(crate) fn render(&mut self, _: VoidOpt) -> Result<Data> {
@ -36,7 +36,8 @@ impl App {
// Reload preview if collision is resolved
if collision && !COLLISION.load(Ordering::Relaxed) {
act!(mgr:peek, &mut Ctx::active(&mut self.core), true)?;
let cx = &mut Ctx::active(&mut self.core);
act!(mgr:peek, cx, true)?;
}
succ!();
}

View file

@ -14,6 +14,7 @@ impl App {
self.core.parent_mut().map(|f| f.arrow(0));
self.core.current_mut().sync_page(true);
act!(mgr:peek, &mut Ctx::active(&mut self.core))
let cx = &mut Ctx::active(&mut self.core);
act!(mgr:peek, cx)
}
}

View file

@ -18,7 +18,8 @@ impl App {
let new = tasks.paginate();
if tasks.summaries != new {
tasks.summaries = new;
act!(tasks:arrow, &mut Ctx::active(&mut self.core))?;
let cx = &mut Ctx::active(&mut self.core);
act!(tasks:arrow, cx)?;
succ!(render!());
}
}

View file

@ -1,6 +1,5 @@
use anyhow::Result;
use yazi_actor::{Actor, Ctx};
use yazi_dds::body::Body;
use yazi_actor::Ctx;
use yazi_macro::{act, succ};
use yazi_shared::{Layer, event::{CmdCow, Data}};
use yazi_widgets::input::InputMode;
@ -57,16 +56,10 @@ impl<'a> Executor<'a> {
fn mgr(&mut self, cmd: CmdCow) -> Result<Data> {
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
// FIXME: move the DDS part to `yazi-actor`
macro_rules! on {
($name:ident) => {
if cmd.name == stringify!($name) {
let opt = <act!(mgr:$name) as Actor>::Options::try_from(cmd)?;
return if let Ok(body) = Body::try_from(&opt) && App::accept_payload2(cx.core, body)? == true {
succ!()
} else {
act!(mgr:$name, cx, opt)
};
return act!(mgr:$name, cx, cmd)
}
};
}

View file

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

View file

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

View file

@ -1,22 +1,37 @@
#[macro_export]
macro_rules! act {
($layer:ident : $name:ident, $cx:expr, $cmd:expr) => {{
#[allow(unused_imports)]
use ::yazi_actor::Actor;
<act!($layer:$name)>::act($cx, $cmd.try_into()?)
}};
($layer:ident : $name:ident, $cx:expr) => {{
#[allow(unused_imports)]
use yazi_actor::Actor;
<act!($layer:$name)>::act($cx, Default::default())
}};
($name:ident, $cx:expr, $cmd:expr) => {
$cx.$name($cmd.try_into()?)
(@pre $layer:ident : $name:ident, $cx:ident, $opt:ident) => {
<act!($layer:$name) as yazi_actor::Actor>::hook($cx, &$opt).map(|hook| {
<act!(core:preflight) as yazi_actor::Actor>::act($cx, (hook, yazi_dds::body!($layer:$name, &$opt)))
})
};
($name:ident, $cx:expr) => {
$cx.$name(Default::default())
(@impl $layer:ident : $name:ident, $cx:ident, $opt:ident) => {{
$cx.level += 1;
let result = match act!(@pre $layer:$name, $cx, $opt) {
None | Some(Ok(yazi_shared::event::Data::Boolean(false))) => <act!($layer:$name) as yazi_actor::Actor>::act($cx, $opt),
Some(Ok(_)) => Err(anyhow::anyhow!("canceled on preflight")),
Some(e @ Err(_)) => e,
};
$cx.level -= 1;
result
}};
($layer:ident : $name:ident, $cx:ident, $cmd:expr) => {
<act!($layer:$name) as yazi_actor::Actor>::Options::try_from($cmd)
.map_err(anyhow::Error::from)
.and_then(|opt| act!(@impl $layer:$name, $cx, opt))
};
($layer:ident : $name:ident, $cx:ident) => {
act!($layer:$name, $cx, <<act!($layer:$name) as yazi_actor::Actor>::Options as Default>::default())
};
($layer:ident : $name:ident) => {
paste::paste! { yazi_actor::$layer::[<$name:camel>] }
};
($name:ident, $cx:expr, $cmd:expr) => {
$cmd.try_into().map_err(anyhow::Error::from).and_then(|opt| $cx.$name(opt))
};
($name:ident, $cx:expr) => {
$cx.$name(Default::default())
};
}

View file

@ -14,6 +14,19 @@ macro_rules! emit {
};
}
#[macro_export]
macro_rules! relay {
($layer:ident : $name:ident) => {
yazi_shared::event::Cmd::new_relay(concat!(stringify!($layer), ":", stringify!($name)))
};
($layer:ident : $name:ident, $args:expr) => {
yazi_shared::event::Cmd::new_relay_args(
concat!(stringify!($layer), ":", stringify!($name)),
$args,
)
};
}
#[macro_export]
macro_rules! succ {
($data:expr) => {
@ -23,34 +36,3 @@ macro_rules! succ {
return Ok(yazi_shared::event::Data::Nil)
};
}
#[macro_export]
macro_rules! render {
() => {
yazi_shared::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed);
};
($cond:expr) => {
if $cond {
render!();
}
};
($left:expr, > $right:expr) => {{
let val = $left;
if val > $right {
render!();
}
val
}};
}
#[macro_export]
macro_rules! render_and {
($cond:expr) => {
if $cond {
yazi_macro::render!();
true
} else {
false
}
};
}

View file

@ -5,4 +5,5 @@ mod fmt;
mod log;
mod module;
mod platform;
mod render;
mod stdio;

30
yazi-macro/src/render.rs Normal file
View file

@ -0,0 +1,30 @@
#[macro_export]
macro_rules! render {
() => {
yazi_shared::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed);
};
($cond:expr) => {
if $cond {
render!();
}
};
($left:expr, > $right:expr) => {{
let val = $left;
if val > $right {
render!();
}
val
}};
}
#[macro_export]
macro_rules! render_and {
($cond:expr) => {
if $cond {
yazi_macro::render!();
true
} else {
false
}
};
}

View file

@ -8,6 +8,10 @@ description = "Yazi command parser"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" }

View file

@ -1,9 +1,10 @@
use anyhow::bail;
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
use crate::Step;
#[derive(Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ArrowOpt {
pub step: Step,
}
@ -23,3 +24,7 @@ impl TryFrom<CmdCow> for ArrowOpt {
impl From<isize> for ArrowOpt {
fn from(n: isize) -> Self { Self { step: n.into() } }
}
impl IntoLua for ArrowOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(backspace backward close complete delete insert kill r#move paste show);
yazi_macro::mod_flat!(backspace backward close complete delete forward insert kill paste r#move show);

View file

@ -1 +0,0 @@

View file

@ -1,6 +1,8 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_fs::expand_path;
use yazi_shared::{event::CmdCow, url::Url};
#[derive(Debug)]
pub struct CdOpt {
pub target: Url,
pub interactive: bool,
@ -21,8 +23,12 @@ impl From<(Url, CdSource)> for CdOpt {
fn from((target, source): (Url, CdSource)) -> Self { Self { target, interactive: false, source } }
}
impl IntoLua for &CdOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}
// --- Source
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CdSource {
Tab,
Cd,

View file

@ -1,10 +1,15 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
use crate::mgr::QuitOpt;
#[derive(Default)]
#[derive(Debug, Default)]
pub struct CloseOpt(pub QuitOpt);
impl From<CmdCow> for CloseOpt {
fn from(c: CmdCow) -> Self { Self(c.into()) }
}
impl IntoLua for &CloseOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,7 +1,9 @@
use std::{borrow::Cow, ffi::OsStr, path::Path};
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::{SStr, event::CmdCow};
#[derive(Debug)]
pub struct CopyOpt {
pub r#type: SStr,
pub separator: CopySeparator,
@ -18,8 +20,12 @@ impl From<CmdCow> for CopyOpt {
}
}
impl IntoLua for &CopyOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}
// --- Separator
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CopySeparator {
Auto,
Unix,

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct CreateOpt {
pub dir: bool,
pub force: bool,
@ -8,3 +10,7 @@ pub struct CreateOpt {
impl From<CmdCow> for CreateOpt {
fn from(c: CmdCow) -> Self { Self { dir: c.bool("dir"), force: c.bool("force") } }
}
impl IntoLua for &CreateOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1 +0,0 @@

View file

@ -1,7 +1,9 @@
use bitflags::bitflags;
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
bitflags! {
#[derive(Debug)]
pub struct EscapeOpt: u8 {
const FIND = 0b00001;
const VISUAL = 0b00010;
@ -26,3 +28,7 @@ impl From<CmdCow> for EscapeOpt {
})
}
}
impl IntoLua for &EscapeOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,7 +1,8 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_fs::FilterCase;
use yazi_shared::{SStr, event::CmdCow};
#[derive(Default)]
#[derive(Debug, Default)]
pub struct FilterOpt {
pub query: SStr,
pub case: FilterCase,
@ -23,3 +24,7 @@ impl TryFrom<CmdCow> for FilterOpt {
})
}
}
impl IntoLua for &FilterOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,6 +1,8 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_fs::FilterCase;
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct FindOpt {
pub prev: bool,
pub case: FilterCase,
@ -13,3 +15,7 @@ impl TryFrom<CmdCow> for FindOpt {
Ok(Self { prev: c.bool("previous"), case: FilterCase::from(&*c) })
}
}
impl IntoLua for &FindOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct FindArrowOpt {
pub prev: bool,
}
@ -7,3 +9,7 @@ pub struct FindArrowOpt {
impl From<CmdCow> for FindArrowOpt {
fn from(c: CmdCow) -> Self { Self { prev: c.bool("previous") } }
}
impl IntoLua for &FindArrowOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,7 +1,9 @@
use anyhow::bail;
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_fs::FilterCase;
use yazi_shared::{SStr, event::CmdCow};
#[derive(Debug)]
pub struct FindDoOpt {
pub query: SStr,
pub prev: bool,
@ -23,3 +25,7 @@ impl TryFrom<CmdCow> for FindDoOpt {
Ok(Self { query, prev: c.bool("previous"), case: FilterCase::from(&*c) })
}
}
impl IntoLua for &FindDoOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct HardlinkOpt {
pub force: bool,
pub follow: bool,
@ -8,3 +10,7 @@ pub struct HardlinkOpt {
impl From<CmdCow> for HardlinkOpt {
fn from(c: CmdCow) -> Self { Self { force: c.bool("force"), follow: c.bool("follow") } }
}
impl IntoLua for &HardlinkOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct HiddenOpt {
pub state: Option<bool>,
}
@ -15,3 +17,7 @@ impl From<CmdCow> for HiddenOpt {
Self { state }
}
}
impl IntoLua for &HiddenOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,6 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::{event::CmdCow, url::Url};
#[derive(Default)]
#[derive(Debug, Default)]
pub struct HoverOpt {
pub url: Option<Url>,
}
@ -13,7 +14,12 @@ impl From<Option<Url>> for HoverOpt {
fn from(url: Option<Url>) -> Self { Self { url } }
}
impl IntoLua for &HoverOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}
// --- Do
#[derive(Debug)]
pub struct HoverDoOpt {
pub url: Url,
}
@ -21,3 +27,7 @@ pub struct HoverDoOpt {
impl From<Url> for HoverDoOpt {
fn from(url: Url) -> Self { Self { url } }
}
impl IntoLua for &HoverDoOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,6 +1,8 @@
use anyhow::bail;
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::{SStr, event::CmdCow};
#[derive(Debug)]
pub struct LinemodeOpt {
pub new: SStr,
}
@ -20,3 +22,7 @@ impl TryFrom<CmdCow> for LinemodeOpt {
Ok(Self { new })
}
}
impl IntoLua for &LinemodeOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value};
use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct LinkOpt {
pub relative: bool,
pub force: bool,
@ -8,3 +10,7 @@ pub struct LinkOpt {
impl From<CmdCow> for LinkOpt {
fn from(c: CmdCow) -> Self { Self { relative: c.bool("relative"), force: c.bool("force") } }
}
impl IntoLua for &LinkOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

Some files were not shown because too many files have changed in this diff Show more