From 4a2e5addcd097d2e4f367daf5f15d200cdb42434 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 21 Apr 2026 23:34:56 +0800 Subject: [PATCH] feat: new `app:theme` action that hot-reload user themes/flavors (#3906) --- CHANGELOG.md | 2 + Cargo.lock | 32 ++-- Cargo.toml | 4 +- flake.lock | 12 +- yazi-actor/src/app/mod.rs | 1 + yazi-actor/src/app/theme.rs | 25 +++ yazi-actor/src/lives/file.rs | 4 +- yazi-actor/src/lives/lives.rs | 2 +- yazi-actor/src/lives/preference.rs | 2 +- yazi-actor/src/mgr/linemode.rs | 4 +- yazi-adapter/src/drivers/kgp.rs | 2 +- yazi-adapter/src/drivers/ueberzug.rs | 4 +- yazi-adapter/src/lib.rs | 3 +- yazi-binding/src/icon.rs | 29 ++- yazi-binding/src/style.rs | 2 +- yazi-boot/Cargo.toml | 1 + yazi-boot/build.rs | 7 +- yazi-boot/src/lib.rs | 2 +- yazi-cli/build.rs | 7 +- yazi-codegen/src/lib.rs | 37 +++- yazi-config/src/keymap/chord.rs | 5 +- yazi-config/src/keymap/keymap.rs | 2 +- yazi-config/src/lib.rs | 11 +- yazi-config/src/mgr/linemode.rs | 23 --- yazi-config/src/mgr/mgr.rs | 21 ++- yazi-config/src/mgr/mod.rs | 2 +- yazi-config/src/opener/opener.rs | 21 +-- yazi-config/src/plugin/fetcher.rs | 6 +- yazi-config/src/plugin/preloader.rs | 6 +- yazi-config/src/plugin/previewer.rs | 6 +- yazi-config/src/plugin/spotter.rs | 6 +- yazi-config/src/theme/filetype.rs | 42 +---- yazi-config/src/theme/filetype_rule.rs | 27 +++ yazi-config/src/theme/filetype_rules.rs | 21 +++ yazi-config/src/theme/flavor.rs | 4 +- yazi-config/src/theme/icon.rs | 176 ++++++------------ yazi-config/src/theme/icon_cond.rs | 36 ++++ yazi-config/src/theme/icon_conds.rs | 32 ++++ yazi-config/src/theme/icon_glob.rs | 22 +++ yazi-config/src/theme/icon_globs.rs | 32 ++++ yazi-config/src/theme/icon_names.rs | 69 +++++++ yazi-config/src/theme/mod.rs | 2 +- yazi-config/src/theme/theme.rs | 39 ++-- yazi-core/Cargo.toml | 4 + yazi-core/src/app/plugin.rs | 4 +- yazi-core/src/tab/preference.rs | 6 +- yazi-dds/Cargo.toml | 1 + yazi-dds/build.rs | 10 +- yazi-dds/src/client.rs | 3 +- yazi-dds/src/pubsub.rs | 3 +- yazi-dds/src/pump.rs | 3 +- yazi-dds/src/server.rs | 3 +- yazi-dds/src/state.rs | 3 +- yazi-emulator/Cargo.toml | 1 + yazi-emulator/src/emulator.rs | 2 +- yazi-emulator/src/mux.rs | 2 +- yazi-fm/Cargo.toml | 1 + yazi-fm/src/confirm/buttons.rs | 5 +- yazi-fm/src/executor.rs | 4 + yazi-fm/src/logs.rs | 3 +- yazi-fm/src/router.rs | 2 +- yazi-fs/src/cwd.rs | 3 +- yazi-fs/src/mounts/partitions.rs | 2 +- yazi-parser/src/mgr/linemode.rs | 4 +- yazi-parser/src/spark/spark.rs | 3 + yazi-plugin/preset/plugins/file.lua | 13 +- yazi-plugin/src/runtime/runtime.rs | 2 +- yazi-plugin/src/standard.rs | 2 +- yazi-plugin/src/theme/theme.rs | 16 +- yazi-plugin/src/utils/call.rs | 9 +- yazi-runner/Cargo.toml | 5 + yazi-runner/src/lib.rs | 2 +- yazi-runner/src/loader/loader.rs | 3 +- yazi-scheduler/Cargo.toml | 4 + yazi-shared/Cargo.toml | 1 + yazi-shared/src/env.rs | 3 +- yazi-shared/src/event/action.rs | 67 +++---- yazi-shared/src/event/cmd.rs | 57 ++++++ yazi-shared/src/event/de_owned.rs | 2 +- yazi-shared/src/event/event.rs | 2 +- yazi-shared/src/event/mod.rs | 2 +- yazi-shared/src/layer.rs | 1 + yazi-shared/src/lib.rs | 2 +- yazi-shared/src/localset.rs | 3 +- yazi-shared/src/non_empty_string.rs | 12 +- yazi-shared/src/os.rs | 2 +- yazi-shared/src/pool/mod.rs | 4 +- yazi-shim/Cargo.toml | 5 + yazi-shim/src/cell/mod.rs | 1 + .../src => yazi-shim/src/cell}/ro_cell.rs | 0 .../src => yazi-shim/src/cell}/sync_cell.rs | 0 yazi-shim/src/lib.rs | 2 +- yazi-shim/src/serde/mod.rs | 1 + yazi-shim/src/serde/traits.rs | 27 +++ yazi-shim/src/toml/traits.rs | 58 +++++- yazi-term/Cargo.toml | 1 - yazi-term/src/semaphore.rs | 2 +- yazi-term/src/term.rs | 3 +- yazi-tty/Cargo.toml | 1 + yazi-tty/src/lib.rs | 2 +- yazi-vfs/Cargo.toml | 1 + yazi-vfs/src/provider/sftp/mod.rs | 4 +- yazi-watcher/Cargo.toml | 1 + yazi-watcher/src/lib.rs | 6 +- yazi-watcher/src/local/mod.rs | 3 +- yazi-widgets/Cargo.toml | 5 + yazi-widgets/src/clipboard.rs | 2 +- 107 files changed, 799 insertions(+), 405 deletions(-) create mode 100644 yazi-actor/src/app/theme.rs delete mode 100644 yazi-config/src/mgr/linemode.rs create mode 100644 yazi-config/src/theme/filetype_rule.rs create mode 100644 yazi-config/src/theme/filetype_rules.rs create mode 100644 yazi-config/src/theme/icon_cond.rs create mode 100644 yazi-config/src/theme/icon_conds.rs create mode 100644 yazi-config/src/theme/icon_glob.rs create mode 100644 yazi-config/src/theme/icon_globs.rs create mode 100644 yazi-config/src/theme/icon_names.rs create mode 100644 yazi-shared/src/event/cmd.rs create mode 100644 yazi-shim/src/cell/mod.rs rename {yazi-shared/src => yazi-shim/src/cell}/ro_cell.rs (100%) rename {yazi-shared/src => yazi-shim/src/cell}/sync_cell.rs (100%) create mode 100644 yazi-shim/src/serde/mod.rs create mode 100644 yazi-shim/src/serde/traits.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5a10ce..a38321e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - New `--in` for `search` action to set search directory ([#3696]) - Hover cursor over the new file after copying/cutting/linking/hardlinking/extracting ([#3846], [#3854]) - Multi-file spotter ([#3733]) +- New `app:theme` action that hot-reload user themes/flavors ([#3906]) - Dynamic open/opener Lua API ([#3901]) - Dynamic previewer Lua API ([#3891]) - Vim-like `lua` action that runs an inline Lua snippet ([#3813]) @@ -1705,3 +1706,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3891]: https://github.com/sxyazi/yazi/pull/3891 [#3894]: https://github.com/sxyazi/yazi/pull/3894 [#3901]: https://github.com/sxyazi/yazi/pull/3901 +[#3906]: https://github.com/sxyazi/yazi/pull/3906 diff --git a/Cargo.lock b/Cargo.lock index 02671f1b..5da11f00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2866,9 +2866,9 @@ dependencies = [ [[package]] name = "p256" -version = "0.14.0-rc.8" +version = "0.14.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f0a10fe314869359cb2901342b045f4e5a962ef9febc006f03d2a8c848fe4c" +checksum = "8b97e3bf0465157ae90975ff52dbeb1362ba618924878c9f74c25baa27a65f9a" dependencies = [ "ecdsa", "elliptic-curve", @@ -2879,9 +2879,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.14.0-rc.8" +version = "0.14.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b079e66810c55ab3d6ba424e056dc4aefcdb8046c8c3f3816142edbdd7af7721" +checksum = "437f30ebcb1e16ff48acead5f08bd69fbcdbc82421687bb48af5c315a0bfab03" dependencies = [ "ecdsa", "elliptic-curve", @@ -2893,9 +2893,9 @@ dependencies = [ [[package]] name = "p521" -version = "0.14.0-rc.8" +version = "0.14.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eecc34c4c6e6596d5271fecf90ac4f16593fa198e77282214d0c22736aa9266" +checksum = "4e9fd792bab86ecf6249561752fb5a413511f999887107dd054bbda5143743d7" dependencies = [ "base16ct", "ecdsa", @@ -3795,9 +3795,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.60.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b530252dc3ff163b73a7e48c97b925450d2ca53edcb466a46ad0a231e45f998" +checksum = "d937f3f4a79bffd67fc12fd437785effdfc8b94edc89ab90392f9ac9e11cc9fc" dependencies = [ "aes 0.8.4", "bitflags 2.11.1", @@ -4898,9 +4898,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -5884,6 +5884,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-shim", "yazi-vfs", ] @@ -6016,6 +6017,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-shim", ] [[package]] @@ -6030,6 +6032,7 @@ dependencies = [ "tracing", "yazi-macro", "yazi-shared", + "yazi-shim", "yazi-tty", ] @@ -6081,6 +6084,7 @@ dependencies = [ "yazi-runner", "yazi-scheduler", "yazi-shared", + "yazi-shim", "yazi-term", "yazi-tty", "yazi-vfs", @@ -6234,6 +6238,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-shim", ] [[package]] @@ -6301,6 +6306,7 @@ dependencies = [ "uzers", "windows-sys 0.61.2", "yazi-macro", + "yazi-shim", ] [[package]] @@ -6309,6 +6315,7 @@ version = "26.2.2" dependencies = [ "arc-swap", "crossterm 0.29.0", + "hashbrown 0.17.0", "mlua", "ratatui", "serde", @@ -6331,7 +6338,6 @@ dependencies = [ "yazi-config", "yazi-emulator", "yazi-macro", - "yazi-shared", "yazi-shim", "yazi-tty", ] @@ -6346,6 +6352,7 @@ dependencies = [ "windows-sys 0.61.2", "yazi-macro", "yazi-shared", + "yazi-shim", ] [[package]] @@ -6368,6 +6375,7 @@ dependencies = [ "yazi-macro", "yazi-sftp", "yazi-shared", + "yazi-shim", ] [[package]] @@ -6387,6 +6395,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-shim", "yazi-vfs", ] @@ -6409,6 +6418,7 @@ dependencies = [ "yazi-config", "yazi-macro", "yazi-shared", + "yazi-shim", "yazi-tty", ] diff --git a/Cargo.toml b/Cargo.toml index 45da0f1b..b46089e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT" authors = [ "sxyazi " ] homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" -rust-version = "1.92.0" +rust-version = "1.95.0" [profile.dev] debug = "line-tables-only" @@ -63,7 +63,7 @@ percent-encoding = "2.3.2" rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } ratatui = { version = "0.30.0", features = [ "serde", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.3" -russh = { version = "0.60.0", default-features = false, features = [ "ring", "rsa" ] } +russh = { version = "0.60.1", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.149" diff --git a/flake.lock b/flake.lock index 144f9b1a..d485f39e 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1772419343, - "narHash": "sha256-QU3Cd5DJH7dHyMnGEFfPcZDaCAsJQ6tUD+JuUsYqnKU=", + "lastModified": 1776329215, + "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "93178f6a00c22fcdee1c6f5f9ab92f2072072ea9", + "rev": "b86751bc4085f48661017fa226dee99fab6c651b", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1772420823, - "narHash": "sha256-q3oVwz1Rx41D1D+F6vg41kpOkk3Zi3KwnkHEZp7DCGs=", + "lastModified": 1776741231, + "narHash": "sha256-k9G98qzn+7npROUaks8VqCFm7cFtEG8ulQLBBo5lItg=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "458eea8d905c609e9d889423e6b8a1c7bc2f792c", + "rev": "02061303f7c4c964f7b4584dabd9e985b4cd442b", "type": "github" }, "original": { diff --git a/yazi-actor/src/app/mod.rs b/yazi-actor/src/app/mod.rs index 931cacbf..0b0a6086 100644 --- a/yazi-actor/src/app/mod.rs +++ b/yazi-actor/src/app/mod.rs @@ -12,6 +12,7 @@ yazi_macro::mod_flat!( resize resume stop + theme title update_progress ); diff --git a/yazi-actor/src/app/theme.rs b/yazi-actor/src/app/theme.rs new file mode 100644 index 00000000..ea7a510c --- /dev/null +++ b/yazi-actor/src/app/theme.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use yazi_actor::Ctx; +use yazi_config::{THEME, build_flavor}; +use yazi_emulator::EMULATOR; +use yazi_macro::{render, succ}; +use yazi_parser::VoidForm; +use yazi_shared::data::Data; +use yazi_shim::serde::Overlay; + +use crate::Actor; + +pub struct Theme; + +impl Actor for Theme { + type Form = VoidForm; + + const NAME: &str = "theme"; + + fn act(_cx: &mut Ctx, _: Self::Form) -> Result { + THEME.overlay(build_flavor(EMULATOR.light, true)?); + yazi_plugin::theme::reset()?; + + succ!(render!()); + } +} diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index 83ae6ca1..3f259675 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -2,7 +2,7 @@ use std::{ops::Deref, ptr}; use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value}; use yazi_binding::{Range, Style, cached_field}; -use yazi_config::{Selectable, THEME}; +use yazi_config::THEME; use yazi_shared::{path::AsPath, url::UrlLike}; use super::Lives; @@ -111,7 +111,7 @@ impl UserData for File { 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.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style)) + THEME.filetype.match_style(me, mime).map(Style::from) }) }); methods.add_method("is_yanked", |lua, me, ()| { diff --git a/yazi-actor/src/lives/lives.rs b/yazi-actor/src/lives/lives.rs index 1a2b5fff..1b969cd4 100644 --- a/yazi-actor/src/lives/lives.rs +++ b/yazi-actor/src/lives/lives.rs @@ -5,7 +5,7 @@ use mlua::{AnyUserData, UserData}; use scopeguard::defer; use tracing::error; use yazi_plugin::LUA; -use yazi_shared::RoCell; +use yazi_shim::cell::RoCell; use super::{Core, PtrCell}; diff --git a/yazi-actor/src/lives/preference.rs b/yazi-actor/src/lives/preference.rs index 01dff8a7..011a8c64 100644 --- a/yazi-actor/src/lives/preference.rs +++ b/yazi-actor/src/lives/preference.rs @@ -38,7 +38,7 @@ 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)); + cached_field!(fields, linemode, |lua, me| lua.create_string(&*me.linemode)); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); // Sorting diff --git a/yazi-actor/src/mgr/linemode.rs b/yazi-actor/src/mgr/linemode.rs index e78635f8..ab36b1f1 100644 --- a/yazi-actor/src/mgr/linemode.rs +++ b/yazi-actor/src/mgr/linemode.rs @@ -15,8 +15,8 @@ impl Actor for Linemode { fn act(cx: &mut Ctx, form: Self::Form) -> Result { let tab = cx.tab_mut(); - if form.new != tab.pref.linemode { - tab.pref.linemode = form.new.into_owned(); + if form.new != *tab.pref.linemode { + tab.pref.linemode = form.new.into(); render!(); } diff --git a/yazi-adapter/src/drivers/kgp.rs b/yazi-adapter/src/drivers/kgp.rs index b2498448..fdab8251 100644 --- a/yazi-adapter/src/drivers/kgp.rs +++ b/yazi-adapter/src/drivers/kgp.rs @@ -7,7 +7,7 @@ use crossterm::{cursor::MoveTo, queue}; use image::DynamicImage; use ratatui::layout::Rect; use yazi_emulator::{CLOSE, ESCAPE, Emulator, START}; -use yazi_shared::SyncCell; +use yazi_shim::cell::SyncCell; use crate::{adapter::Adapter, image::Image}; diff --git a/yazi-adapter/src/drivers/ueberzug.rs b/yazi-adapter/src/drivers/ueberzug.rs index 0c86073c..2e8c343e 100644 --- a/yazi-adapter/src/drivers/ueberzug.rs +++ b/yazi-adapter/src/drivers/ueberzug.rs @@ -7,8 +7,8 @@ use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, Unb use tracing::{debug, warn}; use yazi_config::YAZI; use yazi_emulator::Dimension; -use yazi_shared::{LOG_LEVEL, RoCell, env_exists}; -use yazi_shim::strum::IntoStr; +use yazi_shared::{LOG_LEVEL, env_exists}; +use yazi_shim::{cell::RoCell, strum::IntoStr}; use crate::Adapter; diff --git a/yazi-adapter/src/lib.rs b/yazi-adapter/src/lib.rs index e6cc408a..01080816 100644 --- a/yazi-adapter/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -3,7 +3,8 @@ yazi_macro::mod_pub!(drivers); yazi_macro::mod_flat!(adapter adapters icc image info); use yazi_emulator::{Brand, CLOSE, EMULATOR, ESCAPE, Emulator, Mux, START, TMUX}; -use yazi_shared::{SyncCell, in_wsl}; +use yazi_shared::in_wsl; +use yazi_shim::cell::SyncCell; pub static ADAPTOR: SyncCell = SyncCell::new(Adapter::Chafa); diff --git a/yazi-binding/src/icon.rs b/yazi-binding/src/icon.rs index cef7fe07..c0ddbb53 100644 --- a/yazi-binding/src/icon.rs +++ b/yazi-binding/src/icon.rs @@ -1,31 +1,30 @@ use std::ops::Deref; -use mlua::{UserData, UserDataFields, Value}; +use mlua::{IntoLua, Lua, Value}; -use crate::{Style, cached_field}; +use crate::Style; pub struct Icon { - inner: &'static yazi_config::Icon, - - v_text: Option, - v_style: Option, + inner: yazi_config::Icon, } impl Deref for Icon { type Target = yazi_config::Icon; - fn deref(&self) -> &Self::Target { self.inner } + fn deref(&self) -> &Self::Target { &self.inner } } -impl From<&'static yazi_config::Icon> for Icon { - fn from(icon: &'static yazi_config::Icon) -> Self { - Self { inner: icon, v_text: None, v_style: None } - } +impl From for Icon { + fn from(inner: yazi_config::Icon) -> Self { Self { inner } } } -impl UserData for Icon { - fn add_fields>(fields: &mut F) { - cached_field!(fields, text, |lua, me| lua.create_string(&me.text)); - cached_field!(fields, style, |_, me| Ok(Style::from(me.style))); +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/style.rs b/yazi-binding/src/style.rs index 83c0899a..0bbd0d97 100644 --- a/yazi-binding/src/style.rs +++ b/yazi-binding/src/style.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui::style::Modifier; -use yazi_shared::SyncCell; +use yazi_shim::cell::SyncCell; use crate::{SER_OPT, elements::Color}; diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index f9ddfda0..3e425674 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -19,6 +19,7 @@ yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } yazi-fs = { path = "../yazi-fs", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-shim = { path = "../yazi-shim", version = "26.2.2" } yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } # External dependencies diff --git a/yazi-boot/build.rs b/yazi-boot/build.rs index 0bf01ae0..93ea359e 100644 --- a/yazi-boot/build.rs +++ b/yazi-boot/build.rs @@ -18,9 +18,14 @@ fn main() -> Result<(), Box> { .semver(true) .build()?, )? - .add_instructions(&GitclBuilder::default().commit_date(true).sha(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/lib.rs b/yazi-boot/src/lib.rs index 7d1260bf..0f9f5f98 100644 --- a/yazi-boot/src/lib.rs +++ b/yazi-boot/src/lib.rs @@ -3,7 +3,7 @@ yazi_macro::mod_pub!(actions); yazi_macro::mod_flat!(args boot); use clap::Parser; -use yazi_shared::RoCell; +use yazi_shim::cell::RoCell; pub static ARGS: RoCell = RoCell::new(); pub static BOOT: RoCell = RoCell::new(); diff --git a/yazi-cli/build.rs b/yazi-cli/build.rs index 8c65f534..32995dea 100644 --- a/yazi-cli/build.rs +++ b/yazi-cli/build.rs @@ -24,9 +24,14 @@ fn main() -> Result<(), Box> { fn generate() -> Result<(), Box> { Emitter::default() .add_instructions(&BuildBuilder::default().build_date(true).build()?)? - .add_instructions(&GitclBuilder::default().commit_date(true).sha(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-codegen/src/lib.rs b/yazi-codegen/src/lib.rs index 0c50d3a0..b63a4fad 100644 --- a/yazi-codegen/src/lib.rs +++ b/yazi-codegen/src/lib.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use quote::quote; mod helper; -use syn::{DeriveInput, parse_macro_input}; +use syn::{Data, DeriveInput, Fields, parse_macro_input}; use crate::helper::{generics_with_de, has_serde_attr, ident_name, named_fields}; @@ -153,6 +153,41 @@ pub fn deserialize_over2(input: TokenStream) -> TokenStream { .into() } +#[proc_macro_derive(Overlay)] +pub fn overlay(input: TokenStream) -> TokenStream { + let DeriveInput { ident, generics, data, .. } = parse_macro_input!(input as DeriveInput); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let stmts: Vec<_> = match data { + Data::Struct(s) => match s.fields { + Fields::Named(fields) => fields + .named + .into_iter() + .map(|f| { + let field_ident = f.ident; + quote! { self.#field_ident.overlay(new.#field_ident); } + }) + .collect(), + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + vec![quote! { self.0.overlay(new.0); }] + } + _ => panic!("expected named fields or a single-field tuple struct"), + }, + _ => panic!("expected struct"), + }; + + quote! { + impl #impl_generics yazi_shim::serde::Overlay for #ident #ty_generics #where_clause { + fn overlay(&self, new: Self) { + use yazi_shim::serde::Overlay; + + #(#stmts)* + } + } + } + .into() +} + #[proc_macro_derive(FromLuaOwned)] pub fn from_lua(input: TokenStream) -> TokenStream { let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput); diff --git a/yazi-config/src/keymap/chord.rs b/yazi-config/src/keymap/chord.rs index 741fb9ef..d99ee567 100644 --- a/yazi-config/src/keymap/chord.rs +++ b/yazi-config/src/keymap/chord.rs @@ -82,9 +82,6 @@ where D: Deserializer<'de>, { let mut actions: Vec = OneOrMany::::deserialize_as(deserializer)?; - if actions.is_empty() { - return Err(de::Error::custom("'run' cannot be empty")); - } let Some(layer) = Layer::from_repr(L) else { return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); @@ -92,7 +89,7 @@ where for action in &mut actions { action.source = Source::Key; - if action.layer == Default::default() { + if action.layer == Layer::Null { action.layer = layer; } } diff --git a/yazi-config/src/keymap/keymap.rs b/yazi-config/src/keymap/keymap.rs index 5830c500..98bdca94 100644 --- a/yazi-config/src/keymap/keymap.rs +++ b/yazi-config/src/keymap/keymap.rs @@ -21,7 +21,7 @@ pub struct Keymap { impl Keymap { pub fn get(&self, layer: Layer) -> &[Chord] { match layer { - Layer::App => &[], + 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(), diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index f7dcaff9..09ccd112 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -4,8 +4,7 @@ yazi_macro::mod_flat!(icon layout mixing pattern platform preset priority select use std::io::{Read, Write}; -use yazi_shared::{RoCell, SyncCell}; -use yazi_shim::toml::{DeserializeOver, DeserializeOverWith}; +use yazi_shim::{cell::{RoCell, SyncCell}, toml::{DeserializeOver, DeserializeOverWith}}; use yazi_tty::TTY; pub static YAZI: RoCell = RoCell::new(); @@ -44,6 +43,11 @@ pub fn init_flavor(light: bool) -> anyhow::Result<()> { } fn try_init_flavor(light: bool, merge: bool) -> anyhow::Result<()> { + THEME.init(build_flavor(light, merge)?); + Ok(()) +} + +pub fn build_flavor(light: bool, merge: bool) -> anyhow::Result { let mut preset = Preset::theme(light)?; if merge { @@ -59,8 +63,7 @@ fn try_init_flavor(light: bool, merge: bool) -> anyhow::Result<()> { )?; } - THEME.init(preset.reshape(light)?); - Ok(()) + Ok(preset.reshape(light)?) } fn wait_for_key(e: anyhow::Error) -> anyhow::Result<()> { diff --git a/yazi-config/src/mgr/linemode.rs b/yazi-config/src/mgr/linemode.rs deleted file mode 100644 index e9afdb6e..00000000 --- a/yazi-config/src/mgr/linemode.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::ops::Deref; - -use serde::{Deserialize, Deserializer, de}; - -#[derive(Debug)] -pub struct MgrLinemode(String); - -impl Deref for MgrLinemode { - type Target = String; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl<'de> Deserialize<'de> for MgrLinemode { - fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - if s.is_empty() || s.len() > 20 { - return Err(de::Error::custom("linemode must be between 1 and 20 characters.")); - } - - Ok(Self(s)) - } -} diff --git a/yazi-config/src/mgr/mgr.rs b/yazi-config/src/mgr/mgr.rs index 68095f80..8db1e4f6 100644 --- a/yazi-config/src/mgr/mgr.rs +++ b/yazi-config/src/mgr/mgr.rs @@ -1,11 +1,11 @@ use anyhow::Result; -use serde::Deserialize; +use arc_swap::ArcSwap; +use serde::{Deserialize, Deserializer, de}; use yazi_codegen::{DeserializeOver, DeserializeOver2}; use yazi_fs::{SortBy, SortFallback}; -use yazi_shared::SyncCell; +use yazi_shim::{arc_swap::IntoPointee, cell::SyncCell}; use super::{MgrRatio, MouseEvents}; -use crate::mgr::MgrLinemode; #[derive(Debug, Deserialize, DeserializeOver, DeserializeOver2)] pub struct Mgr { @@ -20,9 +20,22 @@ pub struct Mgr { pub sort_fallback: SyncCell, // Display - pub linemode: MgrLinemode, + #[serde(deserialize_with = "deserialize_linemode")] + pub linemode: ArcSwap, pub show_hidden: SyncCell, pub show_symlink: SyncCell, pub scrolloff: SyncCell, pub mouse_events: SyncCell, } + +fn deserialize_linemode<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + if s.is_empty() || s.len() > 20 { + return Err(de::Error::custom("linemode must be between 1 and 20 characters.")); + } + + Ok(s.into_pointee()) +} diff --git a/yazi-config/src/mgr/mod.rs b/yazi-config/src/mgr/mod.rs index 4a8bdec2..92e09e50 100644 --- a/yazi-config/src/mgr/mod.rs +++ b/yazi-config/src/mgr/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(linemode mgr mouse ratio); +yazi_macro::mod_flat!(mgr mouse ratio); diff --git a/yazi-config/src/opener/opener.rs b/yazi-config/src/opener/opener.rs index 01e939f1..8d4707bc 100644 --- a/yazi-config/src/opener/opener.rs +++ b/yazi-config/src/opener/opener.rs @@ -2,7 +2,7 @@ use std::{mem, ops::Deref, sync::Arc}; use arc_swap::ArcSwap; use hashbrown::HashMap; -use serde::{Deserialize, Deserializer, de::{MapAccess, Visitor}}; +use serde::{Deserialize, Deserializer}; use yazi_shim::{arc_swap::IntoPointee, toml::{DeserializeOverHook, DeserializeOverWith}}; use super::{OpenerRule, OpenerRules}; @@ -80,23 +80,6 @@ impl DeserializeOverHook for Opener { impl DeserializeOverWith for Opener { fn deserialize_over_with<'de, D: Deserializer<'de>>(self, de: D) -> Result { - struct V(HashMap>); - - impl<'de> Visitor<'de> for V { - type Value = Opener; - - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("a map of opener rules") - } - - fn visit_map>(mut self, mut map: M) -> Result { - while let Some(key) = map.next_key()? { - self.0.insert(key, map.next_value()?); - } - Ok(Opener(self.0.into_pointee())) - } - } - - de.deserialize_map(V(self.unwrap_unchecked())) + Ok(Self(self.0.deserialize_over_with(de)?)) } } diff --git a/yazi-config/src/plugin/fetcher.rs b/yazi-config/src/plugin/fetcher.rs index da5210cf..4b08bb79 100644 --- a/yazi-config/src/plugin/fetcher.rs +++ b/yazi-config/src/plugin/fetcher.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, ops::Deref, sync::Arc}; use hashbrown::HashSet; use serde::Deserialize; use yazi_fs::File; -use yazi_shared::{Id, event::Action}; +use yazi_shared::{Id, event::Cmd}; use crate::{Mixable, Pattern, Priority, Selectable, Selector, plugin::{Fetchers, fetcher_id}}; @@ -15,14 +15,14 @@ pub struct Fetcher { pub idx: u8, #[serde(flatten)] pub selector: Selector, - pub run: Action, + pub run: Cmd, #[serde(default)] pub prio: Priority, pub group: String, } impl Deref for Fetcher { - type Target = Action; + type Target = Cmd; fn deref(&self) -> &Self::Target { &self.run } } diff --git a/yazi-config/src/plugin/preloader.rs b/yazi-config/src/plugin/preloader.rs index 0e5c08ab..0e6e9c65 100644 --- a/yazi-config/src/plugin/preloader.rs +++ b/yazi-config/src/plugin/preloader.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref, sync::Arc}; use serde::Deserialize; use yazi_fs::File; -use yazi_shared::{Id, event::Action}; +use yazi_shared::{Id, event::Cmd}; use crate::{Mixable, Pattern, Priority, Selectable, Selector, plugin::{Preloaders, preloader_id}}; @@ -14,7 +14,7 @@ pub struct Preloader { pub idx: u8, #[serde(flatten)] pub selector: Selector, - pub run: Action, + pub run: Cmd, #[serde(default)] pub next: bool, #[serde(default)] @@ -22,7 +22,7 @@ pub struct Preloader { } impl Deref for Preloader { - type Target = Action; + type Target = Cmd; fn deref(&self) -> &Self::Target { &self.run } } diff --git a/yazi-config/src/plugin/previewer.rs b/yazi-config/src/plugin/previewer.rs index 5aed6685..8d570a8d 100644 --- a/yazi-config/src/plugin/previewer.rs +++ b/yazi-config/src/plugin/previewer.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref, sync::Arc}; use serde::Deserialize; use yazi_fs::File; -use yazi_shared::{Id, event::Action}; +use yazi_shared::{Id, event::Cmd}; use crate::{Mixable, Pattern, Selectable, Selector, plugin::{Previewers, previewer_id}}; @@ -12,11 +12,11 @@ pub struct Previewer { pub id: Id, #[serde(flatten)] pub selector: Selector, - pub run: Action, + pub run: Cmd, } impl Deref for Previewer { - type Target = Action; + type Target = Cmd; #[inline] fn deref(&self) -> &Self::Target { &self.run } diff --git a/yazi-config/src/plugin/spotter.rs b/yazi-config/src/plugin/spotter.rs index 73ca207a..60aafbe3 100644 --- a/yazi-config/src/plugin/spotter.rs +++ b/yazi-config/src/plugin/spotter.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref, sync::Arc}; use serde::Deserialize; use yazi_fs::File; -use yazi_shared::{Id, event::Action}; +use yazi_shared::{Id, event::Cmd}; use crate::{Mixable, Pattern, Selectable, Selector, plugin::{Spotters, spotter_id}}; @@ -12,11 +12,11 @@ pub struct Spotter { pub id: Id, #[serde(flatten)] pub selector: Selector, - pub run: Action, + pub run: Cmd, } impl Deref for Spotter { - type Target = Action; + type Target = Cmd; #[inline] fn deref(&self) -> &Self::Target { &self.run } diff --git a/yazi-config/src/theme/filetype.rs b/yazi-config/src/theme/filetype.rs index 2d19faf1..96515b64 100644 --- a/yazi-config/src/theme/filetype.rs +++ b/yazi-config/src/theme/filetype.rs @@ -1,43 +1,17 @@ -use std::ops::Deref; - use serde::Deserialize; -use yazi_codegen::{DeserializeOver, DeserializeOver2}; +use yazi_codegen::{DeserializeOver, DeserializeOver2, Overlay}; use yazi_fs::File; -use super::Is; -use crate::{Pattern, Selectable, Selector, Style}; +use super::FiletypeRules; +use crate::{Selectable, Style}; -#[derive(Deserialize, DeserializeOver, DeserializeOver2)] +#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)] pub struct Filetype { - rules: Vec, + rules: FiletypeRules, } -impl Deref for Filetype { - type Target = Vec; - - fn deref(&self) -> &Self::Target { &self.rules } -} - -#[derive(Deserialize)] -pub struct FiletypeRule { - #[serde(default)] - is: Is, - #[serde(flatten)] - selector: Selector, - #[serde(flatten)] - pub style: Style, -} - -impl Selectable for FiletypeRule { - fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() } - - fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() } - - fn match_with(&self, file: Option<&File>, mime: Option<&str>) -> bool { - match (self.is.enabled(), file) { - (Some(is), Some(f)) if !is.check(&f.cha) => false, - (Some(_), None) => false, - _ => self.selector.match_with(file, mime), - } +impl Filetype { + pub fn match_style(&self, file: &File, mime: &str) -> Option