refactor: eliminate the tab conceptual hierarchy (#2999)

This commit is contained in:
三咲雅 misaki masa 2025-07-18 14:31:16 +08:00 committed by GitHub
parent 0fd74ae44c
commit c28d2e8180
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
131 changed files with 325 additions and 166 deletions

3
Cargo.lock generated
View file

@ -258,6 +258,9 @@ name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
dependencies = [
"serde",
]
[[package]]
name = "bitstream-io"

View file

@ -23,7 +23,7 @@ lto = false
ansi-to-tui = "7.0.0"
anyhow = "1.0.98"
base64 = "0.22.1"
bitflags = "2.9.1"
bitflags = { version = "2.9.1", features = [ "serde" ] }
clap = { version = "4.5.41", features = [ "derive" ] }
core-foundation-sys = "0.8.7"
crossterm = { version = "0.29.0", features = [ "event-stream" ] }

View file

@ -6,7 +6,7 @@ use crate::Ctx;
pub trait Actor {
type Options;
const NAME: &'static str;
const NAME: &str;
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data>;
}

View file

@ -11,7 +11,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(render!(cx.cmp.scroll(opt.step)));

View file

@ -12,7 +12,7 @@ pub struct Close;
impl Actor for Close {
type Options = CloseOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let cmp = &mut cx.core.cmp;

View file

@ -14,7 +14,7 @@ pub struct Show;
impl Actor for Show {
type Options = ShowOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let cmp = &mut cx.cmp;

View file

@ -15,7 +15,7 @@ pub struct Trigger;
impl Actor for Trigger {
type Options = TriggerOpt;
const NAME: &'static str = "trigger";
const NAME: &str = "trigger";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let cmp = &mut cx.cmp;

View file

@ -10,7 +10,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let confirm = &mut cx.core.confirm;

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = CloseOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if let Some(cb) = cx.confirm.callback.take() {

View file

@ -10,7 +10,7 @@ pub struct Show;
impl Actor for Show {
type Options = ShowOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(confirm:close, cx)?;

View file

@ -11,7 +11,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(render!(cx.help.scroll(opt.step)));

View file

@ -10,7 +10,7 @@ pub struct Escape;
impl Actor for Escape {
type Options = VoidOpt;
const NAME: &'static str = "escape";
const NAME: &str = "escape";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
if cx.help.keyword().is_none() {

View file

@ -10,7 +10,7 @@ pub struct Filter;
impl Actor for Filter {
type Options = VoidOpt;
const NAME: &'static str = "filter";
const NAME: &str = "filter";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let help = &mut cx.help;

View file

@ -10,7 +10,7 @@ pub struct Toggle;
impl Actor for Toggle {
type Options = ToggleOpt;
const NAME: &'static str = "toggle";
const NAME: &str = "toggle";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let help = &mut cx.help;

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = CloseOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let input = &mut cx.input;

View file

@ -11,7 +11,7 @@ pub struct Escape;
impl Actor for Escape {
type Options = VoidOpt;
const NAME: &'static str = "escape";
const NAME: &str = "escape";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
use yazi_widgets::input::InputMode as M;

View file

@ -14,7 +14,7 @@ pub struct Show;
impl Actor for Show {
type Options = ShowOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(input:close, cx)?;

View file

@ -10,7 +10,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, tab::CdSource};
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct Back;
impl Actor for Back {
type Options = VoidOpt;
const NAME: &'static str = "back";
const NAME: &str = "back";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
if let Some(u) = cx.tab_mut().backstack.shift_backward().cloned() {

View file

@ -20,7 +20,7 @@ pub struct BulkRename;
impl Actor for BulkRename {
type Options = VoidOpt;
const NAME: &'static str = "bulk_rename";
const NAME: &str = "bulk_rename";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let Some(opener) = YAZI.opener.block(YAZI.open.all("bulk-rename.txt", "text/plain")) else {

View file

@ -7,7 +7,7 @@ use yazi_config::popup::InputCfg;
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, expand_path};
use yazi_macro::{act, err, render, succ};
use yazi_parser::tab::CdOpt;
use yazi_parser::mgr::CdOpt;
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
use yazi_shared::{Debounce, errors::InputError, event::Data, url::Url};
@ -18,7 +18,7 @@ pub struct Cd;
impl Actor for Cd {
type Options = CdOpt;
const NAME: &'static str = "cd";
const NAME: &str = "cd";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = CloseOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if cx.tabs().len() > 1 {

View file

@ -2,7 +2,7 @@ use std::{ffi::OsString, path::Path};
use anyhow::{Result, bail};
use yazi_macro::{act, succ};
use yazi_parser::tab::CopyOpt;
use yazi_parser::mgr::CopyOpt;
use yazi_shared::event::Data;
use yazi_widgets::CLIPBOARD;
@ -13,7 +13,7 @@ pub struct Copy;
impl Actor for Copy {
type Options = CopyOpt;
const NAME: &'static str = "copy";
const NAME: &str = "copy";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;

View file

@ -14,7 +14,7 @@ pub struct Create;
impl Actor for Create {
type Options = CreateOpt;
const NAME: &'static str = "create";
const NAME: &str = "create";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let cwd = cx.cwd().to_owned();

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, tab::CdSource};
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct Enter;
impl Actor for Enter {
type Options = VoidOpt;
const NAME: &'static str = "enter";
const NAME: &str = "enter";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let Some(h) = cx.hovered().filter(|h| h.is_dir()) else { succ!() };

View file

@ -1,6 +1,6 @@
use anyhow::{Result, bail};
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::{VoidOpt, tab::EscapeOpt};
use yazi_parser::{VoidOpt, mgr::EscapeOpt};
use yazi_proxy::AppProxy;
use yazi_shared::event::Data;
@ -11,7 +11,7 @@ pub struct Escape;
impl Actor for Escape {
type Options = EscapeOpt;
const NAME: &'static str = "escape";
const NAME: &str = "escape";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if opt.is_empty() {
@ -48,7 +48,7 @@ pub struct EscapeFind;
impl Actor for EscapeFind {
type Options = VoidOpt;
const NAME: &'static str = "escape_find";
const NAME: &str = "escape_find";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
succ!(render_and!(cx.tab_mut().finder.take().is_some()))
@ -61,7 +61,7 @@ pub struct EscapeVisual;
impl Actor for EscapeVisual {
type Options = VoidOpt;
const NAME: &'static str = "escape_visual";
const NAME: &str = "escape_visual";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();
@ -93,7 +93,7 @@ pub struct EscapeFilter;
impl Actor for EscapeFilter {
type Options = VoidOpt;
const NAME: &'static str = "escape_filter";
const NAME: &str = "escape_filter";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
if cx.current_mut().files.filter().is_none() {
@ -112,7 +112,7 @@ pub struct EscapeSelect;
impl Actor for EscapeSelect {
type Options = VoidOpt;
const NAME: &'static str = "escape_select";
const NAME: &str = "escape_select";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();
@ -136,7 +136,7 @@ pub struct EscapeSearch;
impl Actor for EscapeSearch {
type Options = VoidOpt;
const NAME: &'static str = "escape_search";
const NAME: &str = "escape_search";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let b = cx.cwd().is_search();

View file

@ -5,7 +5,7 @@ use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_macro::succ;
use yazi_parser::tab::FilterOpt;
use yazi_parser::mgr::FilterOpt;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_shared::{Debounce, errors::InputError, event::Data};
@ -16,7 +16,7 @@ pub struct Filter;
impl Actor for Filter {
type Options = FilterOpt;
const NAME: &'static str = "filter";
const NAME: &str = "filter";
fn act(_: &mut Ctx, opt: Self::Options) -> Result<Data> {
let input = InputProxy::show(InputCfg::filter());

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_fs::Filter;
use yazi_macro::{act, render, succ};
use yazi_parser::tab::FilterOpt;
use yazi_parser::mgr::FilterOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -11,7 +11,7 @@ pub struct FilterDo;
impl Actor for FilterDo {
type Options = FilterOpt;
const NAME: &'static str = "filter_do";
const NAME: &str = "filter_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let filter = if opt.query.is_empty() { None } else { Some(Filter::new(&opt.query, opt.case)?) };

View file

@ -5,7 +5,7 @@ use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_macro::succ;
use yazi_parser::tab::{FindDoOpt, FindOpt};
use yazi_parser::mgr::{FindDoOpt, FindOpt};
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_shared::{Debounce, errors::InputError, event::Data};
@ -16,7 +16,7 @@ pub struct Find;
impl Actor for Find {
type Options = FindOpt;
const NAME: &'static str = "find";
const NAME: &str = "find";
fn act(_: &mut Ctx, opt: Self::Options) -> Result<Data> {
let input = InputProxy::show(InputCfg::find(opt.prev));

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::tab::FindArrowOpt;
use yazi_parser::mgr::FindArrowOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct FindArrow;
impl Actor for FindArrow {
type Options = FindArrowOpt;
const NAME: &'static str = "find_arrow";
const NAME: &str = "find_arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_core::tab::Finder;
use yazi_macro::{act, render, succ};
use yazi_parser::tab::FindDoOpt;
use yazi_parser::mgr::FindDoOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -11,7 +11,7 @@ pub struct FindDo;
impl Actor for FindDo {
type Options = FindDoOpt;
const NAME: &'static str = "find_do";
const NAME: &str = "find_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if opt.query.is_empty() {

View file

@ -11,7 +11,7 @@ pub struct Follow;
impl Actor for Follow {
type Options = VoidOpt;
const NAME: &'static str = "follow";
const NAME: &str = "follow";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let Some(file) = cx.hovered() else { succ!() };

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, tab::CdSource};
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct Forward;
impl Actor for Forward {
type Options = VoidOpt;
const NAME: &'static str = "forward";
const NAME: &str = "forward";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
match cx.tab_mut().backstack.shift_forward().cloned() {

View file

@ -10,7 +10,7 @@ pub struct Hardlink;
impl Actor for Hardlink {
type Options = HardlinkOpt;
const NAME: &'static str = "hardlink";
const NAME: &str = "hardlink";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mgr = &mut cx.core.mgr;

View file

@ -10,7 +10,7 @@ pub struct Hidden;
impl Actor for Hidden {
type Options = HiddenOpt;
const NAME: &'static str = "hidden";
const NAME: &str = "hidden";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_dds::Pubsub;
use yazi_macro::{act, err, render, succ};
use yazi_parser::tab::{HoverDoOpt, HoverOpt};
use yazi_parser::mgr::{HoverDoOpt, HoverOpt};
use yazi_shared::{event::Data, url::Urn};
use crate::{Actor, Ctx};
@ -11,7 +11,7 @@ pub struct Hover;
impl Actor for Hover {
type Options = HoverOpt;
const NAME: &'static str = "hover";
const NAME: &str = "hover";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if let Some(u) = opt.url {
@ -33,7 +33,7 @@ pub struct HoverDo;
impl Actor for HoverDo {
type Options = HoverDoOpt;
const NAME: &'static str = "hover_do";
const NAME: &str = "hover_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
// Hover on the file

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, tab::CdSource};
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct Leave;
impl Actor for Leave {
type Options = VoidOpt;
const NAME: &'static str = "leave";
const NAME: &str = "leave";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let url = cx

View file

@ -10,7 +10,7 @@ pub struct Linemode;
impl Actor for Linemode {
type Options = LinemodeOpt;
const NAME: &'static str = "linemode";
const NAME: &str = "linemode";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -10,7 +10,7 @@ pub struct Link;
impl Actor for Link {
type Options = LinkOpt;
const NAME: &'static str = "link";
const NAME: &str = "link";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mgr = &mut cx.core.mgr;

View file

@ -18,7 +18,7 @@ pub struct Open;
impl Actor for Open {
type Options = OpenOpt;
const NAME: &'static str = "open";
const NAME: &str = "open";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;
@ -78,7 +78,7 @@ pub struct OpenDo;
impl Actor for OpenDo {
type Options = OpenDoOpt;
const NAME: &'static str = "open_do";
const NAME: &str = "open_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mut targets = opt.targets;

View file

@ -10,7 +10,7 @@ pub struct Paste;
impl Actor for Paste {
type Options = PasteOpt;
const NAME: &'static str = "paste";
const NAME: &str = "paste";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mgr = &mut cx.core.mgr;

View file

@ -11,7 +11,7 @@ pub struct Peek;
impl Actor for Peek {
type Options = PeekOpt;
const NAME: &'static str = "peek";
const NAME: &str = "peek";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let Some(hovered) = cx.hovered().cloned() else {

View file

@ -16,7 +16,7 @@ pub struct Quit;
impl Actor for Quit {
type Options = QuitOpt;
const NAME: &'static str = "quit";
const NAME: &str = "quit";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let event = opt.into();

View file

@ -14,7 +14,7 @@ pub struct Refresh;
impl Actor for Refresh {
type Options = VoidOpt;
const NAME: &'static str = "refresh";
const NAME: &str = "refresh";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
if let (_, Some(s)) = (CWD.set(cx.cwd()), YAZI.mgr.title()) {

View file

@ -12,7 +12,7 @@ pub struct Remove;
impl Actor for Remove {
type Options = RemoveOpt;
const NAME: &'static str = "remove";
const NAME: &str = "remove";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;
@ -50,7 +50,7 @@ pub struct RemoveDo;
impl Actor for RemoveDo {
type Options = RemoveOpt;
const NAME: &'static str = "remove_do";
const NAME: &str = "remove_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let mgr = &mut cx.mgr;

View file

@ -15,7 +15,7 @@ pub struct Rename;
impl Actor for Rename {
type Options = RenameOpt;
const NAME: &'static str = "rename";
const NAME: &str = "rename";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_fs::{File, FilesOp};
use yazi_macro::{act, succ};
use yazi_parser::tab::RevealOpt;
use yazi_parser::mgr::RevealOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -11,7 +11,7 @@ pub struct Reveal;
impl Actor for Reveal {
type Options = RevealOpt;
const NAME: &'static str = "reveal";
const NAME: &str = "reveal";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let Some((parent, child)) = opt.target.pair() else { succ!() };

View file

@ -18,7 +18,7 @@ pub struct Search;
impl Actor for Search {
type Options = SearchOpt;
const NAME: &'static str = "search";
const NAME: &str = "search";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
if let Some(handle) = cx.tab_mut().search.take() {
@ -44,7 +44,7 @@ pub struct SearchDo;
impl Actor for SearchDo {
type Options = SearchOpt;
const NAME: &'static str = "search_do";
const NAME: &str = "search_do";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();
@ -99,7 +99,7 @@ pub struct SearchStop;
impl Actor for SearchStop {
type Options = ();
const NAME: &'static str = "search_stop";
const NAME: &str = "search_stop";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -12,7 +12,7 @@ pub struct Seek;
impl Actor for Seek {
type Options = SeekOpt;
const NAME: &'static str = "seek";
const NAME: &str = "seek";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let Some(hovered) = cx.hovered() else {

View file

@ -3,7 +3,7 @@ use std::borrow::Cow;
use anyhow::Result;
use yazi_config::{opener::OpenerRule, popup::InputCfg};
use yazi_macro::{act, succ};
use yazi_parser::tab::ShellOpt;
use yazi_parser::mgr::ShellOpt;
use yazi_proxy::{InputProxy, TasksProxy};
use yazi_shared::event::Data;
@ -14,7 +14,7 @@ pub struct Shell;
impl Actor for Shell {
type Options = ShellOpt;
const NAME: &'static str = "shell";
const NAME: &str = "shell";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;

View file

@ -12,7 +12,7 @@ pub struct Sort;
impl Actor for Sort {
type Options = CmdCow;
const NAME: &'static str = "sort";
const NAME: &str = "sort";
fn act(cx: &mut Ctx, c: Self::Options) -> Result<Data> {
let mut new = cx.tab().pref.clone();

View file

@ -10,7 +10,7 @@ pub struct Spot;
impl Actor for Spot {
type Options = SpotOpt;
const NAME: &'static str = "spot";
const NAME: &str = "spot";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let Some(hovered) = cx.hovered().cloned() else { succ!() };

View file

@ -10,7 +10,7 @@ pub struct Suspend;
impl Actor for Suspend {
type Options = VoidOpt;
const NAME: &'static str = "suspend";
const NAME: &str = "suspend";
fn act(_: &mut Ctx, _: Self::Options) -> Result<Data> {
#[cfg(unix)]

View file

@ -10,7 +10,7 @@ pub struct TabClose;
impl Actor for TabClose {
type Options = TabCloseOpt;
const NAME: &'static str = "tab_close";
const NAME: &str = "tab_close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let len = cx.tabs().len();

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_core::tab::Tab;
use yazi_macro::{act, render, succ};
use yazi_parser::{mgr::TabCreateOpt, tab::CdSource};
use yazi_parser::mgr::{CdSource, TabCreateOpt};
use yazi_proxy::AppProxy;
use yazi_shared::event::Data;
@ -14,7 +14,7 @@ pub struct TabCreate;
impl Actor for TabCreate {
type Options = TabCreateOpt;
const NAME: &'static str = "tab_create";
const NAME: &str = "tab_create";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if cx.tabs().len() >= MAX_TABS {

View file

@ -10,7 +10,7 @@ pub struct TabSwap;
impl Actor for TabSwap {
type Options = ArrowOpt;
const NAME: &'static str = "tab_swap";
const NAME: &str = "tab_swap";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tabs = cx.tabs_mut();

View file

@ -10,7 +10,7 @@ pub struct TabSwitch;
impl Actor for TabSwitch {
type Options = TabSwitchOpt;
const NAME: &'static str = "tab_switch";
const NAME: &str = "tab_switch";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tabs = cx.tabs_mut();

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{render_and, succ};
use yazi_parser::tab::ToggleOpt;
use yazi_parser::mgr::ToggleOpt;
use yazi_proxy::AppProxy;
use yazi_shared::event::Data;
@ -11,7 +11,7 @@ pub struct Toggle;
impl Actor for Toggle {
type Options = ToggleOpt;
const NAME: &'static str = "toggle";
const NAME: &str = "toggle";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::tab::ToggleAllOpt;
use yazi_parser::mgr::ToggleAllOpt;
use yazi_proxy::AppProxy;
use yazi_shared::event::Data;
@ -11,7 +11,7 @@ pub struct ToggleAll;
impl Actor for ToggleAll {
type Options = ToggleAllOpt;
const NAME: &'static str = "toggle_all";
const NAME: &str = "toggle_all";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
use yazi_shared::Either::*;

View file

@ -10,7 +10,7 @@ pub struct Unyank;
impl Actor for Unyank {
type Options = VoidOpt;
const NAME: &'static str = "unyank";
const NAME: &str = "unyank";
fn act(cx: &mut crate::Ctx, _: Self::Options) -> Result<Data> {
let repeek = cx.hovered().is_some_and(|f| f.is_dir() && cx.mgr.yanked.contains_in(&f.url));

View file

@ -12,7 +12,7 @@ pub struct UpdateFiles;
impl Actor for UpdateFiles {
type Options = UpdateFilesOpt;
const NAME: &'static str = "update_files";
const NAME: &str = "update_files";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let revision = cx.current().files.revision;

View file

@ -13,7 +13,7 @@ pub struct UpdateMimes;
impl Actor for UpdateMimes {
type Options = UpdateMimesOpt;
const NAME: &'static str = "update_mimes";
const NAME: &str = "update_mimes";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let linked = LINKED.read();

View file

@ -10,7 +10,7 @@ pub struct UpdatePaged;
impl Actor for UpdatePaged {
type Options = UpdatePagedOpt;
const NAME: &'static str = "update_paged";
const NAME: &str = "update_paged";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if opt.only_if.is_some_and(|u| u != *cx.cwd()) {

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::tab::UpdatePeekedOpt;
use yazi_parser::mgr::UpdatePeekedOpt;
use yazi_shared::event::Data;
use crate::Actor;
@ -10,7 +10,7 @@ pub struct UpdatePeeked;
impl Actor for UpdatePeeked {
type Options = UpdatePeekedOpt;
const NAME: &'static str = "update_peeked";
const NAME: &str = "update_peeked";
fn act(cx: &mut crate::Ctx, opt: Self::Options) -> Result<Data> {
let Some(hovered) = cx.hovered().map(|h| &h.url) else {

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::tab::UpdateSpottedOpt;
use yazi_parser::mgr::UpdateSpottedOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -10,7 +10,7 @@ pub struct UpdateSpotted;
impl Actor for UpdateSpotted {
type Options = UpdateSpottedOpt;
const NAME: &'static str = "update_spotted";
const NAME: &str = "update_spotted";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -10,7 +10,7 @@ pub struct UpdateTasks;
impl Actor for UpdateTasks {
type Options = UpdateTasksOpt;
const NAME: &'static str = "update_tasks";
const NAME: &str = "update_tasks";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
cx.mgr.watcher.push_files(opt.urls);

View file

@ -11,7 +11,7 @@ pub struct UpdateYanked;
impl Actor for UpdateYanked {
type Options = UpdateYankedOpt<'static>;
const NAME: &'static str = "update_yanked";
const NAME: &str = "update_yanked";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if opt.urls.is_empty() && cx.mgr.yanked.is_empty() {

View file

@ -3,7 +3,7 @@ use std::collections::BTreeSet;
use anyhow::Result;
use yazi_core::tab::Mode;
use yazi_macro::{render, succ};
use yazi_parser::tab::VisualModeOpt;
use yazi_parser::mgr::VisualModeOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
@ -13,7 +13,7 @@ pub struct VisualMode;
impl Actor for VisualMode {
type Options = VisualModeOpt;
const NAME: &'static str = "visual_mode";
const NAME: &str = "visual_mode";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tab = cx.tab_mut();

View file

@ -12,7 +12,7 @@ pub struct Watch;
impl Actor for Watch {
type Options = VoidOpt;
const NAME: &'static str = "watch";
const NAME: &str = "watch";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let it = iter::once(cx.core.mgr.tabs.active().cwd())

View file

@ -11,7 +11,7 @@ pub struct Yank;
impl Actor for Yank {
type Options = YankOpt;
const NAME: &'static str = "yank";
const NAME: &str = "yank";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;

View file

@ -11,7 +11,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(render!(cx.pick.scroll(opt.step)));

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = CloseOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let pick = &mut cx.pick;

View file

@ -10,7 +10,7 @@ pub struct Show;
impl Actor for Show {
type Options = ShowOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(pick:close, cx)?;

View file

@ -10,7 +10,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let spot = &mut cx.tab_mut().spot;

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = VoidOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
succ!(cx.tab_mut().spot.reset());

View file

@ -11,7 +11,7 @@ pub struct Copy;
impl Actor for Copy {
type Options = CopyOpt;
const NAME: &'static str = "copy";
const NAME: &str = "copy";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let spot = &cx.tab().spot;

View file

@ -10,7 +10,7 @@ pub struct Swipe;
impl Actor for Swipe {
type Options = ArrowOpt;
const NAME: &'static str = "swipe";
const NAME: &str = "swipe";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
act!(mgr:arrow, cx, opt)?;

View file

@ -11,7 +11,7 @@ pub struct Arrow;
impl Actor for Arrow {
type Options = ArrowOpt;
const NAME: &'static str = "arrow";
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let tasks = &mut cx.tasks;

View file

@ -10,7 +10,7 @@ pub struct Cancel;
impl Actor for Cancel {
type Options = VoidOpt;
const NAME: &'static str = "cancel";
const NAME: &str = "cancel";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tasks = &mut cx.tasks;

View file

@ -10,7 +10,7 @@ pub struct Close;
impl Actor for Close {
type Options = VoidOpt;
const NAME: &'static str = "close";
const NAME: &str = "close";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tasks = &mut cx.tasks;

View file

@ -17,7 +17,7 @@ pub struct Inspect;
impl Actor for Inspect {
type Options = VoidOpt;
const NAME: &'static str = "inspect";
const NAME: &str = "inspect";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let ongoing = cx.tasks.ongoing().clone();

View file

@ -10,7 +10,7 @@ pub struct OpenWith;
impl Actor for OpenWith {
type Options = OpenWithOpt;
const NAME: &'static str = "open_with";
const NAME: &str = "open_with";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(cx.tasks.process_from_opener(

View file

@ -10,7 +10,7 @@ pub struct ProcessExec;
impl Actor for ProcessExec {
type Options = ProcessExecOpt;
const NAME: &'static str = "process_exec";
const NAME: &str = "process_exec";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(cx.tasks.scheduler.process_open(opt));

View file

@ -10,7 +10,7 @@ pub struct Show;
impl Actor for Show {
type Options = VoidOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let tasks = &mut cx.tasks;

View file

@ -10,7 +10,7 @@ pub struct Callback;
impl Actor for Callback {
type Options = CallbackOpt;
const NAME: &'static str = "callback";
const NAME: &str = "callback";
fn act(_: &mut Ctx, opt: Self::Options) -> Result<Data> {
opt.tx.try_send(opt.idx)?;

View file

@ -10,7 +10,7 @@ pub struct Show;
impl Actor for Show {
type Options = ShowOpt;
const NAME: &'static str = "show";
const NAME: &str = "show";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
if opt.cands.is_empty() {

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 stage style url urn utils);
yazi_macro::mod_flat!(cha color composer error file icon id iter permit runtime source stage style url urn utils);

View file

@ -0,0 +1,7 @@
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

@ -2,7 +2,7 @@ use tokio_util::sync::CancellationToken;
use yazi_config::YAZI;
use yazi_fs::File;
use yazi_macro::render;
use yazi_parser::tab::SpotLock;
use yazi_parser::mgr::SpotLock;
use yazi_plugin::isolate;
use yazi_shared::{SStr, url::Url};

View file

@ -7,7 +7,7 @@ use yazi_adapter::ADAPTOR;
use yazi_config::{LAYOUT, YAZI};
use yazi_fs::{File, Files, FilesOp, cha::Cha};
use yazi_macro::render;
use yazi_parser::tab::PreviewLock;
use yazi_parser::mgr::PreviewLock;
use yazi_plugin::{external::Highlighter, isolate};
use yazi_shared::{MIME_DIR, SStr, url::Url};

View file

@ -0,0 +1,72 @@
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,7 +1,36 @@
use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize};
use yazi_parser::mgr::QuitOpt;
use yazi_shared::Source;
pub struct BeforeQuit {
// FIXME
// pub opts: BeforeQuitOpt,
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

@ -3,7 +3,7 @@ use mlua::{ExternalResult, IntoLua, Lua, Value};
use serde::Serialize;
use yazi_shared::Id;
use super::{BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank};
use super::{BeforeQuitBody, BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank};
use crate::Payload;
#[derive(Debug, Serialize)]
@ -23,6 +23,7 @@ pub enum Body<'a> {
Trash(BodyTrash<'a>),
Delete(BodyDelete<'a>),
Mount(BodyMount),
BeforeQuit(BeforeQuitBody),
Custom(BodyCustom),
}
@ -43,6 +44,7 @@ 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)?,
})
}
@ -69,6 +71,7 @@ impl Body<'static> {
| "trash"
| "delete"
| "mount"
| "before-quit"
) {
bail!("Cannot construct system event");
}
@ -103,6 +106,7 @@ impl<'a> Body<'a> {
Self::Trash(_) => "trash",
Self::Delete(_) => "delete",
Self::Mount(_) => "mount",
Self::BeforeQuit(_) => "before-quit",
Self::Custom(b) => b.kind.as_str(),
}
}
@ -133,6 +137,7 @@ 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),
}
}

View file

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

View file

@ -1,5 +1,7 @@
#![allow(clippy::option_map_unit_fn)]
mod macros;
yazi_macro::mod_pub!(body);
yazi_macro::mod_flat!(client payload pubsub pump sendable server state stream);

8
yazi-dds/src/macros.rs Normal file
View file

@ -0,0 +1,8 @@
#[macro_export]
macro_rules! local_or_err {
($name:literal) => {
if !$crate::LOCAL.read().contains_key($name) {
anyhow::bail!("No local event handler found");
}
};
}

View file

@ -93,6 +93,7 @@ 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),
};

View file

@ -1,8 +1,9 @@
use anyhow::{Result, bail};
use mlua::IntoLua;
use mlua::{IntoLua, Value};
use tracing::error;
use yazi_binding::runtime_mut;
use yazi_dds::{LOCAL, Payload, REMOTE};
use yazi_core::Core;
use yazi_dds::{LOCAL, Payload, REMOTE, body::Body};
use yazi_macro::succ;
use yazi_plugin::LUA;
use yazi_shared::event::{CmdCow, Data};
@ -10,7 +11,7 @@ use yazi_shared::event::{CmdCow, Data};
use crate::{app::App, lives::Lives};
impl App {
pub(crate) fn accept_payload(&mut self, mut c: CmdCow) -> Result<Data> {
pub(crate) fn accept_payload(&self, mut c: CmdCow) -> Result<Data> {
let Some(payload) = c.take_any2::<Payload>("payload").transpose()? else {
bail!("'payload' is required for accept_payload");
};
@ -37,4 +38,32 @@ 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

@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_actor::Ctx;
use yazi_boot::BOOT;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, tab::CdSource};
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::{event::Data, url::Url};
use crate::app::App;

View file

@ -1,5 +1,6 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_actor::{Actor, Ctx};
use yazi_dds::body::Body;
use yazi_macro::{act, succ};
use yazi_shared::{Layer, event::{CmdCow, Data}};
use yazi_widgets::input::InputMode;
@ -56,10 +57,16 @@ 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) {
return act!(mgr:$name, cx, cmd);
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)
};
}
};
}

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