mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new app:theme action that hot-reload user themes/flavors (#3906)
This commit is contained in:
parent
369c47539c
commit
4a2e5addcd
107 changed files with 799 additions and 405 deletions
|
|
@ -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
|
||||
|
|
|
|||
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ license = "MIT"
|
|||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
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"
|
||||
|
|
|
|||
12
flake.lock
generated
12
flake.lock
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ yazi_macro::mod_flat!(
|
|||
resize
|
||||
resume
|
||||
stop
|
||||
theme
|
||||
title
|
||||
update_progress
|
||||
);
|
||||
|
|
|
|||
25
yazi-actor/src/app/theme.rs
Normal file
25
yazi-actor/src/app/theme.rs
Normal file
|
|
@ -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<Data> {
|
||||
THEME.overlay(build_flavor(EMULATOR.light, true)?);
|
||||
yazi_plugin::theme::reset()?;
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -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::<AnyUserData>("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, ()| {
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl UserData for Preference {
|
|||
fn add_fields<F: UserDataFields<Self>>(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
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ impl Actor for Linemode {
|
|||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
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!();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Adapter> = SyncCell::new(Adapter::Chafa);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Value>,
|
||||
v_style: Option<Value>,
|
||||
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<yazi_config::Icon> for Icon {
|
||||
fn from(inner: yazi_config::Icon) -> Self { Self { inner } }
|
||||
}
|
||||
|
||||
impl UserData for Icon {
|
||||
fn add_fields<F: UserDataFields<Self>>(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<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("text", self.inner.text.into_lua(lua)?),
|
||||
("style", Style::from(self.inner.style).into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -18,9 +18,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
.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(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Args> = RoCell::new();
|
||||
pub static BOOT: RoCell<Boot> = RoCell::new();
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
fn generate() -> Result<(), Box<dyn Error>> {
|
||||
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(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -82,9 +82,6 @@ where
|
|||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<yazi::Yazi> = 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<theme::Theme> {
|
||||
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<()> {
|
||||
|
|
|
|||
|
|
@ -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<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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<SortFallback>,
|
||||
|
||||
// Display
|
||||
pub linemode: MgrLinemode,
|
||||
#[serde(deserialize_with = "deserialize_linemode")]
|
||||
pub linemode: ArcSwap<String>,
|
||||
pub show_hidden: SyncCell<bool>,
|
||||
pub show_symlink: SyncCell<bool>,
|
||||
pub scrolloff: SyncCell<u8>,
|
||||
pub mouse_events: SyncCell<MouseEvents>,
|
||||
}
|
||||
|
||||
fn deserialize_linemode<'de, D>(deserializer: D) -> Result<ArcSwap<String>, 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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(linemode mgr mouse ratio);
|
||||
yazi_macro::mod_flat!(mgr mouse ratio);
|
||||
|
|
|
|||
|
|
@ -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<Self, D::Error> {
|
||||
struct V(HashMap<String, Arc<OpenerRules>>);
|
||||
|
||||
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<M: MapAccess<'de>>(mut self, mut map: M) -> Result<Self::Value, M::Error> {
|
||||
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)?))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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<FiletypeRule>,
|
||||
rules: FiletypeRules,
|
||||
}
|
||||
|
||||
impl Deref for Filetype {
|
||||
type Target = Vec<FiletypeRule>;
|
||||
|
||||
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<Style> {
|
||||
self.rules.load().iter().find(|rule| rule.matches(file, mime)).map(|rule| rule.style)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
yazi-config/src/theme/filetype_rule.rs
Normal file
27
yazi-config/src/theme/filetype_rule.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
use crate::{Pattern, Selectable};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FiletypeRule {
|
||||
#[serde(default)]
|
||||
is: super::Is,
|
||||
#[serde(flatten)]
|
||||
selector: crate::Selector,
|
||||
#[serde(flatten)]
|
||||
pub style: crate::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<&yazi_fs::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),
|
||||
}
|
||||
}
|
||||
}
|
||||
21
yazi-config/src/theme/filetype_rules.rs
Normal file
21
yazi-config/src/theme/filetype_rules.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::Overlay;
|
||||
use yazi_shim::arc_swap::IntoPointee;
|
||||
|
||||
use crate::theme::FiletypeRule;
|
||||
|
||||
#[derive(Default, Deserialize, Overlay)]
|
||||
pub struct FiletypeRules(ArcSwap<Vec<FiletypeRule>>);
|
||||
|
||||
impl Deref for FiletypeRules {
|
||||
type Target = ArcSwap<Vec<FiletypeRule>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl From<Vec<FiletypeRule>> for FiletypeRules {
|
||||
fn from(inner: Vec<FiletypeRule>) -> Self { Self(inner.into_pointee()) }
|
||||
}
|
||||
|
|
@ -4,13 +4,13 @@ use anyhow::{Context, Result};
|
|||
use arc_swap::ArcSwap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use toml::{Spanned, de::DeTable};
|
||||
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||
use yazi_codegen::{DeserializeOver, DeserializeOver2, Overlay};
|
||||
use yazi_fs::Xdg;
|
||||
use yazi_shim::toml::deserialize_spanned;
|
||||
|
||||
use crate::error_with_input;
|
||||
|
||||
#[derive(Default, Deserialize, DeserializeOver, DeserializeOver2, Serialize)]
|
||||
#[derive(Default, Deserialize, DeserializeOver, DeserializeOver2, Overlay, Serialize)]
|
||||
pub struct Flavor {
|
||||
#[serde(default)]
|
||||
pub dark: ArcSwap<String>,
|
||||
|
|
|
|||
|
|
@ -1,43 +1,41 @@
|
|||
use anyhow::Result;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::{DeserializeOver2, Overlay};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::{Condition, url::UrlLike};
|
||||
use yazi_shared::{NonEmptyString, url::UrlLike};
|
||||
use yazi_shim::toml::DeserializeOverHook;
|
||||
|
||||
use crate::{Icon as I, Mixable, Pattern, mix};
|
||||
use super::{IconCond, IconConds, IconGlob, IconGlobs, IconNames, deserialize_icon_names};
|
||||
use crate::{Icon as I, mix};
|
||||
|
||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||
#[derive(Default, Deserialize, DeserializeOver2, Overlay)]
|
||||
pub struct Icon {
|
||||
globs: Vec<IconPat>,
|
||||
globs: IconGlobs,
|
||||
#[serde(default)]
|
||||
prepend_globs: Vec<IconPat>,
|
||||
prepend_globs: Vec<IconGlob>,
|
||||
#[serde(default)]
|
||||
append_globs: Vec<IconPat>,
|
||||
append_globs: Vec<IconGlob>,
|
||||
|
||||
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||
dirs: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
prepend_dirs: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
append_dirs: HashMap<String, I>,
|
||||
dirs: IconNames,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
prepend_dirs: HashMap<NonEmptyString, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
append_dirs: HashMap<NonEmptyString, I>,
|
||||
|
||||
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||
files: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
prepend_files: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
append_files: HashMap<String, I>,
|
||||
files: IconNames,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
prepend_files: HashMap<NonEmptyString, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
append_files: HashMap<NonEmptyString, I>,
|
||||
|
||||
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||
exts: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
prepend_exts: HashMap<String, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||
append_exts: HashMap<String, I>,
|
||||
exts: IconNames,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
prepend_exts: HashMap<NonEmptyString, I>,
|
||||
#[serde(default, deserialize_with = "deserialize_icon_names")]
|
||||
append_exts: HashMap<NonEmptyString, I>,
|
||||
|
||||
conds: Vec<IconCond>,
|
||||
conds: IconConds,
|
||||
#[serde(default)]
|
||||
prepend_conds: Vec<IconCond>,
|
||||
#[serde(default)]
|
||||
|
|
@ -45,109 +43,49 @@ pub struct Icon {
|
|||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn matches(&self, file: &File, hovered: bool) -> Option<&I> {
|
||||
if let Some(i) = self.match_by_glob(file) {
|
||||
pub fn matches(&self, file: &File, hovered: bool) -> Option<I> {
|
||||
if let Some(i) = self.globs.matches(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
if let Some(i) = self.match_by_name(file) {
|
||||
return Some(i);
|
||||
let name = file.name().unwrap_or_default();
|
||||
match file.is_dir() {
|
||||
true if let Some(i) = self.dirs.matches(name) => Some(i),
|
||||
false if let Some(i) = self.files.matches(name) => Some(i),
|
||||
false if let Some(i) = self.exts.matches(file.url.ext().unwrap_or_default()) => Some(i),
|
||||
_ => self.conds.matches(file, hovered),
|
||||
}
|
||||
|
||||
let f = |s: &str| match s {
|
||||
"dir" => file.is_dir(),
|
||||
"hidden" => file.is_hidden(),
|
||||
"link" => file.is_link(),
|
||||
"orphan" => file.is_orphan(),
|
||||
"dummy" => file.is_dummy(),
|
||||
"block" => file.is_block(),
|
||||
"char" => file.is_char(),
|
||||
"fifo" => file.is_fifo(),
|
||||
"sock" => file.is_sock(),
|
||||
"exec" => file.is_exec(),
|
||||
"sticky" => file.is_sticky(),
|
||||
"hovered" => hovered,
|
||||
_ => false,
|
||||
};
|
||||
self.conds.iter().find(|&c| c.r#if.eval(f) == Some(true)).map(|c| &c.icon)
|
||||
}
|
||||
|
||||
fn match_by_glob(&self, file: &File) -> Option<&I> {
|
||||
self.globs.iter().find(|&g| g.url.match_url(&file.url, file.is_dir())).map(|g| &g.icon)
|
||||
}
|
||||
|
||||
fn match_by_name(&self, file: &File) -> Option<&I> {
|
||||
let name = file.name()?.to_str().ok()?;
|
||||
if file.is_dir() {
|
||||
self.dirs.get(name).or_else(|| self.dirs.get(&name.to_ascii_lowercase()))
|
||||
} else {
|
||||
self
|
||||
.files
|
||||
.get(name)
|
||||
.or_else(|| self.files.get(&name.to_ascii_lowercase()))
|
||||
.or_else(|| self.match_by_ext(file))
|
||||
}
|
||||
}
|
||||
|
||||
fn match_by_ext(&self, file: &File) -> Option<&I> {
|
||||
let ext = file.url.ext()?.to_str().ok()?;
|
||||
self.exts.get(ext).or_else(|| self.exts.get(&ext.to_ascii_lowercase()))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeOverHook for Icon {
|
||||
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||
let dirs: HashMap<NonEmptyString, I> = self
|
||||
.append_dirs
|
||||
.into_iter()
|
||||
.chain(self.dirs.unwrap_unchecked())
|
||||
.chain(self.prepend_dirs)
|
||||
.collect();
|
||||
let files: HashMap<NonEmptyString, I> = self
|
||||
.append_files
|
||||
.into_iter()
|
||||
.chain(self.files.unwrap_unchecked())
|
||||
.chain(self.prepend_files)
|
||||
.collect();
|
||||
let exts: HashMap<NonEmptyString, I> = self
|
||||
.append_exts
|
||||
.into_iter()
|
||||
.chain(self.exts.unwrap_unchecked())
|
||||
.chain(self.prepend_exts)
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
globs: mix(self.prepend_globs, self.globs, self.append_globs),
|
||||
dirs: self.append_dirs.into_iter().chain(self.dirs).chain(self.prepend_dirs).collect(),
|
||||
files: self.append_files.into_iter().chain(self.files).chain(self.prepend_files).collect(),
|
||||
exts: self.append_exts.into_iter().chain(self.exts).chain(self.prepend_exts).collect(),
|
||||
conds: mix(self.prepend_conds, self.conds, self.append_conds),
|
||||
globs: mix(self.prepend_globs, self.globs.unwrap_unchecked(), self.append_globs).into(),
|
||||
dirs: dirs.into(),
|
||||
files: files.into(),
|
||||
exts: exts.into(),
|
||||
conds: mix(self.prepend_conds, self.conds.unwrap_unchecked(), self.append_conds).into(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// --- IconPat
|
||||
#[derive(Deserialize)]
|
||||
pub struct IconPat {
|
||||
pub url: Pattern,
|
||||
#[serde(flatten)]
|
||||
pub icon: I,
|
||||
}
|
||||
|
||||
impl Mixable for IconPat {
|
||||
fn any_file(&self) -> bool { self.url.any_file() }
|
||||
|
||||
fn any_dir(&self) -> bool { self.url.any_dir() }
|
||||
}
|
||||
|
||||
// --- IconNamed
|
||||
#[derive(Deserialize)]
|
||||
struct IconNamed {
|
||||
name: String,
|
||||
#[serde(flatten)]
|
||||
icon: I,
|
||||
}
|
||||
|
||||
fn deserialize_named_icons<'de, D>(deserializer: D) -> Result<HashMap<String, I>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(
|
||||
Vec::<IconNamed>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|entry| (entry.name, entry.icon))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
// --- IconCond
|
||||
#[derive(Deserialize)]
|
||||
pub struct IconCond {
|
||||
pub r#if: Condition,
|
||||
#[serde(flatten)]
|
||||
pub icon: I,
|
||||
}
|
||||
|
||||
impl Mixable for IconCond {}
|
||||
|
|
|
|||
36
yazi-config/src/theme/icon_cond.rs
Normal file
36
yazi-config/src/theme/icon_cond.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use serde::Deserialize;
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::Condition;
|
||||
|
||||
use crate::{Icon, Mixable};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct IconCond {
|
||||
pub r#if: Condition,
|
||||
#[serde(flatten)]
|
||||
pub icon: Icon,
|
||||
}
|
||||
|
||||
impl IconCond {
|
||||
pub fn matches(&self, file: &File, hovered: bool) -> bool {
|
||||
let f = |s: &str| match s {
|
||||
"dir" => file.is_dir(),
|
||||
"hidden" => file.is_hidden(),
|
||||
"link" => file.is_link(),
|
||||
"orphan" => file.is_orphan(),
|
||||
"dummy" => file.is_dummy(),
|
||||
"block" => file.is_block(),
|
||||
"char" => file.is_char(),
|
||||
"fifo" => file.is_fifo(),
|
||||
"sock" => file.is_sock(),
|
||||
"exec" => file.is_exec(),
|
||||
"sticky" => file.is_sticky(),
|
||||
"hovered" => hovered,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
self.r#if.eval(f) == Some(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mixable for IconCond {}
|
||||
32
yazi-config/src/theme/icon_conds.rs
Normal file
32
yazi-config/src/theme/icon_conds.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::Overlay;
|
||||
use yazi_fs::File;
|
||||
use yazi_shim::arc_swap::IntoPointee;
|
||||
|
||||
use crate::{Icon, theme::IconCond};
|
||||
|
||||
#[derive(Default, Deserialize, Overlay)]
|
||||
pub struct IconConds(ArcSwap<Vec<IconCond>>);
|
||||
|
||||
impl Deref for IconConds {
|
||||
type Target = ArcSwap<Vec<IconCond>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl From<Vec<IconCond>> for IconConds {
|
||||
fn from(inner: Vec<IconCond>) -> Self { Self(inner.into_pointee()) }
|
||||
}
|
||||
|
||||
impl IconConds {
|
||||
pub fn matches(&self, file: &File, hovered: bool) -> Option<Icon> {
|
||||
self.0.load().iter().find(|&c| c.matches(file, hovered)).map(|c| c.icon.clone())
|
||||
}
|
||||
|
||||
pub(super) fn unwrap_unchecked(self) -> Vec<IconCond> {
|
||||
Arc::try_unwrap(self.0.into_inner()).expect("unique icon conds arc")
|
||||
}
|
||||
}
|
||||
22
yazi-config/src/theme/icon_glob.rs
Normal file
22
yazi-config/src/theme/icon_glob.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
use crate::{Icon, Mixable, Pattern, Selectable};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct IconGlob {
|
||||
pub url: Pattern,
|
||||
#[serde(flatten)]
|
||||
pub icon: Icon,
|
||||
}
|
||||
|
||||
impl Selectable for IconGlob {
|
||||
fn url_pat(&self) -> Option<&Pattern> { Some(&self.url) }
|
||||
|
||||
fn mime_pat(&self) -> Option<&Pattern> { None }
|
||||
}
|
||||
|
||||
impl Mixable for IconGlob {
|
||||
fn any_file(&self) -> bool { self.url.any_file() }
|
||||
|
||||
fn any_dir(&self) -> bool { self.url.any_dir() }
|
||||
}
|
||||
32
yazi-config/src/theme/icon_globs.rs
Normal file
32
yazi-config/src/theme/icon_globs.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::Overlay;
|
||||
use yazi_fs::File;
|
||||
use yazi_shim::arc_swap::IntoPointee;
|
||||
|
||||
use crate::{Icon, Selectable, theme::IconGlob};
|
||||
|
||||
#[derive(Default, Deserialize, Overlay)]
|
||||
pub struct IconGlobs(ArcSwap<Vec<IconGlob>>);
|
||||
|
||||
impl Deref for IconGlobs {
|
||||
type Target = ArcSwap<Vec<IconGlob>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl From<Vec<IconGlob>> for IconGlobs {
|
||||
fn from(inner: Vec<IconGlob>) -> Self { Self(inner.into_pointee()) }
|
||||
}
|
||||
|
||||
impl IconGlobs {
|
||||
pub fn matches(&self, file: &File) -> Option<Icon> {
|
||||
self.0.load().iter().find(|&g| g.match_with(Some(file), None)).map(|g| g.icon.clone())
|
||||
}
|
||||
|
||||
pub(super) fn unwrap_unchecked(self) -> Vec<IconGlob> {
|
||||
Arc::try_unwrap(self.0.into_inner()).expect("unique icon globs arc")
|
||||
}
|
||||
}
|
||||
69
yazi-config/src/theme/icon_names.rs
Normal file
69
yazi-config/src/theme/icon_names.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_codegen::Overlay;
|
||||
use yazi_shared::{NonEmptyString, strand::AsStrand};
|
||||
use yazi_shim::arc_swap::IntoPointee;
|
||||
|
||||
use crate::Icon;
|
||||
|
||||
#[derive(Default, Overlay)]
|
||||
pub struct IconNames(ArcSwap<HashMap<NonEmptyString, Icon>>);
|
||||
|
||||
impl Deref for IconNames {
|
||||
type Target = ArcSwap<HashMap<NonEmptyString, Icon>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl From<HashMap<NonEmptyString, Icon>> for IconNames {
|
||||
fn from(inner: HashMap<NonEmptyString, Icon>) -> Self { Self(inner.into_pointee()) }
|
||||
}
|
||||
|
||||
impl IconNames {
|
||||
pub fn matches<S>(&self, name: S) -> Option<Icon>
|
||||
where
|
||||
S: AsStrand,
|
||||
{
|
||||
let name = name.as_strand().to_str().ok()?;
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inner = self.0.load();
|
||||
inner.get(name).or_else(|| inner.get(&name.to_ascii_lowercase())).cloned()
|
||||
}
|
||||
|
||||
pub(super) fn unwrap_unchecked(self) -> HashMap<NonEmptyString, Icon> {
|
||||
Arc::try_unwrap(self.0.into_inner()).expect("unique icon names arc")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for IconNames {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
deserialize_icon_names(deserializer).map(Self::from)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn deserialize_icon_names<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashMap<NonEmptyString, Icon>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Helper {
|
||||
name: NonEmptyString,
|
||||
#[serde(flatten)]
|
||||
icon: Icon,
|
||||
}
|
||||
|
||||
Ok(
|
||||
Vec::<Helper>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|entry| (entry.name, entry.icon))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(filetype flavor icon is theme);
|
||||
yazi_macro::mod_flat!(filetype filetype_rule filetype_rules flavor icon icon_cond icon_conds icon_glob icon_globs icon_names is theme);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,14 @@ use std::path::PathBuf;
|
|||
use anyhow::{Context, Result};
|
||||
use arc_swap::ArcSwap;
|
||||
use serde::{Deserialize, Deserializer, de};
|
||||
use yazi_codegen::{DeserializeOver, DeserializeOver1, DeserializeOver2};
|
||||
use yazi_codegen::{DeserializeOver, DeserializeOver1, DeserializeOver2, Overlay};
|
||||
use yazi_fs::{Xdg, ok_or_not_found};
|
||||
use yazi_shared::SyncCell;
|
||||
use yazi_shim::arc_swap::IntoPointee;
|
||||
use yazi_shim::{arc_swap::IntoPointee, cell::SyncCell};
|
||||
|
||||
use super::{Filetype, Flavor, Icon};
|
||||
use crate::{Style, normalize_path};
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver1)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver1, Overlay)]
|
||||
pub struct Theme {
|
||||
pub flavor: Flavor,
|
||||
pub app: App,
|
||||
|
|
@ -35,12 +34,12 @@ pub struct Theme {
|
|||
pub icon: Icon,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct App {
|
||||
pub overall: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Mgr {
|
||||
pub cwd: SyncCell<Style>,
|
||||
|
||||
|
|
@ -72,7 +71,7 @@ pub struct Mgr {
|
|||
pub syntect_theme: ArcSwap<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Tabs {
|
||||
pub active: SyncCell<Style>,
|
||||
pub inactive: SyncCell<Style>,
|
||||
|
|
@ -87,7 +86,7 @@ pub struct TabsSep {
|
|||
pub close: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Mode {
|
||||
pub normal_main: SyncCell<Style>,
|
||||
pub normal_alt: SyncCell<Style>,
|
||||
|
|
@ -99,7 +98,7 @@ pub struct Mode {
|
|||
pub unset_alt: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Indicator {
|
||||
pub parent: SyncCell<Style>,
|
||||
pub current: SyncCell<Style>,
|
||||
|
|
@ -113,7 +112,7 @@ pub struct IndicatorPadding {
|
|||
pub close: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Status {
|
||||
pub overall: SyncCell<Style>,
|
||||
pub sep_left: ArcSwap<StatusSep>,
|
||||
|
|
@ -138,7 +137,7 @@ pub struct StatusSep {
|
|||
pub close: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Which {
|
||||
#[serde(deserialize_with = "deserialize_which_cols")]
|
||||
pub cols: SyncCell<u8>,
|
||||
|
|
@ -151,7 +150,7 @@ pub struct Which {
|
|||
pub separator_style: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Confirm {
|
||||
pub border: SyncCell<Style>,
|
||||
pub title: SyncCell<Style>,
|
||||
|
|
@ -160,10 +159,10 @@ pub struct Confirm {
|
|||
|
||||
pub btn_yes: SyncCell<Style>,
|
||||
pub btn_no: SyncCell<Style>,
|
||||
pub btn_labels: [String; 2],
|
||||
pub btn_labels: ArcSwap<[String; 2]>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Spot {
|
||||
pub border: SyncCell<Style>,
|
||||
pub title: SyncCell<Style>,
|
||||
|
|
@ -172,7 +171,7 @@ pub struct Spot {
|
|||
pub tbl_cell: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Notify {
|
||||
pub title_info: SyncCell<Style>,
|
||||
pub title_warn: SyncCell<Style>,
|
||||
|
|
@ -183,14 +182,14 @@ pub struct Notify {
|
|||
pub icon_error: ArcSwap<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Pick {
|
||||
pub border: SyncCell<Style>,
|
||||
pub active: SyncCell<Style>,
|
||||
pub inactive: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Input {
|
||||
pub border: SyncCell<Style>,
|
||||
pub title: SyncCell<Style>,
|
||||
|
|
@ -198,7 +197,7 @@ pub struct Input {
|
|||
pub selected: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Cmp {
|
||||
pub border: SyncCell<Style>,
|
||||
pub active: SyncCell<Style>,
|
||||
|
|
@ -209,14 +208,14 @@ pub struct Cmp {
|
|||
pub icon_command: ArcSwap<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Tasks {
|
||||
pub border: SyncCell<Style>,
|
||||
pub title: SyncCell<Style>,
|
||||
pub hovered: SyncCell<Style>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2, Overlay)]
|
||||
pub struct Help {
|
||||
pub on: SyncCell<Style>,
|
||||
pub run: SyncCell<Style>,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ rust-version.workspace = true
|
|||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [ "vendored-lua" ]
|
||||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" }
|
||||
yazi-binding = { path = "../yazi-binding", version = "26.2.2" }
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use mlua::{Lua, Table};
|
|||
use serde::Deserialize;
|
||||
use strum::EnumString;
|
||||
use yazi_scheduler::plugin::PluginInEntry;
|
||||
use yazi_shared::{SStr, data::{Data, DataKey}, event::{Action, ActionCow}};
|
||||
use yazi_shared::{SStr, data::{Data, DataKey}, event::{ActionCow, Cmd}};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PluginOpt {
|
||||
|
|
@ -27,7 +27,7 @@ impl TryFrom<ActionCow> for PluginOpt {
|
|||
|
||||
let args = if let Ok(s) = a.second() {
|
||||
let (words, last) = yazi_shared::shell::unix::split(s, true)?;
|
||||
Action::parse_args(words, last)?
|
||||
Cmd::parse_args(words, last)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::{FilesSorter, SortBy, SortFallback};
|
||||
|
||||
|
|
@ -5,7 +7,7 @@ use yazi_fs::{FilesSorter, SortBy, SortFallback};
|
|||
pub struct Preference {
|
||||
// Display
|
||||
pub name: String,
|
||||
pub linemode: String,
|
||||
pub linemode: Arc<String>,
|
||||
pub show_hidden: bool,
|
||||
|
||||
// Sorting
|
||||
|
|
@ -22,7 +24,7 @@ impl Default for Preference {
|
|||
Self {
|
||||
// Display
|
||||
name: String::new(),
|
||||
linemode: YAZI.mgr.linemode.clone(),
|
||||
linemode: YAZI.mgr.linemode.load_full(),
|
||||
show_hidden: YAZI.mgr.show_hidden.get(),
|
||||
|
||||
// Sorting
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ yazi-boot = { path = "../yazi-boot", 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" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::error::Error;
|
||||
use std::{env, error::Error};
|
||||
|
||||
use vergen_gitcl::{Emitter, GitclBuilder};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
Emitter::default()
|
||||
.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");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use serde::{Deserialize, Serialize};
|
|||
use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time};
|
||||
use tracing::error;
|
||||
use yazi_macro::try_format;
|
||||
use yazi_shared::{Id, RoCell};
|
||||
use yazi_shared::Id;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::{ClientReader, ClientWriter, Payload, Pubsub, Server, Stream, ember::{Ember, EmberHey}};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use mlua::Function;
|
|||
use parking_lot::RwLock;
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::FolderStage;
|
||||
use yazi_shared::{Id, RoCell, url::{Url, UrlBuf, UrlBufCov}};
|
||||
use yazi_shared::{Id, url::{Url, UrlBuf, UrlBufCov}};
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::{Client, ID, PEERS, ember::{Ember, EmberBulk, EmberDuplicateItem, EmberHi, EmberMoveItem}};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use std::time::Duration;
|
|||
use tokio::{pin, select, sync::mpsc};
|
||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use yazi_macro::err;
|
||||
use yazi_shared::{RoCell, url::UrlBuf};
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::{Pubsub, ember::{EmberDuplicateItem, EmberMoveItem}};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use hashbrown::HashMap;
|
|||
use parking_lot::RwLock;
|
||||
use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time};
|
||||
use yazi_macro::try_format;
|
||||
use yazi_shared::{Id, RoCell};
|
||||
use yazi_shared::Id;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::{Client, ClientWriter, Payload, Peer, STATE, Stream, ember::{Ember, EmberBye, EmberHey}};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use parking_lot::RwLock;
|
|||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::provider::{FileBuilder, Provider, local::{Gate, Local}};
|
||||
use yazi_shared::{RoCell, timestamp_us};
|
||||
use yazi_shared::timestamp_us;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::CLIENTS;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ workspace = true
|
|||
[dependencies]
|
||||
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-tty = { path = "../yazi-tty", version = "26.2.2" }
|
||||
|
||||
# External dependencies
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use either::Either;
|
|||
use scopeguard::defer;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, warn};
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shim::cell::RoCell;
|
||||
use yazi_tty::{Handle, TTY};
|
||||
|
||||
use crate::{Brand, Dimension, Mux, TMUX, Unknown};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
|||
use anyhow::Result;
|
||||
use tracing::error;
|
||||
use yazi_macro::time;
|
||||
use yazi_shared::SyncCell;
|
||||
use yazi_shim::cell::SyncCell;
|
||||
use yazi_tty::TTY;
|
||||
|
||||
use crate::Emulator;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" }
|
|||
yazi-runner = { path = "../yazi-runner", version = "26.2.2" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||
yazi-shim = { path = "../yazi-shim", version = "26.2.2" }
|
||||
yazi-term = { path = "../yazi-term", version = "26.2.2" }
|
||||
yazi-tty = { path = "../yazi-tty", version = "26.2.2" }
|
||||
yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" }
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ impl Widget for Buttons {
|
|||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let chunks =
|
||||
ratatui::layout::Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).split(area);
|
||||
let labels = THEME.confirm.btn_labels.load();
|
||||
|
||||
Paragraph::new(Span::raw(&THEME.confirm.btn_labels[0]).style(THEME.confirm.btn_yes.get()))
|
||||
Paragraph::new(Span::raw(&labels[0]).style(THEME.confirm.btn_yes.get()))
|
||||
.centered()
|
||||
.render(chunks[0], buf);
|
||||
Paragraph::new(Span::raw(&THEME.confirm.btn_labels[1]).style(THEME.confirm.btn_no.get()))
|
||||
Paragraph::new(Span::raw(&labels[1]).style(THEME.confirm.btn_no.get()))
|
||||
.centered()
|
||||
.render(chunks[1], buf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ impl<'a> Executor<'a> {
|
|||
|
||||
pub(super) fn execute(&mut self, action: ActionCow) -> Result<Data> {
|
||||
match action.layer {
|
||||
Layer::Null => self.null(action),
|
||||
Layer::App => self.app(action),
|
||||
Layer::Mgr => self.mgr(action),
|
||||
Layer::Tasks => self.tasks(action),
|
||||
|
|
@ -30,6 +31,8 @@ impl<'a> Executor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn null(&mut self, _action: ActionCow) -> Result<Data> { succ!() }
|
||||
|
||||
fn app(&mut self, mut action: ActionCow) -> Result<Data> {
|
||||
let cx = &mut Ctx::new(&action, &mut self.app.core, &mut self.app.term)?;
|
||||
|
||||
|
|
@ -47,6 +50,7 @@ impl<'a> Executor<'a> {
|
|||
on!(update_progress);
|
||||
on!(lua);
|
||||
on!(deprecate);
|
||||
on!(theme);
|
||||
on!(quit);
|
||||
|
||||
match &*action.name {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use crossterm::style::{Color, Print, ResetColor, SetForegroundColor};
|
|||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use yazi_fs::Xdg;
|
||||
use yazi_shared::{LOG_LEVEL, RoCell};
|
||||
use yazi_shared::LOG_LEVEL;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
static _GUARD: RoCell<WorkerGuard> = RoCell::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl<'a> Router<'a> {
|
|||
|
||||
use Layer as L;
|
||||
Ok(match layer {
|
||||
L::App | L::Notify => unreachable!(),
|
||||
L::Null | L::App | L::Notify => unreachable!(),
|
||||
L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => {
|
||||
self.matches(layer, key)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::{borrow::Cow, env::{current_dir, set_current_dir}, ops::Deref, path::{Path, PathBuf}, sync::{Arc, atomic::{AtomicBool, Ordering}}};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use yazi_shared::{RoCell, url::{AsUrl, Url, UrlBuf, UrlLike}};
|
||||
use yazi_shared::url::{AsUrl, Url, UrlBuf, UrlLike};
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use crate::{FsUrl, Xdg};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use super::Partition;
|
||||
use crate::cha::Cha;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::{SStr, event::ActionCow};
|
||||
use yazi_shared::event::ActionCow;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LinemodeForm {
|
||||
#[serde(alias = "0")]
|
||||
pub new: SStr,
|
||||
pub new: String,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for LinemodeForm {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub enum Spark<'a> {
|
|||
AppResize(crate::app::ReflowForm),
|
||||
AppResume(crate::app::ResumeForm),
|
||||
AppStop(crate::app::StopForm),
|
||||
AppTheme(crate::VoidForm),
|
||||
AppTitle(crate::app::TitleForm),
|
||||
AppUpdateProgress(crate::app::UpdateProgressForm),
|
||||
|
||||
|
|
@ -205,6 +206,7 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
Self::AppResize(b) => b.into_lua(lua),
|
||||
Self::AppResume(b) => b.into_lua(lua),
|
||||
Self::AppStop(b) => b.into_lua(lua),
|
||||
Self::AppTheme(b) => b.into_lua(lua),
|
||||
Self::AppTitle(b) => b.into_lua(lua),
|
||||
Self::AppUpdateProgress(b) => b.into_lua(lua),
|
||||
|
||||
|
|
@ -346,6 +348,7 @@ try_from_spark!(
|
|||
crate::VoidForm,
|
||||
app:bootstrap,
|
||||
app:focus,
|
||||
app:theme,
|
||||
mgr:back,
|
||||
mgr:bulk_rename,
|
||||
mgr:enter,
|
||||
|
|
|
|||
|
|
@ -31,26 +31,25 @@ function M:spot(job)
|
|||
end
|
||||
|
||||
function M:spot_base(job)
|
||||
local cha = job.file.cha
|
||||
local spotter, previewer = nil, nil
|
||||
local fetchers, preloaders = {}, {}
|
||||
local cha, pair = job.file.cha, { file = job.file, mime = job.mime }
|
||||
local spotter, previewer, fetchers, preloaders = nil, nil, {}, {}
|
||||
|
||||
for _, v in pairs(rt.plugin.spotters:match(job)) do
|
||||
for _, v in pairs(rt.plugin.spotters:match(pair)) do
|
||||
spotter = v
|
||||
break
|
||||
end
|
||||
|
||||
for _, v in pairs(rt.plugin.previewers:match(job)) do
|
||||
for _, v in pairs(rt.plugin.previewers:match(pair)) do
|
||||
previewer = v
|
||||
break
|
||||
end
|
||||
|
||||
for _, v in pairs(rt.plugin.fetchers:match(job)) do
|
||||
for _, v in pairs(rt.plugin.fetchers:match(pair)) do
|
||||
fetchers[#fetchers + 1] = v.name
|
||||
end
|
||||
fetchers = #fetchers ~= 0 and fetchers or { "-" }
|
||||
|
||||
for _, v in pairs(rt.plugin.preloaders:match(job)) do
|
||||
for _, v in pairs(rt.plugin.preloaders:match(pair)) do
|
||||
preloaders[#preloaders + 1] = v.name
|
||||
end
|
||||
preloaders = #preloaders ~= 0 and preloaders or { "-" }
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ fn mgr() -> Composer<ComposerGet, ComposerSet> {
|
|||
b"sort_translit" => m.sort_translit.get().into_lua(lua)?,
|
||||
b"sort_fallback" => lua.to_value_with(&m.sort_fallback, SER_OPT)?,
|
||||
|
||||
b"linemode" => lua.create_string(&*m.linemode)?.into_lua(lua)?,
|
||||
b"linemode" => lua.create_string(&**m.linemode.load())?.into_lua(lua)?,
|
||||
b"show_hidden" => m.show_hidden.get().into_lua(lua)?,
|
||||
b"show_symlink" => m.show_symlink.get().into_lua(lua)?,
|
||||
b"scrolloff" => m.scrolloff.get().into_lua(lua)?,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use mlua::Lua;
|
|||
use yazi_binding::{Runtime, runtime_scope};
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_macro::plugin_preset as preset;
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
pub static LUA: RoCell<Lua> = RoCell::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use mlua::{IntoLua, Lua, Value};
|
|||
use yazi_binding::{Composer, ComposerGet, ComposerSet, Style, Url};
|
||||
use yazi_config::THEME;
|
||||
|
||||
use crate::LUA;
|
||||
|
||||
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
|
||||
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
|
||||
match key {
|
||||
|
|
@ -30,6 +32,8 @@ pub fn compose() -> Composer<ComposerGet, ComposerSet> {
|
|||
Composer::new(get, set)
|
||||
}
|
||||
|
||||
pub fn reset() -> mlua::Result<()> { LUA.globals().raw_set("th", compose()) }
|
||||
|
||||
fn app() -> Composer<ComposerGet, ComposerSet> {
|
||||
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
|
||||
let a = &THEME.app;
|
||||
|
|
@ -223,12 +227,12 @@ fn confirm() -> Composer<ComposerGet, ComposerSet> {
|
|||
|
||||
b"btn_yes" => Style::from(&t.btn_yes).into_lua(lua),
|
||||
b"btn_no" => Style::from(&t.btn_no).into_lua(lua),
|
||||
b"btn_labels" => lua
|
||||
.create_sequence_from([
|
||||
lua.create_string(&t.btn_labels[0])?,
|
||||
lua.create_string(&t.btn_labels[1])?,
|
||||
])?
|
||||
.into_lua(lua),
|
||||
b"btn_labels" => {
|
||||
let labels = t.btn_labels.load();
|
||||
lua
|
||||
.create_sequence_from([lua.create_string(&labels[0])?, lua.create_string(&labels[1])?])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
||||
_ => Ok(Value::Nil),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ use tokio::sync::mpsc;
|
|||
use yazi_binding::deprecate;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_macro::emit;
|
||||
use yazi_shared::{Layer, Source, event::Action};
|
||||
use yazi_shared::{Layer, Source, event::{Action, Cmd}};
|
||||
|
||||
use super::Utils;
|
||||
|
||||
impl Utils {
|
||||
pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (name, args): (String, Table)| {
|
||||
let mut action = Action::new(name, Source::Emit, Some(Layer::Mgr))?;
|
||||
let mut action = Action::new(name, Source::Emit, Layer::Mgr)?;
|
||||
action.args = Sendable::table_to_args(lua, args)?;
|
||||
Ok(emit!(Call(action)))
|
||||
})
|
||||
|
|
@ -20,8 +20,7 @@ impl Utils {
|
|||
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 {
|
||||
name: name.into(),
|
||||
args: Sendable::table_to_args(lua, args)?,
|
||||
cmd: Cmd { name: name.into(), args: Sendable::table_to_args(lua, args)? },
|
||||
layer: Layer::Mgr,
|
||||
source: Source::Emit,
|
||||
}));
|
||||
|
|
@ -31,7 +30,7 @@ impl Utils {
|
|||
|
||||
pub(super) fn exec(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_async_function(|lua, (name, args): (String, Table)| async move {
|
||||
let mut action = Action::new(name, Source::Emit, Some(Layer::Mgr))?;
|
||||
let mut action = Action::new(name, Source::Emit, Layer::Mgr)?;
|
||||
action.args = Sendable::table_to_args(&lua, args)?;
|
||||
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ rust-version.workspace = true
|
|||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [ "vendored-lua" ]
|
||||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-binding = { path = "../yazi-binding", version = "26.2.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "26.2.2" }
|
||||
|
|
@ -20,6 +24,7 @@ yazi-dds = { path = "../yazi-dds", 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" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ yazi_macro::mod_pub!(entry fetcher loader preloader previewer);
|
|||
|
||||
yazi_macro::mod_flat!(runner spot);
|
||||
|
||||
pub static RUNNER: yazi_shared::RoCell<Runner> = yazi_shared::RoCell::new();
|
||||
pub static RUNNER: yazi_shim::cell::RoCell<Runner> = yazi_shim::cell::RoCell::new();
|
||||
|
||||
pub fn init(setter: fn(&mlua::Lua) -> mlua::Result<()>) {
|
||||
crate::loader::init();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use parking_lot::RwLock;
|
|||
use yazi_boot::BOOT;
|
||||
use yazi_fs::provider::local::Local;
|
||||
use yazi_macro::plugin_preset as preset;
|
||||
use yazi_shared::{BytesExt, LOG_LEVEL, RoCell};
|
||||
use yazi_shared::{BytesExt, LOG_LEVEL};
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use super::Chunk;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ rust-version.workspace = true
|
|||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [ "vendored-lua" ]
|
||||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-binding = { path = "../yazi-binding", version = "26.2.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "26.2.2" }
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ workspace = true
|
|||
|
||||
[dependencies]
|
||||
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
||||
yazi-shim = { path = "../yazi-shim", version = "26.2.2" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub static LOG_LEVEL: crate::SyncCell<LogLevel> = crate::SyncCell::new(LogLevel::None);
|
||||
pub static LOG_LEVEL: yazi_shim::cell::SyncCell<LogLevel> =
|
||||
yazi_shim::cell::SyncCell::new(LogLevel::None);
|
||||
|
||||
#[inline]
|
||||
pub fn env_exists(name: &str) -> bool { std::env::var_os(name).is_some_and(|s| !s.is_empty()) }
|
||||
|
|
|
|||
|
|
@ -1,27 +1,35 @@
|
|||
use std::{borrow::Cow, fmt::{self, Display}, mem, str::FromStr};
|
||||
use std::{borrow::Cow, fmt::{self, Display}, ops::{Deref, DerefMut}, str::FromStr};
|
||||
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use hashbrown::HashMap;
|
||||
use anyhow::{Result, bail};
|
||||
use serde_with::DeserializeFromStr;
|
||||
|
||||
use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}, event::Replier};
|
||||
use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}, event::{Cmd, Replier}};
|
||||
|
||||
#[derive(Clone, Debug, Default, DeserializeFromStr)]
|
||||
pub struct Action {
|
||||
pub name: SStr,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub cmd: Cmd,
|
||||
pub layer: Layer,
|
||||
pub source: Source,
|
||||
}
|
||||
|
||||
impl Deref for Action {
|
||||
type Target = Cmd;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.cmd }
|
||||
}
|
||||
|
||||
impl DerefMut for Action {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.cmd }
|
||||
}
|
||||
|
||||
impl Action {
|
||||
pub fn new<N>(name: N, source: Source, default: Option<Layer>) -> Result<Self>
|
||||
pub fn new<N>(name: N, source: Source, layer: Layer) -> Result<Self>
|
||||
where
|
||||
N: Into<SStr>,
|
||||
{
|
||||
let cow: SStr = name.into();
|
||||
let (layer, name) = match cow.find(':') {
|
||||
None => (default.ok_or_else(|| anyhow!("Cannot infer layer from action name: {cow}"))?, cow),
|
||||
None => (layer, cow),
|
||||
Some(i) => (cow[..i].parse()?, match cow {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(&s[i + 1..]),
|
||||
Cow::Owned(mut s) => {
|
||||
|
|
@ -31,14 +39,14 @@ impl Action {
|
|||
}),
|
||||
};
|
||||
|
||||
Ok(Self { name, args: Default::default(), layer, source })
|
||||
Ok(Self { cmd: Cmd { name, args: Default::default() }, layer, source })
|
||||
}
|
||||
|
||||
pub fn new_relay<N>(name: N) -> Self
|
||||
where
|
||||
N: Into<SStr>,
|
||||
{
|
||||
Self::new(name, Source::Relay, None).unwrap_or(Self::null())
|
||||
Self::new(name, Source::Relay, Layer::Null).unwrap_or(Self::null())
|
||||
}
|
||||
|
||||
pub fn new_relay_args<N, D, I>(name: N, args: I) -> Self
|
||||
|
|
@ -47,13 +55,13 @@ impl Action {
|
|||
D: Into<Data>,
|
||||
I: IntoIterator<Item = D>,
|
||||
{
|
||||
let mut action = Self::new(name, Source::Relay, None).unwrap_or(Self::null());
|
||||
let mut action = Self::new(name, Source::Relay, Layer::Null).unwrap_or(Self::null());
|
||||
action.args =
|
||||
args.into_iter().enumerate().map(|(i, a)| (DataKey::Integer(i as i64), a.into())).collect();
|
||||
action
|
||||
}
|
||||
|
||||
fn null() -> Self { Self { name: Cow::Borrowed("null"), ..Default::default() } }
|
||||
fn null() -> Self { Self { cmd: Cmd::null(), layer: Layer::Null, source: Source::Unknown } }
|
||||
|
||||
pub fn len(&self) -> usize { self.args.len() }
|
||||
|
||||
|
|
@ -217,31 +225,6 @@ impl Action {
|
|||
}
|
||||
|
||||
pub fn take_replier(&mut self) -> Option<Replier> { self.take_any("replier") }
|
||||
|
||||
// Parse
|
||||
pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>>
|
||||
where
|
||||
I: IntoIterator<Item = String>,
|
||||
{
|
||||
let mut i = 0i64;
|
||||
words
|
||||
.into_iter()
|
||||
.map(|s| (s, true))
|
||||
.chain(last.into_iter().map(|s| (s, false)))
|
||||
.map(|(word, normal)| {
|
||||
let Some(arg) = word.strip_prefix("--").filter(|&s| normal && !s.is_empty()) else {
|
||||
i += 1;
|
||||
return Ok((DataKey::Integer(i - 1), word.into()));
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
let key = parts.next().expect("at least one part");
|
||||
let val = parts.next().map_or(Data::Boolean(true), Data::from);
|
||||
|
||||
Ok((key.to_owned().into(), val))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Action {
|
||||
|
|
@ -271,13 +254,11 @@ impl FromStr for Action {
|
|||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (mut words, last) = crate::shell::unix::split(s, true)?;
|
||||
if words.is_empty() || words[0].is_empty() {
|
||||
bail!("action name cannot be empty");
|
||||
}
|
||||
let cmd = Cmd::from_str(s)?;
|
||||
|
||||
let mut me = Self::new(cmd.name, Source::Unknown, Layer::Null)?;
|
||||
me.args = cmd.args;
|
||||
|
||||
let mut me = Self::new(mem::take(&mut words[0]), Default::default(), Some(Default::default()))?;
|
||||
me.args = Self::parse_args(words.into_iter().skip(1), last)?;
|
||||
Ok(me)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
yazi-shared/src/event/cmd.rs
Normal file
57
yazi-shared/src/event/cmd.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use std::{mem, str::FromStr};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use hashbrown::HashMap;
|
||||
use serde_with::DeserializeFromStr;
|
||||
|
||||
use crate::{SStr, data::{Data, DataKey}};
|
||||
|
||||
#[derive(Clone, Debug, Default, DeserializeFromStr)]
|
||||
pub struct Cmd {
|
||||
pub name: SStr,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
}
|
||||
|
||||
impl FromStr for Cmd {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (mut words, last) = crate::shell::unix::split(s, true)?;
|
||||
if words.is_empty() || words[0].is_empty() {
|
||||
bail!("command name cannot be empty");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
name: mem::take(&mut words[0]).into(),
|
||||
args: Self::parse_args(words.into_iter().skip(1), last)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
pub fn null() -> Self { Self { name: "null".into(), ..Default::default() } }
|
||||
|
||||
pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>>
|
||||
where
|
||||
I: IntoIterator<Item = String>,
|
||||
{
|
||||
let mut i = 0i64;
|
||||
words
|
||||
.into_iter()
|
||||
.map(|s| (s, true))
|
||||
.chain(last.into_iter().map(|s| (s, false)))
|
||||
.map(|(word, normal)| {
|
||||
let Some(arg) = word.strip_prefix("--").filter(|&s| normal && !s.is_empty()) else {
|
||||
i += 1;
|
||||
return Ok((DataKey::Integer(i - 1), word.into()));
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
let key = parts.next().expect("at least one part");
|
||||
let val = parts.next().map_or(Data::Boolean(true), Data::from);
|
||||
|
||||
Ok((key.to_owned().into(), val))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ impl<'de> serde::Deserializer<'de> for Action {
|
|||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_map(MapDeserializer::new(
|
||||
self.args.into_iter().map(|(key, value)| (KeyDeserializer::Owned(key), value)),
|
||||
self.cmd.args.into_iter().map(|(key, value)| (KeyDeserializer::Owned(key), value)),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crossterm::event::{KeyEvent, MouseEvent};
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
use super::ActionCow;
|
||||
use crate::RoCell;
|
||||
|
||||
static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new();
|
||||
static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
yazi_macro::mod_flat!(action cow de de_owned event);
|
||||
yazi_macro::mod_flat!(action cmd cow de de_owned event);
|
||||
|
||||
pub type Replier = tokio::sync::mpsc::UnboundedSender<anyhow::Result<crate::data::Data>>;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use strum::{Display, EnumString, FromRepr, IntoStaticStr};
|
|||
#[repr(u8)]
|
||||
pub enum Layer {
|
||||
#[default]
|
||||
Null,
|
||||
App,
|
||||
Mgr,
|
||||
Tasks,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
yazi_macro::mod_pub!(data event loc path pool scheme shell strand translit url wtf8);
|
||||
|
||||
yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id last_value layer localset natsort non_empty_string os predictor ro_cell source sync_cell terminal tests throttle time utf8);
|
||||
yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id last_value layer localset natsort non_empty_string os predictor source terminal tests throttle time utf8);
|
||||
|
||||
pub fn init() {
|
||||
LOCAL_SET.with(tokio::task::LocalSet::new);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::RoCell;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
pub static LOCAL_SET: RoCell<LocalSet> = RoCell::new();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::{OsStr, OsString}, fmt::{Display, Formatter}, ops::Deref};
|
||||
use std::{borrow::Borrow, ffi::{OsStr, OsString}, fmt::{Display, Formatter}, ops::Deref};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
|
|
@ -20,6 +20,16 @@ impl Deref for NonEmptyString {
|
|||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl Borrow<str> for NonEmptyString {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str { &self.0 }
|
||||
}
|
||||
|
||||
impl Borrow<String> for NonEmptyString {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &String { &self.0 }
|
||||
}
|
||||
|
||||
impl AsRef<str> for NonEmptyString {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str { &self.0 }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#[cfg(unix)]
|
||||
pub static USERS_CACHE: crate::RoCell<uzers::UsersCache> = crate::RoCell::new();
|
||||
pub static USERS_CACHE: yazi_shim::cell::RoCell<uzers::UsersCache> = yazi_shim::cell::RoCell::new();
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn hostname() -> Option<&'static str> {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
yazi_macro::mod_flat!(cow pool ptr symbol traits);
|
||||
|
||||
static SYMBOLS: crate::RoCell<
|
||||
static SYMBOLS: yazi_shim::cell::RoCell<
|
||||
parking_lot::Mutex<hashbrown::HashMap<SymbolPtr, u64, foldhash::fast::FixedState>>,
|
||||
> = crate::RoCell::new();
|
||||
> = yazi_shim::cell::RoCell::new();
|
||||
|
||||
pub(super) fn init() { SYMBOLS.with(<_>::default); }
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,17 @@ rust-version.workspace = true
|
|||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [ "vendored-lua" ]
|
||||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
||||
|
||||
# External dependencies
|
||||
arc-swap = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
|
|
|||
1
yazi-shim/src/cell/mod.rs
Normal file
1
yazi-shim/src/cell/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(ro_cell sync_cell);
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
yazi_macro::mod_pub!(arc_swap crossterm mlua ratatui strum toml vec);
|
||||
yazi_macro::mod_pub!(arc_swap cell crossterm mlua ratatui serde strum toml vec);
|
||||
|
||||
yazi_macro::mod_flat!(twox);
|
||||
|
|
|
|||
1
yazi-shim/src/serde/mod.rs
Normal file
1
yazi-shim/src/serde/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(traits);
|
||||
27
yazi-shim/src/serde/traits.rs
Normal file
27
yazi-shim/src/serde/traits.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use arc_swap::ArcSwap;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::cell::SyncCell;
|
||||
|
||||
pub trait Overlay<Rhs = Self> {
|
||||
fn overlay(&self, new: Rhs);
|
||||
}
|
||||
|
||||
impl<T> Overlay for ArcSwap<T> {
|
||||
fn overlay(&self, new: Self) { self.store(new.into_inner()); }
|
||||
}
|
||||
|
||||
impl<T> Overlay for SyncCell<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
fn overlay(&self, new: Self) { self.set(new.get()); }
|
||||
}
|
||||
|
||||
impl<T> Overlay for Vec<T> {
|
||||
fn overlay(&self, _: Self) {}
|
||||
}
|
||||
|
||||
impl<K, V, S> Overlay for HashMap<K, V, S> {
|
||||
fn overlay(&self, _: Self) {}
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
use serde::{Deserializer, de::DeserializeSeed};
|
||||
use std::{hash::{BuildHasher, Hash}, sync::Arc};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Deserializer, de::{DeserializeSeed, MapAccess, Visitor}};
|
||||
use toml::de;
|
||||
|
||||
use crate::arc_swap::IntoPointee;
|
||||
|
||||
pub trait DeserializeOver: DeserializeOverWith + DeserializeOverHook {
|
||||
fn deserialize_over(self, input: &str) -> Result<Self, de::Error> {
|
||||
let table = de::DeTable::parse(input)?;
|
||||
|
|
@ -19,11 +25,61 @@ pub trait DeserializeOverWith: Sized {
|
|||
fn deserialize_over_with<'de, D: Deserializer<'de>>(self, de: D) -> Result<Self, D::Error>;
|
||||
}
|
||||
|
||||
impl<K, V, S> DeserializeOverWith for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
for<'de> K: Deserialize<'de>,
|
||||
for<'de> V: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize_over_with<'de, D: Deserializer<'de>>(self, de: D) -> Result<Self, D::Error> {
|
||||
struct HashMapVisitor<K, V, S>(HashMap<K, V, S>);
|
||||
|
||||
impl<'de, K, V, S> Visitor<'de> for HashMapVisitor<K, V, S>
|
||||
where
|
||||
K: Eq + Hash + Deserialize<'de>,
|
||||
V: Deserialize<'de>,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Value = HashMap<K, V, S>;
|
||||
|
||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str("a map") }
|
||||
|
||||
fn visit_map<M: MapAccess<'de>>(mut self, mut map: M) -> Result<Self::Value, M::Error> {
|
||||
while let Some(key) = map.next_key()? {
|
||||
self.0.insert(key, map.next_value()?);
|
||||
}
|
||||
Ok(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_map(HashMapVisitor(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DeserializeOverWith> DeserializeOverWith for ArcSwap<T> {
|
||||
fn deserialize_over_with<'de, D: Deserializer<'de>>(self, de: D) -> Result<Self, D::Error> {
|
||||
Arc::try_unwrap(self.into_inner())
|
||||
.unwrap_or_else(|_| panic!("ArcSwap must have single owner during deserialization"))
|
||||
.deserialize_over_with(de)
|
||||
.map(IntoPointee::into_pointee)
|
||||
}
|
||||
}
|
||||
|
||||
// --- DeserializeOverHook
|
||||
pub trait DeserializeOverHook: Sized {
|
||||
fn deserialize_over_hook(self) -> Result<Self, de::Error> { Ok(self) }
|
||||
}
|
||||
|
||||
impl<T: DeserializeOverHook> DeserializeOverHook for ArcSwap<T> {
|
||||
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||
Arc::try_unwrap(self.into_inner())
|
||||
.unwrap_or_else(|_| panic!("ArcSwap must have single owner during hook"))
|
||||
.deserialize_over_hook()
|
||||
.map(IntoPointee::into_pointee)
|
||||
}
|
||||
}
|
||||
|
||||
// --- DeserializeOverSeed
|
||||
pub struct DeserializeOverSeed<T>(pub T);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ workspace = true
|
|||
yazi-config = { path = "../yazi-config", version = "26.2.2" }
|
||||
yazi-emulator = { path = "../yazi-emulator", 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-tty = { path = "../yazi-tty", version = "26.2.2" }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use tokio::sync::Semaphore;
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shim::cell::RoCell;
|
||||
|
||||
pub static YIELD_TO_SUBPROCESS: RoCell<Semaphore> = RoCell::new();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ use anyhow::Result;
|
|||
use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}};
|
||||
use ratatui::{CompletedFrame, Frame, Terminal, backend::CrosstermBackend, buffer::Buffer, layout::Rect};
|
||||
use yazi_emulator::{Emulator, Mux, TMUX};
|
||||
use yazi_shared::SyncCell;
|
||||
use yazi_shim::crossterm::{If, RestoreBackground, RestoreCursor, SetBackground};
|
||||
use yazi_shim::{cell::SyncCell, crossterm::{If, RestoreBackground, RestoreCursor, SetBackground}};
|
||||
use yazi_tty::{TTY, TtyWriter};
|
||||
|
||||
use crate::{TermOption, TermState};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ workspace = true
|
|||
[dependencies]
|
||||
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" }
|
||||
|
||||
# External dependencies
|
||||
parking_lot = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ yazi_macro::mod_flat!(handle tty);
|
|||
#[cfg(windows)]
|
||||
yazi_macro::mod_flat!(windows);
|
||||
|
||||
pub static TTY: yazi_shared::RoCell<Tty> = yazi_shared::RoCell::new();
|
||||
pub static TTY: yazi_shim::cell::RoCell<Tty> = yazi_shim::cell::RoCell::new();
|
||||
|
||||
pub fn init() { TTY.with(<_>::default); }
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue