From f7fea2b19afeaade31c6fa1a37c124c1fa59e15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 25 May 2026 09:31:04 +0800 Subject: [PATCH 01/28] docs: update contributing guidelines --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c74755d8..21c1e5c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,3 +164,10 @@ We want you to succeed, and it can be discouraging to find that a lot of re-work - Your code passes all tests and lints. - Your pull request description clearly explains the changes and why they are needed. 4. Address any review comments. Make sure to push updates to the same branch on your fork. + +## AI Policy + +1. All issue, PR, and discussion descriptions must be written by humans, not AI. +2. Any use of AI must be disclosed. You must declare which model you used and the extent of AI assistance. +3. Any AI-generated code must be reviewed, tested, and simplified by a human before publishing. This requires you to fully understand how it interacts with the greater system without AI assistance. +4. Any AI tools used must explicitly state they do not assert copyright over the work. From cf8b54179ea0de392629d93081afca9e5587225e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 25 May 2026 10:30:04 +0800 Subject: [PATCH 02/28] feat: terminal drag and drop protocol (#3999) --- Cargo.lock | 37 +++---- Cargo.toml | 2 +- cspell.json | 2 +- yazi-actor/src/tasks/inspect.rs | 3 +- yazi-fm/src/dispatcher.rs | 52 +++++++++- yazi-term/Cargo.toml | 1 + yazi-term/src/error.rs | 6 +- yazi-term/src/event/dnd.rs | 121 +++++++++++++++++++++++ yazi-term/src/event/event.rs | 3 +- yazi-term/src/event/mod.rs | 2 +- yazi-term/src/parser/mod.rs | 2 +- yazi-term/src/parser/osc.rs | 40 ++++++++ yazi-term/src/parser/parser.rs | 51 ++++++++-- yazi-term/src/parser/state.rs | 15 ++- yazi-term/src/sequence/dnd.rs | 170 ++++++++++++++++++++++++++++++++ yazi-term/src/sequence/mod.rs | 2 +- yazi-tui/src/raterm.rs | 8 +- 17 files changed, 474 insertions(+), 43 deletions(-) create mode 100644 yazi-term/src/event/dnd.rs create mode 100644 yazi-term/src/parser/osc.rs create mode 100644 yazi-term/src/sequence/dnd.rs diff --git a/Cargo.lock b/Cargo.lock index 363c3c2b..2496ee06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "av-scenechange" @@ -489,9 +489,9 @@ checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "by_address" @@ -2047,9 +2047,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -3590,9 +3590,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.61.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba341d1fce5e094392f088499c5808d2a60d3a658f58fe480c26ac77b1c7dd3" +checksum = "f67013f080c226e5a34db1c71f2567f44d95a6300005bb6cd4e2c8fe3c326d1b" dependencies = [ "aes", "bitflags 2.11.1", @@ -4934,9 +4934,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -4947,9 +4947,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -4957,9 +4957,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4967,9 +4967,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -4980,9 +4980,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -6159,6 +6159,7 @@ dependencies = [ "ratatui", "rustix 1.1.4", "signal-hook 0.4.4", + "strum 0.28.0", "thiserror 2.0.18", "tokio", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index d7ff624f..516ac590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ percent-encoding = "2.3.2" rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } ratatui = { version = "0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.3" -russh = { version = "0.61.0", default-features = false, features = [ "ring", "rsa" ] } +russh = { version = "0.61.1", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.150" diff --git a/cspell.json b/cspell.json index 289f3fd0..ae46bfbf 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","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","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm"],"version":"0.2","flagWords":[]} \ No newline at end of file +{"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","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes"],"language":"en","version":"0.2"} \ No newline at end of file diff --git a/yazi-actor/src/tasks/inspect.rs b/yazi-actor/src/tasks/inspect.rs index 22150212..a7824471 100644 --- a/yazi-actor/src/tasks/inspect.rs +++ b/yazi-actor/src/tasks/inspect.rs @@ -4,12 +4,11 @@ use anyhow::Result; use scopeguard::defer; use tokio::{io::{AsyncReadExt, stdin}, select, sync::mpsc, time}; use yazi_binding::Permit; -use yazi_term::TERM; use yazi_macro::{succ, writef}; use yazi_parser::VoidForm; use yazi_scheduler::AppProxy; use yazi_shared::data::Data; -use yazi_term::{YIELD_TO_SUBPROCESS, sequence::EraseScreen}; +use yazi_term::{TERM, YIELD_TO_SUBPROCESS, sequence::EraseScreen}; use yazi_tty::TTY; use crate::{Actor, Ctx}; diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs index 53b525dd..34e4057d 100644 --- a/yazi-fm/src/dispatcher.rs +++ b/yazi-fm/src/dispatcher.rs @@ -4,9 +4,10 @@ use anyhow::Result; use tracing::warn; use yazi_actor::Ctx; use yazi_config::keymap::Key; -use yazi_macro::{act, emit}; +use yazi_macro::{act, emit, writef}; use yazi_shared::event::{ActionCow, Event, NEED_RENDER}; -use yazi_term::event::{Event as TermEvent, KeyEvent, MouseEvent}; +use yazi_term::{event::{Event as TermEvent, KeyEvent, MouseEvent}, sequence::{ConfirmDrag, ConfirmDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}}; +use yazi_tty::TTY; use yazi_widgets::input::InputMode; use crate::{Executor, Router, app::App}; @@ -29,6 +30,53 @@ impl<'a> Dispatcher<'a> { Event::Term(TermEvent::FocusIn) => self.dispatch_focus(), Event::Term(TermEvent::FocusOut) => Ok(()), Event::Term(TermEvent::Paste(str)) => self.dispatch_paste(str), + Event::Term(TermEvent::Dnd(dnd)) => { + match dnd { + yazi_term::event::DndEvent::DragOffer(event) => { + tracing::debug!("DragOffer: {event:?}"); + // writef!( + // TTY.writer(), + // "{}{}{}{StartDrag}", + // ConfirmDrag::Either(&["text/uri-list"]), + // PresentDrag(0, b"file:///tmp/cspell.json\r\n"), + // PresentDragIcon { format: 0, width: 6, height: 1, opacity: 0, + // payload: b"drag" }, ) + // .ok(); + } + yazi_term::event::DndEvent::DragAccept(event) => { + tracing::debug!("DragAccept: {event:?}"); + } + yazi_term::event::DndEvent::DragChange(event) => { + tracing::debug!("DragChange: {event:?}"); + } + yazi_term::event::DndEvent::DragLand => { + tracing::debug!("DragLand"); + } + yazi_term::event::DndEvent::DragEnd(event) => { + tracing::debug!("DragEnd: {event:?}"); + } + yazi_term::event::DndEvent::DragSend(event) => { + tracing::debug!("DragSend: {event:?}"); + } + + yazi_term::event::DndEvent::DropEnter(event) => { + tracing::debug!("DropEnter: {event:?}"); + writef!(TTY.writer(), "{}", ConfirmDrop::Copy(&["text/uri-list"])).ok(); + } + yazi_term::event::DndEvent::DropLeave => { + tracing::debug!("DropLeave"); + } + yazi_term::event::DndEvent::DropReady(event) => { + tracing::debug!("DropReady: {event:?}"); + writef!(TTY.writer(), "{}", StartDrop(1)).ok(); + } + yazi_term::event::DndEvent::DropData(event) => { + tracing::debug!("DropData: {event:?}"); + writef!(TTY.writer(), "{}", FinishDrop::Copy).ok(); + } + } + Ok(()) + } }; if let Err(e) = &result { diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index 7d2a506d..f3977dbe 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -24,6 +24,7 @@ bitflags = { workspace = true } futures = { workspace = true } parking_lot = { workspace = true } ratatui = { workspace = true } +strum = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } diff --git a/yazi-term/src/error.rs b/yazi-term/src/error.rs index 3d376b36..8d9cf715 100644 --- a/yazi-term/src/error.rs +++ b/yazi-term/src/error.rs @@ -1,4 +1,4 @@ -use std::str; +use std::{num, str}; pub type Result = std::result::Result; @@ -19,3 +19,7 @@ pub enum ParseError { impl From for ParseError { fn from(_: str::Utf8Error) -> Self { Self::Invalid } } + +impl From for ParseError { + fn from(_: num::ParseIntError) -> Self { Self::Invalid } +} diff --git a/yazi-term/src/event/dnd.rs b/yazi-term/src/event/dnd.rs new file mode 100644 index 00000000..763140ac --- /dev/null +++ b/yazi-term/src/event/dnd.rs @@ -0,0 +1,121 @@ +use std::str::SplitWhitespace; + +use strum::FromRepr; + +use crate::parser::StateOsc72; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum DndEvent { + // Drag + DragOffer(DndDragOffer), + DragAccept(DndDragAccept), + DragChange(DndDragChange), + DragLand, + DragEnd(DndDragEnd), + DragSend(DndDragSend), + + // Drop + DropEnter(DndDropEnter), + DropLeave, + DropReady(DndDropReady), + DropData(DndDropData), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragOffer { + pub x: u32, + pub y: u32, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragAccept { + pub idx: u8, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragChange { + pub op: DndOp, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragEnd { + pub canceled: bool, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragSend { + pub idx: u8, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDropEnter { + pub x: u32, + pub y: u32, + pub op: DndOp, + pub mimes: DndMimeList, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDropReady { + pub x: u32, + pub y: u32, + pub op: DndOp, + pub mimes: DndMimeList, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDropData { + pub idx: u8, + pub payload: Vec, +} + +impl DndEvent { + pub(crate) fn from_state(s: StateOsc72) -> Option { + Some(match s.r#type.unwrap_or_default() { + // Drag + b'o' => Self::DragOffer(DndDragOffer { x: s.x?.try_into().ok()?, y: s.y?.try_into().ok()? }), + b'e' if s.x? == 1 => Self::DragAccept(DndDragAccept { idx: s.y?.try_into().ok()? }), + b'e' if s.x? == 2 => Self::DragChange(DndDragChange { op: DndOp::from_repr(s.op?)? }), + b'e' if s.x? == 3 => Self::DragLand, + b'e' if s.x? == 4 => Self::DragEnd(DndDragEnd { canceled: s.y? != 0 }), + b'e' if s.x? == 5 => Self::DragSend(DndDragSend { idx: s.y?.try_into().ok()? }), + + // Drop + b'm' if s.x == Some(-1) && s.y == Some(-1) => Self::DropLeave, + b'm' => Self::DropEnter(DndDropEnter { + x: s.x?.try_into().ok()?, + y: s.y?.try_into().ok()?, + op: DndOp::from_repr(s.op?)?, + mimes: DndMimeList::new(s.payload)?, + }), + b'M' => Self::DropReady(DndDropReady { + x: s.x?.try_into().ok()?, + y: s.y?.try_into().ok()?, + op: DndOp::from_repr(s.op?)?, + mimes: DndMimeList::new(s.payload)?, + }), + b'r' => Self::DropData(DndDropData { idx: s.x?.try_into().ok()?, payload: s.payload }), + + _ => return None, + }) + } +} + +// --- Operation +#[derive(Clone, Copy, Debug, Eq, FromRepr, PartialEq)] +#[repr(u8)] +pub enum DndOp { + Copy = 1, + Move = 2, + Either = 3, +} + +// --- MIME list +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndMimeList(String); + +impl DndMimeList { + pub fn new(b: Vec) -> Option { Some(Self(String::from_utf8(b).ok()?)) } + + pub fn iter(&self) -> SplitWhitespace<'_> { self.0.split_whitespace() } +} diff --git a/yazi-term/src/event/event.rs b/yazi-term/src/event/event.rs index f83e0f05..0df95509 100644 --- a/yazi-term/src/event/event.rs +++ b/yazi-term/src/event/event.rs @@ -1,4 +1,4 @@ -use crate::{Dimension, event::{KeyEvent, MouseEvent}}; +use crate::{Dimension, event::{DndEvent, KeyEvent, MouseEvent}}; #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { @@ -8,4 +8,5 @@ pub enum Event { FocusIn, FocusOut, Paste(String), + Dnd(DndEvent), } diff --git a/yazi-term/src/event/mod.rs b/yazi-term/src/event/mod.rs index 6a7bbaf9..247ca94b 100644 --- a/yazi-term/src/event/mod.rs +++ b/yazi-term/src/event/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(event keyboard modifiers mouse); +yazi_macro::mod_flat!(dnd event keyboard modifiers mouse); diff --git a/yazi-term/src/parser/mod.rs b/yazi-term/src/parser/mod.rs index cc7f5c59..72b7cbef 100644 --- a/yazi-term/src/parser/mod.rs +++ b/yazi-term/src/parser/mod.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_flat!(csi ground parser state); +yazi_macro::mod_flat!(csi ground osc parser state); #[cfg(windows)] yazi_macro::mod_flat!(windows); diff --git a/yazi-term/src/parser/osc.rs b/yazi-term/src/parser/osc.rs new file mode 100644 index 00000000..268dae2b --- /dev/null +++ b/yazi-term/src/parser/osc.rs @@ -0,0 +1,40 @@ +use crate::{ParseError, Result, parser::{Parser, State}}; + +impl Parser { + pub(super) fn parse_osc72(&mut self) -> Result<()> { + debug_assert!(self.seq.starts_with(b"\x1b]72;")); + debug_assert!(self.seq.ends_with(b"\x1b\\")); + + let mut it = self.seq[5..self.seq.len() - 2].splitn(2, |&b| b == b';'); + let meta = str::from_utf8(it.next().ok_or(ParseError::Invalid)?)?; + let payload = it.next().unwrap_or(&[]); + + let State::Osc72(state) = &mut self.state else { unreachable!() }; + state.has_more = false; // reset has_more for this new sequence + + for part in meta.split(':') { + match part.split_once('=').ok_or(ParseError::Invalid)? { + ("t", v) if let Some(b) = v.bytes().next() => state.r#type = Some(b), + ("x", v) => state.x = Some(v.parse()?), + ("y", v) => state.y = Some(v.parse()?), + ("o", v) => state.op = Some(v.parse()?), + ("m", "1") => state.has_more = true, + _ => {} + } + } + + // For `t=r`, the presence of a payload indicates more data is coming, + // even if `m=1` is not set. + if state.r#type == Some(b'r') && !payload.is_empty() { + state.has_more = true; + } + + // Limit payload size to 1MiB to prevent potential DoS + if state.payload.len() + payload.len() > 1 << 20 { + return Err(ParseError::Invalid); + } + + state.payload.extend(payload); + Ok(()) + } +} diff --git a/yazi-term/src/parser/parser.rs b/yazi-term/src/parser/parser.rs index 826a3c94..79f2a6f2 100644 --- a/yazi-term/src/parser/parser.rs +++ b/yazi-term/src/parser/parser.rs @@ -1,13 +1,13 @@ -use std::{collections::VecDeque, num::NonZeroU8, str}; +use std::{collections::VecDeque, mem, num::NonZeroU8, str}; use yazi_shim::utf8_char_width; use super::state::State; -use crate::event::{Event, KeyCode, KeyEvent, Modifiers}; +use crate::event::{DndEvent, Event, KeyCode, KeyEvent, Modifiers}; #[derive(Debug)] pub struct Parser { - state: State, + pub(super) state: State, pub(super) seq: Vec, pub(crate) events: VecDeque, } @@ -30,7 +30,7 @@ impl Parser { } fn step(&mut self, b: u8) { - match self.state { + match &self.state { State::Ground => self.on_ground(b), State::Esc => self.on_esc(b), State::EscO => self.on_esco(b), @@ -38,9 +38,10 @@ impl Parser { State::NormalMouse => self.on_normal_mouse(b), State::BracketedPaste => self.on_bracketed_paste(b), State::Osc | State::OscSt => self.on_osc(b), + State::Osc72(_) => self.on_osc72(b), State::Dcs | State::DcsSt => self.on_dcs(b), - State::Utf8(n) => self.on_utf8(b, n), - State::AltUtf8(n) => self.on_alt_utf8(b, n), + State::Utf8(n) => self.on_utf8(b, *n), + State::AltUtf8(n) => self.on_alt_utf8(b, *n), } } @@ -51,9 +52,12 @@ impl Parser { /// been seen but no follow-up bytes arrived), this emits a bare /// [`KeyCode::Escape`] event and resets to [`State::Ground`]. pub fn flush(&mut self) { - if self.state == State::Esc { - self.emit_key(KeyCode::Escape); + match &self.state { + State::Esc => self.emit_key(KeyCode::Escape), + State::Osc72(s) if s.has_more => return, + _ => {} } + self.reset(); } @@ -193,8 +197,11 @@ impl Parser { fn on_osc(&mut self, b: u8) { self.seq.push(b); - match (self.state, b) { + match (&self.state, b) { (State::Osc, b'\x07') => self.reset(), // BEL — OSC complete (discard) + (State::Osc, _) if self.seq.starts_with(b"\x1b]72;") => { + self.state = State::Osc72(Default::default()); + } (State::Osc, b'\x1B') => self.state = State::OscSt, (State::Osc, _) => {} // keep accumulating (State::OscSt, b'\\') => self.reset(), // ST (`\x1B\\`) — OSC complete (discard) @@ -204,10 +211,34 @@ impl Parser { } } + fn on_osc72(&mut self, b: u8) { + self.seq.push(b); + + if !self.seq.ends_with(b"\x1b\\") { + return; + } else if self.parse_osc72().is_err() { + return self.reset(); + } + + match mem::take(&mut self.state) { + State::Osc72(s) if s.has_more => { + self.seq.clear(); + self.state = State::Osc72(s); + } + State::Osc72(s) => { + if let Some(e) = DndEvent::from_state(s) { + self.emit(Event::Dnd(e)); + } + self.reset(); + } + _ => unreachable!(), + } + } + fn on_dcs(&mut self, b: u8) { self.seq.push(b); - match (self.state, b) { + match (&self.state, b) { (State::Dcs, b'\x1B') => self.state = State::DcsSt, (State::Dcs, _) => {} (State::DcsSt, b'\\') => self.reset(), // ST — DCS complete (discard) diff --git a/yazi-term/src/parser/state.rs b/yazi-term/src/parser/state.rs index 49780ccb..b2934f53 100644 --- a/yazi-term/src/parser/state.rs +++ b/yazi-term/src/parser/state.rs @@ -1,8 +1,9 @@ use std::num::NonZeroU8; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Default, PartialEq)] pub(crate) enum State { /// Normal ground state. + #[default] Ground, /// Saw `\x1B`, waiting for the next byte. Esc, @@ -16,6 +17,8 @@ pub(crate) enum State { BracketedPaste, /// Inside an OSC sequence (`\x1B]` … BEL or ST). Osc, + /// Inside an OSC 72 (DnD) sequence (`\x1B]72;` … ST). + Osc72(StateOsc72), /// Inside OSC, just saw `\x1B` (potential start of ST = `\x1B\\`). OscSt, /// Inside a DCS sequence (`\x1BP` … ST). @@ -27,3 +30,13 @@ pub(crate) enum State { /// `\x1B` + mid-UTF-8 character: `n` continuation bytes still needed. AltUtf8(NonZeroU8), } + +#[derive(Debug, Default, PartialEq)] +pub(crate) struct StateOsc72 { + pub(crate) r#type: Option, + pub(crate) x: Option, + pub(crate) y: Option, + pub(crate) op: Option, + pub(crate) payload: Vec, + pub(crate) has_more: bool, +} diff --git a/yazi-term/src/sequence/dnd.rs b/yazi-term/src/sequence/dnd.rs new file mode 100644 index 00000000..57f31e68 --- /dev/null +++ b/yazi-term/src/sequence/dnd.rs @@ -0,0 +1,170 @@ +use std::{fmt::{self, Display}, str}; + +use base64::{Engine, engine::general_purpose}; + +/// Enable drag support: `OSC 72 ; t=o:x=1 ; machine id ST` +pub struct EnableDrag<'a>(pub &'a str); + +impl Display for EnableDrag<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\x1b]72;t=o:x=1;{}\x1b\\", self.0) + } +} + +/// Enable drop support: `OSC 72 ; t=a ; MIME list ST` +pub struct EnableDrop<'a>(pub &'a [&'a str]); + +impl Display for EnableDrop<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\x1b]72;t=a;{}\x1b\\", ListDndMimes(self.0)) + } +} + +/// Disable drag support: `OSC 72 ; t=o:x=2 ST` +pub struct DisableDrag; + +impl Display for DisableDrag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b]72;t=o:x=2\x1b\\") } +} + +/// Disable drop support: `OSC 72 ; t=A ST` +pub struct DisableDrop; + +impl Display for DisableDrop { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b]72;t=A\x1b\\") } +} + +/// Confirm drag: `OSC 72 ; t=o:o=operation ST` +pub enum ConfirmDrag<'a> { + Copy(&'a [&'a str]), + Move(&'a [&'a str]), + Either(&'a [&'a str]), +} + +impl Display for ConfirmDrag<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes)), + Self::Move(mimes) => write!(f, "\x1b]72;t=o:o=2;{}\x1b\\", ListDndMimes(mimes)), + Self::Either(mimes) => write!(f, "\x1b]72;t=o:o=3;{}\x1b\\", ListDndMimes(mimes)), + } + } +} + +/// Confirm dropped data: `OSC 72 ; t=m:o=O ; MIME list ST` +pub enum ConfirmDrop<'a> { + Reject, + Copy(&'a [&'a str]), + Move(&'a [&'a str]), +} + +impl Display for ConfirmDrop<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"), + Self::Copy(mimes) => write!(f, "\x1b]72;t=m:o=1;{}\x1b\\", ListDndMimes(mimes)), + Self::Move(mimes) => write!(f, "\x1b]72;t=m:o=2;{}\x1b\\", ListDndMimes(mimes)), + } + } +} + +/// Start dragging: `OSC 72 ; t=P:x=-1 ST` +pub struct StartDrag; + +impl Display for StartDrag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b]72;t=P:x=-1\x1b\\") } +} + +/// Start requesting dropped data: `OSC 72 ; t=r:x=idx ST` +pub struct StartDrop(pub u8); + +impl Display for StartDrop { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\x1b]72;t=r:x={}\x1b\\", self.0) + } +} + +/// Present drag data: `OSC 72 ; t=p:x=idx ; base64 encoded data ST` +pub struct PresentDrag<'a>(pub u8, pub &'a [u8]); + +impl Display for PresentDrag<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let b64 = general_purpose::STANDARD_NO_PAD.encode(self.1).into_bytes(); + let chunks = b64.len().div_ceil(4096); + + for (i, chunk) in b64.chunks(4096).enumerate() { + let s = unsafe { str::from_utf8_unchecked(chunk) }; + if i == 0 { + write!(f, "\x1b]72;t=p:x={}:m={};{s}\x1b\\", self.0, (chunks > 1) as u8)?; + } else { + write!(f, "\x1b]72;m={};{s}\x1b\\", (i + 1 < chunks) as u8)?; + } + } + + write!(f, "\x1b]72;t=p:x={}\x1b\\", self.0) + } +} + +/// Present drag icon data: +/// `OSC 72 ; t=p:x=-1:y=fmt:X=width:Y=height:o=opacity ; base64 payload ST` +pub struct PresentDragIcon<'a> { + pub format: u8, + pub opacity: u16, + pub width: u32, + pub height: u32, + pub payload: &'a [u8], +} + +impl Display for PresentDragIcon<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let b64 = general_purpose::STANDARD_NO_PAD.encode(self.payload).into_bytes(); + let chunks = b64.len().div_ceil(4096); + + for (i, chunk) in b64.chunks(4096).enumerate() { + let s = unsafe { str::from_utf8_unchecked(chunk) }; + if i == 0 { + write!( + f, + "\x1b]72;t=p:x=-1:y={}:X={}:Y={}:o={}:m={};{s}\x1b\\", + self.format, + self.width, + self.height, + self.opacity, + (chunks > 1) as u8 + )?; + } else { + write!(f, "\x1b]72;m={};{s}\x1b\\", (i + 1 < chunks) as u8)?; + } + } + + Ok(()) + } +} + +/// Finish requesting dropped data: `OSC 72 ; t=r:o=operation ST` +#[derive(Clone, Copy)] +pub enum FinishDrop { + Copy = 1, + Move = 2, +} + +impl Display for FinishDrop { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\x1b]72;t=r:o={}\x1b\\", *self as u8) + } +} + +/// Write MIME types separated by spaces. +struct ListDndMimes<'a>(&'a [&'a str]); + +impl Display for ListDndMimes<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (i, &m) in self.0.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + write!(f, "{m}")?; + } + Ok(()) + } +} diff --git a/yazi-term/src/sequence/mod.rs b/yazi-term/src/sequence/mod.rs index 5d166474..401188ba 100644 --- a/yazi-term/src/sequence/mod.rs +++ b/yazi-term/src/sequence/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(clipboard csi_u cursor erase mode query r#if restore_background set_background style sync); +yazi_macro::mod_flat!(clipboard csi_u cursor dnd erase mode query r#if restore_background set_background style sync); diff --git a/yazi-tui/src/raterm.rs b/yazi-tui/src/raterm.rs index 1fd82217..3b528821 100644 --- a/yazi-tui/src/raterm.rs +++ b/yazi-tui/src/raterm.rs @@ -6,7 +6,7 @@ use yazi_config::YAZI; use yazi_emulator::{Emulator, Mux, TMUX}; use yazi_macro::writef; use yazi_shim::cell::SyncCell; -use yazi_term::{TERM, event::{Event, KeyEventKind}, sequence::{DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RequestKeyboardFlags, RestoreBackground, RestoreCursorStyle, SetBackground, SetTitle, ShowCursor}, stream::EventStream}; +use yazi_term::{TERM, event::{Event, KeyEventKind}, sequence::{DisableBracketedPaste, DisableDrag, DisableDrop, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableDrag, EnableDrop, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RequestKeyboardFlags, RestoreBackground, RestoreCursorStyle, SetBackground, SetTitle, ShowCursor}, stream::EventStream}; use yazi_tty::{TTY, TtyWriter}; use crate::{RatermBackend, RatermOption, RatermState}; @@ -42,10 +42,12 @@ impl Raterm { let opt = RatermOption::default(); writef!( TTY.writer(), - "{}{RequestCursorStyle}{RequestCursorBlink}{RequestKeyboardFlags}{RequestDA1}{}{}{EnableBracketedPaste}{EnableFocusChange}{}", + "{}{RequestCursorStyle}{RequestCursorBlink}{RequestKeyboardFlags}{RequestDA1}{}{}{EnableBracketedPaste}{EnableFocusChange}{}{}{}", If(!TMUX.get(), EnterAlternateScreen), If(TMUX.get(), EnterAlternateScreen), SetBackground(&opt.bg), + EnableDrag(""), + EnableDrop(&["text/uri-list"]), If(opt.mouse, EnableMouseCapture), )?; @@ -80,7 +82,7 @@ impl Raterm { _ = writef!( TTY.writer(), - "{}{}{}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", + "{}{DisableDrop}{DisableDrag}{}{}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", If(state.mouse, DisableMouseCapture), If(state.bg, RestoreBackground), If(state.csi_u, PopKeyboardFlags), From 7a51626505decf8dd56b3b993790a44c843d993b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Thu, 28 May 2026 09:25:13 +0800 Subject: [PATCH 03/28] feat: drag and drop (#4005) --- CHANGELOG.md | 2 + Cargo.lock | 36 +++--- README.md | 2 +- cspell.json | 2 +- flake.lock | 12 +- nix/yazi-unwrapped.nix | 3 - yazi-actor/src/app/dnd.rs | 45 ++++++++ yazi-actor/src/app/mod.rs | 1 + yazi-actor/src/app/mouse.rs | 28 ++--- yazi-actor/src/tasks/spawn.rs | 1 + yazi-adapter/src/drivers/iip.rs | 2 +- yazi-binding/Cargo.toml | 1 + yazi-binding/src/dnd.rs | 55 +++++++++ yazi-binding/src/lib.rs | 2 +- yazi-binding/src/mouse.rs | 1 + yazi-binding/src/tty.rs | 74 ++++++++++++ yazi-core/src/tasks/file.rs | 3 +- yazi-core/src/tasks/option.rs | 12 +- yazi-fm/src/dispatcher.rs | 58 ++-------- yazi-parser/src/app/dnd.rs | 19 ++++ yazi-parser/src/app/mod.rs | 2 +- yazi-parser/src/spark/spark.rs | 3 + yazi-plugin/Cargo.toml | 25 +++-- yazi-plugin/preset/components/current.lua | 39 +++++++ yazi-plugin/preset/components/rail.lua | 4 + yazi-plugin/preset/components/root.lua | 16 ++- yazi-plugin/preset/components/tip.lua | 29 +++++ yazi-plugin/preset/plugins/dnd.lua | 45 ++++++++ yazi-plugin/src/runtime/runtime.rs | 3 +- yazi-plugin/src/standard.rs | 1 + yazi-plugin/src/utils/tasks.rs | 3 + yazi-plugin/src/utils/text.rs | 23 ++++ yazi-plugin/src/utils/utils.rs | 2 + yazi-runner/src/loader/loader.rs | 2 + yazi-scheduler/src/file/in.rs | 30 ++++- yazi-scheduler/src/plugin/in.rs | 6 +- yazi-scheduler/src/plugin/plugin.rs | 2 +- yazi-scheduler/src/scheduler.rs | 9 +- yazi-shared/Cargo.toml | 2 +- yazi-shim/Cargo.toml | 20 ++-- yazi-shim/src/base64.rs | 8 ++ yazi-shim/src/lib.rs | 2 +- yazi-shim/src/mlua/mod.rs | 2 +- yazi-shim/src/mlua/sequence.rs | 49 ++++++++ yazi-shim/src/mlua/string.rs | 26 +++++ yazi-shim/src/mlua/traits.rs | 23 +++- yazi-shim/src/percent_encoding.rs | 20 ++++ yazi-term/src/event/dnd.rs | 130 ++++++++++++++++++++-- yazi-term/src/sequence/dnd.rs | 51 +++++---- yazi-term/src/sequence/mod.rs | 2 +- yazi-term/src/sequence/traits.rs | 11 ++ yazi-tty/src/writer.rs | 8 +- 52 files changed, 784 insertions(+), 173 deletions(-) create mode 100644 yazi-actor/src/app/dnd.rs create mode 100644 yazi-binding/src/dnd.rs create mode 100644 yazi-binding/src/tty.rs create mode 100644 yazi-parser/src/app/dnd.rs create mode 100644 yazi-plugin/preset/components/tip.lua create mode 100644 yazi-plugin/preset/plugins/dnd.lua create mode 100644 yazi-shim/src/base64.rs create mode 100644 yazi-shim/src/mlua/sequence.rs create mode 100644 yazi-shim/src/mlua/string.rs create mode 100644 yazi-shim/src/percent_encoding.rs create mode 100644 yazi-term/src/sequence/traits.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ccb14fab..0ba1e102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): ### Added +- Drag and drop ([#4005]) - Bulk create ([#3793]) - Image preview with Überzug++ on Niri ([#3990]) @@ -1729,3 +1730,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3943]: https://github.com/sxyazi/yazi/pull/3943 [#3989]: https://github.com/sxyazi/yazi/pull/3989 [#3990]: https://github.com/sxyazi/yazi/pull/3990 +[#4005]: https://github.com/sxyazi/yazi/pull/4005 diff --git a/Cargo.lock b/Cargo.lock index 2496ee06..e2c3f2dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" dependencies = [ "cipher", "cpubits", @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.11.0-rc.3" +version = "0.11.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" +checksum = "da8c919c118108f144adecad74b425b804ad075580d605d9b33c2d6d1c62a2f8" dependencies = [ "aead", "aes", @@ -716,9 +716,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "compact_str" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" dependencies = [ "castaway", "cfg-if", @@ -2156,9 +2156,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -2201,9 +2201,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "loop9" @@ -2287,9 +2287,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmem" @@ -5685,6 +5685,7 @@ dependencies = [ "yazi-shared", "yazi-shim", "yazi-term", + "yazi-tty", "yazi-vfs", "yazi-widgets", ] @@ -5992,6 +5993,7 @@ dependencies = [ "libc", "mlua", "paste", + "percent-encoding", "ratatui", "serde_json", "tokio", @@ -6135,8 +6137,10 @@ name = "yazi-shim" version = "26.5.6" dependencies = [ "arc-swap", + "base64", "hashbrown 0.17.1", "mlua", + "percent-encoding", "ratatui", "serde", "thiserror 2.0.18", @@ -6264,18 +6268,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 1cd2e097..831a8ae7 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths - 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.) - 🔄 Bulk Rename/Create, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi) -- 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u, OSC 52 +- 🎨 Theme System, Mouse Support, Drag and Drop, Trash Bin, Custom Layouts, CSI u, OSC 52 - ... and more! https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265cc7 diff --git a/cspell.json b/cspell.json index ae46bfbf..41af766c 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"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","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes"],"language":"en","version":"0.2"} \ No newline at end of file +{"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","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist"]} \ No newline at end of file diff --git a/flake.lock b/flake.lock index d485f39e..052f6d36 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1776329215, - "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", + "lastModified": 1779877693, + "narHash": "sha256-NOF9NAREhxr50bbBfVcVOq+ArCMSoe8dP79Pk2uyARk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b86751bc4085f48661017fa226dee99fab6c651b", + "rev": "4100e830e085863741bc69b156ec4ccd53ab5be0", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1776741231, - "narHash": "sha256-k9G98qzn+7npROUaks8VqCFm7cFtEG8ulQLBBo5lItg=", + "lastModified": 1779851998, + "narHash": "sha256-UkkMh3bX9QW4Luqkm98nUaOqKWrU6i65mUnph3WeSSw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "02061303f7c4c964f7b4584dabd9e985b4cd442b", + "rev": "6cddd512fa2bf7231f098d3a2f92f6e4cff71e0a", "type": "github" }, "original": { diff --git a/nix/yazi-unwrapped.nix b/nix/yazi-unwrapped.nix index 3601b485..bba98fbf 100644 --- a/nix/yazi-unwrapped.nix +++ b/nix/yazi-unwrapped.nix @@ -28,9 +28,6 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoLock = { lockFile = "${src}/Cargo.lock"; - #outputHashes = { - # "mlua-0.10.0" = "sha256-Xg6/jc+UP8tbJJ6x1sbAgt8ZHt051xEBBcjmikQqYlw="; - #}; }; env = { diff --git a/yazi-actor/src/app/dnd.rs b/yazi-actor/src/app/dnd.rs new file mode 100644 index 00000000..10f357a5 --- /dev/null +++ b/yazi-actor/src/app/dnd.rs @@ -0,0 +1,45 @@ +use anyhow::Result; +use mlua::{ObjectLike, Table}; +use tracing::error; +use yazi_actor::lives::Lives; +use yazi_binding::runtime_scope; +use yazi_macro::succ; +use yazi_parser::app::DndForm; +use yazi_plugin::LUA; +use yazi_shared::data::Data; + +use crate::{Actor, Ctx}; + +pub struct Dnd; + +impl Actor for Dnd { + type Form = DndForm; + + const NAME: &str = "dnd"; + + fn act(cx: &mut Ctx, form: Self::Form) -> Result { + let event = yazi_binding::DndEvent::from(form.event); + + let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; + let area = yazi_binding::elements::Rect::from(size); + + let result = Lives::scope(cx.core, move || { + runtime_scope!(LUA, "root", { + let root = LUA.globals().raw_get::("Root")?.call_method::
("new", area)?; + + if event.is_drag() { + root.call_method::<()>("drag", event)?; + } else { + root.call_method::<()>("drop", event)?; + } + + Ok(()) + }) + }); + + if let Err(ref e) = result { + error!("{e}"); + } + succ!(result?); + } +} diff --git a/yazi-actor/src/app/mod.rs b/yazi-actor/src/app/mod.rs index 0b0a6086..b6c0bd1a 100644 --- a/yazi-actor/src/app/mod.rs +++ b/yazi-actor/src/app/mod.rs @@ -2,6 +2,7 @@ yazi_macro::mod_flat!( accept_payload bootstrap deprecate + dnd focus lua mouse diff --git a/yazi-actor/src/app/mouse.rs b/yazi-actor/src/app/mouse.rs index 7e121168..3692cf00 100644 --- a/yazi-actor/src/app/mouse.rs +++ b/yazi-actor/src/app/mouse.rs @@ -25,25 +25,25 @@ impl Actor for Mouse { let area = yazi_binding::elements::Rect::from(size); let result = Lives::scope(cx.core, move || { - let root = runtime_scope!(LUA, "root", { - LUA.globals().raw_get::
("Root")?.call_method::
("new", area) - })?; + runtime_scope!(LUA, "root", { + let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; - match event.kind { - MouseEventKind::Down(_) => root.call_method("click", (event, false))?, - MouseEventKind::Up(_) => root.call_method("click", (event, true))?, + match event.kind { + MouseEventKind::Down(_) => root.call_method("click", (event, false))?, + MouseEventKind::Up(_) => root.call_method("click", (event, true))?, - MouseEventKind::ScrollDown => root.call_method("scroll", (event, 1))?, - MouseEventKind::ScrollUp => root.call_method("scroll", (event, -1))?, + MouseEventKind::ScrollDown => root.call_method("scroll", (event, 1))?, + MouseEventKind::ScrollUp => root.call_method("scroll", (event, -1))?, - MouseEventKind::ScrollRight => root.call_method("touch", (event, 1))?, - MouseEventKind::ScrollLeft => root.call_method("touch", (event, -1))?, + MouseEventKind::ScrollRight => root.call_method("touch", (event, 1))?, + MouseEventKind::ScrollLeft => root.call_method("touch", (event, -1))?, - MouseEventKind::Moved => root.call_method("move", event)?, - MouseEventKind::Drag(_) => root.call_method("drag", event)?, - } + MouseEventKind::Moved => root.call_method("move", event)?, + MouseEventKind::Drag(_) => root.call_method("drag", event)?, + } - Ok(()) + Ok(()) + }) }); if let Err(ref e) = result { diff --git a/yazi-actor/src/tasks/spawn.rs b/yazi-actor/src/tasks/spawn.rs index 0928fe3c..963bd757 100644 --- a/yazi-actor/src/tasks/spawn.rs +++ b/yazi-actor/src/tasks/spawn.rs @@ -15,6 +15,7 @@ impl Actor for Spawn { fn act(cx: &mut Ctx, form: Self::Form) -> Result { succ!(match form.opt { + TaskOpt::Cut(r#in) => cx.tasks.scheduler.file_cut(r#in), TaskOpt::Plugin(r#in) => cx.tasks.scheduler.plugin_entry(r#in), }) } diff --git a/yazi-adapter/src/drivers/iip.rs b/yazi-adapter/src/drivers/iip.rs index 8859626b..e8174e8f 100644 --- a/yazi-adapter/src/drivers/iip.rs +++ b/yazi-adapter/src/drivers/iip.rs @@ -1,4 +1,4 @@ -use std::{fmt::Write, io::Write as ioWrite, path::PathBuf}; +use std::{fmt::Write as _, io::Write as _, path::PathBuf}; use anyhow::Result; use base64::{Engine, engine::{Config, general_purpose::STANDARD}}; diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index 1eb4fcc1..ccd2385a 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -25,6 +25,7 @@ yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } yazi-term = { path = "../yazi-term", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } diff --git a/yazi-binding/src/dnd.rs b/yazi-binding/src/dnd.rs new file mode 100644 index 00000000..6689acd8 --- /dev/null +++ b/yazi-binding/src/dnd.rs @@ -0,0 +1,55 @@ +use std::{mem, ops::Deref}; + +use mlua::{UserData, UserDataFields, Value}; +use yazi_shim::strum::IntoStr; +use yazi_term::event::{DndDropArrive, DndEvent as Inner}; + +use crate::{cached_field, cached_field_mut}; + +pub struct DndEvent { + inner: Inner, + + v_mimes: Option, + v_data: Option>, +} + +impl Deref for DndEvent { + type Target = Inner; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl From for DndEvent { + fn from(inner: Inner) -> Self { Self { inner, v_mimes: None, v_data: None } } +} + +impl UserData for DndEvent { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("type", |_, me| Ok(me.inner.r#type())); + + fields.add_field_method_get("x", |_, me| Ok(me.inner.x())); + + fields.add_field_method_get("y", |_, me| Ok(me.inner.y())); + + fields.add_field_method_get("idx", |_, me| Ok(me.inner.idx())); + + fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str))); + + cached_field!(fields, mimes, |lua, me| { + if let Some(mimes) = me.inner.mimes() { + lua.create_sequence_from(mimes.iter())?.into_lua(lua) + } else { + Ok(Value::Nil) + } + }); + + cached_field_mut!(fields, data, |lua, me| { + match &mut me.inner { + Inner::DropArrive(DndDropArrive { data, .. }) => { + lua.create_external_string(mem::take(data))?.into_lua(lua) + } + _ => Ok(Value::Nil), + } + }); + } +} diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index b1cfd06e..548e58aa 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -2,4 +2,4 @@ mod macros; yazi_macro::mod_pub!(config elements process theme); -yazi_macro::mod_flat!(access calculator cha chan chord_cow composer error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style url utils); +yazi_macro::mod_flat!(access calculator cha chan chord_cow composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); diff --git a/yazi-binding/src/mouse.rs b/yazi-binding/src/mouse.rs index bc959bf5..8d837b7d 100644 --- a/yazi-binding/src/mouse.rs +++ b/yazi-binding/src/mouse.rs @@ -18,6 +18,7 @@ impl From for MouseEvent { impl UserData for MouseEvent { fn add_fields>(fields: &mut F) { + fields.add_field("type", "legacy"); fields.add_field_method_get("x", |_, me| Ok(me.column)); fields.add_field_method_get("y", |_, me| Ok(me.row)); fields.add_field_method_get("is_left", |_, me| { diff --git a/yazi-binding/src/tty.rs b/yazi-binding/src/tty.rs new file mode 100644 index 00000000..39f52e74 --- /dev/null +++ b/yazi-binding/src/tty.rs @@ -0,0 +1,74 @@ +use std::io::Write; + +use mlua::{BorrowedBytes, ExternalError, IntoLuaMulti, Lua, MultiValue, Table, UserData, UserDataMethods}; +use yazi_shim::mlua::{ByteString, LuaTableExt}; +use yazi_term::sequence::{ConfirmDrag, ConfirmDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}; +use yazi_tty::TTY; + +use crate::Error; + +pub struct Tty; + +impl Tty { + fn queue(lua: &Lua, kind: &[u8], t: &Table) -> mlua::Result { + let mut w = TTY.writer(); + + let result = match kind { + b"ConfirmDrag" => { + let it = t.raw_get::
("mimes")?.sequence_iter::(lua).flatten(); + write!(w, "{}", match &*t.raw_get::("type")? { + b"copy" => ConfirmDrag::Copy(it), + b"move" => ConfirmDrag::Move(it), + b"either" => ConfirmDrag::Either(it), + _ => return Err("invalid ConfirmDrag type".into_lua_err()), + }) + } + b"ConfirmDrop" => { + let it = t.raw_get::
("mimes")?.sequence_iter::(lua).flatten(); + write!(w, "{}", match &*t.raw_get::("type")? { + b"reject" => ConfirmDrop::Reject, + b"copy" => ConfirmDrop::Copy(it), + b"move" => ConfirmDrop::Move(it), + _ => return Err("invalid ConfirmDrop type".into_lua_err()), + }) + } + b"StartDrag" => write!(w, "{StartDrag}"), + b"StartDrop" => write!(w, "{}", StartDrop(t.raw_get("idx")?)), + b"PresentDrag" => { + write!(w, "{}", PresentDrag(t.raw_get("idx")?, &t.raw_get::("data")?)) + } + b"PresentDragIcon" => { + write!(w, "{}", PresentDragIcon { + format: t.raw_get("format")?, + opacity: t.raw_get("opacity")?, + width: t.raw_get("width")?, + height: t.raw_get("height")?, + data: &t.raw_get::("data")?, + }) + } + b"FinishDrop" => match &*t.raw_get::("type")? { + b"copy" => write!(w, "{}", FinishDrop::Copy), + b"move" => write!(w, "{}", FinishDrop::Move), + _ => return Err("invalid FinishDrop type".into_lua_err()), + }, + _ => return Err("invalid sequence kind".into_lua_err()), + }; + + match result { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), + } + } +} + +impl UserData for Tty { + fn add_methods>(methods: &mut M) { + methods + .add_method("queue", |lua, _, (kind, t): (BorrowedBytes, Table)| Self::queue(lua, &kind, &t)); + + methods.add_method("flush", |lua, _, ()| match TTY.writer().flush() { + Ok(()) => true.into_lua_multi(lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(lua), + }); + } +} diff --git a/yazi-core/src/tasks/file.rs b/yazi-core/src/tasks/file.rs index a737e4d3..c956c76e 100644 --- a/yazi-core/src/tasks/file.rs +++ b/yazi-core/src/tasks/file.rs @@ -1,5 +1,6 @@ use indexmap::IndexSet; use tracing::debug; +use yazi_scheduler::file::FileInCut; use yazi_shared::url::{UrlBuf, UrlBufCov, UrlLike}; use super::Tasks; @@ -17,7 +18,7 @@ impl Tasks { if force && *u == to { debug!("file_cut: same file, skip {to:?}"); } else { - self.scheduler.file_cut(u.0.clone(), to, force); + self.scheduler.file_cut(FileInCut::new(u.0.clone(), to, force)); } } } diff --git a/yazi-core/src/tasks/option.rs b/yazi-core/src/tasks/option.rs index 30269059..fe861cd7 100644 --- a/yazi-core/src/tasks/option.rs +++ b/yazi-core/src/tasks/option.rs @@ -1,10 +1,12 @@ use std::borrow::Cow; -use yazi_scheduler::{TaskIn, plugin::PluginInEntry}; +use yazi_scheduler::{TaskIn, file::FileInCut, plugin::PluginInEntry}; use yazi_shared::{Id, SStr}; #[derive(Clone, Debug)] pub enum TaskOpt { + Cut(FileInCut), + Plugin(PluginInEntry), } @@ -13,12 +15,16 @@ impl TaskIn for TaskOpt { fn id(&self) -> Id { match self { + Self::Cut(r#in) => r#in.id(), + Self::Plugin(r#in) => r#in.id(), } } fn set_id(&mut self, id: Id) -> &mut Self { match self { + Self::Cut(r#in) => _ = r#in.set_id(id), + Self::Plugin(r#in) => _ = r#in.set_id(id), } self @@ -26,12 +32,16 @@ impl TaskIn for TaskOpt { fn title(&self) -> Cow<'_, str> { match self { + Self::Cut(r#in) => r#in.title(), + Self::Plugin(r#in) => r#in.title(), } } fn set_title(&mut self, title: impl Into) -> &mut Self { match self { + Self::Cut(r#in) => _ = r#in.set_title(title), + Self::Plugin(r#in) => _ = r#in.set_title(title), } self diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs index 34e4057d..b25fd32e 100644 --- a/yazi-fm/src/dispatcher.rs +++ b/yazi-fm/src/dispatcher.rs @@ -4,10 +4,9 @@ use anyhow::Result; use tracing::warn; use yazi_actor::Ctx; use yazi_config::keymap::Key; -use yazi_macro::{act, emit, writef}; +use yazi_macro::{act, emit}; use yazi_shared::event::{ActionCow, Event, NEED_RENDER}; -use yazi_term::{event::{Event as TermEvent, KeyEvent, MouseEvent}, sequence::{ConfirmDrag, ConfirmDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}}; -use yazi_tty::TTY; +use yazi_term::event::{DndEvent, Event as TermEvent, KeyEvent, MouseEvent}; use yazi_widgets::input::InputMode; use crate::{Executor, Router, app::App}; @@ -30,53 +29,7 @@ impl<'a> Dispatcher<'a> { Event::Term(TermEvent::FocusIn) => self.dispatch_focus(), Event::Term(TermEvent::FocusOut) => Ok(()), Event::Term(TermEvent::Paste(str)) => self.dispatch_paste(str), - Event::Term(TermEvent::Dnd(dnd)) => { - match dnd { - yazi_term::event::DndEvent::DragOffer(event) => { - tracing::debug!("DragOffer: {event:?}"); - // writef!( - // TTY.writer(), - // "{}{}{}{StartDrag}", - // ConfirmDrag::Either(&["text/uri-list"]), - // PresentDrag(0, b"file:///tmp/cspell.json\r\n"), - // PresentDragIcon { format: 0, width: 6, height: 1, opacity: 0, - // payload: b"drag" }, ) - // .ok(); - } - yazi_term::event::DndEvent::DragAccept(event) => { - tracing::debug!("DragAccept: {event:?}"); - } - yazi_term::event::DndEvent::DragChange(event) => { - tracing::debug!("DragChange: {event:?}"); - } - yazi_term::event::DndEvent::DragLand => { - tracing::debug!("DragLand"); - } - yazi_term::event::DndEvent::DragEnd(event) => { - tracing::debug!("DragEnd: {event:?}"); - } - yazi_term::event::DndEvent::DragSend(event) => { - tracing::debug!("DragSend: {event:?}"); - } - - yazi_term::event::DndEvent::DropEnter(event) => { - tracing::debug!("DropEnter: {event:?}"); - writef!(TTY.writer(), "{}", ConfirmDrop::Copy(&["text/uri-list"])).ok(); - } - yazi_term::event::DndEvent::DropLeave => { - tracing::debug!("DropLeave"); - } - yazi_term::event::DndEvent::DropReady(event) => { - tracing::debug!("DropReady: {event:?}"); - writef!(TTY.writer(), "{}", StartDrop(1)).ok(); - } - yazi_term::event::DndEvent::DropData(event) => { - tracing::debug!("DropData: {event:?}"); - writef!(TTY.writer(), "{}", FinishDrop::Copy).ok(); - } - } - Ok(()) - } + Event::Term(TermEvent::Dnd(dnd)) => self.dispatch_dnd(dnd), }; if let Err(e) = &result { @@ -145,4 +98,9 @@ impl<'a> Dispatcher<'a> { } Ok(()) } + + fn dispatch_dnd(&mut self, dnd: DndEvent) -> Result<()> { + let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term); + act!(app:dnd, cx, dnd).map(|_| ()) + } } diff --git a/yazi-parser/src/app/dnd.rs b/yazi-parser/src/app/dnd.rs new file mode 100644 index 00000000..b879ca51 --- /dev/null +++ b/yazi-parser/src/app/dnd.rs @@ -0,0 +1,19 @@ +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_term::event::DndEvent; + +#[derive(Debug)] +pub struct DndForm { + pub event: DndEvent, +} + +impl From for DndForm { + fn from(event: DndEvent) -> Self { Self { event } } +} + +impl FromLua for DndForm { + fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } +} + +impl IntoLua for DndForm { + fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } +} diff --git a/yazi-parser/src/app/mod.rs b/yazi-parser/src/app/mod.rs index 881d723f..095335e8 100644 --- a/yazi-parser/src/app/mod.rs +++ b/yazi-parser/src/app/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(deprecate lua mouse plugin quit reflow title update_progress); +yazi_macro::mod_flat!(deprecate dnd lua mouse plugin quit reflow title update_progress); diff --git a/yazi-parser/src/spark/spark.rs b/yazi-parser/src/spark/spark.rs index a2f6234f..48f2c080 100644 --- a/yazi-parser/src/spark/spark.rs +++ b/yazi-parser/src/spark/spark.rs @@ -11,6 +11,7 @@ pub enum Spark<'a> { AppAcceptPayload(yazi_dds::Payload<'a>), AppBootstrap(crate::VoidForm), AppDeprecate(crate::app::DeprecateForm), + AppDnd(crate::app::DndForm), AppFocus(crate::VoidForm), AppLua(crate::app::LuaForm), AppMouse(crate::app::MouseForm), @@ -199,6 +200,7 @@ impl<'a> IntoLua for Spark<'a> { Self::AppAcceptPayload(b) => b.into_lua(lua), Self::AppBootstrap(b) => b.into_lua(lua), Self::AppDeprecate(b) => b.into_lua(lua), + Self::AppDnd(b) => b.into_lua(lua), Self::AppFocus(b) => b.into_lua(lua), Self::AppLua(b) => b.into_lua(lua), Self::AppMouse(b) => b.into_lua(lua), @@ -376,6 +378,7 @@ try_from_spark!( // App try_from_spark!(crate::ArrowForm, mgr:arrow, mgr:tab_swap); try_from_spark!(crate::app::DeprecateForm, app:deprecate); +try_from_spark!(crate::app::DndForm, app:dnd); try_from_spark!(crate::app::LuaForm, app:lua); try_from_spark!(crate::app::MouseForm, app:mouse); try_from_spark!(crate::app::PluginForm, app:plugin, app:plugin_do); diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 882a6de3..60cbf122 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -36,18 +36,19 @@ yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } # External dependencies -ansi-to-tui = { workspace = true } -anyhow = { workspace = true } -futures = { workspace = true } -mlua = { workspace = true } -paste = { workspace = true } -ratatui = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -tokio-stream = { workspace = true } -tracing = { workspace = true } -twox-hash = { workspace = true } -unicode-width = { workspace = true } +ansi-to-tui = { workspace = true } +anyhow = { workspace = true } +futures = { workspace = true } +mlua = { workspace = true } +paste = { workspace = true } +percent-encoding = { workspace = true } +ratatui = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +twox-hash = { workspace = true } +unicode-width = { workspace = true } [target."cfg(unix)".dependencies] libc = { workspace = true } diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 74f73fe4..01ea28c6 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -1,5 +1,7 @@ Current = { _id = "current", + _dragging = false, + _dropping = false, } function Current:new(area, tab) @@ -24,6 +26,14 @@ function Current:empty() } end +function Current:dropping() + if Current._dropping then + return Tip:new(self._area, "Drop to move here…"):redraw() + else + return {} + end +end + function Current:reflow() return { self } end function Current:redraw() @@ -44,6 +54,7 @@ function Current:redraw() return { ui.List(left):area(self._area), ui.Text(right):area(self._area):align(ui.Align.RIGHT), + table.unpack(self:dropping()), } end @@ -62,3 +73,31 @@ end function Current:scroll(event, step) ya.emit("arrow", { step }) end function Current:touch(event, step) end + +function Current:drag(event) + if event.type == "offer" then + Current._dragging = require("dnd").offer_uri_list() + elseif event.type == "end" or event.type == "error" then + Current._dragging = false + end +end + +function Current:drop(event) + if Current._dragging then + return + elseif event.type == "enter" then + rt.tty:queue("ConfirmDrop", { type = "move", mimes = { "text/uri-list" } }) + elseif event.type == "ready" then + rt.tty:queue("StartDrop", { idx = 1 }) + elseif event.type == "arrive" then + rt.tty:queue("FinishDrop", { type = "move" }) + require("dnd").cut_uri_list(event.data) + end + rt.tty:flush() + + local d = event.type == "enter" + if Current._dropping ~= d then + Current._dropping = d + ui.render() + end +end diff --git a/yazi-plugin/preset/components/rail.lua b/yazi-plugin/preset/components/rail.lua index 40d938d8..305f9694 100644 --- a/yazi-plugin/preset/components/rail.lua +++ b/yazi-plugin/preset/components/rail.lua @@ -24,6 +24,10 @@ function Rail:scroll(event, step) end function Rail:touch(event, step) end function Rail:drag(event) + if event.type ~= "legacy" then + return + end + local c, x, parent, current, preview = self._chunks, 0, 0, 0, 0 if self._id == "rail-left" then x = math.min(event.x, c[2].right - 2) diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index 201d37fa..02e00858 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -1,6 +1,7 @@ Root = { _id = "root", _dragging = nil, + _dropping = nil, } function Root:new(area) @@ -50,7 +51,7 @@ end -- Mouse events function Root:click(event, up) - local c = Root._dragging or ya.child_at(ui.Rect { x = event.x, y = event.y }, self:reflow()) + local c = ya.child_at(ui.Rect { x = event.x, y = event.y }, self:reflow()) Root._dragging = not up and c or nil if tostring(cx.layer) == "mgr" then @@ -84,3 +85,16 @@ function Root:drag(event) local c = Root._dragging return c and c.drag and c:drag(event) end + +function Root:drop(event) + local d = Root._dropping + local c = event.x and ya.child_at(ui.Rect { x = event.x, y = event.y }, self:reflow()) or d + if d and d.drop and d._id ~= c._id then + d:drop { type = "leave" } + end + + Root._dropping = c + if tostring(cx.layer) == "mgr" then + return c and c.drop and c:drop(event) + end +end diff --git a/yazi-plugin/preset/components/tip.lua b/yazi-plugin/preset/components/tip.lua new file mode 100644 index 00000000..e4c0ed36 --- /dev/null +++ b/yazi-plugin/preset/components/tip.lua @@ -0,0 +1,29 @@ +Tip = { + _id = "tip", +} + +function Tip:new(area, text) + local me = setmetatable({ _area = area, _text = text }, { __index = self }) + me:layout() + return me +end + +function Tip:layout() + self._chunks = ui.Layout() + :direction(ui.Layout.VERTICAL) + :constraints({ + ui.Constraint.Fill(1), + ui.Constraint.Length(1), + ui.Constraint.Fill(1), + }) + :split(self._area) +end + +function Tip:reflow() return {} end + +function Tip:redraw() + return { + ui.Clear(self._chunks[2]), + ui.Text(self._text):area(self._chunks[2]):align(ui.Align.CENTER):fg("black"):bg("yellow"):bold(), + } +end diff --git a/yazi-plugin/preset/plugins/dnd.lua b/yazi-plugin/preset/plugins/dnd.lua new file mode 100644 index 00000000..039cf90b --- /dev/null +++ b/yazi-plugin/preset/plugins/dnd.lua @@ -0,0 +1,45 @@ +local M = {} + +function M.selected_uri_list() + local paths = {} + for _, u in pairs(cx.active.selected) do + paths[#paths + 1] = "file://" .. ya.percent_encode(tostring(u.path)) + end + if #paths == 0 and cx.active.current.hovered then + paths[1] = "file://" .. ya.percent_encode(tostring(cx.active.current.hovered.path)) + end + return paths +end + +function M.cut_uri_list(list) + for line in list:gmatch("[^\r\n]+") do + if line:sub(1, 7) ~= "file://" then + goto continue + end + + local from = Url(ya.percent_decode(line:sub(8))) + if from.name then + local to = cx.active.current.cwd:join(from.name) + ya.async(function() ya.task("cut", { from = from, to = to }):spawn() end) + end + + ::continue:: + end +end + +function M.offer_uri_list() + local list = M.selected_uri_list() + if #list == 0 then + return false + end + + local icon = string.format("%d selected file(s)", #list) + rt.tty:queue("ConfirmDrag", { type = "either", mimes = { "text/uri-list" } }) + rt.tty:queue("PresentDrag", { idx = 0, data = table.concat(list, "\r\n") }) + rt.tty:queue("PresentDragIcon", { format = 0, opacity = 0, width = 6, height = 4, data = icon }) + rt.tty:queue("StartDrag", {}) + rt.tty:flush() + return true +end + +return M diff --git a/yazi-plugin/src/runtime/runtime.rs b/yazi-plugin/src/runtime/runtime.rs index 757ec015..2e2cc163 100644 --- a/yazi-plugin/src/runtime/runtime.rs +++ b/yazi-plugin/src/runtime/runtime.rs @@ -1,5 +1,5 @@ use mlua::{IntoLua, Lua, LuaSerdeExt, Value}; -use yazi_binding::{Composer, ComposerGet, ComposerSet, SER_OPT, Url, config::{OpenRules, Opener}, elements::Wrap}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, SER_OPT, Tty, Url, config::{OpenRules, Opener}, elements::Wrap}; use yazi_boot::ARGS; use yazi_config::YAZI; @@ -7,6 +7,7 @@ pub fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"args" => args().into_lua(lua)?, + b"tty" => Tty.into_lua(lua)?, b"term" => super::term().into_lua(lua)?, b"mgr" => mgr().into_lua(lua)?, b"open" => open().into_lua(lua)?, diff --git a/yazi-plugin/src/standard.rs b/yazi-plugin/src/standard.rs index 811b1673..0dbbb8e5 100644 --- a/yazi-plugin/src/standard.rs +++ b/yazi-plugin/src/standard.rs @@ -59,6 +59,7 @@ fn stage_1(lua: &Lua) -> Result<()> { lua.load(preset!("components/tab")).set_name("tab.lua").exec()?; lua.load(preset!("components/tabs")).set_name("tabs.lua").exec()?; lua.load(preset!("components/tasks")).set_name("tasks.lua").exec()?; + lua.load(preset!("components/tip")).set_name("tip.lua").exec()?; Ok(()) } diff --git a/yazi-plugin/src/utils/tasks.rs b/yazi-plugin/src/utils/tasks.rs index 302fe1b6..4c5c63e7 100644 --- a/yazi-plugin/src/utils/tasks.rs +++ b/yazi-plugin/src/utils/tasks.rs @@ -8,7 +8,10 @@ impl Utils { pub(super) fn task(lua: &Lua) -> mlua::Result { lua.create_function(|lua, (kind, value): (mlua::String, Value)| { Ok(TaskOpt(match &*kind.as_bytes() { + b"cut" => tasks::TaskOpt::Cut(<_>::from_lua(value, lua)?), + b"plugin" => tasks::TaskOpt::Plugin(<_>::from_lua(value, lua)?), + _ => Err(format!("unsupported spawn kind: {}", kind.display()).into_lua_err())?, })) }) diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index 8e3e8e95..f5522195 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -1,5 +1,8 @@ +use std::borrow::Cow; + use mlua::{Function, Lua}; use twox_hash::XxHash3_128; +use yazi_shim::RFC_3986; use yazi_widgets::CLIPBOARD; use super::Utils; @@ -33,4 +36,24 @@ impl Utils { } }) } + + pub(super) fn percent_decode(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, s: mlua::String| { + let b = s.as_bytes(); + match percent_encoding::percent_decode(&b).into() { + Cow::Borrowed(_) => Ok(s), + Cow::Owned(b) => lua.create_external_string(b), + } + }) + } + + pub(super) fn percent_encode(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, s: mlua::String| { + let b = s.as_bytes(); + match percent_encoding::percent_encode(&b, RFC_3986).into() { + Cow::Borrowed(_) => Ok(s), + Cow::Owned(s) => lua.create_external_string(s), + } + }) + } } diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 10245716..3bcbb23a 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -66,6 +66,8 @@ pub fn compose( b"hash" => Utils::hash(lua)?, b"quote" => Utils::quote(lua)?, b"clipboard" => Utils::clipboard(lua)?, + b"percent_encode" => Utils::percent_encode(lua)?, + b"percent_decode" => Utils::percent_decode(lua)?, // Time b"time" => Utils::time(lua)?, diff --git a/yazi-runner/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs index d5c99e18..5dc41e14 100644 --- a/yazi-runner/src/loader/loader.rs +++ b/yazi-runner/src/loader/loader.rs @@ -30,6 +30,7 @@ impl Default for Loader { ("archive".to_owned(), preset!("plugins/archive").into()), ("code".to_owned(), preset!("plugins/code").into()), ("dds".to_owned(), preset!("plugins/dds").into()), + ("dnd".to_owned(), preset!("plugins/dnd").into()), ("empty".to_owned(), preset!("plugins/empty").into()), ("extract".to_owned(), preset!("plugins/extract").into()), ("file".to_owned(), preset!("plugins/file").into()), @@ -71,6 +72,7 @@ impl Default for Loader { ("tab".to_owned(), [][..].into()), ("tabs".to_owned(), [][..].into()), ("tasks".to_owned(), [][..].into()), + ("tip".to_owned(), [][..].into()), // Reserved ("inline".to_owned(), [][..].into()), ]); diff --git a/yazi-scheduler/src/file/in.rs b/yazi-scheduler/src/file/in.rs index 09101ef8..2f6b486f 100644 --- a/yazi-scheduler/src/file/in.rs +++ b/yazi-scheduler/src/file/in.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, mem, path::PathBuf}; +use mlua::{ExternalError, FromLua, Lua, Value}; use tokio::sync::mpsc; use yazi_fs::cha::Cha; use yazi_shared::{Id, url::{UrlBuf, UrlLike}}; @@ -172,7 +173,7 @@ impl FileInCopy { // --- Cut #[derive(Clone, Debug)] -pub(crate) struct FileInCut { +pub struct FileInCut { pub(crate) id: Id, pub(crate) from: UrlBuf, pub(crate) to: UrlBuf, @@ -203,6 +204,19 @@ impl Drop for FileInCut { } impl FileInCut { + pub fn new(from: UrlBuf, to: UrlBuf, force: bool) -> Self { + Self { + follow: !from.scheme().covariant(to.scheme()), + id: Id::ZERO, + from, + to, + force, + cha: None, + retry: 0, + drop: None, + } + } + pub(super) fn into_link(mut self) -> FileInLink { FileInLink { id: self.id, @@ -222,6 +236,20 @@ impl FileInCut { } } +impl FromLua for FileInCut { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + let Value::Table(t) = value else { + return Err("constructing FileInCut from non-table value".into_lua_err()); + }; + + Ok(Self::new( + t.raw_get::("from")?.into(), + t.raw_get::("to")?.into(), + t.raw_get("force")?, + )) + } +} + // --- Link #[derive(Clone, Debug)] pub(crate) struct FileInLink { diff --git a/yazi-scheduler/src/plugin/in.rs b/yazi-scheduler/src/plugin/in.rs index 0d0dc423..371c60f4 100644 --- a/yazi-scheduler/src/plugin/in.rs +++ b/yazi-scheduler/src/plugin/in.rs @@ -72,9 +72,9 @@ impl TaskIn for PluginInEntry { } } -impl PluginInEntry { - pub(crate) fn into_job(self) -> EntryJob { - EntryJob { id: self.id, args: self.args, plugin: self.plugin } +impl From for EntryJob { + fn from(value: PluginInEntry) -> Self { + Self { id: value.id, args: value.args, plugin: value.plugin } } } diff --git a/yazi-scheduler/src/plugin/plugin.rs b/yazi-scheduler/src/plugin/plugin.rs index 43ee7f1b..876fd68e 100644 --- a/yazi-scheduler/src/plugin/plugin.rs +++ b/yazi-scheduler/src/plugin/plugin.rs @@ -20,7 +20,7 @@ impl Plugin { pub(crate) async fn entry(&self, task: PluginInEntry) -> Result<(), PluginOutEntry> { let id = task.id; - RUNNER.entry(task.into_job()).await?; + RUNNER.entry(task.into()).await?; Ok(self.ops.out(id, PluginOutEntry::Succ)) } } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index f569a4bc..35bb3693 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -65,17 +65,16 @@ impl Scheduler { } } - pub fn file_cut(&self, from: UrlBuf, to: UrlBuf, force: bool) { - let follow = !from.scheme().covariant(to.scheme()); - let mut r#in = - FileInCut { id: Id::ZERO, from, to, force, cha: None, follow, retry: 0, drop: None }; + pub fn file_cut(&self, mut r#in: FileInCut) -> Id { + let id = self.add(&mut r#in, |t| t.id); - self.add(&mut r#in, |_| ()); if r#in.to.try_starts_with(&r#in.from).unwrap_or(false) && !r#in.to.covariant(&r#in.from) { self.ops.out(r#in.id, FileOutCut::Fail("Cannot cut directory into itself".to_owned())); } else { self.file.submit(r#in, LOW); } + + id } pub fn file_copy(&self, from: UrlBuf, to: UrlBuf, force: bool, follow: bool) { diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 7dc674df..cdc99fba 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -23,7 +23,7 @@ dyn-clone = { workspace = true } foldhash = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } -memchr = "2.8.0" +memchr = "2.8.1" ordered-float = { workspace = true } parking_lot = { workspace = true } paste = { workspace = true } diff --git a/yazi-shim/Cargo.toml b/yazi-shim/Cargo.toml index b6154dea..ad580071 100644 --- a/yazi-shim/Cargo.toml +++ b/yazi-shim/Cargo.toml @@ -20,15 +20,17 @@ vendored-lua = [ "mlua/vendored" ] yazi-macro = { path = "../yazi-macro", version = "26.5.6" } # External dependencies -arc-swap = { workspace = true } -hashbrown = { workspace = true } -mlua = { workspace = true } -ratatui = { workspace = true } -serde = { workspace = true } -thiserror = { workspace = true } -toml = { workspace = true } -twox-hash = { workspace = true } -unicode-width = { workspace = true } +arc-swap = { workspace = true } +base64 = { workspace = true } +hashbrown = { workspace = true } +mlua = { workspace = true } +percent-encoding = { workspace = true } +ratatui = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +toml = { workspace = true } +twox-hash = { workspace = true } +unicode-width = { workspace = true } [dependencies.unicode-segmentation] version = "1.13.2" diff --git a/yazi-shim/src/base64.rs b/yazi-shim/src/base64.rs new file mode 100644 index 00000000..c3f6dae0 --- /dev/null +++ b/yazi-shim/src/base64.rs @@ -0,0 +1,8 @@ +use base64::{alphabet::STANDARD, engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig}}; + +pub const BASE64_SANE: GeneralPurpose = GeneralPurpose::new( + &STANDARD, + GeneralPurposeConfig::new() + .with_encode_padding(false) + .with_decode_padding_mode(DecodePaddingMode::Indifferent), +); diff --git a/yazi-shim/src/lib.rs b/yazi-shim/src/lib.rs index ad4cb83b..4264d4e1 100644 --- a/yazi-shim/src/lib.rs +++ b/yazi-shim/src/lib.rs @@ -1,6 +1,6 @@ yazi_macro::mod_pub!(arc_swap cell mlua path ratatui serde strum toml vec); -yazi_macro::mod_flat!(twox utf8); +yazi_macro::mod_flat!(base64 percent_encoding twox utf8); #[cfg(windows)] yazi_macro::mod_flat!(win32); diff --git a/yazi-shim/src/mlua/mod.rs b/yazi-shim/src/mlua/mod.rs index ff9b6f21..65220acf 100644 --- a/yazi-shim/src/mlua/mod.rs +++ b/yazi-shim/src/mlua/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(traits); +yazi_macro::mod_flat!(sequence string traits); diff --git a/yazi-shim/src/mlua/sequence.rs b/yazi-shim/src/mlua/sequence.rs new file mode 100644 index 00000000..d6d0f108 --- /dev/null +++ b/yazi-shim/src/mlua/sequence.rs @@ -0,0 +1,49 @@ +use std::marker::PhantomData; + +use mlua::{FromLua, Integer, IntoLua, Lua, Table, ffi}; + +// mlua's `TableSequence<'_, V>` holds `table: &'a Table` and cannot be Clone. +// By owning both `Lua` and `Table` (both are reference-counted; Clone = two +// atomic increments) we get a Clone-able sequence iterator with zero heap +// allocation. +// +// `next()` acquires the Lua lock once per element (same as `raw_get`), but uses +// `lua_rawgeti` to skip the separate integer key push that `raw_get` requires. +// Holding the lock across all elements (as `TableSequence` does) is not +// possible here because `LuaGuard` is `pub(crate)` and cannot be stored in +// Clone types outside mlua. +#[derive(Clone)] +pub struct SequenceIter { + pub(super) lua: Lua, + pub(super) table: Table, + pub(super) index: Integer, + pub(super) _phantom: PhantomData, +} + +impl Iterator for SequenceIter { + type Item = mlua::Result; + + fn next(&mut self) -> Option> { + self.index += 1; + self.lua.exec_raw_lua(|lua| { + unsafe { + let state = lua.state(); + if ffi::lua_checkstack(state, 2) == 0 { + return Some(Err(mlua::Error::StackError)); + } + + if let Err(e) = <&Table as IntoLua>::push_into_stack(&self.table, lua) { + return Some(Err(e)); + }; + + let result = match ffi::lua_rawgeti(state, -1, self.index) { + ffi::LUA_TNIL => None, + _ => Some(::from_stack(-1, lua)), + }; + + ffi::lua_pop(state, 2); // table + value + result + } + }) + } +} diff --git a/yazi-shim/src/mlua/string.rs b/yazi-shim/src/mlua/string.rs new file mode 100644 index 00000000..861e9d84 --- /dev/null +++ b/yazi-shim/src/mlua/string.rs @@ -0,0 +1,26 @@ +use std::{fmt, ops::Deref}; + +use mlua::{FromLua, IntoLua, Lua, Value}; + +#[derive(Clone)] +pub struct ByteString(mlua::String); + +impl Deref for ByteString { + type Target = mlua::String; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl fmt::Display for ByteString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.display().fmt(f) } +} + +impl FromLua for ByteString { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + Ok(ByteString(mlua::String::from_lua(value, lua)?)) + } +} + +impl IntoLua for ByteString { + fn into_lua(self, lua: &Lua) -> mlua::Result { self.0.into_lua(lua) } +} diff --git a/yazi-shim/src/mlua/traits.rs b/yazi-shim/src/mlua/traits.rs index 992430b0..35b98e79 100644 --- a/yazi-shim/src/mlua/traits.rs +++ b/yazi-shim/src/mlua/traits.rs @@ -1,7 +1,10 @@ -use mlua::{ExternalError, Table, Value}; +use std::marker::PhantomData; -use crate::toml::DeserializeOverWith; +use mlua::{ExternalError, FromLua, Lua, Table, Value}; +use crate::{mlua::SequenceIter, toml::DeserializeOverWith}; + +// --- DeserializeOverLua pub trait DeserializeOverLua: DeserializeOverWith { fn deserialize_over_lua(self, table: &Table) -> mlua::Result { let de = mlua::serde::Deserializer::new(Value::Table(table.clone())); @@ -10,3 +13,19 @@ pub trait DeserializeOverLua: DeserializeOverWith { } impl DeserializeOverLua for T {} + +// --- IntoLua +pub trait LuaTableExt { + fn sequence_iter(&self, lua: &Lua) -> SequenceIter; +} + +impl LuaTableExt for Table { + fn sequence_iter(&self, lua: &Lua) -> SequenceIter { + SequenceIter { + lua: lua.clone(), + table: self.clone(), + index: 0, + _phantom: PhantomData, + } + } +} diff --git a/yazi-shim/src/percent_encoding.rs b/yazi-shim/src/percent_encoding.rs new file mode 100644 index 00000000..24e9ce54 --- /dev/null +++ b/yazi-shim/src/percent_encoding.rs @@ -0,0 +1,20 @@ +use percent_encoding::{AsciiSet, CONTROLS}; + +// RFC 3986 path component: encode everything that is not a safe path character. +// Safe chars: unreserved (A-Za-z0-9 -._~) + sub-delims (!$&'()*+,;=) + : @ / +pub const RFC_3986: &AsciiSet = &CONTROLS + .add(b' ') + .add(b'"') + .add(b'#') + .add(b'%') + .add(b'<') + .add(b'>') + .add(b'?') + .add(b'[') + .add(b'\\') + .add(b']') + .add(b'^') + .add(b'`') + .add(b'{') + .add(b'|') + .add(b'}'); diff --git a/yazi-term/src/event/dnd.rs b/yazi-term/src/event/dnd.rs index 763140ac..d3c81b6a 100644 --- a/yazi-term/src/event/dnd.rs +++ b/yazi-term/src/event/dnd.rs @@ -1,6 +1,8 @@ use std::str::SplitWhitespace; -use strum::FromRepr; +use base64::Engine; +use strum::{FromRepr, IntoStaticStr}; +use yazi_shim::BASE64_SANE; use crate::parser::StateOsc72; @@ -13,12 +15,14 @@ pub enum DndEvent { DragLand, DragEnd(DndDragEnd), DragSend(DndDragSend), + DragError(DndDragError), // Drop DropEnter(DndDropEnter), DropLeave, DropReady(DndDropReady), - DropData(DndDropData), + DropArrive(DndDropArrive), + DropError(DndDropError), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -47,6 +51,13 @@ pub struct DndDragSend { pub idx: u8, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDragError { + pub idx: u8, + pub name: String, + pub desc: String, +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct DndDropEnter { pub x: u32, @@ -64,12 +75,96 @@ pub struct DndDropReady { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct DndDropData { - pub idx: u8, - pub payload: Vec, +pub struct DndDropArrive { + pub idx: u8, + pub data: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DndDropError { + pub idx: u8, + pub name: String, + pub desc: String, } impl DndEvent { + pub fn r#type(&self) -> &'static str { + match self { + Self::DragOffer(_) => "offer", + Self::DragAccept(_) => "accept", + Self::DragChange(_) => "change", + Self::DragLand => "land", + Self::DragEnd(_) => "end", + Self::DragSend(_) => "send", + Self::DragError(_) => "error", + + Self::DropEnter(_) => "enter", + Self::DropLeave => "leave", + Self::DropReady(_) => "ready", + Self::DropArrive(_) => "arrive", + Self::DropError(_) => "error", + } + } + + pub fn x(&self) -> Option { + match self { + Self::DragOffer(e) => Some(e.x), + Self::DropEnter(e) => Some(e.x), + Self::DropReady(e) => Some(e.x), + _ => None, + } + } + + pub fn y(&self) -> Option { + match self { + Self::DragOffer(e) => Some(e.y), + Self::DropEnter(e) => Some(e.y), + Self::DropReady(e) => Some(e.y), + _ => None, + } + } + + pub fn idx(&self) -> Option { + match self { + Self::DragAccept(e) => Some(e.idx), + Self::DragSend(e) => Some(e.idx), + Self::DragError(e) => Some(e.idx), + Self::DropArrive(e) => Some(e.idx), + Self::DropError(e) => Some(e.idx), + _ => None, + } + } + + pub fn op(&self) -> Option { + match self { + Self::DragChange(e) => Some(e.op), + Self::DropEnter(e) => Some(e.op), + Self::DropReady(e) => Some(e.op), + _ => None, + } + } + + pub fn mimes(&self) -> Option<&DndMimeList> { + match self { + Self::DropEnter(e) => Some(&e.mimes), + Self::DropReady(e) => Some(&e.mimes), + _ => None, + } + } + + pub fn is_drag(&self) -> bool { + matches!( + self, + Self::DragOffer(_) + | Self::DragAccept(_) + | Self::DragChange(_) + | Self::DragLand + | Self::DragEnd(_) + | Self::DragSend(_) + | Self::DragError(_) + ) + } + pub(crate) fn from_state(s: StateOsc72) -> Option { Some(match s.r#type.unwrap_or_default() { // Drag @@ -79,6 +174,10 @@ impl DndEvent { b'e' if s.x? == 3 => Self::DragLand, b'e' if s.x? == 4 => Self::DragEnd(DndDragEnd { canceled: s.y? != 0 }), b'e' if s.x? == 5 => Self::DragSend(DndDragSend { idx: s.y?.try_into().ok()? }), + b'E' => { + let (name, desc) = parse_error(s.payload)?; + Self::DragError(DndDragError { idx: s.y?.try_into().ok()?, name, desc }) + } // Drop b'm' if s.x == Some(-1) && s.y == Some(-1) => Self::DropLeave, @@ -94,7 +193,14 @@ impl DndEvent { op: DndOp::from_repr(s.op?)?, mimes: DndMimeList::new(s.payload)?, }), - b'r' => Self::DropData(DndDropData { idx: s.x?.try_into().ok()?, payload: s.payload }), + b'r' => Self::DropArrive(DndDropArrive { + idx: s.x?.try_into().ok()?, + data: BASE64_SANE.decode(&s.payload).ok()?, + }), + b'R' => { + let (name, desc) = parse_error(s.payload)?; + Self::DropError(DndDropError { idx: s.x?.try_into().ok()?, name, desc }) + } _ => return None, }) @@ -102,7 +208,7 @@ impl DndEvent { } // --- Operation -#[derive(Clone, Copy, Debug, Eq, FromRepr, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, FromRepr, IntoStaticStr, PartialEq)] #[repr(u8)] pub enum DndOp { Copy = 1, @@ -119,3 +225,13 @@ impl DndMimeList { pub fn iter(&self) -> SplitWhitespace<'_> { self.0.split_whitespace() } } + +// --- Error payload parsing +fn parse_error(payload: Vec) -> Option<(String, String)> { + let s = String::from_utf8(payload).ok()?; + Some(match s.split_once(':') { + Some((name, desc)) => (name.to_owned(), desc.to_owned()), + None if s.is_empty() => return None, + None => (s, String::new()), + }) +} diff --git a/yazi-term/src/sequence/dnd.rs b/yazi-term/src/sequence/dnd.rs index 57f31e68..681cf612 100644 --- a/yazi-term/src/sequence/dnd.rs +++ b/yazi-term/src/sequence/dnd.rs @@ -1,6 +1,9 @@ use std::{fmt::{self, Display}, str}; -use base64::{Engine, engine::general_purpose}; +use base64::Engine; +use yazi_shim::BASE64_SANE; + +use super::traits::Mimelist; /// Enable drag support: `OSC 72 ; t=o:x=1 ; machine id ST` pub struct EnableDrag<'a>(pub &'a str); @@ -12,11 +15,11 @@ impl Display for EnableDrag<'_> { } /// Enable drop support: `OSC 72 ; t=a ; MIME list ST` -pub struct EnableDrop<'a>(pub &'a [&'a str]); +pub struct EnableDrop(pub M); -impl Display for EnableDrop<'_> { +impl Display for EnableDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\x1b]72;t=a;{}\x1b\\", ListDndMimes(self.0)) + write!(f, "\x1b]72;t=a;{}\x1b\\", ListDndMimes(self.0.clone())) } } @@ -35,35 +38,35 @@ impl Display for DisableDrop { } /// Confirm drag: `OSC 72 ; t=o:o=operation ST` -pub enum ConfirmDrag<'a> { - Copy(&'a [&'a str]), - Move(&'a [&'a str]), - Either(&'a [&'a str]), +pub enum ConfirmDrag { + Copy(M), + Move(M), + Either(M), } -impl Display for ConfirmDrag<'_> { +impl Display for ConfirmDrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes)), - Self::Move(mimes) => write!(f, "\x1b]72;t=o:o=2;{}\x1b\\", ListDndMimes(mimes)), - Self::Either(mimes) => write!(f, "\x1b]72;t=o:o=3;{}\x1b\\", ListDndMimes(mimes)), + Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes.clone())), + Self::Move(mimes) => write!(f, "\x1b]72;t=o:o=2;{}\x1b\\", ListDndMimes(mimes.clone())), + Self::Either(mimes) => write!(f, "\x1b]72;t=o:o=3;{}\x1b\\", ListDndMimes(mimes.clone())), } } } /// Confirm dropped data: `OSC 72 ; t=m:o=O ; MIME list ST` -pub enum ConfirmDrop<'a> { +pub enum ConfirmDrop { Reject, - Copy(&'a [&'a str]), - Move(&'a [&'a str]), + Copy(M), + Move(M), } -impl Display for ConfirmDrop<'_> { +impl Display for ConfirmDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"), - Self::Copy(mimes) => write!(f, "\x1b]72;t=m:o=1;{}\x1b\\", ListDndMimes(mimes)), - Self::Move(mimes) => write!(f, "\x1b]72;t=m:o=2;{}\x1b\\", ListDndMimes(mimes)), + Self::Copy(mimes) => write!(f, "\x1b]72;t=m:o=1;{}\x1b\\", ListDndMimes(mimes.clone())), + Self::Move(mimes) => write!(f, "\x1b]72;t=m:o=2;{}\x1b\\", ListDndMimes(mimes.clone())), } } } @@ -89,7 +92,7 @@ pub struct PresentDrag<'a>(pub u8, pub &'a [u8]); impl Display for PresentDrag<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let b64 = general_purpose::STANDARD_NO_PAD.encode(self.1).into_bytes(); + let b64 = BASE64_SANE.encode(self.1).into_bytes(); let chunks = b64.len().div_ceil(4096); for (i, chunk) in b64.chunks(4096).enumerate() { @@ -112,12 +115,12 @@ pub struct PresentDragIcon<'a> { pub opacity: u16, pub width: u32, pub height: u32, - pub payload: &'a [u8], + pub data: &'a [u8], } impl Display for PresentDragIcon<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let b64 = general_purpose::STANDARD_NO_PAD.encode(self.payload).into_bytes(); + let b64 = BASE64_SANE.encode(self.data).into_bytes(); let chunks = b64.len().div_ceil(4096); for (i, chunk) in b64.chunks(4096).enumerate() { @@ -155,11 +158,11 @@ impl Display for FinishDrop { } /// Write MIME types separated by spaces. -struct ListDndMimes<'a>(&'a [&'a str]); +struct ListDndMimes(pub M); -impl Display for ListDndMimes<'_> { +impl Display for ListDndMimes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (i, &m) in self.0.iter().enumerate() { + for (i, m) in self.0.clone().into_iter().enumerate() { if i != 0 { write!(f, " ")?; } diff --git a/yazi-term/src/sequence/mod.rs b/yazi-term/src/sequence/mod.rs index 401188ba..defec32c 100644 --- a/yazi-term/src/sequence/mod.rs +++ b/yazi-term/src/sequence/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(clipboard csi_u cursor dnd erase mode query r#if restore_background set_background style sync); +yazi_macro::mod_flat!(clipboard csi_u cursor dnd erase mode query r#if restore_background set_background style sync traits); diff --git a/yazi-term/src/sequence/traits.rs b/yazi-term/src/sequence/traits.rs new file mode 100644 index 00000000..9911d60b --- /dev/null +++ b/yazi-term/src/sequence/traits.rs @@ -0,0 +1,11 @@ +use std::fmt::Display; + +/// Types that can be iterated to produce a list of MIME types. +pub(super) trait Mimelist: IntoIterator + Clone {} + +impl Mimelist for T +where + T: IntoIterator + Clone, + T::Item: Display, +{ +} diff --git a/yazi-tty/src/writer.rs b/yazi-tty/src/writer.rs index ca997d63..e69d7f9f 100644 --- a/yazi-tty/src/writer.rs +++ b/yazi-tty/src/writer.rs @@ -1,4 +1,4 @@ -use std::{fmt::{self, Debug}, io::{self, BufWriter, Write}, ops::Deref}; +use std::{fmt::{self, Debug}, io::{self, BufWriter}, ops::Deref}; use parking_lot::Mutex; @@ -23,12 +23,6 @@ impl io::Write for TtyWriter<'_> { fn flush(&mut self) -> io::Result<()> { self.0.lock().flush() } } -impl fmt::Write for TtyWriter<'_> { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.0.lock().write_all(s.as_bytes()).map_err(|_| fmt::Error) - } -} - #[cfg(unix)] impl std::os::fd::AsFd for TtyWriter<'_> { fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> { From 6c30430666caa14d598a4cd4dd8e26d0a8596f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Thu, 28 May 2026 18:07:31 +0800 Subject: [PATCH 04/28] fix: a TOCTOU race condition while creating temporary directories (#4008) --- yazi-actor/src/lives/behavior.rs | 29 +++++++++++++++++ yazi-actor/src/lives/mod.rs | 2 +- yazi-actor/src/lives/tasks.rs | 14 +++++--- yazi-actor/src/tasks/spawn.rs | 1 + yazi-config/src/preview/preview.rs | 13 ++------ yazi-dds/src/stream.rs | 12 +++---- yazi-fs/src/fns.rs | 47 ++++++++++++++++++++++++++- yazi-plugin/preset/components/tip.lua | 5 +-- yazi-plugin/preset/plugins/dnd.lua | 1 + 9 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 yazi-actor/src/lives/behavior.rs diff --git a/yazi-actor/src/lives/behavior.rs b/yazi-actor/src/lives/behavior.rs new file mode 100644 index 00000000..c97f3c1e --- /dev/null +++ b/yazi-actor/src/lives/behavior.rs @@ -0,0 +1,29 @@ +use std::ops::Deref; + +use mlua::{AnyUserData, UserData, UserDataMethods}; + +use super::{Lives, PtrCell}; + +pub(super) struct Behavior { + inner: PtrCell, +} + +impl Deref for Behavior { + type Target = yazi_scheduler::Behavior; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Behavior { + pub(super) fn make(inner: &yazi_scheduler::Behavior) -> mlua::Result { + let inner = PtrCell::from(inner); + + Lives::scoped_userdata(Self { inner }) + } +} + +impl UserData for Behavior { + fn add_methods>(methods: &mut M) { + methods.add_method("reset", |_, me, ()| Ok(me.reset())); + } +} diff --git a/yazi-actor/src/lives/mod.rs b/yazi-actor/src/lives/mod.rs index d6c82311..594dc7fd 100644 --- a/yazi-actor/src/lives/mod.rs +++ b/yazi-actor/src/lives/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks which yanked); +yazi_macro::mod_flat!(behavior core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks which yanked); diff --git a/yazi-actor/src/lives/tasks.rs b/yazi-actor/src/lives/tasks.rs index e547c138..b19da6cb 100644 --- a/yazi-actor/src/lives/tasks.rs +++ b/yazi-actor/src/lives/tasks.rs @@ -4,13 +4,14 @@ use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; use yazi_binding::{SER_OPT, cached_field}; use super::{Lives, PtrCell}; -use crate::lives::TaskSnap; +use crate::lives::{Behavior, TaskSnap}; pub(super) struct Tasks { inner: PtrCell, - v_snaps: Option, - v_summary: Option, + v_behavior: Option, + v_snaps: Option, + v_summary: Option, } impl Deref for Tasks { @@ -24,8 +25,9 @@ impl Tasks { Lives::scoped_userdata(Self { inner: inner.into(), - v_snaps: None, - v_summary: None, + v_behavior: None, + v_snaps: None, + v_summary: None, }) } } @@ -34,6 +36,8 @@ impl UserData for Tasks { fn add_fields>(fields: &mut F) { fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); + cached_field!(fields, behavior, |_, me| Behavior::make(&me.scheduler.behavior)); + cached_field!(fields, snaps, |lua, me| { let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?; for snap in &me.snaps { diff --git a/yazi-actor/src/tasks/spawn.rs b/yazi-actor/src/tasks/spawn.rs index 963bd757..efa4b535 100644 --- a/yazi-actor/src/tasks/spawn.rs +++ b/yazi-actor/src/tasks/spawn.rs @@ -16,6 +16,7 @@ impl Actor for Spawn { fn act(cx: &mut Ctx, form: Self::Form) -> Result { succ!(match form.opt { TaskOpt::Cut(r#in) => cx.tasks.scheduler.file_cut(r#in), + TaskOpt::Plugin(r#in) => cx.tasks.scheduler.plugin_entry(r#in), }) } diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index 4556a110..18dfcd41 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -1,11 +1,9 @@ -#[cfg(unix)] -use std::os::unix::fs::DirBuilderExt; -use std::{fs::DirBuilder, path::PathBuf}; +use std::path::PathBuf; use anyhow::{Context, Result}; use serde::{Deserialize, Deserializer, Serialize}; use yazi_codegen::DeserializeOver2; -use yazi_fs::Xdg; +use yazi_fs::{Xdg, create_owned_dir_blocking}; use yazi_shared::{SStr, timestamp_us}; use yazi_shim::toml::DeserializeOverHook; @@ -51,12 +49,7 @@ impl Preview { impl DeserializeOverHook for Preview { fn deserialize_over_hook(self) -> Result { - #[cfg(unix)] - let result = DirBuilder::new().mode(0o700).recursive(true).create(&self.cache_dir); - #[cfg(not(unix))] - let result = DirBuilder::new().recursive(true).create(&self.cache_dir); - - result + create_owned_dir_blocking(&self.cache_dir) .context(format!("Failed to create cache directory: {}", self.cache_dir.display())) .map_err(serde::de::Error::custom)?; diff --git a/yazi-dds/src/stream.rs b/yazi-dds/src/stream.rs index 4b2915da..b09a6a0f 100644 --- a/yazi-dds/src/stream.rs +++ b/yazi-dds/src/stream.rs @@ -53,18 +53,14 @@ impl Stream { #[cfg(unix)] async fn socket_file() -> io::Result<&'static std::path::PathBuf> { - use tokio::{fs::DirBuilder, sync::OnceCell}; - use yazi_fs::Xdg; + use tokio::sync::OnceCell; + use yazi_fs::{Xdg, create_owned_dir}; - static ONCE: tokio::sync::OnceCell = OnceCell::const_new(); + static ONCE: OnceCell = OnceCell::const_new(); ONCE .get_or_try_init(|| async move { let p = Xdg::runtime_dir(); - - #[cfg(unix)] - DirBuilder::new().mode(0o700).recursive(true).create(p).await?; - #[cfg(not(unix))] - DirBuilder::new().recursive(true).create(p).await?; + create_owned_dir(p).await?; Ok(p.join(".dds.sock")) }) diff --git a/yazi-fs/src/fns.rs b/yazi-fs/src/fns.rs index 4718d1f0..4772769f 100644 --- a/yazi-fs/src/fns.rs +++ b/yazi-fs/src/fns.rs @@ -1,4 +1,5 @@ -use tokio::io; +use std::{io, path::Path}; + use yazi_shared::url::{Component, UrlBuf, UrlLike}; #[inline] @@ -74,3 +75,47 @@ fn test_max_common_root() { assert(&["/aa/bb/cc", "/aa/dd/ee"], "/aa"); assert(&["/aa/bb/cc", "/aa/bb/cc/dd/ee", "/aa/bb/cc/ff"], "/aa/bb"); } + +pub async fn create_owned_dir(p: &Path) -> io::Result<()> { + let p = p.to_owned(); + tokio::task::spawn_blocking(move || create_owned_dir_blocking(&p)).await? +} + +pub fn create_owned_dir_blocking(p: &Path) -> io::Result<()> { + #[cfg(unix)] + { + use std::{fs::{DirBuilder, OpenOptions}, mem, os::unix::{fs::{DirBuilderExt, OpenOptionsExt}, io::AsRawFd}}; + + use libc::{O_DIRECTORY, O_NOFOLLOW}; + use uzers::Users; + use yazi_shared::USERS_CACHE; + + DirBuilder::new().mode(0o700).recursive(true).create(p)?; + let dir = OpenOptions::new().read(true).custom_flags(O_DIRECTORY | O_NOFOLLOW).open(p)?; + + let mut stat: libc::stat = unsafe { mem::zeroed() }; + if unsafe { libc::fstat(dir.as_raw_fd(), &mut stat) } != 0 { + return Err(io::Error::last_os_error()); + } + + // Reject directories not owned by the current user. + let uid = USERS_CACHE.get_current_uid(); + if stat.st_uid != uid { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + format!("directory {:?} is owned by uid {} but current uid is {}", p, stat.st_uid, uid), + )); + } + + // Enforce mode 0o700 via the fd. + if unsafe { libc::fchmod(dir.as_raw_fd(), 0o700) } != 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + #[cfg(not(unix))] + { + std::fs::DirBuilder::new().recursive(true).create(p) + } +} diff --git a/yazi-plugin/preset/components/tip.lua b/yazi-plugin/preset/components/tip.lua index e4c0ed36..8012e4ce 100644 --- a/yazi-plugin/preset/components/tip.lua +++ b/yazi-plugin/preset/components/tip.lua @@ -13,7 +13,7 @@ function Tip:layout() :direction(ui.Layout.VERTICAL) :constraints({ ui.Constraint.Fill(1), - ui.Constraint.Length(1), + ui.Constraint.Length(3), ui.Constraint.Fill(1), }) :split(self._area) @@ -24,6 +24,7 @@ function Tip:reflow() return {} end function Tip:redraw() return { ui.Clear(self._chunks[2]), - ui.Text(self._text):area(self._chunks[2]):align(ui.Align.CENTER):fg("black"):bg("yellow"):bold(), + ui.Text(""):area(self._chunks[2]):align(ui.Align.CENTER):bg("yellow"), + ui.Text(self._text):area(self._chunks[2]:pad(ui.Pad.xy(1, 1))):align(ui.Align.CENTER):fg("black"):bold(), } end diff --git a/yazi-plugin/preset/plugins/dnd.lua b/yazi-plugin/preset/plugins/dnd.lua index 039cf90b..fae4c6ea 100644 --- a/yazi-plugin/preset/plugins/dnd.lua +++ b/yazi-plugin/preset/plugins/dnd.lua @@ -12,6 +12,7 @@ function M.selected_uri_list() end function M.cut_uri_list(list) + cx.tasks.behavior:reset() for line in list:gmatch("[^\r\n]+") do if line:sub(1, 7) ~= "file://" then goto continue From 601cb10b32f9458c966451549d9c0fc1ce010153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Fri, 29 May 2026 15:33:03 +0800 Subject: [PATCH 05/28] feat: Unix domain socket on Windows as the underlying transport layer for DDS (#4009) --- Cargo.lock | 31 +++++++++++++++++ yazi-dds/Cargo.toml | 3 ++ yazi-dds/src/client.rs | 4 +-- yazi-dds/src/state.rs | 3 +- yazi-dds/src/stream.rs | 76 ++++++++++++++++++++++++++++++------------ 5 files changed, 92 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2c3f2dc..7478fae3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1438,6 +1438,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "fax" version = "0.2.7" @@ -4319,6 +4325,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "terminfo" version = "0.9.0" @@ -4741,6 +4760,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -5834,6 +5864,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "uds_windows", "vergen-gitcl", "yazi-binding", "yazi-boot", diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index f8f50c2f..cd4a81e5 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -40,3 +40,6 @@ tracing = { workspace = true } [build-dependencies] vergen-gitcl = { version = "9.1.0", features = [ "build" ] } + +[target.'cfg(windows)'.dependencies] +uds_windows = "1.2.1" diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index e193e215..919af9fd 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -48,8 +48,8 @@ impl Client { writer.write_all(payload.as_bytes()).await.ok(); // Retry once } } - Ok(next) = lines.next_line() => { - let Some(line) = next else { + next = lines.next_line() => { + let Ok(Some(line)) = next else { (lines, writer) = Self::reconnect(&mut server).await; continue; }; diff --git a/yazi-dds/src/state.rs b/yazi-dds/src/state.rs index 830f9901..c740921a 100644 --- a/yazi-dds/src/state.rs +++ b/yazi-dds/src/state.rs @@ -26,6 +26,7 @@ impl Deref for State { impl State { pub fn set(&self, kind: &str, sender: u64, body: &str) -> bool { + debug_assert!(kind.starts_with('@')); let Some(inner) = &mut *self.inner.write() else { return false }; if body == "null" { @@ -84,7 +85,7 @@ impl State { let line = mem::take(&mut buf); let mut parts = line.splitn(4, ','); - let Some(kind) = parts.next() else { continue }; + let Some(kind) = parts.next().filter(|s| s.starts_with('@')) else { continue }; let Some(_) = parts.next() else { continue }; inner.insert(kind.to_owned(), line); } diff --git a/yazi-dds/src/stream.rs b/yazi-dds/src/stream.rs index b09a6a0f..5aba44bd 100644 --- a/yazi-dds/src/stream.rs +++ b/yazi-dds/src/stream.rs @@ -1,25 +1,24 @@ -use std::io; +use std::{io, path::PathBuf}; -use tokio::io::{BufReader, Lines, ReadHalf, WriteHalf}; +use tokio::{io::{AsyncBufReadExt, BufReader, Lines, ReadHalf, WriteHalf}, sync::OnceCell}; +use yazi_fs::{Xdg, create_owned_dir, provider::{Provider, local::Local}}; pub struct Stream; -use tokio::io::AsyncBufReadExt; - #[cfg(unix)] pub type ClientReader = Lines>>; -#[cfg(not(unix))] +#[cfg(windows)] pub type ClientReader = Lines>>; #[cfg(unix)] pub(super) type ClientWriter = WriteHalf; -#[cfg(not(unix))] +#[cfg(windows)] pub(super) type ClientWriter = WriteHalf; #[cfg(unix)] pub(super) type ServerListener = tokio::net::UnixListener; -#[cfg(not(unix))] -pub(super) type ServerListener = tokio::net::TcpListener; +#[cfg(windows)] +pub(super) type ServerListener = WinUnixListener; impl Stream { #[cfg(unix)] @@ -29,34 +28,36 @@ impl Stream { Ok((BufReader::new(reader).lines(), writer)) } - #[cfg(not(unix))] + #[cfg(windows)] pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> { - let stream = tokio::net::TcpStream::connect("127.0.0.1:33581").await?; - let (reader, writer) = tokio::io::split(stream); + let p = Self::socket_file().await?; + let uds = tokio::task::spawn_blocking(move || uds_windows::UnixStream::connect(p)).await??; + + let (reader, writer) = tokio::io::split(WinUnixListener::into_tokio(uds)?); Ok((BufReader::new(reader).lines(), writer)) } #[cfg(unix)] pub(super) async fn bind() -> io::Result { - use yazi_fs::provider::Provider; - let p = Self::socket_file().await?; - yazi_fs::provider::local::Local::regular(&p).remove_file().await.ok(); + Local::regular(&p).remove_file().await.ok(); tokio::net::UnixListener::bind(p) } - #[cfg(not(unix))] + #[cfg(windows)] pub(super) async fn bind() -> io::Result { - tokio::net::TcpListener::bind("127.0.0.1:33581").await + let p = Self::socket_file().await?; + Local::regular(&p).remove_file().await.ok(); + + let listener = uds_windows::UnixListener::bind(p)?; + listener.set_nonblocking(true)?; + + Ok(WinUnixListener(listener)) } - #[cfg(unix)] - async fn socket_file() -> io::Result<&'static std::path::PathBuf> { - use tokio::sync::OnceCell; - use yazi_fs::{Xdg, create_owned_dir}; - - static ONCE: OnceCell = OnceCell::const_new(); + async fn socket_file() -> io::Result<&'static PathBuf> { + static ONCE: OnceCell = OnceCell::const_new(); ONCE .get_or_try_init(|| async move { let p = Xdg::runtime_dir(); @@ -67,3 +68,34 @@ impl Stream { .await } } + +// --- WinUnixListener +#[cfg(windows)] +pub(super) struct WinUnixListener(uds_windows::UnixListener); + +#[cfg(windows)] +impl WinUnixListener { + pub(super) async fn accept( + &self, + ) -> io::Result<(tokio::net::TcpStream, uds_windows::SocketAddr)> { + loop { + match self.0.accept() { + Ok((stream, addr)) => return Ok((Self::into_tokio(stream)?, addr)), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + tokio::time::sleep(std::time::Duration::from_millis(20)).await; + } + Err(e) => return Err(e), + } + } + } + + fn into_tokio(uds: uds_windows::UnixStream) -> io::Result { + use std::os::windows::io::{FromRawSocket, IntoRawSocket}; + + let raw = uds.into_raw_socket(); + let std = unsafe { std::net::TcpStream::from_raw_socket(raw) }; + std.set_nonblocking(true)?; + + tokio::net::TcpStream::from_std(std) + } +} From e892bf7d903d9efb7322f47d93c3ac6be5dd797e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sun, 31 May 2026 13:07:11 +0800 Subject: [PATCH 06/28] fix: decouple `yazi_boot` crate init from `yazi_term` init (#4011) --- .github/DISCUSSION_TEMPLATE/1-q-a.yml | 6 +- .github/ISSUE_TEMPLATE/bug.yml | 8 +- .github/ISSUE_TEMPLATE/feature.yml | 6 +- Cargo.lock | 91 +++++++++--------- scripts/validate-form/main.js | 4 +- yazi-binding/src/tty.rs | 4 +- yazi-boot/Cargo.toml | 17 ++-- yazi-boot/build.rs | 19 ---- yazi-boot/src/actions/actions.rs | 14 +-- yazi-boot/src/actions/mod.rs | 2 +- yazi-boot/src/actions/rustc.rs | 12 --- yazi-boot/src/actions/triple.rs | 12 --- yazi-boot/src/actions/version.rs | 14 --- yazi-boot/src/args.rs | 8 +- yazi-cli/Cargo.toml | 19 ++-- yazi-cli/build.rs | 11 --- yazi-cli/src/args.rs | 11 +++ .../src/cache/clear.rs | 10 +- yazi-cli/src/cache/mod.rs | 3 + yazi-cli/src/dds/shot.rs | 6 +- .../debug.rs => yazi-cli/src/env/env.rs | 94 +++++++++---------- yazi-cli/src/env/mod.rs | 3 + yazi-cli/src/main.rs | 28 ++++-- yazi-dds/Cargo.toml | 4 +- yazi-dds/build.rs | 13 --- yazi-dds/src/ember/hey.rs | 4 +- yazi-dds/src/ember/hi.rs | 6 +- yazi-fm/src/main.rs | 14 +-- yazi-runner/Cargo.toml | 2 +- yazi-runner/src/loader/chunk.rs | 2 +- yazi-runner/src/loader/loader.rs | 2 +- yazi-shim/src/mlua/string.rs | 2 +- yazi-term/src/event/keyboard.rs | 20 ++-- yazi-version/Cargo.toml | 16 ++++ yazi-version/README.md | 5 + yazi-version/build.rs | 25 +++++ yazi-version/src/lib.rs | 27 ++++++ 37 files changed, 285 insertions(+), 259 deletions(-) delete mode 100644 yazi-boot/src/actions/rustc.rs delete mode 100644 yazi-boot/src/actions/triple.rs delete mode 100644 yazi-boot/src/actions/version.rs rename yazi-boot/src/actions/clear_cache.rs => yazi-cli/src/cache/clear.rs (69%) create mode 100644 yazi-cli/src/cache/mod.rs rename yazi-boot/src/actions/debug.rs => yazi-cli/src/env/env.rs (65%) create mode 100644 yazi-cli/src/env/mod.rs delete mode 100644 yazi-dds/build.rs create mode 100644 yazi-version/Cargo.toml create mode 100644 yazi-version/README.md create mode 100644 yazi-version/build.rs create mode 100644 yazi-version/src/lib.rs diff --git a/.github/DISCUSSION_TEMPLATE/1-q-a.yml b/.github/DISCUSSION_TEMPLATE/1-q-a.yml index ab64c16e..394e0cf6 100644 --- a/.github/DISCUSSION_TEMPLATE/1-q-a.yml +++ b/.github/DISCUSSION_TEMPLATE/1-q-a.yml @@ -22,10 +22,10 @@ body: validations: required: true - type: textarea - id: debug + id: env attributes: - label: "`yazi --debug` output" - description: Please run `yazi --debug` and paste the debug information here. + label: "`ya env` output" + description: Run `ya env` and paste the environment information here. render: Shell validations: required: true diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 8c33c266..157822ba 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -26,10 +26,10 @@ body: validations: required: true - type: textarea - id: debug + id: env attributes: - label: "`yazi --debug` output" - description: Please run `yazi --debug` and paste the debug information here. + label: "`ya env` output" + description: Run `ya env` and paste the environment information here. render: Shell validations: required: true @@ -65,6 +65,6 @@ body: options: - label: I tried the [latest nightly build](https://yazi-rs.github.io/docs/installation#binaries), and the issue is still reproducible required: true - - label: I updated the debug information (`yazi --debug`) input box to the nightly that I tried + - label: I updated the environment information (`ya env`) field to the nightly that I tried required: true - label: I can reproduce it after disabling all custom configs/plugins (`mv ~/.config/yazi ~/.config/yazi-backup`) diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 26ab7706..647f0534 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -4,10 +4,10 @@ labels: [feature] assignees: [] body: - type: textarea - id: debug + id: env attributes: - label: "`yazi --debug` output" - description: Please run `yazi --debug` and paste the debug information here. + label: "`ya env` output" + description: Run `ya env` and paste the environment information here. render: Shell validations: required: true diff --git a/Cargo.lock b/Cargo.lock index 7478fae3..a8e9faab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,7 +421,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -440,7 +440,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -557,9 +557,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.62" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "jobserver", @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "cmov" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" [[package]] name = "color_quant" @@ -908,11 +908,11 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", "typenum", ] @@ -1196,7 +1196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "crypto-common 0.1.6", + "crypto-common 0.1.7", ] [[package]] @@ -1637,9 +1637,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1647,11 +1647,11 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.4.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" +checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", "rustversion", "typenum", ] @@ -1971,7 +1971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding 0.3.3", - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -2096,9 +2096,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -2330,9 +2330,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -3619,7 +3619,7 @@ dependencies = [ "elliptic-curve", "enum_dispatch", "futures", - "generic-array 1.4.1", + "generic-array 1.4.3", "getrandom 0.2.17", "ghash", "hex-literal", @@ -4018,9 +4018,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook" @@ -4126,9 +4126,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -4750,9 +4750,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -4836,9 +4836,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" dependencies = [ "atomic", "getrandom 0.4.2", @@ -5730,16 +5730,11 @@ dependencies = [ "clap_complete_nushell", "futures", "hashbrown 0.17.1", - "regex", - "vergen-gitcl", - "yazi-adapter", - "yazi-config", - "yazi-emulator", "yazi-fs", "yazi-macro", "yazi-shared", "yazi-shim", - "yazi-term", + "yazi-version", "yazi-vfs", ] @@ -5761,18 +5756,23 @@ dependencies = [ "clap_complete_fig", "clap_complete_nushell", "hashbrown 0.17.1", + "regex", "serde", "serde_json", "tokio", "toml", "twox-hash", - "vergen-gitcl", + "yazi-adapter", "yazi-boot", + "yazi-config", "yazi-dds", + "yazi-emulator", "yazi-fs", "yazi-macro", "yazi-shared", "yazi-term", + "yazi-tty", + "yazi-version", ] [[package]] @@ -5865,13 +5865,13 @@ dependencies = [ "tokio-stream", "tracing", "uds_windows", - "vergen-gitcl", "yazi-binding", "yazi-boot", "yazi-fs", "yazi-macro", "yazi-shared", "yazi-shim", + "yazi-version", ] [[package]] @@ -6086,13 +6086,13 @@ dependencies = [ "tokio-util", "tracing", "yazi-binding", - "yazi-boot", "yazi-config", "yazi-dds", "yazi-fs", "yazi-macro", "yazi-shared", "yazi-shim", + "yazi-version", ] [[package]] @@ -6231,6 +6231,13 @@ dependencies = [ "yazi-tty", ] +[[package]] +name = "yazi-version" +version = "26.5.6" +dependencies = [ + "vergen-gitcl", +] + [[package]] name = "yazi-vfs" version = "26.5.6" @@ -6299,18 +6306,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.49" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.49" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", diff --git a/scripts/validate-form/main.js b/scripts/validate-form/main.js index 925b4e16..bd3fddb8 100644 --- a/scripts/validate-form/main.js +++ b/scripts/validate-form/main.js @@ -13,7 +13,7 @@ function bugReportBody(creator, content, hash) { I noticed that you did not correctly follow the issue template. Please ensure that: - The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). -- The debug information (\`yazi --debug\`) is updated for the newest nightly. +- The environment info (\`ya env\`) is updated for the newest nightly. - The non-optional items in the checklist are checked. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. @@ -32,7 +32,7 @@ function featureRequestBody(creator, content) { I noticed that you did not correctly follow the issue template. Please ensure that: - The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). -- The debug information (\`yazi --debug\`) is updated for the newest nightly. +- The environment info (\`ya env\`) is updated for the newest nightly. - The non-optional items in the checklist are checked. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. diff --git a/yazi-binding/src/tty.rs b/yazi-binding/src/tty.rs index 39f52e74..272133f6 100644 --- a/yazi-binding/src/tty.rs +++ b/yazi-binding/src/tty.rs @@ -55,8 +55,8 @@ impl Tty { }; match result { - Ok(()) => true.into_lua_multi(&lua), - Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), + Ok(()) => true.into_lua_multi(lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(lua), } } } diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index e226fade..4cc27b8e 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -13,21 +13,17 @@ rust-version.workspace = true workspace = true [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" } -yazi-config = { path = "../yazi-config", version = "26.5.6" } -yazi-emulator = { path = "../yazi-emulator", version = "26.5.6" } -yazi-fs = { path = "../yazi-fs", version = "26.5.6" } -yazi-macro = { path = "../yazi-macro", version = "26.5.6" } -yazi-shared = { path = "../yazi-shared", version = "26.5.6" } -yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-term = { path = "../yazi-term", version = "26.5.6" } -yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } +yazi-fs = { path = "../yazi-fs", version = "26.5.6" } +yazi-macro = { path = "../yazi-macro", version = "26.5.6" } +yazi-shared = { path = "../yazi-shared", version = "26.5.6" } +yazi-shim = { path = "../yazi-shim", version = "26.5.6" } +yazi-version = { path = "../yazi-version", version = "26.5.6" } +yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } # External dependencies clap = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } -regex = { workspace = true } [build-dependencies] yazi-shared = { path = "../yazi-shared", version = "26.5.6" } @@ -37,4 +33,3 @@ clap = { workspace = true } clap_complete = "4.6.5" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.6.0" -vergen-gitcl = { version = "9.1.0", features = [ "build", "rustc" ] } diff --git a/yazi-boot/build.rs b/yazi-boot/build.rs index 93ea359e..597e6a00 100644 --- a/yazi-boot/build.rs +++ b/yazi-boot/build.rs @@ -5,27 +5,8 @@ use std::{env, error::Error}; use clap::CommandFactory; use clap_complete::{Shell, generate_to}; -use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder, RustcBuilder}; fn main() -> Result<(), Box> { - Emitter::default() - .add_instructions(&BuildBuilder::default().build_date(true).build()?)? - .add_instructions( - &RustcBuilder::default() - .commit_date(true) - .commit_hash(true) - .host_triple(true) - .semver(true) - .build()?, - )? - .emit()?; - - if env::var_os("YAZI_NO_GITCL").is_none() { - Emitter::default().add_instructions(&GitclBuilder::default().sha(true).build()?)?.emit()?; - } else { - println!("cargo:rustc-env=VERGEN_GIT_SHA=no-gitcl"); - } - if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { return Ok(()); } diff --git a/yazi-boot/src/actions/actions.rs b/yazi-boot/src/actions/actions.rs index d4d120fa..1849be81 100644 --- a/yazi-boot/src/actions/actions.rs +++ b/yazi-boot/src/actions/actions.rs @@ -5,17 +5,17 @@ pub struct Actions; impl Actions { pub(crate) fn act(args: &crate::Args) { if args.debug { - println!("{}", Self::debug().unwrap()); - process::exit(0); - } - - if args.version { - println!("Yazi {}", Self::version()); + println!("`yazi --debug` has been deprecated, use `ya env` instead."); process::exit(0); } if args.clear_cache { - Self::clear_cache(); + println!("`yazi --clear-cache` has been deprecated, use `ya cache clear` instead."); + process::exit(0); + } + + if args.version { + println!("Yazi\n{}", yazi_version::version_full()); process::exit(0); } } diff --git a/yazi-boot/src/actions/mod.rs b/yazi-boot/src/actions/mod.rs index 651f0ab6..239b7755 100644 --- a/yazi-boot/src/actions/mod.rs +++ b/yazi-boot/src/actions/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(actions clear_cache debug rustc triple version); +yazi_macro::mod_flat!(actions); diff --git a/yazi-boot/src/actions/rustc.rs b/yazi-boot/src/actions/rustc.rs deleted file mode 100644 index 834ba6e5..00000000 --- a/yazi-boot/src/actions/rustc.rs +++ /dev/null @@ -1,12 +0,0 @@ -use super::Actions; - -impl Actions { - pub(super) fn rustc() -> String { - format!( - "{} ({} {})", - env!("VERGEN_RUSTC_SEMVER"), - &env!("VERGEN_RUSTC_COMMIT_HASH")[..8], - env!("VERGEN_RUSTC_COMMIT_DATE") - ) - } -} diff --git a/yazi-boot/src/actions/triple.rs b/yazi-boot/src/actions/triple.rs deleted file mode 100644 index 04dafcfc..00000000 --- a/yazi-boot/src/actions/triple.rs +++ /dev/null @@ -1,12 +0,0 @@ -use super::Actions; - -impl Actions { - pub(super) fn triple() -> String { - format!( - "{} ({}-{})", - env!("VERGEN_RUSTC_HOST_TRIPLE"), - std::env::consts::OS, - std::env::consts::ARCH - ) - } -} diff --git a/yazi-boot/src/actions/version.rs b/yazi-boot/src/actions/version.rs deleted file mode 100644 index 84906dac..00000000 --- a/yazi-boot/src/actions/version.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::Actions; - -impl Actions { - pub fn version() -> &'static str { - concat!( - env!("CARGO_PKG_VERSION"), - " (", - env!("VERGEN_GIT_SHA"), - " ", - env!("VERGEN_BUILD_DATE"), - ")" - ) - } -} diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs index 76c50936..187de0f5 100644 --- a/yazi-boot/src/args.rs +++ b/yazi-boot/src/args.rs @@ -18,9 +18,9 @@ pub struct Args { #[arg(long)] pub chooser_file: Option, - /// Clear the cache directory + /// Clear the cache directory (deprecated, use `ya cache clear`) #[arg(long)] - pub clear_cache: bool, + pub clear_cache: bool, // TODO: remove /// Use the specified client ID, must be a globally unique number #[arg(long)] @@ -32,9 +32,9 @@ pub struct Args { #[arg(long)] pub remote_events: Option, - /// Print debug information + /// Print debug information (deprecated, use `ya env`) #[arg(long)] - pub debug: bool, + pub debug: bool, // TODO: remove /// Print version #[arg(short = 'V', long)] diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 8c628d0f..09e86f9b 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -13,17 +13,23 @@ rust-version.workspace = true workspace = true [dependencies] -yazi-boot = { path = "../yazi-boot", version = "26.5.6" } -yazi-dds = { path = "../yazi-dds", version = "26.5.6" } -yazi-fs = { path = "../yazi-fs", version = "26.5.6" } -yazi-macro = { path = "../yazi-macro", version = "26.5.6" } -yazi-shared = { path = "../yazi-shared", version = "26.5.6" } -yazi-term = { path = "../yazi-term", version = "26.5.6" } +yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" } +yazi-boot = { path = "../yazi-boot", version = "26.5.6" } +yazi-config = { path = "../yazi-config", version = "26.5.6" } +yazi-dds = { path = "../yazi-dds", version = "26.5.6" } +yazi-emulator = { path = "../yazi-emulator", version = "26.5.6" } +yazi-fs = { path = "../yazi-fs", version = "26.5.6" } +yazi-macro = { path = "../yazi-macro", version = "26.5.6" } +yazi-shared = { path = "../yazi-shared", version = "26.5.6" } +yazi-term = { path = "../yazi-term", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-version = { path = "../yazi-version", version = "26.5.6" } # External dependencies anyhow = { workspace = true } clap = { workspace = true } hashbrown = { workspace = true } +regex = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } @@ -41,7 +47,6 @@ clap_complete_fig = "4.5.2" clap_complete_nushell = "4.6.0" serde = { workspace = true } serde_json = { workspace = true } -vergen-gitcl = { version = "9.1.0", features = [ "build" ] } [[bin]] name = "ya" diff --git a/yazi-cli/build.rs b/yazi-cli/build.rs index 8d596f34..7c5edb0f 100644 --- a/yazi-cli/build.rs +++ b/yazi-cli/build.rs @@ -5,7 +5,6 @@ use std::{env, error::Error}; use clap::CommandFactory; use clap_complete::{Shell, generate_to}; -use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; fn main() -> Result<(), Box> { let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap().to_string_lossy().replace(r"\", "/"); @@ -21,16 +20,6 @@ fn main() -> Result<(), Box> { } fn generate() -> Result<(), Box> { - Emitter::default() - .add_instructions(&BuildBuilder::default().build_date(true).build()?)? - .emit()?; - - if env::var_os("YAZI_NO_GITCL").is_none() { - Emitter::default().add_instructions(&GitclBuilder::default().sha(true).build()?)?.emit()?; - } else { - println!("cargo:rustc-env=VERGEN_GIT_SHA=no-gitcl"); - } - if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { return Ok(()); } diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 17b75040..de2b5560 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -32,6 +32,11 @@ pub(super) enum Command { PubTo(CommandPubTo), /// Subscribe to messages from all remote instances. Sub(CommandSub), + /// Manage the cache. + #[command(subcommand)] + Cache(CommandCache), + /// Print environment and configuration information. + Env, } #[derive(clap::Args)] @@ -154,6 +159,12 @@ pub(super) struct CommandSub { pub(super) kinds: String, } +#[derive(Subcommand)] +pub(super) enum CommandCache { + /// Clear the cache directory. + Clear, +} + // --- Macros macro_rules! impl_emit_body { ($name:ident) => { diff --git a/yazi-boot/src/actions/clear_cache.rs b/yazi-cli/src/cache/clear.rs similarity index 69% rename from yazi-boot/src/actions/clear_cache.rs rename to yazi-cli/src/cache/clear.rs index b2c2d488..68348834 100644 --- a/yazi-boot/src/actions/clear_cache.rs +++ b/yazi-cli/src/cache/clear.rs @@ -1,18 +1,20 @@ use yazi_config::YAZI; use yazi_fs::Xdg; -use super::Actions; +use crate::cache::Cache; -impl Actions { - pub(super) fn clear_cache() { +impl Cache { + pub(crate) fn clear() -> anyhow::Result<()> { if YAZI.preview.cache_dir == *Xdg::temp_dir() { println!("Clearing cache directory: \n{:?}", YAZI.preview.cache_dir); - std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap(); + std::fs::remove_dir_all(&YAZI.preview.cache_dir)?; } else { println!( "You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}", YAZI.preview.cache_dir ); } + + Ok(()) } } diff --git a/yazi-cli/src/cache/mod.rs b/yazi-cli/src/cache/mod.rs new file mode 100644 index 00000000..77e6daec --- /dev/null +++ b/yazi-cli/src/cache/mod.rs @@ -0,0 +1,3 @@ +yazi_macro::mod_flat!(clear); + +pub(crate) struct Cache; diff --git a/yazi-cli/src/dds/shot.rs b/yazi-cli/src/dds/shot.rs index ed039ed0..4cd767ae 100644 --- a/yazi-cli/src/dds/shot.rs +++ b/yazi-cli/src/dds/shot.rs @@ -44,10 +44,10 @@ impl Dds { } pub(super) fn ensure_version(version: Option<&str>) -> Result<()> { - if version != Some(EmberHi::version()) { + if version != Some(yazi_version::version()) { bail!( - "Incompatible version (Ya {}, Yazi {}). Restart all `ya` and `yazi` processes if you upgrade either one.", - EmberHi::version(), + "Incompatible version (Ya {}, Yazi {}). Restart all `ya` and `yazi` processes if you upgraded either.", + yazi_version::version(), version.unwrap_or("Unknown") ); } diff --git a/yazi-boot/src/actions/debug.rs b/yazi-cli/src/env/env.rs similarity index 65% rename from yazi-boot/src/actions/debug.rs rename to yazi-cli/src/env/env.rs index 26228335..4144a5b1 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-cli/src/env/env.rs @@ -1,4 +1,4 @@ -use std::{env, ffi::OsStr, fmt::Write, path::Path}; +use std::{env, ffi::OsStr, fmt::Write, path::Path, process::Command}; use regex::Regex; use yazi_config::{THEME, YAZI}; @@ -7,22 +7,17 @@ use yazi_fs::Xdg; use yazi_shared::timestamp_us; use yazi_term::TERM; -use super::Actions; +use crate::env::Env; -impl Actions { - pub(super) fn debug() -> Result { +impl Env { + pub(crate) fn print() -> Result { let mut s = String::new(); - writeln!(s, "\nYazi")?; - writeln!(s, " Version : {}", Self::version())?; - writeln!(s, " Debug : {}", cfg!(debug_assertions))?; - writeln!(s, " Triple : {}", Self::triple())?; - writeln!(s, " Rustc : {}", Self::rustc())?; + writeln!(s, "Yazi\n{}", Self::yazi_version())?; writeln!(s, " Backtrace: {:?}", env::var_os("RUST_BACKTRACE"))?; - writeln!(s, "\nYa")?; - writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?; + writeln!(s, "\nYa\n{}", yazi_version::version_full())?; - writeln!(s, "\nConfig")?; + writeln!(s, "Config")?; writeln!(s, " Init : {}", Self::config_state("init.lua"))?; writeln!(s, " Yazi : {}", Self::config_state("yazi.toml"))?; writeln!(s, " Keymap : {}", Self::config_state("keymap.toml"))?; @@ -88,47 +83,55 @@ impl Actions { writeln!(s, "\nMultiplexers")?; writeln!(s, " TMUX : {}", yazi_emulator::TMUX)?; - writeln!(s, " tmux version : {}", Self::process_output("tmux", "-V"))?; + writeln!(s, " tmux version : {}", Self::dep_version("tmux", "-V"))?; writeln!(s, " tmux build flags : enable-sixel={}", Mux::tmux_sixel_flag())?; writeln!(s, " ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME"))?; - writeln!(s, " Zellij version : {}", Self::process_output("zellij", "--version"))?; + writeln!(s, " Zellij version : {}", Self::dep_version("zellij", "--version"))?; writeln!(s, "\nDependencies")?; #[rustfmt::skip] - writeln!(s, " file : {}", Self::process_output(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version"))?; - writeln!(s, " ueberzugpp : {}", Self::process_output("ueberzugpp", "--version"))?; + writeln!(s, " file : {}", Self::dep_version(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version"))?; + writeln!(s, " ueberzugpp : {}", Self::dep_version("ueberzugpp", "--version"))?; #[rustfmt::skip] - writeln!(s, " ffmpeg/ffprobe: {} / {}", Self::process_output("ffmpeg", "-version"), Self::process_output("ffprobe", "-version"))?; - writeln!(s, " pdftoppm : {}", Self::process_output("pdftoppm", "--help"))?; - writeln!(s, " magick : {}", Self::process_output("magick", "--version"))?; - writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?; + writeln!(s, " ffmpeg/ffprobe: {} / {}", Self::dep_version("ffmpeg", "-version"), Self::dep_version("ffprobe", "-version"))?; + writeln!(s, " pdftoppm : {}", Self::dep_version("pdftoppm", "--help"))?; + writeln!(s, " magick : {}", Self::dep_version("magick", "--version"))?; + writeln!(s, " fzf : {}", Self::dep_version("fzf", "--version"))?; #[rustfmt::skip] - writeln!(s, " fd/fdfind : {} / {}", Self::process_output("fd", "--version"), Self::process_output("fdfind", "--version"))?; - writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?; - writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?; - writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?; + writeln!(s, " fd/fdfind : {} / {}", Self::dep_version("fd", "--version"), Self::dep_version("fdfind", "--version"))?; + writeln!(s, " rg : {}", Self::dep_version("rg", "--version"))?; + writeln!(s, " chafa : {}", Self::dep_version("chafa", "--version"))?; + writeln!(s, " zoxide : {}", Self::dep_version("zoxide", "--version"))?; #[rustfmt::skip] - writeln!(s, " 7zz/7z : {} / {}", Self::process_output("7zz", "i"), Self::process_output("7z", "i"))?; - writeln!(s, " resvg : {}", Self::process_output("resvg", "--version"))?; - writeln!(s, " jq : {}", Self::process_output("jq", "--version"))?; + writeln!(s, " 7zz/7z : {} / {}", Self::dep_version("7zz", "i"), Self::dep_version("7z", "i"))?; + writeln!(s, " resvg : {}", Self::dep_version("resvg", "--version"))?; + writeln!(s, " jq : {}", Self::dep_version("jq", "--version"))?; writeln!(s, "\nClipboard")?; #[rustfmt::skip] - writeln!(s, " wl-copy/paste: {} / {}", Self::process_output("wl-copy", "--version"), Self::process_output("wl-paste", "--version"))?; - writeln!(s, " xclip : {}", Self::process_output("xclip", "-version"))?; - writeln!(s, " xsel : {}", Self::process_output("xsel", "--version"))?; + writeln!(s, " wl-copy/paste: {} / {}", Self::dep_version("wl-copy", "--version"), Self::dep_version("wl-paste", "--version"))?; + writeln!(s, " xclip : {}", Self::dep_version("xclip", "-version"))?; + writeln!(s, " xsel : {}", Self::dep_version("xsel", "--version"))?; writeln!(s, "\nRoutine")?; writeln!(s, " `file -bL --mime-type`: {}", Self::file1_output())?; - writeln!( - s, - "\n\nSee https://yazi-rs.github.io/docs/plugins/overview#debugging on how to enable logging or debug runtime errors." - )?; - Ok(s) } + fn yazi_version() -> String { + match Command::new("yazi").arg("--version").output() { + Ok(out) if out.status.success() => { + let s = if out.stdout.is_empty() { out.stderr } else { out.stdout }; + let s = String::from_utf8_lossy(&s); + let s = s.trim(); + s.strip_prefix("Yazi\n").unwrap_or(s).to_owned() + } + Ok(out) => format!("{:?}, {:?}", out.status, String::from_utf8_lossy(&out.stderr)), + Err(e) => format!("{e}"), + } + } + fn config_state(name: &str) -> String { let p = Xdg::config_dir().join(name); match std::fs::read_to_string(&p) { @@ -139,8 +142,8 @@ impl Actions { } } - fn process_output(name: impl AsRef, arg: impl AsRef) -> String { - match std::process::Command::new(&name).arg(arg).output() { + fn dep_version(name: impl AsRef, arg: &str) -> String { + match Command::new(&name).arg(arg).output() { Ok(out) if out.status.success() => { let line = String::from_utf8_lossy(&if out.stdout.is_empty() { out.stderr } else { out.stdout }) @@ -149,15 +152,12 @@ impl Actions { .next() .unwrap_or_default() .to_owned(); - if name.as_ref() == "ya" { - line.trim_start_matches("Ya ").to_owned() - } else { - Regex::new(r"\d+\.\d+(\.\d+-\d+|\.\d+|\b)") - .unwrap() - .find(&line) - .map(|m| m.as_str().to_owned()) - .unwrap_or(line) - } + + Regex::new(r"\d+\.\d+(\.\d+-\d+|\.\d+|\b)") + .unwrap() + .find(&line) + .map(|m| m.as_str().to_owned()) + .unwrap_or(line) } Ok(out) => format!("{:?}, {:?}", out.status, String::from_utf8_lossy(&out.stderr)), Err(e) => format!("{e}"), @@ -171,7 +171,7 @@ impl Actions { std::fs::File::create_new(&p).map(|mut f| f.write_all(b"Hello, World!")).ok(); let program = env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()); - match std::process::Command::new(program).args(["-bL", "--mime-type"]).arg(&p).output() { + match Command::new(program).args(["-bL", "--mime-type"]).arg(&p).output() { Ok(out) => { String::from_utf8_lossy(&out.stdout).trim().lines().next().unwrap_or_default().to_owned() } diff --git a/yazi-cli/src/env/mod.rs b/yazi-cli/src/env/mod.rs new file mode 100644 index 00000000..055b1aa4 --- /dev/null +++ b/yazi-cli/src/env/mod.rs @@ -0,0 +1,3 @@ +yazi_macro::mod_flat!(env); + +pub(crate) struct Env; diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index b971ab9f..df189d6b 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_pub!(dds package shared); +yazi_macro::mod_pub!(cache dds env package shared); yazi_macro::mod_flat!(args); @@ -31,12 +31,7 @@ async fn main() -> ExitCode { async fn run() -> anyhow::Result<()> { if std::env::args_os().nth(1).is_some_and(|s| s == "-V" || s == "--version") { - outln!( - "Ya {} ({} {})", - env!("CARGO_PKG_VERSION"), - env!("VERGEN_GIT_SHA"), - env!("VERGEN_BUILD_DATE") - )?; + outln!("Ya\n{}", yazi_version::version_full())?; return Ok(()); } @@ -110,6 +105,25 @@ async fn run() -> anyhow::Result<()> { tokio::signal::ctrl_c().await?; } + + Command::Cache(cmd) => { + yazi_tty::init(); + yazi_config::init()?; + + match cmd { + CommandCache::Clear => { + cache::Cache::clear()?; + } + } + } + + Command::Env => { + yazi_tty::init(); + yazi_term::init()?; + yazi_config::init()?; + yazi_adapter::init()?; + outln!("{}", env::Env::print()?)?; + } } Ok(()) diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index cd4a81e5..5ee95c22 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -23,6 +23,7 @@ yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } +yazi-version = { path = "../yazi-version", version = "26.5.6" } # External dependencies anyhow = { workspace = true } @@ -38,8 +39,5 @@ tokio = { workspace = true } tokio-stream = { workspace = true } tracing = { workspace = true } -[build-dependencies] -vergen-gitcl = { version = "9.1.0", features = [ "build" ] } - [target.'cfg(windows)'.dependencies] uds_windows = "1.2.1" diff --git a/yazi-dds/build.rs b/yazi-dds/build.rs deleted file mode 100644 index 48fffb89..00000000 --- a/yazi-dds/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::{env, error::Error}; - -use vergen_gitcl::{Emitter, GitclBuilder}; - -fn main() -> Result<(), Box> { - if env::var_os("YAZI_NO_GITCL").is_none() { - Emitter::default().add_instructions(&GitclBuilder::default().sha(true).build()?)?.emit()?; - } else { - println!("cargo:rustc-env=VERGEN_GIT_SHA=no-gitcl"); - } - - Ok(()) -} diff --git a/yazi-dds/src/ember/hey.rs b/yazi-dds/src/ember/hey.rs index 299cbcd6..db1a78db 100644 --- a/yazi-dds/src/ember/hey.rs +++ b/yazi-dds/src/ember/hey.rs @@ -3,7 +3,7 @@ use mlua::{IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; use yazi_shared::{Id, SStr}; -use super::{Ember, EmberHi}; +use super::Ember; use crate::Peer; /// Server handshake @@ -15,7 +15,7 @@ pub struct EmberHey { impl EmberHey { pub fn owned(peers: HashMap) -> Ember<'static> { - Self { peers, version: EmberHi::version().into() }.into() + Self { peers, version: yazi_version::version().into() }.into() } } diff --git a/yazi-dds/src/ember/hi.rs b/yazi-dds/src/ember/hi.rs index f8b1149b..346a8898 100644 --- a/yazi-dds/src/ember/hi.rs +++ b/yazi-dds/src/ember/hi.rs @@ -22,14 +22,10 @@ impl<'a> EmberHi<'a> { { Self { abilities: abilities.into_iter().map(Into::into).collect(), - version: Self::version().into(), + version: yazi_version::version().into(), } .into() } - - pub fn version() -> &'static str { - concat!(env!("CARGO_PKG_VERSION"), " ", env!("VERGEN_GIT_SHA")) - } } impl<'a> From> for Ember<'a> { diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index c3ea749e..e9babc5c 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -14,20 +14,20 @@ async fn main() -> anyhow::Result<()> { Logs::start()?; _ = fdlimit::raise_fd_limit(); - yazi_tty::init(); - - yazi_term::init()?; - yazi_fs::init(); - yazi_config::init()?; - yazi_vfs::init(); - yazi_adapter::init()?; + yazi_tty::init(); + + yazi_config::init()?; yazi_boot::init(); + yazi_term::init()?; + + yazi_adapter::init()?; + yazi_dds::init(); yazi_widgets::init(); diff --git a/yazi-runner/Cargo.toml b/yazi-runner/Cargo.toml index 97434127..18a4eb21 100644 --- a/yazi-runner/Cargo.toml +++ b/yazi-runner/Cargo.toml @@ -18,13 +18,13 @@ vendored-lua = [ "mlua/vendored" ] [dependencies] yazi-binding = { path = "../yazi-binding", version = "26.5.6" } -yazi-boot = { path = "../yazi-boot", version = "26.5.6" } yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-dds = { path = "../yazi-dds", version = "26.5.6" } yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } +yazi-version = { path = "../yazi-version", version = "26.5.6" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-runner/src/loader/chunk.rs b/yazi-runner/src/loader/chunk.rs index 3fbfad08..33cff2b9 100644 --- a/yazi-runner/src/loader/chunk.rs +++ b/yazi-runner/src/loader/chunk.rs @@ -13,7 +13,7 @@ pub struct Chunk { impl Chunk { pub fn compatible(&self) -> bool { - let s = yazi_boot::actions::Actions::version(); + let s = yazi_version::version(); natsort(s.as_bytes(), self.since.as_bytes(), false) != std::cmp::Ordering::Less } diff --git a/yazi-runner/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs index 5dc41e14..07a2b237 100644 --- a/yazi-runner/src/loader/loader.rs +++ b/yazi-runner/src/loader/loader.rs @@ -170,7 +170,7 @@ impl Loader { bail!( "Plugin `{id}` requires at least Yazi {}, but your current version is Yazi {}.", chunk.since, - yazi_boot::actions::Actions::version() + yazi_version::version_long() ); } diff --git a/yazi-shim/src/mlua/string.rs b/yazi-shim/src/mlua/string.rs index 861e9d84..176ecb79 100644 --- a/yazi-shim/src/mlua/string.rs +++ b/yazi-shim/src/mlua/string.rs @@ -17,7 +17,7 @@ impl fmt::Display for ByteString { impl FromLua for ByteString { fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - Ok(ByteString(mlua::String::from_lua(value, lua)?)) + Ok(Self(mlua::String::from_lua(value, lua)?)) } } diff --git a/yazi-term/src/event/keyboard.rs b/yazi-term/src/event/keyboard.rs index 7836230f..5781e756 100644 --- a/yazi-term/src/event/keyboard.rs +++ b/yazi-term/src/event/keyboard.rs @@ -104,16 +104,16 @@ pub enum KeyCode { impl KeyCode { pub(crate) fn from_xterm_modifier(r#final: u8) -> Result { Ok(match r#final { - b'A' => KeyCode::Up, - b'B' => KeyCode::Down, - b'C' => KeyCode::Right, - b'D' => KeyCode::Left, - b'F' => KeyCode::End, - b'H' => KeyCode::Home, - b'P' => KeyCode::Fn(1), - b'Q' => KeyCode::Fn(2), - b'R' => KeyCode::Fn(3), - b'S' => KeyCode::Fn(4), + b'A' => Self::Up, + b'B' => Self::Down, + b'C' => Self::Right, + b'D' => Self::Left, + b'F' => Self::End, + b'H' => Self::Home, + b'P' => Self::Fn(1), + b'Q' => Self::Fn(2), + b'R' => Self::Fn(3), + b'S' => Self::Fn(4), _ => bail!(), }) } diff --git a/yazi-version/Cargo.toml b/yazi-version/Cargo.toml new file mode 100644 index 00000000..93f04a85 --- /dev/null +++ b/yazi-version/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "yazi-version" +description = "Yazi version information" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[build-dependencies] +vergen-gitcl = { version = "9.1.0", features = [ "build", "rustc" ] } diff --git a/yazi-version/README.md b/yazi-version/README.md new file mode 100644 index 00000000..21384463 --- /dev/null +++ b/yazi-version/README.md @@ -0,0 +1,5 @@ +# yazi-version + +This crate is part of [Yazi][source], and it is not supposed to be used outside, as there are no guarantees about the stability of its API. + +[source]: https://github.com/sxyazi/yazi diff --git a/yazi-version/build.rs b/yazi-version/build.rs new file mode 100644 index 00000000..2be047c1 --- /dev/null +++ b/yazi-version/build.rs @@ -0,0 +1,25 @@ +use std::{env, error::Error}; + +use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder, RustcBuilder}; + +fn main() -> Result<(), Box> { + Emitter::default() + .add_instructions(&BuildBuilder::default().build_date(true).build()?)? + .add_instructions( + &RustcBuilder::default() + .commit_date(true) + .commit_hash(true) + .host_triple(true) + .semver(true) + .build()?, + )? + .emit()?; + + if env::var_os("YAZI_NO_GITCL").is_none() { + Emitter::default().add_instructions(&GitclBuilder::default().sha(true).build()?)?.emit()?; + } else { + println!("cargo:rustc-env=VERGEN_GIT_SHA=no-gitcl"); + } + + Ok(()) +} diff --git a/yazi-version/src/lib.rs b/yazi-version/src/lib.rs new file mode 100644 index 00000000..a7bce380 --- /dev/null +++ b/yazi-version/src/lib.rs @@ -0,0 +1,27 @@ +use std::{env::consts::{ARCH, OS}, fmt::Write}; + +pub fn version() -> &'static str { concat!(env!("CARGO_PKG_VERSION"), " ", env!("VERGEN_GIT_SHA")) } + +pub fn version_long() -> &'static str { + concat!( + env!("CARGO_PKG_VERSION"), + " (", + env!("VERGEN_GIT_SHA"), + " ", + env!("VERGEN_BUILD_DATE"), + ")" + ) +} + +pub fn version_full() -> String { + let mut s = String::new(); + + writeln!(s, " Version: {}", version_long()).ok(); + writeln!(s, " Debug : {}", cfg!(debug_assertions)).ok(); + #[rustfmt::skip] + writeln!(s, " Triple : {} ({OS}-{ARCH})", env!("VERGEN_RUSTC_HOST_TRIPLE")).ok(); + #[rustfmt::skip] + writeln!(s, " Rustc : {} ({} {})", env!("VERGEN_RUSTC_SEMVER"), &env!("VERGEN_RUSTC_COMMIT_HASH")[..8], env!("VERGEN_RUSTC_COMMIT_DATE")).ok(); + + s +} From 8e80798984864799cd72b3625b21edc33b1ba1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 1 Jun 2026 13:16:01 +0800 Subject: [PATCH 07/28] feat: new gait for input `backward` and `forward` actions (#4012) --- CHANGELOG.md | 5 +++ yazi-binding/src/tty.rs | 22 +++++------ yazi-config/preset/keymap-default.toml | 20 +++++----- yazi-plugin/preset/components/current.lua | 2 +- yazi-plugin/preset/plugins/dnd.lua | 2 +- yazi-plugin/src/fs/fs.rs | 13 +----- yazi-plugin/src/utils/call.rs | 15 +------ yazi-plugin/src/utils/utils.rs | 1 - yazi-shared/src/chars.rs | 23 ----------- yazi-term/src/sequence/dnd.rs | 12 +++--- yazi-vfs/src/fns.rs | 48 ----------------------- yazi-widgets/src/input/actor/backward.rs | 13 +++--- yazi-widgets/src/input/actor/forward.rs | 8 ++-- yazi-widgets/src/input/actor/kill.rs | 4 +- yazi-widgets/src/input/chars.rs | 37 +++++++++++++++++ yazi-widgets/src/input/gait.rs | 10 +++++ yazi-widgets/src/input/mod.rs | 2 +- yazi-widgets/src/input/parser/backward.rs | 28 +++++++++++-- yazi-widgets/src/input/parser/forward.rs | 30 ++++++++++++-- 19 files changed, 145 insertions(+), 150 deletions(-) create mode 100644 yazi-widgets/src/input/chars.rs create mode 100644 yazi-widgets/src/input/gait.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba1e102..af2c8f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Rename `` to `` ([#3989]) - Remove Legacy Console Mode on Windows ([#3989]) +### Deprecated + +- Deprecate `backward --far` and `forward --far` in favor of `backward wide` and `forward wide`, respectively ([#4012]) + ## [v26.5.6] ### Added @@ -1731,3 +1735,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3989]: https://github.com/sxyazi/yazi/pull/3989 [#3990]: https://github.com/sxyazi/yazi/pull/3990 [#4005]: https://github.com/sxyazi/yazi/pull/4005 +[#4012]: https://github.com/sxyazi/yazi/pull/4012 diff --git a/yazi-binding/src/tty.rs b/yazi-binding/src/tty.rs index 272133f6..2713716c 100644 --- a/yazi-binding/src/tty.rs +++ b/yazi-binding/src/tty.rs @@ -2,7 +2,7 @@ use std::io::Write; use mlua::{BorrowedBytes, ExternalError, IntoLuaMulti, Lua, MultiValue, Table, UserData, UserDataMethods}; use yazi_shim::mlua::{ByteString, LuaTableExt}; -use yazi_term::sequence::{ConfirmDrag, ConfirmDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}; +use yazi_term::sequence::{AgreeDrag, AgreeDrop, FinishDrop, PresentDrag, PresentDragIcon, StartDrag, StartDrop}; use yazi_tty::TTY; use crate::Error; @@ -14,22 +14,22 @@ impl Tty { let mut w = TTY.writer(); let result = match kind { - b"ConfirmDrag" => { + b"AgreeDrag" => { let it = t.raw_get::
("mimes")?.sequence_iter::(lua).flatten(); write!(w, "{}", match &*t.raw_get::("type")? { - b"copy" => ConfirmDrag::Copy(it), - b"move" => ConfirmDrag::Move(it), - b"either" => ConfirmDrag::Either(it), - _ => return Err("invalid ConfirmDrag type".into_lua_err()), + b"copy" => AgreeDrag::Copy(it), + b"move" => AgreeDrag::Move(it), + b"either" => AgreeDrag::Either(it), + _ => return Err("invalid AgreeDrag type".into_lua_err()), }) } - b"ConfirmDrop" => { + b"AgreeDrop" => { let it = t.raw_get::
("mimes")?.sequence_iter::(lua).flatten(); write!(w, "{}", match &*t.raw_get::("type")? { - b"reject" => ConfirmDrop::Reject, - b"copy" => ConfirmDrop::Copy(it), - b"move" => ConfirmDrop::Move(it), - _ => return Err("invalid ConfirmDrop type".into_lua_err()), + b"reject" => AgreeDrop::Reject, + b"copy" => AgreeDrop::Copy(it), + b"move" => AgreeDrop::Move(it), + _ => return Err("invalid AgreeDrop type".into_lua_err()), }) } b"StartDrag" => write!(w, "{StartDrag}"), diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index 7d621ec0..900cf806 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -257,16 +257,16 @@ keymap = [ { on = "", run = "move 1", desc = "Move forward a character" }, # Word-wise movement - { on = "b", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "B", run = "backward --far", desc = "Move back to the start of the current or previous WORD" }, - { on = "w", run = "forward", desc = "Move forward to the start of the next word" }, - { on = "W", run = "forward --far", desc = "Move forward to the start of the next WORD" }, - { on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, - { on = "E", run = "forward --far --end-of-word", desc = "Move forward to the end of the current or next WORD" }, - { on = "", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, - { on = "", run = "backward", desc = "Move back to the start of the current or previous word" }, - { on = "", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "b", run = "backward", desc = "Move back to the start of the current or previous word" }, + { on = "B", run = "backward wide", desc = "Move back to the start of the current or previous WORD" }, + { on = "w", run = "forward", desc = "Move forward to the start of the next word" }, + { on = "W", run = "forward wide", desc = "Move forward to the start of the next WORD" }, + { on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "E", run = "forward wide --end-of-word", desc = "Move forward to the end of the current or next WORD" }, + { on = "", run = "backward lean", desc = "Move back to the start of the current or previous word" }, + { on = "", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = "", run = "backward lean", desc = "Move back to the start of the current or previous word" }, + { on = "", run = "forward lean --end-of-word", desc = "Move forward to the end of the current or next word" }, # Line-wise movement { on = "0", run = "move bol", desc = "Move to the BOL" }, diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 01ea28c6..0c15db76 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -86,7 +86,7 @@ function Current:drop(event) if Current._dragging then return elseif event.type == "enter" then - rt.tty:queue("ConfirmDrop", { type = "move", mimes = { "text/uri-list" } }) + rt.tty:queue("AgreeDrop", { type = "move", mimes = { "text/uri-list" } }) elseif event.type == "ready" then rt.tty:queue("StartDrop", { idx = 1 }) elseif event.type == "arrive" then diff --git a/yazi-plugin/preset/plugins/dnd.lua b/yazi-plugin/preset/plugins/dnd.lua index fae4c6ea..6640e42f 100644 --- a/yazi-plugin/preset/plugins/dnd.lua +++ b/yazi-plugin/preset/plugins/dnd.lua @@ -35,7 +35,7 @@ function M.offer_uri_list() end local icon = string.format("%d selected file(s)", #list) - rt.tty:queue("ConfirmDrag", { type = "either", mimes = { "text/uri-list" } }) + rt.tty:queue("AgreeDrag", { type = "either", mimes = { "text/uri-list" } }) rt.tty:queue("PresentDrag", { idx = 0, data = table.concat(list, "\r\n") }) rt.tty:queue("PresentDragIcon", { format = 0, opacity = 0, width = 6, height = 4, data = icon }) rt.tty:queue("StartDrag", {}) diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index b0d4c6f1..cc9b0905 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, SizeCalculator, Url, UrlRef, deprecate}; +use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, SizeCalculator, Url, UrlRef}; use yazi_config::Pattern; use yazi_fs::{mounts::PARTITIONS, provider::{Attrs, DirReader, FileHolder}}; use yazi_shared::url::{UrlCow, UrlLike}; @@ -23,7 +23,6 @@ pub fn compose() -> Composer { b"remove" => remove(lua)?, b"rename" => rename(lua)?, b"unique" => unique(lua)?, - b"unique_name" => unique_name(lua)?, b"write" => write(lua)?, _ => return Ok(Value::Nil), } @@ -231,16 +230,6 @@ fn unique(lua: &Lua) -> mlua::Result { }) } -fn unique_name(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, url: UrlRef| async move { - deprecate!(lua, "`fs.unique_name()` is deprecated, use `fs.unique()` instead, in your {}\nSee #3677 for more details: https://github.com/sxyazi/yazi/pull/3677"); - match yazi_vfs::unique_name(url.clone(), async { false }).await { - Ok(u) => Url::new(u).into_lua_multi(&lua), - Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), - } - }) -} - fn write(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { match provider::write(&*url, data.as_bytes()).await { diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index 343ad17e..b816e685 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -1,9 +1,8 @@ use mlua::{ExternalError, Function, Lua, Table}; use tokio::sync::mpsc; -use yazi_binding::deprecate; use yazi_dds::Sendable; use yazi_macro::emit; -use yazi_shared::{Layer, Source, event::{Action, Cmd}}; +use yazi_shared::{Layer, Source, event::Action}; use super::Utils; @@ -16,18 +15,6 @@ impl Utils { }) } - pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (name, args): (String, Table)| { - deprecate!(lua, "ya.mgr_emit() has been deprecated since v25.5.28 and will soon-to-be removed in a future release. \n\nUse ya.emit() in your {} instead, see #2653 for details: https://github.com/sxyazi/yazi/pull/2653"); - emit!(Call(Action { - cmd: Cmd { name: name.into(), args: Sendable::table_to_args(lua, args)? }, - layer: Layer::Mgr, - source: Source::Emit, - })); - Ok(()) - }) - } - pub(super) fn exec(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (name, args): (String, Table)| async move { let mut action = Action::new(name, Source::Emit, Layer::Mgr)?; diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 3bcbb23a..c128e66e 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -17,7 +17,6 @@ pub fn compose( // Call b"emit" => Utils::emit(lua)?, - b"mgr_emit" => Utils::mgr_emit(lua)?, b"exec" => Utils::exec(lua)?, // Image diff --git a/yazi-shared/src/chars.rs b/yazi-shared/src/chars.rs index 40bfe8f9..3b72ce8c 100644 --- a/yazi-shared/src/chars.rs +++ b/yazi-shared/src/chars.rs @@ -1,29 +1,6 @@ use core::str; use std::borrow::Cow; -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum CharKind { - Space, - Punct, - Other, -} - -impl CharKind { - pub fn new(c: char) -> Self { - if c.is_whitespace() { - Self::Space - } else if c.is_ascii_punctuation() { - Self::Punct - } else { - Self::Other - } - } - - pub fn vary(self, other: Self, far: bool) -> bool { - if far { (self == Self::Space) != (other == Self::Space) } else { self != other } - } -} - pub fn strip_trailing_newline(mut s: String) -> String { while s.ends_with('\n') || s.ends_with('\r') { s.pop(); diff --git a/yazi-term/src/sequence/dnd.rs b/yazi-term/src/sequence/dnd.rs index 681cf612..9591ff17 100644 --- a/yazi-term/src/sequence/dnd.rs +++ b/yazi-term/src/sequence/dnd.rs @@ -37,14 +37,14 @@ impl Display for DisableDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b]72;t=A\x1b\\") } } -/// Confirm drag: `OSC 72 ; t=o:o=operation ST` -pub enum ConfirmDrag { +/// Agree drag: `OSC 72 ; t=o:o=operation ST` +pub enum AgreeDrag { Copy(M), Move(M), Either(M), } -impl Display for ConfirmDrag { +impl Display for AgreeDrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Copy(mimes) => write!(f, "\x1b]72;t=o:o=1;{}\x1b\\", ListDndMimes(mimes.clone())), @@ -54,14 +54,14 @@ impl Display for ConfirmDrag { } } -/// Confirm dropped data: `OSC 72 ; t=m:o=O ; MIME list ST` -pub enum ConfirmDrop { +/// Agree drop: `OSC 72 ; t=m:o=O ; MIME list ST` +pub enum AgreeDrop { Reject, Copy(M), Move(M), } -impl Display for ConfirmDrop { +impl Display for AgreeDrop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Reject => write!(f, "\x1b]72;t=m:o=0\x1b\\"), diff --git a/yazi-vfs/src/fns.rs b/yazi-vfs/src/fns.rs index c2848c74..445b635f 100644 --- a/yazi-vfs/src/fns.rs +++ b/yazi-vfs/src/fns.rs @@ -1,6 +1,5 @@ use std::io::{self}; -use yazi_macro::ok_or_not_found; use yazi_shared::{strand::{StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}}; use crate::provider; @@ -12,53 +11,6 @@ pub async fn maybe_exists(url: impl AsUrl) -> bool { } } -// TODO: deprecate -pub async fn unique_name(u: UrlBuf, append: F) -> io::Result -where - F: Future, -{ - match provider::symlink_metadata(&u).await { - Ok(_) => _unique_name(u, append.await).await, - Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(u), - Err(e) => Err(e), - } -} - -async fn _unique_name(mut url: UrlBuf, append: bool) -> io::Result { - let Some(stem) = url.stem().map(|s| s.to_owned()) else { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); - }; - - let dot_ext = match url.ext() { - Some(e) => { - let mut s = StrandBuf::with_capacity(url.kind(), e.len() + 1); - s.push_str("."); - s.try_push(e)?; - s - } - None => StrandBuf::default(), - }; - - let mut name = StrandBuf::with_capacity(url.kind(), stem.len() + dot_ext.len() + 5); - for i in 1u64.. { - name.clear(); - name.try_push(&stem)?; - - if append { - name.try_push(&dot_ext)?; - name.push_str(format!("_{i}")); - } else { - name.push_str(format!("_{i}")); - name.try_push(&dot_ext)?; - } - - url.try_set_name(&name)?; - ok_or_not_found!(provider::symlink_metadata(&url).await, break); - } - - Ok(url) -} - pub async fn unique_file(u: UrlBuf, is_dir: bool) -> io::Result { let result = if is_dir { provider::create_dir(&u).await diff --git a/yazi-widgets/src/input/actor/backward.rs b/yazi-widgets/src/input/actor/backward.rs index b3b0ea09..de43d488 100644 --- a/yazi-widgets/src/input/actor/backward.rs +++ b/yazi-widgets/src/input/actor/backward.rs @@ -1,8 +1,8 @@ use anyhow::Result; -use yazi_macro::{act, succ}; -use yazi_shared::{CharKind, data::Data}; +use yazi_macro::act; +use yazi_shared::data::Data; -use crate::input::{Input, parser::BackwardOpt}; +use crate::input::{CharKind, Input, parser::BackwardOpt}; impl Input { pub fn backward(&mut self, opt: BackwardOpt) -> Result { @@ -16,15 +16,12 @@ impl Input { let mut prev = CharKind::new(it.next().unwrap().1); for (i, c) in it { let k = CharKind::new(c); - if prev != CharKind::Space && prev.vary(k, opt.far) { + if prev != CharKind::Space && prev.vary(k, opt.gait) { return act!(r#move, self, -(i as isize)); } prev = k; } - if prev != CharKind::Space { - act!(r#move, self, -(snap.len() as isize))?; - } - succ!(); + act!(r#move, self, -(snap.len() as isize)) } } diff --git a/yazi-widgets/src/input/actor/forward.rs b/yazi-widgets/src/input/actor/forward.rs index 846dfc60..d34d8d2e 100644 --- a/yazi-widgets/src/input/actor/forward.rs +++ b/yazi-widgets/src/input/actor/forward.rs @@ -1,8 +1,8 @@ use anyhow::Result; use yazi_macro::act; -use yazi_shared::{CharKind, data::Data}; +use yazi_shared::data::Data; -use crate::input::{Input, op::InputOp, parser::ForwardOpt}; +use crate::input::{CharKind, Input, op::InputOp, parser::ForwardOpt}; impl Input { pub fn forward(&mut self, opt: ForwardOpt) -> Result { @@ -16,9 +16,9 @@ impl Input { for (i, c) in it { let k = CharKind::new(c); let b = if opt.end_of_word { - prev != CharKind::Space && prev.vary(k, opt.far) && i != 1 + prev != CharKind::Space && prev.vary(k, opt.gait) && i != 1 } else { - k != CharKind::Space && k.vary(prev, opt.far) + k != CharKind::Space && k.vary(prev, opt.gait) }; if b && !matches!(snap.op, InputOp::None | InputOp::Select(_)) { return act!(r#move, self, i as isize); diff --git a/yazi-widgets/src/input/actor/kill.rs b/yazi-widgets/src/input/actor/kill.rs index 36bd1b2d..c2f44931 100644 --- a/yazi-widgets/src/input/actor/kill.rs +++ b/yazi-widgets/src/input/actor/kill.rs @@ -2,9 +2,9 @@ use std::ops::RangeBounds; use anyhow::Result; use yazi_macro::{act, render, succ}; -use yazi_shared::{CharKind, data::Data}; +use yazi_shared::data::Data; -use crate::input::{Input, parser::KillOpt}; +use crate::input::{CharKind, Input, parser::KillOpt}; impl Input { pub fn kill(&mut self, opt: KillOpt) -> Result { diff --git a/yazi-widgets/src/input/chars.rs b/yazi-widgets/src/input/chars.rs new file mode 100644 index 00000000..88dab060 --- /dev/null +++ b/yazi-widgets/src/input/chars.rs @@ -0,0 +1,37 @@ +use crate::input::Gait; + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) enum CharKind { + Space, + Punct, + Other, +} + +impl CharKind { + pub(crate) fn new(c: char) -> Self { + if c.is_whitespace() { + Self::Space + } else if Self::is_punct(c) { + Self::Punct + } else { + Self::Other + } + } + + pub(crate) fn vary(self, other: Self, gait: Gait) -> bool { + match gait { + Gait::Fine => self != other, + Gait::Lean => self == Self::Other && other != Self::Other, + Gait::Wide => (self == Self::Space) != (other == Self::Space), + } + } + + const fn is_punct(c: char) -> bool { + match c { + '_' => false, + c if c.is_ascii_punctuation() => true, + '!' | ',' | ';' | '?' => true, + _ => false, + } + } +} diff --git a/yazi-widgets/src/input/gait.rs b/yazi-widgets/src/input/gait.rs new file mode 100644 index 00000000..0cff9263 --- /dev/null +++ b/yazi-widgets/src/input/gait.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum Gait { + #[default] + Fine, + Lean, + Wide, +} diff --git a/yazi-widgets/src/input/mod.rs b/yazi-widgets/src/input/mod.rs index dde2b4e8..90766a7a 100644 --- a/yazi-widgets/src/input/mod.rs +++ b/yazi-widgets/src/input/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(actor parser); -yazi_macro::mod_flat!(event input mode op option snap snaps widget); +yazi_macro::mod_flat!(chars event gait input mode op option snap snaps widget); diff --git a/yazi-widgets/src/input/parser/backward.rs b/yazi-widgets/src/input/parser/backward.rs index b26c8fca..abba7aac 100644 --- a/yazi-widgets/src/input/parser/backward.rs +++ b/yazi-widgets/src/input/parser/backward.rs @@ -1,13 +1,33 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +use crate::input::Gait; + +#[derive(Debug, Deserialize)] pub struct BackwardOpt { - pub far: bool, + #[serde(alias = "0", default)] + pub gait: Gait, } -impl From for BackwardOpt { - fn from(a: ActionCow) -> Self { Self { far: a.bool("far") } } +impl TryFrom for BackwardOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + // TODO: remove + if a.bool("far") { + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + yazi_macro::emit!(Call(yazi_shared::event::Action::new_relay("app:deprecate").with( + "content", + "`backward --far` is deprecated, use `backward wide` under `[input]` instead".to_string() + ))); + } + + return Ok(Self { gait: Gait::Wide }); + } + Ok(a.deserialize()?) + } } impl FromLua for BackwardOpt { diff --git a/yazi-widgets/src/input/parser/forward.rs b/yazi-widgets/src/input/parser/forward.rs index 13415988..be01b94c 100644 --- a/yazi-widgets/src/input/parser/forward.rs +++ b/yazi-widgets/src/input/parser/forward.rs @@ -1,14 +1,36 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use serde::Deserialize; use yazi_shared::event::ActionCow; -#[derive(Debug)] +use crate::input::Gait; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] pub struct ForwardOpt { - pub far: bool, + #[serde(alias = "0", default)] + pub gait: Gait, + #[serde(default)] pub end_of_word: bool, } -impl From for ForwardOpt { - fn from(a: ActionCow) -> Self { Self { far: a.bool("far"), end_of_word: a.bool("end-of-word") } } +impl TryFrom for ForwardOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + // TODO: remove + if a.bool("far") { + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + yazi_macro::emit!(Call(yazi_shared::event::Action::new_relay("app:deprecate").with( + "content", + "`forward --far` is deprecated, use `forward wide` under `[input]` instead".to_string() + ))); + } + + return Ok(Self { gait: Gait::Wide, end_of_word: a.bool("end-of-word") }); + } + Ok(a.deserialize()?) + } } impl FromLua for ForwardOpt { From 9bdd6777e4bfd95a5e6941e2a73f40b12c23e3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 6 Jun 2026 13:45:52 +0800 Subject: [PATCH 08/28] feat: keymap Lua bindings (#4021) --- .github/pull_request_template.md | 4 + CHANGELOG.md | 1 + Cargo.lock | 283 +++++++++++---------- Cargo.toml | 11 +- yazi-actor/src/lives/which.rs | 2 +- yazi-binding/src/data/any.rs | 16 ++ yazi-binding/src/data/mod.rs | 1 + yazi-binding/src/event/action.rs | 19 ++ yazi-binding/src/event/cmd.rs | 19 ++ yazi-binding/src/event/mod.rs | 1 + yazi-binding/src/keymap/chord.rs | 80 ++++++ yazi-binding/src/{ => keymap}/chord_cow.rs | 0 yazi-binding/src/keymap/chords.rs | 53 ++++ yazi-binding/src/keymap/key.rs | 18 ++ yazi-binding/src/keymap/mod.rs | 1 + yazi-binding/src/keymap/section.rs | 48 ++++ yazi-binding/src/lib.rs | 4 +- yazi-config/Cargo.toml | 1 + yazi-config/src/keymap/chord.rs | 85 ++++++- yazi-config/src/keymap/chords.rs | 57 +++++ yazi-config/src/keymap/cow.rs | 16 +- yazi-config/src/keymap/ids.rs | 6 + yazi-config/src/keymap/key.rs | 5 +- yazi-config/src/keymap/keymap.rs | 44 ++-- yazi-config/src/keymap/mod.rs | 2 +- yazi-config/src/keymap/rules.rs | 53 ---- yazi-config/src/keymap/section.rs | 52 ++++ yazi-config/src/lib.rs | 2 +- yazi-config/src/popup/options.rs | 74 +++--- yazi-core/src/app/plugin.rs | 4 +- yazi-core/src/app/quit.rs | 3 + yazi-core/src/cmp/option.rs | 3 + yazi-core/src/help/help.rs | 10 +- yazi-core/src/mgr/displace.rs | 3 + yazi-core/src/mgr/filter.rs | 3 + yazi-core/src/mgr/find.rs | 3 + yazi-core/src/mgr/open.rs | 8 +- yazi-core/src/mgr/search.rs | 6 +- yazi-core/src/notify/option.rs | 3 + yazi-core/src/spot/lock.rs | 3 + yazi-core/src/tab/preview_lock.rs | 3 + yazi-core/src/tasks/option.rs | 3 + yazi-core/src/which/option.rs | 13 +- yazi-core/src/which/which.rs | 2 +- yazi-dds/Cargo.toml | 1 + yazi-dds/src/ember/yank.rs | 18 +- yazi-dds/src/payload.rs | 4 +- yazi-dds/src/sendable.rs | 94 +++---- yazi-fm/src/help/help.rs | 2 +- yazi-fm/src/router.rs | 3 +- yazi-fs/Cargo.toml | 1 + yazi-fs/src/op.rs | 4 +- yazi-macro/src/data.rs | 116 +++++++++ yazi-macro/src/lib.rs | 1 + yazi-plugin/Cargo.toml | 3 +- yazi-plugin/src/fs/op.rs | 21 +- yazi-plugin/src/keymap/keymap.rs | 23 ++ yazi-plugin/src/keymap/mod.rs | 1 + yazi-plugin/src/lib.rs | 2 +- yazi-plugin/src/slim.rs | 1 + yazi-plugin/src/standard.rs | 1 + yazi-plugin/src/utils/layer.rs | 1 + yazi-scheduler/src/process/option.rs | 3 + yazi-scheduler/src/summary.rs | 3 + yazi-shared/Cargo.toml | 2 + yazi-shared/src/any_data.rs | 8 + yazi-shared/src/completion_token.rs | 3 + yazi-shared/src/data/any.rs | 61 ++++- yazi-shared/src/data/data.rs | 8 +- yazi-shared/src/data/inventory.rs | 9 + yazi-shared/src/data/mod.rs | 2 +- yazi-shared/src/lib.rs | 4 +- yazi-shared/src/url/buf.rs | 3 + yazi-shim/Cargo.toml | 2 +- yazi-shim/src/mlua/traits.rs | 2 +- yazi-term/Cargo.toml | 1 + yazi-term/src/event/keyboard.rs | 10 +- 77 files changed, 1068 insertions(+), 378 deletions(-) create mode 100644 yazi-binding/src/data/any.rs create mode 100644 yazi-binding/src/data/mod.rs create mode 100644 yazi-binding/src/event/action.rs create mode 100644 yazi-binding/src/event/cmd.rs create mode 100644 yazi-binding/src/event/mod.rs create mode 100644 yazi-binding/src/keymap/chord.rs rename yazi-binding/src/{ => keymap}/chord_cow.rs (100%) create mode 100644 yazi-binding/src/keymap/chords.rs create mode 100644 yazi-binding/src/keymap/key.rs create mode 100644 yazi-binding/src/keymap/mod.rs create mode 100644 yazi-binding/src/keymap/section.rs create mode 100644 yazi-config/src/keymap/chords.rs create mode 100644 yazi-config/src/keymap/ids.rs delete mode 100644 yazi-config/src/keymap/rules.rs create mode 100644 yazi-config/src/keymap/section.rs create mode 100644 yazi-macro/src/data.rs create mode 100644 yazi-plugin/src/keymap/keymap.rs create mode 100644 yazi-plugin/src/keymap/mod.rs create mode 100644 yazi-shared/src/any_data.rs create mode 100644 yazi-shared/src/data/inventory.rs diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6ad65887..8c7716aa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,3 +15,7 @@ A clear and concise description of the rationale of the changes, to help our rev If it has already been detailed in the associated issue, please skip this section. --> + +## Checklist + +- [ ] I have read [CONTRIBUTING.md](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md) diff --git a/CHANGELOG.md b/CHANGELOG.md index af2c8f23..9f8db42d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Drag and drop ([#4005]) - Bulk create ([#3793]) - Image preview with Überzug++ on Niri ([#3990]) +- New gait for input `backward` and `forward` actions ([#4012]) ### Changed diff --git a/Cargo.lock b/Cargo.lock index a8e9faab..2ad2822a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" dependencies = [ "crypto-common 0.2.2", - "inout 0.2.2", + "inout", ] [[package]] @@ -165,6 +165,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -378,9 +387,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] @@ -434,15 +443,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "block-padding" version = "0.4.2" @@ -594,9 +594,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -614,7 +614,7 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", "crypto-common 0.2.2", - "inout 0.2.2", + "inout", "zeroize", ] @@ -804,6 +804,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -844,7 +850,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "crossterm_winapi", "mio", "parking_lot", @@ -861,7 +867,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "crossterm_winapi", "derive_more", "document-features", @@ -969,12 +975,12 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "5.0.0-pre.6" +version = "5.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" +checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "curve25519-dalek-derive", "digest 0.11.3", "fiat-crypto", @@ -1274,9 +1280,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "3.0.0-pre.7" +version = "3.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" +checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60" dependencies = [ "curve25519-dalek", "ed25519", @@ -1296,22 +1302,22 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elliptic-curve" -version = "0.14.0-rc.32" +version = "0.14.0-rc.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda94f31325c4275e9706adecbb6f0650dee2f904c915a98e3d81adaaaa757aa" +checksum = "102d3643d30dd8b559613c5cced68317199597fffb278cdc88daa2ef7fafc935" dependencies = [ "base16ct", "crypto-bigint", "crypto-common 0.2.2", "digest 0.11.3", + "ff", + "group", "hkdf", "hybrid-array", "once_cell", "pem-rfc7468", "pkcs8", "rand_core 0.10.1", - "rustcrypto-ff", - "rustcrypto-group", "sec1", "subtle", "zeroize", @@ -1469,6 +1475,16 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core 0.10.1", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.3.0" @@ -1663,10 +1679,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1688,11 +1702,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -1733,6 +1749,17 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core 0.10.1", + "subtle", +] + [[package]] name = "half" version = "2.7.1" @@ -1946,11 +1973,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "inotify-sys", "libc", ] @@ -1964,23 +1991,13 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding 0.3.3", - "generic-array 0.14.7", -] - [[package]] name = "inout" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ - "block-padding 0.4.2", + "block-padding", "hybrid-array", ] @@ -2020,6 +2037,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2110,7 +2136,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "libc", ] @@ -2146,9 +2172,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -2175,7 +2201,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] @@ -2207,9 +2233,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.30" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "loop9" @@ -2220,15 +2246,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "lru" version = "0.18.0" @@ -2287,9 +2304,9 @@ dependencies = [ [[package]] name = "md5" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" @@ -2438,7 +2455,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -2451,7 +2468,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -2497,7 +2514,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "fsevent-sys", "inotify", "kqueue", @@ -2515,7 +2532,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] @@ -2630,7 +2647,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "objc2", ] @@ -2661,7 +2678,7 @@ version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "libc", "once_cell", "onig_sys", @@ -2714,9 +2731,9 @@ dependencies = [ [[package]] name = "p256" -version = "0.14.0-rc.9" +version = "0.14.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b97e3bf0465157ae90975ff52dbeb1362ba618924878c9f74c25baa27a65f9a" +checksum = "41adc63effe99d48837a8cc0e6d7a77e32ae6a07f6000df466178dbc2193093e" dependencies = [ "ecdsa", "elliptic-curve", @@ -2727,9 +2744,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.14.0-rc.9" +version = "0.14.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437f30ebcb1e16ff48acead5f08bd69fbcdbc82421687bb48af5c315a0bfab03" +checksum = "9bd5333afa5ae0347f39e6a0f2c9c155da431583fd71fe5555bd0521b4ccaf02" dependencies = [ "ecdsa", "elliptic-curve", @@ -2741,9 +2758,9 @@ dependencies = [ [[package]] name = "p521" -version = "0.14.0-rc.9" +version = "0.14.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9fd792bab86ecf6249561752fb5a413511f999887107dd054bbda5143743d7" +checksum = "a3a5297f53dc16d35909060ba3032cff7867e8809f01e273ff325579d5f0ceae" dependencies = [ "base16ct", "ecdsa", @@ -2779,6 +2796,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" dependencies = [ + "approx", "bytemuck", "fast-srgb8", "libm", @@ -3046,7 +3064,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -3108,23 +3126,23 @@ dependencies = [ [[package]] name = "primefield" -version = "0.14.0-rc.9" +version = "0.14.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b52e6ee42db392378a95622b463c9740631171d1efce43fa445a569c1600cb6" +checksum = "f845ec3240cd5ed5e1e31cf3ff633a5bf47c698dc4092ba9e767415b3d393406" dependencies = [ "crypto-bigint", "crypto-common 0.2.2", + "ff", "rand_core 0.10.1", - "rustcrypto-ff", "subtle", "zeroize", ] [[package]] name = "primeorder" -version = "0.14.0-rc.9" +version = "0.14.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0556580e42c19833f5d232aca11a7687a503ee41f937b54f5ae1d50fc2a6a36a" +checksum = "7d2793f22b9b6fd11ef3ac1d59bf003c2573593e4968702341605c2748fd90bf" dependencies = [ "elliptic-curve", ] @@ -3334,19 +3352,21 @@ dependencies = [ [[package]] name = "ratatui-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "compact_str", - "hashbrown 0.16.1", + "critical-section", + "hashbrown 0.17.1", "indoc", "itertools", "kasuari", - "lru 0.16.4", + "lru", + "palette", "serde", - "strum 0.27.2", + "strum 0.28.0", "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", @@ -3355,9 +3375,9 @@ dependencies = [ [[package]] name = "ratatui-crossterm" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c" dependencies = [ "cfg-if", "crossterm 0.28.1", @@ -3368,9 +3388,9 @@ dependencies = [ [[package]] name = "ratatui-termion" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cade85a8591fbc911e147951422f0d6fd40f4948b271b6216c7dc01838996f8" +checksum = "5c16cc35a9d9114e0b2bb4b22018b96ae7f5fe60e2595dc73e622b4e78624835" dependencies = [ "instability", "ratatui-core", @@ -3379,9 +3399,9 @@ dependencies = [ [[package]] name = "ratatui-termwiz" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967" dependencies = [ "ratatui-core", "termwiz", @@ -3393,7 +3413,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "hashbrown 0.16.1", "indoc", "instability", @@ -3482,7 +3502,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] @@ -3596,13 +3616,13 @@ dependencies = [ [[package]] name = "russh" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67013f080c226e5a34db1c71f2567f44d95a6300005bb6cd4e2c8fe3c326d1b" +checksum = "bbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0" dependencies = [ "aes", - "bitflags 2.11.1", - "block-padding 0.4.2", + "bitflags 2.13.0", + "block-padding", "byteorder", "bytes", "cbc", @@ -3620,12 +3640,11 @@ dependencies = [ "enum_dispatch", "futures", "generic-array 1.4.3", - "getrandom 0.2.17", + "getrandom 0.4.2", "ghash", "hex-literal", - "hkdf", "hmac", - "inout 0.1.4", + "inout", "internal-russh-num-bigint", "keccak", "log", @@ -3711,34 +3730,13 @@ dependencies = [ "semver", ] -[[package]] -name = "rustcrypto-ff" -version = "0.14.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" -dependencies = [ - "rand_core 0.10.1", - "subtle", -] - -[[package]] -name = "rustcrypto-group" -version = "0.14.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed" -dependencies = [ - "rand_core 0.10.1", - "rustcrypto-ff", - "subtle", -] - [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3751,7 +3749,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3924,9 +3922,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64", "bs58", @@ -3944,9 +3942,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -4378,7 +4376,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64", - "bitflags 2.11.1", + "bitflags 2.13.0", "fancy-regex", "filedescriptor", "finl_unicode", @@ -4779,9 +4777,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-truncate" @@ -5045,7 +5043,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "hashbrown 0.15.5", "indexmap 2.14.0", "semver", @@ -5142,9 +5140,9 @@ dependencies = [ [[package]] name = "wide" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7714cd0430a663154667c74da5d09325c2387695bee18b3f7f72825aa3693a" +checksum = "dfdfe6a32973f2d1b268b8895845a8a96cac2f0191e72c27cc929036060dbf89" dependencies = [ "bytemuck", "safe_arch", @@ -5579,7 +5577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.13.0", "indexmap 2.14.0", "log", "serde", @@ -5790,10 +5788,11 @@ version = "26.5.6" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.11.1", + "bitflags 2.13.0", "globset", "hashbrown 0.17.1", "indexmap 2.14.0", + "mlua", "ratatui", "regex", "serde", @@ -5855,6 +5854,7 @@ dependencies = [ "anyhow", "hashbrown 0.17.1", "indexmap 2.14.0", + "inventory", "mlua", "ordered-float 5.3.0", "parking_lot", @@ -5951,13 +5951,14 @@ version = "26.5.6" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.11.1", + "bitflags 2.13.0", "core-foundation-sys", "dirs", "either", "foldhash 0.2.0", "hashbrown 0.17.1", "libc", + "mlua", "objc2", "parking_lot", "percent-encoding", @@ -5991,7 +5992,7 @@ name = "yazi-parser" version = "26.5.6" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.13.0", "hashbrown 0.17.1", "mlua", "paste", @@ -6021,9 +6022,9 @@ dependencies = [ "ansi-to-tui", "anyhow", "futures", + "inventory", "libc", "mlua", - "paste", "percent-encoding", "ratatui", "serde_json", @@ -6036,6 +6037,7 @@ dependencies = [ "yazi-adapter", "yazi-binding", "yazi-boot", + "yazi-codegen", "yazi-config", "yazi-core", "yazi-dds", @@ -6104,7 +6106,7 @@ dependencies = [ "foldhash 0.2.0", "hashbrown 0.17.1", "libc", - "lru 0.18.0", + "lru", "mlua", "ordered-float 5.3.0", "parking_lot", @@ -6127,7 +6129,7 @@ dependencies = [ name = "yazi-sftp" version = "26.5.6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "parking_lot", "russh", "serde", @@ -6144,8 +6146,10 @@ dependencies = [ "foldhash 0.2.0", "futures", "hashbrown 0.17.1", + "inventory", "libc", "memchr", + "mlua", "ordered-float 5.3.0", "parking_lot", "paste", @@ -6188,11 +6192,12 @@ version = "26.5.6" dependencies = [ "anyhow", "base64", - "bitflags 2.11.1", + "bitflags 2.13.0", "futures", "parking_lot", "ratatui", "rustix 1.1.4", + "serde", "signal-hook 0.4.4", "strum 0.28.0", "thiserror 2.0.18", diff --git a/Cargo.toml b/Cargo.toml index 516ac590..1056c8b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,8 +39,8 @@ ansi-to-tui = "8.0.1" anyhow = "1.0.102" arc-swap = { version = "1.9.1", features = [ "serde" ] } base64 = "0.22.1" -bitflags = { version = "2.11.1", features = [ "serde" ] } -chrono = "0.4.44" +bitflags = { version = "2.13.0", features = [ "serde" ] } +chrono = "0.4.45" clap = { version = "4.6.1", features = [ "derive" ] } core-foundation-sys = "0.8.7" dirs = "6.0.0" @@ -51,6 +51,7 @@ futures = "0.3.32" globset = "0.4.18" hashbrown = { version = "0.17.1", features = [ "serde" ] } indexmap = { version = "2.14.0", features = [ "serde" ] } +inventory = "0.3.24" libc = "0.2.186" lru = "0.18.0" mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] } @@ -60,13 +61,13 @@ parking_lot = "0.12.5" paste = "1.0.15" percent-encoding = "2.3.2" rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } -ratatui = { version = "0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } +ratatui = { version = "=0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.3" -russh = { version = "0.61.1", default-features = false, features = [ "ring", "rsa" ] } +russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.150" -serde_with = "3.20.0" +serde_with = "3.21.0" strum = { version = "0.28.0", features = [ "derive" ] } syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } thiserror = "2.0.18" diff --git a/yazi-actor/src/lives/which.rs b/yazi-actor/src/lives/which.rs index 66016cfd..e44281aa 100644 --- a/yazi-actor/src/lives/which.rs +++ b/yazi-actor/src/lives/which.rs @@ -29,7 +29,7 @@ impl UserData for Which { cached_field!(fields, tx, |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); cached_field!(fields, cands, |lua, me| { - lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::ChordCow)) + lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow)) }); fields.add_field_method_get("active", |_, me| Ok(me.inner.active)); diff --git a/yazi-binding/src/data/any.rs b/yazi-binding/src/data/any.rs new file mode 100644 index 00000000..eb5e353d --- /dev/null +++ b/yazi-binding/src/data/any.rs @@ -0,0 +1,16 @@ +use mlua::UserData; +use yazi_codegen::FromLuaOwned; + +#[derive(FromLuaOwned)] +pub struct DataAny(Box); + +impl DataAny { + pub fn new(value: T) -> Self + where + T: yazi_shared::data::DataAny, + { + Self(Box::new(value)) + } +} + +impl UserData for DataAny {} diff --git a/yazi-binding/src/data/mod.rs b/yazi-binding/src/data/mod.rs new file mode 100644 index 00000000..83b99df1 --- /dev/null +++ b/yazi-binding/src/data/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(any); diff --git a/yazi-binding/src/event/action.rs b/yazi-binding/src/event/action.rs new file mode 100644 index 00000000..d108be34 --- /dev/null +++ b/yazi-binding/src/event/action.rs @@ -0,0 +1,19 @@ +use std::ops::Deref; + +use mlua::UserData; + +pub struct Action { + inner: yazi_shared::event::Action, +} + +impl Deref for Action { + type Target = yazi_shared::event::Action; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Action { + pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } +} + +impl UserData for Action {} diff --git a/yazi-binding/src/event/cmd.rs b/yazi-binding/src/event/cmd.rs new file mode 100644 index 00000000..bd46f44e --- /dev/null +++ b/yazi-binding/src/event/cmd.rs @@ -0,0 +1,19 @@ +use std::ops::Deref; + +use mlua::UserData; + +pub struct Cmd { + inner: yazi_shared::event::Cmd, +} + +impl Deref for Cmd { + type Target = yazi_shared::event::Cmd; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Cmd { + pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } +} + +impl UserData for Cmd {} diff --git a/yazi-binding/src/event/mod.rs b/yazi-binding/src/event/mod.rs new file mode 100644 index 00000000..62b86ac6 --- /dev/null +++ b/yazi-binding/src/event/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(action cmd); diff --git a/yazi-binding/src/keymap/chord.rs b/yazi-binding/src/keymap/chord.rs new file mode 100644 index 00000000..dc0a6b4f --- /dev/null +++ b/yazi-binding/src/keymap/chord.rs @@ -0,0 +1,80 @@ +use std::{ops::Deref, sync::Arc}; + +use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; + +use crate::{Id, Iter, cached_field, event::Action, keymap::Key}; + +pub struct Chord { + inner: Arc, + + v_on: Option, + v_run: Option, +} + +impl Deref for Chord { + type Target = Arc; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl From for Arc { + fn from(value: Chord) -> Self { value.inner } +} + +impl Chord { + pub fn new(inner: impl Into>) -> Self { + Self { inner: inner.into(), v_on: None, v_run: None } + } +} + +impl FromLua for Chord { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + Ok(Self::new(lua.from_value::(value)?)) + } +} + +impl UserData for Chord { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id))); + + cached_field!(fields, on, |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); + + cached_field!(fields, run, |lua, me| lua + .create_sequence_from(me.run.iter().cloned().map(Action::new))); + } +} + +// --- Matcher +pub struct ChordMatcher(pub(super) yazi_config::keymap::ChordMatcher); + +impl TryFrom
for ChordMatcher { + type Error = mlua::Error; + + fn try_from(value: Table) -> Result { + let id: Id = value.raw_get("id").unwrap_or_default(); + + Ok(Self(yazi_config::keymap::ChordMatcher { id: id.0, ..Default::default() })) + } +} + +impl FromLua for ChordMatcher { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + match value { + Value::Table(t) => t.try_into(), + _ => Err("expected a table of ChordMatcher".into_lua_err()), + } + } +} + +// --- ChordIter +pub struct ChordIter(pub(super) yazi_config::keymap::ChordIter); + +impl ChordIter { + pub fn new(inner: impl Into) -> Self { Self(inner.into()) } +} + +impl IntoLua for ChordIter { + fn into_lua(self, lua: &Lua) -> mlua::Result { + Iter::new(self.0.map(Chord::new), None).into_lua(lua) + } +} diff --git a/yazi-binding/src/chord_cow.rs b/yazi-binding/src/keymap/chord_cow.rs similarity index 100% rename from yazi-binding/src/chord_cow.rs rename to yazi-binding/src/keymap/chord_cow.rs diff --git a/yazi-binding/src/keymap/chords.rs b/yazi-binding/src/keymap/chords.rs new file mode 100644 index 00000000..07c160bc --- /dev/null +++ b/yazi-binding/src/keymap/chords.rs @@ -0,0 +1,53 @@ +use std::ops::Deref; + +use mlua::{ExternalError, ExternalResult, MetaMethod, UserData, UserDataMethods}; + +use super::{Chord, ChordMatcher}; +use crate::keymap::ChordIter; + +pub struct Chords { + inner: &'static yazi_config::keymap::Chords, +} + +impl Deref for Chords { + type Target = yazi_config::keymap::Chords; + + fn deref(&self) -> &Self::Target { self.inner } +} + +impl Chords { + pub fn new(inner: &'static yazi_config::keymap::Chords) -> Self { Self { inner } } +} + +impl UserData for Chords { + fn add_methods>(methods: &mut M) { + methods.add_method("match", |_, me, matcher: Option| { + Ok(match matcher { + Some(matcher) => ChordIter::new(yazi_config::keymap::ChordIter { + chords: me.as_erased(), + matcher: matcher.0, + ..Default::default() + }), + None => ChordIter::new(&*me.inner), + }) + }); + + methods.add_method("insert", |_, me, (index, chord): (isize, Chord)| { + let index = match index { + 1.. => index - 1, + 0 => return Err("index must be 1-based or negative".into_lua_err()), + _ => index, + }; + + me.insert(index, chord.clone()).into_lua_err()?; + Ok(chord) + }); + + methods.add_method("remove", |_, me, matcher: ChordMatcher| { + me.remove(matcher.0); + Ok(()) + }); + + methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.load().len())); + } +} diff --git a/yazi-binding/src/keymap/key.rs b/yazi-binding/src/keymap/key.rs new file mode 100644 index 00000000..2e35cad7 --- /dev/null +++ b/yazi-binding/src/keymap/key.rs @@ -0,0 +1,18 @@ +use std::ops::Deref; + +use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; + +use crate::SER_OPT; + +#[derive(Clone, Copy, Default, FromLua)] +pub struct Key(pub yazi_config::keymap::Key); + +impl Deref for Key { + type Target = yazi_config::keymap::Key; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl IntoLua for Key { + fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self.0, SER_OPT) } +} diff --git a/yazi-binding/src/keymap/mod.rs b/yazi-binding/src/keymap/mod.rs new file mode 100644 index 00000000..80857a4a --- /dev/null +++ b/yazi-binding/src/keymap/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(chord chord_cow chords key section); diff --git a/yazi-binding/src/keymap/section.rs b/yazi-binding/src/keymap/section.rs new file mode 100644 index 00000000..7f8ee4de --- /dev/null +++ b/yazi-binding/src/keymap/section.rs @@ -0,0 +1,48 @@ +use std::ops::Deref; + +use anyhow::bail; +use mlua::{UserData, UserDataFields, Value}; +use yazi_config::KEYMAP; +use yazi_shared::Layer; + +use crate::{cached_field, keymap::Chords}; + +pub struct KeymapSection { + inner: &'static yazi_config::keymap::KeymapSection, + + v_rules: Option, +} + +impl Deref for KeymapSection { + type Target = yazi_config::keymap::KeymapSection; + + fn deref(&self) -> &Self::Target { self.inner } +} + +impl TryFrom for KeymapSection { + type Error = anyhow::Error; + + fn try_from(value: Layer) -> Result { + let inner = match value { + Layer::Null | Layer::App => bail!("invalid layer"), + Layer::Mgr => KEYMAP.mgr.as_erased(), + Layer::Tasks => KEYMAP.tasks.as_erased(), + Layer::Spot => KEYMAP.spot.as_erased(), + Layer::Pick => KEYMAP.pick.as_erased(), + Layer::Input => KEYMAP.input.as_erased(), + Layer::Confirm => KEYMAP.confirm.as_erased(), + Layer::Help => KEYMAP.help.as_erased(), + Layer::Cmp => KEYMAP.cmp.as_erased(), + Layer::Which => bail!("invalid layer"), + Layer::Notify => bail!("invalid layer"), + }; + + Ok(Self { inner, v_rules: None }) + } +} + +impl UserData for KeymapSection { + fn add_fields>(fields: &mut F) { + cached_field!(fields, rules, |_, me| Ok(Chords::new(me.inner))); + } +} diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index 548e58aa..b6f1f48b 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -1,5 +1,5 @@ mod macros; -yazi_macro::mod_pub!(config elements process theme); +yazi_macro::mod_pub!(config data elements event keymap process theme); -yazi_macro::mod_flat!(access calculator cha chan chord_cow composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); +yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index ea98433b..9b01c7c9 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -28,6 +28,7 @@ bitflags = { workspace = true } globset = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } +mlua = { workspace = true } ratatui = { workspace = true } regex = { workspace = true } serde = { workspace = true } diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index d99ee567..a69b18be 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -1,17 +1,19 @@ -use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock}; +use std::{borrow::Cow, hash::{Hash, Hasher}, sync::{Arc, OnceLock}}; use regex::Regex; use serde::{Deserialize, Deserializer, de}; use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; -use yazi_shared::{Layer, Source, event::Action}; +use yazi_shared::{Id, Layer, Source, event::Action}; -use super::Key; -use crate::{Mixable, Platform}; +use super::{Key, ids::chord_id}; +use crate::{Mixable, Platform, keymap::Chords}; static RE: OnceLock = OnceLock::new(); -#[derive(Clone, Debug, Default, Deserialize)] -pub struct Chord { +#[derive(Debug, Default, Deserialize)] +pub struct Chord { + #[serde(skip, default = "chord_id")] + pub id: Id, #[serde(deserialize_with = "deserialize_on")] pub on: Vec, #[serde(deserialize_with = "deserialize_run::")] @@ -22,6 +24,18 @@ pub struct Chord { pub r#for: Platform, } +impl Clone for Chord { + fn clone(&self) -> Self { + Self { + id: chord_id(), + on: self.on.clone(), + run: self.run.clone(), + desc: self.desc.clone(), + r#for: self.r#for, + } + } +} + impl PartialEq for Chord { fn eq(&self, other: &Self) -> bool { self.on == other.on } } @@ -33,6 +47,10 @@ impl Hash for Chord { } impl Chord { + pub fn as_erased(self: &Arc) -> &Arc> { + unsafe { &*(self as *const Arc> as *const Arc>) } + } + pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() } pub fn run(&self) -> String { @@ -81,12 +99,12 @@ fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result, where D: Deserializer<'de>, { - let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; - let Some(layer) = Layer::from_repr(L) else { return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); }; + let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; + for action in &mut actions { action.source = Source::Key; if action.layer == Layer::Null { @@ -96,3 +114,54 @@ where Ok(actions) } + +// --- Matcher +#[derive(Default)] +pub struct ChordMatcher { + pub id: Id, + pub all: bool, +} + +impl ChordMatcher { + pub fn matches(&self, chord: &Chord) -> bool { + if self.all { + true + } else if self.id != Id::ZERO { + chord.id == self.id + } else { + false + } + } +} + +// --- Iter +#[derive(Default)] +pub struct ChordIter { + pub chords: Arc>>, + pub matcher: ChordMatcher, + pub offset: usize, +} + +impl From<&Chords> for ChordIter { + fn from(chords: &Chords) -> Self { + Self { + chords: chords.load_full(), + matcher: ChordMatcher { all: true, ..Default::default() }, + ..Default::default() + } + } +} + +impl Iterator for ChordIter { + type Item = Arc; + + fn next(&mut self) -> Option { + while let Some(chord) = self.chords.get(self.offset) { + self.offset += 1; + if self.matcher.matches(chord) { + return Some(chord.clone()); + } + } + None + } +} diff --git a/yazi-config/src/keymap/chords.rs b/yazi-config/src/keymap/chords.rs new file mode 100644 index 00000000..7f6f0807 --- /dev/null +++ b/yazi-config/src/keymap/chords.rs @@ -0,0 +1,57 @@ +use std::{ops::Deref, sync::Arc}; + +use arc_swap::ArcSwap; +use serde::Deserialize; +use yazi_shared::Layer; +use yazi_shim::{arc_swap::{ArcSwapExt, IntoPointee}, vec::{IndexAtError, VecExt}}; + +use super::Chord; +use crate::keymap::ChordMatcher; + +#[derive(Debug, Default, Deserialize)] +pub struct Chords(ArcSwap>>>); + +impl Deref for Chords { + type Target = ArcSwap>>>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl From>>> for Chords { + fn from(inner: Vec>>) -> Self { Self(inner.into_pointee()) } +} + +impl Chords { + pub fn as_erased(&self) -> Arc>>> { + let chords = self.0.load_full(); + unsafe { Arc::from_raw(Arc::into_raw(chords) as *const Vec>>) } + } + + pub fn insert(&self, index: isize, rule: Arc) -> Result<(), IndexAtError> { + self.0.try_rcu(|rules| { + let (before, after) = rules.split_at(rules.index_at(index)?); + Ok( + before + .iter() + .cloned() + .chain([rule.as_erased().clone()]) + .chain(after.iter().cloned()) + .collect::>(), + ) + })?; + + Ok(()) + } + + pub fn remove(&self, matcher: ChordMatcher) { + self.0.rcu(|chords| { + let mut next = Vec::clone(chords); + next.retain(|chord| !matcher.matches(chord.as_erased())); + next + }); + } + + pub(crate) fn unwrap_unchecked(self) -> Vec>> { + Arc::try_unwrap(self.0.into_inner()).expect("unique chords arc") + } +} diff --git a/yazi-config/src/keymap/cow.rs b/yazi-config/src/keymap/cow.rs index 472405d7..cf9a2d1c 100644 --- a/yazi-config/src/keymap/cow.rs +++ b/yazi-config/src/keymap/cow.rs @@ -1,21 +1,25 @@ -use std::ops::Deref; +use std::{ops::Deref, sync::Arc}; use yazi_shared::{Layer, event::ActionCow}; use super::Chord; #[derive(Clone, Debug)] -pub enum ChordCow { +pub enum ChordCow { Owned(Chord), - Borrowed(&'static Chord), + Borrowed(Arc>), } impl From> for ChordCow { fn from(c: Chord) -> Self { Self::Owned(c) } } -impl From<&'static Chord> for ChordCow { - fn from(c: &'static Chord) -> Self { Self::Borrowed(c) } +impl From>> for ChordCow { + fn from(c: Arc>) -> Self { Self::Borrowed(c) } +} + +impl From<&Arc>> for ChordCow { + fn from(c: &Arc>) -> Self { Self::Borrowed(c.clone()) } } impl Deref for ChordCow { @@ -37,7 +41,7 @@ impl ChordCow { pub fn into_seq(self) -> Vec { match self { Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(), - Self::Borrowed(c) => c.run.iter().rev().map(Into::into).collect(), + Self::Borrowed(c) => c.run.iter().rev().cloned().map(Into::into).collect(), } } } diff --git a/yazi-config/src/keymap/ids.rs b/yazi-config/src/keymap/ids.rs new file mode 100644 index 00000000..82149e34 --- /dev/null +++ b/yazi-config/src/keymap/ids.rs @@ -0,0 +1,6 @@ +use yazi_shared::{Id, Ids}; + +pub fn chord_id() -> Id { + static IDS: Ids = Ids::new(); + IDS.next() +} diff --git a/yazi-config/src/keymap/key.rs b/yazi-config/src/keymap/key.rs index 4cd39b47..f36f2a2c 100644 --- a/yazi-config/src/keymap/key.rs +++ b/yazi-config/src/keymap/key.rs @@ -1,14 +1,17 @@ use std::{fmt::{Display, Write}, str::FromStr}; use anyhow::bail; +use serde::{Deserialize, Serialize}; use yazi_term::event::{KeyCode, KeyEvent, Modifiers}; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Key { + #[serde(flatten)] pub code: KeyCode, pub shift: bool, pub ctrl: bool, pub alt: bool, + #[serde(rename = "super")] pub super_: bool, } diff --git a/yazi-config/src/keymap/keymap.rs b/yazi-config/src/keymap/keymap.rs index 98bdca94..36712efe 100644 --- a/yazi-config/src/keymap/keymap.rs +++ b/yazi-config/src/keymap/keymap.rs @@ -1,37 +1,39 @@ +use std::{ops::Deref, sync::Arc}; + use anyhow::{Context, Result}; use serde::Deserialize; use yazi_codegen::{DeserializeOver, DeserializeOver1}; use yazi_fs::{Xdg, ok_or_not_found}; use yazi_shared::Layer; -use super::{Chord, KeymapRules}; +use super::{Chord, KeymapSection}; #[derive(Deserialize, DeserializeOver, DeserializeOver1)] pub struct Keymap { - pub mgr: KeymapRules<{ Layer::Mgr as u8 }>, - pub tasks: KeymapRules<{ Layer::Tasks as u8 }>, - pub spot: KeymapRules<{ Layer::Spot as u8 }>, - pub pick: KeymapRules<{ Layer::Pick as u8 }>, - pub input: KeymapRules<{ Layer::Input as u8 }>, - pub confirm: KeymapRules<{ Layer::Confirm as u8 }>, - pub help: KeymapRules<{ Layer::Help as u8 }>, - pub cmp: KeymapRules<{ Layer::Cmp as u8 }>, + pub mgr: KeymapSection<{ Layer::Mgr as u8 }>, + pub tasks: KeymapSection<{ Layer::Tasks as u8 }>, + pub spot: KeymapSection<{ Layer::Spot as u8 }>, + pub pick: KeymapSection<{ Layer::Pick as u8 }>, + pub input: KeymapSection<{ Layer::Input as u8 }>, + pub confirm: KeymapSection<{ Layer::Confirm as u8 }>, + pub help: KeymapSection<{ Layer::Help as u8 }>, + pub cmp: KeymapSection<{ Layer::Cmp as u8 }>, } impl Keymap { - pub fn get(&self, layer: Layer) -> &[Chord] { + pub fn get(&self, layer: Layer) -> Arc>> { match layer { - Layer::Null | Layer::App => &[], - Layer::Mgr => self.mgr.as_erased_slice(), - Layer::Tasks => self.tasks.as_erased_slice(), - Layer::Spot => self.spot.as_erased_slice(), - Layer::Pick => self.pick.as_erased_slice(), - Layer::Input => self.input.as_erased_slice(), - Layer::Confirm => self.confirm.as_erased_slice(), - Layer::Help => self.help.as_erased_slice(), - Layer::Cmp => self.cmp.as_erased_slice(), - Layer::Which => &[], - Layer::Notify => &[], + Layer::Null | Layer::App => Arc::new(Vec::new()), + Layer::Mgr => self.mgr.deref().as_erased(), + Layer::Tasks => self.tasks.deref().as_erased(), + Layer::Spot => self.spot.deref().as_erased(), + Layer::Pick => self.pick.deref().as_erased(), + Layer::Input => self.input.deref().as_erased(), + Layer::Confirm => self.confirm.deref().as_erased(), + Layer::Help => self.help.deref().as_erased(), + Layer::Cmp => self.cmp.deref().as_erased(), + Layer::Which => Arc::new(Vec::new()), + Layer::Notify => Arc::new(Vec::new()), } } } diff --git a/yazi-config/src/keymap/mod.rs b/yazi-config/src/keymap/mod.rs index ddc3039a..b608ba6a 100644 --- a/yazi-config/src/keymap/mod.rs +++ b/yazi-config/src/keymap/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(chord cow key keymap rules); +yazi_macro::mod_flat!(chord chords cow ids key keymap section); diff --git a/yazi-config/src/keymap/rules.rs b/yazi-config/src/keymap/rules.rs deleted file mode 100644 index b3276bf1..00000000 --- a/yazi-config/src/keymap/rules.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::ops::Deref; - -use hashbrown::HashSet; -use serde::Deserialize; -use yazi_codegen::DeserializeOver2; -use yazi_shim::toml::DeserializeOverHook; - -use super::{Chord, Key}; -use crate::mix; - -#[derive(Default, Deserialize, DeserializeOver2)] -pub struct KeymapRules { - keymap: Vec>, - #[serde(default)] - prepend_keymap: Vec>, - #[serde(default)] - append_keymap: Vec>, -} - -impl Deref for KeymapRules { - type Target = [Chord]; - - fn deref(&self) -> &Self::Target { self.as_erased_slice() } -} - -impl KeymapRules { - pub(super) fn as_erased_slice(&self) -> &[Chord] { - // Safety: `Chord` only changes deserialization behavior; the const parameter - // does not participate in layout, so a shared slice can be reinterpreted as - // the default `Chord` view. - unsafe { &*(self.keymap.as_slice() as *const [Chord] as *const [Chord]) } - } -} - -impl DeserializeOverHook for KeymapRules { - fn deserialize_over_hook(self) -> Result { - #[inline] - fn on(Chord { on, .. }: &Chord) -> [Key; 2] { - [on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()] - } - - let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect(); - let b_seen: HashSet<_> = self.keymap.iter().map(on).collect(); - - let keymap = mix( - self.prepend_keymap, - self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))), - self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))), - ); - - Ok(Self { keymap, ..Default::default() }) - } -} diff --git a/yazi-config/src/keymap/section.rs b/yazi-config/src/keymap/section.rs new file mode 100644 index 00000000..42df0036 --- /dev/null +++ b/yazi-config/src/keymap/section.rs @@ -0,0 +1,52 @@ +use std::{ops::Deref, sync::Arc}; + +use hashbrown::HashSet; +use serde::Deserialize; +use yazi_codegen::DeserializeOver2; +use yazi_shared::Layer; +use yazi_shim::toml::DeserializeOverHook; + +use super::{Chord, Chords, Key}; +use crate::mix; + +#[derive(Default, Deserialize, DeserializeOver2)] +pub struct KeymapSection { + keymap: Chords, + #[serde(default)] + prepend_keymap: Vec>, + #[serde(default)] + append_keymap: Vec>, +} + +impl Deref for KeymapSection { + type Target = Chords; + + fn deref(&self) -> &Self::Target { &self.keymap } +} + +impl KeymapSection { + pub fn as_erased(&self) -> &KeymapSection { + unsafe { &*(self as *const Self as *const KeymapSection) } + } +} + +impl DeserializeOverHook for KeymapSection { + fn deserialize_over_hook(self) -> Result { + #[inline] + fn on(Chord { on, .. }: &Chord) -> [Key; 2] { + [on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()] + } + + let keymap = self.keymap.unwrap_unchecked(); + let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect(); + let b_seen: HashSet<_> = keymap.iter().map(|c| on(c)).collect(); + + let keymap: Vec>> = mix( + self.prepend_keymap, + keymap.into_iter().filter(|v| !a_seen.contains(&on(v))), + self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))), + ); + + Ok(Self { keymap: keymap.into(), ..Default::default() }) + } +} diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 0ee60811..dfa642d6 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs); +yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme vfs which); yazi_macro::mod_flat!(icon layout mixing pattern platform preset priority selectable selector style utils yazi); diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index 6170bfd9..9dcfb7ee 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -1,9 +1,11 @@ use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}}; +use yazi_macro::impl_data_any; use yazi_shared::{scheme::Encode as EncodeScheme, strand::ToStrand, url::{Url, UrlBuf}}; use super::{Offset, Position}; use crate::{YAZI, popup::Origin}; +// --- InputCfg #[derive(Clone, Debug, Default)] pub struct InputCfg { pub title: String, @@ -15,20 +17,7 @@ pub struct InputCfg { pub completion: bool, } -#[derive(Clone, Debug, Default)] -pub struct PickCfg { - pub title: String, - pub items: Vec, - pub position: Position, -} - -#[derive(Clone, Debug, Default)] -pub struct ConfirmCfg { - pub position: Position, - pub title: Line<'static>, - pub body: Paragraph<'static>, - pub list: Paragraph<'static>, -} +impl_data_any!(InputCfg); impl InputCfg { pub fn cd(cwd: Url) -> Self { @@ -117,6 +106,45 @@ impl InputCfg { } } +// --- PickCfg +#[derive(Clone, Debug, Default)] +pub struct PickCfg { + pub title: String, + pub items: Vec, + pub position: Position, +} + +impl_data_any!(PickCfg); + +impl PickCfg { + fn max_height(len: usize) -> u16 { + YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16)) + } + + pub fn open(items: Vec) -> Self { + let max_height = Self::max_height(items.len()); + Self { + title: YAZI.pick.open_title.clone(), + items, + position: Position::new(YAZI.pick.open_origin, Offset { + height: max_height, + ..YAZI.pick.open_offset + }), + } + } +} + +// --- ConfirmCfg +#[derive(Clone, Debug, Default)] +pub struct ConfirmCfg { + pub position: Position, + pub title: Line<'static>, + pub body: Paragraph<'static>, + pub list: Paragraph<'static>, +} + +impl_data_any!(ConfirmCfg); + impl ConfirmCfg { fn new( title: String, @@ -188,21 +216,3 @@ impl ConfirmCfg { Some(Text::from_iter(lines)) } } - -impl PickCfg { - fn max_height(len: usize) -> u16 { - YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16)) - } - - pub fn open(items: Vec) -> Self { - let max_height = Self::max_height(items.len()); - Self { - title: YAZI.pick.open_title.clone(), - items, - position: Position::new(YAZI.pick.open_origin, Offset { - height: max_height, - ..YAZI.pick.open_offset - }), - } - } -} diff --git a/yazi-core/src/app/plugin.rs b/yazi-core/src/app/plugin.rs index 3677cec4..c943f0b1 100644 --- a/yazi-core/src/app/plugin.rs +++ b/yazi-core/src/app/plugin.rs @@ -6,9 +6,9 @@ use hashbrown::HashMap; use mlua::{Lua, Table}; use serde::Deserialize; use strum::EnumString; +use yazi_macro::impl_data_any; use yazi_scheduler::plugin::PluginInEntry; use yazi_shared::{SStr, data::{Data, DataKey}, event::{ActionCow, Cmd}}; - #[derive(Clone, Debug, Default)] pub struct PluginOpt { pub id: SStr, @@ -17,6 +17,8 @@ pub struct PluginOpt { pub callback: Option>, } +impl_data_any!(PluginOpt); + impl TryFrom for PluginOpt { type Error = anyhow::Error; diff --git a/yazi-core/src/app/quit.rs b/yazi-core/src/app/quit.rs index 3d79209b..c977f3c1 100644 --- a/yazi-core/src/app/quit.rs +++ b/yazi-core/src/app/quit.rs @@ -1,6 +1,7 @@ use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; use serde::{Deserialize, Serialize}; use yazi_binding::SER_OPT; +use yazi_macro::impl_data_any; use yazi_shared::{event::ActionCow, strand::StrandBuf}; #[derive(Clone, Debug, Default, Deserialize, Serialize)] @@ -13,6 +14,8 @@ pub struct QuitOpt { pub no_cwd_file: bool, } +impl_data_any!(QuitOpt); + impl TryFrom for QuitOpt { type Error = anyhow::Error; diff --git a/yazi-core/src/cmp/option.rs b/yazi-core/src/cmp/option.rs index 9e1543c2..401c7ab5 100644 --- a/yazi-core/src/cmp/option.rs +++ b/yazi-core/src/cmp/option.rs @@ -1,3 +1,4 @@ +use yazi_macro::impl_data_any; use yazi_shared::{Id, path::PathBufDyn, url::UrlBuf}; use crate::cmp::CmpItem; @@ -9,3 +10,5 @@ pub struct CmpOpt { pub word: PathBufDyn, pub ticket: Id, } + +impl_data_any!(CmpOpt); diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index a6ef0c9a..a59aa23d 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use anyhow::Result; use unicode_width::UnicodeWidthStr; use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}}; @@ -12,7 +14,7 @@ use crate::help::HELP_MARGIN; pub struct Help { pub visible: bool, pub layer: Layer, - pub(super) bindings: Vec<&'static Chord>, + pub(super) bindings: Vec>, // Filter pub keyword: String, @@ -51,10 +53,10 @@ impl Help { if kw.is_empty() { self.keyword = String::new(); - self.bindings = KEYMAP.get(self.layer).iter().collect(); + self.bindings = KEYMAP.get(self.layer).iter().cloned().collect(); } else if self.keyword != kw { self.keyword = kw.to_owned(); - self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).collect(); + self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).cloned().collect(); } render!(self.scroll(0)); @@ -73,7 +75,7 @@ impl Help { } // --- Bindings - pub fn window(&self) -> &[&Chord] { + pub fn window(&self) -> &[Arc] { let end = (self.offset + self.limit()).min(self.bindings.len()); &self.bindings[self.offset..end] } diff --git a/yazi-core/src/mgr/displace.rs b/yazi-core/src/mgr/displace.rs index 11d63448..bdc38148 100644 --- a/yazi-core/src/mgr/displace.rs +++ b/yazi-core/src/mgr/displace.rs @@ -1,3 +1,4 @@ +use yazi_macro::impl_data_any; use yazi_shared::url::UrlBuf; #[derive(Clone, Debug)] @@ -5,3 +6,5 @@ pub struct DisplaceOpt { pub to: Result, pub from: UrlBuf, } + +impl_data_any!(DisplaceOpt); diff --git a/yazi-core/src/mgr/filter.rs b/yazi-core/src/mgr/filter.rs index f7fdc413..448b9f07 100644 --- a/yazi-core/src/mgr/filter.rs +++ b/yazi-core/src/mgr/filter.rs @@ -1,4 +1,5 @@ use yazi_fs::FilterCase; +use yazi_macro::impl_data_any; use yazi_shared::{SStr, event::ActionCow}; #[derive(Clone, Debug, Default)] @@ -8,6 +9,8 @@ pub struct FilterOpt { pub done: bool, } +impl_data_any!(FilterOpt); + impl TryFrom for FilterOpt { type Error = anyhow::Error; diff --git a/yazi-core/src/mgr/find.rs b/yazi-core/src/mgr/find.rs index 18caf255..f9d0561a 100644 --- a/yazi-core/src/mgr/find.rs +++ b/yazi-core/src/mgr/find.rs @@ -1,5 +1,6 @@ use anyhow::bail; use yazi_fs::FilterCase; +use yazi_macro::impl_data_any; use yazi_shared::{SStr, event::ActionCow}; #[derive(Clone, Debug)] @@ -9,6 +10,8 @@ pub struct FindDoOpt { pub case: FilterCase, } +impl_data_any!(FindDoOpt); + impl TryFrom for FindDoOpt { type Error = anyhow::Error; diff --git a/yazi-core/src/mgr/open.rs b/yazi-core/src/mgr/open.rs index ca6c5a89..f0b1e1f1 100644 --- a/yazi-core/src/mgr/open.rs +++ b/yazi-core/src/mgr/open.rs @@ -1,6 +1,8 @@ use yazi_fs::File; +use yazi_macro::impl_data_any; use yazi_shared::{event::ActionCow, url::UrlBuf}; +// --- OpenOpt #[derive(Clone, Debug)] pub struct OpenOpt { pub cwd: Option, @@ -9,6 +11,8 @@ pub struct OpenOpt { pub hovered: bool, } +impl_data_any!(OpenOpt); + impl TryFrom for OpenOpt { type Error = anyhow::Error; @@ -22,10 +26,12 @@ impl TryFrom for OpenOpt { } } -// OpenDoOpt +// --- OpenDoOpt #[derive(Clone, Debug, Default)] pub struct OpenDoOpt { pub cwd: UrlBuf, pub targets: Vec, pub interactive: bool, } + +impl_data_any!(OpenDoOpt); diff --git a/yazi-core/src/mgr/search.rs b/yazi-core/src/mgr/search.rs index 35f11b69..6be41d22 100644 --- a/yazi-core/src/mgr/search.rs +++ b/yazi-core/src/mgr/search.rs @@ -1,8 +1,10 @@ use anyhow::bail; use serde::Deserialize; use strum::{EnumString, IntoStaticStr}; +use yazi_macro::impl_data_any; use yazi_shared::{SStr, event::ActionCow, url::{UrlBuf, UrlLike}}; +// --- SearchOpt #[derive(Clone, Debug)] pub struct SearchOpt { pub via: SearchVia, @@ -12,6 +14,8 @@ pub struct SearchOpt { pub r#in: Option, } +impl_data_any!(SearchOpt); + impl TryFrom for SearchOpt { type Error = anyhow::Error; @@ -37,7 +41,7 @@ impl TryFrom for SearchOpt { } } -// Via +// --- Via #[derive(Clone, Copy, Debug, Deserialize, EnumString, Eq, IntoStaticStr, PartialEq)] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] diff --git a/yazi-core/src/notify/option.rs b/yazi-core/src/notify/option.rs index 0bc33b1b..aa836c9f 100644 --- a/yazi-core/src/notify/option.rs +++ b/yazi-core/src/notify/option.rs @@ -4,6 +4,7 @@ use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; use serde::{Deserialize, Serialize}; use serde_with::{DurationSecondsWithFrac, serde_as}; use yazi_binding::SER_OPT; +use yazi_macro::impl_data_any; use yazi_shared::event::ActionCow; use crate::notify::MessageLevel; @@ -21,6 +22,8 @@ pub struct MessageOpt { pub timeout: Duration, } +impl_data_any!(MessageOpt); + impl TryFrom for MessageOpt { type Error = anyhow::Error; diff --git a/yazi-core/src/spot/lock.rs b/yazi-core/src/spot/lock.rs index 86c84c20..eb221c2d 100644 --- a/yazi-core/src/spot/lock.rs +++ b/yazi-core/src/spot/lock.rs @@ -1,5 +1,6 @@ use mlua::Table; use yazi_binding::{FileRef, elements::Renderable}; +use yazi_macro::impl_data_any; use yazi_shared::Id; #[derive(Clone, Debug)] @@ -13,6 +14,8 @@ pub struct SpotLock { pub data: Vec, } +impl_data_any!(SpotLock); + impl TryFrom
for SpotLock { type Error = mlua::Error; diff --git a/yazi-core/src/tab/preview_lock.rs b/yazi-core/src/tab/preview_lock.rs index b643ceae..8a23cba3 100644 --- a/yazi-core/src/tab/preview_lock.rs +++ b/yazi-core/src/tab/preview_lock.rs @@ -1,5 +1,6 @@ use mlua::Table; use yazi_binding::{FileRef, elements::{Rect, Renderable}}; +use yazi_macro::impl_data_any; use yazi_shared::pool::{InternStr, Symbol}; #[derive(Clone, Debug, Default)] @@ -13,6 +14,8 @@ pub struct PreviewLock { pub data: Vec, } +impl_data_any!(PreviewLock); + impl TryFrom
for PreviewLock { type Error = mlua::Error; diff --git a/yazi-core/src/tasks/option.rs b/yazi-core/src/tasks/option.rs index fe861cd7..67d6c4ea 100644 --- a/yazi-core/src/tasks/option.rs +++ b/yazi-core/src/tasks/option.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use yazi_macro::impl_data_any; use yazi_scheduler::{TaskIn, file::FileInCut, plugin::PluginInEntry}; use yazi_shared::{Id, SStr}; @@ -10,6 +11,8 @@ pub enum TaskOpt { Plugin(PluginInEntry), } +impl_data_any!(TaskOpt); + impl TaskIn for TaskOpt { type Prog = (); diff --git a/yazi-core/src/which/option.rs b/yazi-core/src/which/option.rs index 4a95f83e..b6a5c53a 100644 --- a/yazi-core/src/which/option.rs +++ b/yazi-core/src/which/option.rs @@ -1,16 +1,19 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use tokio::sync::mpsc; use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; +use yazi_macro::impl_data_any; use yazi_shared::{Layer, event::ActionCow}; #[derive(Clone, Debug)] pub struct WhichOpt { - pub tx: Option>>, + pub tx: Option>>, pub cands: Vec, pub silent: bool, pub times: usize, } +impl_data_any!(WhichOpt); + impl TryFrom for WhichOpt { type Error = anyhow::Error; @@ -21,7 +24,7 @@ impl TryFrom for WhichOpt { Ok(Self { tx: a.take_any2("tx").transpose()?, - cands: a.take_any_iter::().map(Into::into).collect(), + cands: a.take_any_iter::().map(Into::into).collect(), silent: a.bool("silent"), times: a.get("times").unwrap_or(0), }) @@ -35,7 +38,7 @@ impl From<(Layer, Key)> for WhichOpt { cands: KEYMAP .get(layer) .iter() - .filter(|c| c.on.len() > 1 && c.on[0] == key) + .filter(|&c| c.on.len() > 1 && c.on[0] == key) .map(Into::into) .collect(), times: 1, @@ -54,7 +57,7 @@ impl FromLua for WhichOpt { tx: t.raw_get::>("tx").ok().map(|t| t.0), cands: t .raw_get::
("cands")? - .sequence_values::() + .sequence_values::() .map(|c| c.map(Into::into)) .collect::>>()?, times: t.raw_get("times").unwrap_or_default(), @@ -69,7 +72,7 @@ impl IntoLua for WhichOpt { lua .create_table_from([ ("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?), - ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::ChordCow))?.into_lua(lua)?), + ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::keymap::ChordCow))?.into_lua(lua)?), ("times", self.times.into_lua(lua)?), ("silent", self.silent.into_lua(lua)?), ])? diff --git a/yazi-core/src/which/which.rs b/yazi-core/src/which/which.rs index 3c50a3b4..6b772761 100644 --- a/yazi-core/src/which/which.rs +++ b/yazi-core/src/which/which.rs @@ -4,7 +4,7 @@ use yazi_macro::{emit, render_and}; #[derive(Default)] pub struct Which { - pub tx: Option>>, + pub tx: Option>>, pub times: usize, pub cands: Vec, diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 5ee95c22..ad770cab 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -29,6 +29,7 @@ yazi-version = { path = "../yazi-version", version = "26.5.6" } anyhow = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } +inventory = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } parking_lot = { workspace = true } diff --git a/yazi-dds/src/ember/yank.rs b/yazi-dds/src/ember/yank.rs index a22eb4f1..a9e6bbfc 100644 --- a/yazi-dds/src/ember/yank.rs +++ b/yazi-dds/src/ember/yank.rs @@ -1,9 +1,10 @@ use std::borrow::Cow; use indexmap::IndexSet; -use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value}; +use mlua::{AnyUserData, FromLua, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value}; use serde::{Deserialize, Serialize}; use yazi_binding::get_metatable; +use yazi_macro::impl_data_any; use yazi_shared::url::UrlBufCov; use super::Ember; @@ -19,6 +20,8 @@ pub struct EmberYank<'a> { pub urls: Cow<'a, IndexSet>, } +impl_data_any!(EmberYank<'static>, from_into_lua = inherit); + impl<'a> EmberYank<'a> { pub fn borrowed(cut: bool, urls: &'a IndexSet) -> Ember<'a> { Self { cut, urls: Cow::Borrowed(urls) }.into() @@ -35,6 +38,19 @@ impl<'a> From> for Ember<'a> { fn from(value: EmberYank<'a>) -> Self { Self::Yank(value) } } +impl FromLua for EmberYank<'static> { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + match value { + Value::UserData(ud) => ud.take::()?.collect(lua), + _ => Err(mlua::Error::FromLuaConversionError { + from: value.type_name(), + to: "EmberYank".to_owned(), + message: Some("expected EmberYankIter userdata".to_owned()), + }), + } + } +} + impl IntoLua for EmberYank<'_> { fn into_lua(self, lua: &Lua) -> mlua::Result { let len = self.urls.len(); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index 5d3ffe77..cb2983ad 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -3,7 +3,7 @@ use std::{fmt::Display, io::Write, str::FromStr}; use anyhow::{Result, anyhow}; use mlua::{IntoLua, Lua, Value}; use yazi_boot::BOOT; -use yazi_macro::{emit, relay}; +use yazi_macro::{emit, impl_data_any, relay}; use yazi_shared::{Id, event::ActionCow}; use crate::{ID, ember::Ember}; @@ -15,6 +15,8 @@ pub struct Payload<'a> { pub body: Ember<'a>, } +impl_data_any!(Payload<'static>); + impl<'a> Payload<'a> { pub fn new(body: Ember<'a>) -> Self { Self { receiver: Id::ZERO, sender: *ID, body } } diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs index 860213e7..93d8f6dc 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-dds/src/sendable.rs @@ -3,26 +3,23 @@ use std::{any::TypeId, borrow::Cow}; use hashbrown::HashMap; use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value}; use ordered_float::OrderedFloat; -use yazi_shared::{data::{Data, DataKey}, replace_cow}; - -use crate::ember; +use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow}; pub struct Sendable; impl Sendable { pub fn value_to_data(lua: &Lua, value: Value) -> mlua::Result { - Ok(match value { - Value::Nil => Data::Nil, - Value::Boolean(b) => Data::Boolean(b), - Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?, - Value::Integer(i) => Data::Integer(i), - Value::Number(n) => Data::Number(n), + match &value { + Value::Nil => return Ok(Data::Nil), + Value::Boolean(b) => return Ok(Data::Boolean(*b)), + Value::Integer(i) => return Ok(Data::Integer(*i)), + Value::Number(n) => return Ok(Data::Number(*n)), Value::String(b) => { - if let Ok(s) = b.to_str() { + return Ok(if let Ok(s) = b.to_str() { Data::String(s.to_owned().into()) } else { Data::Bytes(b.as_bytes().to_owned()) - } + }); } Value::Table(t) => { let (mut i, mut map) = (1, HashMap::with_capacity(t.raw_len())); @@ -36,38 +33,40 @@ impl Sendable { map.insert(k, Self::value_to_data(lua, v)?); } - if map.len() == i as usize - 1 { + return Ok(if map.len() == i as usize - 1 { Data::List((1..i).map(|i| map.remove(&DataKey::Integer(i)).unwrap()).collect()) } else { Data::Dict(map) - } + }); } - Value::Function(_) => Err("function is not supported".into_lua_err())?, - Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::UserData(ud) => match ud.type_id() { Some(t) if t == TypeId::of::() => { - Data::Url(ud.take::()?.into()) + return Ok(Data::Url(ud.take::()?.into())); } Some(t) if t == TypeId::of::() => { - Data::Path(ud.take::()?.into()) + return Ok(Data::Path(ud.take::()?.into())); } Some(t) if t == TypeId::of::() => { - Data::Id(**ud.borrow::()?) + return Ok(Data::Id(**ud.borrow::()?)); } - Some(t) if t == TypeId::of::() => { - Data::Any(Box::new(ud.take::()?)) - } - Some(t) if t == TypeId::of::() => { - Data::Any(Box::new(ud.take::()?.collect(lua)?)) - } - Some(t) if t == TypeId::of::() => { - Data::Any(Box::new(ud.take::()?)) - } - _ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?, + Some(t) if t == TypeId::of::() => return Ok(Data::Any(ud.take::()?.0)), + _ => {} }, - Value::Error(_) => Err("error is not supported".into_lua_err())?, - Value::Other(..) => Err("unknown data is not supported".into_lua_err())?, - }) + Value::LightUserData(_) => {} + Value::Function(_) => {} + Value::Thread(_) => {} + Value::Error(_) => {} + Value::Other(_) => {} + } + + for inv in inventory::iter:: { + match (inv.from_lua)(value.clone(), lua) { + Ok(data) => return Ok(Data::Any(data)), + Err(mlua::Error::UserDataTypeMismatch) => continue, + Err(e) => return Err(e), + } + } + Err(format!("unsupported value included: {value:?}").into_lua_err())? } pub fn data_to_value(lua: &Lua, data: Data) -> mlua::Result { @@ -91,23 +90,7 @@ impl Sendable { Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_external_string(b)?), - Data::Any(b) => { - let mut b = b.into_any(); - macro_rules! try_cast { - ($f:expr) => { - match b.downcast() { - Ok(v) => return $f(*v)?.into_lua(lua), - #[allow(unused_assignments)] - Err(e) => b = e, - } - }; - } - - try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); - try_cast!(|v: ember::EmberYank| v.into_lua(lua)); - try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua)); - Err("unsupported DataAny included".into_lua_err())? - } + Data::Any(a) => a.into_lua(lua)?, _ => Self::data_to_value_ref(lua, &data)?, }) } @@ -138,20 +121,7 @@ impl Sendable { Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_string(b)?), - Data::Any(b) => { - macro_rules! try_cast { - ($f:expr) => { - if let Some(v) = b.as_any().downcast_ref() { - return $f(Clone::clone(v))?.into_lua(lua); - } - }; - } - - try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); - try_cast!(|v: ember::EmberYank| v.into_lua(lua)); - try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua)); - Err("unsupported DataAny included".into_lua_err())? - } + Data::Any(a) => a.to_lua(lua)?, }) } diff --git a/yazi-fm/src/help/help.rs b/yazi-fm/src/help/help.rs index 70f10fb5..b63c3756 100644 --- a/yazi-fm/src/help/help.rs +++ b/yazi-fm/src/help/help.rs @@ -12,7 +12,7 @@ impl<'a> Help<'a> { pub fn new(core: &'a Core) -> Self { Self { core } } fn tips() -> String { - match KEYMAP.help.iter().find(|&c| c.run.iter().any(|c| c.name == "filter")) { + match KEYMAP.help.load().iter().find(|&c| c.run.iter().any(|a| a.name == "filter")) { Some(c) => format!(" (Press `{}` to filter)", c.on()), None => String::new(), } diff --git a/yazi-fm/src/router.rs b/yazi-fm/src/router.rs index 35cbd55f..5b0b0d48 100644 --- a/yazi-fm/src/router.rs +++ b/yazi-fm/src/router.rs @@ -36,7 +36,8 @@ impl<'a> Router<'a> { } fn matches(&mut self, layer: Layer, key: Key) -> bool { - for chord @ Chord { on, .. } in KEYMAP.get(layer) { + for chord in &*KEYMAP.get(layer) { + let Chord { on, .. } = &**chord; if on.is_empty() || on[0] != key { continue; } diff --git a/yazi-fs/Cargo.toml b/yazi-fs/Cargo.toml index aa42cf51..1746f8d4 100644 --- a/yazi-fs/Cargo.toml +++ b/yazi-fs/Cargo.toml @@ -27,6 +27,7 @@ either = { workspace = true } foldhash = { workspace = true } hashbrown = { workspace = true } libc = { workspace = true } +mlua = { workspace = true } parking_lot = { workspace = true } percent-encoding = { workspace = true } rand = { workspace = true } diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index ad4eb92e..ae648d81 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -1,7 +1,7 @@ use std::path::Path; use hashbrown::{HashMap, HashSet}; -use yazi_macro::relay; +use yazi_macro::{impl_data_any, relay}; use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike, UrlMapExt}}; use super::File; @@ -23,6 +23,8 @@ pub enum FilesOp { Upserting(UrlBuf, HashMap), } +impl_data_any!(FilesOp); + impl FilesOp { #[inline] pub fn cwd(&self) -> &UrlBuf { diff --git a/yazi-macro/src/data.rs b/yazi-macro/src/data.rs new file mode 100644 index 00000000..80c3f56e --- /dev/null +++ b/yazi-macro/src/data.rs @@ -0,0 +1,116 @@ +#[macro_export] +macro_rules! impl_data_any { + ($ty:ty) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core); + $crate::impl_data_any!(@into_lua_default); + } + }; + ($ty:ty => $($target:ty),+) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core $($target),+); + $crate::impl_data_any!(@into_lua_default); + } + }; + // --- from_into_lua + ($ty:ty, from_into_lua = inherit) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core); + $crate::impl_data_any!(@from_lua_inherit); + $crate::impl_data_any!(@into_lua_inherit); + } + $crate::impl_data_any!(@from_lua_register $ty); + }; + ($ty:ty => $($target:ty),+ ; from_into_lua = inherit) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core $($target),+); + $crate::impl_data_any!(@from_lua_inherit); + $crate::impl_data_any!(@into_lua_inherit); + } + $crate::impl_data_any!(@from_lua_register $ty); + }; + // --- to_lua + ($ty:ty, to_lua = $body:expr) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core); + $crate::impl_data_any!(@from_lua_inherit); + $crate::impl_data_any!(@into_lua_default); + $crate::impl_data_any!(@to_lua $body); + } + $crate::impl_data_any!(@from_lua_register $ty); + }; + ($ty:ty => $($target:ty),+ ; to_lua = $body:expr) => { + impl ::yazi_shared::data::DataAny for $ty { + $crate::impl_data_any!(@core $($target),+); + $crate::impl_data_any!(@from_lua_inherit); + $crate::impl_data_any!(@into_lua_default); + $crate::impl_data_any!(@to_lua $body); + } + $crate::impl_data_any!(@from_lua_register $ty); + }; + + (@core $($target:ty),*) => { + fn as_any(&self, id: std::any::TypeId) -> Option<&dyn std::any::Any> { + use std::any::{TypeId, Any}; + + if id == TypeId::of::() { + return Some(self as &dyn Any); + } + $( + if id == TypeId::of::<$target>() { + return Some(>::as_ref(self) as &dyn Any); + } + )* + None + } + + fn into_any(self: Box, id: std::any::TypeId) -> Result, Box> { + use std::any::TypeId; + + if id == TypeId::of::() { return Ok(self); } + $( + if id == TypeId::of::<$target>() { + return Ok(Box::new(>::into(*self))); + } + )* + Err(self) + } + }; + (@into_lua_default) => { + fn into_lua(self: Box, lua: &mlua::Lua) -> mlua::Result { + use ::yazi_shared::any_data::AnyData; + use mlua::IntoLua; + + AnyData(self).into_lua(lua) + } + }; + (@into_lua_inherit) => { + fn into_lua(self: Box, lua: &mlua::Lua) -> mlua::Result { + use mlua::IntoLua; + + IntoLua::into_lua(*self, lua) + } + }; + (@to_lua $body:expr) => { + fn to_lua(&self, lua: &mlua::Lua) -> mlua::Result { + use mlua::{self, Lua, Value}; + + ($body as fn(&Self, &Lua) -> mlua::Result)(self, lua) + } + }; + (@from_lua_inherit) => { + fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result> { + use mlua::FromLua; + + ::from_lua(value, lua) + .map(|v| Box::new(v) as Box) + } + }; + (@from_lua_register $ty:ty) => { + ::inventory::submit! { + ::yazi_shared::data::DataInventory { + from_lua: <$ty as ::yazi_shared::data::DataAny>::from_lua, + } + } + }; +} diff --git a/yazi-macro/src/lib.rs b/yazi-macro/src/lib.rs index 7f3b9e48..40110145 100644 --- a/yazi-macro/src/lib.rs +++ b/yazi-macro/src/lib.rs @@ -2,6 +2,7 @@ mod actor; mod asset; mod confirm; mod context; +mod data; mod event; mod fmt; mod fs; diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 60cbf122..55ca4c6d 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -20,6 +20,7 @@ vendored-lua = [ "mlua/vendored" ] yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" } yazi-binding = { path = "../yazi-binding", version = "26.5.6" } yazi-boot = { path = "../yazi-boot", version = "26.5.6" } +yazi-codegen = { path = "../yazi-codegen", version = "26.5.6" } yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-core = { path = "../yazi-core", version = "26.5.6" } yazi-dds = { path = "../yazi-dds", version = "26.5.6" } @@ -39,8 +40,8 @@ yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } ansi-to-tui = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } +inventory = { workspace = true } mlua = { workspace = true } -paste = { workspace = true } percent-encoding = { workspace = true } ratatui = { workspace = true } serde_json = { workspace = true } diff --git a/yazi-plugin/src/fs/op.rs b/yazi-plugin/src/fs/op.rs index 93dcd2c7..ec0ce770 100644 --- a/yazi-plugin/src/fs/op.rs +++ b/yazi-plugin/src/fs/op.rs @@ -1,8 +1,21 @@ -use mlua::{IntoLua, Lua, Table, Value}; +use mlua::{Lua, Table, UserData}; use yazi_binding::{Cha, File, Id, Path, Url}; +use yazi_codegen::FromLuaOwned; +use yazi_macro::impl_data_any; +#[derive(Clone, FromLuaOwned)] pub(super) struct FilesOp(yazi_fs::FilesOp); +impl_data_any!(FilesOp => yazi_fs::FilesOp; from_into_lua = inherit); + +impl From for yazi_fs::FilesOp { + fn from(op: FilesOp) -> Self { op.0 } +} + +impl AsRef for FilesOp { + fn as_ref(&self) -> &yazi_fs::FilesOp { &self.0 } +} + impl FilesOp { pub(super) fn part(_: &Lua, t: Table) -> mlua::Result { let id: Id = t.raw_get("id")?; @@ -41,8 +54,4 @@ impl FilesOp { } } -impl IntoLua for FilesOp { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua.create_any_userdata(self.0)?.into_lua(lua) - } -} +impl UserData for FilesOp {} diff --git a/yazi-plugin/src/keymap/keymap.rs b/yazi-plugin/src/keymap/keymap.rs new file mode 100644 index 00000000..c84aefe6 --- /dev/null +++ b/yazi-plugin/src/keymap/keymap.rs @@ -0,0 +1,23 @@ +use mlua::{IntoLua, Lua, Value}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, keymap::KeymapSection}; +use yazi_shared::Layer; + +pub fn compose() -> Composer { + fn get(lua: &Lua, key: &[u8]) -> mlua::Result { + match key { + b"mgr" => KeymapSection::try_from(Layer::Mgr)?.into_lua(lua), + b"tasks" => KeymapSection::try_from(Layer::Tasks)?.into_lua(lua), + b"spot" => KeymapSection::try_from(Layer::Spot)?.into_lua(lua), + b"pick" => KeymapSection::try_from(Layer::Pick)?.into_lua(lua), + b"input" => KeymapSection::try_from(Layer::Input)?.into_lua(lua), + b"confirm" => KeymapSection::try_from(Layer::Confirm)?.into_lua(lua), + b"cmp" => KeymapSection::try_from(Layer::Cmp)?.into_lua(lua), + b"help" => KeymapSection::try_from(Layer::Help)?.into_lua(lua), + _ => Ok(Value::Nil), + } + } + + fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } + + Composer::new(get, set) +} diff --git a/yazi-plugin/src/keymap/mod.rs b/yazi-plugin/src/keymap/mod.rs new file mode 100644 index 00000000..bfbbc6dd --- /dev/null +++ b/yazi-plugin/src/keymap/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(keymap); diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index a716d0dc..9a3fad99 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_pub!(elements external fs pubsub runtime tasks theme utils); +yazi_macro::mod_pub!(elements external fs keymap pubsub runtime tasks theme utils); yazi_macro::mod_flat!(slim standard); diff --git a/yazi-plugin/src/slim.rs b/yazi-plugin/src/slim.rs index 8d618749..7bd11008 100644 --- a/yazi-plugin/src/slim.rs +++ b/yazi-plugin/src/slim.rs @@ -8,6 +8,7 @@ pub fn slim_lua(lua: &Lua) -> mlua::Result<()> { globals.raw_set("ya", crate::utils::compose(true))?; globals.raw_set("fs", crate::fs::compose())?; globals.raw_set("rt", crate::runtime::compose())?; + globals.raw_set("km", crate::keymap::compose())?; globals.raw_set("th", crate::theme::compose().into_lua(lua)?)?; yazi_binding::Cha::install(lua)?; diff --git a/yazi-plugin/src/standard.rs b/yazi-plugin/src/standard.rs index 0dbbb8e5..69fc4c9b 100644 --- a/yazi-plugin/src/standard.rs +++ b/yazi-plugin/src/standard.rs @@ -27,6 +27,7 @@ fn stage_1(lua: &Lua) -> Result<()> { globals.raw_set("fs", crate::fs::compose())?; globals.raw_set("ps", crate::pubsub::compose())?; globals.raw_set("rt", crate::runtime::compose())?; + globals.raw_set("km", crate::keymap::compose())?; globals.raw_set("th", crate::theme::compose())?; yazi_binding::Error::install(lua)?; diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 096a0aef..4dd058d8 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -25,6 +25,7 @@ impl Utils { .map(|(i, cand)| { let cand = cand?; Ok(ChordCow::Owned(Chord { + id: yazi_config::keymap::chord_id(), on: Self::parse_keys(cand.raw_get("on")?)?, run: vec![relay!(which:callback, [i + 1])], desc: cand.raw_get("desc").unwrap_or_default(), diff --git a/yazi-scheduler/src/process/option.rs b/yazi-scheduler/src/process/option.rs index 2bf5b6c6..6b95af14 100644 --- a/yazi-scheduler/src/process/option.rs +++ b/yazi-scheduler/src/process/option.rs @@ -1,5 +1,6 @@ use std::ffi::OsString; +use yazi_macro::impl_data_any; use yazi_shared::url::{UrlBuf, UrlCow}; // TODO: remove in favor of ShellForm @@ -13,3 +14,5 @@ pub struct ProcessOpt { pub spread: bool, // TODO: remove } + +impl_data_any!(ProcessOpt); diff --git a/yazi-scheduler/src/summary.rs b/yazi-scheduler/src/summary.rs index 4e0134c6..4b6c192d 100644 --- a/yazi-scheduler/src/summary.rs +++ b/yazi-scheduler/src/summary.rs @@ -1,5 +1,6 @@ use ordered_float::OrderedFloat; use serde::Serialize; +use yazi_macro::impl_data_any; use crate::{Ongoing, Progress}; @@ -11,6 +12,8 @@ pub struct TaskSummary { pub percent: Option>, } +impl_data_any!(TaskSummary); + impl From<&Ongoing> for TaskSummary { fn from(value: &Ongoing) -> Self { let mut summary = Self::default(); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index cdc99fba..e398cf5b 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -23,7 +23,9 @@ dyn-clone = { workspace = true } foldhash = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } +inventory = { workspace = true } memchr = "2.8.1" +mlua = { workspace = true } ordered-float = { workspace = true } parking_lot = { workspace = true } paste = { workspace = true } diff --git a/yazi-shared/src/any_data.rs b/yazi-shared/src/any_data.rs new file mode 100644 index 00000000..ecd4208e --- /dev/null +++ b/yazi-shared/src/any_data.rs @@ -0,0 +1,8 @@ +use mlua::UserData; + +use crate::data::DataAny; + +#[derive(Debug)] +pub struct AnyData(pub Box); + +impl UserData for AnyData {} diff --git a/yazi-shared/src/completion_token.rs b/yazi-shared/src/completion_token.rs index 029f5ef0..84504050 100644 --- a/yazi-shared/src/completion_token.rs +++ b/yazi-shared/src/completion_token.rs @@ -1,12 +1,15 @@ use std::sync::{Arc, atomic::{AtomicU8, Ordering}}; use tokio::sync::Notify; +use yazi_macro::impl_data_any; #[derive(Clone, Debug, Default)] pub struct CompletionToken { inner: Arc<(AtomicU8, Notify)>, } +impl_data_any!(CompletionToken); + impl CompletionToken { pub fn complete(&self, success: bool) { let new = if success { 1 } else { 2 }; diff --git a/yazi-shared/src/data/any.rs b/yazi-shared/src/data/any.rs index e11a284e..39edc3b5 100644 --- a/yazi-shared/src/data/any.rs +++ b/yazi-shared/src/data/any.rs @@ -1,20 +1,39 @@ -use std::{any::Any, fmt::{self, Debug}}; +use std::{any::{self, Any, TypeId}, fmt::{self, Debug}}; use dyn_clone::DynClone; +use mlua::{IntoLua, Lua, Value}; + +use crate::any_data::AnyData; pub trait DataAny: Any + Send + Sync + DynClone { - fn as_any(&self) -> &dyn Any; + fn as_any(&self, id: TypeId) -> Option<&dyn Any>; - fn into_any(self: Box) -> Box; + fn into_any(self: Box, id: TypeId) -> Result, Box>; + + fn into_lua(self: Box, lua: &Lua) -> mlua::Result; + + fn to_lua(&self, lua: &Lua) -> mlua::Result { dyn_clone::clone_box(self).into_lua(lua) } + + fn from_lua(_value: Value, _lua: &Lua) -> mlua::Result> + where + Self: Sized, + { + Err(mlua::Error::runtime(format!( + "DataAny::from_lua is not implemented for `{}`", + any::type_name::(), + ))) + } } -impl DataAny for T -where - T: Any + Send + Sync + DynClone, -{ - fn as_any(&self) -> &dyn Any { self } +impl dyn DataAny { + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any(TypeId::of::()).and_then(|a| a.downcast_ref::()) + } - fn into_any(self: Box) -> Box { self } + pub fn downcast(self: Box) -> Result, Box> { + let id = TypeId::of::(); + self.into_any(id).map_or_else(|me| Err(me as Box), |a| a.downcast::()) + } } impl Clone for Box { @@ -26,3 +45,27 @@ impl Debug for dyn DataAny { f.debug_struct("DataAny").finish_non_exhaustive() } } + +impl DataAny for Vec { + fn as_any(&self, id: TypeId) -> Option<&dyn Any> { + (id == TypeId::of::()).then(|| self as &dyn Any) + } + + fn into_any(self: Box, id: TypeId) -> Result, Box> { + if id == TypeId::of::() { Ok(self) } else { Err(self) } + } + + fn into_lua(self: Box, lua: &Lua) -> mlua::Result { AnyData(self).into_lua(lua) } +} + +impl DataAny for tokio::sync::mpsc::UnboundedSender { + fn as_any(&self, id: TypeId) -> Option<&dyn Any> { + (id == TypeId::of::()).then(|| self as &dyn Any) + } + + fn into_any(self: Box, id: TypeId) -> Result, Box> { + if id == TypeId::of::() { Ok(self) } else { Err(self) } + } + + fn into_lua(self: Box, lua: &Lua) -> mlua::Result { AnyData(self).into_lua(lua) } +} diff --git a/yazi-shared/src/data/data.rs b/yazi-shared/src/data/data.rs index 8b85d56a..887fef06 100644 --- a/yazi-shared/src/data/data.rs +++ b/yazi-shared/src/data/data.rs @@ -257,7 +257,7 @@ impl Data { pub fn as_any(&self) -> Option<&T> { match self { - Self::Any(b) => (**b).as_any().downcast_ref::(), + Self::Any(a) => a.downcast_ref::(), _ => None, } } @@ -271,15 +271,15 @@ impl Data { pub fn into_any(self) -> Option { match self { - Self::Any(b) => b.into_any().downcast::().ok().map(|b| *b), + Self::Any(a) => a.downcast::().ok().map(|b| *b), _ => None, } } // FIXME: find a better name pub fn into_any2(self) -> Result { - if let Self::Any(b) = self - && let Ok(t) = b.into_any().downcast::() + if let Self::Any(a) = self + && let Ok(t) = a.downcast::() { Ok(*t) } else { diff --git a/yazi-shared/src/data/inventory.rs b/yazi-shared/src/data/inventory.rs new file mode 100644 index 00000000..b4114236 --- /dev/null +++ b/yazi-shared/src/data/inventory.rs @@ -0,0 +1,9 @@ +use mlua::{Lua, Value}; + +use crate::data::DataAny; + +pub struct DataInventory { + pub from_lua: fn(Value, &Lua) -> mlua::Result>, +} + +inventory::collect!(DataInventory); diff --git a/yazi-shared/src/data/mod.rs b/yazi-shared/src/data/mod.rs index 1f290c8a..1df7c51a 100644 --- a/yazi-shared/src/data/mod.rs +++ b/yazi-shared/src/data/mod.rs @@ -1,4 +1,4 @@ #[macro_use] mod macros; -yazi_macro::mod_flat!(any data de de_bytes de_key de_owned key); +yazi_macro::mod_flat!(any data de de_bytes de_key de_owned inventory key); diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index 98317f74..c0ec258b 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -1,4 +1,6 @@ -yazi_macro::mod_pub!(data event loc path pool scheme shell strand translit url wtf8); +extern crate self as yazi_shared; + +yazi_macro::mod_pub!(any_data data event loc path pool scheme shell strand translit url wtf8); yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id kebab_cased_string last_value layer localset natsort non_empty_string os predictor snake_cased_string source tests throttle time); diff --git a/yazi-shared/src/url/buf.rs b/yazi-shared/src/url/buf.rs index cf3bbc25..c471775b 100644 --- a/yazi-shared/src/url/buf.rs +++ b/yazi-shared/src/url/buf.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, fmt::{Debug, Formatter}, hash::{Hash, Hasher}, path::{Pat use anyhow::Result; use serde::{Deserialize, Serialize, de::{self, IntoDeserializer}}; +use yazi_macro::impl_data_any; use crate::{loc::LocBuf, path::{PathBufDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::{Scheme, SchemeLike}, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlDeserializer, UrlLike}}; @@ -13,6 +14,8 @@ pub enum UrlBuf { Sftp { loc: LocBuf, domain: Symbol }, } +impl_data_any!(UrlBuf); + // FIXME: remove impl Default for UrlBuf { fn default() -> Self { Self::Regular(Default::default()) } diff --git a/yazi-shim/Cargo.toml b/yazi-shim/Cargo.toml index ad580071..8c2a1b52 100644 --- a/yazi-shim/Cargo.toml +++ b/yazi-shim/Cargo.toml @@ -33,4 +33,4 @@ twox-hash = { workspace = true } unicode-width = { workspace = true } [dependencies.unicode-segmentation] -version = "1.13.2" +version = "1.13.3" diff --git a/yazi-shim/src/mlua/traits.rs b/yazi-shim/src/mlua/traits.rs index 35b98e79..68b037b5 100644 --- a/yazi-shim/src/mlua/traits.rs +++ b/yazi-shim/src/mlua/traits.rs @@ -14,7 +14,7 @@ pub trait DeserializeOverLua: DeserializeOverWith { impl DeserializeOverLua for T {} -// --- IntoLua +// --- LuaTableExt pub trait LuaTableExt { fn sequence_iter(&self, lua: &Lua) -> SequenceIter; } diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index f3977dbe..46f62cce 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -24,6 +24,7 @@ bitflags = { workspace = true } futures = { workspace = true } parking_lot = { workspace = true } ratatui = { workspace = true } +serde = { workspace = true } strum = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } diff --git a/yazi-term/src/event/keyboard.rs b/yazi-term/src/event/keyboard.rs index 5781e756..95841580 100644 --- a/yazi-term/src/event/keyboard.rs +++ b/yazi-term/src/event/keyboard.rs @@ -1,4 +1,5 @@ use bitflags::bitflags; +use serde::{Deserialize, Serialize}; use crate::{ParseError, Result, bail, event::Modifiers}; @@ -71,7 +72,8 @@ impl KeyEventState { } // --- Code -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(tag = "type", content = "value")] pub enum KeyCode { Char(char), Enter, @@ -217,7 +219,8 @@ impl KeyCode { } // --- Modifier key -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(untagged)] pub enum ModifierKeyCode { LeftShift, LeftControl, @@ -250,7 +253,8 @@ impl ModifierKeyCode { } // --- Media key -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(untagged)] pub enum MediaKeyCode { Play, Pause, From 8a4b0b208e0cb99efa6fc02ab8b6e14144c5b680 Mon Sep 17 00:00:00 2001 From: cyanyux <120559402+cyanyux@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:53:59 +0800 Subject: [PATCH 09/28] fix(snap): make bundled magick find libgomp at runtime (#4016) --- snap/snapcraft.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 78abbfe2..91e5949f 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -133,9 +133,16 @@ parts: - --prefix=/usr build-attributes: - enable-patchelf + organize: + # `libgomp1` installs libgomp into the multiarch directory, but the + # `magick` binary's RPATH only searches `$ORIGIN/../lib` (i.e. usr/lib). + # Move it alongside the Magick libraries so the bundled binary can load + # it at runtime; otherwise magick fails with: + # "libgomp.so.1: cannot open shared object file: No such file or directory" + usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so*: usr/lib/ prime: - usr/bin/magick - - usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so.1 + - usr/lib/libgomp.so* - usr/lib/libMagickCore-7.Q16HDRI.so* - usr/lib/libMagickWand-7.Q16HDRI.so* From ea7fa2127cc032b669a4825157ace244f06cf31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 6 Jun 2026 14:35:12 +0800 Subject: [PATCH 10/28] perf: eliminate hacks in image and UI conflict resolution (#4022) --- CHANGELOG.md | 5 +++++ yazi-binding/src/data/any.rs | 16 ---------------- yazi-binding/src/data/mod.rs | 1 - yazi-binding/src/lib.rs | 2 +- yazi-fm/src/app/render.rs | 30 +++--------------------------- yazi-tui/src/raterm.rs | 4 +--- yazi-widgets/src/clear.rs | 4 ++-- 7 files changed, 12 insertions(+), 50 deletions(-) delete mode 100644 yazi-binding/src/data/any.rs delete mode 100644 yazi-binding/src/data/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8db42d..ea999c49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Deprecate `backward --far` and `forward --far` in favor of `backward wide` and `forward wide`, respectively ([#4012]) +### Improved + +- Eliminate hacks in image and UI conflict resolution ([#4022]) + ## [v26.5.6] ### Added @@ -1737,3 +1741,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3990]: https://github.com/sxyazi/yazi/pull/3990 [#4005]: https://github.com/sxyazi/yazi/pull/4005 [#4012]: https://github.com/sxyazi/yazi/pull/4012 +[#4022]: https://github.com/sxyazi/yazi/pull/4022 diff --git a/yazi-binding/src/data/any.rs b/yazi-binding/src/data/any.rs deleted file mode 100644 index eb5e353d..00000000 --- a/yazi-binding/src/data/any.rs +++ /dev/null @@ -1,16 +0,0 @@ -use mlua::UserData; -use yazi_codegen::FromLuaOwned; - -#[derive(FromLuaOwned)] -pub struct DataAny(Box); - -impl DataAny { - pub fn new(value: T) -> Self - where - T: yazi_shared::data::DataAny, - { - Self(Box::new(value)) - } -} - -impl UserData for DataAny {} diff --git a/yazi-binding/src/data/mod.rs b/yazi-binding/src/data/mod.rs deleted file mode 100644 index 83b99df1..00000000 --- a/yazi-binding/src/data/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(any); diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index b6f1f48b..4efa34b6 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -1,5 +1,5 @@ mod macros; -yazi_macro::mod_pub!(config data elements event keymap process theme); +yazi_macro::mod_pub!(config elements event keymap process theme); yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); diff --git a/yazi-fm/src/app/render.rs b/yazi-fm/src/app/render.rs index 245c4c21..175abe46 100644 --- a/yazi-fm/src/app/render.rs +++ b/yazi-fm/src/app/render.rs @@ -1,7 +1,7 @@ use std::{io::Write, sync::atomic::{AtomicU8, Ordering}, time::Instant}; use anyhow::Result; -use ratatui::{CompletedFrame, backend::Backend, buffer::Buffer, layout::Position}; +use ratatui::layout::Position; use yazi_actor::{Ctx, lives::Lives}; use yazi_binding::runtime_scope; use yazi_config::LAYOUT; @@ -10,7 +10,6 @@ use yazi_plugin::LUA; use yazi_shared::{data::Data, event::NEED_RENDER}; use yazi_term::{CursorStyle, sequence::{BeginSyncUpdate, EndSyncUpdate, MoveTo, SetCursorStyle, ShowCursor}}; use yazi_tty::TTY; -use yazi_tui::RatermBackend; use yazi_widgets::COLLISION; use crate::{app::App, root::Root}; @@ -30,15 +29,12 @@ impl App { let collision = COLLISION.swap(false, Ordering::Relaxed); let preview_rect = LAYOUT.get().preview; - let frame = term.draw(|f| { + term.draw(|f| { _ = Lives::scope(&self.core, || { runtime_scope!(LUA, "root", Ok(f.render_widget(Root::new(&self.core), f.area()))) }); })?; - if COLLISION.load(Ordering::Relaxed) { - Self::patch(frame); - } if !self.core.notify.messages.is_empty() { self.render_partially()?; } @@ -61,7 +57,7 @@ impl App { Self::routine(true, None); let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c)); - let frame = term.draw_partial(|f| { + term.draw_partial(|f| { _ = Lives::scope(&self.core, || { runtime_scope!(LUA, "root", { f.render_widget(crate::tasks::Progress::new(&self.core), f.area()); @@ -71,29 +67,9 @@ impl App { }); })?; - if COLLISION.load(Ordering::Relaxed) { - Self::patch(frame); - } succ!(); } - #[inline] - fn patch(frame: CompletedFrame) { - let mut new = Buffer::empty(frame.area); - for y in new.area.top()..new.area.bottom() { - for x in new.area.left()..new.area.right() { - let cell = &frame.buffer[(x, y)]; - if cell.skip { - new[(x, y)] = cell.clone(); - } - new[(x, y)].set_skip(!cell.skip); - } - } - - let patches = frame.buffer.diff(&new); - RatermBackend::new(&mut *TTY.lockout()).draw(patches.into_iter()).ok(); - } - fn routine(push: bool, cursor: Option<(Position, CursorStyle)>) { static COUNT: AtomicU8 = AtomicU8::new(0); if push && COUNT.fetch_add(1, Ordering::Relaxed) != 0 { diff --git a/yazi-tui/src/raterm.rs b/yazi-tui/src/raterm.rs index 3b528821..4a7a498b 100644 --- a/yazi-tui/src/raterm.rs +++ b/yazi-tui/src/raterm.rs @@ -123,9 +123,7 @@ impl Raterm { let buffer = frame.buffer_mut(); for y in self.last_area.top()..self.last_area.bottom() { for x in self.last_area.left()..self.last_area.right() { - let mut cell = self.last_buffer[(x, y)].clone(); - cell.skip = false; - buffer[(x, y)] = cell; + buffer[(x, y)] = self.last_buffer[(x, y)].clone(); } } diff --git a/yazi-widgets/src/clear.rs b/yazi-widgets/src/clear.rs index 2b615fe5..c7809780 100644 --- a/yazi-widgets/src/clear.rs +++ b/yazi-widgets/src/clear.rs @@ -1,6 +1,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; -use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use ratatui::{buffer::{Buffer, CellDiffOption}, layout::Rect, widgets::Widget}; use yazi_adapter::ADAPTOR; pub static COLLISION: AtomicBool = AtomicBool::new(false); @@ -23,7 +23,7 @@ impl Widget for Clear { COLLISION.store(true, Ordering::Relaxed); for y in r.top()..r.bottom() { for x in r.left()..r.right() { - buf[(x, y)].set_skip(true); + buf[(x, y)].set_diff_option(CellDiffOption::AlwaysUpdate); } } } From f6113d386539a07d4ae23d24595e603bc0be0343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sun, 7 Jun 2026 21:46:07 +0800 Subject: [PATCH 11/28] feat: advanced app overall style (#4023) --- yazi-adapter/src/drivers/chafa.rs | 8 ++- yazi-adapter/src/drivers/iip.rs | 9 ++-- yazi-adapter/src/drivers/kgp.rs | 17 +++++-- yazi-adapter/src/drivers/sixel.rs | 8 ++- yazi-binding/src/elements/elements.rs | 1 + yazi-binding/src/elements/fill.rs | 53 ++++++++++++++++++++ yazi-binding/src/elements/line.rs | 9 +++- yazi-binding/src/elements/mod.rs | 2 +- yazi-binding/src/elements/renderable.rs | 10 +++- yazi-binding/src/url.rs | 15 ++++-- yazi-config/src/theme/theme.rs | 6 --- yazi-plugin/preset/components/app.lua | 5 ++ yazi-plugin/preset/components/backdrop.lua | 13 +++++ yazi-plugin/preset/components/entity.lua | 4 +- yazi-plugin/preset/components/root.lua | 1 + yazi-plugin/preset/components/status.lua | 4 +- yazi-plugin/preset/plugins/dnd.lua | 2 +- yazi-plugin/preset/plugins/json.lua | 11 +++- yazi-plugin/src/standard.rs | 2 + yazi-runner/src/loader/loader.rs | 2 + yazi-term/src/sequence/mod.rs | 2 +- yazi-term/src/sequence/restore_background.rs | 8 --- yazi-term/src/sequence/set_background.rs | 10 ---- yazi-tui/src/option.rs | 7 +-- yazi-tui/src/raterm.rs | 8 ++- yazi-tui/src/state.rs | 11 +--- 26 files changed, 158 insertions(+), 70 deletions(-) create mode 100644 yazi-binding/src/elements/fill.rs create mode 100644 yazi-plugin/preset/components/app.lua create mode 100644 yazi-plugin/preset/components/backdrop.lua delete mode 100644 yazi-term/src/sequence/restore_background.rs delete mode 100644 yazi-term/src/sequence/set_background.rs diff --git a/yazi-adapter/src/drivers/chafa.rs b/yazi-adapter/src/drivers/chafa.rs index 651eafc5..9b13e96d 100644 --- a/yazi-adapter/src/drivers/chafa.rs +++ b/yazi-adapter/src/drivers/chafa.rs @@ -4,8 +4,9 @@ use ansi_to_tui::IntoText; use anyhow::{Result, anyhow, bail}; use ratatui::layout::Rect; use tokio::process::Command; +use yazi_config::THEME; use yazi_emulator::Emulator; -use yazi_term::sequence::MoveTo; +use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; use crate::Adapter; @@ -71,11 +72,14 @@ impl Chafa { pub(crate) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { + if let Some(c) = THEME.app.overall.get().bg { + write!(w, "{}", SetBg(c))?; + } for y in area.top()..area.bottom() { write!(w, "{}", MoveTo(area.x, y))?; write!(w, "{s}")?; } - Ok(()) + Ok(write!(w, "{ResetAttrs}")?) }) } } diff --git a/yazi-adapter/src/drivers/iip.rs b/yazi-adapter/src/drivers/iip.rs index e8174e8f..33f99bd9 100644 --- a/yazi-adapter/src/drivers/iip.rs +++ b/yazi-adapter/src/drivers/iip.rs @@ -4,9 +4,9 @@ use anyhow::Result; use base64::{Engine, engine::{Config, general_purpose::STANDARD}}; use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEncoder, png::PngEncoder}}; use ratatui::layout::Rect; -use yazi_config::YAZI; +use yazi_config::{THEME, YAZI}; use yazi_emulator::{CLOSE, Emulator, START}; -use yazi_term::sequence::MoveTo; +use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; use crate::{Image, adapter::Adapter}; @@ -29,11 +29,14 @@ impl Iip { pub(crate) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { + if let Some(c) = THEME.app.overall.get().bg { + write!(w, "{}", SetBg(c))?; + } for y in area.top()..area.bottom() { write!(w, "{}", MoveTo(area.x, y))?; write!(w, "{s}")?; } - Ok(()) + Ok(write!(w, "{ResetAttrs}")?) }) } diff --git a/yazi-adapter/src/drivers/kgp.rs b/yazi-adapter/src/drivers/kgp.rs index 9cb533f7..e0391fbc 100644 --- a/yazi-adapter/src/drivers/kgp.rs +++ b/yazi-adapter/src/drivers/kgp.rs @@ -5,9 +5,10 @@ use anyhow::Result; use base64::{Engine, engine::general_purpose}; use image::DynamicImage; use ratatui::{layout::Rect, style::Color}; +use yazi_config::THEME; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; use yazi_shim::cell::SyncCell; -use yazi_term::sequence::{MoveTo, SetFg}; +use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg, SetFg}; use crate::{adapter::Adapter, image::Image}; @@ -333,12 +334,16 @@ impl Kgp { pub(crate) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { + if let Some(c) = THEME.app.overall.get().bg { + write!(w, "{}", SetBg(c))?; + } + for y in area.top()..area.bottom() { write!(w, "{}", MoveTo(area.x, y))?; write!(w, "{s}")?; } - write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?; + write!(w, "{ResetAttrs}{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?; Ok(()) }) } @@ -381,12 +386,16 @@ impl Kgp { } fn place(area: &Rect) -> Result> { - let mut buf = Vec::with_capacity(area.width as usize * area.height as usize * 3 + 50); + let mut buf = Vec::with_capacity(area.width as usize * area.height as usize * 3 + 500); let id = Self::image_id(); let (r, g, b) = ((id >> 16) & 0xff, (id >> 8) & 0xff, id & 0xff); write!(buf, "{}", SetFg(Color::Rgb(r as u8, g as u8, b as u8)))?; + if let Some(c) = THEME.app.overall.get().bg { + write!(buf, "{}", SetBg(c))?; + } + for y in 0..area.height { write!(buf, "{}", MoveTo(area.x, area.y + y))?; for x in 0..area.width { @@ -395,6 +404,8 @@ impl Kgp { write!(buf, "{}", *DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0]))?; } } + + write!(buf, "{ResetAttrs}")?; Ok(buf) } diff --git a/yazi-adapter/src/drivers/sixel.rs b/yazi-adapter/src/drivers/sixel.rs index 1a39661e..d774ae21 100644 --- a/yazi-adapter/src/drivers/sixel.rs +++ b/yazi-adapter/src/drivers/sixel.rs @@ -5,8 +5,9 @@ use image::{DynamicImage, GenericImageView, RgbImage}; use palette::{Srgb, cast::ComponentsAs}; use quantette::{PaletteSize, color_map::IndexedColorMap, wu::{BinnerU8x3, WuU8x3}}; use ratatui::layout::Rect; +use yazi_config::THEME; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; -use yazi_term::sequence::MoveTo; +use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; use crate::{Image, adapter::Adapter}; @@ -34,11 +35,14 @@ impl Sixel { pub(crate) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { + if let Some(c) = THEME.app.overall.get().bg { + write!(w, "{}", SetBg(c))?; + } for y in area.top()..area.bottom() { write!(w, "{}", MoveTo(area.x, y))?; write!(w, "{s}")?; } - Ok(()) + Ok(write!(w, "{ResetAttrs}")?) }) } diff --git a/yazi-binding/src/elements/elements.rs b/yazi-binding/src/elements/elements.rs index 45971550..f6b26c96 100644 --- a/yazi-binding/src/elements/elements.rs +++ b/yazi-binding/src/elements/elements.rs @@ -14,6 +14,7 @@ pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer super::Color::compose(lua)?, b"Constraint" => super::Constraint::compose(lua)?, b"Edge" => super::Edge::compose(lua)?, + b"Fill" => super::Fill::compose(lua)?, b"Gauge" => super::Gauge::compose(lua)?, b"Layout" => super::Layout::compose(lua)?, b"Line" => super::Line::compose(lua)?, diff --git a/yazi-binding/src/elements/fill.rs b/yazi-binding/src/elements/fill.rs new file mode 100644 index 00000000..cdc23eb7 --- /dev/null +++ b/yazi-binding/src/elements/fill.rs @@ -0,0 +1,53 @@ +use mlua::{IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use ratatui::widgets::Widget; + +use super::Area; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Fill { + pub area: Area, + + style: ratatui::style::Style, +} + +impl Fill { + pub fn compose(lua: &Lua) -> mlua::Result { + let new = + lua.create_function(|_, (_, area): (Table, Area)| Ok(Self { area, ..Default::default() }))?; + + let fill = lua.create_table()?; + fill.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; + + fill.into_lua(lua) + } +} + +impl Widget for Fill { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &Fill { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + if self.style == Default::default() { + return; + } + + for pos in rect.positions() { + buf[pos].set_style(self.style); + } + } +} + +impl UserData for Fill { + fn add_methods>(methods: &mut M) { + crate::impl_style_method!(methods, style); + } +} diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 7a38dfcb..2d4fd5de 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, mem, ops::{Deref, DerefMut}}; use ansi_to_tui::IntoText; -use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Function, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; use unicode_width::UnicodeWidthChar; @@ -140,6 +140,13 @@ impl UserData for Line { methods.add_method("visible", |_, me, ()| { Ok(me.iter().flat_map(|s| s.content.chars()).any(|c| c.width().unwrap_or(0) > 0)) }); + methods.add_function("map", |_, (ud, f): (AnyUserData, Function)| { + let mut me = ud.borrow_mut::()?; + for span in &mut me.spans { + *span = f.call::(Span(mem::take(span)))?.0; + } + Ok(ud) + }); methods.add_function("truncate", |lua, (ud, t): (AnyUserData, Table)| { let mut me = ud.borrow_mut::()?; diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index 61dcd02f..49c6da4e 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements gauge layout line list pad pos rect renderable row span table text wrap); +yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge layout line list pad pos rect renderable row span table text wrap); diff --git a/yazi-binding/src/elements/renderable.rs b/yazi-binding/src/elements/renderable.rs index 7730edf5..8e9008de 100644 --- a/yazi-binding/src/elements/renderable.rs +++ b/yazi-binding/src/elements/renderable.rs @@ -3,8 +3,8 @@ use std::any::TypeId; use mlua::{AnyUserData, ExternalError, FromLua, Lua, Value}; use ratatui::widgets::Widget; -use super::{Bar, Border, Clear, Gauge, Line, List, Table, Text}; -use crate::{Error, elements::Area}; +use super::{Area, Bar, Border, Clear, Fill, Gauge, Line, List, Table, Text}; +use crate::Error; #[derive(Clone, Debug)] pub enum Renderable { @@ -13,6 +13,7 @@ pub enum Renderable { List(Box), Bar(Bar), Clear(Clear), + Fill(Fill), Border(Border), Gauge(Box), Table(Box
), @@ -26,6 +27,7 @@ impl Renderable { Self::List(list) => list.area, Self::Bar(bar) => bar.area, Self::Clear(clear) => clear.area, + Self::Fill(fill) => fill.area, Self::Border(border) => border.area, Self::Gauge(gauge) => gauge.area, Self::Table(table) => table.area, @@ -40,6 +42,7 @@ impl Renderable { Self::List(list) => list.area = area, Self::Bar(bar) => bar.area = area, Self::Clear(clear) => clear.area = area, + Self::Fill(fill) => fill.area = area, Self::Border(border) => border.area = area, Self::Gauge(gauge) => gauge.area = area, Self::Table(table) => table.area = area, @@ -66,6 +69,7 @@ impl TryFrom<&AnyUserData> for Renderable { Some(t) if t == TypeId::of::() => Self::List(Box::new(ud.take()?)), Some(t) if t == TypeId::of::() => Self::Bar(ud.take()?), Some(t) if t == TypeId::of::() => Self::Clear(ud.take()?), + Some(t) if t == TypeId::of::() => Self::Fill(ud.take()?), Some(t) if t == TypeId::of::() => Self::Border(ud.take()?), Some(t) if t == TypeId::of::() => Self::Gauge(Box::new(ud.take()?)), Some(t) if t == TypeId::of::
() => Self::Table(Box::new(ud.take()?)), @@ -95,6 +99,7 @@ impl Widget for Renderable { Self::List(list) => list.render(rect, buf), Self::Bar(bar) => bar.render(rect, buf), Self::Clear(clear) => clear.render(rect, buf), + Self::Fill(fill) => fill.render(rect, buf), Self::Border(border) => border.render(rect, buf), Self::Gauge(gauge) => gauge.render(rect, buf), Self::Table(table) => table.render(rect, buf), @@ -113,6 +118,7 @@ impl Widget for &Renderable { Renderable::List(list) => (&**list).render(rect, buf), Renderable::Bar(bar) => bar.render(rect, buf), Renderable::Clear(clear) => clear.render(rect, buf), + Renderable::Fill(fill) => fill.render(rect, buf), Renderable::Border(border) => border.render(rect, buf), Renderable::Gauge(gauge) => (&**gauge).render(rect, buf), Renderable::Table(table) => (&**table).render(rect, buf), diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index dba0b1b3..4064a285 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -130,14 +130,19 @@ impl Url { } } Value::UserData(ref ud) => { - let url = ud.borrow::()?; - if url.scheme().covariant(self.scheme()) { - Self::new(self.try_join(url.loc()).into_lua_err()?).into_lua(lua) + if let Ok(url) = ud.borrow::() { + if url.scheme().covariant(self.scheme()) { + Self::new(self.try_join(url.loc()).into_lua_err()?).into_lua(lua) + } else { + Ok(other) + } + } else if let Ok(path) = ud.borrow::() { + Self::new(self.try_join(&*path).into_lua_err()?).into_lua(lua) } else { - Ok(other) + Err(EXPECTED.into_lua_err())? } } - _ => Err("must be a string or Url".into_lua_err())?, + _ => Err(EXPECTED.into_lua_err())?, } } diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 6f551911..4a8a152f 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -246,12 +246,6 @@ impl Theme { } } -impl App { - pub fn bg_color(&self) -> String { - self.overall.get().bg.map(|c| c.to_string()).unwrap_or_default() - } -} - fn deserialize_syntect_theme<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, diff --git a/yazi-plugin/preset/components/app.lua b/yazi-plugin/preset/components/app.lua new file mode 100644 index 00000000..951e8fd3 --- /dev/null +++ b/yazi-plugin/preset/components/app.lua @@ -0,0 +1,5 @@ +App = { + _id = "app", +} + +function App.bg() return th.app.overall:bg() or "reset" end diff --git a/yazi-plugin/preset/components/backdrop.lua b/yazi-plugin/preset/components/backdrop.lua new file mode 100644 index 00000000..05856f31 --- /dev/null +++ b/yazi-plugin/preset/components/backdrop.lua @@ -0,0 +1,13 @@ +Backdrop = { + _id = "backdrop", +} + +function Backdrop:new(area) return setmetatable({ _area = area }, { __index = self }) end + +function Backdrop:reflow() return {} end + +function Backdrop:redraw() + return { + ui.Fill(self._area):style(th.app.overall), + } +end diff --git a/yazi-plugin/preset/components/entity.lua b/yazi-plugin/preset/components/entity.lua index 5ecabbad..99c61581 100644 --- a/yazi-plugin/preset/components/entity.lua +++ b/yazi-plugin/preset/components/entity.lua @@ -115,9 +115,9 @@ function Entity:style_rev() local s = self:style() local bg = s:bg(true) if bg then - return ui.Style():fg(bg):bg("reset"):reverse(true) + return ui.Style():fg(bg):bg(App.bg()):reverse(true) elseif s:raw().reversed then - return ui.Style():bg("reset"):reverse(true) + return ui.Style():bg(App.bg()):reverse(true) end end diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index 02e00858..15d2557f 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -25,6 +25,7 @@ end function Root:build() self._children = { + Backdrop:new(self._area), Header:new(self._chunks[1], cx.active), Tabs:new(self._chunks[2]), Tab:new(self._chunks[3], cx.active), diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index 1d0e2969..377ae94c 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -41,7 +41,7 @@ function Status:mode() local style = self:style() return ui.Line { - ui.Span(th.status.sep_left.open):fg(style.main:bg()):bg("reset"), + ui.Span(th.status.sep_left.open):fg(style.main:bg()):bg(App.bg()), ui.Span(" " .. mode .. " "):style(style.main), ui.Span(th.status.sep_left.close):fg(style.main:bg()):bg(style.alt:bg()), } @@ -127,7 +127,7 @@ function Status:position() return ui.Line { ui.Span(th.status.sep_right.open):fg(style.main:bg()):bg(style.alt:bg()), ui.Span(string.format(" %2d/%-2d ", math.min(cursor + 1, length), length)):style(style.main), - ui.Span(th.status.sep_right.close):fg(style.main:bg()):bg("reset"), + ui.Span(th.status.sep_right.close):fg(style.main:bg()):bg(App.bg()), } end diff --git a/yazi-plugin/preset/plugins/dnd.lua b/yazi-plugin/preset/plugins/dnd.lua index 6640e42f..552c26de 100644 --- a/yazi-plugin/preset/plugins/dnd.lua +++ b/yazi-plugin/preset/plugins/dnd.lua @@ -20,7 +20,7 @@ function M.cut_uri_list(list) local from = Url(ya.percent_decode(line:sub(8))) if from.name then - local to = cx.active.current.cwd:join(from.name) + local to = cx.active.current.cwd:join(Path.os(from.name)) ya.async(function() ya.task("cut", { from = from, to = to }):spawn() end) end diff --git a/yazi-plugin/preset/plugins/json.lua b/yazi-plugin/preset/plugins/json.lua index 159bfc65..bbe51382 100644 --- a/yazi-plugin/preset/plugins/json.lua +++ b/yazi-plugin/preset/plugins/json.lua @@ -28,7 +28,7 @@ function M:peek(job) i = i + #wrapped for j = from, to do - lines[#lines + 1] = wrapped[j] + lines[#lines + 1] = M.normalize_bg(wrapped[j]) end until i >= job.skip + limit @@ -42,4 +42,13 @@ end function M:seek(job) require("code"):seek(job) end +function M.normalize_bg(line) + local bg = th.app.overall:bg() + if bg then + return line:map(function(span) return span:bg(bg) end) + else + return line + end +end + return M diff --git a/yazi-plugin/src/standard.rs b/yazi-plugin/src/standard.rs index 69fc4c9b..13002745 100644 --- a/yazi-plugin/src/standard.rs +++ b/yazi-plugin/src/standard.rs @@ -47,6 +47,8 @@ fn stage_1(lua: &Lua) -> Result<()> { lua.load(preset!("components/header")).set_name("header.lua").exec()?; lua.load(preset!("components/linemode")).set_name("linemode.lua").exec()?; + lua.load(preset!("components/app")).set_name("app.lua").exec()?; + lua.load(preset!("components/backdrop")).set_name("backdrop.lua").exec()?; lua.load(preset!("components/marker")).set_name("marker.lua").exec()?; lua.load(preset!("components/markers")).set_name("markers.lua").exec()?; lua.load(preset!("components/modal")).set_name("modal.lua").exec()?; diff --git a/yazi-runner/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs index 07a2b237..6f69da1a 100644 --- a/yazi-runner/src/loader/loader.rs +++ b/yazi-runner/src/loader/loader.rs @@ -55,6 +55,8 @@ impl Default for Loader { ("video".to_owned(), preset!("plugins/video").into()), ("zoxide".to_owned(), preset!("plugins/zoxide").into()), // Components + ("app".to_owned(), [][..].into()), + ("backdrop".to_owned(), [][..].into()), ("current".to_owned(), [][..].into()), ("entity".to_owned(), [][..].into()), ("header".to_owned(), [][..].into()), diff --git a/yazi-term/src/sequence/mod.rs b/yazi-term/src/sequence/mod.rs index defec32c..b101ad74 100644 --- a/yazi-term/src/sequence/mod.rs +++ b/yazi-term/src/sequence/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(clipboard csi_u cursor dnd erase mode query r#if restore_background set_background style sync traits); +yazi_macro::mod_flat!(clipboard csi_u cursor dnd erase mode query r#if style sync traits); diff --git a/yazi-term/src/sequence/restore_background.rs b/yazi-term/src/sequence/restore_background.rs deleted file mode 100644 index 136fe8ac..00000000 --- a/yazi-term/src/sequence/restore_background.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::fmt::{self, Display}; - -/// Restore background color to default -pub struct RestoreBackground; - -impl Display for RestoreBackground { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\x1b]111\x1b\\") } -} diff --git a/yazi-term/src/sequence/set_background.rs b/yazi-term/src/sequence/set_background.rs deleted file mode 100644 index f6d944aa..00000000 --- a/yazi-term/src/sequence/set_background.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::fmt::{self, Display}; - -/// Set background color to a RGB value or named color -pub struct SetBackground<'a>(pub &'a str); - -impl Display for SetBackground<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.is_empty() { Ok(()) } else { write!(f, "\x1b]11;{}\x1b\\", self.0) } - } -} diff --git a/yazi-tui/src/option.rs b/yazi-tui/src/option.rs index bdfc81da..04c2c051 100644 --- a/yazi-tui/src/option.rs +++ b/yazi-tui/src/option.rs @@ -1,12 +1,9 @@ -use yazi_config::{THEME, YAZI}; +use yazi_config::YAZI; pub(super) struct RatermOption { - pub bg: String, pub mouse: bool, } impl Default for RatermOption { - fn default() -> Self { - Self { bg: THEME.app.bg_color(), mouse: !YAZI.mgr.mouse_events.get().is_empty() } - } + fn default() -> Self { Self { mouse: !YAZI.mgr.mouse_events.get().is_empty() } } } diff --git a/yazi-tui/src/raterm.rs b/yazi-tui/src/raterm.rs index 4a7a498b..f85f5199 100644 --- a/yazi-tui/src/raterm.rs +++ b/yazi-tui/src/raterm.rs @@ -6,7 +6,7 @@ use yazi_config::YAZI; use yazi_emulator::{Emulator, Mux, TMUX}; use yazi_macro::writef; use yazi_shim::cell::SyncCell; -use yazi_term::{TERM, event::{Event, KeyEventKind}, sequence::{DisableBracketedPaste, DisableDrag, DisableDrop, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableDrag, EnableDrop, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RequestKeyboardFlags, RestoreBackground, RestoreCursorStyle, SetBackground, SetTitle, ShowCursor}, stream::EventStream}; +use yazi_term::{TERM, event::{Event, KeyEventKind}, sequence::{DisableBracketedPaste, DisableDrag, DisableDrop, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableDrag, EnableDrop, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RequestKeyboardFlags, RestoreCursorStyle, SetTitle, ShowCursor}, stream::EventStream}; use yazi_tty::{TTY, TtyWriter}; use crate::{RatermBackend, RatermOption, RatermState}; @@ -42,10 +42,9 @@ impl Raterm { let opt = RatermOption::default(); writef!( TTY.writer(), - "{}{RequestCursorStyle}{RequestCursorBlink}{RequestKeyboardFlags}{RequestDA1}{}{}{EnableBracketedPaste}{EnableFocusChange}{}{}{}", + "{}{RequestCursorStyle}{RequestCursorBlink}{RequestKeyboardFlags}{RequestDA1}{}{EnableBracketedPaste}{EnableFocusChange}{}{}{}", If(!TMUX.get(), EnterAlternateScreen), If(TMUX.get(), EnterAlternateScreen), - SetBackground(&opt.bg), EnableDrag(""), EnableDrop(&["text/uri-list"]), If(opt.mouse, EnableMouseCapture), @@ -82,9 +81,8 @@ impl Raterm { _ = writef!( TTY.writer(), - "{}{DisableDrop}{DisableDrag}{}{}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", + "{}{DisableDrop}{DisableDrag}{}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", If(state.mouse, DisableMouseCapture), - If(state.bg, RestoreBackground), If(state.csi_u, PopKeyboardFlags), RestoreCursorStyle { shape: state.cursor_shape, blink: state.cursor_blink }, If(state.title, SetTitle("")), diff --git a/yazi-tui/src/state.rs b/yazi-tui/src/state.rs index e096e031..645e3c43 100644 --- a/yazi-tui/src/state.rs +++ b/yazi-tui/src/state.rs @@ -2,7 +2,6 @@ use crate::RatermOption; #[derive(Clone, Copy)] pub struct RatermState { - pub bg: bool, pub csi_u: bool, pub mouse: bool, pub title: bool, @@ -13,7 +12,6 @@ pub struct RatermState { impl RatermState { pub(super) const fn default() -> Self { Self { - bg: false, csi_u: false, mouse: false, title: false, @@ -33,13 +31,6 @@ impl RatermState { let cursor_blink = resp.contains("\x1b[?12;1$y"); - Self { - bg: !opt.bg.is_empty(), - csi_u, - mouse: opt.mouse, - title: false, - cursor_shape, - cursor_blink, - } + Self { csi_u, mouse: opt.mouse, title: false, cursor_shape, cursor_blink } } } From 9cfeb67db70241992bad181c3969dbae5f5fd019 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 9 Jun 2026 22:52:43 +0800 Subject: [PATCH 12/28] refactor: make Lua bindings `Send` (#4027) --- Cargo.lock | 79 ++++++------------- Cargo.toml | 7 +- nix/yazi-unwrapped.nix | 5 +- yazi-actor/src/app/lua.rs | 3 +- yazi-actor/src/lives/file.rs | 32 +------- yazi-actor/src/lives/files.rs | 10 +-- yazi-actor/src/lives/folder.rs | 33 +++----- yazi-actor/src/lives/preference.rs | 25 ++---- yazi-actor/src/lives/preview.rs | 10 +-- yazi-actor/src/lives/tab.rs | 49 ++++-------- yazi-actor/src/lives/task.rs | 14 ++-- yazi-actor/src/lives/tasks.rs | 23 ++---- yazi-actor/src/lives/which.rs | 13 ++- yazi-binding/Cargo.toml | 2 + yazi-binding/src/config/fetcher.rs | 9 +-- yazi-binding/src/config/open_rule.rs | 9 +-- yazi-binding/src/config/opener_rule.rs | 12 ++- yazi-binding/src/config/preloader.rs | 9 +-- yazi-binding/src/config/previewer.rs | 9 +-- yazi-binding/src/config/spotter.rs | 9 +-- yazi-binding/src/dnd.rs | 23 ++---- yazi-binding/src/event/action.rs | 17 +++- yazi-binding/src/event/cmd.rs | 21 ++++- yazi-binding/src/file.rs | 21 +---- yazi-binding/src/keymap/chord.rs | 18 +++-- yazi-binding/src/keymap/section.rs | 11 ++- yazi-binding/src/lib.rs | 2 +- yazi-binding/src/macros.rs | 56 ++----------- yazi-binding/src/path.rs | 27 ++----- yazi-binding/src/process/output.rs | 18 ++--- yazi-binding/src/scheme.rs | 15 ++-- {yazi-dds => yazi-binding}/src/sendable.rs | 46 +++++------ yazi-binding/src/url.rs | 55 +++---------- yazi-config/src/keymap/chord.rs | 26 +----- .../src/keymap/{cow.rs => chord_cow.rs} | 0 yazi-config/src/keymap/mod.rs | 2 +- yazi-dds/Cargo.toml | 1 - yazi-dds/src/ember/custom.rs | 2 +- yazi-dds/src/lib.rs | 2 +- yazi-emulator/Cargo.toml | 12 +-- yazi-plugin/src/utils/call.rs | 2 +- yazi-plugin/src/utils/layer.rs | 2 +- yazi-plugin/src/utils/sync.rs | 3 +- yazi-runner/Cargo.toml | 1 - yazi-runner/src/entry/job.rs | 2 +- yazi-runner/src/fetcher/job.rs | 3 +- yazi-runner/src/preloader/job.rs | 3 +- yazi-runner/src/previewer/job.rs | 3 +- yazi-runner/src/spot.rs | 3 +- yazi-scheduler/src/plugin/in.rs | 2 +- .../src/event/{cow.rs => action_cow.rs} | 0 yazi-shared/src/event/actions.rs | 57 +++++++++++++ yazi-shared/src/event/mod.rs | 2 +- yazi-shim/src/mlua/cache.rs | 9 +++ yazi-shim/src/mlua/mod.rs | 2 +- yazi-shim/src/mlua/traits.rs | 68 +++++++++++++++- 56 files changed, 395 insertions(+), 504 deletions(-) rename {yazi-dds => yazi-binding}/src/sendable.rs (84%) rename yazi-config/src/keymap/{cow.rs => chord_cow.rs} (100%) rename yazi-shared/src/event/{cow.rs => action_cow.rs} (100%) create mode 100644 yazi-shared/src/event/actions.rs create mode 100644 yazi-shim/src/mlua/cache.rs diff --git a/Cargo.lock b/Cargo.lock index 2ad2822a..9f54b156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "imgref" -version = "1.12.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" +checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" [[package]] name = "indexmap" @@ -3337,9 +3337,8 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "ratatui" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +version = "0.30.1" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "instability", "ratatui-core", @@ -3353,8 +3352,7 @@ dependencies = [ [[package]] name = "ratatui-core" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "bitflags 2.13.0", "compact_str", @@ -3366,7 +3364,7 @@ dependencies = [ "lru", "palette", "serde", - "strum 0.28.0", + "strum", "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", @@ -3376,8 +3374,7 @@ dependencies = [ [[package]] name = "ratatui-crossterm" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "cfg-if", "crossterm 0.28.1", @@ -3389,8 +3386,7 @@ dependencies = [ [[package]] name = "ratatui-termion" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c16cc35a9d9114e0b2bb4b22018b96ae7f5fe60e2595dc73e622b4e78624835" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "instability", "ratatui-core", @@ -3400,8 +3396,7 @@ dependencies = [ [[package]] name = "ratatui-termwiz" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "ratatui-core", "termwiz", @@ -3409,19 +3404,18 @@ dependencies = [ [[package]] name = "ratatui-widgets" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +version = "0.3.1" +source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2" dependencies = [ "bitflags 2.13.0", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "indoc", "instability", "itertools", "line-clipping", "ratatui-core", "serde", - "strum 0.27.2", + "strum", "time", "unicode-segmentation", "unicode-width", @@ -4221,34 +4215,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", -] - [[package]] name = "strum" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ - "strum_macros 0.28.0", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", + "strum_macros", ] [[package]] @@ -5131,9 +5104,9 @@ dependencies = [ [[package]] name = "which" -version = "8.0.2" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" +checksum = "c789537cf2f7f55be8e6192f92e464174ee55f91af622777f7f1ceb0dbccd03e" dependencies = [ "libc", ] @@ -5674,7 +5647,7 @@ dependencies = [ "palette", "quantette", "ratatui", - "strum 0.28.0", + "strum", "tokio", "tracing", "yazi-config", @@ -5695,8 +5668,10 @@ dependencies = [ "anyhow", "futures", "hashbrown 0.17.1", + "inventory", "libc", "mlua", + "ordered-float 5.3.0", "paste", "ratatui", "serde_json", @@ -5797,7 +5772,7 @@ dependencies = [ "regex", "serde", "serde_with", - "strum 0.28.0", + "strum", "tokio", "toml", "tracing", @@ -5823,7 +5798,7 @@ dependencies = [ "ratatui", "serde", "serde_with", - "strum 0.28.0", + "strum", "syntect", "tokio", "tokio-stream", @@ -5856,7 +5831,6 @@ dependencies = [ "indexmap 2.14.0", "inventory", "mlua", - "ordered-float 5.3.0", "parking_lot", "paste", "serde", @@ -5966,7 +5940,7 @@ dependencies = [ "regex", "scopeguard", "serde", - "strum 0.28.0", + "strum", "tokio", "tracing", "trash", @@ -5999,7 +5973,7 @@ dependencies = [ "ratatui", "serde", "serde_with", - "strum 0.28.0", + "strum", "tokio", "yazi-binding", "yazi-boot", @@ -6089,7 +6063,6 @@ dependencies = [ "tracing", "yazi-binding", "yazi-config", - "yazi-dds", "yazi-fs", "yazi-macro", "yazi-shared", @@ -6111,7 +6084,7 @@ dependencies = [ "ordered-float 5.3.0", "parking_lot", "serde", - "strum 0.28.0", + "strum", "tokio", "tracing", "yazi-binding", @@ -6156,7 +6129,7 @@ dependencies = [ "percent-encoding", "serde", "serde_with", - "strum 0.28.0", + "strum", "thiserror 2.0.18", "tokio", "typed-path", @@ -6199,7 +6172,7 @@ dependencies = [ "rustix 1.1.4", "serde", "signal-hook 0.4.4", - "strum 0.28.0", + "strum", "thiserror 2.0.18", "tokio", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index 1056c8b0..8e5b77ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ parking_lot = "0.12.5" paste = "1.0.15" percent-encoding = "2.3.2" rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } -ratatui = { version = "=0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } +ratatui = { version = "0.30.1", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.3" russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" @@ -91,3 +91,8 @@ module_inception = "allow" option_map_unit_fn = "allow" unit_arg = "allow" use_self = "warn" + +[patch.crates-io] +ratatui = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" } +ratatui-core = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-core", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" } +ratatui-widgets = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-widgets", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" } diff --git a/nix/yazi-unwrapped.nix b/nix/yazi-unwrapped.nix index bba98fbf..543a6470 100644 --- a/nix/yazi-unwrapped.nix +++ b/nix/yazi-unwrapped.nix @@ -28,6 +28,9 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoLock = { lockFile = "${src}/Cargo.lock"; + outputHashes = { + "ratatui-0.30.1" = "sha256-wkEOgwAhW0ObtPDlJmOOoY5qf9c/P79ktN4b43jtcGw="; + }; }; env = { @@ -72,7 +75,7 @@ rustPlatform.buildRustPackage (finalAttrs: { hash = "sha256-kEVXejDg4ChFoMNBvKlwdFEyUuTcY2VuK9j0PdafKus="; }; }; - + meta = { description = "Blazing fast terminal file manager written in Rust, based on async I/O"; diff --git a/yazi-actor/src/app/lua.rs b/yazi-actor/src/app/lua.rs index 65a50abf..a2c793b1 100644 --- a/yazi-actor/src/app/lua.rs +++ b/yazi-actor/src/app/lua.rs @@ -1,6 +1,5 @@ use anyhow::Result; -use yazi_binding::runtime_scope; -use yazi_dds::Sendable; +use yazi_binding::{Sendable, runtime_scope}; use yazi_macro::succ; use yazi_parser::app::LuaForm; use yazi_plugin::LUA; diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index 3f259675..1aebfff2 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -1,7 +1,7 @@ use std::{ops::Deref, ptr}; -use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value}; -use yazi_binding::{Range, Style, cached_field}; +use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods}; +use yazi_binding::{Range, Style}; use yazi_config::THEME; use yazi_shared::{path::AsPath, url::UrlLike}; @@ -12,16 +12,6 @@ pub(super) struct File { idx: usize, folder: PtrCell, tab: PtrCell, - - v_cha: Option, - v_url: Option, - v_link_to: Option, - - v_name: Option, - v_path: Option, - v_cache: Option, - - v_bare: Option, } impl Deref for File { @@ -45,21 +35,7 @@ impl File { Ok(match super::FILE_CACHE.borrow_mut().entry(PtrCell(&folder.files[idx])) { Entry::Occupied(oe) => oe.into_mut().clone(), Entry::Vacant(ve) => { - let ud = Lives::scoped_userdata(Self { - idx, - folder: folder.into(), - tab: tab.into(), - - v_cha: None, - v_url: None, - v_link_to: None, - - v_name: None, - v_path: None, - v_cache: None, - - v_bare: None, - })?; + let ud = Lives::scoped_userdata(Self { idx, folder: folder.into(), tab: tab.into() })?; ve.insert(ud.clone()); ud } @@ -73,7 +49,7 @@ impl File { impl UserData for File { fn add_fields>(fields: &mut F) { yazi_binding::impl_file_fields!(fields); - cached_field!(fields, bare, |_, me| Ok(yazi_binding::File::new(&**me))); + fields.add_cached_field("bare", |_, me| Ok(yazi_binding::File::new(&**me))); fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered())); diff --git a/yazi-actor/src/lives/files.rs b/yazi-actor/src/lives/files.rs index 884f03dc..e67998ae 100644 --- a/yazi-actor/src/lives/files.rs +++ b/yazi-actor/src/lives/files.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; -use yazi_binding::cached_field; +use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; +use yazi_shim::mlua::UserDataFieldsExt; use super::{File, Filter, Lives, PtrCell}; @@ -9,8 +9,6 @@ pub(super) struct Files { window: Range, folder: PtrCell, tab: PtrCell, - - v_filter: Option, } impl Deref for Files { @@ -25,13 +23,13 @@ impl Files { folder: &yazi_core::tab::Folder, tab: &yazi_core::tab::Tab, ) -> mlua::Result { - Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into(), v_filter: None }) + Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into() }) } } impl UserData for Files { fn add_fields>(fields: &mut F) { - cached_field!(fields, filter, |_, me| me.filter().map(Filter::make).transpose()); + fields.add_static_field("filter", |_, me| me.filter().map(Filter::make).transpose()); } fn add_methods>(methods: &mut M) { diff --git a/yazi-actor/src/lives/folder.rs b/yazi-actor/src/lives/folder.rs index ab4ed872..c6f59d6e 100644 --- a/yazi-actor/src/lives/folder.rs +++ b/yazi-actor/src/lives/folder.rs @@ -1,8 +1,9 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, UserData, UserDataFields, Value}; -use yazi_binding::{FolderStage, Url, cached_field}; +use mlua::{AnyUserData, UserData, UserDataFields}; +use yazi_binding::{FolderStage, Url}; use yazi_config::LAYOUT; +use yazi_shim::mlua::UserDataFieldsExt; use super::{File, Files, Lives, PtrCell}; @@ -10,12 +11,6 @@ pub(super) struct Folder { window: Range, inner: PtrCell, tab: PtrCell, - - v_cwd: Option, - v_files: Option, - v_stage: Option, - v_window: Option, - v_hovered: Option, } impl Deref for Folder { @@ -38,30 +33,20 @@ impl Folder { } }; - Lives::scoped_userdata(Self { - window, - inner: inner.into(), - tab: tab.into(), - - v_cwd: None, - v_files: None, - v_stage: None, - v_window: None, - v_hovered: None, - }) + Lives::scoped_userdata(Self { window, inner: inner.into(), tab: tab.into() }) } } impl UserData for Folder { fn add_fields>(fields: &mut F) { - cached_field!(fields, cwd, |_, me| Ok(Url::new(&me.url))); - cached_field!(fields, files, |_, me| Files::make(0..me.files.len(), me, &me.tab)); - cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage.clone()))); - cached_field!(fields, window, |_, me| Files::make(me.window.clone(), me, &me.tab)); + fields.add_cached_field("cwd", |_, me| Ok(Url::new(&me.url))); + fields.add_static_field("files", |_, me| Files::make(0..me.files.len(), me, &me.tab)); + fields.add_cached_field("stage", |_, me| Ok(FolderStage::new(me.stage.clone()))); + fields.add_static_field("window", |_, me| Files::make(me.window.clone(), me, &me.tab)); fields.add_field_method_get("offset", |_, me| Ok(me.offset)); fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); - cached_field!(fields, hovered, |_, me| { + fields.add_static_field("hovered", |_, me| { me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose() }); } diff --git a/yazi-actor/src/lives/preference.rs b/yazi-actor/src/lives/preference.rs index 011a8c64..db772993 100644 --- a/yazi-actor/src/lives/preference.rs +++ b/yazi-actor/src/lives/preference.rs @@ -1,18 +1,12 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields, Value}; -use yazi_binding::cached_field; -use yazi_shim::strum::IntoStr; +use mlua::{AnyUserData, UserData, UserDataFields}; +use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr}; use super::{Lives, PtrCell}; pub(super) struct Preference { inner: PtrCell, - - v_name: Option, - v_linemode: Option, - - v_sort_by: Option, } impl Deref for Preference { @@ -23,26 +17,19 @@ impl Deref for Preference { impl Preference { pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result { - Lives::scoped_userdata(Self { - inner: inner.into(), - - v_name: None, - v_linemode: None, - - v_sort_by: None, - }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } impl UserData for Preference { fn add_fields>(fields: &mut F) { // Display - cached_field!(fields, name, |lua, me| lua.create_string(&me.name)); - cached_field!(fields, linemode, |lua, me| lua.create_string(&*me.linemode)); + fields.add_cached_field("name", |lua, me| lua.create_string(&me.name)); + fields.add_cached_field("linemode", |lua, me| lua.create_string(&*me.linemode)); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); // Sorting - cached_field!(fields, sort_by, |_, me| Ok(me.sort_by.into_str())); + fields.add_cached_field("sort_by", |_, me| Ok(me.sort_by.into_str())); fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse)); fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first)); diff --git a/yazi-actor/src/lives/preview.rs b/yazi-actor/src/lives/preview.rs index 30a4a1c1..fee0997e 100644 --- a/yazi-actor/src/lives/preview.rs +++ b/yazi-actor/src/lives/preview.rs @@ -1,15 +1,13 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields, Value}; -use yazi_binding::cached_field; +use mlua::{AnyUserData, UserData, UserDataFields}; use yazi_config::LAYOUT; +use yazi_shim::mlua::UserDataFieldsExt; use super::{Folder, Lives, PtrCell}; pub(super) struct Preview { tab: PtrCell, - - v_folder: Option, } impl Deref for Preview { @@ -20,14 +18,14 @@ impl Deref for Preview { impl Preview { pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result { - Lives::scoped_userdata(Self { tab: tab.into(), v_folder: None }) + Lives::scoped_userdata(Self { tab: tab.into() }) } } impl UserData for Preview { fn add_fields>(fields: &mut F) { fields.add_field_method_get("skip", |_, me| Ok(me.skip)); - cached_field!(fields, folder, |_, me| { + fields.add_static_field("folder", |_, me| { me.tab .hovered_folder() .map(|f| { diff --git a/yazi-actor/src/lives/tab.rs b/yazi-actor/src/lives/tab.rs index 69d5abb0..33571779 100644 --- a/yazi-actor/src/lives/tab.rs +++ b/yazi-actor/src/lives/tab.rs @@ -1,21 +1,13 @@ use std::{borrow::Cow, ops::Deref}; -use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value}; -use yazi_binding::{Id, UrlRef, cached_field}; +use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods}; +use yazi_binding::{Id, UrlRef}; +use yazi_shim::mlua::UserDataFieldsExt; use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected}; pub(super) struct Tab { inner: PtrCell, - - v_name: Option, - v_mode: Option, - v_pref: Option, - v_current: Option, - v_parent: Option, - v_selected: Option, - v_preview: Option, - v_finder: Option, } impl Deref for Tab { @@ -26,42 +18,29 @@ impl Deref for Tab { impl Tab { pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result { - Lives::scoped_userdata(Self { - inner: inner.into(), - - v_name: None, - v_mode: None, - v_pref: None, - v_current: None, - v_parent: None, - v_selected: None, - v_preview: None, - v_finder: None, - }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } impl UserData for Tab { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua, me| { - match me.name() { - Cow::Borrowed(s) => lua.create_string(s), - Cow::Owned(s) => lua.create_external_string(s), - } + fields.add_cached_field("name", |lua, me| match me.name() { + Cow::Borrowed(s) => lua.create_string(s), + Cow::Owned(s) => lua.create_external_string(s), }); - cached_field!(fields, mode, |_, me| Mode::make(&me.mode)); - cached_field!(fields, pref, |_, me| Preference::make(&me.pref)); - cached_field!(fields, current, |_, me| Folder::make(None, &me.current, me)); - cached_field!(fields, parent, |_, me| { + fields.add_static_field("mode", |_, me| Mode::make(&me.mode)); + fields.add_static_field("pref", |_, me| Preference::make(&me.pref)); + fields.add_static_field("current", |_, me| Folder::make(None, &me.current, me)); + fields.add_static_field("parent", |_, me| { me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() }); - cached_field!(fields, selected, |_, me| Selected::make(&me.selected)); + fields.add_static_field("selected", |_, me| Selected::make(&me.selected)); - cached_field!(fields, preview, |_, me| Preview::make(me)); - cached_field!(fields, finder, |_, me| me.finder.as_ref().map(Finder::make).transpose()); + fields.add_static_field("preview", |_, me| Preview::make(me)); + fields.add_static_field("finder", |_, me| me.finder.as_ref().map(Finder::make).transpose()); } fn add_methods>(methods: &mut M) { diff --git a/yazi-actor/src/lives/task.rs b/yazi-actor/src/lives/task.rs index 7deaae8e..ca465a01 100644 --- a/yazi-actor/src/lives/task.rs +++ b/yazi-actor/src/lives/task.rs @@ -1,16 +1,14 @@ use std::ops::Deref; -use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; -use yazi_binding::{SER_OPT, cached_field}; +use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields}; +use yazi_binding::SER_OPT; use yazi_scheduler::Progress; +use yazi_shim::mlua::UserDataFieldsExt; use super::{Lives, PtrCell}; pub(super) struct TaskSnap { inner: PtrCell, - - v_title: Option, - v_prog: Option, } impl Deref for TaskSnap { @@ -21,14 +19,14 @@ impl Deref for TaskSnap { impl TaskSnap { pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result { - Lives::scoped_userdata(Self { inner: inner.into(), v_title: None, v_prog: None }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } impl UserData for TaskSnap { fn add_fields>(fields: &mut F) { - cached_field!(fields, title, |lua, me| lua.create_string(&me.title)); - cached_field!(fields, prog, |lua, me| lua.to_value_with(&me.prog, SER_OPT)); + fields.add_cached_field("title", |lua, me| lua.create_string(&me.title)); + fields.add_cached_field("prog", |lua, me| lua.to_value_with(&me.prog, SER_OPT)); fields.add_field_method_get("running", |_, me| Ok(me.prog.running())); fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked())); diff --git a/yazi-actor/src/lives/tasks.rs b/yazi-actor/src/lives/tasks.rs index b19da6cb..d6c31dce 100644 --- a/yazi-actor/src/lives/tasks.rs +++ b/yazi-actor/src/lives/tasks.rs @@ -1,17 +1,14 @@ use std::ops::Deref; -use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; -use yazi_binding::{SER_OPT, cached_field}; +use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields}; +use yazi_binding::SER_OPT; +use yazi_shim::mlua::UserDataFieldsExt; use super::{Lives, PtrCell}; use crate::lives::{Behavior, TaskSnap}; pub(super) struct Tasks { inner: PtrCell, - - v_behavior: Option, - v_snaps: Option, - v_summary: Option, } impl Deref for Tasks { @@ -22,13 +19,7 @@ impl Deref for Tasks { impl Tasks { pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result { - Lives::scoped_userdata(Self { - inner: inner.into(), - - v_behavior: None, - v_snaps: None, - v_summary: None, - }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } @@ -36,9 +27,9 @@ impl UserData for Tasks { fn add_fields>(fields: &mut F) { fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); - cached_field!(fields, behavior, |_, me| Behavior::make(&me.scheduler.behavior)); + fields.add_static_field("behavior", |_, me| Behavior::make(&me.scheduler.behavior)); - cached_field!(fields, snaps, |lua, me| { + fields.add_static_field("snaps", |lua, me| { let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?; for snap in &me.snaps { tbl.raw_push(TaskSnap::make(snap)?)?; @@ -46,6 +37,6 @@ impl UserData for Tasks { Ok(tbl) }); - cached_field!(fields, summary, |lua, me| lua.to_value_with(&me.summary, SER_OPT)); + fields.add_cached_field("summary", |lua, me| lua.to_value_with(&me.summary, SER_OPT)); } } diff --git a/yazi-actor/src/lives/which.rs b/yazi-actor/src/lives/which.rs index e44281aa..8046728a 100644 --- a/yazi-actor/src/lives/which.rs +++ b/yazi-actor/src/lives/which.rs @@ -1,15 +1,12 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields, Value}; -use yazi_binding::cached_field; +use mlua::{AnyUserData, UserData, UserDataFields}; +use yazi_shim::mlua::UserDataFieldsExt; use super::{Lives, PtrCell}; pub(super) struct Which { inner: PtrCell, - - v_tx: Option, - v_cands: Option, } impl Deref for Which { @@ -20,15 +17,15 @@ impl Deref for Which { impl Which { pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result { - Lives::scoped_userdata(Self { inner: inner.into(), v_tx: None, v_cands: None }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } impl UserData for Which { fn add_fields>(fields: &mut F) { - cached_field!(fields, tx, |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); + fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); - cached_field!(fields, cands, |lua, me| { + fields.add_cached_field("cands", |lua, me| { lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow)) }); diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index ccd2385a..9a27f52e 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -34,7 +34,9 @@ ansi-to-tui = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } +inventory = { workspace = true } mlua = { workspace = true } +ordered-float = { workspace = true } paste = { workspace = true } ratatui = { workspace = true } serde_json = { workspace = true } diff --git a/yazi-binding/src/config/fetcher.rs b/yazi-binding/src/config/fetcher.rs index 00ab805e..68d76725 100644 --- a/yazi-binding/src/config/fetcher.rs +++ b/yazi-binding/src/config/fetcher.rs @@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use yazi_config::YAZI; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{FileRef, Id, Iter, cached_field}; +use crate::{FileRef, Id, Iter}; #[derive(Clone)] pub struct Fetcher { inner: Arc, - - v_name: Option, } impl Deref for Fetcher { @@ -20,7 +19,7 @@ impl Deref for Fetcher { impl Fetcher { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_name: None } + Self { inner: inner.into() } } } @@ -28,7 +27,7 @@ impl UserData for Fetcher { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); + fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); } } diff --git a/yazi-binding/src/config/open_rule.rs b/yazi-binding/src/config/open_rule.rs index 73acfaf7..64fdc129 100644 --- a/yazi-binding/src/config/open_rule.rs +++ b/yazi-binding/src/config/open_rule.rs @@ -2,13 +2,12 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use yazi_config::YAZI; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{FileRef, Id, Iter, cached_field}; +use crate::{FileRef, Id, Iter}; pub struct OpenRule { inner: Arc, - - v_use: Option, } impl Deref for OpenRule { @@ -23,7 +22,7 @@ impl From for Arc { impl OpenRule { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_use: None } + Self { inner: inner.into() } } } @@ -36,7 +35,7 @@ impl FromLua for OpenRule { impl UserData for OpenRule { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, use, |lua, me| { + fields.add_cached_field("use", |lua, me| { lua.create_sequence_from(me.r#use.iter().map(|s| s.as_str())) }); } diff --git a/yazi-binding/src/config/opener_rule.rs b/yazi-binding/src/config/opener_rule.rs index 0ce390f3..3e1b03ef 100644 --- a/yazi-binding/src/config/opener_rule.rs +++ b/yazi-binding/src/config/opener_rule.rs @@ -1,14 +1,12 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{Id, Iter, cached_field}; +use crate::{Id, Iter}; pub struct OpenerRule { inner: Arc, - - v_run: Option, - v_desc: Option, } impl Deref for OpenerRule { @@ -23,7 +21,7 @@ impl From for Arc { impl OpenerRule { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_run: None, v_desc: None } + Self { inner: inner.into() } } } @@ -39,10 +37,10 @@ impl FromLua for OpenerRule { impl UserData for OpenerRule { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, run, |lua, me| lua.create_string(&*me.run)); + fields.add_cached_field("run", |lua, me| lua.create_string(&*me.run)); fields.add_field_method_get("block", |_, me| Ok(me.block)); fields.add_field_method_get("orphan", |_, me| Ok(me.orphan)); - cached_field!(fields, desc, |lua, me| lua.create_string(&*me.desc)); + fields.add_cached_field("desc", |lua, me| lua.create_string(&*me.desc)); } } diff --git a/yazi-binding/src/config/preloader.rs b/yazi-binding/src/config/preloader.rs index 1d81e0eb..3848ceda 100644 --- a/yazi-binding/src/config/preloader.rs +++ b/yazi-binding/src/config/preloader.rs @@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use yazi_config::YAZI; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{FileRef, Id, Iter, cached_field}; +use crate::{FileRef, Id, Iter}; #[derive(Clone)] pub struct Preloader { inner: Arc, - - v_name: Option, } impl Deref for Preloader { @@ -20,7 +19,7 @@ impl Deref for Preloader { impl Preloader { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_name: None } + Self { inner: inner.into() } } } @@ -28,7 +27,7 @@ impl UserData for Preloader { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); + fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); } } diff --git a/yazi-binding/src/config/previewer.rs b/yazi-binding/src/config/previewer.rs index 4863a972..b3c5e4eb 100644 --- a/yazi-binding/src/config/previewer.rs +++ b/yazi-binding/src/config/previewer.rs @@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; use yazi_config::YAZI; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{FileRef, Id, Iter, cached_field}; +use crate::{FileRef, Id, Iter}; #[derive(Clone)] pub struct Previewer { inner: Arc, - - v_name: Option, } impl Deref for Previewer { @@ -24,7 +23,7 @@ impl From for Arc { impl Previewer { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_name: None } + Self { inner: inner.into() } } } @@ -38,7 +37,7 @@ impl UserData for Previewer { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); + fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); } } diff --git a/yazi-binding/src/config/spotter.rs b/yazi-binding/src/config/spotter.rs index 719331ff..d116dde8 100644 --- a/yazi-binding/src/config/spotter.rs +++ b/yazi-binding/src/config/spotter.rs @@ -2,14 +2,13 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; use yazi_config::YAZI; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{FileRef, Id, Iter, cached_field}; +use crate::{FileRef, Id, Iter}; #[derive(Clone)] pub struct Spotter { inner: Arc, - - v_name: Option, } impl Deref for Spotter { @@ -20,7 +19,7 @@ impl Deref for Spotter { impl Spotter { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_name: None } + Self { inner: inner.into() } } } @@ -28,7 +27,7 @@ impl UserData for Spotter { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - cached_field!(fields, name, |lua, me| lua.create_string(&*me.name)); + fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); } } diff --git a/yazi-binding/src/dnd.rs b/yazi-binding/src/dnd.rs index 6689acd8..b294e0f6 100644 --- a/yazi-binding/src/dnd.rs +++ b/yazi-binding/src/dnd.rs @@ -1,16 +1,11 @@ use std::{mem, ops::Deref}; -use mlua::{UserData, UserDataFields, Value}; -use yazi_shim::strum::IntoStr; +use mlua::{IntoLua, UserData, UserDataFields, Value}; +use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr}; use yazi_term::event::{DndDropArrive, DndEvent as Inner}; -use crate::{cached_field, cached_field_mut}; - pub struct DndEvent { inner: Inner, - - v_mimes: Option, - v_data: Option>, } impl Deref for DndEvent { @@ -20,7 +15,7 @@ impl Deref for DndEvent { } impl From for DndEvent { - fn from(inner: Inner) -> Self { Self { inner, v_mimes: None, v_data: None } } + fn from(inner: Inner) -> Self { Self { inner } } } impl UserData for DndEvent { @@ -35,7 +30,7 @@ impl UserData for DndEvent { fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str))); - cached_field!(fields, mimes, |lua, me| { + fields.add_cached_field("mimes", |lua, me| { if let Some(mimes) = me.inner.mimes() { lua.create_sequence_from(mimes.iter())?.into_lua(lua) } else { @@ -43,13 +38,11 @@ impl UserData for DndEvent { } }); - cached_field_mut!(fields, data, |lua, me| { - match &mut me.inner { - Inner::DropArrive(DndDropArrive { data, .. }) => { - lua.create_external_string(mem::take(data))?.into_lua(lua) - } - _ => Ok(Value::Nil), + fields.add_cached_field_mut("data", |lua, me| match &mut me.inner { + Inner::DropArrive(DndDropArrive { data, .. }) => { + lua.create_external_string(mem::take(data))?.into_lua(lua) } + _ => Ok(Value::Nil), }); } } diff --git a/yazi-binding/src/event/action.rs b/yazi-binding/src/event/action.rs index d108be34..d100e10a 100644 --- a/yazi-binding/src/event/action.rs +++ b/yazi-binding/src/event/action.rs @@ -1,6 +1,9 @@ -use std::ops::Deref; +use std::{mem, ops::{Deref, DerefMut}}; -use mlua::UserData; +use mlua::{UserData, UserDataFields}; +use yazi_shim::mlua::UserDataFieldsExt; + +use crate::event::Cmd; pub struct Action { inner: yazi_shared::event::Action, @@ -12,8 +15,16 @@ impl Deref for Action { fn deref(&self) -> &Self::Target { &self.inner } } +impl DerefMut for Action { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } +} + impl Action { pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } } -impl UserData for Action {} +impl UserData for Action { + fn add_fields>(fields: &mut F) { + fields.add_cached_field_mut("cmd", |_, me| Ok(Cmd::new(mem::take(&mut me.cmd)))); + } +} diff --git a/yazi-binding/src/event/cmd.rs b/yazi-binding/src/event/cmd.rs index bd46f44e..5bd17115 100644 --- a/yazi-binding/src/event/cmd.rs +++ b/yazi-binding/src/event/cmd.rs @@ -1,6 +1,9 @@ -use std::ops::Deref; +use std::{mem, ops::{Deref, DerefMut}}; -use mlua::UserData; +use mlua::{UserData, UserDataFields}; +use yazi_shim::mlua::UserDataFieldsExt; + +use crate::Sendable; pub struct Cmd { inner: yazi_shared::event::Cmd, @@ -12,8 +15,20 @@ impl Deref for Cmd { fn deref(&self) -> &Self::Target { &self.inner } } +impl DerefMut for Cmd { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } +} + impl Cmd { pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } } -impl UserData for Cmd {} +impl UserData for Cmd { + fn add_fields>(fields: &mut F) { + fields.add_cached_field_mut("name", |_, me| Ok(mem::take(&mut me.name))); + + fields.add_cached_field_mut("args", |lua, me| { + Sendable::args_to_table(lua, mem::take(&mut me.args)) + }); + } +} diff --git a/yazi-binding/src/file.rs b/yazi-binding/src/file.rs index 0ef0b640..0224fb91 100644 --- a/yazi-binding/src/file.rs +++ b/yazi-binding/src/file.rs @@ -11,14 +11,6 @@ const EXPECTED: &str = "expected a table, File, or fs::File"; #[derive(Clone)] pub struct File { pub(crate) inner: yazi_fs::File, - - v_cha: Option, - v_url: Option, - v_link_to: Option, - - v_name: Option, - v_path: Option, - v_cache: Option, } impl Deref for File { @@ -32,18 +24,7 @@ impl From for yazi_fs::File { } impl File { - pub fn new(inner: impl Into) -> Self { - Self { - inner: inner.into(), - v_cha: None, - v_url: None, - v_link_to: None, - - v_name: None, - v_path: None, - v_cache: None, - } - } + pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } pub fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set("File", lua.create_function(|_, file: Self| Ok(file))?) diff --git a/yazi-binding/src/keymap/chord.rs b/yazi-binding/src/keymap/chord.rs index dc0a6b4f..057c6015 100644 --- a/yazi-binding/src/keymap/chord.rs +++ b/yazi-binding/src/keymap/chord.rs @@ -1,14 +1,12 @@ use std::{ops::Deref, sync::Arc}; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{Id, Iter, cached_field, event::Action, keymap::Key}; +use crate::{Id, Iter, event::Action, keymap::Key}; pub struct Chord { inner: Arc, - - v_on: Option, - v_run: Option, } impl Deref for Chord { @@ -23,7 +21,7 @@ impl From for Arc { impl Chord { pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into(), v_on: None, v_run: None } + Self { inner: inner.into() } } } @@ -37,10 +35,14 @@ impl UserData for Chord { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id))); - cached_field!(fields, on, |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); + fields + .add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); - cached_field!(fields, run, |lua, me| lua - .create_sequence_from(me.run.iter().cloned().map(Action::new))); + fields.add_cached_field("run", |lua, me| { + lua.create_sequence_from(me.run.iter().cloned().map(Action::new)) + }); + + fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc)); } } diff --git a/yazi-binding/src/keymap/section.rs b/yazi-binding/src/keymap/section.rs index 7f8ee4de..99894e0c 100644 --- a/yazi-binding/src/keymap/section.rs +++ b/yazi-binding/src/keymap/section.rs @@ -1,16 +1,15 @@ use std::ops::Deref; use anyhow::bail; -use mlua::{UserData, UserDataFields, Value}; +use mlua::{UserData, UserDataFields}; use yazi_config::KEYMAP; use yazi_shared::Layer; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{cached_field, keymap::Chords}; +use crate::keymap::Chords; pub struct KeymapSection { inner: &'static yazi_config::keymap::KeymapSection, - - v_rules: Option, } impl Deref for KeymapSection { @@ -37,12 +36,12 @@ impl TryFrom for KeymapSection { Layer::Notify => bail!("invalid layer"), }; - Ok(Self { inner, v_rules: None }) + Ok(Self { inner }) } } impl UserData for KeymapSection { fn add_fields>(fields: &mut F) { - cached_field!(fields, rules, |_, me| Ok(Chords::new(me.inner))); + fields.add_cached_field("rules", |_, me| Ok(Chords::new(me.inner))); } } diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index 4efa34b6..8c52be9e 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -2,4 +2,4 @@ mod macros; yazi_macro::mod_pub!(config elements event keymap process theme); -yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); +yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector sendable stage style tty url utils); diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 94f7c220..18b415e6 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -43,48 +43,6 @@ macro_rules! deprecate { }}; } -#[macro_export] -macro_rules! cached_field { - ($fields:ident, $key:ident, $value:expr) => { - $fields.add_field_function_get(stringify!($key), |lua, ud| { - use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData}; - ud.borrow_mut_scoped::>(|me| match paste::paste! { &me.[] } { - Some(v) if !v.is_userdata() => Ok(v.clone()), - Some(v @ UserData(ud)) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => { - Ok(v.clone()) - } - _ => { - let v = ($value as fn(&Lua, &Self) -> Result<_>)(lua, me)?.into_lua(lua)?; - paste::paste! { me.[] = Some(v.clone()) }; - Ok(v) - } - })? - }); - }; -} - -#[macro_export] -macro_rules! cached_field_mut { - ($fields:ident, $key:ident, $value:expr) => { - $fields.add_field_function_get(stringify!($key), |lua, ud| { - use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData}; - ud.borrow_mut_scoped::>(|me| match paste::paste! { &me.[] } { - Some(Ok(v)) if !v.is_userdata() => Ok(v.clone()), - Some(Ok(v @ UserData(ud))) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => { - Ok(v.clone()) - } - Some(Err(e)) => Err(e.clone()), - _ => { - let v = - ($value as fn(&Lua, &mut Self) -> Result<_>)(lua, me).and_then(|v| v.into_lua(lua)); - paste::paste! { me.[] = Some(v.clone()) }; - v - } - })? - }); - }; -} - #[macro_export] macro_rules! impl_area_method { ($methods:ident) => { @@ -245,19 +203,21 @@ macro_rules! impl_style_shorthands { #[macro_export] macro_rules! impl_file_fields { ($fields:ident) => { - $crate::cached_field!($fields, cha, |_, me| Ok($crate::Cha(me.cha))); - $crate::cached_field!($fields, url, |_, me| Ok($crate::Url::new(me.url_owned()))); - $crate::cached_field!($fields, link_to, |_, me| Ok(me.link_to.as_ref().map($crate::Path::new))); + use yazi_shim::mlua::UserDataFieldsExt; - $crate::cached_field!($fields, name, |lua, me| { + $fields.add_cached_field("cha", |_, me| Ok($crate::Cha(me.cha))); + $fields.add_cached_field("url", |_, me| Ok($crate::Url::new(me.url_owned()))); + $fields.add_cached_field("link_to", |_, me| Ok(me.link_to.as_ref().map($crate::Path::new))); + + $fields.add_cached_field("name", |lua, me| { me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - $crate::cached_field!($fields, path, |_, me| { + $fields.add_cached_field("path", |_, me| { use yazi_fs::FsUrl; use yazi_shared::url::AsUrl; Ok($crate::Path::new(me.url.as_url().unified_path())) }); - $crate::cached_field!($fields, cache, |_, me| { + $fields.add_cached_field("cache", |_, me| { use yazi_fs::FsUrl; Ok(me.url.cache().map($crate::Path::new)) }); diff --git a/yazi-binding/src/path.rs b/yazi-binding/src/path.rs index 09cc38d2..4ec51086 100644 --- a/yazi-binding/src/path.rs +++ b/yazi-binding/src/path.rs @@ -3,19 +3,13 @@ use std::ops::Deref; use mlua::{ExternalError, ExternalResult, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; use yazi_codegen::FromLuaOwned; use yazi_shared::{path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, Strand, StrandCow}}; - -use crate::cached_field; +use yazi_shim::mlua::UserDataFieldsExt; pub type PathRef = UserDataRef; #[derive(FromLuaOwned)] pub struct Path { inner: PathBufDyn, - - v_ext: Option, - v_name: Option, - v_parent: Option, - v_stem: Option, } impl Deref for Path { @@ -37,16 +31,7 @@ impl AsStrand for &Path { } impl Path { - pub fn new(path: impl Into) -> Self { - Self { - inner: path.into(), - - v_ext: None, - v_name: None, - v_parent: None, - v_stem: None, - } - } + pub fn new(path: impl Into) -> Self { Self { inner: path.into() } } pub fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set( @@ -110,14 +95,14 @@ impl Path { impl UserData for Path { fn add_fields>(fields: &mut F) { - cached_field!(fields, ext, |lua, me| { + fields.add_cached_field("ext", |lua, me| { me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - cached_field!(fields, name, |lua, me| { + fields.add_cached_field("name", |lua, me| { me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new))); - cached_field!(fields, stem, |lua, me| { + fields.add_cached_field("parent", |_, me| Ok(me.parent().map(Self::new))); + fields.add_cached_field("stem", |lua, me| { me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); diff --git a/yazi-binding/src/process/output.rs b/yazi-binding/src/process/output.rs index 97508849..7b0056cd 100644 --- a/yazi-binding/src/process/output.rs +++ b/yazi-binding/src/process/output.rs @@ -1,31 +1,25 @@ use std::mem; -use mlua::{UserData, UserDataFields, Value}; +use mlua::{UserData, UserDataFields}; +use yazi_shim::mlua::UserDataFieldsExt; use super::Status; -use crate::{cached_field, cached_field_mut}; pub struct Output { inner: std::process::Output, - - v_status: Option, - v_stdout: Option>, - v_stderr: Option>, } impl Output { - pub fn new(inner: std::process::Output) -> Self { - Self { inner, v_status: None, v_stdout: None, v_stderr: None } - } + pub fn new(inner: std::process::Output) -> Self { Self { inner } } } impl UserData for Output { fn add_fields>(fields: &mut F) { - cached_field!(fields, status, |_, me| Ok(Status::new(me.inner.status))); - cached_field_mut!(fields, stdout, |lua, me| { + fields.add_cached_field("status", |_, me| Ok(Status::new(me.inner.status))); + fields.add_cached_field_mut("stdout", |lua, me| { lua.create_external_string(mem::take(&mut me.inner.stdout)) }); - cached_field_mut!(fields, stderr, |lua, me| { + fields.add_cached_field_mut("stderr", |lua, me| { lua.create_external_string(mem::take(&mut me.inner.stderr)) }); } diff --git a/yazi-binding/src/scheme.rs b/yazi-binding/src/scheme.rs index 93bc9fd8..d4fe16a6 100644 --- a/yazi-binding/src/scheme.rs +++ b/yazi-binding/src/scheme.rs @@ -1,17 +1,14 @@ use std::ops::Deref; -use mlua::{UserData, UserDataFields, Value}; +use mlua::{UserData, UserDataFields}; use yazi_fs::FsScheme; use yazi_shared::scheme::SchemeLike; -use yazi_shim::strum::IntoStr; +use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr}; -use crate::{Path, cached_field}; +use crate::Path; pub struct Scheme { inner: yazi_shared::scheme::Scheme, - - v_kind: Option, - v_cache: Option, } impl Deref for Scheme { @@ -22,14 +19,14 @@ impl Deref for Scheme { impl Scheme { pub fn new(scheme: impl Into) -> Self { - Self { inner: scheme.into(), v_kind: None, v_cache: None } + Self { inner: scheme.into() } } } impl UserData for Scheme { fn add_fields>(fields: &mut F) { - cached_field!(fields, kind, |_, me| Ok(me.kind().into_str())); - cached_field!(fields, cache, |_, me| Ok(me.cache().map(Path::new))); + fields.add_cached_field("kind", |_, me| Ok(me.kind().into_str())); + fields.add_cached_field("cache", |_, me| Ok(me.cache().map(Path::new))); fields.add_field_method_get("is_virtual", |_, me| Ok(me.is_virtual())); } diff --git a/yazi-dds/src/sendable.rs b/yazi-binding/src/sendable.rs similarity index 84% rename from yazi-dds/src/sendable.rs rename to yazi-binding/src/sendable.rs index 93d8f6dc..4d474b0d 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-binding/src/sendable.rs @@ -5,6 +5,8 @@ use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value}; use ordered_float::OrderedFloat; use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow}; +use crate::{Id, Path, Url}; + pub struct Sendable; impl Sendable { @@ -40,14 +42,14 @@ impl Sendable { }); } Value::UserData(ud) => match ud.type_id() { - Some(t) if t == TypeId::of::() => { - return Ok(Data::Url(ud.take::()?.into())); + Some(t) if t == TypeId::of::() => { + return Ok(Data::Url(ud.take::()?.into())); } - Some(t) if t == TypeId::of::() => { - return Ok(Data::Path(ud.take::()?.into())); + Some(t) if t == TypeId::of::() => { + return Ok(Data::Path(ud.take::()?.into())); } - Some(t) if t == TypeId::of::() => { - return Ok(Data::Id(**ud.borrow::()?)); + Some(t) if t == TypeId::of::() => { + return Ok(Data::Id(**ud.borrow::()?)); } Some(t) if t == TypeId::of::() => return Ok(Data::Any(ud.take::()?.0)), _ => {} @@ -87,8 +89,8 @@ impl Sendable { } Value::Table(tbl) } - Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, - Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, + Data::Url(u) => Url::new(u).into_lua(lua)?, + Data::Path(u) => Path::new(u).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_external_string(b)?), Data::Any(a) => a.into_lua(lua)?, _ => Self::data_to_value_ref(lua, &data)?, @@ -117,9 +119,9 @@ impl Sendable { } Value::Table(tbl) } - Data::Id(i) => yazi_binding::Id(*i).into_lua(lua)?, - Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, - Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, + Data::Id(i) => Id(*i).into_lua(lua)?, + Data::Url(u) => Url::new(u).into_lua(lua)?, + Data::Path(u) => Path::new(u).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Any(a) => a.to_lua(lua)?, }) @@ -201,15 +203,9 @@ impl Sendable { Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::UserData(ud) => match ud.type_id() { - Some(t) if t == TypeId::of::() => { - DataKey::Url(ud.take::()?.into()) - } - Some(t) if t == TypeId::of::() => { - DataKey::Path(ud.take::()?.into()) - } - Some(t) if t == TypeId::of::() => { - DataKey::Id(**ud.borrow::()?) - } + Some(t) if t == TypeId::of::() => DataKey::Url(ud.take::()?.into()), + Some(t) if t == TypeId::of::() => DataKey::Path(ud.take::()?.into()), + Some(t) if t == TypeId::of::() => DataKey::Id(**ud.borrow::()?), _ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?, }, Value::Error(_) => Err("error is not supported".into_lua_err())?, @@ -220,8 +216,8 @@ impl Sendable { fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result { match key { DataKey::String(Cow::Owned(s)) => lua.create_external_string(s).map(Value::String), - DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua), - DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua), + DataKey::Url(u) => Url::new(u).into_lua(lua), + DataKey::Path(u) => Path::new(u).into_lua(lua), DataKey::Bytes(b) => lua.create_external_string(b).map(Value::String), _ => Self::key_to_value_ref(lua, &key), } @@ -234,9 +230,9 @@ impl Sendable { DataKey::Integer(i) => Value::Integer(*i), DataKey::Number(n) => Value::Number(n.0), DataKey::String(s) => Value::String(lua.create_string(&**s)?), - DataKey::Id(i) => yazi_binding::Id(*i).into_lua(lua)?, - DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, - DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, + DataKey::Id(i) => Id(*i).into_lua(lua)?, + DataKey::Url(u) => Url::new(u).into_lua(lua)?, + DataKey::Path(u) => Path::new(u).into_lua(lua)?, DataKey::Bytes(b) => Value::String(lua.create_string(b)?), }) } diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index 4064a285..edc34b7e 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -4,8 +4,9 @@ use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, MetaMethod, use yazi_codegen::FromLuaOwned; use yazi_fs::{FsHash64, FsHash128, FsUrl}; use yazi_shared::{path::{PathLike, StripPrefixError}, scheme::{SchemeCow, SchemeLike}, strand::{StrandLike, ToStrand}, url::{AsUrl, UrlCow, UrlLike}}; +use yazi_shim::mlua::UserDataFieldsExt; -use crate::{Path, Scheme, cached_field}; +use crate::{Path, Scheme}; pub type UrlRef = UserDataRef; @@ -14,19 +15,6 @@ const EXPECTED: &str = "expected a string, Url, or Path"; #[derive(FromLuaOwned)] pub struct Url { inner: yazi_shared::url::UrlBuf, - - v_path: Option, - v_name: Option, - v_stem: Option, - v_ext: Option, - v_urn: Option, - v_base: Option, - v_parent: Option, - - v_scheme: Option, - v_domain: Option, - - v_cache: Option, } impl Deref for Url { @@ -62,24 +50,7 @@ impl TryFrom<&[u8]> for Url { } impl Url { - pub fn new(url: impl Into) -> Self { - Self { - inner: url.into(), - - v_path: None, - v_name: None, - v_stem: None, - v_ext: None, - v_urn: None, - v_base: None, - v_parent: None, - - v_scheme: None, - v_domain: None, - - v_cache: None, - } - } + pub fn new(url: impl Into) -> Self { Self { inner: url.into() } } pub fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set( @@ -171,28 +142,28 @@ impl Url { impl UserData for Url { fn add_fields>(fields: &mut F) { - cached_field!(fields, path, |_, me| Ok(Path::new(me.loc()))); - cached_field!(fields, name, |lua, me| { + fields.add_cached_field("path", |_, me| Ok(Path::new(me.loc()))); + fields.add_cached_field("name", |lua, me| { me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - cached_field!(fields, stem, |lua, me| { + fields.add_cached_field("stem", |lua, me| { me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - cached_field!(fields, ext, |lua, me| { + fields.add_cached_field("ext", |lua, me| { me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); - cached_field!(fields, urn, |_, me| Ok(Path::new(me.urn()))); - cached_field!(fields, base, |_, me| { + fields.add_cached_field("urn", |_, me| Ok(Path::new(me.urn()))); + fields.add_cached_field("base", |_, me| { Ok(Some(me.base()).filter(|u| !u.loc().is_empty()).map(Self::new)) }); - cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new))); + fields.add_cached_field("parent", |_, me| Ok(me.parent().map(Self::new))); - cached_field!(fields, scheme, |_, me| Ok(Scheme::new(me.scheme()))); - cached_field!(fields, domain, |lua, me| { + fields.add_cached_field("scheme", |_, me| Ok(Scheme::new(me.scheme()))); + fields.add_cached_field("domain", |lua, me| { me.scheme().domain().map(|s| lua.create_string(s)).transpose() }); - cached_field!(fields, cache, |_, me| Ok(me.cache().map(Path::new))); + fields.add_cached_field("cache", |_, me| Ok(me.cache().map(Path::new))); fields.add_field_method_get("is_regular", |_, me| Ok(me.is_regular())); fields.add_field_method_get("is_search", |_, me| Ok(me.is_search())); diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index a69b18be..98aba87e 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, hash::{Hash, Hasher}, sync::{Arc, OnceLock}}; use regex::Regex; use serde::{Deserialize, Deserializer, de}; use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; -use yazi_shared::{Id, Layer, Source, event::Action}; +use yazi_shared::{Id, Layer, event::{Actions, deserialize_actions}}; use super::{Key, ids::chord_id}; use crate::{Mixable, Platform, keymap::Chords}; @@ -16,8 +16,8 @@ pub struct Chord { pub id: Id, #[serde(deserialize_with = "deserialize_on")] pub on: Vec, - #[serde(deserialize_with = "deserialize_run::")] - pub run: Vec, + #[serde(deserialize_with = "deserialize_actions::")] + pub run: Actions, #[serde(default)] pub desc: String, #[serde(default)] @@ -95,26 +95,6 @@ where Ok(keys) } -fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let Some(layer) = Layer::from_repr(L) else { - return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); - }; - - let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; - - for action in &mut actions { - action.source = Source::Key; - if action.layer == Layer::Null { - action.layer = layer; - } - } - - Ok(actions) -} - // --- Matcher #[derive(Default)] pub struct ChordMatcher { diff --git a/yazi-config/src/keymap/cow.rs b/yazi-config/src/keymap/chord_cow.rs similarity index 100% rename from yazi-config/src/keymap/cow.rs rename to yazi-config/src/keymap/chord_cow.rs diff --git a/yazi-config/src/keymap/mod.rs b/yazi-config/src/keymap/mod.rs index b608ba6a..bd0d6786 100644 --- a/yazi-config/src/keymap/mod.rs +++ b/yazi-config/src/keymap/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(chord chords cow ids key keymap section); +yazi_macro::mod_flat!(chord chord_cow chords ids key keymap section); diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index ad770cab..e763a4d9 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -31,7 +31,6 @@ hashbrown = { workspace = true } indexmap = { workspace = true } inventory = { workspace = true } mlua = { workspace = true } -ordered-float = { workspace = true } parking_lot = { workspace = true } paste = { workspace = true } serde = { workspace = true } diff --git a/yazi-dds/src/ember/custom.rs b/yazi-dds/src/ember/custom.rs index 8f3a8f64..78d2de76 100644 --- a/yazi-dds/src/ember/custom.rs +++ b/yazi-dds/src/ember/custom.rs @@ -1,9 +1,9 @@ use mlua::{IntoLua, Lua, Value}; use serde::Serialize; +use yazi_binding::Sendable; use yazi_shared::data::Data; use super::Ember; -use crate::Sendable; #[derive(Clone, Debug)] pub struct EmberCustom { diff --git a/yazi-dds/src/lib.rs b/yazi-dds/src/lib.rs index aa41ee8f..d97c5a25 100644 --- a/yazi-dds/src/lib.rs +++ b/yazi-dds/src/lib.rs @@ -1,6 +1,6 @@ yazi_macro::mod_pub!(ember); -yazi_macro::mod_flat!(client payload pubsub pump sendable server state stream); +yazi_macro::mod_flat!(client payload pubsub pump server state stream); pub fn init() { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); diff --git a/yazi-emulator/Cargo.toml b/yazi-emulator/Cargo.toml index 7f3c1fb8..5afd92ef 100644 --- a/yazi-emulator/Cargo.toml +++ b/yazi-emulator/Cargo.toml @@ -20,9 +20,9 @@ yazi-term = { path = "../yazi-term", version = "26.5.6" } yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies -anyhow = { workspace = true } -either = { workspace = true } -ratatui = { workspace = true } -scopeguard = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } +anyhow = { workspace = true } +either = { workspace = true } +ratatui = { workspace = true } +scopeguard = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index b816e685..5703e9a5 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -1,6 +1,6 @@ use mlua::{ExternalError, Function, Lua, Table}; use tokio::sync::mpsc; -use yazi_dds::Sendable; +use yazi_binding::Sendable; use yazi_macro::emit; use yazi_shared::{Layer, Source, event::Action}; diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 4dd058d8..586ffedf 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -27,7 +27,7 @@ impl Utils { Ok(ChordCow::Owned(Chord { id: yazi_config::keymap::chord_id(), on: Self::parse_keys(cand.raw_get("on")?)?, - run: vec![relay!(which:callback, [i + 1])], + run: relay!(which:callback, [i + 1]).into(), desc: cand.raw_get("desc").unwrap_or_default(), r#for: Platform::All, })) diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index 1f224783..0706bc7f 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -2,9 +2,8 @@ use anyhow::Context; use futures::future::join_all; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; use tokio::sync::mpsc; -use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, runtime, runtime_mut}; +use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, Sendable, runtime, runtime_mut}; use yazi_core::{AppProxy, app::PluginOpt}; -use yazi_dds::Sendable; use yazi_runner::loader::LOADER; use yazi_shared::{LOCAL_SET, data::Data}; diff --git a/yazi-runner/Cargo.toml b/yazi-runner/Cargo.toml index 18a4eb21..29506f35 100644 --- a/yazi-runner/Cargo.toml +++ b/yazi-runner/Cargo.toml @@ -19,7 +19,6 @@ vendored-lua = [ "mlua/vendored" ] [dependencies] yazi-binding = { path = "../yazi-binding", version = "26.5.6" } yazi-config = { path = "../yazi-config", version = "26.5.6" } -yazi-dds = { path = "../yazi-dds", version = "26.5.6" } yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } diff --git a/yazi-runner/src/entry/job.rs b/yazi-runner/src/entry/job.rs index 773616ae..57af8558 100644 --- a/yazi-runner/src/entry/job.rs +++ b/yazi-runner/src/entry/job.rs @@ -1,6 +1,6 @@ use hashbrown::HashMap; use mlua::{IntoLua, Lua, Value}; -use yazi_dds::Sendable; +use yazi_binding::Sendable; use yazi_shared::{Id, SStr, data::{Data, DataKey}}; #[derive(Clone, Debug, Default)] diff --git a/yazi-runner/src/fetcher/job.rs b/yazi-runner/src/fetcher/job.rs index 5ef3eccf..bde5d942 100644 --- a/yazi-runner/src/fetcher/job.rs +++ b/yazi-runner/src/fetcher/job.rs @@ -1,8 +1,7 @@ use std::sync::Arc; use mlua::{IntoLua, Lua, Value}; -use yazi_binding::File; -use yazi_dds::Sendable; +use yazi_binding::{File, Sendable}; pub struct FetchJob { pub fetcher: Arc, diff --git a/yazi-runner/src/preloader/job.rs b/yazi-runner/src/preloader/job.rs index 1ae1ebe5..cc396b05 100644 --- a/yazi-runner/src/preloader/job.rs +++ b/yazi-runner/src/preloader/job.rs @@ -1,9 +1,8 @@ use std::sync::Arc; use mlua::{IntoLua, Lua, Value}; -use yazi_binding::{File, elements::Rect}; +use yazi_binding::{File, Sendable, elements::Rect}; use yazi_config::LAYOUT; -use yazi_dds::Sendable; pub struct PreloadJob { pub preloader: Arc, diff --git a/yazi-runner/src/previewer/job.rs b/yazi-runner/src/previewer/job.rs index 09a2a1ab..34b0b2b3 100644 --- a/yazi-runner/src/previewer/job.rs +++ b/yazi-runner/src/previewer/job.rs @@ -1,9 +1,8 @@ use std::sync::Arc; use mlua::{IntoLua, Lua, Value}; -use yazi_binding::{File, elements::Rect}; +use yazi_binding::{File, Sendable, elements::Rect}; use yazi_config::LAYOUT; -use yazi_dds::Sendable; use yazi_shared::pool::Symbol; #[derive(Clone, Debug)] diff --git a/yazi-runner/src/spot.rs b/yazi-runner/src/spot.rs index e23e8f7b..e63fd245 100644 --- a/yazi-runner/src/spot.rs +++ b/yazi-runner/src/spot.rs @@ -4,9 +4,8 @@ use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmS use tokio::{runtime::Handle, select}; use tokio_util::sync::CancellationToken; use tracing::error; -use yazi_binding::{File, Id}; +use yazi_binding::{File, Id, Sendable}; use yazi_config::plugin::Spotter; -use yazi_dds::Sendable; use yazi_shared::{Ids, pool::Symbol}; use crate::{Runner, loader::LOADER}; diff --git a/yazi-scheduler/src/plugin/in.rs b/yazi-scheduler/src/plugin/in.rs index 371c60f4..0f2c453d 100644 --- a/yazi-scheduler/src/plugin/in.rs +++ b/yazi-scheduler/src/plugin/in.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use hashbrown::HashMap; use mlua::{ExternalError, FromLua, Lua, Value}; -use yazi_dds::Sendable; +use yazi_binding::Sendable; use yazi_runner::entry::EntryJob; use yazi_shared::{Id, SStr, data::{Data, DataKey}}; diff --git a/yazi-shared/src/event/cow.rs b/yazi-shared/src/event/action_cow.rs similarity index 100% rename from yazi-shared/src/event/cow.rs rename to yazi-shared/src/event/action_cow.rs diff --git a/yazi-shared/src/event/actions.rs b/yazi-shared/src/event/actions.rs new file mode 100644 index 00000000..96892cc0 --- /dev/null +++ b/yazi-shared/src/event/actions.rs @@ -0,0 +1,57 @@ +use std::{ops::{Deref, DerefMut}, slice, vec}; + +use serde::{Deserializer, de}; +use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; + +use crate::{Layer, Source, event::Action}; + +#[derive(Clone, Debug, Default)] +pub struct Actions(Vec); + +impl Deref for Actions { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Actions { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl From for Actions { + fn from(value: Action) -> Self { Self(vec![value]) } +} + +impl IntoIterator for Actions { + type IntoIter = vec::IntoIter; + type Item = Action; + + fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } +} + +impl<'a> IntoIterator for &'a Actions { + type IntoIter = slice::Iter<'a, Action>; + type Item = &'a Action; + + fn into_iter(self) -> Self::IntoIter { self.0.iter() } +} + +pub fn deserialize_actions<'de, const L: u8, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let Some(layer) = Layer::from_repr(L) else { + return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); + }; + + let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; + + for action in &mut actions { + action.source = Source::Key; + if action.layer == Layer::Null { + action.layer = layer; + } + } + + Ok(Actions(actions)) +} diff --git a/yazi-shared/src/event/mod.rs b/yazi-shared/src/event/mod.rs index 49963f67..2b0615a8 100644 --- a/yazi-shared/src/event/mod.rs +++ b/yazi-shared/src/event/mod.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_flat!(action cmd cow de de_owned event); +yazi_macro::mod_flat!(action action_cow actions cmd de de_owned event); pub type Replier = tokio::sync::mpsc::UnboundedSender>; diff --git a/yazi-shim/src/mlua/cache.rs b/yazi-shim/src/mlua/cache.rs new file mode 100644 index 00000000..5897fc56 --- /dev/null +++ b/yazi-shim/src/mlua/cache.rs @@ -0,0 +1,9 @@ +use mlua::{Error::UserDataDestructed, Value}; + +pub(super) fn is_alive(v: &Value) -> bool { + if let Value::UserData(ud) = v { + !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) + } else { + true + } +} diff --git a/yazi-shim/src/mlua/mod.rs b/yazi-shim/src/mlua/mod.rs index 65220acf..de755c23 100644 --- a/yazi-shim/src/mlua/mod.rs +++ b/yazi-shim/src/mlua/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(sequence string traits); +yazi_macro::mod_flat!(cache sequence string traits); diff --git a/yazi-shim/src/mlua/traits.rs b/yazi-shim/src/mlua/traits.rs index 68b037b5..82488cba 100644 --- a/yazi-shim/src/mlua/traits.rs +++ b/yazi-shim/src/mlua/traits.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; -use mlua::{ExternalError, FromLua, Lua, Table, Value}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; -use crate::{mlua::SequenceIter, toml::DeserializeOverWith}; +use crate::{mlua::{SequenceIter, is_alive}, toml::DeserializeOverWith}; // --- DeserializeOverLua pub trait DeserializeOverLua: DeserializeOverWith { @@ -29,3 +29,67 @@ impl LuaTableExt for Table { } } } + +// --- UserDataFieldsExt +pub trait UserDataFieldsExt: UserDataFields { + fn add_cached_field( + &mut self, + key: &'static str, + compute: fn(&Lua, &S) -> mlua::Result, + ) -> &mut Self { + self.add_field_function_get(key, move |lua, ud| { + match ud.named_user_value::>(key)? { + Some(v) if is_alive(&v) => Ok(v), + _ => { + let v = compute(lua, &*ud.borrow::()?)?.into_lua(lua)?; + ud.set_named_user_value(key, &v)?; + Ok(v) + } + } + }); + self + } + + fn add_cached_field_mut( + &mut self, + key: &'static str, + compute: fn(&Lua, &mut S) -> mlua::Result, + ) -> &mut Self { + self.add_field_function_get(key, move |lua, ud| { + match ud.named_user_value::>(key)? { + Some(Value::Error(e)) => Err(*e), + Some(v) if is_alive(&v) => Ok(v), + _ => { + let v = match compute(lua, &mut *ud.borrow_mut::()?).and_then(|r| r.into_lua(lua)) { + Ok(v) => v, + Err(e) => Value::Error(Box::new(e)), + }; + + ud.set_named_user_value(key, &v)?; + Ok(v) + } + } + }); + self + } + + fn add_static_field( + &mut self, + key: &'static str, + compute: fn(&Lua, &S) -> mlua::Result, + ) -> &mut Self { + self.add_field_function_get(key, move |lua, ud| { + match ud.named_user_value::>(key)? { + Some(v) => Ok(v), + _ => { + let v = compute(lua, &*ud.borrow::()?)?.into_lua(lua)?; + ud.set_named_user_value(key, &v)?; + Ok(v) + } + } + }); + self + } +} + +impl> UserDataFieldsExt for F {} From 4086fbb16662aa5c13ddceb97fdcb21a91410dd8 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Wed, 10 Jun 2026 10:52:31 +0530 Subject: [PATCH 13/28] feat(nix): shell completions for `ya` CLI (#4030) --- nix/yazi-unwrapped.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nix/yazi-unwrapped.nix b/nix/yazi-unwrapped.nix index 543a6470..6bd05cbc 100644 --- a/nix/yazi-unwrapped.nix +++ b/nix/yazi-unwrapped.nix @@ -54,6 +54,11 @@ rustPlatform.buildRustPackage (finalAttrs: { --fish ./yazi-boot/completions/yazi.fish \ --zsh ./yazi-boot/completions/_yazi + installShellCompletion --cmd ya \ + --bash ./yazi-cli/completions/ya.bash \ + --fish ./yazi-cli/completions/ya.fish \ + --zsh ./yazi-cli/completions/_ya + # Resize logo for RES in 16 24 32 48 64 128 256; do mkdir -p $out/share/icons/hicolor/"$RES"x"$RES"/apps From 07a5deb1095321917e172770fc1630a57dedce52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 10 Jun 2026 15:09:49 +0800 Subject: [PATCH 14/28] feat: dynamic keymap Lua API (#4031) --- CHANGELOG.md | 2 + CONTRIBUTING.md | 1 + Cargo.lock | 1 + yazi-actor/src/lives/which.rs | 2 +- yazi-binding/Cargo.toml | 1 + yazi-binding/src/event/action.rs | 32 +++++++++++--- yazi-binding/src/event/actions.rs | 50 +++++++++++++++++++++ yazi-binding/src/event/mod.rs | 2 +- yazi-binding/src/keymap/chord.rs | 52 ++++++++++++++++------ yazi-binding/src/keymap/chord_cow.rs | 15 ------- yazi-binding/src/keymap/chords.rs | 32 ++++++++++---- yazi-binding/src/keymap/mod.rs | 2 +- yazi-binding/src/keymap/section.rs | 36 ++++++++------- yazi-config/src/keymap/chord.rs | 13 +++--- yazi-config/src/keymap/chord_arc.rs | 66 ++++++++++++++++++++++++++++ yazi-config/src/keymap/chord_cow.rs | 47 -------------------- yazi-config/src/keymap/chords.rs | 39 +++++++++++----- yazi-config/src/keymap/keymap.rs | 5 ++- yazi-config/src/keymap/mod.rs | 2 +- yazi-config/src/keymap/section.rs | 6 +-- yazi-core/src/help/help.rs | 8 ++-- yazi-core/src/which/option.rs | 14 +++--- yazi-core/src/which/sorter.rs | 5 ++- yazi-core/src/which/which.rs | 10 ++--- yazi-fm/src/router.rs | 6 +-- yazi-plugin/src/utils/layer.rs | 4 +- yazi-proxy/src/which.rs | 6 +-- yazi-shared/src/event/actions.rs | 27 ++++++++---- 28 files changed, 317 insertions(+), 169 deletions(-) create mode 100644 yazi-binding/src/event/actions.rs delete mode 100644 yazi-binding/src/keymap/chord_cow.rs create mode 100644 yazi-config/src/keymap/chord_arc.rs delete mode 100644 yazi-config/src/keymap/chord_cow.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ea999c49..ce4e5940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Drag and drop ([#4005]) - Bulk create ([#3793]) +- Dynamic keymap Lua API ([#4031]) - Image preview with Überzug++ on Niri ([#3990]) - New gait for input `backward` and `forward` actions ([#4012]) @@ -1742,3 +1743,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#4005]: https://github.com/sxyazi/yazi/pull/4005 [#4012]: https://github.com/sxyazi/yazi/pull/4012 [#4022]: https://github.com/sxyazi/yazi/pull/4022 +[#4031]: https://github.com/sxyazi/yazi/pull/4031 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21c1e5c9..ce91de1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,7 @@ This guide will help you understand how to contribute to the project. 3. [Development Setup](#development-setup) 4. [How to Contribute](#how-to-contribute) 5. [Pull Requests](#pull-requests) +6. [AI Policy](#ai-policy) ## Getting Started diff --git a/Cargo.lock b/Cargo.lock index 9f54b156..0f36b1b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5674,6 +5674,7 @@ dependencies = [ "ordered-float 5.3.0", "paste", "ratatui", + "serde", "serde_json", "tokio", "tokio-stream", diff --git a/yazi-actor/src/lives/which.rs b/yazi-actor/src/lives/which.rs index 8046728a..eaa90e43 100644 --- a/yazi-actor/src/lives/which.rs +++ b/yazi-actor/src/lives/which.rs @@ -26,7 +26,7 @@ impl UserData for Which { fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); fields.add_cached_field("cands", |lua, me| { - lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow)) + lua.create_sequence_from(me.inner.cands.iter().map(yazi_binding::keymap::Chord::from)) }); fields.add_field_method_get("active", |_, me| Ok(me.inner.active)); diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index 9a27f52e..52506079 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -39,6 +39,7 @@ mlua = { workspace = true } ordered-float = { workspace = true } paste = { workspace = true } ratatui = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } diff --git a/yazi-binding/src/event/action.rs b/yazi-binding/src/event/action.rs index d100e10a..984429c6 100644 --- a/yazi-binding/src/event/action.rs +++ b/yazi-binding/src/event/action.rs @@ -1,6 +1,6 @@ -use std::{mem, ops::{Deref, DerefMut}}; +use std::ops::{Deref, DerefMut}; -use mlua::{UserData, UserDataFields}; +use mlua::{FromLua, Lua, UserData, UserDataFields, Value}; use yazi_shim::mlua::UserDataFieldsExt; use crate::event::Cmd; @@ -19,12 +19,34 @@ impl DerefMut for Action { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } +impl From for yazi_shared::event::Action { + fn from(value: Action) -> Self { value.inner } +} + +impl From for yazi_shared::event::Actions { + fn from(value: Action) -> Self { Self::from(value.inner) } +} + impl Action { pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } } -impl UserData for Action { - fn add_fields>(fields: &mut F) { - fields.add_cached_field_mut("cmd", |_, me| Ok(Cmd::new(mem::take(&mut me.cmd)))); +impl FromLua for Action { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + Ok(match value { + Value::String(s) => Self { inner: s.to_str()?.parse()? }, + Value::UserData(ud) => ud.take()?, + _ => Err(mlua::Error::FromLuaConversionError { + from: value.type_name(), + to: "Action".to_owned(), + message: Some("expected a string or an Action".to_string()), + })?, + }) + } +} + +impl UserData for Action { + fn add_fields>(fields: &mut F) { + fields.add_cached_field("cmd", |_, me| Ok(Cmd::new(me.cmd.clone()))); } } diff --git a/yazi-binding/src/event/actions.rs b/yazi-binding/src/event/actions.rs new file mode 100644 index 00000000..2cd1a942 --- /dev/null +++ b/yazi-binding/src/event/actions.rs @@ -0,0 +1,50 @@ +use std::ops::{Deref, DerefMut}; + +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; + +use crate::event::Action; + +#[derive(Clone)] +pub struct Actions { + inner: yazi_shared::event::Actions, +} + +impl Deref for Actions { + type Target = yazi_shared::event::Actions; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl DerefMut for Actions { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } +} + +impl From for yazi_shared::event::Actions { + fn from(value: Actions) -> Self { value.inner } +} + +impl Actions { + pub fn new(inner: yazi_shared::event::Actions) -> Self { Self { inner } } +} + +impl FromLua for Actions { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + let inner = match value { + Value::Table(t) => t + .sequence_values::() + .map(|a| a.map(Into::into)) + .collect::>>()? + .into(), + v @ Value::String(_) => Action::from_lua(v, lua)?.into(), + _ => Err("expected a string or a table of actions".into_lua_err())?, + }; + + Ok(Self { inner }) + } +} + +impl IntoLua for Actions { + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua.create_sequence_from(self.inner.into_iter().map(Action::new))?.into_lua(lua) + } +} diff --git a/yazi-binding/src/event/mod.rs b/yazi-binding/src/event/mod.rs index 62b86ac6..c0011e6d 100644 --- a/yazi-binding/src/event/mod.rs +++ b/yazi-binding/src/event/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(action cmd); +yazi_macro::mod_flat!(action actions cmd); diff --git a/yazi-binding/src/keymap/chord.rs b/yazi-binding/src/keymap/chord.rs index 057c6015..9c417349 100644 --- a/yazi-binding/src/keymap/chord.rs +++ b/yazi-binding/src/keymap/chord.rs @@ -1,33 +1,61 @@ -use std::{ops::Deref, sync::Arc}; +use std::ops::Deref; -use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; +use serde::Deserialize; +use yazi_codegen::FromLuaOwned; use yazi_shim::mlua::UserDataFieldsExt; -use crate::{Id, Iter, event::Action, keymap::Key}; +use crate::{Id, Iter, event::Actions, keymap::Key}; +#[derive(FromLuaOwned)] pub struct Chord { - inner: Arc, + inner: yazi_config::keymap::ChordArc, } impl Deref for Chord { - type Target = Arc; + type Target = yazi_config::keymap::ChordArc; fn deref(&self) -> &Self::Target { &self.inner } } -impl From for Arc { +impl From<&yazi_config::keymap::ChordArc> for Chord { + fn from(value: &yazi_config::keymap::ChordArc) -> Self { Self { inner: value.clone() } } +} + +impl From for yazi_config::keymap::ChordArc { fn from(value: Chord) -> Self { value.inner } } impl Chord { - pub fn new(inner: impl Into>) -> Self { + pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } } -impl FromLua for Chord { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - Ok(Self::new(lua.from_value::(value)?)) +impl TryFrom<(Value, yazi_shared::Layer)> for Chord { + type Error = mlua::Error; + + fn try_from((value, layer): (Value, yazi_shared::Layer)) -> Result { + use yazi_config::keymap::Chord as C; + use yazi_shared::Layer as L; + + let de = mlua::serde::Deserializer::new(value); + let inner = match layer { + L::Null => C::<{ L::Null as u8 }>::deserialize(de)?.into(), + L::App => C::<{ L::App as u8 }>::deserialize(de)?.into(), + L::Mgr => C::<{ L::Mgr as u8 }>::deserialize(de)?.into(), + L::Tasks => C::<{ L::Tasks as u8 }>::deserialize(de)?.into(), + L::Spot => C::<{ L::Spot as u8 }>::deserialize(de)?.into(), + L::Pick => C::<{ L::Pick as u8 }>::deserialize(de)?.into(), + L::Input => C::<{ L::Input as u8 }>::deserialize(de)?.into(), + L::Confirm => C::<{ L::Confirm as u8 }>::deserialize(de)?.into(), + L::Help => C::<{ L::Help as u8 }>::deserialize(de)?.into(), + L::Cmp => C::<{ L::Cmp as u8 }>::deserialize(de)?.into(), + L::Which => C::<{ L::Which as u8 }>::deserialize(de)?.into(), + L::Notify => C::<{ L::Notify as u8 }>::deserialize(de)?.into(), + }; + + Ok(Self { inner }) } } @@ -38,9 +66,7 @@ impl UserData for Chord { fields .add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); - fields.add_cached_field("run", |lua, me| { - lua.create_sequence_from(me.run.iter().cloned().map(Action::new)) - }); + fields.add_cached_field("run", |_, me| Ok(Actions::new(me.run.clone()))); fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc)); } diff --git a/yazi-binding/src/keymap/chord_cow.rs b/yazi-binding/src/keymap/chord_cow.rs deleted file mode 100644 index dc5821db..00000000 --- a/yazi-binding/src/keymap/chord_cow.rs +++ /dev/null @@ -1,15 +0,0 @@ -use mlua::UserData; -use yazi_codegen::FromLuaOwned; - -#[derive(Clone, FromLuaOwned)] -pub struct ChordCow(pub yazi_config::keymap::ChordCow); - -impl From for ChordCow { - fn from(value: yazi_config::keymap::ChordCow) -> Self { Self(value) } -} - -impl From for yazi_config::keymap::ChordCow { - fn from(value: ChordCow) -> Self { value.0 } -} - -impl UserData for ChordCow {} diff --git a/yazi-binding/src/keymap/chords.rs b/yazi-binding/src/keymap/chords.rs index 07c160bc..2b1a3f6a 100644 --- a/yazi-binding/src/keymap/chords.rs +++ b/yazi-binding/src/keymap/chords.rs @@ -1,12 +1,14 @@ use std::ops::Deref; -use mlua::{ExternalError, ExternalResult, MetaMethod, UserData, UserDataMethods}; +use mlua::{ExternalError, ExternalResult, MetaMethod, Table, UserData, UserDataMethods, Value}; +use yazi_shim::mlua::DeserializeOverLua; use super::{Chord, ChordMatcher}; -use crate::keymap::ChordIter; +use crate::{event::Actions, keymap::ChordIter}; pub struct Chords { - inner: &'static yazi_config::keymap::Chords, + pub(super) inner: &'static yazi_config::keymap::Chords, + pub(super) layer: yazi_shared::Layer, } impl Deref for Chords { @@ -15,10 +17,6 @@ impl Deref for Chords { fn deref(&self) -> &Self::Target { self.inner } } -impl Chords { - pub fn new(inner: &'static yazi_config::keymap::Chords) -> Self { Self { inner } } -} - impl UserData for Chords { fn add_methods>(methods: &mut M) { methods.add_method("match", |_, me, matcher: Option| { @@ -32,14 +30,16 @@ impl UserData for Chords { }) }); - methods.add_method("insert", |_, me, (index, chord): (isize, Chord)| { + methods.add_method("insert", |_, me, (index, value): (isize, Value)| { let index = match index { 1.. => index - 1, 0 => return Err("index must be 1-based or negative".into_lua_err()), _ => index, }; + let chord = Chord::try_from((value, me.layer))?; me.insert(index, chord.clone()).into_lua_err()?; + Ok(chord) }); @@ -48,6 +48,22 @@ impl UserData for Chords { Ok(()) }); + methods.add_method("update", |_, me, (matcher, table): (ChordMatcher, Table)| { + let mut run: Option = table.raw_get("run")?; + if let Some(run) = &mut run { + table.raw_remove("run")?; + run.set(me.layer, yazi_shared::Source::Key); + } + + me.update(matcher.0, |mut chord| { + chord = chord.deserialize_over_lua(&table)?; + if let Some(run) = &run { + chord.run = run.clone().into(); + } + Ok(chord) + }) + }); + methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.load().len())); } } diff --git a/yazi-binding/src/keymap/mod.rs b/yazi-binding/src/keymap/mod.rs index 80857a4a..b8f2ac0e 100644 --- a/yazi-binding/src/keymap/mod.rs +++ b/yazi-binding/src/keymap/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(chord chord_cow chords key section); +yazi_macro::mod_flat!(chord chords key section); diff --git a/yazi-binding/src/keymap/section.rs b/yazi-binding/src/keymap/section.rs index 99894e0c..48fd44a9 100644 --- a/yazi-binding/src/keymap/section.rs +++ b/yazi-binding/src/keymap/section.rs @@ -3,13 +3,13 @@ use std::ops::Deref; use anyhow::bail; use mlua::{UserData, UserDataFields}; use yazi_config::KEYMAP; -use yazi_shared::Layer; use yazi_shim::mlua::UserDataFieldsExt; use crate::keymap::Chords; pub struct KeymapSection { inner: &'static yazi_config::keymap::KeymapSection, + layer: yazi_shared::Layer, } impl Deref for KeymapSection { @@ -18,30 +18,32 @@ impl Deref for KeymapSection { fn deref(&self) -> &Self::Target { self.inner } } -impl TryFrom for KeymapSection { +impl TryFrom for KeymapSection { type Error = anyhow::Error; - fn try_from(value: Layer) -> Result { - let inner = match value { - Layer::Null | Layer::App => bail!("invalid layer"), - Layer::Mgr => KEYMAP.mgr.as_erased(), - Layer::Tasks => KEYMAP.tasks.as_erased(), - Layer::Spot => KEYMAP.spot.as_erased(), - Layer::Pick => KEYMAP.pick.as_erased(), - Layer::Input => KEYMAP.input.as_erased(), - Layer::Confirm => KEYMAP.confirm.as_erased(), - Layer::Help => KEYMAP.help.as_erased(), - Layer::Cmp => KEYMAP.cmp.as_erased(), - Layer::Which => bail!("invalid layer"), - Layer::Notify => bail!("invalid layer"), + fn try_from(layer: yazi_shared::Layer) -> Result { + use yazi_shared::Layer as L; + + let inner = match layer { + L::Null | L::App => bail!("invalid layer"), + L::Mgr => KEYMAP.mgr.as_erased(), + L::Tasks => KEYMAP.tasks.as_erased(), + L::Spot => KEYMAP.spot.as_erased(), + L::Pick => KEYMAP.pick.as_erased(), + L::Input => KEYMAP.input.as_erased(), + L::Confirm => KEYMAP.confirm.as_erased(), + L::Help => KEYMAP.help.as_erased(), + L::Cmp => KEYMAP.cmp.as_erased(), + L::Which => bail!("invalid layer"), + L::Notify => bail!("invalid layer"), }; - Ok(Self { inner }) + Ok(Self { inner, layer }) } } impl UserData for KeymapSection { fn add_fields>(fields: &mut F) { - fields.add_cached_field("rules", |_, me| Ok(Chords::new(me.inner))); + fields.add_cached_field("rules", |_, me| Ok(Chords { inner: me.inner, layer: me.layer })); } } diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index 98aba87e..587a63c4 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -3,14 +3,15 @@ use std::{borrow::Cow, hash::{Hash, Hasher}, sync::{Arc, OnceLock}}; use regex::Regex; use serde::{Deserialize, Deserializer, de}; use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; +use yazi_codegen::DeserializeOver2; use yazi_shared::{Id, Layer, event::{Actions, deserialize_actions}}; use super::{Key, ids::chord_id}; -use crate::{Mixable, Platform, keymap::Chords}; +use crate::{Mixable, Platform, keymap::{ChordArc, Chords}}; static RE: OnceLock = OnceLock::new(); -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Deserialize, DeserializeOver2)] pub struct Chord { #[serde(skip, default = "chord_id")] pub id: Id, @@ -47,10 +48,6 @@ impl Hash for Chord { } impl Chord { - pub fn as_erased(self: &Arc) -> &Arc> { - unsafe { &*(self as *const Arc> as *const Arc>) } - } - pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() } pub fn run(&self) -> String { @@ -117,7 +114,7 @@ impl ChordMatcher { // --- Iter #[derive(Default)] pub struct ChordIter { - pub chords: Arc>>, + pub chords: Arc>, pub matcher: ChordMatcher, pub offset: usize, } @@ -133,7 +130,7 @@ impl From<&Chords> for ChordIter { } impl Iterator for ChordIter { - type Item = Arc; + type Item = ChordArc; fn next(&mut self) -> Option { while let Some(chord) = self.chords.get(self.offset) { diff --git a/yazi-config/src/keymap/chord_arc.rs b/yazi-config/src/keymap/chord_arc.rs new file mode 100644 index 00000000..c1fe3fee --- /dev/null +++ b/yazi-config/src/keymap/chord_arc.rs @@ -0,0 +1,66 @@ +use std::{ops::{Deref, DerefMut}, sync::Arc}; + +use serde::Deserialize; +use yazi_shared::{Layer, event::ActionCow}; + +use crate::{Mixable, keymap::Chord}; + +#[derive(Clone, Debug, Default, Deserialize)] +#[repr(transparent)] +pub struct ChordArc(Arc>); + +impl Deref for ChordArc { + type Target = Arc>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for ChordArc { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl From<&ChordArc> for ChordArc { + fn from(value: &ChordArc) -> Self { value.clone() } +} + +impl From> for ChordArc { + fn from(value: Chord) -> Self { ChordArc(Arc::new(value)).into_erased() } +} + +impl From> for Chord { + fn from(value: ChordArc) -> Self { + match Arc::try_unwrap(value.0) { + Ok(c) => c, + Err(arc) => Self::clone(&arc), + } + } +} + +impl From<&ChordArc> for Chord { + fn from(value: &ChordArc) -> Self { Self::clone(value) } +} + +impl ChordArc { + pub fn as_erased(&self) -> &ChordArc { + unsafe { &*(self as *const ChordArc as *const ChordArc) } + } + + pub fn into_erased(self) -> ChordArc { + ChordArc(unsafe { Arc::from_raw(Arc::into_raw(self.0) as *const Chord) }) + } + + pub fn to_seq(&self) -> Vec { + self.run.iter().rev().cloned().map(Into::into).collect() + } + + pub fn into_seq(self) -> Vec { + match Arc::try_unwrap(self.0) { + Ok(c) => c.run.into_iter().rev().map(Into::into).collect(), + Err(arc) => Self(arc).to_seq(), + } + } +} + +impl Mixable for ChordArc { + fn filter(&self) -> bool { self.0.filter() } +} diff --git a/yazi-config/src/keymap/chord_cow.rs b/yazi-config/src/keymap/chord_cow.rs deleted file mode 100644 index cf9a2d1c..00000000 --- a/yazi-config/src/keymap/chord_cow.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use yazi_shared::{Layer, event::ActionCow}; - -use super::Chord; - -#[derive(Clone, Debug)] -pub enum ChordCow { - Owned(Chord), - Borrowed(Arc>), -} - -impl From> for ChordCow { - fn from(c: Chord) -> Self { Self::Owned(c) } -} - -impl From>> for ChordCow { - fn from(c: Arc>) -> Self { Self::Borrowed(c) } -} - -impl From<&Arc>> for ChordCow { - fn from(c: &Arc>) -> Self { Self::Borrowed(c.clone()) } -} - -impl Deref for ChordCow { - type Target = Chord; - - fn deref(&self) -> &Self::Target { - match self { - Self::Owned(c) => c, - Self::Borrowed(c) => c, - } - } -} - -impl Default for ChordCow { - fn default() -> Self { Self::Owned(Chord::default()) } -} - -impl ChordCow { - pub fn into_seq(self) -> Vec { - match self { - Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(), - Self::Borrowed(c) => c.run.iter().rev().cloned().map(Into::into).collect(), - } - } -} diff --git a/yazi-config/src/keymap/chords.rs b/yazi-config/src/keymap/chords.rs index 7f6f0807..06a35f49 100644 --- a/yazi-config/src/keymap/chords.rs +++ b/yazi-config/src/keymap/chords.rs @@ -5,29 +5,28 @@ use serde::Deserialize; use yazi_shared::Layer; use yazi_shim::{arc_swap::{ArcSwapExt, IntoPointee}, vec::{IndexAtError, VecExt}}; -use super::Chord; -use crate::keymap::ChordMatcher; +use crate::keymap::{Chord, ChordArc, ChordMatcher}; #[derive(Debug, Default, Deserialize)] -pub struct Chords(ArcSwap>>>); +pub struct Chords(ArcSwap>>); impl Deref for Chords { - type Target = ArcSwap>>>; + type Target = ArcSwap>>; fn deref(&self) -> &Self::Target { &self.0 } } -impl From>>> for Chords { - fn from(inner: Vec>>) -> Self { Self(inner.into_pointee()) } +impl From>> for Chords { + fn from(inner: Vec>) -> Self { Self(inner.into_pointee()) } } impl Chords { - pub fn as_erased(&self) -> Arc>>> { + pub fn as_erased(&self) -> Arc>> { let chords = self.0.load_full(); - unsafe { Arc::from_raw(Arc::into_raw(chords) as *const Vec>>) } + unsafe { Arc::from_raw(Arc::into_raw(chords) as *const Vec>) } } - pub fn insert(&self, index: isize, rule: Arc) -> Result<(), IndexAtError> { + pub fn insert(&self, index: isize, rule: ChordArc) -> Result<(), IndexAtError> { self.0.try_rcu(|rules| { let (before, after) = rules.split_at(rules.index_at(index)?); Ok( @@ -46,12 +45,30 @@ impl Chords { pub fn remove(&self, matcher: ChordMatcher) { self.0.rcu(|chords| { let mut next = Vec::clone(chords); - next.retain(|chord| !matcher.matches(chord.as_erased())); + next.retain(|arc| !matcher.matches(arc.as_erased())); next }); } - pub(crate) fn unwrap_unchecked(self) -> Vec>> { + pub fn update( + &self, + matcher: ChordMatcher, + f: impl Fn(Chord) -> Result, + ) -> Result<(), E> { + self.0.try_rcu(|rules| { + let mut next = Vec::clone(rules); + for arc in &mut next { + if matcher.matches(arc.as_erased()) { + *arc = f(Chord::clone(arc.as_erased()))?.into(); + } + } + Ok(Arc::new(next)) + })?; + + Ok(()) + } + + pub(crate) fn unwrap_unchecked(self) -> Vec> { Arc::try_unwrap(self.0.into_inner()).expect("unique chords arc") } } diff --git a/yazi-config/src/keymap/keymap.rs b/yazi-config/src/keymap/keymap.rs index 36712efe..1ee131d8 100644 --- a/yazi-config/src/keymap/keymap.rs +++ b/yazi-config/src/keymap/keymap.rs @@ -6,7 +6,8 @@ use yazi_codegen::{DeserializeOver, DeserializeOver1}; use yazi_fs::{Xdg, ok_or_not_found}; use yazi_shared::Layer; -use super::{Chord, KeymapSection}; +use super::KeymapSection; +use crate::keymap::ChordArc; #[derive(Deserialize, DeserializeOver, DeserializeOver1)] pub struct Keymap { @@ -21,7 +22,7 @@ pub struct Keymap { } impl Keymap { - pub fn get(&self, layer: Layer) -> Arc>> { + pub fn get(&self, layer: Layer) -> Arc> { match layer { Layer::Null | Layer::App => Arc::new(Vec::new()), Layer::Mgr => self.mgr.deref().as_erased(), diff --git a/yazi-config/src/keymap/mod.rs b/yazi-config/src/keymap/mod.rs index bd0d6786..eda8e777 100644 --- a/yazi-config/src/keymap/mod.rs +++ b/yazi-config/src/keymap/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(chord chord_cow chords ids key keymap section); +yazi_macro::mod_flat!(chord chord_arc chords ids key keymap section); diff --git a/yazi-config/src/keymap/section.rs b/yazi-config/src/keymap/section.rs index 42df0036..a309d164 100644 --- a/yazi-config/src/keymap/section.rs +++ b/yazi-config/src/keymap/section.rs @@ -1,4 +1,4 @@ -use std::{ops::Deref, sync::Arc}; +use std::ops::Deref; use hashbrown::HashSet; use serde::Deserialize; @@ -7,7 +7,7 @@ use yazi_shared::Layer; use yazi_shim::toml::DeserializeOverHook; use super::{Chord, Chords, Key}; -use crate::mix; +use crate::{keymap::ChordArc, mix}; #[derive(Default, Deserialize, DeserializeOver2)] pub struct KeymapSection { @@ -41,7 +41,7 @@ impl DeserializeOverHook for KeymapSection { let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect(); let b_seen: HashSet<_> = keymap.iter().map(|c| on(c)).collect(); - let keymap: Vec>> = mix( + let keymap: Vec> = mix( self.prepend_keymap, keymap.into_iter().filter(|v| !a_seen.contains(&on(v))), self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))), diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index a59aa23d..35c1abd8 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -1,8 +1,6 @@ -use std::sync::Arc; - use anyhow::Result; use unicode_width::UnicodeWidthStr; -use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}}; +use yazi_config::{KEYMAP, YAZI, keymap::{ChordArc, Key}}; use yazi_macro::{act, render, render_and}; use yazi_shared::Layer; use yazi_term::{CursorStyle, TERM, event::KeyCode}; @@ -14,7 +12,7 @@ use crate::help::HELP_MARGIN; pub struct Help { pub visible: bool, pub layer: Layer, - pub(super) bindings: Vec>, + pub(super) bindings: Vec, // Filter pub keyword: String, @@ -75,7 +73,7 @@ impl Help { } // --- Bindings - pub fn window(&self) -> &[Arc] { + pub fn window(&self) -> &[ChordArc] { let end = (self.offset + self.limit()).min(self.bindings.len()); &self.bindings[self.offset..end] } diff --git a/yazi-core/src/which/option.rs b/yazi-core/src/which/option.rs index b6a5c53a..db3a917c 100644 --- a/yazi-core/src/which/option.rs +++ b/yazi-core/src/which/option.rs @@ -1,13 +1,13 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use tokio::sync::mpsc; -use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; +use yazi_config::{KEYMAP, keymap::{ChordArc, Key}}; use yazi_macro::impl_data_any; use yazi_shared::{Layer, event::ActionCow}; #[derive(Clone, Debug)] pub struct WhichOpt { - pub tx: Option>>, - pub cands: Vec, + pub tx: Option>>, + pub cands: Vec, pub silent: bool, pub times: usize, } @@ -24,7 +24,7 @@ impl TryFrom for WhichOpt { Ok(Self { tx: a.take_any2("tx").transpose()?, - cands: a.take_any_iter::().map(Into::into).collect(), + cands: a.take_any_iter::().map(Into::into).collect(), silent: a.bool("silent"), times: a.get("times").unwrap_or(0), }) @@ -39,7 +39,7 @@ impl From<(Layer, Key)> for WhichOpt { .get(layer) .iter() .filter(|&c| c.on.len() > 1 && c.on[0] == key) - .map(Into::into) + .cloned() .collect(), times: 1, silent: false, @@ -57,7 +57,7 @@ impl FromLua for WhichOpt { tx: t.raw_get::>("tx").ok().map(|t| t.0), cands: t .raw_get::
("cands")? - .sequence_values::() + .sequence_values::() .map(|c| c.map(Into::into)) .collect::>>()?, times: t.raw_get("times").unwrap_or_default(), @@ -72,7 +72,7 @@ impl IntoLua for WhichOpt { lua .create_table_from([ ("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?), - ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::keymap::ChordCow))?.into_lua(lua)?), + ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::keymap::Chord::new))?.into_lua(lua)?), ("times", self.times.into_lua(lua)?), ("silent", self.silent.into_lua(lua)?), ])? diff --git a/yazi-core/src/which/sorter.rs b/yazi-core/src/which/sorter.rs index a74c9215..6b5f41ac 100644 --- a/yazi-core/src/which/sorter.rs +++ b/yazi-core/src/which/sorter.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, mem}; -use yazi_config::{YAZI, keymap::ChordCow, which::SortBy}; +use yazi_config::{YAZI, keymap::ChordArc, which::SortBy}; use yazi_shared::{natsort, translit::Transliterator}; #[derive(Clone, Copy, PartialEq)] @@ -23,7 +23,7 @@ impl Default for WhichSorter { } impl WhichSorter { - pub fn sort(&self, items: &mut Vec) { + pub fn sort(&self, items: &mut Vec) { if self.by == SortBy::None || items.is_empty() { return; } @@ -53,6 +53,7 @@ impl WhichSorter { if self.reverse { ordering.reverse() } else { ordering } }); + // TODO: remove the `mem::take` since that involves Arc atomics refcount changes *items = indices.into_iter().map(|i| mem::take(&mut items[i])).collect(); } } diff --git a/yazi-core/src/which/which.rs b/yazi-core/src/which/which.rs index 6b772761..9392d89a 100644 --- a/yazi-core/src/which/which.rs +++ b/yazi-core/src/which/which.rs @@ -1,12 +1,12 @@ use tokio::sync::mpsc; -use yazi_config::keymap::{ChordCow, Key}; +use yazi_config::keymap::{ChordArc, Key}; use yazi_macro::{emit, render_and}; #[derive(Default)] pub struct Which { - pub tx: Option>>, + pub tx: Option>>, pub times: usize, - pub cands: Vec, + pub cands: Vec, // Active state pub active: bool, @@ -31,7 +31,7 @@ impl Which { render_and!(true) } - pub fn dismiss(&mut self, chord: Option) { + pub fn dismiss(&mut self, chord: Option) { self.times = 0; self.cands.clear(); @@ -39,7 +39,7 @@ impl Which { self.silent = false; if let Some(tx) = self.tx.take() { - _ = tx.send(chord.clone().map(Into::into)); + _ = tx.send(chord.as_ref().map(Into::into)); } if let Some(chord) = chord { emit!(Seq(chord.into_seq())); diff --git a/yazi-fm/src/router.rs b/yazi-fm/src/router.rs index 5b0b0d48..151fa709 100644 --- a/yazi-fm/src/router.rs +++ b/yazi-fm/src/router.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_actor::Ctx; -use yazi_config::{KEYMAP, keymap::{Chord, ChordCow, Key}}; +use yazi_config::{KEYMAP, keymap::{Chord, Key}}; use yazi_macro::act; use yazi_shared::Layer; @@ -37,7 +37,7 @@ impl<'a> Router<'a> { fn matches(&mut self, layer: Layer, key: Key) -> bool { for chord in &*KEYMAP.get(layer) { - let Chord { on, .. } = &**chord; + let Chord { on, .. } = chord.as_ref(); if on.is_empty() || on[0] != key { continue; } @@ -46,7 +46,7 @@ impl<'a> Router<'a> { let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term); act!(which:activate, cx, (layer, key)).ok(); } else { - Dispatcher::new(self.app).dispatch_seq(ChordCow::from(chord).into_seq()); + Dispatcher::new(self.app).dispatch_seq(chord.to_seq()); } return true; } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 586ffedf..80c4b9f4 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -3,7 +3,7 @@ use std::{str::FromStr, time::Duration}; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value}; use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_binding::{InputRx, elements::{Line, Pos, Text}, runtime}; -use yazi_config::{Platform, keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}}; +use yazi_config::{Platform, keymap::{Chord, ChordArc, Key}, popup::{ConfirmCfg, InputCfg}}; use yazi_core::notify::MessageOpt; use yazi_macro::relay; use yazi_proxy::{ConfirmProxy, InputProxy, NotifyProxy, WhichProxy}; @@ -24,7 +24,7 @@ impl Utils { .enumerate() .map(|(i, cand)| { let cand = cand?; - Ok(ChordCow::Owned(Chord { + Ok(ChordArc::from(Chord::<{ Layer::Null as u8 }> { id: yazi_config::keymap::chord_id(), on: Self::parse_keys(cand.raw_get("on")?)?, run: relay!(which:callback, [i + 1]).into(), diff --git a/yazi-proxy/src/which.rs b/yazi-proxy/src/which.rs index 3d52d8ab..1d1fa095 100644 --- a/yazi-proxy/src/which.rs +++ b/yazi-proxy/src/which.rs @@ -1,12 +1,12 @@ use tokio::sync::mpsc; -use yazi_config::keymap::ChordCow; +use yazi_config::keymap::ChordArc; use yazi_core::which::WhichOpt; use yazi_macro::{emit, relay}; pub struct WhichProxy; impl WhichProxy { - pub async fn activate(cands: Vec, silent: bool) -> Option { + pub async fn activate(cands: Vec, silent: bool) -> Option { let (tx, mut rx) = mpsc::unbounded_channel(); emit!(Call(relay!(which:activate).with_any("opt", WhichOpt { tx: Some(tx), @@ -14,6 +14,6 @@ impl WhichProxy { silent, times: 0, }))); - Some(rx.recv().await??.0) + Some(rx.recv().await??.into()) } } diff --git a/yazi-shared/src/event/actions.rs b/yazi-shared/src/event/actions.rs index 96892cc0..f0f6f937 100644 --- a/yazi-shared/src/event/actions.rs +++ b/yazi-shared/src/event/actions.rs @@ -22,6 +22,21 @@ impl From for Actions { fn from(value: Action) -> Self { Self(vec![value]) } } +impl From> for Actions { + fn from(value: Vec) -> Self { Self(value) } +} + +impl Actions { + pub fn set(&mut self, layer: Layer, source: Source) { + for action in &mut self.0 { + action.source = source; + if action.layer == Layer::Null { + action.layer = layer; + } + } + } +} + impl IntoIterator for Actions { type IntoIter = vec::IntoIter; type Item = Action; @@ -44,14 +59,8 @@ where return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); }; - let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; + let mut actions = Actions(OneOrMany::::deserialize_as(deserializer)?); + actions.set(layer, Source::Key); - for action in &mut actions { - action.source = Source::Key; - if action.layer == Layer::Null { - action.layer = layer; - } - } - - Ok(Actions(actions)) + Ok(actions) } From 9b920ed2e3ebc126cf8ba3e51acfc5be8b8cbf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Thu, 11 Jun 2026 20:44:22 +0800 Subject: [PATCH 15/28] refactor: unify rendering behavior with a new renderer (#4038) --- cspell.json | 2 +- yazi-actor/src/app/accept_payload.rs | 2 +- yazi-actor/src/app/dnd.rs | 2 +- yazi-actor/src/app/lua.rs | 2 +- yazi-actor/src/app/mouse.rs | 2 +- yazi-actor/src/app/plugin_do.rs | 2 +- yazi-actor/src/app/reflow.rs | 2 +- yazi-actor/src/core/preflight.rs | 2 +- yazi-actor/src/lib.rs | 2 + yazi-actor/src/lives/core.rs | 4 +- yazi-actor/src/lives/file.rs | 72 +++++++++++------------- yazi-actor/src/lives/lives.rs | 36 ++++++------ yazi-actor/src/lives/mod.rs | 6 +- yazi-actor/src/lives/mut_cell.rs | 11 ++++ yazi-binding/src/elements/elements.rs | 23 -------- yazi-binding/src/elements/mod.rs | 2 +- yazi-binding/src/elements/renderables.rs | 29 ++++++++++ yazi-fm/src/app/render.rs | 10 ++-- yazi-fm/src/main.rs | 4 +- yazi-fm/src/mgr/modal.rs | 19 +++---- yazi-fm/src/renderer.rs | 44 +++++++++++++++ yazi-fm/src/root.rs | 15 ++--- yazi-fm/src/tasks/list.rs | 18 ++---- yazi-fm/src/tasks/progress.rs | 20 +++---- yazi-fm/src/tasks/tasks.rs | 4 +- yazi-plugin/src/elements/elements.rs | 2 +- yazi-shim/src/cell/ro_cell.rs | 1 + 27 files changed, 189 insertions(+), 149 deletions(-) create mode 100644 yazi-actor/src/lives/mut_cell.rs create mode 100644 yazi-binding/src/elements/renderables.rs create mode 100644 yazi-fm/src/renderer.rs diff --git a/cspell.json b/cspell.json index 41af766c..99144de9 100644 --- a/cspell.json +++ b/cspell.json @@ -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","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist"]} \ No newline at end of file +{"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","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist","renderables","redrawer"],"language":"en","version":"0.2","flagWords":[]} \ No newline at end of file diff --git a/yazi-actor/src/app/accept_payload.rs b/yazi-actor/src/app/accept_payload.rs index e9de9bed..151e83b6 100644 --- a/yazi-actor/src/app/accept_payload.rs +++ b/yazi-actor/src/app/accept_payload.rs @@ -29,7 +29,7 @@ impl Actor for AcceptPayload { drop(lock); let kind = kind.to_owned(); - succ!(Lives::scope(cx.core, || { + succ!(Lives::scope(cx.core, |_| { let body = payload.body.into_lua(&LUA)?; for (id, cb) in handlers { if let Err(e) = runtime_scope!(LUA, &id, cb.call::<()>(body.clone())) { diff --git a/yazi-actor/src/app/dnd.rs b/yazi-actor/src/app/dnd.rs index 10f357a5..014aa552 100644 --- a/yazi-actor/src/app/dnd.rs +++ b/yazi-actor/src/app/dnd.rs @@ -23,7 +23,7 @@ impl Actor for Dnd { let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; let area = yazi_binding::elements::Rect::from(size); - let result = Lives::scope(cx.core, move || { + let result = Lives::scope(cx.core, move |_| { runtime_scope!(LUA, "root", { let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; diff --git a/yazi-actor/src/app/lua.rs b/yazi-actor/src/app/lua.rs index a2c793b1..73e4e181 100644 --- a/yazi-actor/src/app/lua.rs +++ b/yazi-actor/src/app/lua.rs @@ -16,7 +16,7 @@ impl Actor for Lua { fn act(cx: &mut Ctx, form: Self::Form) -> Result { let chunk = LUA.load(&*form.code).set_name("anonymous"); - let result = Lives::scope(cx.core, || { + let result = Lives::scope(cx.core, |_| { runtime_scope!(LUA, "inline", Sendable::value_to_data(&LUA, chunk.eval()?)) }); succ!(result?); diff --git a/yazi-actor/src/app/mouse.rs b/yazi-actor/src/app/mouse.rs index 3692cf00..8ecdbf10 100644 --- a/yazi-actor/src/app/mouse.rs +++ b/yazi-actor/src/app/mouse.rs @@ -24,7 +24,7 @@ impl Actor for Mouse { let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; let area = yazi_binding::elements::Rect::from(size); - let result = Lives::scope(cx.core, move || { + let result = Lives::scope(cx.core, move |_| { runtime_scope!(LUA, "root", { let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; diff --git a/yazi-actor/src/app/plugin_do.rs b/yazi-actor/src/app/plugin_do.rs index c62a390a..dc647bab 100644 --- a/yazi-actor/src/app/plugin_do.rs +++ b/yazi-actor/src/app/plugin_do.rs @@ -43,7 +43,7 @@ impl Actor for PluginDo { }; drop(loader); - let result = Lives::scope(cx.core, || { + let result = Lives::scope(cx.core, |_| { if let Some(cb) = opt.callback { cb(&LUA, plugin) } else { diff --git a/yazi-actor/src/app/reflow.rs b/yazi-actor/src/app/reflow.rs index cd4390a1..958a1a51 100644 --- a/yazi-actor/src/app/reflow.rs +++ b/yazi-actor/src/app/reflow.rs @@ -21,7 +21,7 @@ impl Actor for Reflow { let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; let mut layout = LAYOUT.get(); - let result = Lives::scope(cx.core, || { + let result = Lives::scope(cx.core, |_| { let comps = (form.reflow)((Position::ORIGIN, size).into())?; for v in comps.sequence_values::() { diff --git a/yazi-actor/src/core/preflight.rs b/yazi-actor/src/core/preflight.rs index 56f6e4d0..92e8d0c9 100644 --- a/yazi-actor/src/core/preflight.rs +++ b/yazi-actor/src/core/preflight.rs @@ -16,7 +16,7 @@ impl Preflight { return Ok(opt.1); }; - Ok(Lives::scope(cx.core, || { + Ok(Lives::scope(cx.core, |_| { let mut body = opt.1.into_lua(&LUA)?; for (id, cb) in handlers { match runtime_scope!(LUA, &id, cb.call::(&body)) { diff --git a/yazi-actor/src/lib.rs b/yazi-actor/src/lib.rs index 6706a76c..15eb9f27 100644 --- a/yazi-actor/src/lib.rs +++ b/yazi-actor/src/lib.rs @@ -3,3 +3,5 @@ extern crate self as yazi_actor; yazi_macro::mod_pub!(app cmp confirm core help input lives mgr notify pick spot tasks which); yazi_macro::mod_flat!(actor context); + +pub fn init() { lives::init(); } diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index a32022c9..f7f6acdc 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -1,10 +1,12 @@ use std::ops::Deref; -use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataMethods, UserDataRef, Value}; use paste::paste; use super::{Lives, PtrCell}; +pub(super) type CoreRef = UserDataRef; + pub(super) struct Core { inner: PtrCell, diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index 1aebfff2..bc802aa9 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -5,8 +5,8 @@ use yazi_binding::{Range, Style}; use yazi_config::THEME; use yazi_shared::{path::AsPath, url::UrlLike}; -use super::Lives; -use crate::lives::PtrCell; +use super::{FILE_CACHE, Lives}; +use crate::lives::{CoreRef, PtrCell}; pub(super) struct File { idx: usize, @@ -32,7 +32,7 @@ impl File { ) -> mlua::Result { use hashbrown::hash_map::Entry; - Ok(match super::FILE_CACHE.borrow_mut().entry(PtrCell(&folder.files[idx])) { + Ok(match unsafe { (*FILE_CACHE.get()).assume_init_mut() }.entry(PtrCell(&folder.files[idx])) { Entry::Occupied(oe) => oe.into_mut().clone(), Entry::Vacant(ve) => { let ud = Lives::scoped_userdata(Self { idx, folder: folder.into(), tab: tab.into() })?; @@ -71,9 +71,8 @@ impl UserData for File { Ok(if me.is_dir() { me.folder.files.sizes.get(&me.urn()).copied() } else { Some(me.len) }) }); methods.add_method("mime", |lua, me, ()| { - lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| { - core.mgr.mimetype.get(&me.url).map(|s| lua.create_string(s)).transpose() - })? + let core: CoreRef = lua.named_registry_value("cx")?; + core.mgr.mimetype.get(&me.url).map(|s| lua.create_string(s)).transpose() }); methods.add_method("prefix", |lua, me, ()| { if !me.url.has_trail() { @@ -85,20 +84,18 @@ impl UserData for File { Some(lua.create_string(comp.as_path().encoded_bytes())).transpose() }); methods.add_method("style", |lua, me, ()| { - lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| { - let mime = core.mgr.mimetype.get(&me.url).unwrap_or_default(); - THEME.filetype.match_style(me, mime).map(Style::from) - }) + let core: CoreRef = lua.named_registry_value("cx")?; + let mime = core.mgr.mimetype.get(&me.url).unwrap_or_default(); + Ok(THEME.filetype.match_style(me, mime).map(Style::from)) }); methods.add_method("is_yanked", |lua, me, ()| { - lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| { - if !core.mgr.yanked.contains(&me.url) { - 0u8 - } else if core.mgr.yanked.cut { - 2u8 - } else { - 1u8 - } + let core: CoreRef = lua.named_registry_value("cx")?; + Ok(if !core.mgr.yanked.contains(&me.url) { + 0u8 + } else if core.mgr.yanked.cut { + 2u8 + } else { + 1u8 }) }); methods.add_method("is_marked", |_, me, ()| { @@ -115,31 +112,30 @@ impl UserData for File { }); methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains(&me.url))); methods.add_method("found", |lua, me, ()| { - lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| { - let Some(finder) = &core.active().finder else { - return Ok(None); - }; + let core: CoreRef = lua.named_registry_value("cx")?; + let Some(finder) = &core.active().finder else { + return Ok(None); + }; - let Some(idx) = finder.matched_idx(&me.folder, me.urn()) else { - return Ok(None); - }; + let Some(idx) = finder.matched_idx(&me.folder, me.urn()) else { + return Ok(None); + }; - Some(lua.create_sequence_from([idx.into_lua(lua)?, finder.matched.len().into_lua(lua)?])) - .transpose() - }) + lua.create_sequence_from([idx.into_lua(lua)?, finder.matched.len().into_lua(lua)?]).map(Some) }); methods.add_method("highlights", |lua, me, ()| { - lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| { - let Some(finder) = &core.active().finder else { - return None; - }; - if me.folder.url != me.tab.current.url { - return None; - } + let core: CoreRef = lua.named_registry_value("cx")?; + let Some(finder) = &core.active().finder else { + return Ok(None); + }; + if me.folder.url != me.tab.current.url { + return Ok(None); + } + let Some(Some(h)) = me.url.name().map(|s| finder.filter.highlighted(s)) else { + return Ok(None); + }; - let h = finder.filter.highlighted(me.url.name()?)?; - Some(h.into_iter().map(Range::from).collect::>()) - }) + lua.create_sequence_from(h.into_iter().map(Range::from)).map(Some) }); } } diff --git a/yazi-actor/src/lives/lives.rs b/yazi-actor/src/lives/lives.rs index 1b969cd4..91db36c0 100644 --- a/yazi-actor/src/lives/lives.rs +++ b/yazi-actor/src/lives/lives.rs @@ -1,39 +1,37 @@ -use std::cell::RefCell; +use std::mem::MaybeUninit; use hashbrown::HashMap; use mlua::{AnyUserData, UserData}; use scopeguard::defer; use tracing::error; use yazi_plugin::LUA; -use yazi_shim::cell::RoCell; use super::{Core, PtrCell}; +use crate::lives::MutCell; -static TO_DESTROY: RoCell>> = RoCell::new_const(RefCell::new(Vec::new())); -pub(super) static FILE_CACHE: RoCell, AnyUserData>>> = - RoCell::new(); +pub(super) static TO_DESTROY: MutCell> = MutCell::new(Vec::new()); +pub(super) static FILE_CACHE: MutCell, AnyUserData>>> = + MutCell::new(MaybeUninit::uninit()); pub struct Lives; impl Lives { - pub fn scope(core: &yazi_core::Core, f: F) -> mlua::Result + pub fn scope(core: &mut yazi_core::Core, f: F) -> mlua::Result where - F: FnOnce() -> mlua::Result, + F: FnOnce(&mut yazi_core::Core) -> mlua::Result, { - FILE_CACHE.init(Default::default()); - defer! { FILE_CACHE.drop(); } - - let result = LUA.scope(|scope| { - scope.add_destructor(|| { - for ud in TO_DESTROY.borrow_mut().drain(..) { + defer! { + unsafe { + (*FILE_CACHE.get()).assume_init_mut().clear(); + for ud in (*TO_DESTROY.get()).drain(..) { ud.destroy().expect("failed to destruct scoped userdata"); } - }); + } + } - LUA.set_named_registry_value("cx", scope.create_any_userdata_ref(core)?)?; - LUA.globals().raw_set("cx", Core::make(core)?)?; - f() - }); + LUA.set_named_registry_value("cx", Core::make(core)?)?; + LUA.globals().raw_set("cx", Core::make(core)?)?; + let result = f(core); if let Err(ref e) = result { error!("{e}"); @@ -46,7 +44,7 @@ impl Lives { T: UserData + 'static, { let ud = LUA.create_userdata(data)?; - TO_DESTROY.borrow_mut().push(ud.clone()); + unsafe { &mut *TO_DESTROY.get() }.push(ud.clone()); Ok(ud) } } diff --git a/yazi-actor/src/lives/mod.rs b/yazi-actor/src/lives/mod.rs index 594dc7fd..d804327b 100644 --- a/yazi-actor/src/lives/mod.rs +++ b/yazi-actor/src/lives/mod.rs @@ -1 +1,5 @@ -yazi_macro::mod_flat!(behavior core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks which yanked); +yazi_macro::mod_flat!(behavior core file files filter finder folder lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked); + +pub(super) fn init() { + unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) }; +} diff --git a/yazi-actor/src/lives/mut_cell.rs b/yazi-actor/src/lives/mut_cell.rs new file mode 100644 index 00000000..25b27c8d --- /dev/null +++ b/yazi-actor/src/lives/mut_cell.rs @@ -0,0 +1,11 @@ +use std::cell::UnsafeCell; + +pub(super) struct MutCell(UnsafeCell); + +unsafe impl Sync for MutCell {} + +impl MutCell { + pub(super) const fn new(value: T) -> Self { Self(UnsafeCell::new(value)) } + + pub(super) fn get(&self) -> *mut T { self.0.get() } +} diff --git a/yazi-binding/src/elements/elements.rs b/yazi-binding/src/elements/elements.rs index f6b26c96..7d73a134 100644 --- a/yazi-binding/src/elements/elements.rs +++ b/yazi-binding/src/elements/elements.rs @@ -1,7 +1,5 @@ use mlua::{IntoLua, Lua, Value}; -use tracing::error; -use super::Renderable; use crate::{Composer, ComposerGet, ComposerSet}; pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer { @@ -37,24 +35,3 @@ pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer(value: Value, buf: &mut ratatui::buffer::Buffer, trans: F) -where - F: FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect + Copy, -{ - match value { - Value::Table(tbl) => { - for widget in tbl.sequence_values::() { - match widget { - Ok(w) => w.render_with(buf, trans), - Err(e) => error!("Failed to convert to renderable elements: {e}"), - } - } - } - Value::UserData(ud) => match Renderable::try_from(&ud) { - Ok(w) => w.render_with(buf, trans), - Err(e) => error!("Failed to convert to renderable element: {e}"), - }, - _ => error!("Expected a renderable element, or a table of them, got: {value:?}"), - } -} diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index 49c6da4e..b9c31b3d 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge layout line list pad pos rect renderable row span table text wrap); +yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge layout line list pad pos rect renderable renderables row span table text wrap); diff --git a/yazi-binding/src/elements/renderables.rs b/yazi-binding/src/elements/renderables.rs new file mode 100644 index 00000000..20f7d98b --- /dev/null +++ b/yazi-binding/src/elements/renderables.rs @@ -0,0 +1,29 @@ +use mlua::Value; +use tracing::error; + +use crate::elements::Renderable; + +pub struct Renderables; + +impl Renderables { + pub fn reduce(value: Value, mut reducer: F) + where + F: FnMut(Renderable), + { + match value { + Value::Table(tbl) => { + for element in tbl.sequence_values::() { + match element { + Ok(r) => reducer(r), + Err(e) => error!("Failed to convert to renderable elements: {e}"), + } + } + } + Value::UserData(ud) => match Renderable::try_from(&ud) { + Ok(w) => reducer(w), + Err(e) => error!("Failed to convert to renderable element: {e}"), + }, + _ => error!("Expected a renderable element, or a table of them, got: {value:?}"), + } + } +} diff --git a/yazi-fm/src/app/render.rs b/yazi-fm/src/app/render.rs index 175abe46..a137d3ad 100644 --- a/yazi-fm/src/app/render.rs +++ b/yazi-fm/src/app/render.rs @@ -30,8 +30,8 @@ impl App { let collision = COLLISION.swap(false, Ordering::Relaxed); let preview_rect = LAYOUT.get().preview; term.draw(|f| { - _ = Lives::scope(&self.core, || { - runtime_scope!(LUA, "root", Ok(f.render_widget(Root::new(&self.core), f.area()))) + _ = Lives::scope(&mut self.core, |core| { + runtime_scope!(LUA, "root", Ok(f.render_widget(Root::new(core), f.area()))) }); })?; @@ -58,10 +58,10 @@ impl App { let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c)); term.draw_partial(|f| { - _ = Lives::scope(&self.core, || { + _ = Lives::scope(&mut self.core, |core| { runtime_scope!(LUA, "root", { - f.render_widget(crate::tasks::Progress::new(&self.core), f.area()); - f.render_widget(crate::notify::Notify::new(&self.core), f.area()); + f.render_widget(crate::tasks::Progress::new(core), f.area()); + f.render_widget(crate::notify::Notify::new(core), f.area()); Ok(()) }) }); diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index e9babc5c..f6710142 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -4,7 +4,7 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; 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); +yazi_macro::mod_flat!(dispatcher executor logs panic renderer root router signals); #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -40,5 +40,7 @@ async fn main() -> anyhow::Result<()> { yazi_dds::serve(); + yazi_actor::init(); + yazi_shared::LOCAL_SET.run_until(app::App::serve()).await } diff --git a/yazi-fm/src/mgr/modal.rs b/yazi-fm/src/mgr/modal.rs index dd578862..acf77a2a 100644 --- a/yazi-fm/src/mgr/modal.rs +++ b/yazi-fm/src/mgr/modal.rs @@ -1,29 +1,24 @@ -use mlua::{ObjectLike, Table}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use tracing::error; -use yazi_binding::elements::render_once; use yazi_core::Core; -use yazi_plugin::LUA; + +use crate::Renderer; pub(crate) struct Modal<'a> { - core: &'a Core, + core: &'a mut Core, } impl<'a> Modal<'a> { #[inline] - pub(crate) fn new(core: &'a Core) -> Self { Self { core } } + pub(crate) fn new(core: &'a mut Core) -> Self { Self { core } } } impl Widget for Modal<'_> { fn render(self, area: Rect, buf: &mut Buffer) { - let mut f = || { - let area = yazi_binding::elements::Rect::from(area); - let root = LUA.globals().raw_get::
("Modal")?.call_method::
("new", area)?; + let result = + Renderer::new(self.core, "Modal").with_redrawer("children_redraw").render(area, buf); - render_once(root.call_method("children_redraw", ())?, buf, |p| self.core.mgr.area(p)); - Ok::<_, mlua::Error>(()) - }; - if let Err(e) = f() { + if let Err(e) = result { error!("Failed to redraw the `Modal` component:\n{e}"); } } diff --git a/yazi-fm/src/renderer.rs b/yazi-fm/src/renderer.rs new file mode 100644 index 00000000..7c53af05 --- /dev/null +++ b/yazi-fm/src/renderer.rs @@ -0,0 +1,44 @@ +use mlua::{ObjectLike, Table}; +use ratatui::{buffer::Buffer, layout::Rect}; +use yazi_binding::elements::Renderables; +use yazi_core::Core; +use yazi_plugin::LUA; + +pub(super) struct Renderer<'a> { + core: &'a mut Core, + component: &'a str, + constructor: &'a str, + redrawer: &'a str, +} + +impl<'a> Renderer<'a> { + pub(super) fn new(core: &'a mut Core, component: &'a str) -> Self { + Self { core, component, constructor: "new", redrawer: "redraw" } + } + + pub(super) fn with_constructor(mut self, constructor: &'a str) -> Self { + self.constructor = constructor; + self + } + + pub(super) fn with_redrawer(mut self, redrawer: &'a str) -> Self { + self.redrawer = redrawer; + self + } + + pub(super) fn render(self, area: Rect, buf: &mut Buffer) -> mlua::Result<()> { + let area = yazi_binding::elements::Rect::from(area); + + let value = LUA + .globals() + .raw_get::
(self.component)? + .call_method::
(self.constructor, area)? + .call_method("redraw", ())?; + + Renderables::reduce(value, |element| { + element.render_with(buf, |p| self.core.mgr.area(p)); + }); + + Ok::<_, mlua::Error>(()) + } +} diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 77f60683..67d4daf6 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -1,18 +1,18 @@ use mlua::{ObjectLike, Table}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use tracing::error; -use yazi_binding::elements::render_once; use yazi_core::Core; use yazi_plugin::LUA; use super::{cmp, confirm, help, input, mgr, pick, spot, tasks, which}; +use crate::Renderer; pub(super) struct Root<'a> { - core: &'a Core, + core: &'a mut Core, } impl<'a> Root<'a> { - pub(super) fn new(core: &'a Core) -> Self { Self { core } } + pub(super) fn new(core: &'a mut Core) -> Self { Self { core } } pub(super) fn reflow(area: Rect) -> mlua::Result
{ let area = yazi_binding::elements::Rect::from(area); @@ -23,14 +23,7 @@ impl<'a> Root<'a> { impl Widget for Root<'_> { fn render(self, area: Rect, buf: &mut Buffer) { - let mut f = || { - let area = yazi_binding::elements::Rect::from(area); - let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; - - render_once(root.call_method("redraw", ())?, buf, |p| self.core.mgr.area(p)); - Ok::<_, mlua::Error>(()) - }; - if let Err(e) = f() { + if let Err(e) = Renderer::new(self.core, "Root").render(area, buf) { error!("Failed to redraw the `Root` component:\n{e}"); } diff --git a/yazi-fm/src/tasks/list.rs b/yazi-fm/src/tasks/list.rs index b09c46be..a75f35f1 100644 --- a/yazi-fm/src/tasks/list.rs +++ b/yazi-fm/src/tasks/list.rs @@ -1,29 +1,21 @@ -use mlua::{ObjectLike, Table}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use tracing::error; -use yazi_binding::elements::render_once; use yazi_core::Core; -use yazi_plugin::LUA; + +use crate::Renderer; pub(crate) struct List<'a> { - core: &'a Core, + core: &'a mut Core, } impl<'a> List<'a> { #[inline] - pub(crate) fn new(core: &'a Core) -> Self { Self { core } } + pub(crate) fn new(core: &'a mut Core) -> Self { Self { core } } } impl Widget for List<'_> { fn render(self, area: Rect, buf: &mut Buffer) { - let mut f = || { - let area = yazi_binding::elements::Rect::from(area); - let root = LUA.globals().raw_get::
("Tasks")?.call_method::
("new", area)?; - - render_once(root.call_method("redraw", ())?, buf, |p| self.core.mgr.area(p)); - Ok::<_, mlua::Error>(()) - }; - if let Err(e) = f() { + if let Err(e) = Renderer::new(self.core, "Tasks").render(area, buf) { error!("Failed to redraw the `Tasks` component:\n{e}"); } } diff --git a/yazi-fm/src/tasks/progress.rs b/yazi-fm/src/tasks/progress.rs index 1040c704..cb500523 100644 --- a/yazi-fm/src/tasks/progress.rs +++ b/yazi-fm/src/tasks/progress.rs @@ -1,30 +1,24 @@ -use mlua::{ObjectLike, Table}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use tracing::error; -use yazi_binding::elements::render_once; use yazi_config::LAYOUT; use yazi_core::Core; -use yazi_plugin::LUA; + +use crate::Renderer; pub(crate) struct Progress<'a> { - core: &'a Core, + core: &'a mut Core, } impl<'a> Progress<'a> { - pub(crate) fn new(core: &'a Core) -> Self { Self { core } } + pub(crate) fn new(core: &'a mut Core) -> Self { Self { core } } } impl Widget for Progress<'_> { fn render(self, _: Rect, buf: &mut Buffer) { - let mut f = || { - let area = yazi_binding::elements::Rect::from(LAYOUT.get().progress); - let progress = - LUA.globals().raw_get::
("Progress")?.call_method::
("use", area)?; + let area = LAYOUT.get().progress; + let result = Renderer::new(self.core, "Progress").with_constructor("use").render(area, buf); - render_once(progress.call_method("redraw", ())?, buf, |p| self.core.mgr.area(p)); - Ok::<_, mlua::Error>(()) - }; - if let Err(e) = f() { + if let Err(e) = result { error!("Failed to redraw the `Progress` component:\n{e}"); } } diff --git a/yazi-fm/src/tasks/tasks.rs b/yazi-fm/src/tasks/tasks.rs index c9e98835..f7fd5193 100644 --- a/yazi-fm/src/tasks/tasks.rs +++ b/yazi-fm/src/tasks/tasks.rs @@ -5,11 +5,11 @@ use yazi_core::{Core, tasks::TASKS_PERCENT}; use crate::tasks::List; pub(crate) struct Tasks<'a> { - core: &'a Core, + core: &'a mut Core, } impl<'a> Tasks<'a> { - pub(crate) fn new(core: &'a Core) -> Self { Self { core } } + pub(crate) fn new(core: &'a mut Core) -> Self { Self { core } } pub(super) fn area(area: Rect) -> Rect { let chunk = layout::Layout::vertical([ diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 84e06003..a11babc4 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -123,7 +123,7 @@ pub(super) fn redraw(lua: &Lua) -> mlua::Result { Value::UserData(ud) => lua.create_sequence_from([ud]), _ => { error!( - "Failed to `redraw()` the `{}` component: expected a table or UserData", + "Failed to `redraw()` the `{}` component: expected an element or a table of them", id.display(), ); lua.create_table() diff --git a/yazi-shim/src/cell/ro_cell.rs b/yazi-shim/src/cell/ro_cell.rs index b5d48f99..fcfa8b87 100644 --- a/yazi-shim/src/cell/ro_cell.rs +++ b/yazi-shim/src/cell/ro_cell.rs @@ -55,6 +55,7 @@ impl RoCell { self.inner.get().replace(MaybeUninit::uninit()).assume_init() } } + } impl Default for RoCell { From b37be0529aca0c49768f0ed731b83af108acd6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 13 Jun 2026 19:37:29 +0800 Subject: [PATCH 16/28] feat: new `ui.Input` element (#4040) --- CHANGELOG.md | 2 + Cargo.lock | 132 +++++++++--------------- Cargo.toml | 2 +- yazi-actor/src/cmp/trigger.rs | 2 +- yazi-actor/src/input/close.rs | 26 +++-- yazi-actor/src/input/complete.rs | 9 +- yazi-actor/src/input/escape.rs | 12 ++- yazi-actor/src/input/show.rs | 15 +-- yazi-binding/Cargo.toml | 1 + yazi-binding/src/elements/area.rs | 40 +++---- yazi-binding/src/elements/bar.rs | 15 ++- yazi-binding/src/elements/border.rs | 14 ++- yazi-binding/src/elements/clear.rs | 17 ++- yazi-binding/src/elements/color.rs | 10 +- yazi-binding/src/elements/elements.rs | 1 + yazi-binding/src/elements/fill.rs | 17 ++- yazi-binding/src/elements/gauge.rs | 16 ++- yazi-binding/src/elements/input.rs | 117 +++++++++++++++++++++ yazi-binding/src/elements/line.rs | 45 ++++---- yazi-binding/src/elements/list.rs | 17 ++- yazi-binding/src/elements/mod.rs | 2 +- yazi-binding/src/elements/pos.rs | 34 +++--- yazi-binding/src/elements/renderable.rs | 91 ++++++++-------- yazi-binding/src/elements/row.rs | 22 ++-- yazi-binding/src/elements/span.rs | 22 ++-- yazi-binding/src/elements/spatial.rs | 7 ++ yazi-binding/src/elements/table.rs | 14 ++- yazi-binding/src/elements/text.rs | 57 +++++++--- yazi-binding/src/macros.rs | 7 +- yazi-config/src/popup/input.rs | 4 - yazi-config/src/popup/position.rs | 40 ++++++- yazi-core/src/core.rs | 20 ++-- yazi-core/src/input/guard.rs | 46 +++++++++ yazi-core/src/input/input.rs | 40 +++++-- yazi-core/src/input/mod.rs | 2 +- yazi-core/src/pick/pick.rs | 4 +- yazi-core/src/proxy.rs | 2 +- yazi-core/src/tab/folder.rs | 3 +- yazi-fm/src/app/mod.rs | 2 +- yazi-fm/src/app/render.rs | 42 ++------ yazi-fm/src/app/sync_guard.rs | 52 ++++++++++ yazi-fm/src/cmp/cmp.rs | 2 +- yazi-fm/src/dispatcher.rs | 11 +- yazi-fm/src/executor.rs | 31 +++--- yazi-fm/src/input/input.rs | 10 +- yazi-fm/src/mgr/preview.rs | 1 + yazi-fm/src/renderer.rs | 12 ++- yazi-fm/src/root.rs | 2 +- yazi-fm/src/router.rs | 11 +- yazi-fm/src/spot/spot.rs | 1 + yazi-macro/src/input.rs | 2 +- yazi-parser/src/input/close.rs | 10 +- yazi-parser/src/spark/kind.rs | 4 + yazi-parser/src/spark/spark.rs | 4 + yazi-plugin/preset/components/root.lua | 6 +- yazi-plugin/src/utils/preview.rs | 6 +- yazi-plugin/src/utils/spot.rs | 10 +- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/id.rs | 1 + yazi-shim/src/cell/ro_cell.rs | 1 - yazi-version/Cargo.toml | 2 +- yazi-version/build.rs | 13 +-- yazi-widgets/src/input/actor/casefy.rs | 2 +- yazi-widgets/src/input/actor/escape.rs | 2 +- yazi-widgets/src/input/actor/move.rs | 2 +- yazi-widgets/src/input/actor/replace.rs | 2 +- yazi-widgets/src/input/input.rs | 25 +++-- yazi-widgets/src/input/option.rs | 10 +- yazi-widgets/src/input/snap.rs | 1 - yazi-widgets/src/input/snaps.rs | 2 +- yazi-widgets/src/input/widget.rs | 2 - 71 files changed, 802 insertions(+), 411 deletions(-) create mode 100644 yazi-binding/src/elements/input.rs create mode 100644 yazi-binding/src/elements/spatial.rs create mode 100644 yazi-core/src/input/guard.rs create mode 100644 yazi-fm/src/app/sync_guard.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ce4e5940..dc2923f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Drag and drop ([#4005]) - Bulk create ([#3793]) - Dynamic keymap Lua API ([#4031]) +- New `ui.Input` element ([#4040]) - Image preview with Überzug++ on Niri ([#3990]) - New gait for input `backward` and `forward` actions ([#4012]) @@ -1744,3 +1745,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#4012]: https://github.com/sxyazi/yazi/pull/4012 [#4022]: https://github.com/sxyazi/yazi/pull/4022 [#4031]: https://github.com/sxyazi/yazi/pull/4031 +[#4040]: https://github.com/sxyazi/yazi/pull/4040 diff --git a/Cargo.lock b/Cargo.lock index 0f36b1b3..0b8d5ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,6 +462,31 @@ dependencies = [ "cipher", ] +[[package]] +name = "bon" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f04f6fef12d70d42a77b1433c9e0f065238479a6cefc4f5bab105e9873a3c3" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d0bd4c2f75335ad98052a37efb54f428b492f64340257143b3429c8a508fa7b" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1000,38 +1025,14 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - [[package]] name = "darling" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", + "darling_core", + "darling_macro", ] [[package]] @@ -1047,24 +1048,13 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.117", -] - [[package]] name = "darling_macro" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core 0.23.0", + "darling_core", "quote", "syn 2.0.117", ] @@ -1133,37 +1123,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.117", -] - [[package]] name = "derive_more" version = "2.1.1" @@ -2007,7 +1966,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" dependencies = [ - "darling 0.23.0", + "darling", "indoc", "proc-macro2", "quote", @@ -2310,9 +2269,9 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmem" @@ -3532,9 +3491,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -3555,9 +3514,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rfc6979" @@ -3940,7 +3899,7 @@ version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ - "darling 0.23.0", + "darling", "proc-macro2", "quote", "syn 2.0.117", @@ -4847,12 +4806,12 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vergen" -version = "9.1.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" +checksum = "7bdf18a54cf91b4d98a8e8b67f6321606539fbcdcac02536286ad1de37b53fd2" dependencies = [ "anyhow", - "derive_builder", + "bon", "rustc_version", "rustversion", "time", @@ -4861,12 +4820,12 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "9.1.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" +checksum = "4961429ed12888cb3c6dd20f7dc9508c821091a3ba5fec0156ed5a654c1c4572" dependencies = [ "anyhow", - "derive_builder", + "bon", "rustversion", "time", "vergen", @@ -4875,12 +4834,12 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "9.1.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +checksum = "910e8471e27130bbc019e9bfa6bda16dfc4c6dd7c5d0793da70a9256caeae984" dependencies = [ "anyhow", - "derive_builder", + "bon", "rustversion", ] @@ -5672,6 +5631,7 @@ dependencies = [ "libc", "mlua", "ordered-float 5.3.0", + "parking_lot", "paste", "ratatui", "serde", diff --git a/Cargo.toml b/Cargo.toml index 8e5b77ef..451b5a5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ paste = "1.0.15" percent-encoding = "2.3.2" rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } ratatui = { version = "0.30.1", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } -regex = "1.12.3" +regex = "1.12.4" russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs index ce9a3468..7c40a551 100644 --- a/yazi-actor/src/cmp/trigger.rs +++ b/yazi-actor/src/cmp/trigger.rs @@ -22,7 +22,7 @@ impl Actor for Trigger { if form.ticket.is_some_and(|t| t != cx.cmp.ticket) { succ!(); } else if form.ticket.is_none() { - cx.cmp.ticket = cx.input.ticket.current(); + cx.cmp.ticket = cx.input.lock().map(|g| g.ticket.current()).unwrap_or_default(); } cx.cmp.handle.take().map(|h| h.abort()); diff --git a/yazi-actor/src/input/close.rs b/yazi-actor/src/input/close.rs index 0c0a8eb5..9f67ad23 100644 --- a/yazi-actor/src/input/close.rs +++ b/yazi-actor/src/input/close.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_macro::{act, render, succ}; -use yazi_parser::input::CloseForm; -use yazi_shared::data::Data; +use yazi_parser::{input::CloseForm, spark::SparkKind}; +use yazi_shared::{Source, data::Data}; use yazi_widgets::input::InputEvent; use crate::{Actor, Ctx}; @@ -14,16 +14,28 @@ impl Actor for Close { const NAME: &str = "close"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let input = &mut cx.input; - input.visible = false; - input.ticket.next(); + let Some(mut guard) = cx.input.lock_mut() else { + succ!(); + }; - if let Some(tx) = input.tx.take() { - let value = input.snap().value.clone(); + guard.ticket.next(); + if let Some(tx) = guard.tx.take() { + let value = guard.snap().value.clone(); _ = tx.send(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) }); } + drop(guard); + cx.input.main_visible = false; + act!(cmp:close, cx)?; succ!(render!()); } + + fn hook(cx: &Ctx, _form: &Self::Form) -> Option { + match cx.source() { + Source::Key => Some(SparkKind::KeyInputClose), + Source::Ind => Some(SparkKind::IndInputClose), + _ => None, + } + } } diff --git a/yazi-actor/src/input/complete.rs b/yazi-actor/src/input/complete.rs index 5380618d..394c92c8 100644 --- a/yazi-actor/src/input/complete.rs +++ b/yazi-actor/src/input/complete.rs @@ -13,11 +13,14 @@ impl Actor for Complete { const NAME: &str = "complete"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let input = &mut cx.input; - if !input.visible || input.ticket.current() != form.ticket { + let Some(mut guard) = cx.input.lock_mut() else { + succ!(); + }; + + if guard.ticket.current() != form.ticket { succ!(); } - act!(complete, input, form) + act!(complete, guard, form) } } diff --git a/yazi-actor/src/input/escape.rs b/yazi-actor/src/input/escape.rs index 495e0528..6977792c 100644 --- a/yazi-actor/src/input/escape.rs +++ b/yazi-actor/src/input/escape.rs @@ -15,16 +15,20 @@ impl Actor for Escape { fn act(cx: &mut Ctx, _: Self::Form) -> Result { use yazi_widgets::input::InputMode as M; - let input = &mut cx.input; + let Some(mut guard) = cx.input.lock_mut() else { + succ!(); + }; - let mode = input.snap().mode; + let (mode, op) = (guard.snap().mode, guard.snap().op); + act!(escape, guard)?; + + drop(guard); match mode { - M::Normal if input.snap_mut().op == InputOp::None => act!(input:close, cx), + M::Normal if op == InputOp::None => act!(input:close, cx), M::Insert => act!(cmp:close, cx), M::Normal | M::Replace => Ok(().into()), }?; - act!(escape, cx.input)?; succ!(render!()); } } diff --git a/yazi-actor/src/input/show.rs b/yazi-actor/src/input/show.rs index 9b3ee137..a9cb9a9c 100644 --- a/yazi-actor/src/input/show.rs +++ b/yazi-actor/src/input/show.rs @@ -1,6 +1,5 @@ -use std::ops::DerefMut; - use anyhow::Result; +use ratatui::widgets::Padding; use yazi_macro::{act, render, succ}; use yazi_shared::data::Data; use yazi_widgets::input::InputOpt; @@ -14,14 +13,16 @@ impl Actor for Show { const NAME: &str = "show"; - fn act(cx: &mut Ctx, form: Self::Form) -> Result { + fn act(cx: &mut Ctx, mut form: Self::Form) -> Result { act!(input:close, cx)?; let input = &mut cx.input; - input.visible = true; - input.title = form.cfg.title.clone(); - input.position = form.cfg.position; - *input.deref_mut() = yazi_widgets::input::Input::new(form)?; + input.main_visible = true; + input.main_title = form.cfg.title.clone(); + input.main_position = form.cfg.position; + + form.cfg.position = form.cfg.position.padding(Padding::uniform(1)); + input.main = yazi_widgets::input::Input::new(form)?; succ!(render!()); } diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index 52506079..f5fdd02e 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -37,6 +37,7 @@ hashbrown = { workspace = true } inventory = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } +parking_lot = { workspace = true } paste = { workspace = true } ratatui = { workspace = true } serde = { workspace = true } diff --git a/yazi-binding/src/elements/area.rs b/yazi-binding/src/elements/area.rs index f2cf04cd..5abb3991 100644 --- a/yazi-binding/src/elements/area.rs +++ b/yazi-binding/src/elements/area.rs @@ -8,31 +8,38 @@ const EXPECTED: &str = "expected a Pos or Rect"; #[derive(Clone, Copy)] pub enum Area { - Pos(Pos), Rect(Rect), + Pos(Pos), } impl Default for Area { fn default() -> Self { Self::Rect(Default::default()) } } +impl Debug for Area { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Rect(rect) => write!(f, "{:?}", **rect), + Self::Pos(pos) => write!(f, "{:?}", **pos), + } + } +} + impl Area { pub fn size(self) -> ratatui::layout::Size { match self { - Self::Pos(pos) => { - ratatui::layout::Size { width: pos.offset.width, height: pos.offset.height } - } Self::Rect(rect) => (*rect).into(), + Self::Pos(pos) => ratatui::layout::Size { width: pos.width, height: pos.height }, } } pub fn inner(self, padding: ratatui::widgets::Padding) -> Self { match self { + Self::Rect(rect) => Self::Rect(rect.pad(padding.into())), Self::Pos(mut pos) => { pos.pad += padding; Self::Pos(pos) } - Self::Rect(rect) => Self::Rect(rect.pad(padding.into())), } } @@ -41,8 +48,8 @@ impl Area { f: impl FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect, ) -> ratatui::layout::Rect { match self { - Self::Pos(pos) => *Rect(f(*pos)).pad(pos.pad), Self::Rect(rect) => *rect, + Self::Pos(pos) => *Rect(f(*pos)).pad(pos.pad), } } } @@ -55,10 +62,14 @@ impl From for Area { fn from(rect: ratatui::layout::Rect) -> Self { Self::Rect(rect.into()) } } -impl TryFrom for Area { +impl From for Area { + fn from(pos: yazi_config::popup::Position) -> Self { Self::Pos(pos.into()) } +} + +impl TryFrom<&AnyUserData> for Area { type Error = mlua::Error; - fn try_from(value: AnyUserData) -> Result { + fn try_from(value: &AnyUserData) -> Result { Ok(if let Ok(rect) = value.borrow::() { Self::Rect(*rect) } else if let Ok(pos) = value.borrow::() { @@ -72,7 +83,7 @@ impl TryFrom for Area { impl FromLua for Area { fn from_lua(value: Value, _: &Lua) -> mlua::Result { match value { - Value::UserData(ud) => Self::try_from(ud), + Value::UserData(ud) => Self::try_from(&ud), _ => Err(EXPECTED.into_lua_err()), } } @@ -81,17 +92,8 @@ impl FromLua for Area { impl IntoLua for Area { fn into_lua(self, lua: &Lua) -> mlua::Result { match self { - Self::Pos(pos) => pos.into_lua(lua), Self::Rect(rect) => rect.into_lua(lua), - } - } -} - -impl Debug for Area { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Pos(pos) => write!(f, "{:?}", **pos), - Self::Rect(rect) => write!(f, "{:?}", **rect), + Self::Pos(pos) => pos.into_lua(lua), } } } diff --git a/yazi-binding/src/elements/bar.rs b/yazi-binding/src/elements/bar.rs index eb94303b..42d4a549 100644 --- a/yazi-binding/src/elements/bar.rs +++ b/yazi-binding/src/elements/bar.rs @@ -2,10 +2,11 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMetho use ratatui::widgets::{Borders, Widget}; use super::{Area, Edge}; +use crate::elements::Spatial; #[derive(Clone, Debug, Default)] pub struct Bar { - pub(super) area: Area, + area: Area, edge: Edge, symbol: String, @@ -23,6 +24,18 @@ impl Bar { } } +impl TryFrom<&AnyUserData> for Bar { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { value.take() } +} + +impl Spatial for Bar { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Bar { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/border.rs b/yazi-binding/src/elements/border.rs index a56ac58d..bb1e2497 100644 --- a/yazi-binding/src/elements/border.rs +++ b/yazi-binding/src/elements/border.rs @@ -2,7 +2,7 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMetho use ratatui::widgets::{Borders, Widget}; use super::{Area, Edge}; -use crate::elements::Line; +use crate::elements::{Line, Spatial}; // Type const PLAIN: u8 = 0; @@ -44,6 +44,18 @@ impl Border { } } +impl TryFrom<&AnyUserData> for Border { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { value.take() } +} + +impl Spatial for Border { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Border { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/clear.rs b/yazi-binding/src/elements/clear.rs index 5bcf0c2b..dac63093 100644 --- a/yazi-binding/src/elements/clear.rs +++ b/yazi-binding/src/elements/clear.rs @@ -1,11 +1,12 @@ -use mlua::{IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; use super::Area; +use crate::elements::Spatial; #[derive(Clone, Copy, Debug, Default)] pub struct Clear { - pub area: Area, + area: Area, } impl Clear { @@ -19,6 +20,18 @@ impl Clear { } } +impl TryFrom<&AnyUserData> for Clear { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { Ok(*value.borrow()?) } +} + +impl Spatial for Clear { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Clear { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/color.rs b/yazi-binding/src/elements/color.rs index 503f6d5d..cbd0eedd 100644 --- a/yazi-binding/src/elements/color.rs +++ b/yazi-binding/src/elements/color.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use mlua::{ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, Value}; #[derive(Clone, Copy, Default)] pub struct Color(pub ratatui::style::Color); @@ -16,12 +16,18 @@ impl Color { } } +impl TryFrom<&AnyUserData> for Color { + type Error = mlua::Error; + + fn try_from(ud: &AnyUserData) -> Result { Ok(*ud.borrow()?) } +} + impl FromLua for Color { fn from_lua(value: Value, _: &Lua) -> mlua::Result { Ok(Self(match value { Value::Nil => ratatui::style::Color::Reset, Value::String(s) => ratatui::style::Color::from_str(&s.to_str()?).into_lua_err()?, - Value::UserData(ud) => ud.borrow::()?.0, + Value::UserData(ud) => return Self::try_from(&ud), _ => Err("expected a Color".into_lua_err())?, })) } diff --git a/yazi-binding/src/elements/elements.rs b/yazi-binding/src/elements/elements.rs index 7d73a134..6ef49bb4 100644 --- a/yazi-binding/src/elements/elements.rs +++ b/yazi-binding/src/elements/elements.rs @@ -14,6 +14,7 @@ pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer super::Edge::compose(lua)?, b"Fill" => super::Fill::compose(lua)?, b"Gauge" => super::Gauge::compose(lua)?, + b"Input" => super::Input::compose(lua)?, b"Layout" => super::Layout::compose(lua)?, b"Line" => super::Line::compose(lua)?, b"List" => super::List::compose(lua)?, diff --git a/yazi-binding/src/elements/fill.rs b/yazi-binding/src/elements/fill.rs index cdc23eb7..6ca356b9 100644 --- a/yazi-binding/src/elements/fill.rs +++ b/yazi-binding/src/elements/fill.rs @@ -1,11 +1,12 @@ -use mlua::{IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; use super::Area; +use crate::elements::Spatial; #[derive(Clone, Copy, Debug, Default)] pub struct Fill { - pub area: Area, + area: Area, style: ratatui::style::Style, } @@ -22,6 +23,18 @@ impl Fill { } } +impl TryFrom<&AnyUserData> for Fill { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { Ok(*value.borrow()?) } +} + +impl Spatial for Fill { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Fill { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/gauge.rs b/yazi-binding/src/elements/gauge.rs index d9eb611c..4d9dd3d6 100644 --- a/yazi-binding/src/elements/gauge.rs +++ b/yazi-binding/src/elements/gauge.rs @@ -2,11 +2,11 @@ use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData use ratatui::widgets::Widget; use super::{Area, Span}; -use crate::Style; +use crate::{Style, elements::Spatial}; #[derive(Clone, Debug, Default)] pub struct Gauge { - pub(super) area: Area, + area: Area, ratio: f64, label: Option>, @@ -25,6 +25,18 @@ impl Gauge { } } +impl TryFrom<&AnyUserData> for Gauge { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { value.take() } +} + +impl Spatial for Gauge { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Gauge { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/input.rs b/yazi-binding/src/elements/input.rs new file mode 100644 index 00000000..f6ea1399 --- /dev/null +++ b/yazi-binding/src/elements/input.rs @@ -0,0 +1,117 @@ +use std::sync::Arc; + +use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use parking_lot::Mutex; +use ratatui::widgets::Widget; + +use super::Area; +use crate::{elements::Spatial, impl_area_method}; + +const EXPECTED: &str = "expected a table or a Input"; + +#[derive(Clone, Debug, Default)] +pub struct Input { + pub focus: bool, + + inner: Arc>, +} + +impl Input { + pub fn compose(lua: &Lua) -> mlua::Result { + let new = lua.create_function(|_, (_, input): (Table, Self)| Ok(input))?; + + let input = lua.create_table()?; + input.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; + input.into_lua(lua) + } +} + +impl From<&Input> for Arc> { + fn from(input: &Input) -> Self { input.inner.clone() } +} + +impl TryFrom<&AnyUserData> for Input { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { + Ok(value.borrow::()?.clone()) + } +} + +impl TryFrom
for Input { + type Error = mlua::Error; + + fn try_from(tb: Table) -> Result { + let input = yazi_widgets::input::Input::new(yazi_widgets::input::InputOpt { + cfg: yazi_config::popup::InputCfg { + title: String::new(), + value: tb.raw_get("value")?, + cursor: None, + obscure: false, + position: Default::default(), + realtime: false, + completion: false, + }, + tx: None, + })?; + + Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() }) + } +} + +impl Spatial for Input { + fn area(&self) -> Area { self.inner.lock().pos.into() } + + fn set_area(&mut self, area: Area) { + self.inner.lock().repos(match area { + Area::Rect(rect) => yazi_config::popup::Position { + origin: yazi_config::popup::Origin::TopLeft, + offset: yazi_config::popup::Offset { + x: rect.x as i16, + y: rect.y as i16, + width: rect.width, + height: rect.height, + }, + }, + Area::Pos(pos) => pos.into(), + }); + } +} + +impl Widget for Input { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &Input { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + self.inner.lock().render(rect, buf) + } +} + +impl FromLua for Input { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + match value { + Value::Table(tb) => Self::try_from(tb), + _ => Err(EXPECTED.into_lua_err()), + } + } +} + +impl UserData for Input { + fn add_methods>(methods: &mut M) { + impl_area_method!(methods); + + methods.add_function("focus", |lua, (ud, focus): (AnyUserData, bool)| { + ud.borrow_mut::()?.focus = focus; + ud.into_lua(lua) + }); + } +} diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 2d4fd5de..31cb4f21 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -6,13 +6,13 @@ use ratatui::widgets::Widget; use unicode_width::UnicodeWidthChar; use super::{Area, Span}; -use crate::elements::Align; +use crate::elements::{Align, Spatial}; const EXPECTED: &str = "expected a string, Span, Line, or a table of them"; #[derive(Clone, Debug, Default)] pub struct Line { - pub(super) area: Area, + area: Area, pub(super) inner: ratatui::text::Line<'static>, } @@ -57,6 +57,20 @@ impl From> for Line { } } +impl TryFrom<&AnyUserData> for Line { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { + Ok(if let Ok(line) = value.take() { + line + } else if let Ok(Span(span)) = value.take() { + Self { inner: span.into(), ..Default::default() } + } else { + Err(EXPECTED.into_lua_err())? + }) + } +} + impl TryFrom
for Line { type Error = mlua::Error; @@ -86,6 +100,12 @@ impl From for ratatui::text::Line<'static> { fn from(value: Line) -> Self { value.inner } } +impl Spatial for Line { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Line { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where @@ -106,22 +126,11 @@ impl Widget for &Line { impl FromLua for Line { fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(Self { - inner: match value { - Value::Table(tb) => return Self::try_from(tb), - Value::String(s) => s.to_string_lossy().into(), - Value::UserData(ud) => { - if let Ok(Span(span)) = ud.take() { - span.into() - } else if let Ok(line) = ud.take() { - return Ok(line); - } else { - Err(EXPECTED.into_lua_err())? - } - } - _ => Err(EXPECTED.into_lua_err())?, - }, - ..Default::default() + Ok(match value { + Value::Table(tb) => Self::try_from(tb)?, + Value::String(s) => Self { inner: s.to_string_lossy().into(), ..Default::default() }, + Value::UserData(ud) => Self::try_from(&ud)?, + _ => Err(EXPECTED.into_lua_err())?, }) } } diff --git a/yazi-binding/src/elements/list.rs b/yazi-binding/src/elements/list.rs index 09f7b973..55c83972 100644 --- a/yazi-binding/src/elements/list.rs +++ b/yazi-binding/src/elements/list.rs @@ -1,12 +1,13 @@ -use mlua::{IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; use super::{Area, Text}; +use crate::elements::Spatial; // --- List #[derive(Clone, Debug, Default)] pub struct List { - pub(super) area: Area, + area: Area, inner: ratatui::widgets::List<'static>, } @@ -24,6 +25,18 @@ impl List { } } +impl TryFrom<&AnyUserData> for List { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { value.take() } +} + +impl Spatial for List { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for List { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index b9c31b3d..2aad50fe 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge layout line list pad pos rect renderable renderables row span table text wrap); +yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge input layout line list pad pos rect renderable renderables row span spatial table text wrap); diff --git a/yazi-binding/src/elements/pos.rs b/yazi-binding/src/elements/pos.rs index d509d945..24a4a1e8 100644 --- a/yazi-binding/src/elements/pos.rs +++ b/yazi-binding/src/elements/pos.rs @@ -30,6 +30,14 @@ impl From for yazi_config::popup::Position { fn from(value: Pos) -> Self { value.inner } } +impl TryFrom<&AnyUserData> for Pos { + type Error = mlua::Error; + + fn try_from(ud: &AnyUserData) -> Result { + if let Ok(pos) = ud.borrow() { Ok(*pos) } else { Err(EXPECTED.into_lua_err()) } + } +} + impl TryFrom
for Pos { type Error = mlua::Error; @@ -50,17 +58,11 @@ impl TryFrom
for Pos { impl FromLua for Pos { fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(match value { - Value::Table(tbl) => Self::try_from(tbl)?, - Value::UserData(ud) => { - if let Ok(pos) = ud.borrow() { - *pos - } else { - Err(EXPECTED.into_lua_err())? - } - } - _ => Err(EXPECTED.into_lua_err())?, - }) + match value { + Value::Table(tbl) => Self::try_from(tbl), + Value::UserData(ud) => Self::try_from(&ud), + _ => Err(EXPECTED.into_lua_err()), + } } } @@ -75,7 +77,7 @@ impl Pos { } pub fn with_height(mut self, height: u16) -> Self { - self.inner.offset.height = height; + self.inner.height = height; self } } @@ -84,10 +86,10 @@ impl UserData for Pos { fn add_fields>(fields: &mut F) { // TODO: cache fields.add_field_method_get("1", |_, me| Ok(me.origin.into_str())); - fields.add_field_method_get("x", |_, me| Ok(me.offset.x)); - fields.add_field_method_get("y", |_, me| Ok(me.offset.y)); - fields.add_field_method_get("w", |_, me| Ok(me.offset.width)); - fields.add_field_method_get("h", |_, me| Ok(me.offset.height)); + fields.add_field_method_get("x", |_, me| Ok(me.x)); + fields.add_field_method_get("y", |_, me| Ok(me.y)); + fields.add_field_method_get("w", |_, me| Ok(me.width)); + fields.add_field_method_get("h", |_, me| Ok(me.height)); } fn add_methods>(methods: &mut M) { diff --git a/yazi-binding/src/elements/renderable.rs b/yazi-binding/src/elements/renderable.rs index 8e9008de..cb7f889e 100644 --- a/yazi-binding/src/elements/renderable.rs +++ b/yazi-binding/src/elements/renderable.rs @@ -3,8 +3,8 @@ use std::any::TypeId; use mlua::{AnyUserData, ExternalError, FromLua, Lua, Value}; use ratatui::widgets::Widget; -use super::{Area, Bar, Border, Clear, Fill, Gauge, Line, List, Table, Text}; -use crate::Error; +use super::{Area, Bar, Border, Clear, Fill, Gauge, Input, Line, List, Table, Text}; +use crate::{Error, elements::Spatial}; #[derive(Clone, Debug)] pub enum Renderable { @@ -15,38 +15,14 @@ pub enum Renderable { Clear(Clear), Fill(Fill), Border(Border), + Input(Input), Gauge(Box), Table(Box
), } impl Renderable { - pub fn area(&self) -> Area { - match self { - Self::Line(line) => line.area, - Self::Text(text) => text.area, - Self::List(list) => list.area, - Self::Bar(bar) => bar.area, - Self::Clear(clear) => clear.area, - Self::Fill(fill) => fill.area, - Self::Border(border) => border.area, - Self::Gauge(gauge) => gauge.area, - Self::Table(table) => table.area, - } - } - pub fn with_area(mut self, area: impl Into) -> Self { - let area = area.into(); - match &mut self { - Self::Line(line) => line.area = area, - Self::Text(text) => text.area = area, - Self::List(list) => list.area = area, - Self::Bar(bar) => bar.area = area, - Self::Clear(clear) => clear.area = area, - Self::Fill(fill) => fill.area = area, - Self::Border(border) => border.area = area, - Self::Gauge(gauge) => gauge.area = area, - Self::Table(table) => table.area = area, - } + self.set_area(area.into()); self } @@ -64,15 +40,16 @@ impl TryFrom<&AnyUserData> for Renderable { fn try_from(ud: &AnyUserData) -> Result { Ok(match ud.type_id() { - Some(t) if t == TypeId::of::() => Self::Line(ud.take()?), - Some(t) if t == TypeId::of::() => Self::Text(ud.take()?), - Some(t) if t == TypeId::of::() => Self::List(Box::new(ud.take()?)), - Some(t) if t == TypeId::of::() => Self::Bar(ud.take()?), - Some(t) if t == TypeId::of::() => Self::Clear(ud.take()?), - Some(t) if t == TypeId::of::() => Self::Fill(ud.take()?), - Some(t) if t == TypeId::of::() => Self::Border(ud.take()?), - Some(t) if t == TypeId::of::() => Self::Gauge(Box::new(ud.take()?)), - Some(t) if t == TypeId::of::
() => Self::Table(Box::new(ud.take()?)), + Some(t) if t == TypeId::of::() => Self::Line(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Text(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::List(Box::new(ud.try_into()?)), + Some(t) if t == TypeId::of::() => Self::Bar(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Clear(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Fill(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Border(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Input(ud.try_into()?), + Some(t) if t == TypeId::of::() => Self::Gauge(Box::new(ud.try_into()?)), + Some(t) if t == TypeId::of::
() => Self::Table(Box::new(ud.try_into()?)), _ => Err(format!("expected a renderable userdata, not: {ud:#?}").into_lua_err())?, }) } @@ -80,11 +57,39 @@ impl TryFrom<&AnyUserData> for Renderable { impl From for Renderable { fn from(error: Error) -> Self { - Self::Text(Text { - inner: error.into_string().into(), - wrap: ratatui::widgets::Wrap { trim: false }.into(), - ..Default::default() - }) + Self::Text(Text::from(error.into_string()).wrap(ratatui::widgets::Wrap { trim: false })) + } +} + +impl Spatial for Renderable { + fn area(&self) -> Area { + match self { + Self::Line(line) => line.area(), + Self::Text(text) => text.area(), + Self::List(list) => list.area(), + Self::Bar(bar) => bar.area(), + Self::Clear(clear) => clear.area(), + Self::Fill(fill) => fill.area(), + Self::Border(border) => border.area(), + Self::Input(input) => input.area(), + Self::Gauge(gauge) => gauge.area(), + Self::Table(table) => table.area(), + } + } + + fn set_area(&mut self, area: Area) { + match self { + Self::Line(line) => line.set_area(area), + Self::Text(text) => text.set_area(area), + Self::List(list) => list.set_area(area), + Self::Bar(bar) => bar.set_area(area), + Self::Clear(clear) => clear.set_area(area), + Self::Fill(fill) => fill.set_area(area), + Self::Border(border) => border.set_area(area), + Self::Input(input) => input.set_area(area), + Self::Gauge(gauge) => gauge.set_area(area), + Self::Table(table) => table.set_area(area), + } } } @@ -101,6 +106,7 @@ impl Widget for Renderable { Self::Clear(clear) => clear.render(rect, buf), Self::Fill(fill) => fill.render(rect, buf), Self::Border(border) => border.render(rect, buf), + Self::Input(input) => input.render(rect, buf), Self::Gauge(gauge) => gauge.render(rect, buf), Self::Table(table) => table.render(rect, buf), } @@ -120,6 +126,7 @@ impl Widget for &Renderable { Renderable::Clear(clear) => clear.render(rect, buf), Renderable::Fill(fill) => fill.render(rect, buf), Renderable::Border(border) => border.render(rect, buf), + Renderable::Input(input) => input.render(rect, buf), Renderable::Gauge(gauge) => (&**gauge).render(rect, buf), Renderable::Table(table) => (&**table).render(rect, buf), } diff --git a/yazi-binding/src/elements/row.rs b/yazi-binding/src/elements/row.rs index 969f81ef..9b31f5de 100644 --- a/yazi-binding/src/elements/row.rs +++ b/yazi-binding/src/elements/row.rs @@ -36,18 +36,20 @@ impl From for ratatui::widgets::Row<'static> { } } +impl TryFrom<&AnyUserData> for Row { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { + if let Ok(row) = value.take() { Ok(row) } else { Err(EXPECTED.into_lua_err()) } + } +} + impl FromLua for Row { fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(match value { - Value::UserData(ud) => { - if let Ok(row) = ud.take() { - row - } else { - Err(EXPECTED.into_lua_err())? - } - } - _ => Err(EXPECTED.into_lua_err())?, - }) + match value { + Value::UserData(ud) => Self::try_from(&ud), + _ => Err(EXPECTED.into_lua_err()), + } } } diff --git a/yazi-binding/src/elements/span.rs b/yazi-binding/src/elements/span.rs index 717a0d73..7b53f8ce 100644 --- a/yazi-binding/src/elements/span.rs +++ b/yazi-binding/src/elements/span.rs @@ -58,19 +58,21 @@ impl Span { } } +impl TryFrom<&AnyUserData> for Span { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { + if let Ok(span) = value.take() { Ok(span) } else { Err(EXPECTED.into_lua_err()) } + } +} + impl FromLua for Span { fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(Self(match value { - Value::String(s) => s.to_string_lossy().into(), - Value::UserData(ud) => { - if let Ok(Self(span)) = ud.take() { - span - } else { - Err(EXPECTED.into_lua_err())? - } - } + Ok(match value { + Value::String(s) => Self(s.to_string_lossy().into()), + Value::UserData(ud) => Self::try_from(&ud)?, _ => Err(EXPECTED.into_lua_err())?, - })) + }) } } diff --git a/yazi-binding/src/elements/spatial.rs b/yazi-binding/src/elements/spatial.rs new file mode 100644 index 00000000..665e44d4 --- /dev/null +++ b/yazi-binding/src/elements/spatial.rs @@ -0,0 +1,7 @@ +use crate::elements::Area; + +pub trait Spatial { + fn area(&self) -> Area; + + fn set_area(&mut self, area: Area); +} diff --git a/yazi-binding/src/elements/table.rs b/yazi-binding/src/elements/table.rs index f7defacf..d8077904 100644 --- a/yazi-binding/src/elements/table.rs +++ b/yazi-binding/src/elements/table.rs @@ -2,12 +2,12 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserData, UserDataMethods, Val use ratatui::widgets::{StatefulWidget, Widget}; use super::{Area, Row}; -use crate::{Style, elements::Constraint}; +use crate::{Style, elements::{Constraint, Spatial}}; // --- Table #[derive(Clone, Debug, Default)] pub struct Table { - pub area: Area, + area: Area, rows: Vec, header: Option>, @@ -60,10 +60,16 @@ impl Table { } } -impl TryFrom for Table { +impl TryFrom<&AnyUserData> for Table { type Error = mlua::Error; - fn try_from(value: AnyUserData) -> Result { value.take() } + fn try_from(value: &AnyUserData) -> Result { value.take() } +} + +impl Spatial for Table { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } } impl Widget for Table { diff --git a/yazi-binding/src/elements/text.rs b/yazi-binding/src/elements/text.rs index 64b0f8cf..cf532308 100644 --- a/yazi-binding/src/elements/text.rs +++ b/yazi-binding/src/elements/text.rs @@ -3,15 +3,16 @@ use std::{any::TypeId, mem}; use ansi_to_tui::IntoText; use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; +use yazi_shared::SStr; use super::{Area, Line, Span, Wrap}; -use crate::{Error, elements::Align}; +use crate::{Error, elements::{Align, Spatial}}; const EXPECTED: &str = "expected a string, Line, Span, or a table of them"; #[derive(Clone, Debug, Default)] pub struct Text { - pub area: Area, + area: Area, // TODO: block pub inner: ratatui::text::Text<'static>, @@ -31,6 +32,19 @@ impl Text { text.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; text.into_lua(lua) } + + pub fn wrap(mut self, wrap: impl Into) -> Self { + self.wrap = wrap.into(); + self + } +} + +impl From> for Text { + fn from(inner: ratatui::text::Text<'static>) -> Self { Self { inner, ..Default::default() } } +} + +impl From for Text { + fn from(value: SStr) -> Self { Self { inner: value.into(), ..Default::default() } } } impl TryFrom
for Text { @@ -74,6 +88,12 @@ impl From for ratatui::widgets::Paragraph<'static> { } } +impl Spatial for Text { + fn area(&self) -> Area { self.area } + + fn set_area(&mut self, area: Area) { self.area = area; } +} + impl Widget for Text { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where @@ -100,24 +120,33 @@ impl Widget for &Text { } } -impl FromLua for Text { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - let inner = match value { - Value::Table(tb) => return Self::try_from(tb), - Value::String(s) => s.to_string_lossy().into(), - Value::UserData(ud) => match ud.type_id() { - Some(t) if t == TypeId::of::() => ud.take::()?.inner.into(), - Some(t) if t == TypeId::of::() => ud.take::()?.0.into(), - Some(t) if t == TypeId::of::() => return ud.take(), - Some(t) if t == TypeId::of::() => ud.take::()?.into_string().into(), - _ => Err(EXPECTED.into_lua_err())?, - }, +impl TryFrom<&AnyUserData> for Text { + type Error = mlua::Error; + + fn try_from(value: &AnyUserData) -> Result { + let inner = match value.type_id() { + Some(t) if t == TypeId::of::() => return value.take(), + Some(t) if t == TypeId::of::() => value.take::()?.inner.into(), + Some(t) if t == TypeId::of::() => value.take::()?.0.into(), + Some(t) if t == TypeId::of::() => value.take::()?.into_string().into(), _ => Err(EXPECTED.into_lua_err())?, }; + Ok(Self { inner, ..Default::default() }) } } +impl FromLua for Text { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + Ok(match value { + Value::Table(tb) => Self::try_from(tb)?, + Value::String(s) => Self { inner: s.to_string_lossy().into(), ..Default::default() }, + Value::UserData(ud) => Self::try_from(&ud)?, + _ => Err(EXPECTED.into_lua_err())?, + }) + } +} + impl UserData for Text { fn add_methods>(methods: &mut M) { crate::impl_area_method!(methods); diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 18b415e6..bba655bf 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -50,11 +50,14 @@ macro_rules! impl_area_method { "area", |lua, (ud, area): (mlua::AnyUserData, Option)| { use mlua::IntoLua; + + use crate::elements::Spatial; + if let Some(v) = area { - ud.borrow_mut::()?.area = $crate::elements::Area::try_from(v)?; + ud.borrow_mut::()?.set_area((&v).try_into()?); ud.into_lua(lua) } else { - ud.borrow::()?.area.into_lua(lua) + ud.borrow::()?.area().into_lua(lua) } }, ); diff --git a/yazi-config/src/popup/input.rs b/yazi-config/src/popup/input.rs index 55e335d7..71aababc 100644 --- a/yazi-config/src/popup/input.rs +++ b/yazi-config/src/popup/input.rs @@ -42,7 +42,3 @@ pub struct Input { pub shell_origin: Origin, pub shell_offset: Offset, } - -impl Input { - pub const fn border(&self) -> u16 { 2 } -} diff --git a/yazi-config/src/popup/position.rs b/yazi-config/src/popup/position.rs index 3ea8bd89..fa48c69b 100644 --- a/yazi-config/src/popup/position.rs +++ b/yazi-config/src/popup/position.rs @@ -1,4 +1,6 @@ -use ratatui::layout::Rect; +use std::ops::{Deref, DerefMut}; + +use ratatui::{layout::Rect, widgets::Padding}; use yazi_term::Dimension; use super::{Offset, Origin}; @@ -9,6 +11,16 @@ pub struct Position { pub offset: Offset, } +impl Deref for Position { + type Target = Offset; + + fn deref(&self) -> &Self::Target { &self.offset } +} + +impl DerefMut for Position { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.offset } +} + impl Position { pub const fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } } @@ -62,4 +74,30 @@ impl Position { height: height.min(rows.saturating_sub(new_y)), } } + + pub fn padding(mut self, padding: Padding) -> Self { + use Origin::*; + let h_reduction = padding.left + padding.right; + let v_reduction = padding.top + padding.bottom; + + self.x = self.x.saturating_add_unsigned(padding.left); + self.y = self.y.saturating_add_unsigned(padding.top); + + self.width = self.width.saturating_sub(h_reduction); + self.height = self.height.saturating_sub(v_reduction); + + self.x = self.x.saturating_sub_unsigned(match self.origin { + TopCenter | BottomCenter | Center => h_reduction / 2, + TopRight | BottomRight => h_reduction, + _ => 0, + }); + + self.y = self.y.saturating_sub_unsigned(match self.origin { + BottomLeft | BottomCenter | BottomRight => v_reduction, + Center => v_reduction / 2, + _ => 0, + }); + + self + } } diff --git a/yazi-core/src/core.rs b/yazi-core/src/core.rs index fcb940cd..0e924c26 100644 --- a/yazi-core/src/core.rs +++ b/yazi-core/src/core.rs @@ -1,8 +1,8 @@ -use ratatui::layout::{Position, Rect}; +use ratatui::layout::{Margin, Position, Rect}; use yazi_shared::Layer; use yazi_term::CursorStyle; -use crate::{cmp::Cmp, confirm::Confirm, help::Help, input::Input, mgr::Mgr, notify::Notify, pick::Pick, tab::{Folder, Tab}, tasks::Tasks, which::Which}; +use crate::{cmp::Cmp, confirm::Confirm, help::Help, input::{Input, InputGuard}, mgr::Mgr, notify::Notify, pick::Pick, tab::{Folder, Tab}, tasks::Tasks, which::Which}; pub struct Core { pub mgr: Mgr, @@ -32,16 +32,18 @@ impl Core { } pub fn cursor(&self) -> Option<(Position, CursorStyle)> { - if self.input.visible { - let Rect { x, y, .. } = self.mgr.area(self.input.position); - return Some(( - Position { x: x + 1 + self.input.cursor(), y: y + 1 }, - self.input.cursor_shape(), - )); + if let Some(guard) = self.input.lock() { + let Rect { x, y, .. } = match &guard { + InputGuard::Main(_) => self.mgr.area(self.input.main_position).inner(Margin::new(1, 1)), + InputGuard::Alt(alt) => self.mgr.area(alt.pos), + }; + return Some((Position { x: x + guard.cursor(), y }, guard.cursor_shape())); } + if let Some((x, y)) = self.help.cursor() { return Some((Position { x, y }, self.help.cursor_shape())); } + None } @@ -54,7 +56,7 @@ impl Core { Layer::Help } else if self.confirm.visible { Layer::Confirm - } else if self.input.visible { + } else if self.input.focus() { Layer::Input } else if self.pick.visible { Layer::Pick diff --git a/yazi-core/src/input/guard.rs b/yazi-core/src/input/guard.rs new file mode 100644 index 00000000..c0328928 --- /dev/null +++ b/yazi-core/src/input/guard.rs @@ -0,0 +1,46 @@ +use std::ops::{Deref, DerefMut}; + +use parking_lot::MutexGuard; + +// --- InputGuard +pub enum InputGuard<'a> { + Main(&'a yazi_widgets::input::Input), + Alt(MutexGuard<'a, yazi_widgets::input::Input>), +} + +impl Deref for InputGuard<'_> { + type Target = yazi_widgets::input::Input; + + fn deref(&self) -> &Self::Target { + match self { + Self::Main(main) => main, + Self::Alt(alt) => alt, + } + } +} + +// --- InputMutGuard +pub enum InputMutGuard<'a> { + Main(&'a mut yazi_widgets::input::Input), + Alt(MutexGuard<'a, yazi_widgets::input::Input>), +} + +impl Deref for InputMutGuard<'_> { + type Target = yazi_widgets::input::Input; + + fn deref(&self) -> &Self::Target { + match self { + Self::Main(main) => main, + Self::Alt(alt) => alt, + } + } +} + +impl DerefMut for InputMutGuard<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Self::Main(main) => main, + Self::Alt(alt) => alt, + } + } +} diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index 130c4e5a..ed057875 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -1,22 +1,40 @@ -use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use parking_lot::Mutex; use yazi_config::popup::Position; +use crate::input::{InputGuard, InputMutGuard}; + #[derive(Default)] pub struct Input { - pub(super) inner: yazi_widgets::input::Input, + pub main: yazi_widgets::input::Input, + pub alt: Option>>, - pub visible: bool, - pub title: String, - pub position: Position, + pub main_visible: bool, + pub main_title: String, + pub main_position: Position, } -impl Deref for Input { - type Target = yazi_widgets::input::Input; +impl Input { + pub fn focus(&self) -> bool { self.main_visible || self.alt.is_some() } - fn deref(&self) -> &Self::Target { &self.inner } -} + pub fn lock(&self) -> Option> { + if self.main_visible { + Some(InputGuard::Main(&self.main)) + } else if let Some(alt) = &self.alt { + Some(InputGuard::Alt(alt.lock())) + } else { + None + } + } -impl DerefMut for Input { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } + pub fn lock_mut(&mut self) -> Option> { + if self.main_visible { + Some(InputMutGuard::Main(&mut self.main)) + } else if let Some(alt) = &self.alt { + Some(InputMutGuard::Alt(alt.lock())) + } else { + None + } + } } diff --git a/yazi-core/src/input/mod.rs b/yazi-core/src/input/mod.rs index 9ad1aafa..60b5cf6d 100644 --- a/yazi-core/src/input/mod.rs +++ b/yazi-core/src/input/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(input); +yazi_macro::mod_flat!(guard input); diff --git a/yazi-core/src/pick/pick.rs b/yazi-core/src/pick/pick.rs index 6808b14c..0123964f 100644 --- a/yazi-core/src/pick/pick.rs +++ b/yazi-core/src/pick/pick.rs @@ -26,9 +26,7 @@ impl Pick { impl Scrollable for Pick { fn total(&self) -> usize { self.items.len() } - fn limit(&self) -> usize { - self.position.offset.height.saturating_sub(YAZI.pick.border()) as usize - } + fn limit(&self) -> usize { self.position.height.saturating_sub(YAZI.pick.border()) as usize } fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor } diff --git a/yazi-core/src/proxy.rs b/yazi-core/src/proxy.rs index a8e1457e..626b934c 100644 --- a/yazi-core/src/proxy.rs +++ b/yazi-core/src/proxy.rs @@ -50,7 +50,7 @@ impl MgrProxy { skip: job.skip, area: area.into(), data: vec![ - Renderable::Clear(yazi_binding::elements::Clear { area: area.into() }), + Renderable::Clear(Default::default()).with_area(area), Renderable::from(Error::custom(error)).with_area(area), ], }); diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index 6f2acea0..a32da3a7 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -155,7 +155,8 @@ impl Folder { len.saturating_sub(limit).min(self.offset) } else { len.saturating_sub(limit).min(self.cursor.saturating_sub(limit) + 1 + scrolloff) - }; + } + .min(self.cursor); old != self.offset } diff --git a/yazi-fm/src/app/mod.rs b/yazi-fm/src/app/mod.rs index 723744fb..694aef48 100644 --- a/yazi-fm/src/app/mod.rs +++ b/yazi-fm/src/app/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(app render); +yazi_macro::mod_flat!(app render sync_guard); diff --git a/yazi-fm/src/app/render.rs b/yazi-fm/src/app/render.rs index a137d3ad..7f948463 100644 --- a/yazi-fm/src/app/render.rs +++ b/yazi-fm/src/app/render.rs @@ -1,17 +1,15 @@ -use std::{io::Write, sync::atomic::{AtomicU8, Ordering}, time::Instant}; +use std::{sync::atomic::Ordering, time::Instant}; use anyhow::Result; -use ratatui::layout::Position; use yazi_actor::{Ctx, lives::Lives}; use yazi_binding::runtime_scope; use yazi_config::LAYOUT; -use yazi_macro::{act, succ, writef}; +use yazi_macro::{act, succ}; use yazi_plugin::LUA; use yazi_shared::{data::Data, event::NEED_RENDER}; -use yazi_term::{CursorStyle, sequence::{BeginSyncUpdate, EndSyncUpdate, MoveTo, SetCursorStyle, ShowCursor}}; -use yazi_tty::TTY; use yazi_widgets::COLLISION; +use super::SyncGuard; use crate::{app::App, root::Root}; impl App { @@ -24,9 +22,7 @@ impl App { return self.render_partially(); } - Self::routine(true, None); - let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c)); - + let guard = SyncGuard::enter(); let collision = COLLISION.swap(false, Ordering::Relaxed); let preview_rect = LAYOUT.get().preview; term.draw(|f| { @@ -45,6 +41,8 @@ impl App { } else if preview_rect != LAYOUT.get().preview { act!(mgr:peek, cx)?; // Reload preview if layout changed } + + guard.finish(self.core.cursor()); succ!(); } @@ -54,9 +52,7 @@ impl App { return self.render(false); } - Self::routine(true, None); - let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c)); - + let guard = SyncGuard::enter(); term.draw_partial(|f| { _ = Lives::scope(&mut self.core, |core| { runtime_scope!(LUA, "root", { @@ -67,29 +63,7 @@ impl App { }); })?; + guard.finish(self.core.cursor()); succ!(); } - - fn routine(push: bool, cursor: Option<(Position, CursorStyle)>) { - static COUNT: AtomicU8 = AtomicU8::new(0); - if push && COUNT.fetch_add(1, Ordering::Relaxed) != 0 { - return; - } else if !push && COUNT.fetch_sub(1, Ordering::Relaxed) != 1 { - return; - } - - if push { - write!(TTY.writer(), "{BeginSyncUpdate}").ok(); - } else if let Some((Position { x, y }, shape)) = cursor { - writef!( - TTY.writer(), - "{}{}{ShowCursor}{EndSyncUpdate}", - SetCursorStyle(shape as u8), - MoveTo(x, y), - ) - .ok(); - } else { - writef!(TTY.writer(), "{EndSyncUpdate}").ok(); - }; - } } diff --git a/yazi-fm/src/app/sync_guard.rs b/yazi-fm/src/app/sync_guard.rs new file mode 100644 index 00000000..19724c2f --- /dev/null +++ b/yazi-fm/src/app/sync_guard.rs @@ -0,0 +1,52 @@ +use std::{io::Write, sync::atomic::{AtomicU8, Ordering}}; + +use ratatui::layout::Position; +use yazi_macro::writef; +use yazi_term::{CursorStyle, sequence::{BeginSyncUpdate, EndSyncUpdate, MoveTo, SetCursorStyle, ShowCursor}}; +use yazi_tty::TTY; + +static DEPTH: AtomicU8 = AtomicU8::new(0); + +pub(super) struct SyncGuard { + finished: bool, +} + +impl SyncGuard { + pub(super) fn enter() -> Self { + if DEPTH.fetch_add(1, Ordering::Relaxed) == 0 { + write!(TTY.writer(), "{BeginSyncUpdate}").ok(); + } + + Self { finished: false } + } + + pub(super) fn finish(mut self, cursor: Option<(Position, CursorStyle)>) { + self.finished = true; + if DEPTH.fetch_sub(1, Ordering::Relaxed) != 1 { + return; + } + + _ = if let Some((Position { x, y }, shape)) = cursor { + writef!( + TTY.writer(), + "{}{}{ShowCursor}{EndSyncUpdate}", + SetCursorStyle(shape as u8), + MoveTo(x, y), + ) + } else { + writef!(TTY.writer(), "{EndSyncUpdate}") + }; + } +} + +impl Drop for SyncGuard { + fn drop(&mut self) { + if self.finished { + return; + } + + if DEPTH.fetch_sub(1, Ordering::Relaxed) == 1 { + writef!(TTY.writer(), "{EndSyncUpdate}").ok(); + } + } +} diff --git a/yazi-fm/src/cmp/cmp.rs b/yazi-fm/src/cmp/cmp.rs index df231612..e4b4261d 100644 --- a/yazi-fm/src/cmp/cmp.rs +++ b/yazi-fm/src/cmp/cmp.rs @@ -37,7 +37,7 @@ impl Widget for Cmp<'_> { }) .collect(); - let input_area = self.core.mgr.area(self.core.input.position); + let input_area = self.core.mgr.area(self.core.input.main_position); let mut area = Position::sticky(TERM.dimension(), input_area, Offset { x: 1, y: 0, diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs index b25fd32e..6e24e65b 100644 --- a/yazi-fm/src/dispatcher.rs +++ b/yazi-fm/src/dispatcher.rs @@ -88,12 +88,11 @@ impl<'a> Dispatcher<'a> { } fn dispatch_paste(&mut self, str: String) -> Result<()> { - if self.app.core.input.visible { - let input = &mut self.app.core.input; - if input.mode() == InputMode::Insert { - input.type_str(&str)?; - } else if input.mode() == InputMode::Replace { - input.replace_str(&str)?; + if let Some(mut guard) = self.app.core.input.lock_mut() { + if guard.mode() == InputMode::Insert { + guard.type_str(&str)?; + } else if guard.mode() == InputMode::Replace { + guard.replace_str(&str)?; } } Ok(()) diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 7f7c6c5c..671119cd 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -249,40 +249,39 @@ impl<'a> Executor<'a> { } fn input(&mut self, action: ActionCow) -> Result { - let mode = self.app.core.input.mode(); - let cx = &mut Ctx::new(&action, &mut self.app.core, &mut self.app.term)?; + let Some(mut guard) = self.app.core.input.lock_mut() else { succ!() }; macro_rules! on { ($name:ident) => { if action.name == stringify!($name) { - return act!(input:$name, cx, action); + on!(input:$name, action); } }; + ($layer:ident : $name:ident, $opt:expr) => {{ + drop(guard); + let cx = &mut Ctx::new(&action, &mut self.app.core, &mut self.app.term)?; + return act!($layer:$name, cx, $opt); + }}; } on!(escape); on!(show); on!(close); - match mode { - InputMode::Normal => { - match action.name.as_ref() { - // Help - "help" => return act!(help:toggle, cx, Layer::Input), - // Plugin - "plugin" => return act!(app:plugin, cx, action), - // Lua - "lua" => return act!(app:lua, cx, action), - _ => {} - } - } + match guard.mode() { + InputMode::Normal => match action.name.as_ref() { + "help" => on!(help:toggle, Layer::Input), + "plugin" => on!(app:plugin, action), + "lua" => on!(app:lua, action), + _ => {} + }, InputMode::Insert => { on!(complete); } InputMode::Replace => {} }; - self.app.core.input.execute(action) + guard.execute(action) } fn confirm(&mut self, action: ActionCow) -> Result { diff --git a/yazi-fm/src/input/input.rs b/yazi-fm/src/input/input.rs index fa5bbc49..c396a9b1 100644 --- a/yazi-fm/src/input/input.rs +++ b/yazi-fm/src/input/input.rs @@ -13,16 +13,16 @@ impl<'a> Input<'a> { impl Widget for Input<'_> { fn render(self, _: Rect, buf: &mut Buffer) { let input = &self.core.input; - let area = self.core.mgr.area(input.position); - yazi_widgets::Clear.render(area, buf); + let outer = self.core.mgr.area(input.main_position); + yazi_widgets::Clear.render(outer, buf); Block::bordered() .border_type(BorderType::Rounded) .border_style(THEME.input.border.get()) - .title(Line::styled(&input.title, THEME.input.title.get())) - .render(area, buf); + .title(Line::styled(&input.main_title, THEME.input.title.get())) + .render(outer, buf); - input.render(area.inner(Margin::new(1, 1)), buf); + input.main.render(outer.inner(Margin::new(1, 1)), buf); } } diff --git a/yazi-fm/src/mgr/preview.rs b/yazi-fm/src/mgr/preview.rs index 7227edc3..bb3a8ef6 100644 --- a/yazi-fm/src/mgr/preview.rs +++ b/yazi-fm/src/mgr/preview.rs @@ -1,4 +1,5 @@ use ratatui::{buffer::Buffer, widgets::Widget}; +use yazi_binding::elements::Spatial; use yazi_config::LAYOUT; use yazi_core::Core; diff --git a/yazi-fm/src/renderer.rs b/yazi-fm/src/renderer.rs index 7c53af05..87c2bf89 100644 --- a/yazi-fm/src/renderer.rs +++ b/yazi-fm/src/renderer.rs @@ -1,6 +1,6 @@ use mlua::{ObjectLike, Table}; use ratatui::{buffer::Buffer, layout::Rect}; -use yazi_binding::elements::Renderables; +use yazi_binding::elements::{Renderable, Renderables}; use yazi_core::Core; use yazi_plugin::LUA; @@ -33,9 +33,17 @@ impl<'a> Renderer<'a> { .globals() .raw_get::
(self.component)? .call_method::
(self.constructor, area)? - .call_method("redraw", ())?; + .call_method(self.redrawer, ())?; + self.core.input.alt = None; Renderables::reduce(value, |element| { + match &element { + Renderable::Input(input) if input.focus => { + self.core.input.alt = Some(input.into()); + } + _ => {} + } + element.render_with(buf, |p| self.core.mgr.area(p)); }); diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 67d4daf6..1b5f3962 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -42,7 +42,7 @@ impl Widget for Root<'_> { pick::Pick::new(self.core).render(area, buf); } - if self.core.input.visible { + if self.core.input.main_visible { input::Input::new(self.core).render(area, buf); } diff --git a/yazi-fm/src/router.rs b/yazi-fm/src/router.rs index 151fa709..b2479c61 100644 --- a/yazi-fm/src/router.rs +++ b/yazi-fm/src/router.rs @@ -14,17 +14,20 @@ impl<'a> Router<'a> { pub(super) fn new(app: &'a mut App) -> Self { Self { app } } pub(super) fn route(&mut self, key: Key) -> Result { - let core = &mut self.app.core; - let layer = core.layer(); + use Layer as L; + let core = &mut self.app.core; if core.help.visible && core.help.r#type(&key)? { return Ok(true); } - if core.input.visible && core.input.r#type(&key)? { + + if let Some(mut guard) = core.input.lock_mut() + && guard.r#type(&key)? + { return Ok(true); } - use Layer as L; + let layer = core.layer(); Ok(match layer { L::Null | L::App | L::Notify => unreachable!(), L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => { diff --git a/yazi-fm/src/spot/spot.rs b/yazi-fm/src/spot/spot.rs index 7dd5cc6f..9da17fe2 100644 --- a/yazi-fm/src/spot/spot.rs +++ b/yazi-fm/src/spot/spot.rs @@ -1,4 +1,5 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use yazi_binding::elements::Spatial; use yazi_core::Core; pub(crate) struct Spot<'a> { diff --git a/yazi-macro/src/input.rs b/yazi-macro/src/input.rs index 25506e8e..e240c1b6 100644 --- a/yazi-macro/src/input.rs +++ b/yazi-macro/src/input.rs @@ -2,7 +2,7 @@ macro_rules! input { ($cx:ident, $cfg:expr) => {{ let (tx, rx) = ::tokio::sync::mpsc::unbounded_channel(); - match $crate::act!(input:show, $cx, yazi_widgets::input::InputOpt { cfg: $cfg, tx }) { + match $crate::act!(input:show, $cx, yazi_widgets::input::InputOpt { cfg: $cfg, tx: Some(tx) }) { Ok(_) => Ok(rx), Err(e) => Err(e) } diff --git a/yazi-parser/src/input/close.rs b/yazi-parser/src/input/close.rs index fc2065eb..32e7dd9e 100644 --- a/yazi-parser/src/input/close.rs +++ b/yazi-parser/src/input/close.rs @@ -1,7 +1,9 @@ -use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; +use serde::{Deserialize, Serialize}; +use yazi_binding::SER_OPT; use yazi_shared::event::ActionCow; -#[derive(Debug, Default)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct CloseForm { pub submit: bool, } @@ -15,9 +17,9 @@ impl From for CloseForm { } impl FromLua for CloseForm { - fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { lua.from_value(value) } } impl IntoLua for CloseForm { - fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self, SER_OPT) } } diff --git a/yazi-parser/src/spark/kind.rs b/yazi-parser/src/spark/kind.rs index 382e00ea..4eeb31f6 100644 --- a/yazi-parser/src/spark/kind.rs +++ b/yazi-parser/src/spark/kind.rs @@ -20,6 +20,10 @@ pub enum SparkKind { // mgr:quit KeyQuit, + // input:close + KeyInputClose, + IndInputClose, + // which:activate IndWhichActivate, diff --git a/yazi-parser/src/spark/spark.rs b/yazi-parser/src/spark/spark.rs index 48f2c080..97cfd306 100644 --- a/yazi-parser/src/spark/spark.rs +++ b/yazi-parser/src/spark/spark.rs @@ -181,6 +181,10 @@ impl<'a> Spark<'a> { // mgr:quit KeyQuit => Self::Quit(<_>::from_lua(value, lua)?), + // input:close + KeyInputClose => Self::InputClose(<_>::from_lua(value, lua)?), + IndInputClose => Self::InputClose(<_>::from_lua(value, lua)?), + // which:activate IndWhichActivate => Self::WhichActivate(<_>::from_lua(value, lua)?), diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index 15d2557f..466994a1 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -56,7 +56,7 @@ function Root:click(event, up) Root._dragging = not up and c or nil if tostring(cx.layer) == "mgr" then - return c and c:click(event, up) + return c and c.click and c:click(event, up) end end @@ -65,7 +65,7 @@ function Root:scroll(event, step) return end local c = ya.child_at(ui.Rect { x = event.x, y = event.y }, self:reflow()) - return c and c:scroll(event, step) + return c and c.scroll and c:scroll(event, step) end function Root:touch(event, step) @@ -73,7 +73,7 @@ function Root:touch(event, step) return end local c = ya.child_at(ui.Rect { x = event.x, y = event.y }, self:reflow()) - return c and c:touch(event, step) + return c and c.touch and c:touch(event, step) end function Root:move(event) end diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 5c996f74..63570798 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,5 +1,5 @@ use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{Error, elements::{Area, Renderable, Text}}; +use yazi_binding::{Error, elements::{Area, Renderable}}; use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; @@ -25,7 +25,7 @@ impl Utils { } }; - lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })]; + lock.data = vec![Renderable::Text(inner.into()).with_area(area)]; MgrProxy::update_peeked(lock); ().into_lua_multi(&lua) @@ -43,7 +43,7 @@ impl Utils { Err(e) => { if let Ok(err) = ud.take::() { vec![ - Renderable::Clear(yazi_binding::elements::Clear { area: lock.area.into() }), + Renderable::Clear(Default::default()).with_area(lock.area), Renderable::from(err).with_area(lock.area), ] } else { diff --git a/yazi-plugin/src/utils/spot.rs b/yazi-plugin/src/utils/spot.rs index e0b4b709..687f2133 100644 --- a/yazi-plugin/src/utils/spot.rs +++ b/yazi-plugin/src/utils/spot.rs @@ -1,5 +1,5 @@ use mlua::{AnyUserData, Function, Lua, Table}; -use yazi_binding::elements::{Edge, Renderable}; +use yazi_binding::elements::{Edge, Renderable, Spatial}; use yazi_config::THEME; use yazi_core::spot::SpotLock; use yazi_proxy::MgrProxy; @@ -10,13 +10,13 @@ impl Utils { pub(super) fn spot_table(lua: &Lua) -> mlua::Result { lua.create_function(|_, (t, table): (mlua::Table, AnyUserData)| { let mut lock = SpotLock::try_from(t)?; - let mut table = yazi_binding::elements::Table::try_from(table)?; + let mut table = yazi_binding::elements::Table::try_from(&table)?; - let area = table.area; - table.area = area.inner(ratatui::widgets::Padding::uniform(1)); + let area = table.area(); + table.set_area(area.inner(ratatui::widgets::Padding::uniform(1))); lock.data = vec![ - Renderable::Clear(yazi_binding::elements::Clear { area }), + Renderable::Clear(Default::default()).with_area(area), Renderable::Border(yazi_binding::elements::Border { area, edge: Edge(ratatui::widgets::Borders::ALL), diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index e398cf5b..a96bb065 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -24,7 +24,7 @@ foldhash = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } inventory = { workspace = true } -memchr = "2.8.1" +memchr = "2.8.2" mlua = { workspace = true } ordered-float = { workspace = true } parking_lot = { workspace = true } diff --git a/yazi-shared/src/id.rs b/yazi-shared/src/id.rs index 3394c554..8d3af867 100644 --- a/yazi-shared/src/id.rs +++ b/yazi-shared/src/id.rs @@ -45,6 +45,7 @@ impl PartialEq for Id { } // --- Ids +#[derive(Debug)] pub struct Ids { next: AtomicU64, } diff --git a/yazi-shim/src/cell/ro_cell.rs b/yazi-shim/src/cell/ro_cell.rs index fcfa8b87..b5d48f99 100644 --- a/yazi-shim/src/cell/ro_cell.rs +++ b/yazi-shim/src/cell/ro_cell.rs @@ -55,7 +55,6 @@ impl RoCell { self.inner.get().replace(MaybeUninit::uninit()).assume_init() } } - } impl Default for RoCell { diff --git a/yazi-version/Cargo.toml b/yazi-version/Cargo.toml index 93f04a85..44dfdecc 100644 --- a/yazi-version/Cargo.toml +++ b/yazi-version/Cargo.toml @@ -13,4 +13,4 @@ rust-version.workspace = true workspace = true [build-dependencies] -vergen-gitcl = { version = "9.1.0", features = [ "build", "rustc" ] } +vergen-gitcl = { version = "10.0.0", features = [ "build", "rustc" ] } diff --git a/yazi-version/build.rs b/yazi-version/build.rs index 2be047c1..3f9a46af 100644 --- a/yazi-version/build.rs +++ b/yazi-version/build.rs @@ -1,22 +1,17 @@ use std::{env, error::Error}; -use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder, RustcBuilder}; +use vergen_gitcl::{Build, Emitter, Gitcl, Rustc}; fn main() -> Result<(), Box> { Emitter::default() - .add_instructions(&BuildBuilder::default().build_date(true).build()?)? + .add_instructions(&Build::builder().build_date(true).build())? .add_instructions( - &RustcBuilder::default() - .commit_date(true) - .commit_hash(true) - .host_triple(true) - .semver(true) - .build()?, + &Rustc::builder().commit_date(true).commit_hash(true).host_triple(true).semver(true).build(), )? .emit()?; if env::var_os("YAZI_NO_GITCL").is_none() { - Emitter::default().add_instructions(&GitclBuilder::default().sha(true).build()?)?.emit()?; + Emitter::default().add_instructions(&Gitcl::builder().sha(true).build())?.emit()?; } else { println!("cargo:rustc-env=VERGEN_GIT_SHA=no-gitcl"); } diff --git a/yazi-widgets/src/input/actor/casefy.rs b/yazi-widgets/src/input/actor/casefy.rs index 317162f6..72494c71 100644 --- a/yazi-widgets/src/input/actor/casefy.rs +++ b/yazi-widgets/src/input/actor/casefy.rs @@ -22,7 +22,7 @@ impl Input { snap.value.replace_range(start..end, &casefied); snap.op = InputOp::None; snap.cursor = range.start; - self.snaps.tag(self.limit).then(|| self.flush_type()); + self.snaps.tag(self.pos.width as usize).then(|| self.flush_type()); act!(r#move, self)?; succ!(render!()); diff --git a/yazi-widgets/src/input/actor/escape.rs b/yazi-widgets/src/input/actor/escape.rs index b2e90a04..d9e45d77 100644 --- a/yazi-widgets/src/input/actor/escape.rs +++ b/yazi-widgets/src/input/actor/escape.rs @@ -19,7 +19,7 @@ impl Input { snap.mode = InputMode::Normal; } } - self.snaps.tag(self.limit); + self.snaps.tag(self.pos.width as usize); succ!(); } } diff --git a/yazi-widgets/src/input/actor/move.rs b/yazi-widgets/src/input/actor/move.rs index 3b817006..d3958f42 100644 --- a/yazi-widgets/src/input/actor/move.rs +++ b/yazi-widgets/src/input/actor/move.rs @@ -15,7 +15,7 @@ impl Input { render!(self.handle_op(opt.step.add(&snap.value, snap.cursor), false)); let n_cur = self.snap().cursor; - let (limit, snap) = (self.limit, self.snap_mut()); + let (limit, snap) = (self.pos.width as usize, self.snap_mut()); if snap.value.is_empty() { succ!(snap.offset = 0); } diff --git a/yazi-widgets/src/input/actor/replace.rs b/yazi-widgets/src/input/actor/replace.rs index 652b7563..84cad48f 100644 --- a/yazi-widgets/src/input/actor/replace.rs +++ b/yazi-widgets/src/input/actor/replace.rs @@ -29,7 +29,7 @@ impl Input { (Some(_), Some((len, _))) => snap.value.replace_range(start..start + len, &s), } - self.snaps.tag(self.limit).then(|| self.flush_type()); + self.snaps.tag(self.pos.width as usize).then(|| self.flush_type()); succ!(render!()); } } diff --git a/yazi-widgets/src/input/input.rs b/yazi-widgets/src/input/input.rs index 22269482..a9fcf6da 100644 --- a/yazi-widgets/src/input/input.rs +++ b/yazi-widgets/src/input/input.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Range}; use anyhow::Result; use tokio::sync::mpsc; -use yazi_config::YAZI; +use yazi_config::{YAZI, popup::Position}; use yazi_macro::act; use yazi_shared::Ids; use yazi_shim::path::CROSS_SEPARATOR; @@ -11,10 +11,10 @@ use yazi_term::CursorStyle; use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp}; use crate::{CLIPBOARD, input::{InputEvent, InputOpt}}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct Input { + pub pos: Position, pub snaps: InputSnaps, - pub limit: usize, pub obscure: bool, pub realtime: bool, pub completion: bool, @@ -25,15 +25,15 @@ pub struct Input { impl Input { pub fn new(opt: InputOpt) -> Result { - let limit = opt.cfg.position.offset.width.saturating_sub(YAZI.input.border()) as usize; + let limit = opt.cfg.position.width as usize; let mut input = Self { + pos: opt.cfg.position, snaps: InputSnaps::new(opt.cfg.value, opt.cfg.obscure, limit), - limit, obscure: opt.cfg.obscure, realtime: opt.cfg.realtime, completion: opt.cfg.completion, - tx: Some(opt.tx), + tx: opt.tx, ..Default::default() }; @@ -45,6 +45,11 @@ impl Input { Ok(input) } + pub fn repos(&mut self, pos: Position) { + self.pos = pos; + self.snap_mut().resize(pos.width as usize); + } + pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool { let old = self.snap().clone(); let snap = self.snap_mut(); @@ -81,7 +86,7 @@ impl Input { return false; } if !matches!(old.op, InputOp::None | InputOp::Select(_)) { - self.snaps.tag(self.limit).then(|| self.flush_type()); + self.snaps.tag(self.pos.width as usize).then(|| self.flush_type()); } true } @@ -111,9 +116,9 @@ impl Input { pub fn display(&self) -> Cow<'_, str> { if self.obscure { - "•".repeat(self.snap().window(self.limit).len()).into() + "•".repeat(self.snap().window(self.pos.width as usize).len()).into() } else { - self.snap().slice(self.snap().window(self.limit)).into() + self.snap().slice(self.snap().window(self.pos.width as usize)).into() } } @@ -142,7 +147,7 @@ impl Input { let (start, end) = if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) }; - let win = snap.window(self.limit); + let win = snap.window(self.pos.width as usize); let Range { start, end } = start.max(win.start)..end.min(win.end); let s = snap.width(snap.offset..start); diff --git a/yazi-widgets/src/input/option.rs b/yazi-widgets/src/input/option.rs index 4e6f1dd7..5acfa986 100644 --- a/yazi-widgets/src/input/option.rs +++ b/yazi-widgets/src/input/option.rs @@ -9,7 +9,7 @@ use crate::input::InputEvent; #[derive(Debug)] pub struct InputOpt { pub cfg: InputCfg, - pub tx: mpsc::UnboundedSender, + pub tx: Option>, } impl TryFrom for InputOpt { @@ -17,14 +17,10 @@ impl TryFrom for InputOpt { fn try_from(mut a: ActionCow) -> Result { let Some(cfg) = a.take_any("cfg") else { - bail!("Invalid 'cfg' in InputOpt"); + bail!("invalid 'cfg' in InputOpt"); }; - let Some(tx) = a.take_any("tx") else { - bail!("Invalid 'tx' in InputOpt"); - }; - - Ok(Self { cfg, tx }) + Ok(Self { cfg, tx: a.take_any("tx") }) } } diff --git a/yazi-widgets/src/input/snap.rs b/yazi-widgets/src/input/snap.rs index 0ae7228f..112988b5 100644 --- a/yazi-widgets/src/input/snap.rs +++ b/yazi-widgets/src/input/snap.rs @@ -34,7 +34,6 @@ impl InputSnap { snap } - #[inline] pub(super) fn resize(&mut self, limit: usize) { let count = self.count(); let limit = if self.obscure { diff --git a/yazi-widgets/src/input/snaps.rs b/yazi-widgets/src/input/snaps.rs index 0bfbd539..780ee383 100644 --- a/yazi-widgets/src/input/snaps.rs +++ b/yazi-widgets/src/input/snaps.rs @@ -2,7 +2,7 @@ use std::mem; use super::InputSnap; -#[derive(PartialEq, Eq)] +#[derive(Debug, Eq, PartialEq)] pub struct InputSnaps { idx: usize, versions: Vec, diff --git a/yazi-widgets/src/input/widget.rs b/yazi-widgets/src/input/widget.rs index b05741f9..ad4381cd 100644 --- a/yazi-widgets/src/input/widget.rs +++ b/yazi-widgets/src/input/widget.rs @@ -10,8 +10,6 @@ impl Widget for &Input { where Self: Sized, { - crate::Clear.render(area, buf); - Line::styled(self.display(), THEME.input.value.get()).render(area, buf); if let Some(Range { start, end }) = self.selected() { From f1e93d7f528b22fdeaf66b198c73e32a7040a90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sun, 14 Jun 2026 19:58:23 +0800 Subject: [PATCH 17/28] feat: new `input` DDS event watching changes in input content (#4043) --- Cargo.lock | 5 +++++ yazi-actor/Cargo.toml | 1 + yazi-actor/src/input/close.rs | 4 ++-- yazi-actor/src/lives/core.rs | 9 +++++--- yazi-actor/src/lives/input.rs | 29 ++++++++++++++++++++++++ yazi-actor/src/lives/input_alt.rs | 27 +++++++++++++++++++++++ yazi-actor/src/lives/mod.rs | 2 +- yazi-binding/src/elements/border.rs | 12 +++++++++- yazi-binding/src/elements/input.rs | 2 +- yazi-dds/Cargo.toml | 24 ++++++++++---------- yazi-dds/src/ember/ember.rs | 7 +++++- yazi-dds/src/ember/input.rs | 34 +++++++++++++++++++++++++++++ yazi-dds/src/ember/mod.rs | 2 +- yazi-dds/src/payload.rs | 1 + yazi-dds/src/pubsub.rs | 2 ++ yazi-fm/src/executor.rs | 18 ++++++++++----- yazi-macro/src/input.rs | 11 +++++++++- yazi-plugin/src/utils/spot.rs | 1 + yazi-proxy/Cargo.toml | 6 +++-- yazi-proxy/src/input.rs | 13 ++++++++--- yazi-widgets/Cargo.toml | 2 ++ yazi-widgets/src/input/callback.rs | 22 +++++++++++++++++++ yazi-widgets/src/input/event.rs | 9 +++++++- yazi-widgets/src/input/input.rs | 18 +++++++-------- yazi-widgets/src/input/mod.rs | 2 +- yazi-widgets/src/input/option.rs | 7 +++--- 26 files changed, 220 insertions(+), 50 deletions(-) create mode 100644 yazi-actor/src/lives/input.rs create mode 100644 yazi-actor/src/lives/input_alt.rs create mode 100644 yazi-dds/src/ember/input.rs create mode 100644 yazi-widgets/src/input/callback.rs diff --git a/Cargo.lock b/Cargo.lock index 0b8d5ae9..ac4b38ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5565,6 +5565,7 @@ dependencies = [ "indexmap 2.14.0", "libc", "mlua", + "parking_lot", "paste", "ratatui", "scopeguard", @@ -6001,8 +6002,10 @@ version = "26.5.6" dependencies = [ "anyhow", "tokio", + "tracing", "yazi-config", "yazi-core", + "yazi-dds", "yazi-macro", "yazi-scheduler", "yazi-shared", @@ -6227,11 +6230,13 @@ version = "26.5.6" dependencies = [ "anyhow", "clipboard-win", + "dyn-clone", "futures", "mlua", "parking_lot", "ratatui", "serde", + "strum", "tokio", "unicode-width", "yazi-adapter", diff --git a/yazi-actor/Cargo.toml b/yazi-actor/Cargo.toml index c9b748d5..e39c009f 100644 --- a/yazi-actor/Cargo.toml +++ b/yazi-actor/Cargo.toml @@ -46,6 +46,7 @@ futures = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } mlua = { workspace = true } +parking_lot = { workspace = true } paste = { workspace = true } ratatui = { workspace = true } scopeguard = { workspace = true } diff --git a/yazi-actor/src/input/close.rs b/yazi-actor/src/input/close.rs index 9f67ad23..2bc5cd3e 100644 --- a/yazi-actor/src/input/close.rs +++ b/yazi-actor/src/input/close.rs @@ -19,9 +19,9 @@ impl Actor for Close { }; guard.ticket.next(); - if let Some(tx) = guard.tx.take() { + if let Some(cb) = guard.cb.take() { let value = guard.snap().value.clone(); - _ = tx.send(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) }); + cb(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) }); } drop(guard); diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index f7f6acdc..c3ea731f 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -14,8 +14,9 @@ pub(super) struct Core { c_tabs: Option, c_tasks: Option, c_yanked: Option, - c_layer: Option, + c_input: Option, c_which: Option, + c_layer: Option, } impl Deref for Core { @@ -33,8 +34,9 @@ impl Core { c_tabs: None, c_tasks: None, c_yanked: None, - c_layer: None, + c_input: None, c_which: None, + c_layer: None, }) } } @@ -59,10 +61,11 @@ impl UserData for Core { b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)), b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)), b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)), + b"input" => reuse!(input, super::Input::make(&me.input)), + b"which" => reuse!(which, super::Which::make(&me.which)), b"layer" => { reuse!(layer, Ok::<_, mlua::Error>(yazi_binding::Layer::from(me.layer()))) } - b"which" => reuse!(which, super::Which::make(&me.which)), _ => Value::Nil, }) }); diff --git a/yazi-actor/src/lives/input.rs b/yazi-actor/src/lives/input.rs new file mode 100644 index 00000000..6bb6f555 --- /dev/null +++ b/yazi-actor/src/lives/input.rs @@ -0,0 +1,29 @@ +use std::ops::Deref; + +use mlua::{AnyUserData, UserData, UserDataFields}; +use yazi_shim::mlua::UserDataFieldsExt; + +use super::{Lives, PtrCell}; +use crate::lives::InputAlt; + +pub(super) struct Input { + inner: PtrCell, +} + +impl Deref for Input { + type Target = yazi_core::input::Input; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Input { + pub(super) fn make(inner: &yazi_core::input::Input) -> mlua::Result { + Lives::scoped_userdata(Self { inner: inner.into() }) + } +} + +impl UserData for Input { + fn add_fields>(fields: &mut F) { + fields.add_cached_field("alt", |_, me| me.alt.as_ref().map(InputAlt::make).transpose()); + } +} diff --git a/yazi-actor/src/lives/input_alt.rs b/yazi-actor/src/lives/input_alt.rs new file mode 100644 index 00000000..8ef49f55 --- /dev/null +++ b/yazi-actor/src/lives/input_alt.rs @@ -0,0 +1,27 @@ +use std::{ops::Deref, sync::Arc}; + +use mlua::{AnyUserData, UserData, UserDataFields}; +use parking_lot::Mutex; +use yazi_shim::mlua::UserDataFieldsExt; + +use super::{Lives, PtrCell}; + +pub(super) struct InputAlt(PtrCell>>); + +impl Deref for InputAlt { + type Target = Arc>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl InputAlt { + pub(super) fn make(inner: &Arc>) -> mlua::Result { + Lives::scoped_userdata(Self(inner.into())) + } +} + +impl UserData for InputAlt { + fn add_fields>(fields: &mut F) { + fields.add_cached_field("value", |lua, me| lua.create_string(me.lock().value())); + } +} diff --git a/yazi-actor/src/lives/mod.rs b/yazi-actor/src/lives/mod.rs index d804327b..ea093ddb 100644 --- a/yazi-actor/src/lives/mod.rs +++ b/yazi-actor/src/lives/mod.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_flat!(behavior core file files filter finder folder lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked); +yazi_macro::mod_flat!(behavior core file files filter finder folder input input_alt lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked); pub(super) fn init() { unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) }; diff --git a/yazi-binding/src/elements/border.rs b/yazi-binding/src/elements/border.rs index bb1e2497..8c7956ba 100644 --- a/yazi-binding/src/elements/border.rs +++ b/yazi-binding/src/elements/border.rs @@ -19,6 +19,7 @@ pub struct Border { pub edge: Edge, pub r#type: ratatui::widgets::BorderType, pub style: ratatui::style::Style, + pub merge: ratatui::symbols::merge::MergeStrategy, pub titles: Vec<(ratatui::widgets::TitlePosition, ratatui::text::Line<'static>)>, } @@ -64,7 +65,8 @@ impl Widget for Border { let mut block = ratatui::widgets::Block::default() .borders(self.edge.0) .border_type(self.r#type) - .border_style(self.style); + .border_style(self.style) + .merge_borders(self.merge); for title in self.titles { block = match title { @@ -116,5 +118,13 @@ impl UserData for Border { ud.borrow_mut::()?.edge = edge; Ok(ud) }); + methods.add_function("merge", |_, (ud, exact): (AnyUserData, bool)| { + ud.borrow_mut::()?.merge = if exact { + ratatui::symbols::merge::MergeStrategy::Exact + } else { + ratatui::symbols::merge::MergeStrategy::Fuzzy + }; + Ok(ud) + }); } } diff --git a/yazi-binding/src/elements/input.rs b/yazi-binding/src/elements/input.rs index f6ea1399..3095bbf9 100644 --- a/yazi-binding/src/elements/input.rs +++ b/yazi-binding/src/elements/input.rs @@ -52,7 +52,7 @@ impl TryFrom
for Input { realtime: false, completion: false, }, - tx: None, + cb: None, })?; Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() }) diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index e763a4d9..db89ac92 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -26,18 +26,18 @@ yazi-shim = { path = "../yazi-shim", version = "26.5.6" } yazi-version = { path = "../yazi-version", version = "26.5.6" } # External dependencies -anyhow = { workspace = true } -hashbrown = { workspace = true } -indexmap = { workspace = true } -inventory = { workspace = true } -mlua = { workspace = true } -parking_lot = { workspace = true } -paste = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -tokio-stream = { workspace = true } -tracing = { workspace = true } +anyhow = { workspace = true } +hashbrown = { workspace = true } +indexmap = { workspace = true } +inventory = { workspace = true } +mlua = { workspace = true } +parking_lot = { workspace = true } +paste = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } [target.'cfg(windows)'.dependencies] uds_windows = "1.2.1" diff --git a/yazi-dds/src/ember/ember.rs b/yazi-dds/src/ember/ember.rs index aeafaec1..6687fb04 100644 --- a/yazi-dds/src/ember/ember.rs +++ b/yazi-dds/src/ember/ember.rs @@ -2,7 +2,7 @@ use anyhow::{Result, bail}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use yazi_shared::Id; -use super::{EmberBulkRename, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank}; +use super::{EmberBulkRename, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberInput, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank}; use crate::Payload; #[derive(Clone, Debug)] @@ -22,6 +22,7 @@ pub enum Ember<'a> { Trash(EmberTrash<'a>), Delete(EmberDelete<'a>), Download(EmberDownload<'a>), + Input(EmberInput<'a>), Mount(EmberMount), Custom(EmberCustom), } @@ -44,6 +45,7 @@ impl Ember<'static> { "trash" => Self::Trash(serde_json::from_str(body)?), "delete" => Self::Delete(serde_json::from_str(body)?), "download" => Self::Download(serde_json::from_str(body)?), + "input" => Self::Input(serde_json::from_str(body)?), "mount" => Self::Mount(serde_json::from_str(body)?), _ => EmberCustom::from_str(kind, body)?, }) @@ -72,6 +74,7 @@ impl Ember<'static> { | "trash" | "delete" | "download" + | "input" | "mount" ) || kind.starts_with("key-") || kind.starts_with("ind-") @@ -111,6 +114,7 @@ impl<'a> Ember<'a> { Self::Trash(_) => "trash", Self::Delete(_) => "delete", Self::Download(_) => "download", + Self::Input(_) => "input", Self::Mount(_) => "mount", Self::Custom(b) => b.kind.as_str(), } @@ -139,6 +143,7 @@ impl<'a> IntoLua for Ember<'a> { Self::Trash(b) => b.into_lua(lua), Self::Delete(b) => b.into_lua(lua), Self::Download(b) => b.into_lua(lua), + Self::Input(b) => b.into_lua(lua), Self::Mount(b) => b.into_lua(lua), Self::Custom(b) => b.into_lua(lua), } diff --git a/yazi-dds/src/ember/input.rs b/yazi-dds/src/ember/input.rs new file mode 100644 index 00000000..151f1ed0 --- /dev/null +++ b/yazi-dds/src/ember/input.rs @@ -0,0 +1,34 @@ +use std::borrow::Cow; + +use mlua::{IntoLua, Lua, Value}; +use serde::{Deserialize, Serialize}; + +use super::Ember; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct EmberInput<'a> { + pub r#type: Cow<'a, str>, + pub value: Cow<'a, str>, +} + +impl<'a> EmberInput<'a> { + pub fn borrowed(r#type: &'a str, value: &'a str) -> Ember<'a> { + Self { r#type: r#type.into(), value: value.into() }.into() + } +} + +impl EmberInput<'static> { + pub fn owned(r#type: &'static str, value: &str) -> Ember<'static> { + Self { r#type: r#type.into(), value: value.to_owned().into() }.into() + } +} + +impl<'a> From> for Ember<'a> { + fn from(value: EmberInput<'a>) -> Self { Self::Input(value) } +} + +impl IntoLua for EmberInput<'_> { + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua.create_table_from([("type", self.r#type), ("value", self.value)])?.into_lua(lua) + } +} diff --git a/yazi-dds/src/ember/mod.rs b/yazi-dds/src/ember/mod.rs index 6a83139d..283039db 100644 --- a/yazi-dds/src/ember/mod.rs +++ b/yazi-dds/src/ember/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_flat!( - bulk_rename bye cd custom delete download duplicate ember hey hi hover load mount r#move rename tab trash yank + bulk_rename bye cd custom delete download duplicate ember hey hi hover input load mount r#move rename tab trash yank ); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index cb2983ad..57dea9ef 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -103,6 +103,7 @@ impl Display for Payload<'_> { Ember::Trash(b) => serde_json::to_string(b), Ember::Delete(b) => serde_json::to_string(b), Ember::Download(b) => serde_json::to_string(b), + Ember::Input(b) => serde_json::to_string(b), Ember::Mount(b) => serde_json::to_string(b), Ember::Custom(b) => serde_json::to_string(b), }; diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index ff012795..a9fef7a4 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -174,5 +174,7 @@ impl Pubsub { pub_after!(download(urls: Vec), (&urls), (urls)); + pub_after!(input(r#type: &'static str, value: &str), (r#type, value)); + pub_after!(mount(), ()); } diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 671119cd..2ed1b401 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -249,12 +249,12 @@ impl<'a> Executor<'a> { } fn input(&mut self, action: ActionCow) -> Result { - let Some(mut guard) = self.app.core.input.lock_mut() else { succ!() }; - + let mut guard; macro_rules! on { ($name:ident) => { if action.name == stringify!($name) { - on!(input:$name, action); + let cx = &mut Ctx::new(&action, &mut self.app.core, &mut self.app.term)?; + return act!(input:$name, cx, action); } }; ($layer:ident : $name:ident, $opt:expr) => {{ @@ -268,6 +268,11 @@ impl<'a> Executor<'a> { on!(show); on!(close); + guard = match self.app.core.input.lock_mut() { + Some(g) => g, + None => succ!(), + }; + match guard.mode() { InputMode::Normal => match action.name.as_ref() { "help" => on!(help:toggle, Layer::Input), @@ -275,9 +280,10 @@ impl<'a> Executor<'a> { "lua" => on!(app:lua, action), _ => {} }, - InputMode::Insert => { - on!(complete); - } + InputMode::Insert => match action.name.as_ref() { + "complete" => on!(input:complete, action), + _ => {} + }, InputMode::Replace => {} }; diff --git a/yazi-macro/src/input.rs b/yazi-macro/src/input.rs index e240c1b6..da482e43 100644 --- a/yazi-macro/src/input.rs +++ b/yazi-macro/src/input.rs @@ -1,8 +1,17 @@ #[macro_export] macro_rules! input { ($cx:ident, $cfg:expr) => {{ + use yazi_dds::Pubsub; + use yazi_shim::strum::IntoStr; + use yazi_widgets::input::{InputCallback, InputEvent, InputOpt}; + let (tx, rx) = ::tokio::sync::mpsc::unbounded_channel(); - match $crate::act!(input:show, $cx, yazi_widgets::input::InputOpt { cfg: $cfg, tx: Some(tx) }) { + let cb: Box = Box::new(move |event| { + $crate::err!(Pubsub::pub_after_input((&event).into_str(), event.value())); + tx.send(event).ok(); + }); + + match $crate::act!(input:show, $cx, InputOpt { cfg: $cfg, cb: Some(cb) }) { Ok(_) => Ok(rx), Err(e) => Err(e) } diff --git a/yazi-plugin/src/utils/spot.rs b/yazi-plugin/src/utils/spot.rs index 687f2133..916c04ef 100644 --- a/yazi-plugin/src/utils/spot.rs +++ b/yazi-plugin/src/utils/spot.rs @@ -22,6 +22,7 @@ impl Utils { edge: Edge(ratatui::widgets::Borders::ALL), r#type: ratatui::widgets::BorderType::Rounded, style: THEME.spot.border.get().into(), + merge: Default::default(), titles: vec![( ratatui::widgets::TitlePosition::Top, ratatui::text::Line::raw("Spot").centered().style(THEME.spot.title.get()), diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 6ca45327..88e4ef78 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -17,10 +17,12 @@ yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-core = { path = "../yazi-core", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" } +yazi-dds = { path = "../yazi-dds", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } # External dependencies -anyhow = { workspace = true } -tokio = { workspace = true } +anyhow = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } diff --git a/yazi-proxy/src/input.rs b/yazi-proxy/src/input.rs index 3e0b7270..b99fca5c 100644 --- a/yazi-proxy/src/input.rs +++ b/yazi-proxy/src/input.rs @@ -1,14 +1,21 @@ use tokio::sync::mpsc; use yazi_config::popup::InputCfg; -use yazi_macro::{emit, relay}; -use yazi_widgets::input::InputEvent; +use yazi_dds::Pubsub; +use yazi_macro::{emit, err, relay}; +use yazi_shim::strum::IntoStr; +use yazi_widgets::input::{InputCallback, InputEvent}; pub struct InputProxy; impl InputProxy { pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver { let (tx, rx) = mpsc::unbounded_channel(); - emit!(Call(relay!(input:show).with_any("tx", tx).with_any("cfg", cfg))); + let cb: Box = Box::new(move |event| { + err!(Pubsub::pub_after_input((&event).into_str(), event.value())); + tx.send(event).ok(); + }); + + emit!(Call(relay!(input:show).with_any("cb", cb).with_any("cfg", cfg))); rx } } diff --git a/yazi-widgets/Cargo.toml b/yazi-widgets/Cargo.toml index 0dd98c4c..69045cb7 100644 --- a/yazi-widgets/Cargo.toml +++ b/yazi-widgets/Cargo.toml @@ -27,11 +27,13 @@ yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies anyhow = { workspace = true } +dyn-clone = { workspace = true } futures = { workspace = true } mlua = { workspace = true } parking_lot = { workspace = true } ratatui = { workspace = true } serde = { workspace = true } +strum = { workspace = true } tokio = { workspace = true } unicode-width = { workspace = true } diff --git a/yazi-widgets/src/input/callback.rs b/yazi-widgets/src/input/callback.rs new file mode 100644 index 00000000..157124be --- /dev/null +++ b/yazi-widgets/src/input/callback.rs @@ -0,0 +1,22 @@ +use std::fmt; + +use dyn_clone::DynClone; +use yazi_macro::impl_data_any; + +use crate::input::InputEvent; + +pub trait InputCallback: Fn(InputEvent) + Send + Sync + DynClone + 'static {} + +impl InputCallback for T {} + +impl Clone for Box { + fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } +} + +impl fmt::Debug for dyn InputCallback { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InputCallback").finish_non_exhaustive() + } +} + +impl_data_any!(Box); diff --git a/yazi-widgets/src/input/event.rs b/yazi-widgets/src/input/event.rs index fc1fefb1..40781c70 100644 --- a/yazi-widgets/src/input/event.rs +++ b/yazi-widgets/src/input/event.rs @@ -1,6 +1,7 @@ +use strum::IntoStaticStr; use yazi_shared::Id; -#[derive(Debug)] +#[derive(Debug, IntoStaticStr)] pub enum InputEvent { Submit(String), Cancel(String), @@ -10,5 +11,11 @@ pub enum InputEvent { } impl InputEvent { + pub fn value(&self) -> &str { + match self { + Self::Submit(v) | Self::Cancel(v) | Self::Type(v) | Self::Trigger(v, _) => v.as_str(), + } + } + pub fn is_submit(&self) -> bool { matches!(self, Self::Submit(_)) } } diff --git a/yazi-widgets/src/input/input.rs b/yazi-widgets/src/input/input.rs index a9fcf6da..1f5ecb55 100644 --- a/yazi-widgets/src/input/input.rs +++ b/yazi-widgets/src/input/input.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, ops::Range}; use anyhow::Result; -use tokio::sync::mpsc; use yazi_config::{YAZI, popup::Position}; use yazi_macro::act; use yazi_shared::Ids; @@ -9,7 +8,7 @@ use yazi_shim::path::CROSS_SEPARATOR; use yazi_term::CursorStyle; use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp}; -use crate::{CLIPBOARD, input::{InputEvent, InputOpt}}; +use crate::{CLIPBOARD, input::{InputCallback, InputEvent, InputOpt}}; #[derive(Debug, Default)] pub struct Input { @@ -19,7 +18,7 @@ pub struct Input { pub realtime: bool, pub completion: bool, - pub tx: Option>, + pub cb: Option>, pub ticket: Ids, } @@ -33,7 +32,7 @@ impl Input { realtime: opt.cfg.realtime, completion: opt.cfg.completion, - tx: opt.tx, + cb: opt.cb, ..Default::default() }; @@ -93,20 +92,19 @@ impl Input { pub(super) fn flush_type(&mut self) { self.ticket.next(); - if let Some(tx) = self.tx.as_ref().filter(|_| self.realtime) { - tx.send(InputEvent::Type(self.value().to_owned())).ok(); + if let Some(cb) = self.cb.as_ref().filter(|_| self.realtime) { + cb(InputEvent::Type(self.value().to_owned())); } self.flush_trigger(true); } pub(super) fn flush_trigger(&self, force: bool) { - if let Some(tx) = self.tx.as_ref().filter(|_| self.completion) { - tx.send(InputEvent::Trigger( + if let Some(cb) = self.cb.as_ref().filter(|_| self.completion) { + cb(InputEvent::Trigger( self.partition().0.to_owned(), (!force).then_some(self.ticket.current()), - )) - .ok(); + )); } } } diff --git a/yazi-widgets/src/input/mod.rs b/yazi-widgets/src/input/mod.rs index 90766a7a..027cf6c3 100644 --- a/yazi-widgets/src/input/mod.rs +++ b/yazi-widgets/src/input/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(actor parser); -yazi_macro::mod_flat!(chars event gait input mode op option snap snaps widget); +yazi_macro::mod_flat!(callback chars event gait input mode op option snap snaps widget); diff --git a/yazi-widgets/src/input/option.rs b/yazi-widgets/src/input/option.rs index 5acfa986..4bc9b5fc 100644 --- a/yazi-widgets/src/input/option.rs +++ b/yazi-widgets/src/input/option.rs @@ -1,15 +1,14 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use tokio::sync::mpsc; use yazi_config::popup::InputCfg; use yazi_shared::event::ActionCow; -use crate::input::InputEvent; +use crate::input::InputCallback; #[derive(Debug)] pub struct InputOpt { pub cfg: InputCfg, - pub tx: Option>, + pub cb: Option>, } impl TryFrom for InputOpt { @@ -20,7 +19,7 @@ impl TryFrom for InputOpt { bail!("invalid 'cfg' in InputOpt"); }; - Ok(Self { cfg, tx: a.take_any("tx") }) + Ok(Self { cfg, cb: a.take_any("cb") }) } } From 446a5721bf5c607e67e92e44b461f7fd4c62f488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Thu, 18 Jun 2026 15:06:31 +0800 Subject: [PATCH 18/28] refactor: move Lua bindings down into each subcrates (#4049) --- Cargo.lock | 420 ++++++------------ Cargo.toml | 1 + yazi-actor/src/app/dnd.rs | 8 +- yazi-actor/src/app/lua.rs | 4 +- yazi-actor/src/app/mouse.rs | 20 +- yazi-actor/src/app/title.rs | 3 +- yazi-actor/src/context.rs | 4 +- yazi-actor/src/input/show.rs | 17 +- yazi-actor/src/lives/core.rs | 4 +- yazi-actor/src/lives/file.rs | 13 +- yazi-actor/src/lives/folder.rs | 5 +- yazi-actor/src/lives/lives.rs | 5 +- yazi-actor/src/lives/selected.rs | 3 +- yazi-actor/src/lives/tab.rs | 4 +- yazi-actor/src/lives/task.rs | 3 +- yazi-actor/src/lives/tasks.rs | 3 +- yazi-actor/src/lives/which.rs | 2 +- yazi-actor/src/lives/yanked.rs | 6 +- yazi-actor/src/mgr/bulk_create.rs | 12 +- yazi-actor/src/mgr/bulk_rename.rs | 12 +- yazi-actor/src/mgr/cd.rs | 6 +- yazi-actor/src/mgr/create.rs | 6 +- yazi-actor/src/mgr/download.rs | 2 +- yazi-actor/src/mgr/filter.rs | 4 +- yazi-actor/src/mgr/find.rs | 4 +- yazi-actor/src/mgr/open.rs | 2 +- yazi-actor/src/mgr/open_do.rs | 7 +- yazi-actor/src/mgr/rename.rs | 8 +- yazi-actor/src/mgr/reveal.rs | 2 +- yazi-actor/src/mgr/search.rs | 4 +- yazi-actor/src/mgr/shell.rs | 7 +- yazi-actor/src/mgr/tab_rename.rs | 4 +- yazi-actor/src/tasks/inspect.rs | 4 +- yazi-adapter/Cargo.toml | 5 +- yazi-adapter/src/adapter.rs | 123 ++--- yazi-adapter/src/adapters.rs | 62 --- yazi-adapter/src/drivers/chafa.rs | 14 +- yazi-adapter/src/drivers/driver.rs | 59 +++ yazi-adapter/src/drivers/drivers.rs | 94 ++++ yazi-adapter/src/drivers/iip.rs | 14 +- yazi-adapter/src/drivers/kgp.rs | 14 +- yazi-adapter/src/drivers/kgp_old.rs | 12 +- yazi-adapter/src/drivers/mod.rs | 2 +- yazi-adapter/src/drivers/sixel.rs | 14 +- yazi-adapter/src/drivers/ueberzug.rs | 30 +- yazi-adapter/src/icc.rs | 1 + yazi-adapter/src/info.rs | 43 -- yazi-adapter/src/lib.rs | 13 +- yazi-binding/Cargo.toml | 13 +- yazi-binding/src/access.rs | 42 -- yazi-binding/src/composer.rs | 29 +- yazi-binding/src/config/fetcher.rs | 74 --- yazi-binding/src/config/fetchers.rs | 17 - yazi-binding/src/config/mod.rs | 1 - yazi-binding/src/config/open_rule.rs | 84 ---- yazi-binding/src/config/open_rules.rs | 39 -- yazi-binding/src/config/opener.rs | 45 -- yazi-binding/src/config/opener_rule.rs | 79 ---- yazi-binding/src/config/opener_rules.rs | 57 --- yazi-binding/src/config/preloader.rs | 74 --- yazi-binding/src/config/preloaders.rs | 17 - yazi-binding/src/config/previewer.rs | 84 ---- yazi-binding/src/config/previewers.rs | 33 -- yazi-binding/src/config/spotter.rs | 74 --- yazi-binding/src/config/spotters.rs | 17 - yazi-binding/src/dnd.rs | 48 -- yazi-binding/src/elements/area.rs | 7 +- yazi-binding/src/elements/bar.rs | 9 - yazi-binding/src/elements/elements.rs | 38 -- yazi-binding/src/elements/fill.rs | 9 - yazi-binding/src/elements/gauge.rs | 2 +- yazi-binding/src/elements/input.rs | 117 ----- yazi-binding/src/elements/line.rs | 9 - yazi-binding/src/elements/list.rs | 9 - yazi-binding/src/elements/mod.rs | 2 +- yazi-binding/src/elements/pos.rs | 15 +- yazi-binding/src/elements/table.rs | 2 +- yazi-binding/src/elements/text.rs | 2 +- yazi-binding/src/elements/wrap.rs | 12 +- yazi-binding/src/error.rs | 8 +- yazi-binding/src/event/action.rs | 52 --- yazi-binding/src/event/actions.rs | 50 --- yazi-binding/src/event/cmd.rs | 34 -- yazi-binding/src/event/mod.rs | 1 - yazi-binding/src/file.rs | 78 ---- yazi-binding/src/icon.rs | 30 -- yazi-binding/src/image.rs | 52 ++- yazi-binding/src/keymap/chord.rs | 108 ----- yazi-binding/src/keymap/chords.rs | 69 --- yazi-binding/src/keymap/key.rs | 18 - yazi-binding/src/keymap/mod.rs | 1 - yazi-binding/src/keymap/section.rs | 49 -- yazi-binding/src/layer.rs | 15 - yazi-binding/src/lib.rs | 4 +- yazi-binding/src/macros.rs | 18 +- yazi-binding/src/mouse.rs | 37 -- yazi-binding/src/position/mod.rs | 1 + .../src/position}/offset.rs | 0 .../src/position}/origin.rs | 0 .../src/position}/position.rs | 5 +- yazi-binding/src/process/command.rs | 2 +- yazi-binding/src/scheme.rs | 33 -- yazi-binding/src/selector.rs | 21 - yazi-binding/src/stage.rs | 21 - yazi-binding/src/style/mod.rs | 1 + yazi-binding/src/{ => style}/style.rs | 19 +- .../src/style/style_flat.rs | 10 +- yazi-binding/src/theme/custom_field.rs | 18 - yazi-binding/src/theme/custom_section.rs | 28 -- yazi-binding/src/theme/mod.rs | 1 - yazi-boot/src/args.rs | 2 +- yazi-cli/src/args.rs | 2 +- yazi-cli/src/dds/exec.rs | 2 +- yazi-cli/src/dds/shot.rs | 2 +- yazi-cli/src/env/env.rs | 2 +- yazi-cli/src/package/dependency.rs | 2 +- yazi-codegen/Cargo.toml | 2 +- yazi-config/Cargo.toml | 4 +- yazi-config/src/icon.rs | 17 +- yazi-config/src/inject.rs | 15 + yazi-config/src/keymap/chord.rs | 36 +- yazi-config/src/keymap/chord_arc.rs | 48 +- yazi-config/src/keymap/chords.rs | 87 +++- yazi-config/src/keymap/ids.rs | 2 +- yazi-config/src/keymap/keymap.rs | 35 +- yazi-config/src/keymap/mod.rs | 2 +- yazi-config/src/keymap/section.rs | 18 +- yazi-config/src/lib.rs | 5 +- yazi-config/src/open/mod.rs | 2 +- yazi-config/src/open/open.rs | 14 +- .../src/open/{rule.rs => open_rule.rs} | 2 +- yazi-config/src/open/open_rule_arc.rs | 43 ++ yazi-config/src/open/open_rule_matcher.rs | 81 ++++ yazi-config/src/open/open_rules.rs | 124 ++++++ yazi-config/src/open/rules.rs | 133 ------ yazi-config/src/opener/mod.rs | 2 +- yazi-config/src/opener/opener.rs | 62 ++- .../src/opener/{rule.rs => opener_rule.rs} | 47 +- yazi-config/src/opener/opener_rule_arc.rs | 43 ++ yazi-config/src/opener/opener_rule_matcher.rs | 70 +++ yazi-config/src/opener/opener_rules.rs | 61 +++ yazi-config/src/opener/opener_rules_arc.rs | 61 +++ .../src/opener/opener_rules_matcher.rs | 33 ++ yazi-config/src/opener/rules.rs | 92 ---- yazi-config/src/plugin/fetcher.rs | 43 +- yazi-config/src/plugin/fetcher_arc.rs | 34 ++ yazi-config/src/plugin/fetchers.rs | 36 +- yazi-config/src/plugin/ids.rs | 2 +- yazi-config/src/plugin/mod.rs | 2 +- yazi-config/src/plugin/plugin.rs | 10 +- yazi-config/src/plugin/preloader.rs | 43 +- yazi-config/src/plugin/preloader_arc.rs | 34 ++ yazi-config/src/plugin/preloaders.rs | 33 +- yazi-config/src/plugin/previewer.rs | 43 +- yazi-config/src/plugin/previewer_arc.rs | 40 ++ yazi-config/src/plugin/previewers.rs | 48 +- yazi-config/src/plugin/spotter.rs | 43 +- yazi-config/src/plugin/spotter_arc.rs | 34 ++ yazi-config/src/plugin/spotters.rs | 33 +- yazi-config/src/popup/confirm.rs | 4 +- yazi-config/src/popup/input.rs | 80 +++- yazi-config/src/popup/mod.rs | 2 +- yazi-config/src/popup/options.rs | 125 +----- yazi-config/src/popup/pick.rs | 18 +- yazi-config/src/preview/preview.rs | 4 +- yazi-config/src/preview/wrap.rs | 9 + yazi-config/src/selectable.rs | 2 +- yazi-config/src/theme/custom_field.rs | 13 +- yazi-config/src/theme/custom_section_arc.rs | 27 ++ yazi-config/src/theme/filetype.rs | 7 +- yazi-config/src/theme/filetype_rule.rs | 6 +- yazi-config/src/theme/icon.rs | 2 +- yazi-config/src/theme/icon_cond.rs | 2 +- yazi-config/src/theme/icon_conds.rs | 2 +- yazi-config/src/theme/icon_globs.rs | 2 +- yazi-config/src/theme/mod.rs | 2 +- yazi-config/src/theme/theme.rs | 406 +++++++++-------- yazi-core/Cargo.toml | 1 + yazi-core/src/app/plugin.rs | 4 +- yazi-core/src/app/quit.rs | 2 +- yazi-core/src/cmp/cmp.rs | 2 +- yazi-core/src/cmp/option.rs | 2 +- yazi-core/src/confirm/confirm.rs | 2 +- yazi-core/src/core.rs | 6 +- yazi-core/src/help/help.rs | 22 +- yazi-core/src/highlighter.rs | 2 +- yazi-core/src/input/input.rs | 2 +- yazi-core/src/mgr/displace.rs | 2 +- yazi-core/src/mgr/filter.rs | 3 +- yazi-core/src/mgr/find.rs | 3 +- yazi-core/src/mgr/mgr.rs | 4 +- yazi-core/src/mgr/open.rs | 2 +- yazi-core/src/mgr/search.rs | 3 +- yazi-core/src/mgr/tabs.rs | 2 +- yazi-core/src/notify/level.rs | 5 +- yazi-core/src/notify/option.rs | 2 +- yazi-core/src/pick/pick.rs | 6 +- yazi-core/src/proxy.rs | 3 +- yazi-core/src/spot/lock.rs | 7 +- yazi-core/src/spot/spot.rs | 2 +- yazi-core/src/tab/folder.rs | 4 +- yazi-core/src/tab/preview.rs | 6 +- yazi-core/src/tab/preview_lock.rs | 4 +- yazi-core/src/tab/tab.rs | 13 +- yazi-core/src/tasks/option.rs | 3 +- yazi-core/src/tasks/prework.rs | 2 +- yazi-core/src/which/option.rs | 21 +- yazi-core/src/which/which.rs | 7 +- yazi-dds/src/client.rs | 2 +- yazi-dds/src/ember/bulk_rename.rs | 5 +- yazi-dds/src/ember/cd.rs | 4 +- yazi-dds/src/ember/custom.rs | 3 +- yazi-dds/src/ember/delete.rs | 3 +- yazi-dds/src/ember/download.rs | 3 +- yazi-dds/src/ember/duplicate.rs | 7 +- yazi-dds/src/ember/ember.rs | 2 +- yazi-dds/src/ember/hey.rs | 3 +- yazi-dds/src/ember/hi.rs | 2 +- yazi-dds/src/ember/hover.rs | 4 +- yazi-dds/src/ember/load.rs | 6 +- yazi-dds/src/ember/move.rs | 7 +- yazi-dds/src/ember/rename.rs | 6 +- yazi-dds/src/ember/tab.rs | 2 +- yazi-dds/src/ember/trash.rs | 3 +- yazi-dds/src/ember/yank.rs | 10 +- yazi-dds/src/lib.rs | 2 +- yazi-dds/src/payload.rs | 6 +- yazi-dds/src/pubsub.rs | 2 +- yazi-dds/src/server.rs | 2 +- yazi-emulator/src/emulator.rs | 4 +- yazi-emulator/src/mux.rs | 3 +- yazi-fm/Cargo.toml | 1 - yazi-fm/src/app/render.rs | 6 +- yazi-fm/src/app/sync_guard.rs | 14 +- yazi-fm/src/cmp/cmp.rs | 7 +- yazi-fm/src/confirm/confirm.rs | 2 +- yazi-fm/src/dispatcher.rs | 3 +- yazi-fm/src/help/help.rs | 2 +- yazi-fm/src/input/input.rs | 2 +- yazi-fm/src/logs.rs | 2 +- yazi-fm/src/notify/notify.rs | 2 +- yazi-fm/src/pick/pick.rs | 2 +- yazi-fm/src/renderer.rs | 2 +- yazi-fm/src/router.rs | 13 +- yazi-fm/src/tasks/tasks.rs | 2 +- yazi-fm/src/which/which.rs | 2 +- yazi-fs/Cargo.toml | 10 +- yazi-fs/src/cha/cha.rs | 3 +- .../src/cha.rs => yazi-fs/src/cha/lua.rs | 19 +- yazi-fs/src/cha/mod.rs | 2 +- yazi-fs/src/{ => file}/file.rs | 0 yazi-fs/src/file/inventory.rs | 9 + yazi-fs/src/file/lua.rs | 51 +++ yazi-fs/src/file/mod.rs | 1 + yazi-fs/src/files.rs | 4 +- yazi-fs/src/hash.rs | 20 +- yazi-fs/src/lib.rs | 6 +- yazi-fs/src/op.rs | 7 +- yazi-fs/src/path/expand.rs | 3 +- yazi-fs/src/scheme.rs | 12 +- yazi-fs/src/sorter.rs | 2 +- yazi-fs/src/stage.rs | 20 +- yazi-fs/src/url.rs | 12 +- yazi-macro/src/input.rs | 6 +- yazi-parser/Cargo.toml | 1 + yazi-parser/src/app/deprecate.rs | 3 +- yazi-parser/src/app/lua.rs | 3 +- yazi-parser/src/app/quit.rs | 2 +- yazi-parser/src/app/title.rs | 3 +- yazi-parser/src/cmp/trigger.rs | 3 +- yazi-parser/src/input/close.rs | 2 +- yazi-parser/src/input/mod.rs | 2 +- yazi-parser/src/input/show.rs | 29 ++ yazi-parser/src/mgr/close.rs | 2 +- yazi-parser/src/mgr/copy.rs | 3 +- yazi-parser/src/mgr/hidden.rs | 2 +- yazi-parser/src/mgr/rename.rs | 3 +- yazi-parser/src/mgr/shell.rs | 3 +- yazi-parser/src/mgr/sort.rs | 2 +- yazi-parser/src/mgr/stash.rs | 10 +- yazi-parser/src/mgr/tab_rename.rs | 3 +- yazi-parser/src/mgr/update_mimes.rs | 3 +- yazi-parser/src/spark/spark.rs | 4 +- yazi-parser/src/spot/copy.rs | 3 +- yazi-parser/src/tasks/update_succeed.rs | 2 +- yazi-parser/src/which/activate.rs | 6 +- yazi-plugin/Cargo.toml | 1 + yazi-plugin/src/elements/mod.rs | 1 - yazi-plugin/src/external/fd.rs | 2 +- yazi-plugin/src/external/rg.rs | 2 +- yazi-plugin/src/external/rga.rs | 2 +- .../src => yazi-plugin/src/fs}/calculator.rs | 7 +- yazi-plugin/src/fs/fs.rs | 30 +- yazi-plugin/src/fs/mod.rs | 2 +- yazi-plugin/src/fs/op.rs | 33 +- yazi-plugin/src/keymap/keymap.rs | 19 +- yazi-plugin/src/lib.rs | 2 +- yazi-plugin/src/pubsub/pubsub.rs | 5 +- yazi-plugin/src/runtime/plugin.rs | 11 +- yazi-plugin/src/runtime/runtime.rs | 17 +- yazi-plugin/src/slim.rs | 10 +- yazi-plugin/src/standard.rs | 10 +- yazi-plugin/src/tasks/task.rs | 4 +- yazi-plugin/src/theme/theme.rs | 12 +- yazi-plugin/src/ui/mod.rs | 1 + yazi-plugin/src/ui/ui.rs | 50 +++ .../src/{elements/elements.rs => ui/utils.rs} | 23 +- yazi-plugin/src/utils/app.rs | 8 +- yazi-plugin/src/utils/cache.rs | 6 +- yazi-plugin/src/utils/call.rs | 3 +- yazi-plugin/src/utils/image.rs | 10 +- yazi-plugin/src/utils/json.rs | 3 +- yazi-plugin/src/utils/layer.rs | 31 +- yazi-plugin/src/utils/log.rs | 13 +- yazi-plugin/src/utils/preview.rs | 3 +- yazi-plugin/src/utils/spot.rs | 3 +- yazi-plugin/src/utils/sync.rs | 4 +- yazi-proxy/src/cmp.rs | 2 +- yazi-proxy/src/input.rs | 9 +- yazi-proxy/src/mgr.rs | 4 +- yazi-proxy/src/tasks.rs | 2 +- yazi-proxy/src/which.rs | 2 +- yazi-runner/src/entry/job.rs | 4 +- yazi-runner/src/fetcher/job.rs | 12 +- yazi-runner/src/preloader/job.rs | 14 +- yazi-runner/src/previewer/job.rs | 19 +- yazi-runner/src/spot.rs | 16 +- yazi-scheduler/Cargo.toml | 1 + yazi-scheduler/src/behavior.rs | 2 +- yazi-scheduler/src/fetch/in.rs | 10 +- yazi-scheduler/src/file/in.rs | 8 +- yazi-scheduler/src/hook/in.rs | 2 +- yazi-scheduler/src/in.rs | 3 +- yazi-scheduler/src/ongoing.rs | 2 +- yazi-scheduler/src/op.rs | 2 +- yazi-scheduler/src/plugin/in.rs | 4 +- yazi-scheduler/src/preload/in.rs | 10 +- yazi-scheduler/src/preload/preload.rs | 3 +- yazi-scheduler/src/process/in.rs | 2 +- yazi-scheduler/src/proxy.rs | 3 +- yazi-scheduler/src/scheduler.rs | 14 +- yazi-scheduler/src/size/in.rs | 2 +- yazi-scheduler/src/task.rs | 2 +- yazi-shared/Cargo.toml | 7 +- yazi-shared/src/data/any.rs | 4 +- yazi-shared/src/data/data.rs | 7 +- yazi-shared/src/data/key.rs | 5 +- yazi-shared/src/data/macros.rs | 6 +- yazi-shared/src/data/mod.rs | 2 +- .../src => yazi-shared/src/data}/sendable.rs | 39 +- yazi-shared/src/event/action.rs | 24 +- yazi-shared/src/event/actions.rs | 19 +- yazi-shared/src/event/cmd.rs | 14 +- yazi-shared/src/{ => id}/id.rs | 32 +- yazi-shared/src/id/ids.rs | 30 ++ .../src/id.rs => yazi-shared/src/id/lua.rs | 15 +- yazi-shared/src/id/mod.rs | 1 + yazi-shared/src/layer.rs | 8 + yazi-shared/src/lib.rs | 4 +- yazi-shared/src/path/buf.rs | 6 +- .../path.rs => yazi-shared/src/path/lua.rs | 47 +- yazi-shared/src/path/mod.rs | 2 +- yazi-shared/src/scheme/inventory.rs | 9 + yazi-shared/src/scheme/lua.rs | 20 + yazi-shared/src/scheme/mod.rs | 2 +- yazi-shared/src/shell/windows.rs | 2 +- yazi-shared/src/strand/buf.rs | 3 +- yazi-shared/src/strand/strand.rs | 3 +- yazi-shared/src/url/buf.rs | 3 +- yazi-shared/src/url/inventory.rs | 9 + .../src/url.rs => yazi-shared/src/url/lua.rs | 111 ++--- yazi-shared/src/url/mod.rs | 2 +- yazi-shim/Cargo.toml | 1 + .../src/error => yazi-shim/src/fs}/error.rs | 2 +- .../src/error => yazi-shim/src/fs}/mod.rs | 0 .../src/error => yazi-shim/src/fs}/serde.rs | 0 yazi-shim/src/lib.rs | 4 +- .../src/mlua/metatable.rs | 5 +- yazi-shim/src/mlua/mod.rs | 2 +- yazi-shim/src/mlua/ser.rs | 4 + .../src/alias.rs => yazi-shim/src/sstr.rs | 0 {yazi-shared => yazi-shim}/src/wtf8/mod.rs | 0 .../src/wtf8/validator.rs | 0 {yazi-shared => yazi-shim}/src/wtf8/wtf8.rs | 0 yazi-term/Cargo.toml | 2 +- yazi-term/src/dimension.rs | 2 + .../src/event/conversion.rs | 76 +--- yazi-term/src/event/keyboard.rs | 20 +- yazi-term/src/event/lua.rs | 62 +++ yazi-term/src/event/mod.rs | 2 +- yazi-term/src/event/modifiers.rs | 3 +- yazi-term/src/lib.rs | 4 +- yazi-term/src/style.rs | 11 - yazi-tty/Cargo.toml | 9 +- yazi-tty/src/lib.rs | 4 +- .../src/tty.rs => yazi-tty/src/lua.rs | 17 +- .../src/sequence/clipboard.rs | 0 {yazi-term => yazi-tty}/src/sequence/csi_u.rs | 0 .../src/sequence/cursor.rs | 35 +- {yazi-term => yazi-tty}/src/sequence/dnd.rs | 0 {yazi-term => yazi-tty}/src/sequence/erase.rs | 0 {yazi-term => yazi-tty}/src/sequence/if.rs | 0 {yazi-term => yazi-tty}/src/sequence/mod.rs | 0 {yazi-term => yazi-tty}/src/sequence/mode.rs | 0 {yazi-term => yazi-tty}/src/sequence/query.rs | 0 {yazi-term => yazi-tty}/src/sequence/style.rs | 0 {yazi-term => yazi-tty}/src/sequence/sync.rs | 0 .../src/sequence/traits.rs | 0 yazi-tui/src/backend.rs | 3 +- yazi-tui/src/raterm.rs | 4 +- yazi-vfs/Cargo.toml | 14 +- yazi-vfs/src/file.rs | 2 +- yazi-vfs/src/files.rs | 2 +- yazi-vfs/src/op.rs | 4 +- yazi-vfs/src/provider/gate.rs | 39 +- .../src/fd.rs => yazi-vfs/src/provider/lua.rs | 13 +- yazi-vfs/src/provider/mod.rs | 2 +- yazi-watcher/src/local/local.rs | 2 +- yazi-watcher/src/remote/remote.rs | 2 +- yazi-widgets/Cargo.toml | 6 +- yazi-widgets/src/clear.rs | 46 -- .../src/clear}/clear.rs | 29 +- yazi-widgets/src/clear/inventory.rs | 7 + yazi-widgets/src/clear/mod.rs | 1 + yazi-widgets/src/clipboard.rs | 3 +- yazi-widgets/src/input/actor/type.rs | 4 +- yazi-widgets/src/input/event.rs | 2 +- yazi-widgets/src/input/input.rs | 40 +- yazi-widgets/src/input/input_arc.rs | 86 ++++ yazi-widgets/src/input/mod.rs | 2 +- yazi-widgets/src/input/option.rs | 74 ++- yazi-widgets/src/input/parser/complete.rs | 2 +- yazi-widgets/src/input/parser/kill.rs | 3 +- .../src/input/stream.rs | 9 +- yazi-widgets/src/input/styles.rs | 29 ++ yazi-widgets/src/input/widget.rs | 12 +- yazi-widgets/src/lib.rs | 4 +- .../src}/renderable.rs | 12 +- .../src}/renderables.rs | 2 +- 439 files changed, 3465 insertions(+), 4058 deletions(-) delete mode 100644 yazi-adapter/src/adapters.rs create mode 100644 yazi-adapter/src/drivers/driver.rs create mode 100644 yazi-adapter/src/drivers/drivers.rs delete mode 100644 yazi-adapter/src/info.rs delete mode 100644 yazi-binding/src/access.rs delete mode 100644 yazi-binding/src/config/fetcher.rs delete mode 100644 yazi-binding/src/config/fetchers.rs delete mode 100644 yazi-binding/src/config/mod.rs delete mode 100644 yazi-binding/src/config/open_rule.rs delete mode 100644 yazi-binding/src/config/open_rules.rs delete mode 100644 yazi-binding/src/config/opener.rs delete mode 100644 yazi-binding/src/config/opener_rule.rs delete mode 100644 yazi-binding/src/config/opener_rules.rs delete mode 100644 yazi-binding/src/config/preloader.rs delete mode 100644 yazi-binding/src/config/preloaders.rs delete mode 100644 yazi-binding/src/config/previewer.rs delete mode 100644 yazi-binding/src/config/previewers.rs delete mode 100644 yazi-binding/src/config/spotter.rs delete mode 100644 yazi-binding/src/config/spotters.rs delete mode 100644 yazi-binding/src/dnd.rs delete mode 100644 yazi-binding/src/elements/elements.rs delete mode 100644 yazi-binding/src/elements/input.rs delete mode 100644 yazi-binding/src/event/action.rs delete mode 100644 yazi-binding/src/event/actions.rs delete mode 100644 yazi-binding/src/event/cmd.rs delete mode 100644 yazi-binding/src/event/mod.rs delete mode 100644 yazi-binding/src/file.rs delete mode 100644 yazi-binding/src/icon.rs delete mode 100644 yazi-binding/src/keymap/chord.rs delete mode 100644 yazi-binding/src/keymap/chords.rs delete mode 100644 yazi-binding/src/keymap/key.rs delete mode 100644 yazi-binding/src/keymap/mod.rs delete mode 100644 yazi-binding/src/keymap/section.rs delete mode 100644 yazi-binding/src/layer.rs delete mode 100644 yazi-binding/src/mouse.rs create mode 100644 yazi-binding/src/position/mod.rs rename {yazi-config/src/popup => yazi-binding/src/position}/offset.rs (100%) rename {yazi-config/src/popup => yazi-binding/src/position}/origin.rs (100%) rename {yazi-config/src/popup => yazi-binding/src/position}/position.rs (93%) delete mode 100644 yazi-binding/src/scheme.rs delete mode 100644 yazi-binding/src/selector.rs delete mode 100644 yazi-binding/src/stage.rs create mode 100644 yazi-binding/src/style/mod.rs rename yazi-binding/src/{ => style}/style.rs (89%) rename yazi-config/src/style.rs => yazi-binding/src/style/style_flat.rs (94%) delete mode 100644 yazi-binding/src/theme/custom_field.rs delete mode 100644 yazi-binding/src/theme/custom_section.rs delete mode 100644 yazi-binding/src/theme/mod.rs create mode 100644 yazi-config/src/inject.rs rename yazi-config/src/open/{rule.rs => open_rule.rs} (97%) create mode 100644 yazi-config/src/open/open_rule_arc.rs create mode 100644 yazi-config/src/open/open_rule_matcher.rs create mode 100644 yazi-config/src/open/open_rules.rs delete mode 100644 yazi-config/src/open/rules.rs rename yazi-config/src/opener/{rule.rs => opener_rule.rs} (50%) create mode 100644 yazi-config/src/opener/opener_rule_arc.rs create mode 100644 yazi-config/src/opener/opener_rule_matcher.rs create mode 100644 yazi-config/src/opener/opener_rules.rs create mode 100644 yazi-config/src/opener/opener_rules_arc.rs create mode 100644 yazi-config/src/opener/opener_rules_matcher.rs delete mode 100644 yazi-config/src/opener/rules.rs create mode 100644 yazi-config/src/plugin/fetcher_arc.rs create mode 100644 yazi-config/src/plugin/preloader_arc.rs create mode 100644 yazi-config/src/plugin/previewer_arc.rs create mode 100644 yazi-config/src/plugin/spotter_arc.rs create mode 100644 yazi-config/src/theme/custom_section_arc.rs rename yazi-binding/src/cha.rs => yazi-fs/src/cha/lua.rs (86%) rename yazi-fs/src/{ => file}/file.rs (100%) create mode 100644 yazi-fs/src/file/inventory.rs create mode 100644 yazi-fs/src/file/lua.rs create mode 100644 yazi-fs/src/file/mod.rs create mode 100644 yazi-parser/src/input/show.rs delete mode 100644 yazi-plugin/src/elements/mod.rs rename {yazi-binding/src => yazi-plugin/src/fs}/calculator.rs (93%) create mode 100644 yazi-plugin/src/ui/mod.rs create mode 100644 yazi-plugin/src/ui/ui.rs rename yazi-plugin/src/{elements/elements.rs => ui/utils.rs} (92%) rename {yazi-binding/src => yazi-shared/src/data}/sendable.rs (86%) rename yazi-shared/src/{ => id}/id.rs (63%) create mode 100644 yazi-shared/src/id/ids.rs rename yazi-binding/src/id.rs => yazi-shared/src/id/lua.rs (55%) create mode 100644 yazi-shared/src/id/mod.rs rename yazi-binding/src/path.rs => yazi-shared/src/path/lua.rs (76%) create mode 100644 yazi-shared/src/scheme/inventory.rs create mode 100644 yazi-shared/src/scheme/lua.rs create mode 100644 yazi-shared/src/url/inventory.rs rename yazi-binding/src/url.rs => yazi-shared/src/url/lua.rs (62%) rename {yazi-fs/src/error => yazi-shim/src/fs}/error.rs (97%) rename {yazi-fs/src/error => yazi-shim/src/fs}/mod.rs (100%) rename {yazi-fs/src/error => yazi-shim/src/fs}/serde.rs (100%) rename yazi-binding/src/utils.rs => yazi-shim/src/mlua/metatable.rs (55%) create mode 100644 yazi-shim/src/mlua/ser.rs rename yazi-shared/src/alias.rs => yazi-shim/src/sstr.rs (100%) rename {yazi-shared => yazi-shim}/src/wtf8/mod.rs (100%) rename {yazi-shared => yazi-shim}/src/wtf8/validator.rs (100%) rename {yazi-shared => yazi-shim}/src/wtf8/wtf8.rs (100%) rename yazi-config/src/keymap/key.rs => yazi-term/src/event/conversion.rs (68%) create mode 100644 yazi-term/src/event/lua.rs delete mode 100644 yazi-term/src/style.rs rename yazi-binding/src/tty.rs => yazi-tty/src/lua.rs (81%) rename {yazi-term => yazi-tty}/src/sequence/clipboard.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/csi_u.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/cursor.rs (73%) rename {yazi-term => yazi-tty}/src/sequence/dnd.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/erase.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/if.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/mod.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/mode.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/query.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/style.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/sync.rs (100%) rename {yazi-term => yazi-tty}/src/sequence/traits.rs (100%) rename yazi-binding/src/fd.rs => yazi-vfs/src/provider/lua.rs (80%) delete mode 100644 yazi-widgets/src/clear.rs rename {yazi-binding/src/elements => yazi-widgets/src/clear}/clear.rs (60%) create mode 100644 yazi-widgets/src/clear/inventory.rs create mode 100644 yazi-widgets/src/clear/mod.rs create mode 100644 yazi-widgets/src/input/input_arc.rs rename yazi-binding/src/input.rs => yazi-widgets/src/input/stream.rs (84%) create mode 100644 yazi-widgets/src/input/styles.rs rename {yazi-binding/src/elements => yazi-widgets/src}/renderable.rs (92%) rename {yazi-binding/src/elements => yazi-widgets/src}/renderables.rs (95%) diff --git a/Cargo.lock b/Cargo.lock index ac4b38ba..571993bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.6.0-rc.10" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" +checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99" dependencies = [ "crypto-common 0.2.2", "inout", @@ -198,7 +198,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -435,9 +435,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", "zeroize", @@ -464,9 +464,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.2" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f04f6fef12d70d42a77b1433c9e0f065238479a6cefc4f5bab105e9873a3c3" +checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" dependencies = [ "bon-macros", "rustversion", @@ -474,9 +474,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.9.2" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d0bd4c2f75335ad98052a37efb54f428b492f64340257143b3429c8a508fa7b" +checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" dependencies = [ "darling", "ident_case", @@ -484,7 +484,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -541,7 +541,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -582,9 +582,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.63" +version = "1.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", "jobserver", @@ -637,7 +637,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "crypto-common 0.2.2", "inout", "zeroize", @@ -703,7 +703,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -922,13 +922,13 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" +checksum = "97bb4a855e3b10f84c4e7e895a7de01db7f9a7b7eb7f73ed9773fd52ac686451" dependencies = [ "cpubits", "ctutils", - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "num-traits", "rand_core 0.10.1", @@ -953,19 +953,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "rand_core 0.10.1", ] [[package]] name = "crypto-primes" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" dependencies = [ "crypto-bigint", - "libm", "rand_core 0.10.1", ] @@ -1022,7 +1021,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1045,7 +1044,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1056,7 +1055,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1093,7 +1092,7 @@ checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1119,7 +1118,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -1142,7 +1140,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1170,7 +1168,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "const-oid", "crypto-common 0.2.2", "ctutils", @@ -1297,7 +1295,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1317,7 +1315,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1495,12 +1493,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foldhash" version = "0.2.0" @@ -1578,7 +1570,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1656,17 +1648,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", "wasm-bindgen", ] @@ -1736,15 +1726,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - [[package]] name = "hashbrown" version = "0.16.1" @@ -1753,7 +1734,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.2.0", + "foldhash", ] [[package]] @@ -1764,7 +1745,7 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.2.0", + "foldhash", "serde", "serde_core", ] @@ -1847,12 +1828,6 @@ dependencies = [ "cc", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1970,7 +1945,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1993,7 +1968,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2038,13 +2013,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -2111,12 +2085,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lebe" version = "0.5.3" @@ -2378,7 +2346,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2527,7 +2495,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2771,7 +2739,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2879,7 +2847,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2942,7 +2910,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3080,14 +3048,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "primefield" -version = "0.14.0-rc.10" +version = "0.14.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f845ec3240cd5ed5e1e31cf3ff633a5bf47c698dc4092ba9e767415b3d393406" +checksum = "8675564771a62f69a0af716b03e89b917b963c7b173b5855575e84fd4f605ca0" dependencies = [ "crypto-bigint", "crypto-common 0.2.2", @@ -3125,7 +3093,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3153,7 +3121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3256,7 +3224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -3486,7 +3454,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3593,7 +3561,7 @@ dependencies = [ "enum_dispatch", "futures", "generic-array 1.4.3", - "getrandom 0.4.2", + "getrandom 0.4.3", "ghash", "hex-literal", "hmac", @@ -3848,7 +3816,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3902,7 +3870,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4071,9 +4039,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" @@ -4192,7 +4160,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4220,9 +4188,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4262,7 +4230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -4369,7 +4337,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4380,7 +4348,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4428,12 +4396,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -4445,15 +4412,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" dependencies = [ "num-conv", "time-core", @@ -4499,7 +4466,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4597,7 +4564,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4730,12 +4697,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "universal-hash" version = "0.6.1" @@ -4766,12 +4727,12 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.2" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ "atomic", - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -4876,27 +4837,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -4907,9 +4859,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -4917,9 +4869,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4927,60 +4879,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.0", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver", -] - [[package]] name = "weezl" version = "0.1.12" @@ -5063,9 +4981,9 @@ dependencies = [ [[package]] name = "which" -version = "8.0.3" +version = "8.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c789537cf2f7f55be8e6192f92e464174ee55f91af622777f7f1ceb0dbccd03e" +checksum = "48d7cd18d4acb58fb3cdfe9ea54e6cd96a4e7d4cc45c56338b236e82dad47248" dependencies = [ "libc", ] @@ -5186,7 +5104,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5197,7 +5115,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5208,7 +5126,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5219,7 +5137,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5445,100 +5363,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.0", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "wyz" version = "0.5.1" @@ -5603,6 +5433,7 @@ dependencies = [ "anyhow", "base64", "image", + "inventory", "moxcms", "palette", "quantette", @@ -5616,8 +5447,8 @@ dependencies = [ "yazi-macro", "yazi-shared", "yazi-shim", - "yazi-term", "yazi-tty", + "yazi-widgets", ] [[package]] @@ -5628,31 +5459,22 @@ dependencies = [ "anyhow", "futures", "hashbrown 0.17.1", + "image", "inventory", "libc", "mlua", "ordered-float 5.3.0", - "parking_lot", - "paste", "ratatui", "serde", "serde_json", + "strum", "tokio", - "tokio-stream", "tracing", "unicode-width", "windows-sys 0.61.2", - "yazi-adapter", "yazi-codegen", - "yazi-config", - "yazi-fs", "yazi-macro", - "yazi-shared", "yazi-shim", - "yazi-term", - "yazi-tty", - "yazi-vfs", - "yazi-widgets", ] [[package]] @@ -5716,7 +5538,7 @@ version = "26.5.6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5729,15 +5551,16 @@ dependencies = [ "globset", "hashbrown 0.17.1", "indexmap 2.14.0", + "inventory", "mlua", "ratatui", "regex", "serde", "serde_with", - "strum", "tokio", "toml", "tracing", + "yazi-binding", "yazi-codegen", "yazi-fs", "yazi-macro", @@ -5745,6 +5568,7 @@ dependencies = [ "yazi-shim", "yazi-term", "yazi-tty", + "yazi-widgets", ] [[package]] @@ -5779,6 +5603,7 @@ dependencies = [ "yazi-shared", "yazi-shim", "yazi-term", + "yazi-tty", "yazi-vfs", "yazi-watcher", "yazi-widgets", @@ -5849,7 +5674,6 @@ dependencies = [ "mlua", "paste", "ratatui", - "scopeguard", "signal-hook-tokio", "tikv-jemallocator", "tokio", @@ -5891,8 +5715,9 @@ dependencies = [ "core-foundation-sys", "dirs", "either", - "foldhash 0.2.0", + "foldhash", "hashbrown 0.17.1", + "inventory", "libc", "mlua", "objc2", @@ -5909,6 +5734,7 @@ dependencies = [ "typed-path", "uzers", "windows-sys 0.61.2", + "yazi-binding", "yazi-ffi", "yazi-macro", "yazi-shared", @@ -5946,6 +5772,7 @@ dependencies = [ "yazi-macro", "yazi-scheduler", "yazi-shared", + "yazi-shim", "yazi-term", "yazi-vfs", "yazi-widgets", @@ -5986,6 +5813,7 @@ dependencies = [ "yazi-shared", "yazi-shim", "yazi-term", + "yazi-tty", "yazi-vfs", "yazi-widgets", ] @@ -6040,7 +5868,7 @@ version = "26.5.6" dependencies = [ "anyhow", "async-priority-channel", - "foldhash 0.2.0", + "foldhash", "hashbrown 0.17.1", "libc", "lru", @@ -6058,6 +5886,7 @@ dependencies = [ "yazi-macro", "yazi-runner", "yazi-shared", + "yazi-shim", "yazi-term", "yazi-vfs", ] @@ -6080,7 +5909,7 @@ version = "26.5.6" dependencies = [ "anyhow", "dyn-clone", - "foldhash 0.2.0", + "foldhash", "futures", "hashbrown 0.17.1", "inventory", @@ -6099,6 +5928,7 @@ dependencies = [ "typed-path", "uzers", "windows-sys 0.61.2", + "yazi-codegen", "yazi-macro", "yazi-shim", "yazi-term", @@ -6108,6 +5938,7 @@ dependencies = [ name = "yazi-shim" version = "26.5.6" dependencies = [ + "anyhow", "arc-swap", "base64", "hashbrown 0.17.1", @@ -6131,8 +5962,8 @@ dependencies = [ "base64", "bitflags 2.13.0", "futures", + "mlua", "parking_lot", - "ratatui", "rustix 1.1.4", "serde", "signal-hook 0.4.4", @@ -6149,10 +5980,15 @@ dependencies = [ name = "yazi-tty" version = "26.5.9" dependencies = [ + "base64", + "bitflags 2.13.0", "libc", + "mlua", "parking_lot", + "ratatui", "tracing", "windows-sys 0.61.2", + "yazi-binding", "yazi-macro", "yazi-shim", ] @@ -6190,11 +6026,13 @@ dependencies = [ "either", "futures", "hashbrown 0.17.1", + "mlua", "parking_lot", "russh", "tokio", "tracing", "typed-path", + "yazi-binding", "yazi-config", "yazi-fs", "yazi-macro", @@ -6232,15 +6070,17 @@ dependencies = [ "clipboard-win", "dyn-clone", "futures", + "inventory", "mlua", "parking_lot", "ratatui", "serde", "strum", "tokio", + "tokio-stream", + "tracing", "unicode-width", - "yazi-adapter", - "yazi-config", + "yazi-binding", "yazi-macro", "yazi-shared", "yazi-shim", @@ -6250,29 +6090,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" diff --git a/Cargo.toml b/Cargo.toml index 451b5a5e..f107575a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ foldhash = "0.2.0" futures = "0.3.32" globset = "0.4.18" hashbrown = { version = "0.17.1", features = [ "serde" ] } +image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] } indexmap = { version = "2.14.0", features = [ "serde" ] } inventory = "0.3.24" libc = "0.2.186" diff --git a/yazi-actor/src/app/dnd.rs b/yazi-actor/src/app/dnd.rs index 014aa552..576418d8 100644 --- a/yazi-actor/src/app/dnd.rs +++ b/yazi-actor/src/app/dnd.rs @@ -18,8 +18,6 @@ impl Actor for Dnd { const NAME: &str = "dnd"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let event = yazi_binding::DndEvent::from(form.event); - let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; let area = yazi_binding::elements::Rect::from(size); @@ -27,10 +25,10 @@ impl Actor for Dnd { runtime_scope!(LUA, "root", { let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; - if event.is_drag() { - root.call_method::<()>("drag", event)?; + if form.event.is_drag() { + root.call_method::<()>("drag", form.event)?; } else { - root.call_method::<()>("drop", event)?; + root.call_method::<()>("drop", form.event)?; } Ok(()) diff --git a/yazi-actor/src/app/lua.rs b/yazi-actor/src/app/lua.rs index 73e4e181..fedf031e 100644 --- a/yazi-actor/src/app/lua.rs +++ b/yazi-actor/src/app/lua.rs @@ -1,9 +1,9 @@ use anyhow::Result; -use yazi_binding::{Sendable, runtime_scope}; +use yazi_binding::runtime_scope; use yazi_macro::succ; use yazi_parser::app::LuaForm; use yazi_plugin::LUA; -use yazi_shared::data::Data; +use yazi_shared::data::{Data, Sendable}; use crate::{Actor, Ctx, lives::Lives}; diff --git a/yazi-actor/src/app/mouse.rs b/yazi-actor/src/app/mouse.rs index 8ecdbf10..7eaecb61 100644 --- a/yazi-actor/src/app/mouse.rs +++ b/yazi-actor/src/app/mouse.rs @@ -19,8 +19,6 @@ impl Actor for Mouse { const NAME: &str = "mouse"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let event = yazi_binding::MouseEvent::from(form.event); - let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() }; let area = yazi_binding::elements::Rect::from(size); @@ -28,18 +26,18 @@ impl Actor for Mouse { runtime_scope!(LUA, "root", { let root = LUA.globals().raw_get::
("Root")?.call_method::
("new", area)?; - match event.kind { - MouseEventKind::Down(_) => root.call_method("click", (event, false))?, - MouseEventKind::Up(_) => root.call_method("click", (event, true))?, + match form.event.kind { + MouseEventKind::Down(_) => root.call_method("click", (form.event, false))?, + MouseEventKind::Up(_) => root.call_method("click", (form.event, true))?, - MouseEventKind::ScrollDown => root.call_method("scroll", (event, 1))?, - MouseEventKind::ScrollUp => root.call_method("scroll", (event, -1))?, + MouseEventKind::ScrollDown => root.call_method("scroll", (form.event, 1))?, + MouseEventKind::ScrollUp => root.call_method("scroll", (form.event, -1))?, - MouseEventKind::ScrollRight => root.call_method("touch", (event, 1))?, - MouseEventKind::ScrollLeft => root.call_method("touch", (event, -1))?, + MouseEventKind::ScrollRight => root.call_method("touch", (form.event, 1))?, + MouseEventKind::ScrollLeft => root.call_method("touch", (form.event, -1))?, - MouseEventKind::Moved => root.call_method("move", event)?, - MouseEventKind::Drag(_) => root.call_method("drag", event)?, + MouseEventKind::Moved => root.call_method("move", form.event)?, + MouseEventKind::Drag(_) => root.call_method("drag", form.event)?, } Ok(()) diff --git a/yazi-actor/src/app/title.rs b/yazi-actor/src/app/title.rs index e6011789..7b19d9e2 100644 --- a/yazi-actor/src/app/title.rs +++ b/yazi-actor/src/app/title.rs @@ -3,8 +3,7 @@ use yazi_actor::Ctx; use yazi_macro::{succ, writef}; use yazi_parser::{app::TitleForm, spark::SparkKind}; use yazi_shared::{Source, data::Data}; -use yazi_term::sequence::SetTitle; -use yazi_tty::TTY; +use yazi_tty::{TTY, sequence::SetTitle}; use yazi_tui::RatermState; use crate::Actor; diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs index 1ad02d79..fbdc2863 100644 --- a/yazi-actor/src/context.rs +++ b/yazi-actor/src/context.rs @@ -2,8 +2,8 @@ use std::ops::{Deref, DerefMut}; use anyhow::{Result, anyhow}; use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}}; -use yazi_fs::File; -use yazi_shared::{Id, Source, event::Action, url::UrlBuf}; +use yazi_fs::file::File; +use yazi_shared::{Source, event::Action, id::Id, url::UrlBuf}; use yazi_tui::Raterm; pub struct Ctx<'a> { diff --git a/yazi-actor/src/input/show.rs b/yazi-actor/src/input/show.rs index a9cb9a9c..00517668 100644 --- a/yazi-actor/src/input/show.rs +++ b/yazi-actor/src/input/show.rs @@ -1,28 +1,31 @@ use anyhow::Result; use ratatui::widgets::Padding; +use yazi_config::{THEME, YAZI}; use yazi_macro::{act, render, succ}; +use yazi_parser::input::ShowForm; use yazi_shared::data::Data; -use yazi_widgets::input::InputOpt; use crate::{Actor, Ctx}; pub struct Show; impl Actor for Show { - type Form = InputOpt; + type Form = ShowForm; const NAME: &str = "show"; - fn act(cx: &mut Ctx, mut form: Self::Form) -> Result { + fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result { act!(input:close, cx)?; let input = &mut cx.input; input.main_visible = true; - input.main_title = form.cfg.title.clone(); - input.main_position = form.cfg.position; + input.main_title = opt.title.clone(); + input.main_position = opt.position; - form.cfg.position = form.cfg.position.padding(Padding::uniform(1)); - input.main = yazi_widgets::input::Input::new(form)?; + opt.styles = (&THEME.input).into(); + opt.blinking = YAZI.input.cursor_blink; + opt.position = opt.position.padding(Padding::uniform(1)); + input.main = yazi_widgets::input::Input::new(opt)?; succ!(render!()); } diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index c3ea731f..666a219b 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -63,9 +63,7 @@ impl UserData for Core { b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)), b"input" => reuse!(input, super::Input::make(&me.input)), b"which" => reuse!(which, super::Which::make(&me.which)), - b"layer" => { - reuse!(layer, Ok::<_, mlua::Error>(yazi_binding::Layer::from(me.layer()))) - } + b"layer" => reuse!(layer, Ok::<_, mlua::Error>(me.layer())), _ => Value::Nil, }) }); diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index bc802aa9..adca9c5f 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -1,7 +1,7 @@ use std::{ops::Deref, ptr}; use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods}; -use yazi_binding::{Range, Style}; +use yazi_binding::{Range, style::Style}; use yazi_config::THEME; use yazi_shared::{path::AsPath, url::UrlLike}; @@ -15,13 +15,13 @@ pub(super) struct File { } impl Deref for File { - type Target = yazi_fs::File; + type Target = yazi_fs::file::File; fn deref(&self) -> &Self::Target { &self.folder.files[self.idx] } } -impl AsRef for File { - fn as_ref(&self) -> &yazi_fs::File { self } +impl AsRef for File { + fn as_ref(&self) -> &yazi_fs::file::File { self } } impl File { @@ -49,7 +49,7 @@ impl File { impl UserData for File { fn add_fields>(fields: &mut F) { yazi_binding::impl_file_fields!(fields); - fields.add_cached_field("bare", |_, me| Ok(yazi_binding::File::new(&**me))); + fields.add_cached_field("bare", |_, me| Ok(yazi_fs::file::File::from(&**me))); fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered())); @@ -63,9 +63,8 @@ impl UserData for File { yazi_binding::impl_file_methods!(methods); methods.add_method("icon", |_, me, ()| { - use yazi_binding::Icon; // TODO: use a cache - Ok(yazi_config::THEME.icon.matches(me, me.is_hovered()).map(Icon::from)) + Ok(yazi_config::THEME.icon.matches(me, me.is_hovered())) }); methods.add_method("size", |_, me, ()| { Ok(if me.is_dir() { me.folder.files.sizes.get(&me.urn()).copied() } else { Some(me.len) }) diff --git a/yazi-actor/src/lives/folder.rs b/yazi-actor/src/lives/folder.rs index c6f59d6e..e7fa3e38 100644 --- a/yazi-actor/src/lives/folder.rs +++ b/yazi-actor/src/lives/folder.rs @@ -1,7 +1,6 @@ use std::ops::{Deref, Range}; use mlua::{AnyUserData, UserData, UserDataFields}; -use yazi_binding::{FolderStage, Url}; use yazi_config::LAYOUT; use yazi_shim::mlua::UserDataFieldsExt; @@ -39,9 +38,9 @@ impl Folder { impl UserData for Folder { fn add_fields>(fields: &mut F) { - fields.add_cached_field("cwd", |_, me| Ok(Url::new(&me.url))); + fields.add_cached_field("cwd", |_, me| Ok(me.url.clone())); fields.add_static_field("files", |_, me| Files::make(0..me.files.len(), me, &me.tab)); - fields.add_cached_field("stage", |_, me| Ok(FolderStage::new(me.stage.clone()))); + fields.add_cached_field("stage", |_, me| Ok(me.stage.clone())); fields.add_static_field("window", |_, me| Files::make(me.window.clone(), me, &me.tab)); fields.add_field_method_get("offset", |_, me| Ok(me.offset)); diff --git a/yazi-actor/src/lives/lives.rs b/yazi-actor/src/lives/lives.rs index 91db36c0..64b8046f 100644 --- a/yazi-actor/src/lives/lives.rs +++ b/yazi-actor/src/lives/lives.rs @@ -10,8 +10,9 @@ use super::{Core, PtrCell}; use crate::lives::MutCell; pub(super) static TO_DESTROY: MutCell> = MutCell::new(Vec::new()); -pub(super) static FILE_CACHE: MutCell, AnyUserData>>> = - MutCell::new(MaybeUninit::uninit()); +pub(super) static FILE_CACHE: MutCell< + MaybeUninit, AnyUserData>>, +> = MutCell::new(MaybeUninit::uninit()); pub struct Lives; diff --git a/yazi-actor/src/lives/selected.rs b/yazi-actor/src/lives/selected.rs index 2956dd37..68694257 100644 --- a/yazi-actor/src/lives/selected.rs +++ b/yazi-actor/src/lives/selected.rs @@ -1,4 +1,5 @@ use mlua::AnyUserData; +use yazi_shared::url::UrlBuf; use super::Lives; use crate::lives::PtrCell; @@ -11,7 +12,7 @@ impl Selected { let inner = PtrCell::from(inner); Lives::scoped_userdata(yazi_binding::Iter::new( - inner.as_static().values().map(yazi_binding::Url::new), + inner.as_static().values().map(UrlBuf::from), Some(inner.len()), )) } diff --git a/yazi-actor/src/lives/tab.rs b/yazi-actor/src/lives/tab.rs index 33571779..af7e8ba8 100644 --- a/yazi-actor/src/lives/tab.rs +++ b/yazi-actor/src/lives/tab.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, ops::Deref}; use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods}; -use yazi_binding::{Id, UrlRef}; +use yazi_shared::url::UrlRef; use yazi_shim::mlua::UserDataFieldsExt; use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected}; @@ -24,7 +24,7 @@ impl Tab { impl UserData for Tab { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); + fields.add_field_method_get("id", |_, me| Ok(me.id)); fields.add_cached_field("name", |lua, me| match me.name() { Cow::Borrowed(s) => lua.create_string(s), Cow::Owned(s) => lua.create_external_string(s), diff --git a/yazi-actor/src/lives/task.rs b/yazi-actor/src/lives/task.rs index ca465a01..964a2f63 100644 --- a/yazi-actor/src/lives/task.rs +++ b/yazi-actor/src/lives/task.rs @@ -1,9 +1,8 @@ use std::ops::Deref; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields}; -use yazi_binding::SER_OPT; use yazi_scheduler::Progress; -use yazi_shim::mlua::UserDataFieldsExt; +use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt}; use super::{Lives, PtrCell}; diff --git a/yazi-actor/src/lives/tasks.rs b/yazi-actor/src/lives/tasks.rs index d6c31dce..b00cf715 100644 --- a/yazi-actor/src/lives/tasks.rs +++ b/yazi-actor/src/lives/tasks.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields}; -use yazi_binding::SER_OPT; -use yazi_shim::mlua::UserDataFieldsExt; +use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt}; use super::{Lives, PtrCell}; use crate::lives::{Behavior, TaskSnap}; diff --git a/yazi-actor/src/lives/which.rs b/yazi-actor/src/lives/which.rs index eaa90e43..3e80cb7f 100644 --- a/yazi-actor/src/lives/which.rs +++ b/yazi-actor/src/lives/which.rs @@ -26,7 +26,7 @@ impl UserData for Which { fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); fields.add_cached_field("cands", |lua, me| { - lua.create_sequence_from(me.inner.cands.iter().map(yazi_binding::keymap::Chord::from)) + lua.create_sequence_from(me.inner.cands.iter().cloned()) }); fields.add_field_method_get("active", |_, me| Ok(me.inner.active)); diff --git a/yazi-actor/src/lives/yanked.rs b/yazi-actor/src/lives/yanked.rs index d5b9ee92..1f30826c 100644 --- a/yazi-actor/src/lives/yanked.rs +++ b/yazi-actor/src/lives/yanked.rs @@ -1,7 +1,9 @@ use std::ops::Deref; use mlua::{AnyUserData, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods}; -use yazi_binding::{Iter, get_metatable}; +use yazi_binding::Iter; +use yazi_shared::url::UrlBuf; +use yazi_shim::mlua::get_metatable; use super::{Lives, PtrCell}; @@ -23,7 +25,7 @@ impl Yanked { Lives::scoped_userdata(Self { inner, iter: Lives::scoped_userdata(Iter::new( - inner.as_static().iter().map(yazi_binding::Url::new), + inner.as_static().iter().map(UrlBuf::from), Some(inner.len()), ))?, }) diff --git a/yazi-actor/src/mgr/bulk_create.rs b/yazi-actor/src/mgr/bulk_create.rs index 1b6defa0..e96f59c3 100644 --- a/yazi-actor/src/mgr/bulk_create.rs +++ b/yazi-actor/src/mgr/bulk_create.rs @@ -1,18 +1,18 @@ -use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}, sync::Arc}; +use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}}; use anyhow::{Result, anyhow}; use scopeguard::defer; use yazi_binding::Permit; -use yazi_config::{YAZI, opener::OpenerRule}; -use yazi_fs::{File, FilesOp, Splatter, provider::{Provider, local::Local}}; +use yazi_config::{YAZI, opener::OpenerRuleArc}; +use yazi_fs::{FilesOp, Splatter, file::File, provider::{Provider, local::Local}}; use yazi_macro::{succ, writef}; use yazi_parser::VoidForm; use yazi_proxy::TasksProxy; use yazi_scheduler::{AppProxy, NotifyProxy}; use yazi_shared::{data::Data, strand::Strand, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; use yazi_shim::path::CROSS_SEPARATOR; -use yazi_term::{YIELD_TO_SUBPROCESS, sequence::EraseScreen}; -use yazi_tty::TTY; +use yazi_term::YIELD_TO_SUBPROCESS; +use yazi_tty::{TTY, sequence::EraseScreen}; use yazi_vfs::{VfsFile, provider}; use yazi_watcher::WATCHER; @@ -106,7 +106,7 @@ impl BulkCreate { Ok(()) } - fn opener() -> Option> { + fn opener() -> Option { YAZI .open .match_dummy(Path::new("bulk-create.txt"), "text/plain") diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index b472aa44..0bd71e9b 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -1,20 +1,20 @@ -use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path, sync::Arc}; +use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path}; use anyhow::{Result, anyhow}; use hashbrown::HashMap; use scopeguard::defer; use tokio::io::AsyncWriteExt; use yazi_binding::Permit; -use yazi_config::{YAZI, opener::OpenerRule}; +use yazi_config::{YAZI, opener::OpenerRuleArc}; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}}; +use yazi_fs::{FilesOp, Splatter, file::File, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}}; use yazi_macro::{err, succ, writef}; use yazi_parser::VoidForm; use yazi_proxy::TasksProxy; use yazi_scheduler::{AppProxy, NotifyProxy}; use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; -use yazi_term::{YIELD_TO_SUBPROCESS, sequence::EraseScreen}; -use yazi_tty::TTY; +use yazi_term::YIELD_TO_SUBPROCESS; +use yazi_tty::{TTY, sequence::EraseScreen}; use yazi_vfs::{VfsFile, maybe_exists, provider}; use yazi_watcher::WATCHER; @@ -152,7 +152,7 @@ impl BulkRename { Ok(()) } - fn opener() -> Option> { + fn opener() -> Option { YAZI .open .match_dummy(Path::new("bulk-rename.txt"), "text/plain") diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs index 7192af9b..11da268d 100644 --- a/yazi-actor/src/mgr/cd.rs +++ b/yazi-actor/src/mgr/cd.rs @@ -3,10 +3,10 @@ use std::{mem, time::Duration}; use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_core::mgr::CdSource; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, path::{clean_url, expand_url}}; +use yazi_fs::{FilesOp, file::File, path::{clean_url, expand_url}}; use yazi_macro::{act, err, input, render, succ}; use yazi_parser::mgr::CdForm; use yazi_proxy::{CmpProxy, MgrProxy}; @@ -63,7 +63,7 @@ impl Actor for Cd { impl Cd { fn cd_interactive(cx: &mut Ctx) -> Result { - let input = input!(cx, InputCfg::cd(cx.cwd().as_url()))?; + let input = input!(cx, YAZI.input.cd(cx.cwd().as_url()))?; tokio::spawn(async move { let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); diff --git a/yazi-actor/src/mgr/create.rs b/yazi-actor/src/mgr/create.rs index f1c7d2f1..08c6b027 100644 --- a/yazi-actor/src/mgr/create.rs +++ b/yazi-actor/src/mgr/create.rs @@ -1,6 +1,6 @@ use anyhow::{Result, bail}; -use yazi_config::popup::{ConfirmCfg, InputCfg}; -use yazi_fs::{File, FilesOp}; +use yazi_config::{YAZI, popup::ConfirmCfg}; +use yazi_fs::{FilesOp, file::File}; use yazi_macro::{input, ok_or_not_found, succ}; use yazi_parser::mgr::CreateForm; use yazi_proxy::{ConfirmProxy, MgrProxy}; @@ -20,7 +20,7 @@ impl Actor for Create { fn act(cx: &mut Ctx, form: Self::Form) -> Result { let cwd = cx.cwd().to_owned(); - let mut input = input!(cx, InputCfg::create(form.dir))?; + let mut input = input!(cx, YAZI.input.create(form.dir))?; tokio::spawn(async move { let Some(InputEvent::Submit(name)) = input.recv().await else { return }; diff --git a/yazi-actor/src/mgr/download.rs b/yazi-actor/src/mgr/download.rs index 8e9c174e..61b02d8a 100644 --- a/yazi-actor/src/mgr/download.rs +++ b/yazi-actor/src/mgr/download.rs @@ -4,7 +4,7 @@ use anyhow::Result; use futures::{StreamExt, stream::FuturesUnordered}; use hashbrown::HashSet; use yazi_core::mgr::OpenOpt; -use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}}; +use yazi_fs::{FsScheme, file::File, provider::{Provider, local::Local}}; use yazi_macro::succ; use yazi_parser::mgr::DownloadForm; use yazi_proxy::MgrProxy; diff --git a/yazi-actor/src/mgr/filter.rs b/yazi-actor/src/mgr/filter.rs index dd070369..ade4752f 100644 --- a/yazi-actor/src/mgr/filter.rs +++ b/yazi-actor/src/mgr/filter.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_core::mgr::FilterOpt; use yazi_macro::{input, succ}; use yazi_parser::mgr::FilterForm; @@ -21,7 +21,7 @@ impl Actor for Filter { const NAME: &str = "filter"; fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result { - let input = input!(cx, InputCfg::filter())?; + let input = input!(cx, YAZI.input.filter())?; tokio::spawn(async move { let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); diff --git a/yazi-actor/src/mgr/find.rs b/yazi-actor/src/mgr/find.rs index 8d1bcaf9..262df048 100644 --- a/yazi-actor/src/mgr/find.rs +++ b/yazi-actor/src/mgr/find.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_core::mgr::FindDoOpt; use yazi_macro::{input, succ}; use yazi_parser::mgr::FindForm; @@ -21,7 +21,7 @@ impl Actor for Find { const NAME: &str = "find"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let input = input!(cx, InputCfg::find(form.prev))?; + let input = input!(cx, YAZI.input.find(form.prev))?; tokio::spawn(async move { let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 4a39468c..2d789ed0 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -3,7 +3,7 @@ use futures::StreamExt; use hashbrown::HashSet; use yazi_boot::ARGS; use yazi_core::mgr::OpenDoOpt; -use yazi_fs::File; +use yazi_fs::file::File; use yazi_macro::{act, succ}; use yazi_parser::mgr::OpenForm; use yazi_proxy::MgrProxy; diff --git a/yazi-actor/src/mgr/open_do.rs b/yazi-actor/src/mgr/open_do.rs index defae85b..253151fb 100644 --- a/yazi-actor/src/mgr/open_do.rs +++ b/yazi-actor/src/mgr/open_do.rs @@ -1,7 +1,8 @@ use anyhow::Result; use hashbrown::HashMap; use indexmap::IndexSet; -use yazi_config::{YAZI, popup::PickCfg}; +use yazi_config::YAZI; +use yazi_fs::file::File; use yazi_macro::succ; use yazi_parser::mgr::OpenDoForm; use yazi_proxy::{PickProxy, TasksProxy}; @@ -40,7 +41,7 @@ impl Actor for OpenDo { succ!(); } - let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect())); + let pick = PickProxy::show(YAZI.pick.open(openers.iter().map(|o| o.desc()).collect())); let urls: Vec<_> = [UrlCow::default()] .into_iter() .chain(targets.into_iter().map(|(file, _)| file.url.into())) @@ -63,7 +64,7 @@ impl Actor for OpenDo { impl OpenDo { // TODO: remove - fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(yazi_fs::File, &str)>) { + fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(File, &str)>) { let mut openers = HashMap::new(); for (file, mime) in targets { if let Some(open) = YAZI.open.matches(&file, mime) diff --git a/yazi-actor/src/mgr/rename.rs b/yazi-actor/src/mgr/rename.rs index 1d956406..22d54467 100644 --- a/yazi-actor/src/mgr/rename.rs +++ b/yazi-actor/src/mgr/rename.rs @@ -1,11 +1,11 @@ use anyhow::Result; -use yazi_config::popup::{ConfirmCfg, InputCfg}; +use yazi_config::{YAZI, popup::ConfirmCfg}; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp}; +use yazi_fs::{FilesOp, file::File}; use yazi_macro::{act, err, input, ok_or_not_found, succ}; use yazi_parser::mgr::RenameForm; use yazi_proxy::{ConfirmProxy, MgrProxy}; -use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}}; +use yazi_shared::{data::Data, id::Id, url::{UrlBuf, UrlLike}}; use yazi_vfs::{VfsFile, maybe_exists, provider}; use yazi_watcher::WATCHER; use yazi_widgets::input::InputEvent; @@ -42,7 +42,7 @@ impl Actor for Rename { }; let (tab, old) = (cx.tab().id, hovered.url_owned()); - let mut input = input!(cx, InputCfg::rename().with_value(name).with_cursor(cursor))?; + let mut input = input!(cx, YAZI.input.rename().with_value(name).with_cursor(cursor))?; tokio::spawn(async move { let Some(InputEvent::Submit(name)) = input.recv().await else { return }; diff --git a/yazi-actor/src/mgr/reveal.rs b/yazi-actor/src/mgr/reveal.rs index bc83fdfd..e7133d06 100644 --- a/yazi-actor/src/mgr/reveal.rs +++ b/yazi-actor/src/mgr/reveal.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use yazi_fs::{File, FilesOp}; +use yazi_fs::{FilesOp, file::File}; use yazi_macro::{act, render, succ}; use yazi_parser::mgr::RevealForm; use yazi_shared::{data::Data, url::UrlLike}; diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs index 9602490b..70b380a8 100644 --- a/yazi-actor/src/mgr/search.rs +++ b/yazi-actor/src/mgr/search.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, time::Duration}; use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_core::mgr::{CdSource, SearchVia}; use yazi_fs::{FilesOp, cha::Cha}; use yazi_macro::{act, input, succ}; @@ -28,7 +28,7 @@ impl Actor for Search { handle.abort(); } - let mut input = input!(cx, InputCfg::search(opt.via.into()).with_value(&*opt.subject))?; + let mut input = input!(cx, YAZI.input.search(opt.via.into()).with_value(&*opt.subject))?; tokio::spawn(async move { if let Some(InputEvent::Submit(subject)) = input.recv().await { diff --git a/yazi-actor/src/mgr/shell.rs b/yazi-actor/src/mgr/shell.rs index f935b359..33fb79c3 100644 --- a/yazi-actor/src/mgr/shell.rs +++ b/yazi-actor/src/mgr/shell.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use anyhow::Result; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_macro::{act, input, succ}; use yazi_parser::mgr::ShellForm; use yazi_proxy::TasksProxy; @@ -25,7 +25,10 @@ impl Actor for Shell { let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect(); let input = if form.interactive { - Some(input!(cx, InputCfg::shell(form.block).with_value(&*form.run).with_cursor(form.cursor))?) + Some(input!( + cx, + YAZI.input.shell(form.block).with_value(&*form.run).with_cursor(form.cursor) + )?) } else { None }; diff --git a/yazi-actor/src/mgr/tab_rename.rs b/yazi-actor/src/mgr/tab_rename.rs index 238c5067..7a037129 100644 --- a/yazi-actor/src/mgr/tab_rename.rs +++ b/yazi-actor/src/mgr/tab_rename.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use anyhow::Result; -use yazi_config::popup::InputCfg; +use yazi_config::YAZI; use yazi_macro::{act, input, render, succ}; use yazi_parser::mgr::TabRenameForm; use yazi_proxy::MgrProxy; @@ -29,7 +29,7 @@ impl Actor for TabRename { let mut input = input!( cx, - InputCfg::tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name))) + YAZI.input.tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name))) )?; tokio::spawn(async move { diff --git a/yazi-actor/src/tasks/inspect.rs b/yazi-actor/src/tasks/inspect.rs index a7824471..e3606cd0 100644 --- a/yazi-actor/src/tasks/inspect.rs +++ b/yazi-actor/src/tasks/inspect.rs @@ -8,8 +8,8 @@ use yazi_macro::{succ, writef}; use yazi_parser::VoidForm; use yazi_scheduler::AppProxy; use yazi_shared::data::Data; -use yazi_term::{TERM, YIELD_TO_SUBPROCESS, sequence::EraseScreen}; -use yazi_tty::TTY; +use yazi_term::{TERM, YIELD_TO_SUBPROCESS}; +use yazi_tty::{TTY, sequence::EraseScreen}; use crate::{Actor, Ctx}; diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index f3acea37..db923d82 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -19,14 +19,15 @@ yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-term = { path = "../yazi-term", version = "26.5.6" } yazi-tty = { path = "../yazi-tty", version = "26.5.9" } +yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } # External dependencies ansi-to-tui = { workspace = true } anyhow = { workspace = true } base64 = { workspace = true } -image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] } +image = { workspace = true } +inventory = { workspace = true } moxcms = "0.8.1" palette = { version = "0.7.6", default-features = false } quantette = { version = "0.6.0", default-features = false } diff --git a/yazi-adapter/src/adapter.rs b/yazi-adapter/src/adapter.rs index f3fc75b2..72953de4 100644 --- a/yazi-adapter/src/adapter.rs +++ b/yazi-adapter/src/adapter.rs @@ -1,105 +1,56 @@ -use std::{env, path::PathBuf}; +use std::{fmt::{self, Debug}, ops::Deref}; use anyhow::Result; use ratatui::layout::Rect; -use strum::{Display, IntoStaticStr}; -use tracing::warn; -use yazi_emulator::{Emulator, TMUX}; -use yazi_shared::env_exists; +use yazi_emulator::EMULATOR; +use yazi_shim::cell::SyncCell; +use yazi_widgets::clear::ClearInventory; -use crate::{Adapters, SHOWN, drivers}; +use crate::{ADAPTOR, drivers::{Driver, Drivers}}; -#[derive(Clone, Copy, Debug, Display, Eq, IntoStaticStr, PartialEq)] -#[strum(serialize_all = "kebab-case")] -pub enum Adapter { - Kgp, - KgpOld, - Iip, - Sixel, +pub struct Adapter { + driver: Driver, + shown: SyncCell>, + pub collision: SyncCell, +} - // Supported by Überzug++ - X11, - Wayland, - Chafa, +impl Deref for Adapter { + type Target = Driver; + + fn deref(&self) -> &Self::Target { &self.driver } +} + +impl Debug for Adapter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.driver.fmt(f) } } impl Adapter { - pub async fn image_show

(self, path: P, max: Rect) -> Result - where - P: Into, - { - if max.is_empty() { - return Ok(Rect::default()); - } - - let path = path.into(); - match self { - Self::Kgp => drivers::Kgp::image_show(path, max).await, - Self::KgpOld => drivers::KgpOld::image_show(path, max).await, - Self::Iip => drivers::Iip::image_show(path, max).await, - Self::Sixel => drivers::Sixel::image_show(path, max).await, - Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max).await, - Self::Chafa => drivers::Chafa::image_show(path, max).await, + pub(super) fn new() -> Self { + Self { + driver: Drivers::matches(&EMULATOR), + shown: SyncCell::new(None), + collision: SyncCell::new(false), } } - pub fn image_hide(self) -> Result<()> { - if let Some(area) = SHOWN.replace(None) { self.image_erase(area) } else { Ok(()) } + pub fn image_hide(&self) -> Result<()> { + if let Some(area) = self.shown.replace(None) { self.driver.image_erase(area) } else { Ok(()) } } - pub fn image_erase(self, area: Rect) -> Result<()> { - match self { - Self::Kgp => drivers::Kgp::image_erase(area), - Self::KgpOld => drivers::KgpOld::image_erase(area), - Self::Iip => drivers::Iip::image_erase(area), - Self::Sixel => drivers::Sixel::image_erase(area), - Self::X11 | Self::Wayland => drivers::Ueberzug::image_erase(area), - Self::Chafa => drivers::Chafa::image_erase(area), - } - } - - #[inline] - pub fn shown_load(self) -> Option { SHOWN.get() } - - #[inline] - pub(super) fn shown_store(area: Rect) { SHOWN.set(Some(area)); } - - pub(super) fn start(self) { drivers::Ueberzug::start(self); } - - #[inline] - pub(super) fn needs_ueberzug(self) -> bool { - !matches!(self, Self::Kgp | Self::KgpOld | Self::Iip | Self::Sixel) - } + pub(super) fn shown_store(&self, area: Rect) { self.shown.set(Some(area)); } } -impl Adapter { - pub fn matches(emulator: &Emulator) -> Self { - let mut adapters: Adapters = emulator.into(); - if env_exists("ZELLIJ_SESSION_NAME") { - adapters.retain(|p| *p == Self::Sixel); - } else if TMUX.get() { - adapters.retain(|p| *p != Self::KgpOld); - } - if let Some(p) = adapters.first() { - return *p; - } +inventory::submit! { + ClearInventory { + clear: |area| { + let overlap = area.intersection(ADAPTOR.shown.get()?); + if overlap.area() == 0 { + return None; + } - let supported_compositor = drivers::Ueberzug::supported_compositor(); - match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() { - "x11" => return Self::X11, - "wayland" if supported_compositor => return Self::Wayland, - "wayland" if !supported_compositor => return Self::Chafa, - _ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"), - } - if env_exists("WAYLAND_DISPLAY") { - return if supported_compositor { Self::Wayland } else { Self::Chafa }; - } - match env::var("DISPLAY").unwrap_or_default().as_str() { - s if !s.is_empty() && !s.contains("/org.xquartz") => return Self::X11, - _ => {} - } - - warn!("[Adapter] Falling back to chafa"); - Self::Chafa + ADAPTOR.driver.image_erase(overlap).ok(); + ADAPTOR.collision.set(true); + Some(overlap) + }, } } diff --git a/yazi-adapter/src/adapters.rs b/yazi-adapter/src/adapters.rs deleted file mode 100644 index 77912c41..00000000 --- a/yazi-adapter/src/adapters.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use crate::Adapter; - -pub(super) struct Adapters(Vec); - -impl Deref for Adapters { - type Target = Vec; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for Adapters { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl From<&yazi_emulator::Emulator> for Adapters { - fn from(value: &yazi_emulator::Emulator) -> Self { value.kind.either_into() } -} - -impl From for Adapters { - fn from(value: yazi_emulator::Brand) -> Self { - use yazi_emulator::Brand as B; - - use crate::Adapter as A; - - Self(match value { - B::Kitty => vec![A::Kgp], - B::Konsole => vec![A::KgpOld], - B::Iterm2 => vec![A::Iip, A::Sixel], - B::WezTerm => vec![A::Iip, A::Sixel], - B::Foot => vec![A::Sixel], - B::Ghostty => vec![A::Kgp], - B::Microsoft => vec![A::Sixel], - B::Warp => vec![A::Iip, A::KgpOld], - B::Rio => vec![A::Kgp], - B::BlackBox => vec![A::Sixel], - B::VSCode => vec![A::Iip, A::Sixel], - B::Tabby => vec![A::Iip, A::Sixel], - B::Hyper => vec![A::Iip, A::Sixel], - B::Mintty => vec![A::Iip], - B::Tmux => vec![], - B::VTerm => vec![], - B::Apple => vec![], - B::Urxvt => vec![], - B::Bobcat => vec![A::Iip, A::Sixel], - }) - } -} - -impl From for Adapters { - fn from(value: yazi_emulator::Unknown) -> Self { - use Adapter as A; - - Self(match (value.kgp, value.sixel) { - (true, true) => vec![A::Sixel, A::KgpOld], - (true, false) => vec![A::KgpOld], - (false, true) => vec![A::Sixel], - (false, false) => vec![], - }) - } -} diff --git a/yazi-adapter/src/drivers/chafa.rs b/yazi-adapter/src/drivers/chafa.rs index 9b13e96d..2bdbf445 100644 --- a/yazi-adapter/src/drivers/chafa.rs +++ b/yazi-adapter/src/drivers/chafa.rs @@ -6,14 +6,14 @@ use ratatui::layout::Rect; use tokio::process::Command; use yazi_config::THEME; use yazi_emulator::Emulator; -use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; +use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg}; -use crate::Adapter; +use crate::ADAPTOR; -pub(crate) struct Chafa; +pub(super) struct Chafa; impl Chafa { - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let child = Command::new("chafa") .args([ "-f", @@ -58,8 +58,8 @@ impl Chafa { height: lines.len() as u16, }; - Adapter::Chafa.image_hide()?; - Adapter::shown_store(area); + ADAPTOR.image_hide()?; + ADAPTOR.shown_store(area); Emulator::move_lock((max.x, max.y), |w| { for (i, line) in lines.into_iter().enumerate() { w.write_all(line)?; @@ -69,7 +69,7 @@ impl Chafa { }) } - pub(crate) fn image_erase(area: Rect) -> Result<()> { + pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { if let Some(c) = THEME.app.overall.get().bg { diff --git a/yazi-adapter/src/drivers/driver.rs b/yazi-adapter/src/drivers/driver.rs new file mode 100644 index 00000000..3764b1fb --- /dev/null +++ b/yazi-adapter/src/drivers/driver.rs @@ -0,0 +1,59 @@ +use std::path::PathBuf; + +use anyhow::Result; +use ratatui::layout::Rect; +use strum::{Display, IntoStaticStr}; + +use crate::drivers::{Chafa, Iip, Kgp, KgpOld, Sixel, Ueberzug}; + +#[derive(Clone, Copy, Debug, Display, Eq, IntoStaticStr, PartialEq)] +#[strum(serialize_all = "kebab-case")] +pub enum Driver { + Kgp, + KgpOld, + Iip, + Sixel, + + // Supported by Überzug++ + X11, + Wayland, + Chafa, +} + +impl Driver { + pub async fn image_show

(self, path: P, max: Rect) -> Result + where + P: Into, + { + if max.is_empty() { + return Ok(Rect::default()); + } + + let path = path.into(); + match self { + Self::Kgp => Kgp::image_show(path, max).await, + Self::KgpOld => KgpOld::image_show(path, max).await, + Self::Iip => Iip::image_show(path, max).await, + Self::Sixel => Sixel::image_show(path, max).await, + Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await, + Self::Chafa => Chafa::image_show(path, max).await, + } + } + + pub fn image_erase(self, area: Rect) -> Result<()> { + match self { + Self::Kgp => Kgp::image_erase(area), + Self::KgpOld => KgpOld::image_erase(area), + Self::Iip => Iip::image_erase(area), + Self::Sixel => Sixel::image_erase(area), + Self::X11 | Self::Wayland => Ueberzug::image_erase(area), + Self::Chafa => Chafa::image_erase(area), + } + } + + pub(crate) fn start(self) { Ueberzug::start(self); } + + pub(crate) fn needs_ueberzug(self) -> bool { + !matches!(self, Self::Kgp | Self::KgpOld | Self::Iip | Self::Sixel) + } +} diff --git a/yazi-adapter/src/drivers/drivers.rs b/yazi-adapter/src/drivers/drivers.rs new file mode 100644 index 00000000..ed442005 --- /dev/null +++ b/yazi-adapter/src/drivers/drivers.rs @@ -0,0 +1,94 @@ +use std::{env, ops::{Deref, DerefMut}}; + +use tracing::warn; +use yazi_emulator::{Emulator, TMUX}; +use yazi_shared::env_exists; + +use crate::drivers::{Driver as D, Ueberzug}; + +pub(crate) struct Drivers(Vec); + +impl Deref for Drivers { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Drivers { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl From<&yazi_emulator::Emulator> for Drivers { + fn from(value: &yazi_emulator::Emulator) -> Self { value.kind.either_into() } +} + +impl From for Drivers { + fn from(value: yazi_emulator::Brand) -> Self { + use yazi_emulator::Brand as B; + + Self(match value { + B::Kitty => vec![D::Kgp], + B::Konsole => vec![D::KgpOld], + B::Iterm2 => vec![D::Iip, D::Sixel], + B::WezTerm => vec![D::Iip, D::Sixel], + B::Foot => vec![D::Sixel], + B::Ghostty => vec![D::Kgp], + B::Microsoft => vec![D::Sixel], + B::Warp => vec![D::Iip, D::KgpOld], + B::Rio => vec![D::Kgp], + B::BlackBox => vec![D::Sixel], + B::VSCode => vec![D::Iip, D::Sixel], + B::Tabby => vec![D::Iip, D::Sixel], + B::Hyper => vec![D::Iip, D::Sixel], + B::Mintty => vec![D::Iip], + B::Tmux => vec![], + B::VTerm => vec![], + B::Apple => vec![], + B::Urxvt => vec![], + B::Bobcat => vec![D::Iip, D::Sixel], + }) + } +} + +impl From for Drivers { + fn from(value: yazi_emulator::Unknown) -> Self { + Self(match (value.kgp, value.sixel) { + (true, true) => vec![D::Sixel, D::KgpOld], + (true, false) => vec![D::KgpOld], + (false, true) => vec![D::Sixel], + (false, false) => vec![], + }) + } +} + +impl Drivers { + pub fn matches(emulator: &Emulator) -> D { + let mut adapters: Self = emulator.into(); + if env_exists("ZELLIJ_SESSION_NAME") { + adapters.retain(|p| *p == D::Sixel); + } else if TMUX.get() { + adapters.retain(|p| *p != D::KgpOld); + } + if let Some(p) = adapters.first() { + return *p; + } + + let supported_compositor = Ueberzug::supported_compositor(); + match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() { + "x11" => return D::X11, + "wayland" if supported_compositor => return D::Wayland, + "wayland" if !supported_compositor => return D::Chafa, + _ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"), + } + if env_exists("WAYLAND_DISPLAY") { + return if supported_compositor { D::Wayland } else { D::Chafa }; + } + match env::var("DISPLAY").unwrap_or_default().as_str() { + s if !s.is_empty() && !s.contains("/org.xquartz") => return D::X11, + _ => {} + } + + warn!("[Adapter] Falling back to chafa"); + D::Chafa + } +} diff --git a/yazi-adapter/src/drivers/iip.rs b/yazi-adapter/src/drivers/iip.rs index 33f99bd9..7968f4dd 100644 --- a/yazi-adapter/src/drivers/iip.rs +++ b/yazi-adapter/src/drivers/iip.rs @@ -6,27 +6,27 @@ use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEn use ratatui::layout::Rect; use yazi_config::{THEME, YAZI}; use yazi_emulator::{CLOSE, Emulator, START}; -use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; +use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg}; -use crate::{Image, adapter::Adapter}; +use crate::{ADAPTOR, Image}; -pub(crate) struct Iip; +pub(super) struct Iip; impl Iip { - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let img = Image::downscale(path, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adapter::Iip.image_hide()?; - Adapter::shown_store(area); + ADAPTOR.image_hide()?; + ADAPTOR.shown_store(area); Emulator::move_lock((max.x, max.y), |w| { w.write_all(&b)?; Ok(area) }) } - pub(crate) fn image_erase(area: Rect) -> Result<()> { + pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { if let Some(c) = THEME.app.overall.get().bg { diff --git a/yazi-adapter/src/drivers/kgp.rs b/yazi-adapter/src/drivers/kgp.rs index e0391fbc..1a38d8d3 100644 --- a/yazi-adapter/src/drivers/kgp.rs +++ b/yazi-adapter/src/drivers/kgp.rs @@ -8,9 +8,9 @@ use ratatui::{layout::Rect, style::Color}; use yazi_config::THEME; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; use yazi_shim::cell::SyncCell; -use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg, SetFg}; +use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg, SetFg}; -use crate::{adapter::Adapter, image::Image}; +use crate::{ADAPTOR, image::Image}; static DIACRITICS: [char; 297] = [ '\u{0305}', @@ -312,18 +312,18 @@ static DIACRITICS: [char; 297] = [ '\u{1D244}', ]; -pub(crate) struct Kgp; +pub(super) struct Kgp; impl Kgp { - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let img = Image::downscale(path, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b1 = Self::encode(img).await?; let b2 = Self::place(&area)?; - Adapter::Kgp.image_hide()?; - Adapter::shown_store(area); + ADAPTOR.image_hide()?; + ADAPTOR.shown_store(area); Emulator::move_lock((area.x, area.y), |w| { w.write_all(&b1)?; w.write_all(&b2)?; @@ -331,7 +331,7 @@ impl Kgp { }) } - pub(crate) fn image_erase(area: Rect) -> Result<()> { + pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { if let Some(c) = THEME.app.overall.get().bg { diff --git a/yazi-adapter/src/drivers/kgp_old.rs b/yazi-adapter/src/drivers/kgp_old.rs index bb78103b..59842c10 100644 --- a/yazi-adapter/src/drivers/kgp_old.rs +++ b/yazi-adapter/src/drivers/kgp_old.rs @@ -8,25 +8,25 @@ use ratatui::layout::Rect; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; use yazi_tty::TTY; -use crate::{Image, adapter::Adapter}; +use crate::{ADAPTOR, Image}; -pub(crate) struct KgpOld; +pub(super) struct KgpOld; impl KgpOld { - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let img = Image::downscale(path, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adapter::KgpOld.image_hide()?; - Adapter::shown_store(area); + ADAPTOR.image_hide()?; + ADAPTOR.shown_store(area); Emulator::move_lock((area.x, area.y), |w| { w.write_all(&b)?; Ok(area) }) } - pub(crate) fn image_erase(_: Rect) -> Result<()> { + pub(super) fn image_erase(_: Rect) -> Result<()> { let mut w = TTY.lockout(); write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?; w.flush()?; diff --git a/yazi-adapter/src/drivers/mod.rs b/yazi-adapter/src/drivers/mod.rs index 57eeaf4f..20e82086 100644 --- a/yazi-adapter/src/drivers/mod.rs +++ b/yazi-adapter/src/drivers/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(chafa iip kgp kgp_old sixel ueberzug); +yazi_macro::mod_flat!(chafa driver drivers iip kgp kgp_old sixel ueberzug); diff --git a/yazi-adapter/src/drivers/sixel.rs b/yazi-adapter/src/drivers/sixel.rs index d774ae21..f06994ef 100644 --- a/yazi-adapter/src/drivers/sixel.rs +++ b/yazi-adapter/src/drivers/sixel.rs @@ -7,11 +7,11 @@ use quantette::{PaletteSize, color_map::IndexedColorMap, wu::{BinnerU8x3, WuU8x3 use ratatui::layout::Rect; use yazi_config::THEME; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; -use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg}; +use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg}; -use crate::{Image, adapter::Adapter}; +use crate::{ADAPTOR, Image}; -pub(crate) struct Sixel; +pub(super) struct Sixel; struct QuantizeOutput { indices: Vec, @@ -19,20 +19,20 @@ struct QuantizeOutput { } impl Sixel { - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let img = Image::downscale(path, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adapter::Sixel.image_hide()?; - Adapter::shown_store(area); + ADAPTOR.image_hide()?; + ADAPTOR.shown_store(area); Emulator::move_lock((area.x, area.y), |w| { w.write_all(&b)?; Ok(area) }) } - pub(crate) fn image_erase(area: Rect) -> Result<()> { + pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |w| { if let Some(c) = THEME.app.overall.get().bg { diff --git a/yazi-adapter/src/drivers/ueberzug.rs b/yazi-adapter/src/drivers/ueberzug.rs index 6ab4aabd..8e2dc9e0 100644 --- a/yazi-adapter/src/drivers/ueberzug.rs +++ b/yazi-adapter/src/drivers/ueberzug.rs @@ -10,21 +10,21 @@ use yazi_emulator::Dimension; use yazi_shared::{LOG_LEVEL, env_exists}; use yazi_shim::{cell::RoCell, strum::IntoStr}; -use crate::Adapter; +use crate::{ADAPTOR, drivers::Driver}; type Cmd = Option<(PathBuf, Rect)>; static DEMON: RoCell>> = RoCell::new(); -pub(crate) struct Ueberzug; +pub(super) struct Ueberzug; impl Ueberzug { - pub(crate) fn start(adapter: Adapter) { - if !adapter.needs_ueberzug() { + pub(super) fn start(driver: Driver) { + if !driver.needs_ueberzug() { return DEMON.init(None); } - let mut child = Self::create_demon(adapter).ok(); + let mut child = Self::create_demon(driver).ok(); let (tx, mut rx) = mpsc::unbounded_channel(); tokio::spawn(async move { @@ -34,17 +34,17 @@ impl Ueberzug { child = None; } if child.is_none() { - child = Self::create_demon(adapter).ok(); + child = Self::create_demon(driver).ok(); } if let Some(c) = &mut child { - Self::send_command(adapter, c, cmd).await.ok(); + Self::send_command(driver, c, cmd).await.ok(); } } }); DEMON.init(Some(tx)) } - pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result { + pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result { let Some(tx) = &*DEMON else { bail!("uninitialized ueberzugpp"); }; @@ -64,11 +64,11 @@ impl Ueberzug { .unwrap_or(max); tx.send(Some((path, area)))?; - Adapter::shown_store(area); + ADAPTOR.shown_store(area); Ok(area) } - pub(crate) fn image_erase(_: Rect) -> Result<()> { + pub(super) fn image_erase(_: Rect) -> Result<()> { if let Some(tx) = &*DEMON { Ok(tx.send(None)?) } else { @@ -79,16 +79,16 @@ impl Ueberzug { // Currently Überzug++'s Wayland output only supports Niri, Sway, Hyprland and // Wayfire as it requires information from specific compositor socket directly. // These environment variables are from ueberzugpp src/canvas/wayland/config.cpp - pub(crate) fn supported_compositor() -> bool { + pub(super) fn supported_compositor() -> bool { env_exists("NIRI_SOCKET") || env_exists("SWAYSOCK") || env_exists("HYPRLAND_INSTANCE_SIGNATURE") || env_exists("WAYFIRE_SOCKET") } - fn create_demon(adapter: Adapter) -> Result { + fn create_demon(driver: Driver) -> Result { let result = Command::new("ueberzugpp") - .args(["layer", "-so", adapter.into_str()]) + .args(["layer", "-so", driver.into_str()]) .env("SPDLOG_LEVEL", if LOG_LEVEL.get().is_none() { "" } else { "debug" }) .kill_on_drop(true) .stdin(Stdio::piped()) @@ -113,7 +113,7 @@ impl Ueberzug { rect } - async fn send_command(adapter: Adapter, child: &mut Child, cmd: Cmd) -> Result<()> { + async fn send_command(driver: Driver, child: &mut Child, cmd: Cmd) -> Result<()> { let s = if let Some((path, rect)) = cmd { debug!("ueberzugpp rect before adjustment: {:?}", rect); let rect = Self::adjust_rect(rect); @@ -132,7 +132,7 @@ impl Ueberzug { format!(r#"{{"action":"remove","identifier":"yazi"}}{}"#, '\n') }; - debug!("`ueberzugpp layer -so {adapter}` command: {s}"); + debug!("`ueberzugpp layer -so {driver}` command: {s}"); child.stdin.as_mut().unwrap().write_all(s.as_bytes()).await?; Ok(()) diff --git a/yazi-adapter/src/icc.rs b/yazi-adapter/src/icc.rs index 6b3f0541..cf97bdb8 100644 --- a/yazi-adapter/src/icc.rs +++ b/yazi-adapter/src/icc.rs @@ -3,6 +3,7 @@ use image::{ColorType, DynamicImage, GrayAlphaImage, GrayImage, ImageDecoder, Rg use moxcms::{CicpColorPrimaries, ColorProfile, DataColorSpace, Layout, TransferCharacteristics, TransformOptions}; pub(super) struct Icc; + impl Icc { pub(super) fn transform(mut decoder: impl ImageDecoder) -> anyhow::Result { if let Some(layout) = Self::color_type_to_layout(decoder.color_type()) diff --git a/yazi-adapter/src/info.rs b/yazi-adapter/src/info.rs deleted file mode 100644 index 86d4052e..00000000 --- a/yazi-adapter/src/info.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::path::PathBuf; - -use image::{ImageDecoder, ImageError}; - -pub type ImageFormat = image::ImageFormat; -pub type ImageColor = image::ColorType; -pub type ImageOrientation = image::metadata::Orientation; - -#[derive(Clone, Copy)] -pub struct ImageInfo { - pub format: ImageFormat, - pub width: u32, - pub height: u32, - pub color: ImageColor, - pub orientation: Option, -} - -impl ImageInfo { - pub async fn new(path: PathBuf) -> image::ImageResult { - tokio::task::spawn_blocking(move || { - let reader = image::ImageReader::open(path)?.with_guessed_format()?; - - let Some(format) = reader.format() else { - return Err(ImageError::IoError(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "unknown image format", - ))); - }; - - let mut decoder = reader.into_decoder()?; - let (width, height) = decoder.dimensions(); - Ok(Self { - format, - width, - height, - color: decoder.color_type(), - orientation: decoder.orientation().ok(), - }) - }) - .await - .map_err(|e| ImageError::IoError(e.into()))? - } -} diff --git a/yazi-adapter/src/lib.rs b/yazi-adapter/src/lib.rs index 01080816..347bd4e0 100644 --- a/yazi-adapter/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -1,15 +1,12 @@ yazi_macro::mod_pub!(drivers); -yazi_macro::mod_flat!(adapter adapters icc image info); +yazi_macro::mod_flat!(adapter icc image); use yazi_emulator::{Brand, CLOSE, EMULATOR, ESCAPE, Emulator, Mux, START, TMUX}; use yazi_shared::in_wsl; -use yazi_shim::cell::SyncCell; +use yazi_shim::cell::{RoCell, SyncCell}; -pub static ADAPTOR: SyncCell = SyncCell::new(Adapter::Chafa); - -// Image state -static SHOWN: SyncCell> = SyncCell::new(None); +pub static ADAPTOR: RoCell = RoCell::new(); // WSL support pub static WSL: SyncCell = SyncCell::new(false); @@ -34,7 +31,7 @@ pub fn init() -> anyhow::Result<()> { EMULATOR.init(emulator); yazi_config::init_flavor(EMULATOR.light)?; - ADAPTOR.set(Adapter::matches(&EMULATOR)); - ADAPTOR.get().start(); + ADAPTOR.init(Adapter::new()); + ADAPTOR.start(); Ok(()) } diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index f5fdd02e..b21d4278 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -17,33 +17,24 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" } yazi-codegen = { path = "../yazi-codegen", version = "26.5.6" } -yazi-config = { path = "../yazi-config", version = "26.5.6" } -yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } -yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-term = { path = "../yazi-term", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.9" } -yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } -yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } # External dependencies ansi-to-tui = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } +image = { workspace = true } inventory = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } -parking_lot = { workspace = true } -paste = { workspace = true } ratatui = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +strum = { workspace = true } tokio = { workspace = true } -tokio-stream = { workspace = true } tracing = { workspace = true } unicode-width = { workspace = true } diff --git a/yazi-binding/src/access.rs b/yazi-binding/src/access.rs deleted file mode 100644 index 1134d982..00000000 --- a/yazi-binding/src/access.rs +++ /dev/null @@ -1,42 +0,0 @@ -use mlua::{AnyUserData, IntoLuaMulti, UserData, UserDataMethods, Value}; -use yazi_fs::provider::FileBuilder; - -use crate::{Error, Fd, UrlRef}; - -#[derive(Default)] -pub struct Access(yazi_vfs::provider::Gate); - -impl UserData for Access { - fn add_methods>(methods: &mut M) { - methods.add_function("append", |_, (ud, append): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.append(append); - Ok(ud) - }); - methods.add_function("create", |_, (ud, create): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.create(create); - Ok(ud) - }); - methods.add_function("create_new", |_, (ud, create_new): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.create_new(create_new); - Ok(ud) - }); - methods.add_async_method("open", |lua, me, url: UrlRef| async move { - match me.0.open(&*url).await { - Ok(fd) => Fd(fd).into_lua_multi(&lua), - Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), - } - }); - methods.add_function("read", |_, (ud, read): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.read(read); - Ok(ud) - }); - methods.add_function("truncate", |_, (ud, truncate): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.truncate(truncate); - Ok(ud) - }); - methods.add_function("write", |_, (ud, write): (AnyUserData, bool)| { - ud.borrow_mut::()?.0.write(write); - Ok(ud) - }); - } -} diff --git a/yazi-binding/src/composer.rs b/yazi-binding/src/composer.rs index ebfc5bd2..e12d237f 100644 --- a/yazi-binding/src/composer.rs +++ b/yazi-binding/src/composer.rs @@ -5,10 +5,9 @@ pub type ComposerGet = fn(&Lua, &[u8]) -> mlua::Result; pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result; pub struct Composer { - get: G, - set: S, - parent: Option<(G, S)>, - cache: HashMap, Value>, + get: G, + set: S, + cache: HashMap, Value>, } impl Composer @@ -16,13 +15,7 @@ where G: Fn(&Lua, &[u8]) -> mlua::Result + 'static, S: Fn(&Lua, &[u8], Value) -> mlua::Result + 'static, { - #[inline] - pub fn new(get: G, set: S) -> Self { Self { get, set, parent: None, cache: Default::default() } } - - #[inline] - pub fn with_parent(get: G, set: S, p_get: G, p_set: S) -> Self { - Self { get, set, parent: Some((p_get, p_set)), cache: Default::default() } - } + pub fn new(get: G, set: S) -> Self { Self { get, set, cache: Default::default() } } } impl UserData for Composer @@ -37,14 +30,9 @@ where return Ok(v.clone()); } - let mut value = (me.get)(lua, &key)?; - if value.is_nil() - && let Some((p_get, _)) = &me.parent - { - value = p_get(lua, &key)?; - } - + let value = (me.get)(lua, &key)?; me.cache.insert(key.to_owned(), value.clone()); + Ok(value) }); @@ -56,11 +44,6 @@ where if value.is_nil() { me.cache.remove(key.as_ref()); - } else if let Some((_, p_set)) = &me.parent { - match p_set(lua, key.as_ref(), value)? { - Value::Nil => me.cache.remove(key.as_ref()), - v => me.cache.insert(key.to_owned(), v), - }; } else { me.cache.insert(key.to_owned(), value); } diff --git a/yazi-binding/src/config/fetcher.rs b/yazi-binding/src/config/fetcher.rs deleted file mode 100644 index 68d76725..00000000 --- a/yazi-binding/src/config/fetcher.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; -use yazi_config::YAZI; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{FileRef, Id, Iter}; - -#[derive(Clone)] -pub struct Fetcher { - inner: Arc, -} - -impl Deref for Fetcher { - type Target = yazi_config::plugin::Fetcher; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl Fetcher { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl UserData for Fetcher { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - - fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); - } -} - -// --- Matcher -pub struct FetcherMatcher(yazi_config::plugin::FetcherMatcher<'static>); - -impl FetcherMatcher { - pub fn new(inner: impl Into>) -> Self { - Self(inner.into()) - } -} - -impl TryFrom

for FetcherMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - let file: Option = value.raw_get("file")?; - let mime: Option = value.raw_get("mime")?; - - Ok(Self(yazi_config::plugin::FetcherMatcher { - fetchers: YAZI.plugin.fetchers.load_full(), - id: id.0, - file: file.map(|f| f.inner.clone().into()), - mime: mime.map(Into::into), - ..Default::default() - })) - } -} - -impl FromLua for FetcherMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of FetcherMatcher".into_lua_err()), - } - } -} - -impl IntoLua for FetcherMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(Fetcher::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/fetchers.rs b/yazi-binding/src/config/fetchers.rs deleted file mode 100644 index 66bc3fdb..00000000 --- a/yazi-binding/src/config/fetchers.rs +++ /dev/null @@ -1,17 +0,0 @@ -use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods}; -use yazi_config::YAZI; - -use crate::config::FetcherMatcher; - -pub struct Fetchers; - -impl UserData for Fetchers { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, _, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => FetcherMatcher::new(&YAZI.plugin.fetchers).into_lua(lua), - }); - - methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.fetchers.load().len())); - } -} diff --git a/yazi-binding/src/config/mod.rs b/yazi-binding/src/config/mod.rs deleted file mode 100644 index 06de0823..00000000 --- a/yazi-binding/src/config/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(fetcher fetchers open_rule open_rules opener opener_rule opener_rules preloader preloaders previewer previewers spotter spotters); diff --git a/yazi-binding/src/config/open_rule.rs b/yazi-binding/src/config/open_rule.rs deleted file mode 100644 index 64fdc129..00000000 --- a/yazi-binding/src/config/open_rule.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; -use yazi_config::YAZI; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{FileRef, Id, Iter}; - -pub struct OpenRule { - inner: Arc, -} - -impl Deref for OpenRule { - type Target = Arc; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for Arc { - fn from(value: OpenRule) -> Self { value.inner } -} - -impl OpenRule { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl FromLua for OpenRule { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - Ok(Self::new(lua.from_value::(value)?)) - } -} - -impl UserData for OpenRule { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - fields.add_cached_field("use", |lua, me| { - lua.create_sequence_from(me.r#use.iter().map(|s| s.as_str())) - }); - } -} - -// --- Matcher -pub struct OpenRuleMatcher(pub(super) yazi_config::open::OpenRuleMatcher<'static>); - -impl OpenRuleMatcher { - pub fn new(inner: impl Into>) -> Self { - Self(inner.into()) - } -} - -impl TryFrom
for OpenRuleMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - let file: Option = value.raw_get("file")?; - let mime: Option = value.raw_get("mime")?; - - Ok(Self(yazi_config::open::OpenRuleMatcher { - rules: YAZI.open.load_full(), - id: id.0, - file: file.map(|f| f.inner.clone().into()), - mime: mime.map(Into::into), - ..Default::default() - })) - } -} - -impl FromLua for OpenRuleMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of OpenRuleMatcher".into_lua_err()), - } - } -} - -impl IntoLua for OpenRuleMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(OpenRule::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/open_rules.rs b/yazi-binding/src/config/open_rules.rs deleted file mode 100644 index 1f1f7689..00000000 --- a/yazi-binding/src/config/open_rules.rs +++ /dev/null @@ -1,39 +0,0 @@ -use mlua::{ExternalError, ExternalResult, IntoLua, MetaMethod, Table, UserData, UserDataMethods}; -use yazi_config::YAZI; -use yazi_shim::mlua::DeserializeOverLua; - -use crate::config::{OpenRule, OpenRuleMatcher}; - -pub struct OpenRules; - -impl UserData for OpenRules { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, _, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => OpenRuleMatcher::new(&*YAZI.open).into_lua(lua), - }); - - methods.add_method("insert", |_, _, (index, rule): (isize, OpenRule)| { - let index = match index { - 1.. => index - 1, - 0 => return Err("index must be 1-based or negative".into_lua_err()), - _ => index, - }; - - YAZI.open.insert(index, rule.clone()).into_lua_err()?; - Ok(rule) - }); - - methods.add_method("remove", |_, _, matcher: OpenRuleMatcher| { - YAZI.open.remove(matcher.0); - Ok(()) - }); - - methods.add_method("update", |_, _, (matcher, table): (OpenRuleMatcher, Table)| { - YAZI.open.update(matcher.0, |rule| rule.deserialize_over_lua(&table))?; - Ok(()) - }); - - methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.open.load().len())); - } -} diff --git a/yazi-binding/src/config/opener.rs b/yazi-binding/src/config/opener.rs deleted file mode 100644 index f3bbd76c..00000000 --- a/yazi-binding/src/config/opener.rs +++ /dev/null @@ -1,45 +0,0 @@ -use mlua::{ExternalError, FromLua, IntoLua, IntoLuaMulti, MetaMethod, UserData, UserDataMethods, Value}; -use yazi_config::{YAZI, opener::OpenerRulesMatcher}; - -use crate::config::OpenerRules; - -pub struct Opener; - -impl UserData for Opener { - fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Index, |lua, _, key: mlua::String| { - let key = key.to_str()?; - match YAZI.opener.load().get(&*key) { - Some(rules) => OpenerRules::new(rules.clone()).into_lua(lua), - None => Ok(Value::Nil), - } - }); - - methods.add_meta_method(MetaMethod::NewIndex, |lua, _, (key, value): (mlua::String, Value)| { - let key = key.to_str()?; - match value { - t @ Value::Table(_) => { - YAZI.opener.insert(&key, &*OpenerRules::from_lua(t, lua)?); - } - Value::Nil => { - YAZI.opener.remove(&key); - } - _ => return Err("expected a table or nil".into_lua_err()), - } - Ok(()) - }); - - methods.add_meta_method(MetaMethod::Pairs, |lua, _, ()| { - let mut matcher = OpenerRulesMatcher::from(&YAZI.opener); - let iter = lua.create_function_mut(move |lua, ()| { - if let Some((name, rules)) = matcher.next() { - (name, OpenerRules::new(rules)).into_lua_multi(lua) - } else { - ().into_lua_multi(lua) - } - })?; - - Ok(iter) - }); - } -} diff --git a/yazi-binding/src/config/opener_rule.rs b/yazi-binding/src/config/opener_rule.rs deleted file mode 100644 index 3e1b03ef..00000000 --- a/yazi-binding/src/config/opener_rule.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{Id, Iter}; - -pub struct OpenerRule { - inner: Arc, -} - -impl Deref for OpenerRule { - type Target = Arc; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for Arc { - fn from(value: OpenerRule) -> Self { value.inner } -} - -impl OpenerRule { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl FromLua for OpenerRule { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - let mut inner: yazi_config::opener::OpenerRule = lua.from_value(value)?; - inner.fill(); - - Ok(Self::new(inner)) - } -} - -impl UserData for OpenerRule { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - fields.add_cached_field("run", |lua, me| lua.create_string(&*me.run)); - fields.add_field_method_get("block", |_, me| Ok(me.block)); - fields.add_field_method_get("orphan", |_, me| Ok(me.orphan)); - fields.add_cached_field("desc", |lua, me| lua.create_string(&*me.desc)); - } -} - -// --- Matcher -pub struct OpenerRuleMatcher(pub(super) yazi_config::opener::OpenerRuleMatcher); - -impl OpenerRuleMatcher { - pub fn new(inner: impl Into) -> Self { - Self(inner.into()) - } -} - -impl TryFrom
for OpenerRuleMatcher { - type Error = mlua::Error; - - fn try_from(t: Table) -> Result { - let id: Id = t.raw_get("id").unwrap_or_default(); - - Ok(Self(yazi_config::opener::OpenerRuleMatcher { id: id.0, ..Default::default() })) - } -} - -impl FromLua for OpenerRuleMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of OpenerRuleMatcher".into_lua_err()), - } - } -} - -impl IntoLua for OpenerRuleMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(OpenerRule::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/opener_rules.rs b/yazi-binding/src/config/opener_rules.rs deleted file mode 100644 index d694bdec..00000000 --- a/yazi-binding/src/config/opener_rules.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, ExternalResult, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, UserData, UserDataMethods, Value}; -use yazi_shim::toml::DeserializeOverHook; - -use crate::config::{OpenerRule, OpenerRuleMatcher}; - -pub struct OpenerRules { - inner: Arc, -} - -impl Deref for OpenerRules { - type Target = Arc; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl OpenerRules { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl FromLua for OpenerRules { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - let inner: yazi_config::opener::OpenerRules = lua.from_value(value)?; - - Ok(Self::new(inner.deserialize_over_hook().into_lua_err()?)) - } -} - -impl UserData for OpenerRules { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, me, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => OpenerRuleMatcher::new(&*me.inner).into_lua(lua), - }); - - methods.add_method("insert", |_, me, (index, rule): (isize, OpenerRule)| { - let index = match index { - 1.. => index - 1, - 0 => return Err("index must be 1-based or negative".into_lua_err()), - _ => index, - }; - - me.inner.insert(index, rule.clone()).into_lua_err()?; - Ok(rule) - }); - - methods.add_method("remove", |_, me, matcher: OpenerRuleMatcher| { - me.inner.remove(matcher.0); - Ok(()) - }); - - methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.inner.load().len())); - } -} diff --git a/yazi-binding/src/config/preloader.rs b/yazi-binding/src/config/preloader.rs deleted file mode 100644 index 3848ceda..00000000 --- a/yazi-binding/src/config/preloader.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; -use yazi_config::YAZI; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{FileRef, Id, Iter}; - -#[derive(Clone)] -pub struct Preloader { - inner: Arc, -} - -impl Deref for Preloader { - type Target = yazi_config::plugin::Preloader; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl Preloader { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl UserData for Preloader { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - - fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); - } -} - -// --- Matcher -pub struct PreloaderMatcher(pub(super) yazi_config::plugin::PreloaderMatcher<'static>); - -impl PreloaderMatcher { - pub fn new(inner: impl Into>) -> Self { - Self(inner.into()) - } -} - -impl TryFrom
for PreloaderMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - let file: Option = value.raw_get("file")?; - let mime: Option = value.raw_get("mime")?; - - Ok(Self(yazi_config::plugin::PreloaderMatcher { - preloaders: YAZI.plugin.preloaders.load_full(), - id: id.0, - file: file.map(|f| f.inner.clone().into()), - mime: mime.map(Into::into), - ..Default::default() - })) - } -} - -impl FromLua for PreloaderMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of PreloaderMatcher".into_lua_err()), - } - } -} - -impl IntoLua for PreloaderMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(Preloader::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/preloaders.rs b/yazi-binding/src/config/preloaders.rs deleted file mode 100644 index f1c13769..00000000 --- a/yazi-binding/src/config/preloaders.rs +++ /dev/null @@ -1,17 +0,0 @@ -use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods}; -use yazi_config::YAZI; - -use crate::config::PreloaderMatcher; - -pub struct Preloaders; - -impl UserData for Preloaders { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, _, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => PreloaderMatcher::new(&YAZI.plugin.preloaders).into_lua(lua), - }); - - methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.preloaders.load().len())); - } -} diff --git a/yazi-binding/src/config/previewer.rs b/yazi-binding/src/config/previewer.rs deleted file mode 100644 index b3c5e4eb..00000000 --- a/yazi-binding/src/config/previewer.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value}; -use yazi_config::YAZI; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{FileRef, Id, Iter}; - -#[derive(Clone)] -pub struct Previewer { - inner: Arc, -} - -impl Deref for Previewer { - type Target = yazi_config::plugin::Previewer; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for Arc { - fn from(value: Previewer) -> Self { value.inner } -} - -impl Previewer { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl FromLua for Previewer { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - Ok(Self::new(lua.from_value::(value)?)) - } -} - -impl UserData for Previewer { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - - fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); - } -} - -// --- Matcher -pub struct PreviewerMatcher(pub(super) yazi_config::plugin::PreviewerMatcher<'static>); - -impl PreviewerMatcher { - pub fn new(inner: impl Into>) -> Self { - Self(inner.into()) - } -} - -impl TryFrom
for PreviewerMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - let file: Option = value.raw_get("file")?; - let mime: Option = value.raw_get("mime")?; - - Ok(Self(yazi_config::plugin::PreviewerMatcher { - previewers: YAZI.plugin.previewers.load_full(), - id: id.0, - file: file.map(|f| f.inner.clone().into()), - mime: mime.map(Into::into), - ..Default::default() - })) - } -} - -impl FromLua for PreviewerMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of PreviewerMatcher".into_lua_err()), - } - } -} - -impl IntoLua for PreviewerMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(Previewer::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/previewers.rs b/yazi-binding/src/config/previewers.rs deleted file mode 100644 index 66934839..00000000 --- a/yazi-binding/src/config/previewers.rs +++ /dev/null @@ -1,33 +0,0 @@ -use mlua::{ExternalError, ExternalResult, IntoLua, MetaMethod, UserData, UserDataMethods}; -use yazi_config::YAZI; - -use crate::config::{Previewer, PreviewerMatcher}; - -pub struct Previewers; - -impl UserData for Previewers { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, _, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => PreviewerMatcher::new(&YAZI.plugin.previewers).into_lua(lua), - }); - - methods.add_method("insert", |_, _, (index, previewer): (isize, Previewer)| { - let index = match index { - 1.. => index - 1, - 0 => return Err("index must be 1-based or negative".into_lua_err()), - _ => index, - }; - - YAZI.plugin.previewers.insert(index, previewer.clone().into()).into_lua_err()?; - Ok(previewer) - }); - - methods.add_method("remove", |_, _, matcher: PreviewerMatcher| { - YAZI.plugin.previewers.remove(matcher.0); - Ok(()) - }); - - methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.previewers.load().len())); - } -} diff --git a/yazi-binding/src/config/spotter.rs b/yazi-binding/src/config/spotter.rs deleted file mode 100644 index d116dde8..00000000 --- a/yazi-binding/src/config/spotter.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; -use yazi_config::YAZI; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{FileRef, Id, Iter}; - -#[derive(Clone)] -pub struct Spotter { - inner: Arc, -} - -impl Deref for Spotter { - type Target = yazi_config::plugin::Spotter; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl Spotter { - pub fn new(inner: impl Into>) -> Self { - Self { inner: inner.into() } - } -} - -impl UserData for Spotter { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); - - fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name)); - } -} - -// --- Matcher -pub struct SpotterMatcher(pub(super) yazi_config::plugin::SpotterMatcher<'static>); - -impl SpotterMatcher { - pub fn new(inner: impl Into>) -> Self { - Self(inner.into()) - } -} - -impl TryFrom
for SpotterMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - let file: Option = value.raw_get("file")?; - let mime: Option = value.raw_get("mime")?; - - Ok(Self(yazi_config::plugin::SpotterMatcher { - spotters: YAZI.plugin.spotters.load_full(), - id: id.0, - file: file.map(|f| f.inner.clone().into()), - mime: mime.map(Into::into), - ..Default::default() - })) - } -} - -impl FromLua for SpotterMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of SpotterMatcher".into_lua_err()), - } - } -} - -impl IntoLua for SpotterMatcher { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(Spotter::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/config/spotters.rs b/yazi-binding/src/config/spotters.rs deleted file mode 100644 index 8afda780..00000000 --- a/yazi-binding/src/config/spotters.rs +++ /dev/null @@ -1,17 +0,0 @@ -use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods}; -use yazi_config::YAZI; - -use crate::config::SpotterMatcher; - -pub struct Spotters; - -impl UserData for Spotters { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |lua, _, matcher: Option| match matcher { - Some(matcher) => matcher.into_lua(lua), - None => SpotterMatcher::new(&YAZI.plugin.spotters).into_lua(lua), - }); - - methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.spotters.load().len())); - } -} diff --git a/yazi-binding/src/dnd.rs b/yazi-binding/src/dnd.rs deleted file mode 100644 index b294e0f6..00000000 --- a/yazi-binding/src/dnd.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::{mem, ops::Deref}; - -use mlua::{IntoLua, UserData, UserDataFields, Value}; -use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr}; -use yazi_term::event::{DndDropArrive, DndEvent as Inner}; - -pub struct DndEvent { - inner: Inner, -} - -impl Deref for DndEvent { - type Target = Inner; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for DndEvent { - fn from(inner: Inner) -> Self { Self { inner } } -} - -impl UserData for DndEvent { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("type", |_, me| Ok(me.inner.r#type())); - - fields.add_field_method_get("x", |_, me| Ok(me.inner.x())); - - fields.add_field_method_get("y", |_, me| Ok(me.inner.y())); - - fields.add_field_method_get("idx", |_, me| Ok(me.inner.idx())); - - fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str))); - - fields.add_cached_field("mimes", |lua, me| { - if let Some(mimes) = me.inner.mimes() { - lua.create_sequence_from(mimes.iter())?.into_lua(lua) - } else { - Ok(Value::Nil) - } - }); - - fields.add_cached_field_mut("data", |lua, me| match &mut me.inner { - Inner::DropArrive(DndDropArrive { data, .. }) => { - lua.create_external_string(mem::take(data))?.into_lua(lua) - } - _ => Ok(Value::Nil), - }); - } -} diff --git a/yazi-binding/src/elements/area.rs b/yazi-binding/src/elements/area.rs index 5abb3991..1f3e8d4f 100644 --- a/yazi-binding/src/elements/area.rs +++ b/yazi-binding/src/elements/area.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Value}; use super::{Pos, Rect}; +use crate::position::Position; const EXPECTED: &str = "expected a Pos or Rect"; @@ -45,7 +46,7 @@ impl Area { pub fn transform( self, - f: impl FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect, + f: impl FnOnce(Position) -> ratatui::layout::Rect, ) -> ratatui::layout::Rect { match self { Self::Rect(rect) => *rect, @@ -62,8 +63,8 @@ impl From for Area { fn from(rect: ratatui::layout::Rect) -> Self { Self::Rect(rect.into()) } } -impl From for Area { - fn from(pos: yazi_config::popup::Position) -> Self { Self::Pos(pos.into()) } +impl From for Area { + fn from(value: Position) -> Self { Self::Pos(value.into()) } } impl TryFrom<&AnyUserData> for Area { diff --git a/yazi-binding/src/elements/bar.rs b/yazi-binding/src/elements/bar.rs index 42d4a549..61ed138c 100644 --- a/yazi-binding/src/elements/bar.rs +++ b/yazi-binding/src/elements/bar.rs @@ -36,15 +36,6 @@ impl Spatial for Bar { fn set_area(&mut self, area: Area) { self.area = area; } } -impl Widget for Bar { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - (&self).render(rect, buf); - } -} - impl Widget for &Bar { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/elements.rs b/yazi-binding/src/elements/elements.rs deleted file mode 100644 index 6ef49bb4..00000000 --- a/yazi-binding/src/elements/elements.rs +++ /dev/null @@ -1,38 +0,0 @@ -use mlua::{IntoLua, Lua, Value}; - -use crate::{Composer, ComposerGet, ComposerSet}; - -pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer { - fn get(lua: &Lua, key: &[u8]) -> mlua::Result { - match key { - b"Align" => super::Align::compose(lua)?, - b"Bar" => super::Bar::compose(lua)?, - b"Border" => super::Border::compose(lua)?, - b"Clear" => super::Clear::compose(lua)?, - b"Color" => super::Color::compose(lua)?, - b"Constraint" => super::Constraint::compose(lua)?, - b"Edge" => super::Edge::compose(lua)?, - b"Fill" => super::Fill::compose(lua)?, - b"Gauge" => super::Gauge::compose(lua)?, - b"Input" => super::Input::compose(lua)?, - b"Layout" => super::Layout::compose(lua)?, - b"Line" => super::Line::compose(lua)?, - b"List" => super::List::compose(lua)?, - b"Pad" => super::Pad::compose(lua)?, - b"Pos" => super::Pos::compose(lua)?, - b"Rect" => super::Rect::compose(lua)?, - b"Row" => super::Row::compose(lua)?, - b"Span" => super::Span::compose(lua)?, - b"Style" => crate::Style::compose(lua)?, - b"Table" => super::Table::compose(lua)?, - b"Text" => super::Text::compose(lua)?, - b"Wrap" => super::Wrap::compose(lua)?, - _ => return Ok(Value::Nil), - } - .into_lua(lua) - } - - fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - - Composer::with_parent(get, set, p_get, p_set) -} diff --git a/yazi-binding/src/elements/fill.rs b/yazi-binding/src/elements/fill.rs index 6ca356b9..00881bdf 100644 --- a/yazi-binding/src/elements/fill.rs +++ b/yazi-binding/src/elements/fill.rs @@ -35,15 +35,6 @@ impl Spatial for Fill { fn set_area(&mut self, area: Area) { self.area = area; } } -impl Widget for Fill { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - (&self).render(rect, buf); - } -} - impl Widget for &Fill { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/gauge.rs b/yazi-binding/src/elements/gauge.rs index 4d9dd3d6..250317f7 100644 --- a/yazi-binding/src/elements/gauge.rs +++ b/yazi-binding/src/elements/gauge.rs @@ -2,7 +2,7 @@ use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData use ratatui::widgets::Widget; use super::{Area, Span}; -use crate::{Style, elements::Spatial}; +use crate::{elements::Spatial, style::Style}; #[derive(Clone, Debug, Default)] pub struct Gauge { diff --git a/yazi-binding/src/elements/input.rs b/yazi-binding/src/elements/input.rs deleted file mode 100644 index 3095bbf9..00000000 --- a/yazi-binding/src/elements/input.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::sync::Arc; - -use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; -use parking_lot::Mutex; -use ratatui::widgets::Widget; - -use super::Area; -use crate::{elements::Spatial, impl_area_method}; - -const EXPECTED: &str = "expected a table or a Input"; - -#[derive(Clone, Debug, Default)] -pub struct Input { - pub focus: bool, - - inner: Arc>, -} - -impl Input { - pub fn compose(lua: &Lua) -> mlua::Result { - let new = lua.create_function(|_, (_, input): (Table, Self)| Ok(input))?; - - let input = lua.create_table()?; - input.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; - input.into_lua(lua) - } -} - -impl From<&Input> for Arc> { - fn from(input: &Input) -> Self { input.inner.clone() } -} - -impl TryFrom<&AnyUserData> for Input { - type Error = mlua::Error; - - fn try_from(value: &AnyUserData) -> Result { - Ok(value.borrow::()?.clone()) - } -} - -impl TryFrom
for Input { - type Error = mlua::Error; - - fn try_from(tb: Table) -> Result { - let input = yazi_widgets::input::Input::new(yazi_widgets::input::InputOpt { - cfg: yazi_config::popup::InputCfg { - title: String::new(), - value: tb.raw_get("value")?, - cursor: None, - obscure: false, - position: Default::default(), - realtime: false, - completion: false, - }, - cb: None, - })?; - - Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() }) - } -} - -impl Spatial for Input { - fn area(&self) -> Area { self.inner.lock().pos.into() } - - fn set_area(&mut self, area: Area) { - self.inner.lock().repos(match area { - Area::Rect(rect) => yazi_config::popup::Position { - origin: yazi_config::popup::Origin::TopLeft, - offset: yazi_config::popup::Offset { - x: rect.x as i16, - y: rect.y as i16, - width: rect.width, - height: rect.height, - }, - }, - Area::Pos(pos) => pos.into(), - }); - } -} - -impl Widget for Input { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - (&self).render(rect, buf); - } -} - -impl Widget for &Input { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - self.inner.lock().render(rect, buf) - } -} - -impl FromLua for Input { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(tb) => Self::try_from(tb), - _ => Err(EXPECTED.into_lua_err()), - } - } -} - -impl UserData for Input { - fn add_methods>(methods: &mut M) { - impl_area_method!(methods); - - methods.add_function("focus", |lua, (ud, focus): (AnyUserData, bool)| { - ud.borrow_mut::()?.focus = focus; - ud.into_lua(lua) - }); - } -} diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 31cb4f21..93180363 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -106,15 +106,6 @@ impl Spatial for Line { fn set_area(&mut self, area: Area) { self.area = area; } } -impl Widget for Line { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - (&self).render(rect, buf); - } -} - impl Widget for &Line { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/list.rs b/yazi-binding/src/elements/list.rs index 55c83972..5dba2f0d 100644 --- a/yazi-binding/src/elements/list.rs +++ b/yazi-binding/src/elements/list.rs @@ -37,15 +37,6 @@ impl Spatial for List { fn set_area(&mut self, area: Area) { self.area = area; } } -impl Widget for List { - fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) - where - Self: Sized, - { - (&self).render(rect, buf); - } -} - impl Widget for &List { fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) where diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index 2aad50fe..fa027fe9 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge input layout line list pad pos rect renderable renderables row span spatial table text wrap); +yazi_macro::mod_flat!(align area bar border cell color constraint edge fill gauge layout line list pad pos rect row span spatial table text wrap); diff --git a/yazi-binding/src/elements/pos.rs b/yazi-binding/src/elements/pos.rs index 24a4a1e8..ac74de16 100644 --- a/yazi-binding/src/elements/pos.rs +++ b/yazi-binding/src/elements/pos.rs @@ -4,29 +4,28 @@ use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, Me use yazi_shim::strum::IntoStr; use super::Pad; +use crate::position::{Offset, Origin, Position}; const EXPECTED: &str = "expected a Pos"; #[derive(Clone, Copy, Default)] pub struct Pos { - inner: yazi_config::popup::Position, + inner: Position, pub(super) pad: Pad, } impl Deref for Pos { - type Target = yazi_config::popup::Position; + type Target = Position; fn deref(&self) -> &Self::Target { &self.inner } } -impl From for Pos { - fn from(value: yazi_config::popup::Position) -> Self { - Self { inner: value, ..Default::default() } - } +impl From for Pos { + fn from(value: Position) -> Self { Self { inner: value, ..Default::default() } } } -impl From for yazi_config::popup::Position { +impl From for Position { fn from(value: Pos) -> Self { value.inner } } @@ -42,8 +41,6 @@ impl TryFrom
for Pos { type Error = mlua::Error; fn try_from(t: Table) -> Result { - use yazi_config::popup::{Offset, Origin, Position}; - Ok(Self::from(Position { origin: Origin::from_str(&t.raw_get::(1)?.to_str()?).into_lua_err()?, offset: Offset { diff --git a/yazi-binding/src/elements/table.rs b/yazi-binding/src/elements/table.rs index d8077904..6be313c4 100644 --- a/yazi-binding/src/elements/table.rs +++ b/yazi-binding/src/elements/table.rs @@ -2,7 +2,7 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserData, UserDataMethods, Val use ratatui::widgets::{StatefulWidget, Widget}; use super::{Area, Row}; -use crate::{Style, elements::{Constraint, Spatial}}; +use crate::{elements::{Constraint, Spatial}, style::Style}; // --- Table #[derive(Clone, Debug, Default)] diff --git a/yazi-binding/src/elements/text.rs b/yazi-binding/src/elements/text.rs index cf532308..8089cfbe 100644 --- a/yazi-binding/src/elements/text.rs +++ b/yazi-binding/src/elements/text.rs @@ -3,7 +3,7 @@ use std::{any::TypeId, mem}; use ansi_to_tui::IntoText; use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::widgets::Widget; -use yazi_shared::SStr; +use yazi_shim::SStr; use super::{Area, Line, Span, Wrap}; use crate::{Error, elements::{Align, Spatial}}; diff --git a/yazi-binding/src/elements/wrap.rs b/yazi-binding/src/elements/wrap.rs index 85f205f8..cce28cb1 100644 --- a/yazi-binding/src/elements/wrap.rs +++ b/yazi-binding/src/elements/wrap.rs @@ -1,10 +1,9 @@ use std::ops::Deref; use mlua::{FromLua, IntoLua, Lua, Value}; -use yazi_config::preview::PreviewWrap; #[derive(Clone, Copy, Debug, Default)] -pub struct Wrap(pub(super) Option); +pub struct Wrap(pub Option); impl Deref for Wrap { type Target = Option; @@ -26,15 +25,6 @@ impl From for Wrap { fn from(value: ratatui::widgets::Wrap) -> Self { Self(Some(value)) } } -impl From for Wrap { - fn from(value: PreviewWrap) -> Self { - Self(match value { - PreviewWrap::No => None, - PreviewWrap::Yes => Some(ratatui::widgets::Wrap { trim: false }), - }) - } -} - impl FromLua for Wrap { fn from_lua(value: Value, _: &Lua) -> mlua::Result { let Value::Integer(n) = value else { diff --git a/yazi-binding/src/error.rs b/yazi-binding/src/error.rs index ff075c4b..ddf5e52b 100644 --- a/yazi-binding/src/error.rs +++ b/yazi-binding/src/error.rs @@ -2,12 +2,12 @@ use std::{borrow::Cow, fmt::Display}; use mlua::{ExternalError, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; use yazi_codegen::FromLuaOwned; -use yazi_shared::SStr; +use yazi_shim::SStr; #[derive(FromLuaOwned)] pub enum Error { Io(std::io::Error), - Fs(yazi_fs::error::Error), + Fs(yazi_shim::fs::Error), Serde(serde_json::Error), Custom(SStr), } @@ -18,7 +18,7 @@ impl Error { let fs = lua.create_function(|_, value: Value| { Ok(Self::Fs(match value { - Value::Table(t) => yazi_fs::error::Error::custom( + Value::Table(t) => yazi_shim::fs::Error::custom( &t.raw_get::("kind")?.to_str()?, t.raw_get("code")?, &t.raw_get::("message")?.to_str()?, @@ -64,7 +64,7 @@ impl UserData for Error { }); fields.add_field_method_get("kind", |_, me| { Ok(match me { - Self::Io(e) => Some(yazi_fs::error::Error::from(e.kind()).kind_str()), + Self::Io(e) => Some(yazi_shim::fs::Error::from(e.kind()).kind_str()), Self::Fs(e) => Some(e.kind_str()), _ => None, }) diff --git a/yazi-binding/src/event/action.rs b/yazi-binding/src/event/action.rs deleted file mode 100644 index 984429c6..00000000 --- a/yazi-binding/src/event/action.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use mlua::{FromLua, Lua, UserData, UserDataFields, Value}; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::event::Cmd; - -pub struct Action { - inner: yazi_shared::event::Action, -} - -impl Deref for Action { - type Target = yazi_shared::event::Action; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl DerefMut for Action { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } -} - -impl From for yazi_shared::event::Action { - fn from(value: Action) -> Self { value.inner } -} - -impl From for yazi_shared::event::Actions { - fn from(value: Action) -> Self { Self::from(value.inner) } -} - -impl Action { - pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } -} - -impl FromLua for Action { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(match value { - Value::String(s) => Self { inner: s.to_str()?.parse()? }, - Value::UserData(ud) => ud.take()?, - _ => Err(mlua::Error::FromLuaConversionError { - from: value.type_name(), - to: "Action".to_owned(), - message: Some("expected a string or an Action".to_string()), - })?, - }) - } -} - -impl UserData for Action { - fn add_fields>(fields: &mut F) { - fields.add_cached_field("cmd", |_, me| Ok(Cmd::new(me.cmd.clone()))); - } -} diff --git a/yazi-binding/src/event/actions.rs b/yazi-binding/src/event/actions.rs deleted file mode 100644 index 2cd1a942..00000000 --- a/yazi-binding/src/event/actions.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; - -use crate::event::Action; - -#[derive(Clone)] -pub struct Actions { - inner: yazi_shared::event::Actions, -} - -impl Deref for Actions { - type Target = yazi_shared::event::Actions; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl DerefMut for Actions { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } -} - -impl From for yazi_shared::event::Actions { - fn from(value: Actions) -> Self { value.inner } -} - -impl Actions { - pub fn new(inner: yazi_shared::event::Actions) -> Self { Self { inner } } -} - -impl FromLua for Actions { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - let inner = match value { - Value::Table(t) => t - .sequence_values::() - .map(|a| a.map(Into::into)) - .collect::>>()? - .into(), - v @ Value::String(_) => Action::from_lua(v, lua)?.into(), - _ => Err("expected a string or a table of actions".into_lua_err())?, - }; - - Ok(Self { inner }) - } -} - -impl IntoLua for Actions { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua.create_sequence_from(self.inner.into_iter().map(Action::new))?.into_lua(lua) - } -} diff --git a/yazi-binding/src/event/cmd.rs b/yazi-binding/src/event/cmd.rs deleted file mode 100644 index 5bd17115..00000000 --- a/yazi-binding/src/event/cmd.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{mem, ops::{Deref, DerefMut}}; - -use mlua::{UserData, UserDataFields}; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::Sendable; - -pub struct Cmd { - inner: yazi_shared::event::Cmd, -} - -impl Deref for Cmd { - type Target = yazi_shared::event::Cmd; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl DerefMut for Cmd { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } -} - -impl Cmd { - pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } -} - -impl UserData for Cmd { - fn add_fields>(fields: &mut F) { - fields.add_cached_field_mut("name", |_, me| Ok(mem::take(&mut me.name))); - - fields.add_cached_field_mut("args", |lua, me| { - Sendable::args_to_table(lua, mem::take(&mut me.args)) - }); - } -} diff --git a/yazi-binding/src/event/mod.rs b/yazi-binding/src/event/mod.rs deleted file mode 100644 index c0011e6d..00000000 --- a/yazi-binding/src/event/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(action actions cmd); diff --git a/yazi-binding/src/file.rs b/yazi-binding/src/file.rs deleted file mode 100644 index 0224fb91..00000000 --- a/yazi-binding/src/file.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::ops::Deref; - -use mlua::{AnyUserData, ExternalError, FromLua, Lua, ObjectLike, Table, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; - -use crate::{Cha, Url, impl_file_fields, impl_file_methods}; - -pub type FileRef = UserDataRef; - -const EXPECTED: &str = "expected a table, File, or fs::File"; - -#[derive(Clone)] -pub struct File { - pub(crate) inner: yazi_fs::File, -} - -impl Deref for File { - type Target = yazi_fs::File; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for yazi_fs::File { - fn from(value: File) -> Self { value.inner } -} - -impl File { - pub fn new(inner: impl Into) -> Self { Self { inner: inner.into() } } - - pub fn install(lua: &Lua) -> mlua::Result<()> { - lua.globals().raw_set("File", lua.create_function(|_, file: Self| Ok(file))?) - } -} - -impl TryFrom
for File { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - Ok(Self::new(yazi_fs::File { - url: value.raw_get::("url")?.into(), - cha: *value.raw_get::("cha")?, - ..Default::default() - })) - } -} - -impl TryFrom for File { - type Error = mlua::Error; - - fn try_from(value: AnyUserData) -> Result { - Ok(if let Ok(me) = value.take::() { me } else { value.get("bare")? }) - } -} - -impl FromLua for File { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(tbl) => Self::try_from(tbl), - Value::UserData(ud) => Self::try_from(ud), - _ => Err(EXPECTED.into_lua_err())?, - } - } -} - -impl UserData for File { - fn add_fields>(fields: &mut F) { - impl_file_fields!(fields); - } - - fn add_methods>(methods: &mut M) { - impl_file_methods!(methods); - - methods.add_method("icon", |_, me, ()| { - use crate::Icon; - // TODO: use a cache - Ok(yazi_config::THEME.icon.matches(me, false).map(Icon::from)) - }); - } -} diff --git a/yazi-binding/src/icon.rs b/yazi-binding/src/icon.rs deleted file mode 100644 index c0ddbb53..00000000 --- a/yazi-binding/src/icon.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::ops::Deref; - -use mlua::{IntoLua, Lua, Value}; - -use crate::Style; - -pub struct Icon { - inner: yazi_config::Icon, -} - -impl Deref for Icon { - type Target = yazi_config::Icon; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From for Icon { - fn from(inner: yazi_config::Icon) -> Self { Self { inner } } -} - -impl IntoLua for Icon { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua - .create_table_from([ - ("text", self.inner.text.into_lua(lua)?), - ("style", Style::from(self.inner.style).into_lua(lua)?), - ])? - .into_lua(lua) - } -} diff --git a/yazi-binding/src/image.rs b/yazi-binding/src/image.rs index 4a5bdfa9..24944ff7 100644 --- a/yazi-binding/src/image.rs +++ b/yazi-binding/src/image.rs @@ -1,17 +1,43 @@ -use std::ops::Deref; +use std::path::PathBuf; +use image::{ImageDecoder, ImageError}; use mlua::{MetaMethod, UserData, UserDataFields, UserDataMethods}; -pub struct ImageInfo(yazi_adapter::ImageInfo); - -impl Deref for ImageInfo { - type Target = yazi_adapter::ImageInfo; - - fn deref(&self) -> &Self::Target { &self.0 } +// --- ImageInfo +#[derive(Clone, Copy)] +pub struct ImageInfo { + pub format: image::ImageFormat, + pub width: u32, + pub height: u32, + pub color: image::ColorType, + pub orientation: Option, } -impl From for ImageInfo { - fn from(value: yazi_adapter::ImageInfo) -> Self { Self(value) } +impl ImageInfo { + pub async fn new(path: PathBuf) -> image::ImageResult { + tokio::task::spawn_blocking(move || { + let reader = image::ImageReader::open(path)?.with_guessed_format()?; + + let Some(format) = reader.format() else { + return Err(ImageError::IoError(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "unknown image format", + ))); + }; + + let mut decoder = reader.into_decoder()?; + let (width, height) = decoder.dimensions(); + Ok(Self { + format, + width, + height, + color: decoder.color_type(), + orientation: decoder.orientation().ok(), + }) + }) + .await + .map_err(|e| ImageError::IoError(e.into()))? + } } impl UserData for ImageInfo { @@ -25,12 +51,12 @@ impl UserData for ImageInfo { } // --- ImageFormat -struct ImageFormat(yazi_adapter::ImageFormat); +struct ImageFormat(image::ImageFormat); impl UserData for ImageFormat { fn add_methods>(methods: &mut M) { methods.add_meta_method(MetaMethod::ToString, |_, me, ()| { - use yazi_adapter::ImageFormat as F; + use image::ImageFormat as F; Ok(match me.0 { F::Png => "PNG", @@ -55,12 +81,12 @@ impl UserData for ImageFormat { } // --- ImageColor -struct ImageColor(yazi_adapter::ImageColor); +struct ImageColor(image::ColorType); impl UserData for ImageColor { fn add_methods>(methods: &mut M) { methods.add_meta_method(MetaMethod::ToString, |_, me, ()| { - use yazi_adapter::ImageColor as C; + use image::ColorType as C; Ok(match me.0 { C::L8 => "L8", diff --git a/yazi-binding/src/keymap/chord.rs b/yazi-binding/src/keymap/chord.rs deleted file mode 100644 index 9c417349..00000000 --- a/yazi-binding/src/keymap/chord.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::ops::Deref; - -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value}; -use serde::Deserialize; -use yazi_codegen::FromLuaOwned; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::{Id, Iter, event::Actions, keymap::Key}; - -#[derive(FromLuaOwned)] -pub struct Chord { - inner: yazi_config::keymap::ChordArc, -} - -impl Deref for Chord { - type Target = yazi_config::keymap::ChordArc; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl From<&yazi_config::keymap::ChordArc> for Chord { - fn from(value: &yazi_config::keymap::ChordArc) -> Self { Self { inner: value.clone() } } -} - -impl From for yazi_config::keymap::ChordArc { - fn from(value: Chord) -> Self { value.inner } -} - -impl Chord { - pub fn new(inner: impl Into) -> Self { - Self { inner: inner.into() } - } -} - -impl TryFrom<(Value, yazi_shared::Layer)> for Chord { - type Error = mlua::Error; - - fn try_from((value, layer): (Value, yazi_shared::Layer)) -> Result { - use yazi_config::keymap::Chord as C; - use yazi_shared::Layer as L; - - let de = mlua::serde::Deserializer::new(value); - let inner = match layer { - L::Null => C::<{ L::Null as u8 }>::deserialize(de)?.into(), - L::App => C::<{ L::App as u8 }>::deserialize(de)?.into(), - L::Mgr => C::<{ L::Mgr as u8 }>::deserialize(de)?.into(), - L::Tasks => C::<{ L::Tasks as u8 }>::deserialize(de)?.into(), - L::Spot => C::<{ L::Spot as u8 }>::deserialize(de)?.into(), - L::Pick => C::<{ L::Pick as u8 }>::deserialize(de)?.into(), - L::Input => C::<{ L::Input as u8 }>::deserialize(de)?.into(), - L::Confirm => C::<{ L::Confirm as u8 }>::deserialize(de)?.into(), - L::Help => C::<{ L::Help as u8 }>::deserialize(de)?.into(), - L::Cmp => C::<{ L::Cmp as u8 }>::deserialize(de)?.into(), - L::Which => C::<{ L::Which as u8 }>::deserialize(de)?.into(), - L::Notify => C::<{ L::Notify as u8 }>::deserialize(de)?.into(), - }; - - Ok(Self { inner }) - } -} - -impl UserData for Chord { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id))); - - fields - .add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key))); - - fields.add_cached_field("run", |_, me| Ok(Actions::new(me.run.clone()))); - - fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc)); - } -} - -// --- Matcher -pub struct ChordMatcher(pub(super) yazi_config::keymap::ChordMatcher); - -impl TryFrom
for ChordMatcher { - type Error = mlua::Error; - - fn try_from(value: Table) -> Result { - let id: Id = value.raw_get("id").unwrap_or_default(); - - Ok(Self(yazi_config::keymap::ChordMatcher { id: id.0, ..Default::default() })) - } -} - -impl FromLua for ChordMatcher { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::Table(t) => t.try_into(), - _ => Err("expected a table of ChordMatcher".into_lua_err()), - } - } -} - -// --- ChordIter -pub struct ChordIter(pub(super) yazi_config::keymap::ChordIter); - -impl ChordIter { - pub fn new(inner: impl Into) -> Self { Self(inner.into()) } -} - -impl IntoLua for ChordIter { - fn into_lua(self, lua: &Lua) -> mlua::Result { - Iter::new(self.0.map(Chord::new), None).into_lua(lua) - } -} diff --git a/yazi-binding/src/keymap/chords.rs b/yazi-binding/src/keymap/chords.rs deleted file mode 100644 index 2b1a3f6a..00000000 --- a/yazi-binding/src/keymap/chords.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::ops::Deref; - -use mlua::{ExternalError, ExternalResult, MetaMethod, Table, UserData, UserDataMethods, Value}; -use yazi_shim::mlua::DeserializeOverLua; - -use super::{Chord, ChordMatcher}; -use crate::{event::Actions, keymap::ChordIter}; - -pub struct Chords { - pub(super) inner: &'static yazi_config::keymap::Chords, - pub(super) layer: yazi_shared::Layer, -} - -impl Deref for Chords { - type Target = yazi_config::keymap::Chords; - - fn deref(&self) -> &Self::Target { self.inner } -} - -impl UserData for Chords { - fn add_methods>(methods: &mut M) { - methods.add_method("match", |_, me, matcher: Option| { - Ok(match matcher { - Some(matcher) => ChordIter::new(yazi_config::keymap::ChordIter { - chords: me.as_erased(), - matcher: matcher.0, - ..Default::default() - }), - None => ChordIter::new(&*me.inner), - }) - }); - - methods.add_method("insert", |_, me, (index, value): (isize, Value)| { - let index = match index { - 1.. => index - 1, - 0 => return Err("index must be 1-based or negative".into_lua_err()), - _ => index, - }; - - let chord = Chord::try_from((value, me.layer))?; - me.insert(index, chord.clone()).into_lua_err()?; - - Ok(chord) - }); - - methods.add_method("remove", |_, me, matcher: ChordMatcher| { - me.remove(matcher.0); - Ok(()) - }); - - methods.add_method("update", |_, me, (matcher, table): (ChordMatcher, Table)| { - let mut run: Option = table.raw_get("run")?; - if let Some(run) = &mut run { - table.raw_remove("run")?; - run.set(me.layer, yazi_shared::Source::Key); - } - - me.update(matcher.0, |mut chord| { - chord = chord.deserialize_over_lua(&table)?; - if let Some(run) = &run { - chord.run = run.clone().into(); - } - Ok(chord) - }) - }); - - methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.load().len())); - } -} diff --git a/yazi-binding/src/keymap/key.rs b/yazi-binding/src/keymap/key.rs deleted file mode 100644 index 2e35cad7..00000000 --- a/yazi-binding/src/keymap/key.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::ops::Deref; - -use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; - -use crate::SER_OPT; - -#[derive(Clone, Copy, Default, FromLua)] -pub struct Key(pub yazi_config::keymap::Key); - -impl Deref for Key { - type Target = yazi_config::keymap::Key; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl IntoLua for Key { - fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self.0, SER_OPT) } -} diff --git a/yazi-binding/src/keymap/mod.rs b/yazi-binding/src/keymap/mod.rs deleted file mode 100644 index b8f2ac0e..00000000 --- a/yazi-binding/src/keymap/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(chord chords key section); diff --git a/yazi-binding/src/keymap/section.rs b/yazi-binding/src/keymap/section.rs deleted file mode 100644 index 48fd44a9..00000000 --- a/yazi-binding/src/keymap/section.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::ops::Deref; - -use anyhow::bail; -use mlua::{UserData, UserDataFields}; -use yazi_config::KEYMAP; -use yazi_shim::mlua::UserDataFieldsExt; - -use crate::keymap::Chords; - -pub struct KeymapSection { - inner: &'static yazi_config::keymap::KeymapSection, - layer: yazi_shared::Layer, -} - -impl Deref for KeymapSection { - type Target = yazi_config::keymap::KeymapSection; - - fn deref(&self) -> &Self::Target { self.inner } -} - -impl TryFrom for KeymapSection { - type Error = anyhow::Error; - - fn try_from(layer: yazi_shared::Layer) -> Result { - use yazi_shared::Layer as L; - - let inner = match layer { - L::Null | L::App => bail!("invalid layer"), - L::Mgr => KEYMAP.mgr.as_erased(), - L::Tasks => KEYMAP.tasks.as_erased(), - L::Spot => KEYMAP.spot.as_erased(), - L::Pick => KEYMAP.pick.as_erased(), - L::Input => KEYMAP.input.as_erased(), - L::Confirm => KEYMAP.confirm.as_erased(), - L::Help => KEYMAP.help.as_erased(), - L::Cmp => KEYMAP.cmp.as_erased(), - L::Which => bail!("invalid layer"), - L::Notify => bail!("invalid layer"), - }; - - Ok(Self { inner, layer }) - } -} - -impl UserData for KeymapSection { - fn add_fields>(fields: &mut F) { - fields.add_cached_field("rules", |_, me| Ok(Chords { inner: me.inner, layer: me.layer })); - } -} diff --git a/yazi-binding/src/layer.rs b/yazi-binding/src/layer.rs deleted file mode 100644 index 1e85aa92..00000000 --- a/yazi-binding/src/layer.rs +++ /dev/null @@ -1,15 +0,0 @@ -use mlua::{MetaMethod, UserData, UserDataMethods}; -use yazi_shim::strum::IntoStr; - -#[derive(Clone, Copy)] -pub struct Layer(yazi_shared::Layer); - -impl From for Layer { - fn from(event: yazi_shared::Layer) -> Self { Self(event) } -} - -impl UserData for Layer { - fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.0.into_str())); - } -} diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index 8c52be9e..3e92c973 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -1,5 +1,5 @@ mod macros; -yazi_macro::mod_pub!(config elements event keymap process theme); +yazi_macro::mod_pub!(elements position process style); -yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector sendable stage style tty url utils); +yazi_macro::mod_flat!(chan composer error handle image iter permit range runtime); diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index bba655bf..272ebf87 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -50,8 +50,7 @@ macro_rules! impl_area_method { "area", |lua, (ud, area): (mlua::AnyUserData, Option)| { use mlua::IntoLua; - - use crate::elements::Spatial; + use $crate::elements::Spatial; if let Some(v) = area { ud.borrow_mut::()?.set_area((&v).try_into()?); @@ -67,7 +66,7 @@ macro_rules! impl_area_method { #[macro_export] macro_rules! impl_style_method { ($methods:ident, $($field:tt).+) => { - $methods.add_function("style", |_, (ud, style): (mlua::AnyUserData, $crate::Style)| { + $methods.add_function("style", |_, (ud, style): (mlua::AnyUserData, $crate::style::Style)| { ud.borrow_mut::()?.$($field).+ = style.0; Ok(ud) }); @@ -208,21 +207,22 @@ macro_rules! impl_file_fields { ($fields:ident) => { use yazi_shim::mlua::UserDataFieldsExt; - $fields.add_cached_field("cha", |_, me| Ok($crate::Cha(me.cha))); - $fields.add_cached_field("url", |_, me| Ok($crate::Url::new(me.url_owned()))); - $fields.add_cached_field("link_to", |_, me| Ok(me.link_to.as_ref().map($crate::Path::new))); + $fields.add_cached_field("cha", |_, me| Ok(me.cha)); + $fields.add_cached_field("url", |_, me| Ok(me.url_owned())); + $fields.add_cached_field("link_to", |_, me| Ok(me.link_to.clone())); $fields.add_cached_field("name", |lua, me| { me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose() }); $fields.add_cached_field("path", |_, me| { use yazi_fs::FsUrl; - use yazi_shared::url::AsUrl; - Ok($crate::Path::new(me.url.as_url().unified_path())) + use yazi_shared::{path::PathBufDyn, url::AsUrl}; + Ok(PathBufDyn::from(me.url.as_url().unified_path())) }); $fields.add_cached_field("cache", |_, me| { use yazi_fs::FsUrl; - Ok(me.url.cache().map($crate::Path::new)) + use yazi_shared::path::PathBufDyn; + Ok(me.url.cache().map(PathBufDyn::from)) }); }; } diff --git a/yazi-binding/src/mouse.rs b/yazi-binding/src/mouse.rs deleted file mode 100644 index 8d837b7d..00000000 --- a/yazi-binding/src/mouse.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::ops::Deref; - -use mlua::{UserData, UserDataFields}; -use yazi_term::event::{MouseButton, MouseEventKind}; - -#[derive(Clone, Copy)] -pub struct MouseEvent(yazi_term::event::MouseEvent); - -impl Deref for MouseEvent { - type Target = yazi_term::event::MouseEvent; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl From for MouseEvent { - fn from(event: yazi_term::event::MouseEvent) -> Self { Self(event) } -} - -impl UserData for MouseEvent { - fn add_fields>(fields: &mut F) { - fields.add_field("type", "legacy"); - fields.add_field_method_get("x", |_, me| Ok(me.column)); - fields.add_field_method_get("y", |_, me| Ok(me.row)); - fields.add_field_method_get("is_left", |_, me| { - use MouseEventKind as K; - Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Left)) - }); - fields.add_field_method_get("is_right", |_, me| { - use MouseEventKind as K; - Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Right)) - }); - fields.add_field_method_get("is_middle", |_, me| { - use MouseEventKind as K; - Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Middle)) - }); - } -} diff --git a/yazi-binding/src/position/mod.rs b/yazi-binding/src/position/mod.rs new file mode 100644 index 00000000..eef64651 --- /dev/null +++ b/yazi-binding/src/position/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(offset origin position); diff --git a/yazi-config/src/popup/offset.rs b/yazi-binding/src/position/offset.rs similarity index 100% rename from yazi-config/src/popup/offset.rs rename to yazi-binding/src/position/offset.rs diff --git a/yazi-config/src/popup/origin.rs b/yazi-binding/src/position/origin.rs similarity index 100% rename from yazi-config/src/popup/origin.rs rename to yazi-binding/src/position/origin.rs diff --git a/yazi-config/src/popup/position.rs b/yazi-binding/src/position/position.rs similarity index 93% rename from yazi-config/src/popup/position.rs rename to yazi-binding/src/position/position.rs index fa48c69b..f370012e 100644 --- a/yazi-config/src/popup/position.rs +++ b/yazi-binding/src/position/position.rs @@ -1,7 +1,6 @@ use std::ops::{Deref, DerefMut}; use ratatui::{layout::Rect, widgets::Padding}; -use yazi_term::Dimension; use super::{Offset, Origin}; @@ -24,7 +23,7 @@ impl DerefMut for Position { impl Position { pub const fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } } - pub fn rect(&self, Dimension { cols, rows, .. }: Dimension) -> Rect { + pub fn rect(&self, (cols, rows): (u16, u16)) -> Rect { use Origin::*; let Offset { x, y, width, height } = self.offset; @@ -54,7 +53,7 @@ impl Position { } } - pub fn sticky(Dimension { cols, rows, .. }: Dimension, base: Rect, offset: Offset) -> Rect { + pub fn sticky((cols, rows): (u16, u16), base: Rect, offset: Offset) -> Rect { let Offset { x, y, width, height } = offset; let above = diff --git a/yazi-binding/src/process/command.rs b/yazi-binding/src/process/command.rs index 519c6189..4949b266 100644 --- a/yazi-binding/src/process/command.rs +++ b/yazi-binding/src/process/command.rs @@ -2,7 +2,7 @@ use std::{any::TypeId, ffi::OsStr, io, process::Stdio}; use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; -use yazi_shared::wtf8::FromWtf8; +use yazi_shim::wtf8::FromWtf8; use super::{Child, output::Output}; use crate::{Error, process::Status}; diff --git a/yazi-binding/src/scheme.rs b/yazi-binding/src/scheme.rs deleted file mode 100644 index d4fe16a6..00000000 --- a/yazi-binding/src/scheme.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::ops::Deref; - -use mlua::{UserData, UserDataFields}; -use yazi_fs::FsScheme; -use yazi_shared::scheme::SchemeLike; -use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr}; - -use crate::Path; - -pub struct Scheme { - inner: yazi_shared::scheme::Scheme, -} - -impl Deref for Scheme { - type Target = yazi_shared::scheme::Scheme; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl Scheme { - pub fn new(scheme: impl Into) -> Self { - Self { inner: scheme.into() } - } -} - -impl UserData for Scheme { - fn add_fields>(fields: &mut F) { - fields.add_cached_field("kind", |_, me| Ok(me.kind().into_str())); - fields.add_cached_field("cache", |_, me| Ok(me.cache().map(Path::new))); - - fields.add_field_method_get("is_virtual", |_, me| Ok(me.is_virtual())); - } -} diff --git a/yazi-binding/src/selector.rs b/yazi-binding/src/selector.rs deleted file mode 100644 index d5d281ef..00000000 --- a/yazi-binding/src/selector.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::ops::Deref; - -use mlua::{FromLua, Lua, LuaSerdeExt, UserData, Value}; - -pub struct Selector(yazi_config::Selector); - -impl Deref for Selector { - type Target = yazi_config::Selector; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl From for yazi_config::Selector { - fn from(value: Selector) -> Self { value.0 } -} - -impl FromLua for Selector { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { Ok(Self(lua.from_value(value)?)) } -} - -impl UserData for Selector {} diff --git a/yazi-binding/src/stage.rs b/yazi-binding/src/stage.rs deleted file mode 100644 index 9678175c..00000000 --- a/yazi-binding/src/stage.rs +++ /dev/null @@ -1,21 +0,0 @@ -use mlua::{IntoLuaMulti, MetaMethod, UserData, UserDataMethods}; - -pub struct FolderStage(yazi_fs::FolderStage); - -impl FolderStage { - pub fn new(inner: yazi_fs::FolderStage) -> Self { Self(inner) } -} - -impl UserData for FolderStage { - fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Call, |lua, me, ()| { - use yazi_fs::FolderStage::*; - - match &me.0 { - Loading => false.into_lua_multi(lua), - Loaded => true.into_lua_multi(lua), - Failed(e) => (true, crate::Error::Fs(e.clone())).into_lua_multi(lua), - } - }); - } -} diff --git a/yazi-binding/src/style/mod.rs b/yazi-binding/src/style/mod.rs new file mode 100644 index 00000000..11ea7d99 --- /dev/null +++ b/yazi-binding/src/style/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(style style_flat); diff --git a/yazi-binding/src/style.rs b/yazi-binding/src/style/style.rs similarity index 89% rename from yazi-binding/src/style.rs rename to yazi-binding/src/style/style.rs index 45592ef2..29a19298 100644 --- a/yazi-binding/src/style.rs +++ b/yazi-binding/src/style/style.rs @@ -2,9 +2,9 @@ use std::ops::Deref; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::style::Modifier; -use yazi_shim::cell::SyncCell; +use yazi_shim::{cell::SyncCell, mlua::SER_OPT}; -use crate::{SER_OPT, elements::Color}; +use crate::{elements::Color, style::StyleFlat}; #[derive(Clone, Copy, Default)] pub struct Style(pub ratatui::style::Style); @@ -15,18 +15,18 @@ impl Deref for Style { fn deref(&self) -> &Self::Target { &self.0 } } -impl From for Style { - fn from(value: yazi_config::Style) -> Self { Self(value.into()) } +impl From for Style { + fn from(value: StyleFlat) -> Self { Self(value.into()) } +} + +impl From<&SyncCell> for Style { + fn from(value: &SyncCell) -> Self { value.get().into() } } impl From