mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
perf: rewrite config parser to double the startup speed (#2508)
This commit is contained in:
parent
db0300c902
commit
1a2a18e333
74 changed files with 1131 additions and 1567 deletions
330
Cargo.lock
generated
330
Cargo.lock
generated
|
|
@ -328,9 +328,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.16"
|
||||
version = "1.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
|
@ -689,17 +689,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
|
|
@ -840,15 +829,6 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
|
|
@ -1047,151 +1027,12 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.5"
|
||||
|
|
@ -1404,12 +1245,6 @@ version = "0.4.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
|
@ -2404,12 +2239,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
|
@ -2455,17 +2284,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "5.2.0"
|
||||
|
|
@ -2620,16 +2438,6 @@ dependencies = [
|
|||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.1"
|
||||
|
|
@ -2870,35 +2678,12 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
|
@ -2926,36 +2711,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
"validator_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"once_cell",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
|
|
@ -3301,18 +3056,6 @@ version = "0.0.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "yazi-adapter"
|
||||
version = "25.3.7"
|
||||
|
|
@ -3395,7 +3138,7 @@ dependencies = [
|
|||
"serde",
|
||||
"toml",
|
||||
"tracing",
|
||||
"validator",
|
||||
"yazi-codegen",
|
||||
"yazi-fs",
|
||||
"yazi-macro",
|
||||
"yazi-shared",
|
||||
|
|
@ -3645,30 +3388,6 @@ dependencies = [
|
|||
"yazi-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.23"
|
||||
|
|
@ -3689,49 +3408,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use base64::{Engine, engine::{Config, general_purpose::STANDARD}};
|
|||
use crossterm::{cursor::MoveTo, queue};
|
||||
use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEncoder, png::PngEncoder}};
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use crate::{CLOSE, Emulator, Image, START, adapter::Adapter};
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ impl Iip {
|
|||
if img.color().has_alpha() {
|
||||
PngEncoder::new(&mut b).write_image(&img.into_rgba8(), w, h, ExtendedColorType::Rgba8)?;
|
||||
} else {
|
||||
JpegEncoder::new_with_quality(&mut b, PREVIEW.image_quality).encode_image(&img)?;
|
||||
JpegEncoder::new_with_quality(&mut b, YAZI.preview.image_quality).encode_image(&img)?;
|
||||
};
|
||||
|
||||
let mut buf = String::with_capacity(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use color_quant::NeuQuant;
|
|||
use crossterm::{cursor::MoveTo, queue};
|
||||
use image::DynamicImage;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ impl Sixel {
|
|||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let img = img.into_rgba8();
|
||||
let nq = NeuQuant::new(PREVIEW.sixel_fraction as i32, 256 - alpha as usize, &img);
|
||||
let nq = NeuQuant::new(YAZI.preview.sixel_fraction as i32, 256 - alpha as usize, &img);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(1 << 16);
|
||||
write!(buf, "{START}P0;1;8q\"1;1;{};{}", img.width(), img.height())?;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use image::ImageReader;
|
|||
use ratatui::layout::Rect;
|
||||
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
|
||||
use tracing::{debug, warn};
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_shared::{LOG_LEVEL, RoCell, env_exists};
|
||||
|
||||
use crate::{Adapter, Dimension};
|
||||
|
|
@ -57,8 +57,8 @@ impl Ueberzug {
|
|||
.map(|(r1, r2)| Rect {
|
||||
x: max.x,
|
||||
y: max.y,
|
||||
width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _),
|
||||
height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _),
|
||||
width: max.width.min((w.min(YAZI.preview.max_width as _) as f64 / r1).ceil() as _),
|
||||
height: max.height.min((h.min(YAZI.preview.max_height as _) as f64 / r2).ceil() as _),
|
||||
})
|
||||
.unwrap_or(max);
|
||||
|
||||
|
|
@ -101,8 +101,8 @@ impl Ueberzug {
|
|||
}
|
||||
|
||||
fn adjust_rect(mut rect: Rect) -> Rect {
|
||||
let scale = PREVIEW.ueberzug_scale;
|
||||
let (x, y, w, h) = PREVIEW.ueberzug_offset;
|
||||
let scale = YAZI.preview.ueberzug_scale;
|
||||
let (x, y, w, h) = YAZI.preview.ueberzug_offset;
|
||||
|
||||
rect.x = 0f32.max(rect.x as f32 * scale + x) as u16;
|
||||
rect.y = 0f32.max(rect.y as f32 * scale + y) as u16;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
|||
use anyhow::Result;
|
||||
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::{PREVIEW, TASKS};
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use crate::Dimension;
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ pub struct Image;
|
|||
impl Image {
|
||||
pub async fn precache(path: &Path, cache: PathBuf) -> Result<()> {
|
||||
let (mut img, orientation, icc) = Self::decode_from(path).await?;
|
||||
let (w, h) = Self::flip_size(orientation, (PREVIEW.max_width, PREVIEW.max_height));
|
||||
let (w, h) = Self::flip_size(orientation, (YAZI.preview.max_width, YAZI.preview.max_height));
|
||||
|
||||
let buf = tokio::task::spawn_blocking(move || {
|
||||
if img.width() > w || img.height() > h {
|
||||
|
|
@ -29,7 +29,7 @@ impl Image {
|
|||
icc.map(|b| encoder.set_icc_profile(b));
|
||||
encoder.write_image(&rgba, rgba.width(), rgba.height(), ExtendedColorType::Rgba8)?;
|
||||
} else {
|
||||
let mut encoder = JpegEncoder::new_with_quality(&mut buf, PREVIEW.image_quality);
|
||||
let mut encoder = JpegEncoder::new_with_quality(&mut buf, YAZI.preview.image_quality);
|
||||
icc.map(|b| encoder.set_icc_profile(b));
|
||||
encoder.encode_image(&img.into_rgb8())?;
|
||||
}
|
||||
|
|
@ -68,9 +68,9 @@ impl Image {
|
|||
Dimension::ratio()
|
||||
.map(|(r1, r2)| {
|
||||
let (w, h) = ((rect.width as f64 * r1) as u32, (rect.height as f64 * r2) as u32);
|
||||
(w.min(PREVIEW.max_width), h.min(PREVIEW.max_height))
|
||||
(w.min(YAZI.preview.max_width), h.min(YAZI.preview.max_height))
|
||||
})
|
||||
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height))
|
||||
.unwrap_or((YAZI.preview.max_width, YAZI.preview.max_height))
|
||||
}
|
||||
|
||||
pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect {
|
||||
|
|
@ -86,7 +86,7 @@ impl Image {
|
|||
|
||||
#[inline]
|
||||
fn filter() -> FilterType {
|
||||
match PREVIEW.image_filter.as_str() {
|
||||
match YAZI.preview.image_filter.as_str() {
|
||||
"nearest" => FilterType::Nearest,
|
||||
"triangle" => FilterType::Triangle,
|
||||
"catmull-rom" => FilterType::CatmullRom,
|
||||
|
|
@ -98,14 +98,14 @@ impl Image {
|
|||
|
||||
async fn decode_from(path: &Path) -> ImageResult<(DynamicImage, Orientation, Option<Vec<u8>>)> {
|
||||
let mut limits = Limits::no_limits();
|
||||
if TASKS.image_alloc > 0 {
|
||||
limits.max_alloc = Some(TASKS.image_alloc as u64);
|
||||
if YAZI.tasks.image_alloc > 0 {
|
||||
limits.max_alloc = Some(YAZI.tasks.image_alloc as u64);
|
||||
}
|
||||
if TASKS.image_bound[0] > 0 {
|
||||
limits.max_image_width = Some(TASKS.image_bound[0] as u32);
|
||||
if YAZI.tasks.image_bound[0] > 0 {
|
||||
limits.max_image_width = Some(YAZI.tasks.image_bound[0] as u32);
|
||||
}
|
||||
if TASKS.image_bound[1] > 0 {
|
||||
limits.max_image_height = Some(TASKS.image_bound[1] as u32);
|
||||
if YAZI.tasks.image_bound[1] > 0 {
|
||||
limits.max_image_height = Some(YAZI.tasks.image_bound[1] as u32);
|
||||
}
|
||||
|
||||
let path = path.to_owned();
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use yazi_config::PREVIEW;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::Xdg;
|
||||
|
||||
use super::Actions;
|
||||
|
||||
impl Actions {
|
||||
pub(super) fn clear_cache() {
|
||||
if PREVIEW.cache_dir == Xdg::cache_dir() {
|
||||
println!("Clearing cache directory: \n{:?}", PREVIEW.cache_dir);
|
||||
std::fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
|
||||
if YAZI.preview.cache_dir == Xdg::cache_dir() {
|
||||
println!("Clearing cache directory: \n{:?}", YAZI.preview.cache_dir);
|
||||
std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap();
|
||||
} else {
|
||||
println!(
|
||||
"You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}",
|
||||
PREVIEW.cache_dir
|
||||
YAZI.preview.cache_dir
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,17 +56,17 @@ impl Actions {
|
|||
writeln!(
|
||||
s,
|
||||
" default : {:?}",
|
||||
yazi_config::OPEN.openers("f75a.txt", "text/plain").and_then(|a| a.first().cloned())
|
||||
yazi_config::YAZI.opener.first(yazi_config::YAZI.open.all("f75a.txt", "text/plain"))
|
||||
)?;
|
||||
writeln!(
|
||||
s,
|
||||
" block-create: {:?}",
|
||||
yazi_config::OPEN.block_opener("bulk-create.txt", "text/plain")
|
||||
yazi_config::YAZI.opener.block(yazi_config::YAZI.open.all("bulk-create.txt", "text/plain"))
|
||||
)?;
|
||||
writeln!(
|
||||
s,
|
||||
" block-rename: {:?}",
|
||||
yazi_config::OPEN.block_opener("bulk-rename.txt", "text/plain")
|
||||
yazi_config::YAZI.opener.block(yazi_config::YAZI.open.all("bulk-rename.txt", "text/plain"))
|
||||
)?;
|
||||
|
||||
writeln!(s, "\nMultiplexers")?;
|
||||
|
|
|
|||
|
|
@ -69,9 +69,6 @@ pub(super) struct CommandPack {
|
|||
/// Upgrade all packages.
|
||||
#[arg(short = 'u', long)]
|
||||
pub(super) upgrade: bool,
|
||||
/// Migrate all packages.
|
||||
#[arg(short = 'm', long)]
|
||||
pub(super) migrate: bool, // TODO: remove this
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
|
|
|
|||
|
|
@ -62,10 +62,7 @@ async fn run() -> anyhow::Result<()> {
|
|||
|
||||
Command::Pack(cmd) => {
|
||||
package::init()?;
|
||||
package::Package::load().await?.sync().await.ok();
|
||||
if cmd.migrate {
|
||||
outln!("Migration successful")?;
|
||||
} else if cmd.install {
|
||||
if cmd.install {
|
||||
package::Package::load().await?.install(false).await?;
|
||||
} else if cmd.list {
|
||||
package::Package::load().await?.print()?;
|
||||
|
|
|
|||
|
|
@ -48,17 +48,7 @@ Please manually delete it from `{}` and re-run the command.",
|
|||
};
|
||||
|
||||
for file in files {
|
||||
// TODO: remove this
|
||||
let (from, to) = if *file == "main.lua" {
|
||||
if maybe_exists(from.join(file)).await {
|
||||
(from.join(file), to.join(file))
|
||||
} else {
|
||||
(from.join("init.lua"), to.join("main.lua"))
|
||||
}
|
||||
} else {
|
||||
(from.join(file), to.join(file))
|
||||
};
|
||||
|
||||
let (from, to) = (from.join(file), to.join(file));
|
||||
copy_and_seal(&from, &to)
|
||||
.await
|
||||
.with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use std::{path::{Path, PathBuf}, str::FromStr};
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use tokio::fs;
|
||||
use yazi_fs::{Xdg, ok_or_not_found, remove_sealed, unique_name};
|
||||
use yazi_fs::Xdg;
|
||||
use yazi_macro::outln;
|
||||
|
||||
use super::Dependency;
|
||||
|
|
@ -84,85 +84,6 @@ impl Package {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: remove this
|
||||
pub(crate) async fn sync(&mut self) -> Result<()> {
|
||||
async fn make_readonly(p: &Path) -> Result<()> {
|
||||
let mut perms = fs::metadata(p).await?.permissions();
|
||||
perms.set_readonly(true);
|
||||
fs::set_permissions(p, perms).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match fs::read_dir(Xdg::config_dir().join("plugins")).await {
|
||||
Ok(mut it) => {
|
||||
while let Some(entry) = it.next_entry().await? {
|
||||
let dir = entry.path();
|
||||
if !dir.is_dir() || dir.extension().is_none_or(|s| s != "yazi") {
|
||||
continue;
|
||||
}
|
||||
|
||||
match fs::symlink_metadata(dir.join("init.lua")).await {
|
||||
Ok(_) => {}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
|
||||
ok_or_not_found(
|
||||
fs::rename(
|
||||
dir.join("main.lua"),
|
||||
unique_name(dir.join("main.lua.bak").into(), async { false }).await?,
|
||||
)
|
||||
.await,
|
||||
)?;
|
||||
|
||||
ok_or_not_found(fs::rename(dir.join("init.lua"), dir.join("main.lua")).await)?;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(e) => Err(e)
|
||||
.context(format!("failed to read `{}`", Xdg::config_dir().join("plugins").display()))?,
|
||||
}
|
||||
|
||||
for d in &mut self.plugins {
|
||||
let dir = Xdg::config_dir().join(format!("plugins/{}", d.name));
|
||||
for f in ["LICENSE", "README.md", "main.lua"] {
|
||||
make_readonly(&dir.join(f)).await.ok();
|
||||
}
|
||||
|
||||
let tracker = dir.join("DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY");
|
||||
if fs::read(&tracker).await.is_ok_and(|v| v.is_empty()) {
|
||||
if d.hash.is_empty() {
|
||||
d.hash = d.hash().await?;
|
||||
}
|
||||
remove_sealed(&tracker).await.ok();
|
||||
}
|
||||
}
|
||||
for d in &mut self.flavors {
|
||||
let dir = Xdg::config_dir().join(format!("flavors/{}", d.name));
|
||||
for f in [
|
||||
"LICENSE",
|
||||
"LICENSE-tmtheme",
|
||||
"README.md",
|
||||
"filestyle.toml",
|
||||
"flavor.toml",
|
||||
"preview.png",
|
||||
"tmtheme.xml",
|
||||
] {
|
||||
make_readonly(&dir.join(f)).await.ok();
|
||||
}
|
||||
|
||||
let tracker = dir.join("DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY");
|
||||
if fs::read(&tracker).await.is_ok_and(|v| v.is_empty()) {
|
||||
if d.hash.is_empty() {
|
||||
d.hash = d.hash().await?;
|
||||
}
|
||||
remove_sealed(&tracker).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
self.save().await
|
||||
}
|
||||
|
||||
async fn add(&mut self, use_: &str) -> Result<()> {
|
||||
let mut dep = Dependency::from_str(use_)?;
|
||||
if let Some(d) = self.identical(&dep) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{FnArg, ItemFn};
|
||||
use syn::{Attribute, Data, DeriveInput, Fields, FnArg, ItemFn, parse_macro_input};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn command(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
|
@ -23,7 +23,7 @@ pub fn command(_: TokenStream, item: TokenStream) -> TokenStream {
|
|||
|
||||
// Add `__` prefix to the original function name
|
||||
let name_ori = f.sig.ident;
|
||||
f.sig.ident = format_ident!("__{}", name_ori);
|
||||
f.sig.ident = format_ident!("__{name_ori}");
|
||||
let name_new = &f.sig.ident;
|
||||
|
||||
// Collect the rest of the arguments
|
||||
|
|
@ -39,3 +39,146 @@ pub fn command(_: TokenStream, item: TokenStream) -> TokenStream {
|
|||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(DeserializeOver1)]
|
||||
pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||
// Parse the input tokens into a syntax tree
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
// Get the name of the struct
|
||||
let name = &input.ident;
|
||||
let shadow_name = format_ident!("__{name}Shadow");
|
||||
|
||||
// Process the struct fields
|
||||
let (shadow_fields, field_calls) = match &input.data {
|
||||
Data::Struct(struct_) => match &struct_.fields {
|
||||
Fields::Named(fields) => {
|
||||
let mut shadow_fields = Vec::with_capacity(fields.named.len());
|
||||
let mut field_calls = Vec::with_capacity(fields.named.len());
|
||||
|
||||
for field in &fields.named {
|
||||
let name = &field.ident;
|
||||
let attrs: Vec<&Attribute> =
|
||||
field.attrs.iter().filter(|&a| a.path().is_ident("serde")).collect();
|
||||
|
||||
shadow_fields.push(quote! {
|
||||
#(#attrs)*
|
||||
pub(crate) #name: Option<toml::Value>
|
||||
});
|
||||
field_calls.push(quote! {
|
||||
if let Some(value) = shadow.#name {
|
||||
self.#name = self.#name.deserialize_over(value).map_err(serde::de::Error::custom)?;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(shadow_fields, field_calls)
|
||||
}
|
||||
_ => panic!("DeserializeOver1 only supports structs with named fields"),
|
||||
},
|
||||
_ => panic!("DeserializeOver1 only supports structs"),
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(crate) struct #shadow_name {
|
||||
#(#shadow_fields),*
|
||||
}
|
||||
|
||||
impl #name {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_over<'de, D>(self, deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
self.deserialize_over_with::<D>(Self::deserialize_shadow(deserializer)?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_shadow<'de, D>(deserializer: D) -> Result<#shadow_name, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#shadow_name::deserialize(deserializer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_over_with<'de, D>(mut self, shadow: #shadow_name) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#(#field_calls)*
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(DeserializeOver2)]
|
||||
pub fn deserialize_over2(input: TokenStream) -> TokenStream {
|
||||
// Parse the input tokens into a syntax tree
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
// Get the name of the struct
|
||||
let name = &input.ident;
|
||||
let shadow_name = format_ident!("__{name}Shadow");
|
||||
|
||||
// Process the struct fields
|
||||
let (shadow_fields, field_assignments) = match &input.data {
|
||||
Data::Struct(struct_) => match &struct_.fields {
|
||||
Fields::Named(fields) => {
|
||||
let mut shadow_fields = Vec::with_capacity(fields.named.len());
|
||||
let mut field_assignments = Vec::with_capacity(fields.named.len());
|
||||
|
||||
for field in &fields.named {
|
||||
let (ty, name) = (&field.ty, &field.ident);
|
||||
shadow_fields.push(quote! {
|
||||
pub(crate) #name: Option<#ty>
|
||||
});
|
||||
field_assignments.push(quote! {
|
||||
if let Some(value) = shadow.#name {
|
||||
self.#name = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(shadow_fields, field_assignments)
|
||||
}
|
||||
_ => panic!("DeserializeOver2 only supports structs with named fields"),
|
||||
},
|
||||
_ => panic!("DeserializeOver2 only supports structs"),
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(crate) struct #shadow_name {
|
||||
#(#shadow_fields),*
|
||||
}
|
||||
|
||||
impl #name {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_over<'de, D>(mut self, deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>
|
||||
{
|
||||
Ok(self.deserialize_over_with(Self::deserialize_shadow(deserializer)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_shadow<'de, D>(deserializer: D) -> Result<#shadow_name, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>
|
||||
{
|
||||
#shadow_name::deserialize(deserializer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_over_with(mut self, shadow: #shadow_name) -> Self {
|
||||
#(#field_assignments)*
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
@ -24,7 +25,6 @@ regex = { workspace = true }
|
|||
serde = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
validator = { version = "0.20.0", features = [ "derive" ] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||
|
|
|
|||
|
|
@ -1,23 +1,21 @@
|
|||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver1;
|
||||
use yazi_shared::Layer;
|
||||
|
||||
use super::Chord;
|
||||
use crate::{Preset, keymap::Key};
|
||||
use super::{Chord, KeymapRule};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize, DeserializeOver1)]
|
||||
pub struct Keymap {
|
||||
pub mgr: Vec<Chord>,
|
||||
pub tasks: Vec<Chord>,
|
||||
pub spot: Vec<Chord>,
|
||||
pub pick: Vec<Chord>,
|
||||
pub input: Vec<Chord>,
|
||||
pub confirm: Vec<Chord>,
|
||||
pub help: Vec<Chord>,
|
||||
pub cmp: Vec<Chord>,
|
||||
#[serde(rename = "manager")]
|
||||
pub mgr: KeymapRule,
|
||||
pub tasks: KeymapRule,
|
||||
pub spot: KeymapRule,
|
||||
pub pick: KeymapRule,
|
||||
pub input: KeymapRule,
|
||||
pub confirm: KeymapRule,
|
||||
pub help: KeymapRule,
|
||||
pub cmp: KeymapRule,
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
|
|
@ -38,77 +36,17 @@ impl Keymap {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Keymap {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse your keymap.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Keymap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
#[serde(rename = "manager")]
|
||||
mgr: Inner, // TODO: remove serde(rename)
|
||||
tasks: Inner,
|
||||
spot: Inner,
|
||||
pick: Inner,
|
||||
input: Inner,
|
||||
confirm: Inner,
|
||||
help: Inner,
|
||||
cmp: Inner,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct Inner {
|
||||
keymap: IndexSet<Chord>,
|
||||
#[serde(default)]
|
||||
prepend_keymap: IndexSet<Chord>,
|
||||
#[serde(default)]
|
||||
append_keymap: IndexSet<Chord>,
|
||||
}
|
||||
|
||||
fn mix(l: Layer, a: IndexSet<Chord>, b: IndexSet<Chord>, c: IndexSet<Chord>) -> Vec<Chord> {
|
||||
#[inline]
|
||||
fn on(Chord { on, .. }: &Chord) -> [Key; 2] {
|
||||
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
|
||||
}
|
||||
|
||||
let a_seen: HashSet<_> = a.iter().map(on).collect();
|
||||
let b_seen: HashSet<_> = b.iter().map(on).collect();
|
||||
|
||||
Preset::mix(
|
||||
a,
|
||||
b.into_iter().filter(|v| !a_seen.contains(&on(v))),
|
||||
c.into_iter().filter(|v| !b_seen.contains(&on(v))),
|
||||
)
|
||||
.filter(|chord| !chord.noop())
|
||||
.map(|chord| chord.with_layer(l))
|
||||
.collect()
|
||||
}
|
||||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
impl Keymap {
|
||||
pub(crate) fn reshape(self) -> Result<Self> {
|
||||
Ok(Self {
|
||||
#[rustfmt::skip]
|
||||
mgr: mix(Layer::Mgr, shadow.mgr.prepend_keymap, shadow.mgr.keymap, shadow.mgr.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
tasks: mix(Layer::Tasks, shadow.tasks.prepend_keymap, shadow.tasks.keymap, shadow.tasks.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
spot: mix(Layer::Spot, shadow.spot.prepend_keymap, shadow.spot.keymap, shadow.spot.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
pick: mix(Layer::Pick, shadow.pick.prepend_keymap, shadow.pick.keymap, shadow.pick.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
input: mix(Layer::Input, shadow.input.prepend_keymap, shadow.input.keymap, shadow.input.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
confirm: mix(Layer::Confirm, shadow.confirm.prepend_keymap, shadow.confirm.keymap, shadow.confirm.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
help: mix(Layer::Help, shadow.help.prepend_keymap, shadow.help.keymap, shadow.help.append_keymap),
|
||||
#[rustfmt::skip]
|
||||
cmp: mix(Layer::Cmp, shadow.cmp.prepend_keymap, shadow.cmp.keymap, shadow.cmp.append_keymap),
|
||||
mgr: self.mgr.reshape(Layer::Mgr)?,
|
||||
tasks: self.tasks.reshape(Layer::Tasks)?,
|
||||
spot: self.spot.reshape(Layer::Spot)?,
|
||||
pick: self.pick.reshape(Layer::Pick)?,
|
||||
input: self.input.reshape(Layer::Input)?,
|
||||
confirm: self.confirm.reshape(Layer::Confirm)?,
|
||||
help: self.help.reshape(Layer::Help)?,
|
||||
cmp: self.cmp.reshape(Layer::Cmp)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(chord cow deserializers key keymap);
|
||||
yazi_macro::mod_flat!(chord cow deserializers key keymap rule);
|
||||
|
|
|
|||
47
yazi-config/src/keymap/rule.rs
Normal file
47
yazi-config/src/keymap/rule.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use std::{collections::HashSet, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_shared::Layer;
|
||||
|
||||
use super::Chord;
|
||||
use crate::{Preset, keymap::Key};
|
||||
|
||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||
pub struct KeymapRule {
|
||||
pub keymap: Vec<Chord>,
|
||||
#[serde(default)]
|
||||
prepend_keymap: Vec<Chord>,
|
||||
#[serde(default)]
|
||||
append_keymap: Vec<Chord>,
|
||||
}
|
||||
|
||||
impl Deref for KeymapRule {
|
||||
type Target = Vec<Chord>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.keymap }
|
||||
}
|
||||
|
||||
impl KeymapRule {
|
||||
pub(crate) fn reshape(self, layer: Layer) -> Result<Self> {
|
||||
#[inline]
|
||||
fn on(Chord { on, .. }: &Chord) -> [Key; 2] {
|
||||
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
|
||||
}
|
||||
|
||||
let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect();
|
||||
let b_seen: HashSet<_> = self.keymap.iter().map(on).collect();
|
||||
|
||||
let keymap = Preset::mix(
|
||||
self.prepend_keymap,
|
||||
self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
|
||||
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
|
||||
)
|
||||
.filter(|chord| !chord.noop())
|
||||
.map(|chord| chord.with_layer(layer))
|
||||
.collect();
|
||||
|
||||
Ok(Self { keymap, ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,16 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
yazi_macro::mod_pub!(keymap mgr open plugin popup preview tasks theme which);
|
||||
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which);
|
||||
|
||||
yazi_macro::mod_flat!(layout pattern preset priority);
|
||||
yazi_macro::mod_flat!(layout pattern preset priority yazi);
|
||||
|
||||
use std::{io::{Read, Write}, str::FromStr};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use yazi_shared::{RoCell, SyncCell, tty::TTY};
|
||||
|
||||
pub static YAZI: RoCell<yazi::Yazi> = RoCell::new();
|
||||
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
|
||||
pub static MGR: RoCell<mgr::Mgr> = RoCell::new();
|
||||
pub static OPEN: RoCell<open::Open> = RoCell::new();
|
||||
pub static PLUGIN: RoCell<plugin::Plugin> = RoCell::new();
|
||||
pub static PREVIEW: RoCell<preview::Preview> = RoCell::new();
|
||||
pub static TASKS: RoCell<tasks::Tasks> = RoCell::new();
|
||||
pub static THEME: RoCell<theme::Theme> = RoCell::new();
|
||||
pub static INPUT: RoCell<popup::Input> = RoCell::new();
|
||||
pub static CONFIRM: RoCell<popup::Confirm> = RoCell::new();
|
||||
pub static PICK: RoCell<popup::Pick> = RoCell::new();
|
||||
pub static WHICH: RoCell<which::Which> = RoCell::new();
|
||||
|
||||
pub static LAYOUT: SyncCell<Layout> = SyncCell::new(Layout::default());
|
||||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
|
|
@ -30,53 +21,47 @@ pub fn init() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_flavor(light: bool) -> anyhow::Result<()> {
|
||||
let mut flavor_toml = Preset::flavor(light, true);
|
||||
if let Err(e) = flavor_toml {
|
||||
wait_for_key(e)?;
|
||||
flavor_toml = Preset::flavor(light, false);
|
||||
fn try_init(merge: bool) -> anyhow::Result<()> {
|
||||
let mut yazi = Preset::yazi()?;
|
||||
let mut keymap = Preset::keymap()?;
|
||||
|
||||
if merge {
|
||||
let dir = yazi_fs::Xdg::config_dir();
|
||||
yazi = yazi.deserialize_over(toml::Deserializer::new(
|
||||
&std::fs::read_to_string(dir.join("yazi.toml")).unwrap_or_default(),
|
||||
))?;
|
||||
keymap = keymap.deserialize_over(toml::Deserializer::new(
|
||||
&std::fs::read_to_string(dir.join("keymap.toml")).unwrap_or_default(),
|
||||
))?;
|
||||
}
|
||||
|
||||
let mut theme: theme::Theme = <_>::from_str(&flavor_toml.unwrap())?;
|
||||
theme.mgr.syntect_theme = theme
|
||||
.flavor
|
||||
.syntect_path(light)
|
||||
.unwrap_or_else(|| yazi_fs::expand_path(&theme.mgr.syntect_theme));
|
||||
|
||||
THEME.init(theme);
|
||||
YAZI.init(yazi.reshape()?);
|
||||
KEYMAP.init(keymap.reshape()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_init(merge: bool) -> anyhow::Result<()> {
|
||||
let (yazi_toml, keymap_toml) = if merge {
|
||||
let p = yazi_fs::Xdg::config_dir();
|
||||
(Preset::yazi(&p)?, Preset::keymap(&p)?)
|
||||
} else {
|
||||
(yazi_macro::config_preset!("yazi"), yazi_macro::config_preset!("keymap"))
|
||||
};
|
||||
pub fn init_flavor(light: bool) -> anyhow::Result<()> {
|
||||
if let Err(e) = try_init_flavor(light, true) {
|
||||
wait_for_key(e)?;
|
||||
try_init_flavor(light, false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let keymap = <_>::from_str(&keymap_toml)?;
|
||||
let mgr = <_>::from_str(&yazi_toml)?;
|
||||
let open = <_>::from_str(&yazi_toml)?;
|
||||
let plugin = <_>::from_str(&yazi_toml)?;
|
||||
let preview = <_>::from_str(&yazi_toml)?;
|
||||
let tasks = <_>::from_str(&yazi_toml)?;
|
||||
let input = <_>::from_str(&yazi_toml)?;
|
||||
let confirm = <_>::from_str(&yazi_toml)?;
|
||||
let pick = <_>::from_str(&yazi_toml)?;
|
||||
let which = <_>::from_str(&yazi_toml)?;
|
||||
fn try_init_flavor(light: bool, merge: bool) -> anyhow::Result<()> {
|
||||
let mut theme = Preset::theme(light)?;
|
||||
|
||||
KEYMAP.init(keymap);
|
||||
MGR.init(mgr);
|
||||
OPEN.init(open);
|
||||
PLUGIN.init(plugin);
|
||||
PREVIEW.init(preview);
|
||||
TASKS.init(tasks);
|
||||
INPUT.init(input);
|
||||
CONFIRM.init(confirm);
|
||||
PICK.init(pick);
|
||||
WHICH.init(which);
|
||||
if merge {
|
||||
let shadow = theme::Theme::deserialize_shadow(toml::Deserializer::new(
|
||||
&std::fs::read_to_string(yazi_fs::Xdg::config_dir().join("theme.toml")).unwrap_or_default(),
|
||||
))?;
|
||||
|
||||
let flavor = shadow.flavor.as_ref().map(theme::Flavor::from).unwrap_or_default().read(light)?;
|
||||
theme = theme.deserialize_over_with::<toml::Value>(shadow)?;
|
||||
theme = theme.deserialize_over(toml::Deserializer::new(&flavor))?;
|
||||
}
|
||||
|
||||
THEME.init(theme.reshape(light)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::SortBy;
|
||||
|
||||
use super::{MgrRatio, MouseEvents};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||
#[derive(Debug, Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Mgr {
|
||||
pub ratio: MgrRatio,
|
||||
|
||||
|
|
@ -19,7 +17,6 @@ pub struct Mgr {
|
|||
pub sort_translit: bool,
|
||||
|
||||
// Display
|
||||
#[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))]
|
||||
pub linemode: String,
|
||||
pub show_hidden: bool,
|
||||
pub show_symlink: bool,
|
||||
|
|
@ -28,20 +25,12 @@ pub struct Mgr {
|
|||
pub title_format: String,
|
||||
}
|
||||
|
||||
impl FromStr for Mgr {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
#[serde(rename = "manager")]
|
||||
mgr: Mgr, // TODO: remove serde(rename)
|
||||
impl Mgr {
|
||||
pub(crate) fn reshape(self) -> Result<Self> {
|
||||
if self.linemode.is_empty() || self.linemode.len() > 20 {
|
||||
bail!("[mgr].linemode must be between 1 and 20 characters.");
|
||||
}
|
||||
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [manager] section in your yazi.toml")?;
|
||||
outer.mgr.validate()?;
|
||||
|
||||
Ok(outer.mgr)
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(open opener rule);
|
||||
yazi_macro::mod_flat!(open rule);
|
||||
|
|
|
|||
|
|
@ -1,106 +1,74 @@
|
|||
use std::{collections::HashMap, path::Path, str::FromStr};
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_shared::MIME_DIR;
|
||||
|
||||
use super::Opener;
|
||||
use crate::{Preset, open::OpenRule};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||
pub struct Open {
|
||||
rules: Vec<OpenRule>,
|
||||
openers: HashMap<String, IndexSet<Opener>>,
|
||||
rules: Vec<OpenRule>,
|
||||
#[serde(default)]
|
||||
prepend_rules: Vec<OpenRule>,
|
||||
#[serde(default)]
|
||||
append_rules: Vec<OpenRule>,
|
||||
}
|
||||
|
||||
impl Deref for Open {
|
||||
type Target = Vec<OpenRule>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.rules }
|
||||
}
|
||||
|
||||
impl Open {
|
||||
pub fn openers<P, M>(&self, path: P, mime: M) -> Option<IndexSet<&Opener>>
|
||||
pub fn all<'a, 'b, P, M>(&'a self, path: P, mime: M) -> impl Iterator<Item = &'a str> + 'b
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
M: AsRef<str>,
|
||||
'a: 'b,
|
||||
P: AsRef<Path> + 'b,
|
||||
M: AsRef<str> + 'b,
|
||||
{
|
||||
let is_dir = mime.as_ref() == MIME_DIR;
|
||||
self.rules.iter().find_map(|rule| {
|
||||
if rule.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
|
||||
|| rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_dir))
|
||||
{
|
||||
let openers = rule
|
||||
.use_
|
||||
.iter()
|
||||
.filter_map(|use_| self.openers.get(use_))
|
||||
.flatten()
|
||||
.collect::<IndexSet<_>>();
|
||||
|
||||
if openers.is_empty() { None } else { Some(openers) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
self
|
||||
.rules
|
||||
.iter()
|
||||
.filter(move |&r| {
|
||||
r.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
|
||||
|| r.name.as_ref().is_some_and(|p| p.match_path(&path, is_dir))
|
||||
})
|
||||
.flat_map(|r| &r.use_)
|
||||
.map(String::as_str)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn block_opener<P, M>(&self, path: P, mime: M) -> Option<&Opener>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
self.openers(path, mime).and_then(|o| o.into_iter().find(|o| o.block))
|
||||
}
|
||||
|
||||
pub fn common_openers(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> Vec<&Opener> {
|
||||
let grouped: Vec<_> = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect();
|
||||
let flat: IndexSet<_> = grouped.iter().flatten().copied().collect();
|
||||
flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Open {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse the [open] or [opener] section in your yazi.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Open {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
opener: HashMap<String, Vec<Opener>>,
|
||||
open: OuterOpen,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct OuterOpen {
|
||||
rules: Vec<OpenRule>,
|
||||
#[serde(default)]
|
||||
prepend_rules: Vec<OpenRule>,
|
||||
#[serde(default)]
|
||||
append_rules: Vec<OpenRule>,
|
||||
}
|
||||
|
||||
let mut outer = Outer::deserialize(deserializer)?;
|
||||
|
||||
if outer.open.append_rules.iter().any(|r| r.any_file()) {
|
||||
outer.open.rules.retain(|r| !r.any_file());
|
||||
}
|
||||
if outer.open.append_rules.iter().any(|r| r.any_dir()) {
|
||||
outer.open.rules.retain(|r| !r.any_dir());
|
||||
}
|
||||
|
||||
let openers = outer
|
||||
.opener
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into_iter().filter_map(|o| o.take()).collect::<IndexSet<_>>()))
|
||||
pub fn common<'a>(
|
||||
&'a self,
|
||||
targets: &[(impl AsRef<Path>, impl AsRef<str>)],
|
||||
) -> IndexSet<&'a str> {
|
||||
let each: Vec<IndexSet<&str>> = targets
|
||||
.iter()
|
||||
.map(|(p, m)| self.all(p, m).collect::<IndexSet<_>>())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
|
||||
let mut flat: IndexSet<_> = each.iter().flatten().copied().collect();
|
||||
flat.retain(|use_| each.iter().all(|e| e.contains(use_)));
|
||||
flat
|
||||
}
|
||||
}
|
||||
|
||||
impl Open {
|
||||
pub(crate) fn reshape(self) -> Result<Self> {
|
||||
let any_file = self.append_rules.iter().any(|r| r.any_file());
|
||||
let any_dir = self.append_rules.iter().any(|r| r.any_dir());
|
||||
|
||||
let it =
|
||||
self.rules.into_iter().filter(|r| !(any_file && r.any_file() || any_dir && r.any_dir()));
|
||||
|
||||
Ok(Self {
|
||||
#[rustfmt::skip]
|
||||
rules: Preset::mix(outer.open.prepend_rules, outer.open.rules, outer.open.append_rules).collect(),
|
||||
openers,
|
||||
rules: Preset::mix(self.prepend_rules, it, self.append_rules).collect(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Opener {
|
||||
pub run: String,
|
||||
pub block: bool,
|
||||
pub orphan: bool,
|
||||
pub desc: String,
|
||||
pub for_: Option<String>,
|
||||
pub spread: bool,
|
||||
}
|
||||
|
||||
impl Opener {
|
||||
pub fn take(mut self) -> Option<Self> {
|
||||
if let Some(for_) = self.for_.take() {
|
||||
match for_.as_str() {
|
||||
"unix" if cfg!(unix) => {}
|
||||
os if os == std::env::consts::OS => {}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Opener {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
run: String,
|
||||
#[serde(default)]
|
||||
block: bool,
|
||||
#[serde(default)]
|
||||
orphan: bool,
|
||||
desc: Option<String>,
|
||||
#[serde(rename = "for")]
|
||||
for_: Option<String>,
|
||||
}
|
||||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
|
||||
let run = shadow.run;
|
||||
if run.is_empty() {
|
||||
return Err(serde::de::Error::custom("`run` cannot be empty"));
|
||||
}
|
||||
|
||||
let desc = shadow.desc.unwrap_or_else(|| run.split_whitespace().next().unwrap().to_string());
|
||||
|
||||
let spread = run.contains("$@") || run.contains("%*") || run.contains("$*");
|
||||
Ok(Self { run, block: shadow.block, orphan: shadow.orphan, desc, for_: shadow.for_, spread })
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,12 @@ use serde::{Deserialize, Deserializer, de::{self, Visitor}};
|
|||
use crate::pattern::Pattern;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct OpenRule {
|
||||
pub(super) name: Option<Pattern>,
|
||||
pub(super) mime: Option<Pattern>,
|
||||
pub struct OpenRule {
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
#[serde(rename = "use")]
|
||||
#[serde(deserialize_with = "OpenRule::deserialize")]
|
||||
pub(super) use_: Vec<String>,
|
||||
pub use_: Vec<String>,
|
||||
}
|
||||
|
||||
impl OpenRule {
|
||||
|
|
@ -39,7 +39,7 @@ impl OpenRule {
|
|||
where
|
||||
A: de::SeqAccess<'de>,
|
||||
{
|
||||
let mut uses = vec![];
|
||||
let mut uses = Vec::with_capacity(seq.size_hint().unwrap_or(0));
|
||||
while let Some(use_) = seq.next_element::<String>()? {
|
||||
uses.push(use_);
|
||||
}
|
||||
|
|
|
|||
1
yazi-config/src/opener/mod.rs
Normal file
1
yazi-config/src/opener/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(opener rule);
|
||||
71
yazi-config/src/opener/opener.rs
Normal file
71
yazi-config/src/opener/opener.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use std::{collections::HashMap, mem, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexSet;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::OpenerRule;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Opener(HashMap<String, Vec<OpenerRule>>);
|
||||
|
||||
impl Deref for Opener {
|
||||
type Target = HashMap<String, Vec<OpenerRule>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl Opener {
|
||||
pub fn all<'a, I>(&'a self, uses: I) -> Vec<&'a OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
uses.flat_map(|use_| self.get(use_)).flatten().collect()
|
||||
}
|
||||
|
||||
pub fn first<'a, 'b, I>(&'a self, uses: I) -> Option<&'a OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'b str>,
|
||||
{
|
||||
uses.flat_map(|use_| self.get(use_)).flatten().next()
|
||||
}
|
||||
|
||||
pub fn block<'a, I>(&'a self, uses: I) -> Option<&'a OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
uses.flat_map(|use_| self.get(use_)).flatten().find(|&o| o.block)
|
||||
}
|
||||
}
|
||||
|
||||
impl Opener {
|
||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
||||
for rules in self.0.values_mut() {
|
||||
*rules = mem::take(rules)
|
||||
.into_iter()
|
||||
.map(|mut r| (r.for_.take(), r))
|
||||
.filter(|(for_, _)| match for_.as_ref().map(|s| s.as_str()) {
|
||||
Some("unix") if cfg!(unix) => true,
|
||||
Some(os) if os == std::env::consts::OS => true,
|
||||
Some(_) => false,
|
||||
None => true,
|
||||
})
|
||||
.map(|(_, r)| r.reshape())
|
||||
.collect::<Result<IndexSet<_>>>()?
|
||||
.into_iter()
|
||||
.collect();
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_over<'de, D>(mut self, deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let map: HashMap<String, Vec<OpenerRule>> = HashMap::deserialize(deserializer)?;
|
||||
self.0.extend(map);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
49
yazi-config/src/opener/rule.rs
Normal file
49
yazi-config/src/opener/rule.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use anyhow::{Result, bail};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct OpenerRule {
|
||||
pub run: String,
|
||||
#[serde(default)]
|
||||
pub block: bool,
|
||||
#[serde(default)]
|
||||
pub orphan: bool,
|
||||
#[serde(default)]
|
||||
pub desc: String,
|
||||
#[serde(rename = "for")]
|
||||
pub for_: Option<String>,
|
||||
#[serde(skip)]
|
||||
pub spread: bool,
|
||||
}
|
||||
|
||||
impl OpenerRule {
|
||||
#[inline]
|
||||
pub fn desc(&self) -> String {
|
||||
if !self.desc.is_empty() {
|
||||
self.desc.clone()
|
||||
} else if let Some(first) = self.run.split_whitespace().next() {
|
||||
first.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenerRule {
|
||||
pub(super) fn reshape(mut self) -> Result<Self> {
|
||||
if self.run.is_empty() {
|
||||
bail!("[open].rules.*.run cannot be empty.");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
self.spread = self.run.contains("$@") || self.run.contains("$*");
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.spread = self.run.contains("%*");
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,39 @@
|
|||
use std::{collections::HashSet, path::Path, str::FromStr};
|
||||
use std::{collections::HashSet, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use tracing::warn;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::File;
|
||||
|
||||
use super::{Fetcher, Preloader, Previewer, Spotter};
|
||||
use crate::{Preset, plugin::MAX_PREWORKERS};
|
||||
|
||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||
pub struct Plugin {
|
||||
pub fetchers: Vec<Fetcher>,
|
||||
pub spotters: Vec<Spotter>,
|
||||
pub preloaders: Vec<Preloader>,
|
||||
pub previewers: Vec<Previewer>,
|
||||
pub fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
prepend_fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
append_fetchers: Vec<Fetcher>,
|
||||
|
||||
pub spotters: Vec<Spotter>,
|
||||
#[serde(default)]
|
||||
prepend_spotters: Vec<Spotter>,
|
||||
#[serde(default)]
|
||||
append_spotters: Vec<Spotter>,
|
||||
|
||||
pub preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
prepend_preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
append_preloaders: Vec<Preloader>,
|
||||
|
||||
pub previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
prepend_previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
append_previewers: Vec<Previewer>,
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
|
|
@ -71,90 +92,48 @@ impl Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Plugin {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse the [plugin] section in your yazi.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Plugin {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
plugin: Shadow,
|
||||
impl Plugin {
|
||||
// TODO: remove .retain() and .collect()
|
||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
||||
if self.append_spotters.iter().any(|r| r.any_file()) {
|
||||
self.spotters.retain(|r| !r.any_file());
|
||||
}
|
||||
if self.append_spotters.iter().any(|r| r.any_dir()) {
|
||||
self.spotters.retain(|r| !r.any_dir());
|
||||
}
|
||||
if self.append_previewers.iter().any(|r| r.any_file()) {
|
||||
self.previewers.retain(|r| !r.any_file());
|
||||
}
|
||||
if self.append_previewers.iter().any(|r| r.any_dir()) {
|
||||
self.previewers.retain(|r| !r.any_dir());
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
prepend_fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
append_fetchers: Vec<Fetcher>,
|
||||
self.fetchers =
|
||||
Preset::mix(self.prepend_fetchers, self.fetchers, self.append_fetchers).collect();
|
||||
self.spotters =
|
||||
Preset::mix(self.prepend_spotters, self.spotters, self.append_spotters).collect();
|
||||
self.preloaders =
|
||||
Preset::mix(self.prepend_preloaders, self.preloaders, self.append_preloaders).collect();
|
||||
self.previewers =
|
||||
Preset::mix(self.prepend_previewers, self.previewers, self.append_previewers).collect();
|
||||
|
||||
spotters: Vec<Spotter>,
|
||||
#[serde(default)]
|
||||
prepend_spotters: Vec<Spotter>,
|
||||
#[serde(default)]
|
||||
append_spotters: Vec<Spotter>,
|
||||
|
||||
preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
prepend_preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
append_preloaders: Vec<Preloader>,
|
||||
|
||||
previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
prepend_previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
append_previewers: Vec<Previewer>,
|
||||
}
|
||||
|
||||
let mut shadow = Outer::deserialize(deserializer)?.plugin;
|
||||
if shadow.append_spotters.iter().any(|r| r.any_file()) {
|
||||
shadow.spotters.retain(|r| !r.any_file());
|
||||
}
|
||||
if shadow.append_spotters.iter().any(|r| r.any_dir()) {
|
||||
shadow.spotters.retain(|r| !r.any_dir());
|
||||
}
|
||||
if shadow.append_previewers.iter().any(|r| r.any_file()) {
|
||||
shadow.previewers.retain(|r| !r.any_file());
|
||||
}
|
||||
if shadow.append_previewers.iter().any(|r| r.any_dir()) {
|
||||
shadow.previewers.retain(|r| !r.any_dir());
|
||||
}
|
||||
|
||||
shadow.fetchers =
|
||||
Preset::mix(shadow.prepend_fetchers, shadow.fetchers, shadow.append_fetchers).collect();
|
||||
shadow.spotters =
|
||||
Preset::mix(shadow.prepend_spotters, shadow.spotters, shadow.append_spotters).collect();
|
||||
shadow.preloaders =
|
||||
Preset::mix(shadow.prepend_preloaders, shadow.preloaders, shadow.append_preloaders).collect();
|
||||
shadow.previewers =
|
||||
Preset::mix(shadow.prepend_previewers, shadow.previewers, shadow.append_previewers).collect();
|
||||
|
||||
if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize {
|
||||
if self.fetchers.len() + self.preloaders.len() > MAX_PREWORKERS as usize {
|
||||
panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}");
|
||||
}
|
||||
|
||||
for (i, p) in shadow.fetchers.iter_mut().enumerate() {
|
||||
for (i, p) in self.fetchers.iter_mut().enumerate() {
|
||||
p.idx = i as u8;
|
||||
}
|
||||
for (i, p) in shadow.preloaders.iter_mut().enumerate() {
|
||||
p.idx = shadow.fetchers.len() as u8 + i as u8;
|
||||
for (i, p) in self.preloaders.iter_mut().enumerate() {
|
||||
p.idx = self.fetchers.len() as u8 + i as u8;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
fetchers: shadow.fetchers,
|
||||
spotters: shadow.spotters,
|
||||
preloaders: shadow.preloaders,
|
||||
previewers: shadow.previewers,
|
||||
fetchers: self.fetchers,
|
||||
spotters: self.spotters,
|
||||
preloaders: self.preloaders,
|
||||
previewers: self.previewers,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, DeserializeOver2)]
|
||||
pub struct Confirm {
|
||||
// trash
|
||||
pub trash_title: String,
|
||||
|
|
@ -30,22 +28,6 @@ pub struct Confirm {
|
|||
pub quit_offset: Offset,
|
||||
}
|
||||
|
||||
impl FromStr for Confirm {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
confirm: Confirm,
|
||||
}
|
||||
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [confirm] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.confirm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Confirm {
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, DeserializeOver2)]
|
||||
pub struct Input {
|
||||
pub cursor_blink: bool,
|
||||
|
||||
|
|
@ -48,19 +46,3 @@ pub struct Input {
|
|||
impl Input {
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
||||
impl FromStr for Input {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
input: Input,
|
||||
}
|
||||
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [input] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.input)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
|
|||
use yazi_shared::url::Url;
|
||||
|
||||
use super::{Offset, Origin, Position};
|
||||
use crate::{CONFIRM, INPUT, PICK};
|
||||
use crate::YAZI;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InputCfg {
|
||||
|
|
@ -33,8 +33,8 @@ pub struct ConfirmCfg {
|
|||
impl InputCfg {
|
||||
pub fn cd() -> Self {
|
||||
Self {
|
||||
title: INPUT.cd_title.to_owned(),
|
||||
position: Position::new(INPUT.cd_origin, INPUT.cd_offset),
|
||||
title: YAZI.input.cd_title.to_owned(),
|
||||
position: Position::new(YAZI.input.cd_origin, YAZI.input.cd_offset),
|
||||
completion: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -42,24 +42,24 @@ impl InputCfg {
|
|||
|
||||
pub fn create(dir: bool) -> Self {
|
||||
Self {
|
||||
title: INPUT.create_title[dir as usize].to_owned(),
|
||||
position: Position::new(INPUT.create_origin, INPUT.create_offset),
|
||||
title: YAZI.input.create_title[dir as usize].to_owned(),
|
||||
position: Position::new(YAZI.input.create_origin, YAZI.input.create_offset),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename() -> Self {
|
||||
Self {
|
||||
title: INPUT.rename_title.to_owned(),
|
||||
position: Position::new(INPUT.rename_origin, INPUT.rename_offset),
|
||||
title: YAZI.input.rename_title.to_owned(),
|
||||
position: Position::new(YAZI.input.rename_origin, YAZI.input.rename_offset),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter() -> Self {
|
||||
Self {
|
||||
title: INPUT.filter_title.to_owned(),
|
||||
position: Position::new(INPUT.filter_origin, INPUT.filter_offset),
|
||||
title: YAZI.input.filter_title.to_owned(),
|
||||
position: Position::new(YAZI.input.filter_origin, YAZI.input.filter_offset),
|
||||
realtime: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -67,8 +67,8 @@ impl InputCfg {
|
|||
|
||||
pub fn find(prev: bool) -> Self {
|
||||
Self {
|
||||
title: INPUT.find_title[prev as usize].to_owned(),
|
||||
position: Position::new(INPUT.find_origin, INPUT.find_offset),
|
||||
title: YAZI.input.find_title[prev as usize].to_owned(),
|
||||
position: Position::new(YAZI.input.find_origin, YAZI.input.find_offset),
|
||||
realtime: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -76,16 +76,16 @@ impl InputCfg {
|
|||
|
||||
pub fn search(name: &str) -> Self {
|
||||
Self {
|
||||
title: INPUT.search_title.replace("{n}", name),
|
||||
position: Position::new(INPUT.search_origin, INPUT.search_offset),
|
||||
title: YAZI.input.search_title.replace("{n}", name),
|
||||
position: Position::new(YAZI.input.search_origin, YAZI.input.search_offset),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shell(block: bool) -> Self {
|
||||
Self {
|
||||
title: INPUT.shell_title[block as usize].to_owned(),
|
||||
position: Position::new(INPUT.shell_origin, INPUT.shell_offset),
|
||||
title: YAZI.input.shell_title[block as usize].to_owned(),
|
||||
position: Position::new(YAZI.input.shell_origin, YAZI.input.shell_offset),
|
||||
highlight: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -121,8 +121,8 @@ impl ConfirmCfg {
|
|||
|
||||
pub fn trash(urls: &[yazi_shared::url::Url]) -> Self {
|
||||
Self::new(
|
||||
Self::replace_number(&CONFIRM.trash_title, urls.len()),
|
||||
(CONFIRM.trash_origin, CONFIRM.trash_offset),
|
||||
Self::replace_number(&YAZI.confirm.trash_title, urls.len()),
|
||||
(YAZI.confirm.trash_origin, YAZI.confirm.trash_offset),
|
||||
None,
|
||||
Self::truncate_list(urls.iter(), urls.len(), 100),
|
||||
)
|
||||
|
|
@ -130,8 +130,8 @@ impl ConfirmCfg {
|
|||
|
||||
pub fn delete(urls: &[yazi_shared::url::Url]) -> Self {
|
||||
Self::new(
|
||||
Self::replace_number(&CONFIRM.delete_title, urls.len()),
|
||||
(CONFIRM.delete_origin, CONFIRM.delete_offset),
|
||||
Self::replace_number(&YAZI.confirm.delete_title, urls.len()),
|
||||
(YAZI.confirm.delete_origin, YAZI.confirm.delete_offset),
|
||||
None,
|
||||
Self::truncate_list(urls.iter(), urls.len(), 100),
|
||||
)
|
||||
|
|
@ -139,18 +139,18 @@ impl ConfirmCfg {
|
|||
|
||||
pub fn overwrite(url: &Url) -> Self {
|
||||
Self::new(
|
||||
CONFIRM.overwrite_title.to_owned(),
|
||||
(CONFIRM.overwrite_origin, CONFIRM.overwrite_offset),
|
||||
Some(Text::raw(&CONFIRM.overwrite_content)),
|
||||
YAZI.confirm.overwrite_title.to_owned(),
|
||||
(YAZI.confirm.overwrite_origin, YAZI.confirm.overwrite_offset),
|
||||
Some(Text::raw(&YAZI.confirm.overwrite_content)),
|
||||
Some(url.to_string().into()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn quit(len: usize, names: Vec<String>) -> Self {
|
||||
Self::new(
|
||||
Self::replace_number(&CONFIRM.quit_title, len),
|
||||
(CONFIRM.quit_origin, CONFIRM.quit_offset),
|
||||
Some(Text::raw(&CONFIRM.quit_content)),
|
||||
Self::replace_number(&YAZI.confirm.quit_title, len),
|
||||
(YAZI.confirm.quit_origin, YAZI.confirm.quit_offset),
|
||||
Some(Text::raw(&YAZI.confirm.quit_content)),
|
||||
Self::truncate_list(names.into_iter(), len, 10),
|
||||
)
|
||||
}
|
||||
|
|
@ -179,15 +179,18 @@ impl ConfirmCfg {
|
|||
impl PickCfg {
|
||||
#[inline]
|
||||
fn max_height(len: usize) -> u16 {
|
||||
PICK.open_offset.height.min(PICK.border().saturating_add(len as u16))
|
||||
YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16))
|
||||
}
|
||||
|
||||
pub fn open(items: Vec<String>) -> Self {
|
||||
let max_height = Self::max_height(items.len());
|
||||
Self {
|
||||
title: PICK.open_title.to_owned(),
|
||||
title: YAZI.pick.open_title.to_owned(),
|
||||
items,
|
||||
position: Position::new(PICK.open_origin, Offset { height: max_height, ..PICK.open_offset }),
|
||||
position: Position::new(YAZI.pick.open_origin, Offset {
|
||||
height: max_height,
|
||||
..YAZI.pick.open_offset
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, DeserializeOver2)]
|
||||
pub struct Pick {
|
||||
// open
|
||||
pub open_title: String,
|
||||
|
|
@ -16,19 +14,3 @@ pub struct Pick {
|
|||
impl Pick {
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
||||
impl FromStr for Pick {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
pick: Pick,
|
||||
}
|
||||
|
||||
let outer =
|
||||
toml::from_str::<Outer>(s).context("Failed to parse the [pick] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.pick)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,25 @@
|
|||
use std::{borrow::Cow, path::{Path, PathBuf}, str::FromStr};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use toml::{Table, Value};
|
||||
use yazi_fs::Xdg;
|
||||
|
||||
use crate::theme::Flavor;
|
||||
use crate::{Yazi, keymap::Keymap, theme::Theme};
|
||||
|
||||
pub(crate) struct Preset;
|
||||
|
||||
impl Preset {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("yazi.toml"), yazi_macro::config_preset!("yazi"))
|
||||
#[inline]
|
||||
pub(super) fn yazi() -> Result<Yazi, toml::de::Error> {
|
||||
toml::from_str(&yazi_macro::config_preset!("yazi"))
|
||||
}
|
||||
|
||||
pub(crate) fn keymap(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("keymap.toml"), yazi_macro::config_preset!("keymap"))
|
||||
#[inline]
|
||||
pub(super) fn keymap() -> Result<Keymap, toml::de::Error> {
|
||||
toml::from_str(&yazi_macro::config_preset!("keymap"))
|
||||
}
|
||||
|
||||
pub(crate) fn flavor(light: bool, merge: bool) -> Result<Cow<'static, str>> {
|
||||
let theme = if merge {
|
||||
std::fs::read_to_string(Xdg::config_dir().join("theme.toml")).unwrap_or_default()
|
||||
#[inline]
|
||||
pub(super) fn theme(light: bool) -> Result<Theme, toml::de::Error> {
|
||||
toml::from_str(&if light {
|
||||
yazi_macro::theme_preset!("light")
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let flavor = Flavor::from_str(&theme)?;
|
||||
|
||||
let preset =
|
||||
if light { yazi_macro::theme_preset!("light") } else { yazi_macro::theme_preset!("dark") };
|
||||
|
||||
Self::merge_str(&theme, &Self::merge_str(&flavor.read(light)?, &preset)?)
|
||||
yazi_macro::theme_preset!("dark")
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -41,43 +31,4 @@ impl Preset {
|
|||
{
|
||||
a.into_iter().chain(b).chain(c)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn merge_str(user: &str, base: &str) -> Result<Cow<'static, str>> {
|
||||
let mut t = user.parse()?;
|
||||
Self::merge(&mut t, base.parse()?, 2);
|
||||
|
||||
Ok(t.to_string().into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn merge_path(user: PathBuf, base: Cow<str>) -> Result<Cow<str>> {
|
||||
let s = std::fs::read_to_string(&user).unwrap_or_default();
|
||||
if s.is_empty() {
|
||||
return Ok(base);
|
||||
}
|
||||
|
||||
Self::merge_str(&s, &base).with_context(|| format!("failed to parse config: {user:?}"))
|
||||
}
|
||||
|
||||
fn merge(a: &mut Table, b: Table, max: u8) {
|
||||
for (k, v) in b {
|
||||
let Some(a) = a.get_mut(&k) else {
|
||||
a.insert(k, v);
|
||||
continue;
|
||||
};
|
||||
|
||||
if max <= 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(a) = a.as_table_mut() {
|
||||
if let Value::Table(b) = v {
|
||||
Self::merge(a, b, max - 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*a = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::{borrow::Cow, path::PathBuf, str::FromStr};
|
||||
use std::{borrow::Cow, path::PathBuf};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use validator::Validate;
|
||||
use anyhow::{Context, Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::{Xdg, expand_path};
|
||||
use yazi_shared::timestamp_us;
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ use super::PreviewWrap;
|
|||
#[rustfmt::skip]
|
||||
const TABS: &[&str] = &["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "];
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Preview {
|
||||
pub wrap: PreviewWrap,
|
||||
pub tab_size: u8,
|
||||
|
|
@ -44,70 +44,24 @@ impl Preview {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Preview {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let preview: Self =
|
||||
toml::from_str(s).context("Failed to parse the [preview] section in your yazi.toml")?;
|
||||
|
||||
std::fs::create_dir_all(&preview.cache_dir).context("Failed to create cache directory")?;
|
||||
|
||||
Ok(preview)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Preview {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
preview: Shadow,
|
||||
}
|
||||
#[derive(Deserialize, Validate)]
|
||||
struct Shadow {
|
||||
wrap: PreviewWrap,
|
||||
tab_size: u8,
|
||||
max_width: u32,
|
||||
max_height: u32,
|
||||
|
||||
cache_dir: Option<String>,
|
||||
|
||||
#[validate(range(min = 0, max = 100))]
|
||||
image_delay: u8,
|
||||
image_filter: String,
|
||||
#[validate(range(min = 50, max = 90))]
|
||||
image_quality: u8,
|
||||
#[validate(range(min = 10, max = 20))]
|
||||
sixel_fraction: u8,
|
||||
|
||||
ueberzug_scale: f32,
|
||||
ueberzug_offset: (f32, f32, f32, f32),
|
||||
impl Preview {
|
||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
||||
if self.image_delay > 100 {
|
||||
bail!("[preview].image_delay must be between 0 and 100.");
|
||||
} else if self.image_quality < 50 || self.image_quality > 90 {
|
||||
bail!("[preview].image_quality must be between 50 and 90.");
|
||||
} else if self.sixel_fraction < 10 || self.sixel_fraction > 20 {
|
||||
bail!("[preview].sixel_fraction must be between 10 and 20.");
|
||||
}
|
||||
|
||||
let preview = Outer::deserialize(deserializer)?.preview;
|
||||
preview.validate().map_err(serde::de::Error::custom)?;
|
||||
self.cache_dir = if self.cache_dir.as_os_str().is_empty() {
|
||||
Xdg::cache_dir()
|
||||
} else {
|
||||
expand_path(&self.cache_dir)
|
||||
};
|
||||
|
||||
Ok(Preview {
|
||||
wrap: preview.wrap,
|
||||
tab_size: preview.tab_size,
|
||||
max_width: preview.max_width,
|
||||
max_height: preview.max_height,
|
||||
std::fs::create_dir_all(&self.cache_dir).context("Failed to create cache directory")?;
|
||||
|
||||
cache_dir: preview
|
||||
.cache_dir
|
||||
.filter(|p| !p.is_empty())
|
||||
.map_or_else(Xdg::cache_dir, expand_path),
|
||||
|
||||
image_delay: preview.image_delay,
|
||||
image_filter: preview.image_filter,
|
||||
image_quality: preview.image_quality,
|
||||
sixel_fraction: preview.sixel_fraction,
|
||||
|
||||
ueberzug_scale: preview.ueberzug_scale,
|
||||
ueberzug_offset: preview.ueberzug_offset,
|
||||
})
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{Result, bail};
|
||||
use serde::Deserialize;
|
||||
use validator::Validate;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
|
||||
#[derive(Debug, Deserialize, Validate)]
|
||||
#[derive(Debug, Deserialize, DeserializeOver2)]
|
||||
pub struct Tasks {
|
||||
#[validate(range(min = 1, message = "Cannot be less than 1"))]
|
||||
pub micro_workers: u8,
|
||||
#[validate(range(min = 1, message = "Cannot be less than 1"))]
|
||||
pub macro_workers: u8,
|
||||
#[validate(range(min = 1, message = "Cannot be less than 1"))]
|
||||
pub bizarre_retry: u8,
|
||||
|
||||
pub image_alloc: u32,
|
||||
|
|
@ -19,19 +14,15 @@ pub struct Tasks {
|
|||
pub suppress_preload: bool,
|
||||
}
|
||||
|
||||
impl FromStr for Tasks {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
tasks: Tasks,
|
||||
impl Tasks {
|
||||
pub(crate) fn reshape(self) -> Result<Self> {
|
||||
if self.micro_workers < 1 {
|
||||
bail!("[tasks].micro_workers must be at least 1.");
|
||||
} else if self.macro_workers < 1 {
|
||||
bail!("[tasks].macro_workers must be at least 1.");
|
||||
} else if self.bizarre_retry < 1 {
|
||||
bail!("[tasks].bizarre_retry` must be at least 1.");
|
||||
}
|
||||
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [tasks] section in your yazi.toml")?;
|
||||
outer.tasks.validate()?;
|
||||
|
||||
Ok(outer.tasks)
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,35 @@
|
|||
use serde::{Deserialize, Deserializer};
|
||||
use std::ops::Deref;
|
||||
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::theme::{Color, Style, StyleShadow};
|
||||
use yazi_shared::theme::Style;
|
||||
|
||||
use super::Is;
|
||||
use crate::Pattern;
|
||||
|
||||
#[derive(Deserialize, DeserializeOver2)]
|
||||
pub struct Filetype {
|
||||
pub is: Is,
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
rules: Vec<FiletypeRule>,
|
||||
}
|
||||
|
||||
impl Deref for Filetype {
|
||||
type Target = Vec<FiletypeRule>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.rules }
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FiletypeRule {
|
||||
#[serde(default)]
|
||||
is: Is,
|
||||
name: Option<Pattern>,
|
||||
mime: Option<Pattern>,
|
||||
#[serde(flatten)]
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl Filetype {
|
||||
impl FiletypeRule {
|
||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
||||
if !self.is.check(&file.cha) {
|
||||
return false;
|
||||
|
|
@ -22,69 +39,3 @@ impl Filetype {
|
|||
|| self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Filetype {
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Filetype>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct FiletypeOuter {
|
||||
rules: Vec<FiletypeRule>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct FiletypeRule {
|
||||
#[serde(default)]
|
||||
is: Is,
|
||||
name: Option<Pattern>,
|
||||
mime: Option<Pattern>,
|
||||
|
||||
fg: Option<Color>,
|
||||
bg: Option<Color>,
|
||||
#[serde(default)]
|
||||
bold: bool,
|
||||
#[serde(default)]
|
||||
dim: bool,
|
||||
#[serde(default)]
|
||||
italic: bool,
|
||||
#[serde(default)]
|
||||
underline: bool,
|
||||
#[serde(default)]
|
||||
blink: bool,
|
||||
#[serde(default)]
|
||||
blink_rapid: bool,
|
||||
#[serde(default)]
|
||||
reversed: bool,
|
||||
#[serde(default)]
|
||||
hidden: bool,
|
||||
#[serde(default)]
|
||||
crossed: bool,
|
||||
}
|
||||
|
||||
Ok(
|
||||
FiletypeOuter::deserialize(deserializer)?
|
||||
.rules
|
||||
.into_iter()
|
||||
.map(|r| Filetype {
|
||||
is: r.is,
|
||||
name: r.name,
|
||||
mime: r.mime,
|
||||
style: StyleShadow {
|
||||
fg: r.fg,
|
||||
bg: r.bg,
|
||||
bold: r.bold,
|
||||
dim: r.dim,
|
||||
italic: r.italic,
|
||||
underline: r.underline,
|
||||
blink: r.blink,
|
||||
blink_rapid: r.blink_rapid,
|
||||
reversed: r.reversed,
|
||||
hidden: r.hidden,
|
||||
crossed: r.crossed,
|
||||
}
|
||||
.into(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,39 @@
|
|||
use std::{borrow::Cow, path::PathBuf, str::FromStr};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use toml::Value;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::Xdg;
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
#[derive(Default, Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Flavor {
|
||||
#[serde(default)]
|
||||
pub dark: String,
|
||||
#[serde(default)]
|
||||
pub light: String,
|
||||
}
|
||||
|
||||
impl FromStr for Flavor {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
#[serde(default)]
|
||||
flavor: Flavor,
|
||||
impl From<&Value> for Flavor {
|
||||
fn from(value: &Value) -> Self {
|
||||
let mut me = Self::default();
|
||||
if let Value::Table(t) = value {
|
||||
if let Some(s) = t.get("dark").and_then(|v| v.as_str()) {
|
||||
me.dark = s.to_owned();
|
||||
}
|
||||
if let Some(s) = t.get("light").and_then(|v| v.as_str()) {
|
||||
me.light = s.to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(
|
||||
toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [flavor] section in your theme.toml")?
|
||||
.flavor,
|
||||
)
|
||||
me
|
||||
}
|
||||
}
|
||||
|
||||
impl Flavor {
|
||||
pub(crate) fn read(&self, light: bool) -> Result<Cow<'static, str>> {
|
||||
pub(crate) fn read(&self, light: bool) -> Result<String> {
|
||||
Ok(match if light { self.light.as_str() } else { self.dark.as_str() } {
|
||||
"" => Cow::Borrowed(""),
|
||||
"" => String::new(),
|
||||
name => {
|
||||
let p = Xdg::config_dir().join(format!("flavors/{name}.yazi/flavor.toml"));
|
||||
std::fs::read_to_string(&p).with_context(|| format!("Failed to load flavor {p:?}"))?.into()
|
||||
std::fs::read_to_string(&p).with_context(|| format!("Failed to load flavor {p:?}"))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
209
yazi-config/src/theme/icon.rs
Normal file
209
yazi-config/src/theme/icon.rs
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
use std::{collections::HashMap, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::{Condition, theme::{Color, Icon as I, Style}};
|
||||
|
||||
use crate::Pattern;
|
||||
|
||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||
pub struct Icon {
|
||||
globs: PatIcons,
|
||||
#[serde(default)]
|
||||
prepend_globs: PatIcons,
|
||||
#[serde(default)]
|
||||
append_globs: PatIcons,
|
||||
|
||||
dirs: StrIcons,
|
||||
#[serde(default)]
|
||||
prepend_dirs: StrIcons,
|
||||
#[serde(default)]
|
||||
append_dirs: StrIcons,
|
||||
|
||||
files: StrIcons,
|
||||
#[serde(default)]
|
||||
prepend_files: StrIcons,
|
||||
#[serde(default)]
|
||||
append_files: StrIcons,
|
||||
|
||||
exts: StrIcons,
|
||||
#[serde(default)]
|
||||
prepend_exts: StrIcons,
|
||||
#[serde(default)]
|
||||
append_exts: StrIcons,
|
||||
|
||||
conds: CondIcons,
|
||||
#[serde(default)]
|
||||
prepend_conds: CondIcons,
|
||||
#[serde(default)]
|
||||
append_conds: CondIcons,
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn matches(&self, file: &File) -> Option<&I> {
|
||||
if let Some(i) = self.match_by_glob(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
if let Some(i) = self.match_by_name(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
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(),
|
||||
_ => false,
|
||||
};
|
||||
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_glob(&self, file: &File) -> Option<&I> {
|
||||
self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_name(&self, file: &File) -> Option<&I> {
|
||||
let name = file.name().to_str()?;
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_ext(&self, file: &File) -> Option<&I> {
|
||||
let ext = file.url.extension()?.to_str()?;
|
||||
self.exts.get(ext).or_else(|| self.exts.get(&ext.to_ascii_lowercase()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub(crate) fn reshape(self) -> Result<Self> {
|
||||
Ok(Self {
|
||||
globs: PatIcons(
|
||||
self.prepend_globs.0.into_iter().chain(self.globs.0).chain(self.append_globs.0).collect(),
|
||||
),
|
||||
dirs: StrIcons(
|
||||
self.prepend_dirs.0.into_iter().chain(self.dirs.0).chain(self.append_dirs.0).collect(),
|
||||
),
|
||||
files: StrIcons(
|
||||
self.prepend_files.0.into_iter().chain(self.files.0).chain(self.append_files.0).collect(),
|
||||
),
|
||||
exts: StrIcons(
|
||||
self.prepend_exts.0.into_iter().chain(self.exts.0).chain(self.append_exts.0).collect(),
|
||||
),
|
||||
conds: CondIcons(
|
||||
self.prepend_conds.0.into_iter().chain(self.conds.0).chain(self.append_conds.0).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PatIcons(Vec<(Pattern, I)>);
|
||||
|
||||
impl Deref for PatIcons {
|
||||
type Target = Vec<(Pattern, I)>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PatIcons {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
name: Pattern,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
|
||||
Ok(Self(
|
||||
<Vec<Shadow>>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|s| (s.name, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StrIcons(HashMap<String, I>);
|
||||
|
||||
impl Deref for StrIcons {
|
||||
type Target = HashMap<String, I>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for StrIcons {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
name: String,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
|
||||
Ok(Self(
|
||||
<Vec<Shadow>>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|s| (s.name, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CondIcons(Vec<(Condition, I)>);
|
||||
|
||||
impl Deref for CondIcons {
|
||||
type Target = Vec<(Condition, I)>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CondIcons {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
#[serde(rename = "if")]
|
||||
if_: Condition,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
|
||||
Ok(Self(
|
||||
<Vec<Shadow>>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|s| (s.if_, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::{Condition, theme::{Color, Icon, Style}};
|
||||
|
||||
use crate::{Pattern, Preset};
|
||||
|
||||
pub struct Icons {
|
||||
globs: Vec<(Pattern, Icon)>,
|
||||
dirs: HashMap<String, Icon>,
|
||||
files: HashMap<String, Icon>,
|
||||
exts: HashMap<String, Icon>,
|
||||
conds: Vec<(Condition, Icon)>,
|
||||
}
|
||||
|
||||
impl Icons {
|
||||
pub fn matches(&self, file: &File) -> Option<&Icon> {
|
||||
if let Some(i) = self.match_by_glob(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
if let Some(i) = self.match_by_name(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
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(),
|
||||
_ => false,
|
||||
};
|
||||
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_glob(&self, file: &File) -> Option<&Icon> {
|
||||
self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_name(&self, file: &File) -> Option<&Icon> {
|
||||
let name = file.name().to_str()?;
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_ext(&self, file: &File) -> Option<&Icon> {
|
||||
let ext = file.url.extension()?.to_str()?;
|
||||
self.exts.get(ext).or_else(|| self.exts.get(&ext.to_ascii_lowercase()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Icons {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
globs: Vec<ShadowPat>,
|
||||
#[serde(default)]
|
||||
prepend_globs: Vec<ShadowPat>,
|
||||
#[serde(default)]
|
||||
append_globs: Vec<ShadowPat>,
|
||||
|
||||
dirs: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_dirs: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_dirs: Vec<ShadowStr>,
|
||||
|
||||
files: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_files: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_files: Vec<ShadowStr>,
|
||||
|
||||
exts: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_exts: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_exts: Vec<ShadowStr>,
|
||||
|
||||
conds: Vec<ShadowCond>,
|
||||
#[serde(default)]
|
||||
prepend_conds: Vec<ShadowCond>,
|
||||
#[serde(default)]
|
||||
append_conds: Vec<ShadowCond>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowPat {
|
||||
name: Pattern,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowStr {
|
||||
name: String,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowCond {
|
||||
#[serde(rename = "if")]
|
||||
if_: Condition,
|
||||
text: String,
|
||||
fg: Option<Color>,
|
||||
}
|
||||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
|
||||
let globs = Preset::mix(shadow.prepend_globs, shadow.globs, shadow.append_globs)
|
||||
.map(|v| (v.name, Icon { text: v.text, style: Style { fg: v.fg, ..Default::default() } }))
|
||||
.collect();
|
||||
|
||||
let conds = Preset::mix(shadow.prepend_conds, shadow.conds, shadow.append_conds)
|
||||
.map(|v| (v.if_, Icon { text: v.text, style: Style { fg: v.fg, ..Default::default() } }))
|
||||
.collect();
|
||||
|
||||
fn as_map(it: impl Iterator<Item = ShadowStr>) -> HashMap<String, Icon> {
|
||||
let mut map = HashMap::with_capacity(it.size_hint().0);
|
||||
for v in it {
|
||||
map
|
||||
.entry(v.name)
|
||||
.or_insert(Icon { text: v.text, style: Style { fg: v.fg, ..Default::default() } });
|
||||
}
|
||||
map.shrink_to_fit();
|
||||
map
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
globs,
|
||||
dirs: as_map(Preset::mix(shadow.prepend_dirs, shadow.dirs, shadow.append_dirs)),
|
||||
files: as_map(Preset::mix(shadow.prepend_files, shadow.files, shadow.append_files)),
|
||||
exts: as_map(Preset::mix(shadow.prepend_exts, shadow.exts, shadow.append_exts)),
|
||||
conds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(filetype flavor icons is theme);
|
||||
yazi_macro::mod_flat!(filetype flavor icon is theme);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use std::{path::PathBuf, str::FromStr};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_codegen::{DeserializeOver1, DeserializeOver2};
|
||||
use yazi_fs::expand_path;
|
||||
use yazi_shared::theme::Style;
|
||||
|
||||
use super::{Filetype, Flavor, Icons};
|
||||
use super::{Filetype, Flavor, Icon};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver1, Serialize)]
|
||||
pub struct Theme {
|
||||
pub flavor: Flavor,
|
||||
#[serde(rename = "manager")]
|
||||
|
|
@ -25,25 +26,13 @@ pub struct Theme {
|
|||
pub help: Help,
|
||||
|
||||
// File-specific styles
|
||||
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
|
||||
pub filetypes: Vec<Filetype>,
|
||||
#[serde(rename = "icon", skip_serializing)]
|
||||
pub icons: Icons,
|
||||
#[serde(skip_serializing)]
|
||||
pub filetype: Filetype,
|
||||
#[serde(skip_serializing)]
|
||||
pub icon: Icon,
|
||||
}
|
||||
|
||||
impl FromStr for Theme {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let theme: Self = toml::from_str(s).context("Failed to parse your theme.toml")?;
|
||||
theme.mgr.validate()?;
|
||||
theme.which.validate()?;
|
||||
|
||||
Ok(theme)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Mgr {
|
||||
cwd: Style,
|
||||
|
||||
|
|
@ -64,7 +53,6 @@ pub struct Mgr {
|
|||
// Tab
|
||||
tab_active: Style,
|
||||
tab_inactive: Style,
|
||||
#[validate(range(min = 1, message = "Must be greater than 0"))]
|
||||
tab_width: u8,
|
||||
|
||||
// Count
|
||||
|
|
@ -80,7 +68,7 @@ pub struct Mgr {
|
|||
pub syntect_theme: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Mode {
|
||||
pub normal_main: Style,
|
||||
pub normal_alt: Style,
|
||||
|
|
@ -92,7 +80,7 @@ pub struct Mode {
|
|||
pub unset_alt: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Status {
|
||||
pub overall: Style,
|
||||
pub sep_left: StatusSep,
|
||||
|
|
@ -111,15 +99,14 @@ pub struct Status {
|
|||
pub progress_error: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct StatusSep {
|
||||
pub open: String,
|
||||
pub close: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Which {
|
||||
#[validate(range(min = 1, max = 3, message = "Must be between 1 and 3"))]
|
||||
pub cols: u8,
|
||||
pub mask: Style,
|
||||
pub cand: Style,
|
||||
|
|
@ -130,7 +117,7 @@ pub struct Which {
|
|||
pub separator_style: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Confirm {
|
||||
pub border: Style,
|
||||
pub title: Style,
|
||||
|
|
@ -142,7 +129,7 @@ pub struct Confirm {
|
|||
pub btn_labels: [String; 2],
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Spot {
|
||||
pub border: Style,
|
||||
pub title: Style,
|
||||
|
|
@ -151,7 +138,7 @@ pub struct Spot {
|
|||
pub tbl_cell: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Notify {
|
||||
pub title_info: Style,
|
||||
pub title_warn: Style,
|
||||
|
|
@ -162,14 +149,14 @@ pub struct Notify {
|
|||
pub icon_error: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Pick {
|
||||
pub border: Style,
|
||||
pub active: Style,
|
||||
pub inactive: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Input {
|
||||
pub border: Style,
|
||||
pub title: Style,
|
||||
|
|
@ -177,7 +164,7 @@ pub struct Input {
|
|||
pub selected: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Cmp {
|
||||
pub border: Style,
|
||||
pub active: Style,
|
||||
|
|
@ -188,14 +175,14 @@ pub struct Cmp {
|
|||
pub icon_command: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Tasks {
|
||||
pub border: Style,
|
||||
pub title: Style,
|
||||
pub hovered: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Help {
|
||||
pub on: Style,
|
||||
pub run: Style,
|
||||
|
|
@ -204,3 +191,20 @@ pub struct Help {
|
|||
pub hovered: Style,
|
||||
pub footer: Style,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
pub(crate) fn reshape(mut self, light: bool) -> Result<Self> {
|
||||
if self.mgr.tab_width < 1 {
|
||||
bail!("[mgr].tab_width must be greater than 0");
|
||||
} else if self.which.cols < 1 || self.which.cols > 3 {
|
||||
bail!("[which].cols must be between 1 and 3");
|
||||
}
|
||||
|
||||
self.icon = self.icon.reshape()?;
|
||||
|
||||
self.mgr.syntect_theme =
|
||||
self.flavor.syntect_path(light).unwrap_or_else(|| expand_path(&self.mgr.syntect_theme));
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
|
||||
use super::SortBy;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||
#[derive(Debug, Deserialize, DeserializeOver2, Serialize)]
|
||||
pub struct Which {
|
||||
// Sorting
|
||||
pub sort_by: SortBy,
|
||||
|
|
@ -14,19 +11,3 @@ pub struct Which {
|
|||
pub sort_reverse: bool,
|
||||
pub sort_translit: bool,
|
||||
}
|
||||
|
||||
impl FromStr for Which {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
which: Which,
|
||||
}
|
||||
|
||||
let outer: Outer =
|
||||
toml::from_str(s).context("Failed to parse the [which] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.which)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
yazi-config/src/yazi.rs
Normal file
37
yazi-config/src/yazi.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver1;
|
||||
|
||||
use crate::{mgr, open, opener, plugin, popup, preview, tasks, which};
|
||||
|
||||
#[derive(Deserialize, DeserializeOver1)]
|
||||
pub struct Yazi {
|
||||
#[serde(rename = "manager")]
|
||||
pub mgr: mgr::Mgr, // TODO: remove serde(rename)
|
||||
pub preview: preview::Preview,
|
||||
pub opener: opener::Opener,
|
||||
pub open: open::Open,
|
||||
pub tasks: tasks::Tasks,
|
||||
pub plugin: plugin::Plugin,
|
||||
pub input: popup::Input,
|
||||
pub confirm: popup::Confirm,
|
||||
pub pick: popup::Pick,
|
||||
pub which: which::Which,
|
||||
}
|
||||
|
||||
impl Yazi {
|
||||
pub(super) fn reshape(self) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mgr: self.mgr.reshape()?,
|
||||
preview: self.preview.reshape()?,
|
||||
opener: self.opener.reshape()?,
|
||||
open: self.open.reshape()?,
|
||||
tasks: self.tasks.reshape()?,
|
||||
plugin: self.plugin.reshape()?,
|
||||
input: self.input,
|
||||
confirm: self.confirm,
|
||||
pick: self.pick,
|
||||
which: self.which,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use crossterm::{cursor::SetCursorStyle, event::KeyCode};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_adapter::Dimension;
|
||||
use yazi_config::{INPUT, KEYMAP, keymap::{Chord, Key}};
|
||||
use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}};
|
||||
use yazi_macro::{render, render_and};
|
||||
use yazi_shared::Layer;
|
||||
|
||||
|
|
@ -115,6 +115,10 @@ impl Help {
|
|||
|
||||
#[inline]
|
||||
pub fn cursor_shape(&self) -> SetCursorStyle {
|
||||
if INPUT.cursor_blink { SetCursorStyle::BlinkingBlock } else { SetCursorStyle::SteadyBlock }
|
||||
if YAZI.input.cursor_blink {
|
||||
SetCursorStyle::BlinkingBlock
|
||||
} else {
|
||||
SetCursorStyle::SteadyBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use tokio::sync::mpsc;
|
||||
use yazi_config::{INPUT, popup::InputCfg};
|
||||
use yazi_config::{YAZI, popup::InputCfg};
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::{errors::InputError, event::CmdCow};
|
||||
use yazi_widgets::input::InputCallback;
|
||||
|
|
@ -45,7 +45,7 @@ impl Input {
|
|||
});
|
||||
self.inner = yazi_widgets::input::Input::new(
|
||||
opt.cfg.value,
|
||||
opt.cfg.position.offset.width.saturating_sub(INPUT.border()) as usize,
|
||||
opt.cfg.position.offset.width.saturating_sub(YAZI.input.border()) as usize,
|
||||
cb,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::HashMap, ffi::{OsStr, OsString}, io::{Read,
|
|||
use anyhow::{Result, anyhow};
|
||||
use scopeguard::defer;
|
||||
use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt};
|
||||
use yazi_config::{OPEN, PREVIEW};
|
||||
use yazi_config::YAZI;
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file};
|
||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER};
|
||||
|
|
@ -13,7 +13,7 @@ use crate::mgr::Mgr;
|
|||
|
||||
impl Mgr {
|
||||
pub(super) fn bulk_rename(&self) {
|
||||
let Some(opener) = OPEN.block_opener("bulk-rename.txt", "text/plain") else {
|
||||
let Some(opener) = YAZI.opener.block(YAZI.open.all("bulk-rename.txt", "text/plain")) else {
|
||||
return AppProxy::notify_warn("Bulk rename", "No text opener found");
|
||||
};
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl Mgr {
|
|||
|
||||
let cwd = self.cwd().clone();
|
||||
tokio::spawn(async move {
|
||||
let tmp = PREVIEW.tmpfile("bulk");
|
||||
let tmp = YAZI.preview.tmpfile("bulk");
|
||||
let s = old.iter().map(|o| o.as_os_str()).collect::<Vec<_>>().join(OsStr::new("\n"));
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsString};
|
|||
|
||||
use tracing::error;
|
||||
use yazi_boot::ARGS;
|
||||
use yazi_config::{OPEN, PLUGIN, popup::PickCfg};
|
||||
use yazi_config::{YAZI, popup::PickCfg};
|
||||
use yazi_fs::File;
|
||||
use yazi_macro::emit;
|
||||
use yazi_plugin::isolate;
|
||||
|
|
@ -64,7 +64,7 @@ impl Mgr {
|
|||
}
|
||||
|
||||
done.extend(files.iter().map(|f| (f.url_owned(), "")));
|
||||
for (fetcher, files) in PLUGIN.mime_fetchers(files) {
|
||||
for (fetcher, files) in YAZI.plugin.mime_fetchers(files) {
|
||||
if let Err(e) = isolate::fetch(CmdCow::from(&fetcher.run), files).await {
|
||||
error!("Fetch mime failed on opening: {e}");
|
||||
}
|
||||
|
|
@ -90,16 +90,16 @@ impl Mgr {
|
|||
return tasks.process_from_files(opt.cwd, opt.hovered, targets);
|
||||
}
|
||||
|
||||
let openers: Vec<_> = OPEN.common_openers(&targets);
|
||||
let openers: Vec<_> = YAZI.opener.all(YAZI.open.common(&targets).into_iter());
|
||||
if openers.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
|
||||
tokio::spawn(async move {
|
||||
let result = yazi_proxy::PickProxy::show(PickCfg::open(
|
||||
openers.iter().map(|o| o.desc.clone()).collect(),
|
||||
));
|
||||
let result =
|
||||
yazi_proxy::PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
||||
|
||||
if let Ok(choice) = result.await {
|
||||
TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::MAIN_SEPARATOR;
|
||||
|
||||
use crossterm::{execute, terminal::SetTitle};
|
||||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::CWD;
|
||||
use yazi_shared::{event::CmdCow, tty::TTY};
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ use crate::{mgr::Mgr, tasks::Tasks};
|
|||
|
||||
impl Mgr {
|
||||
pub fn refresh(&mut self, _: CmdCow, tasks: &Tasks) {
|
||||
if CWD.set(self.cwd()) && !MGR.title_format.is_empty() {
|
||||
if CWD.set(self.cwd()) && !YAZI.mgr.title_format.is_empty() {
|
||||
execute!(TTY.writer(), SetTitle(self.title())).ok();
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +35,6 @@ impl Mgr {
|
|||
format!("{}", self.cwd().display())
|
||||
};
|
||||
|
||||
MGR.title_format.replace("{cwd}", &cwd)
|
||||
YAZI.mgr.title_format.replace("{cwd}", &cwd)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_config::PLUGIN;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_shared::event::{CmdCow, Data};
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl Mgr {
|
|||
return self.active_mut().preview.reset();
|
||||
};
|
||||
|
||||
let Some(previewer) = PLUGIN.previewer(&hovered.url, mime) else {
|
||||
let Some(previewer) = YAZI.plugin.previewer(&hovered.url, mime) else {
|
||||
return self.active_mut().preview.reset();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use tokio::sync::oneshot::Sender;
|
||||
use yazi_config::{PICK, popup::Position};
|
||||
use yazi_config::{YAZI, popup::Position};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Pick {
|
||||
|
|
@ -24,7 +24,7 @@ impl Pick {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn limit(&self) -> usize {
|
||||
self.position.offset.height.saturating_sub(PICK.border()) as usize
|
||||
self.position.offset.height.saturating_sub(YAZI.pick.border()) as usize
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use yazi_config::PLUGIN;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::File;
|
||||
use yazi_plugin::{isolate, utils::SpotLock};
|
||||
use yazi_shared::url::Url;
|
||||
|
|
@ -22,7 +22,7 @@ impl Spot {
|
|||
return;
|
||||
}
|
||||
|
||||
let Some(spotter) = PLUGIN.spotter(&file.url, &mime) else {
|
||||
let Some(spotter) = YAZI.plugin.spotter(&file.url, &mime) else {
|
||||
return self.close(());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, fmt::Display};
|
||||
|
||||
use anyhow::bail;
|
||||
use yazi_config::{open::Opener, popup::InputCfg};
|
||||
use yazi_config::{opener::OpenerRule, popup::InputCfg};
|
||||
use yazi_proxy::{AppProxy, InputProxy, TasksProxy};
|
||||
use yazi_shared::{event::{CmdCow, Data}, url::Url};
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ impl Tab {
|
|||
}
|
||||
|
||||
TasksProxy::open_with(
|
||||
Cow::Owned(Opener {
|
||||
Cow::Owned(OpenerRule {
|
||||
run: opt.run.into_owned(),
|
||||
block: opt.block,
|
||||
orphan: opt.orphan,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::mem;
|
||||
|
||||
use yazi_config::{LAYOUT, MGR};
|
||||
use yazi_config::{LAYOUT, YAZI};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha};
|
||||
use yazi_proxy::MgrProxy;
|
||||
|
|
@ -24,7 +24,7 @@ impl Default for Folder {
|
|||
Self {
|
||||
url: Default::default(),
|
||||
cha: Default::default(),
|
||||
files: Files::new(MGR.show_hidden),
|
||||
files: Files::new(YAZI.mgr.show_hidden),
|
||||
stage: Default::default(),
|
||||
offset: Default::default(),
|
||||
cursor: Default::default(),
|
||||
|
|
@ -140,7 +140,7 @@ impl Folder {
|
|||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.get().limit();
|
||||
let scrolloff = (limit / 2).min(MGR.scrolloff as usize);
|
||||
let scrolloff = (limit / 2).min(YAZI.mgr.scrolloff as usize);
|
||||
|
||||
self.cursor = new.min(len.saturating_sub(1));
|
||||
self.offset = if self.cursor < (self.offset + limit).min(len).saturating_sub(scrolloff) {
|
||||
|
|
@ -157,7 +157,7 @@ impl Folder {
|
|||
let max = self.files.len().saturating_sub(1);
|
||||
|
||||
let limit = LAYOUT.get().limit();
|
||||
let scrolloff = (limit / 2).min(MGR.scrolloff as usize);
|
||||
let scrolloff = (limit / 2).min(YAZI.mgr.scrolloff as usize);
|
||||
|
||||
self.cursor = new.min(max);
|
||||
self.offset = if self.cursor < self.offset + scrolloff {
|
||||
|
|
@ -174,7 +174,7 @@ impl Folder {
|
|||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.get().limit();
|
||||
let scrolloff = (limit / 2).min(MGR.scrolloff as usize);
|
||||
let scrolloff = (limit / 2).min(YAZI.mgr.scrolloff as usize);
|
||||
|
||||
self.offset = if self.cursor < (self.offset + limit).min(len).saturating_sub(scrolloff) {
|
||||
len.saturating_sub(limit).min(self.offset)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::{FilesSorter, SortBy};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
|
|
@ -19,15 +19,15 @@ impl Default for Preference {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
// Sorting
|
||||
sort_by: MGR.sort_by,
|
||||
sort_sensitive: MGR.sort_sensitive,
|
||||
sort_reverse: MGR.sort_reverse,
|
||||
sort_dir_first: MGR.sort_dir_first,
|
||||
sort_translit: MGR.sort_translit,
|
||||
sort_by: YAZI.mgr.sort_by,
|
||||
sort_sensitive: YAZI.mgr.sort_sensitive,
|
||||
sort_reverse: YAZI.mgr.sort_reverse,
|
||||
sort_dir_first: YAZI.mgr.sort_dir_first,
|
||||
sort_translit: YAZI.mgr.sort_translit,
|
||||
|
||||
// Display
|
||||
linemode: MGR.linemode.to_owned(),
|
||||
show_hidden: MGR.show_hidden,
|
||||
linemode: YAZI.mgr.linemode.to_owned(),
|
||||
show_hidden: YAZI.mgr.show_hidden,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use tokio::{pin, task::JoinHandle};
|
|||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use yazi_adapter::ADAPTOR;
|
||||
use yazi_config::PLUGIN;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::{File, Files, FilesOp, cha::Cha};
|
||||
use yazi_macro::render;
|
||||
use yazi_plugin::{external::Highlighter, isolate, utils::PreviewLock};
|
||||
|
|
@ -27,7 +27,7 @@ impl Preview {
|
|||
return;
|
||||
}
|
||||
|
||||
let Some(previewer) = PLUGIN.previewer(&file.url, &mime) else {
|
||||
let Some(previewer) = YAZI.plugin.previewer(&file.url, &mime) else {
|
||||
return self.reset();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_config::{PLUGIN, plugin::MAX_PREWORKERS};
|
||||
use yazi_config::{YAZI, plugin::MAX_PREWORKERS};
|
||||
use yazi_fs::{File, Files, SortBy};
|
||||
|
||||
use super::Tasks;
|
||||
|
|
@ -10,7 +10,7 @@ impl Tasks {
|
|||
let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default();
|
||||
for f in paged {
|
||||
let hash = f.hash();
|
||||
for g in PLUGIN.fetchers(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||
for g in YAZI.plugin.fetchers(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||
match loaded.get_mut(&hash) {
|
||||
Some(n) if *n & (1 << g.idx) != 0 => continue,
|
||||
Some(n) => *n |= 1 << g.idx,
|
||||
|
|
@ -23,7 +23,7 @@ impl Tasks {
|
|||
drop(loaded);
|
||||
for (i, tasks) in tasks.into_iter().enumerate() {
|
||||
if !tasks.is_empty() {
|
||||
self.scheduler.fetch_paged(&PLUGIN.fetchers[i], tasks);
|
||||
self.scheduler.fetch_paged(&YAZI.plugin.fetchers[i], tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ impl Tasks {
|
|||
let mut loaded = self.scheduler.prework.loaded.lock();
|
||||
for f in paged {
|
||||
let hash = f.hash();
|
||||
for p in PLUGIN.preloaders(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||
for p in YAZI.plugin.preloaders(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||
match loaded.get_mut(&hash) {
|
||||
Some(n) if *n & (1 << p.idx) != 0 => continue,
|
||||
Some(n) => *n |= 1 << p.idx,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, collections::HashMap, ffi::OsString, mem};
|
||||
|
||||
use yazi_config::{OPEN, open::Opener};
|
||||
use yazi_config::{YAZI, opener::OpenerRule};
|
||||
use yazi_proxy::options::ProcessExecOpt;
|
||||
use yazi_shared::url::Url;
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ impl Tasks {
|
|||
pub fn process_from_files(&self, cwd: Url, hovered: Url, targets: Vec<(Url, &str)>) {
|
||||
let mut openers = HashMap::new();
|
||||
for (url, mime) in targets {
|
||||
if let Some(opener) = OPEN.openers(&url, mime).and_then(|o| o.first().copied()) {
|
||||
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&url, mime)) {
|
||||
openers.entry(opener).or_insert_with(|| vec![hovered.clone()]).push(url);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ impl Tasks {
|
|||
pub fn process_from_opener(
|
||||
&self,
|
||||
cwd: Url,
|
||||
opener: Cow<'static, Opener>,
|
||||
opener: Cow<'static, OpenerRule>,
|
||||
mut args: Vec<OsString>,
|
||||
) {
|
||||
if opener.spread {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, mem};
|
||||
|
||||
use yazi_config::{WHICH, keymap::ChordCow, which::SortBy};
|
||||
use yazi_config::{YAZI, keymap::ChordCow, which::SortBy};
|
||||
use yazi_shared::{natsort, translit::Transliterator};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
|
|
@ -14,10 +14,10 @@ pub struct WhichSorter {
|
|||
impl Default for WhichSorter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
by: WHICH.sort_by,
|
||||
sensitive: WHICH.sort_sensitive,
|
||||
reverse: WHICH.sort_reverse,
|
||||
translit: WHICH.sort_translit,
|
||||
by: YAZI.which.sort_by,
|
||||
sensitive: YAZI.which.sort_sensitive,
|
||||
reverse: YAZI.which.sort_reverse,
|
||||
translit: YAZI.which.sort_translit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crossterm::event::{MouseEvent, MouseEventKind};
|
||||
use mlua::{ObjectLike, Table};
|
||||
use tracing::error;
|
||||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_plugin::LUA;
|
||||
|
||||
use crate::{app::App, lives::Lives};
|
||||
|
|
@ -24,7 +24,7 @@ impl App {
|
|||
let area = yazi_plugin::elements::Rect::from(size);
|
||||
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
|
||||
|
||||
if matches!(event.kind, MouseEventKind::Down(_) if MGR.mouse_events.draggable()) {
|
||||
if matches!(event.kind, MouseEventKind::Down(_) if YAZI.mgr.mouse_events.draggable()) {
|
||||
root.raw_set("_drag_start", event)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ impl UserData for File {
|
|||
methods.add_method("style", |lua, me, ()| {
|
||||
lua.named_registry_value::<AnyUserData>("cx")?.borrow_scoped(|cx: &Ctx| {
|
||||
let mime = cx.mgr.mimetype.by_file(me).unwrap_or_default();
|
||||
THEME.filetypes.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style))
|
||||
THEME.filetype.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style))
|
||||
})
|
||||
});
|
||||
methods.add_method("is_hovered", |_, me, ()| Ok(me.idx == me.folder().cursor));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
|
||||
use futures::StreamExt;
|
||||
use tokio::{select, sync::{mpsc, oneshot}};
|
||||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_shared::event::Event;
|
||||
|
||||
pub(super) struct Signals {
|
||||
|
|
@ -62,7 +62,7 @@ impl Signals {
|
|||
Event::Key(key).emit()
|
||||
}
|
||||
CrosstermEvent::Mouse(mouse) => {
|
||||
if MGR.mouse_events.contains(mouse.kind.into()) {
|
||||
if YAZI.mgr.mouse_events.contains(mouse.kind.into()) {
|
||||
Event::Mouse(mouse).emit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crossterm::{Command, event::{DisableBracketedPaste, EnableBracketedPaste, Ke
|
|||
use cursor::RestoreCursor;
|
||||
use ratatui::{CompletedFrame, Frame, Terminal, backend::CrosstermBackend, buffer::Buffer, layout::Rect};
|
||||
use yazi_adapter::{Emulator, Mux};
|
||||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_shared::{SyncCell, tty::{TTY, TtyWriter}};
|
||||
|
||||
static CSI_U: AtomicBool = AtomicBool::new(false);
|
||||
|
|
@ -94,7 +94,7 @@ impl Term {
|
|||
PopKeyboardEnhancementFlags.write_ansi(&mut TTY.writer()).ok();
|
||||
}
|
||||
|
||||
if !MGR.title_format.is_empty() {
|
||||
if !YAZI.mgr.title_format.is_empty() {
|
||||
execute!(TTY.writer(), SetTitle("")).ok();
|
||||
}
|
||||
|
||||
|
|
@ -160,13 +160,13 @@ impl DerefMut for Term {
|
|||
// --- Mouse support
|
||||
mod mouse {
|
||||
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
||||
use yazi_config::MGR;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
pub struct SetMouse(pub bool);
|
||||
|
||||
impl crossterm::Command for SetMouse {
|
||||
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
||||
if MGR.mouse_events.is_empty() {
|
||||
if YAZI.mgr.mouse_events.is_empty() {
|
||||
Ok(())
|
||||
} else if self.0 {
|
||||
EnableMouseCapture.write_ansi(f)
|
||||
|
|
@ -177,7 +177,7 @@ mod mouse {
|
|||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> std::io::Result<()> {
|
||||
if MGR.mouse_events.is_empty() {
|
||||
if YAZI.mgr.mouse_events.is_empty() {
|
||||
Ok(())
|
||||
} else if self.0 {
|
||||
EnableMouseCapture.execute_winapi()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use mlua::{Function, IntoLua, Lua, UserData, Value};
|
||||
use yazi_config::PLUGIN;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use crate::{Composer, file::FileRef, url::UrlRef};
|
||||
|
||||
|
|
@ -21,25 +21,25 @@ impl Plugin {
|
|||
|
||||
fn fetchers(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (file, mime): (FileRef, mlua::String)| {
|
||||
lua.create_sequence_from(PLUGIN.fetchers(&file.url, &mime.to_str()?).map(Fetcher))
|
||||
lua.create_sequence_from(YAZI.plugin.fetchers(&file.url, &mime.to_str()?).map(Fetcher))
|
||||
})
|
||||
}
|
||||
|
||||
fn spotter(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| {
|
||||
Ok(PLUGIN.spotter(&url, &mime.to_str()?).map(Spotter))
|
||||
Ok(YAZI.plugin.spotter(&url, &mime.to_str()?).map(Spotter))
|
||||
})
|
||||
}
|
||||
|
||||
fn preloaders(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| {
|
||||
lua.create_sequence_from(PLUGIN.preloaders(&url, &mime.to_str()?).map(Preloader))
|
||||
lua.create_sequence_from(YAZI.plugin.preloaders(&url, &mime.to_str()?).map(Preloader))
|
||||
})
|
||||
}
|
||||
|
||||
fn previewer(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| {
|
||||
Ok(PLUGIN.previewer(&url, &mime.to_str()?).map(Previewer))
|
||||
Ok(YAZI.plugin.previewer(&url, &mime.to_str()?).map(Previewer))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use mlua::{IntoLua, Lua, LuaSerdeExt, SerializeOptions, Value};
|
||||
use yazi_adapter::EMULATOR;
|
||||
use yazi_boot::ARGS;
|
||||
use yazi_config::{MGR, PREVIEW, TASKS, THEME};
|
||||
use yazi_config::{THEME, YAZI};
|
||||
|
||||
use crate::{Composer, url::Url};
|
||||
|
||||
|
|
@ -43,21 +43,22 @@ impl<'a> Runtime<'a> {
|
|||
|
||||
fn mgr(lua: &Lua) -> mlua::Result<Value> {
|
||||
Composer::make(lua, 15, |lua, key| {
|
||||
let m = &YAZI.mgr;
|
||||
match key {
|
||||
b"ratio" => lua.to_value_with(&MGR.ratio, OPTS)?,
|
||||
b"ratio" => lua.to_value_with(&m.ratio, OPTS)?,
|
||||
|
||||
b"sort_by" => lua.to_value_with(&MGR.sort_by, OPTS)?,
|
||||
b"sort_sensitive" => lua.to_value_with(&MGR.sort_sensitive, OPTS)?,
|
||||
b"sort_reverse" => lua.to_value_with(&MGR.sort_reverse, OPTS)?,
|
||||
b"sort_dir_first" => lua.to_value_with(&MGR.sort_dir_first, OPTS)?,
|
||||
b"sort_translit" => lua.to_value_with(&MGR.sort_translit, OPTS)?,
|
||||
b"sort_by" => lua.to_value_with(&m.sort_by, OPTS)?,
|
||||
b"sort_sensitive" => lua.to_value_with(&m.sort_sensitive, OPTS)?,
|
||||
b"sort_reverse" => lua.to_value_with(&m.sort_reverse, OPTS)?,
|
||||
b"sort_dir_first" => lua.to_value_with(&m.sort_dir_first, OPTS)?,
|
||||
b"sort_translit" => lua.to_value_with(&m.sort_translit, OPTS)?,
|
||||
|
||||
b"linemode" => lua.to_value_with(&MGR.linemode, OPTS)?,
|
||||
b"show_hidden" => lua.to_value_with(&MGR.show_hidden, OPTS)?,
|
||||
b"show_symlink" => lua.to_value_with(&MGR.show_symlink, OPTS)?,
|
||||
b"scrolloff" => lua.to_value_with(&MGR.scrolloff, OPTS)?,
|
||||
b"mouse_events" => lua.to_value_with(&MGR.mouse_events, OPTS)?,
|
||||
b"title_format" => lua.to_value_with(&MGR.title_format, OPTS)?,
|
||||
b"linemode" => lua.to_value_with(&m.linemode, OPTS)?,
|
||||
b"show_hidden" => lua.to_value_with(&m.show_hidden, OPTS)?,
|
||||
b"show_symlink" => lua.to_value_with(&m.show_symlink, OPTS)?,
|
||||
b"scrolloff" => lua.to_value_with(&m.scrolloff, OPTS)?,
|
||||
b"mouse_events" => lua.to_value_with(&m.mouse_events, OPTS)?,
|
||||
b"title_format" => lua.to_value_with(&m.title_format, OPTS)?,
|
||||
_ => return Ok(Value::Nil),
|
||||
}
|
||||
.into_lua(lua)
|
||||
|
|
@ -66,21 +67,22 @@ impl<'a> Runtime<'a> {
|
|||
|
||||
fn preview(lua: &Lua) -> mlua::Result<Value> {
|
||||
Composer::make(lua, 15, |lua, key| {
|
||||
let p = &YAZI.preview;
|
||||
match key {
|
||||
b"wrap" => lua.to_value_with(&PREVIEW.wrap, OPTS)?,
|
||||
b"tab_size" => lua.to_value_with(&PREVIEW.tab_size, OPTS)?,
|
||||
b"max_width" => lua.to_value_with(&PREVIEW.max_width, OPTS)?,
|
||||
b"max_height" => lua.to_value_with(&PREVIEW.max_height, OPTS)?,
|
||||
b"wrap" => lua.to_value_with(&p.wrap, OPTS)?,
|
||||
b"tab_size" => lua.to_value_with(&p.tab_size, OPTS)?,
|
||||
b"max_width" => lua.to_value_with(&p.max_width, OPTS)?,
|
||||
b"max_height" => lua.to_value_with(&p.max_height, OPTS)?,
|
||||
|
||||
b"cache_dir" => lua.to_value_with(&PREVIEW.cache_dir, OPTS)?,
|
||||
b"cache_dir" => lua.to_value_with(&p.cache_dir, OPTS)?,
|
||||
|
||||
b"image_delay" => lua.to_value_with(&PREVIEW.image_delay, OPTS)?,
|
||||
b"image_filter" => lua.to_value_with(&PREVIEW.image_filter, OPTS)?,
|
||||
b"image_quality" => lua.to_value_with(&PREVIEW.image_quality, OPTS)?,
|
||||
b"sixel_fraction" => lua.to_value_with(&PREVIEW.sixel_fraction, OPTS)?,
|
||||
b"image_delay" => lua.to_value_with(&p.image_delay, OPTS)?,
|
||||
b"image_filter" => lua.to_value_with(&p.image_filter, OPTS)?,
|
||||
b"image_quality" => lua.to_value_with(&p.image_quality, OPTS)?,
|
||||
b"sixel_fraction" => lua.to_value_with(&p.sixel_fraction, OPTS)?,
|
||||
|
||||
b"ueberzug_scale" => lua.to_value_with(&PREVIEW.ueberzug_scale, OPTS)?,
|
||||
b"ueberzug_offset" => lua.to_value_with(&PREVIEW.ueberzug_offset, OPTS)?,
|
||||
b"ueberzug_scale" => lua.to_value_with(&p.ueberzug_scale, OPTS)?,
|
||||
b"ueberzug_offset" => lua.to_value_with(&p.ueberzug_offset, OPTS)?,
|
||||
_ => return Ok(Value::Nil),
|
||||
}
|
||||
.into_lua(lua)
|
||||
|
|
@ -89,15 +91,16 @@ impl<'a> Runtime<'a> {
|
|||
|
||||
fn tasks(lua: &Lua) -> mlua::Result<Value> {
|
||||
Composer::make(lua, 10, |lua, key| {
|
||||
let t = &YAZI.tasks;
|
||||
match key {
|
||||
b"micro_workers" => lua.to_value_with(&TASKS.micro_workers, OPTS)?,
|
||||
b"macro_workers" => lua.to_value_with(&TASKS.macro_workers, OPTS)?,
|
||||
b"bizarre_retry" => lua.to_value_with(&TASKS.bizarre_retry, OPTS)?,
|
||||
b"micro_workers" => lua.to_value_with(&t.micro_workers, OPTS)?,
|
||||
b"macro_workers" => lua.to_value_with(&t.macro_workers, OPTS)?,
|
||||
b"bizarre_retry" => lua.to_value_with(&t.bizarre_retry, OPTS)?,
|
||||
|
||||
b"image_alloc" => lua.to_value_with(&TASKS.image_alloc, OPTS)?,
|
||||
b"image_bound" => lua.to_value_with(&TASKS.image_bound, OPTS)?,
|
||||
b"image_alloc" => lua.to_value_with(&t.image_alloc, OPTS)?,
|
||||
b"image_bound" => lua.to_value_with(&t.image_bound, OPTS)?,
|
||||
|
||||
b"suppress_preload" => lua.to_value_with(&TASKS.suppress_preload, OPTS)?,
|
||||
b"suppress_preload" => lua.to_value_with(&t.suppress_preload, OPTS)?,
|
||||
_ => return Ok(Value::Nil),
|
||||
}
|
||||
.into_lua(lua)
|
||||
|
|
@ -108,7 +111,7 @@ impl<'a> Runtime<'a> {
|
|||
|
||||
// TODO: remove this
|
||||
pub fn install_manager(self) -> mlua::Result<Self> {
|
||||
self.lua.globals().raw_set("MANAGER", self.lua.to_value_with(&*MGR, OPTS)?)?;
|
||||
self.lua.globals().raw_set("MANAGER", self.lua.to_value_with(&YAZI.mgr, OPTS)?)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +123,7 @@ impl<'a> Runtime<'a> {
|
|||
|
||||
// TODO: remove this
|
||||
pub fn install_preview(self) -> mlua::Result<Self> {
|
||||
self.lua.globals().raw_set("PREVIEW", self.lua.to_value_with(&*PREVIEW, OPTS)?)?;
|
||||
self.lua.globals().raw_set("PREVIEW", self.lua.to_value_with(&YAZI.preview, OPTS)?)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
yazi-plugin/src/external/highlighter.rs
vendored
13
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -4,7 +4,7 @@ use anyhow::{Result, anyhow};
|
|||
use ratatui::{layout::Size, text::{Line, Span, Text}};
|
||||
use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}};
|
||||
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}};
|
||||
use yazi_config::{PREVIEW, THEME, preview::PreviewWrap};
|
||||
use yazi_config::{THEME, YAZI, preview::PreviewWrap};
|
||||
use yazi_shared::{Ids, errors::PeekError, replace_to_printable};
|
||||
|
||||
static INCR: Ids = Ids::new();
|
||||
|
|
@ -72,7 +72,7 @@ impl Highlighter {
|
|||
} else if !plain {
|
||||
before.push(String::from_utf8_lossy(&buf).into_owned());
|
||||
Self::line_height(&before[before.len() - 1], size.width)
|
||||
} else if PREVIEW.wrap == PreviewWrap::Yes {
|
||||
} else if YAZI.preview.wrap == PreviewWrap::Yes {
|
||||
Self::line_height(&String::from_utf8_lossy(&buf), size.width)
|
||||
} else {
|
||||
1
|
||||
|
|
@ -89,7 +89,7 @@ impl Highlighter {
|
|||
}
|
||||
|
||||
Ok(if plain {
|
||||
Text::from(replace_to_printable(&after, PREVIEW.tab_size))
|
||||
Text::from(replace_to_printable(&after, YAZI.preview.tab_size))
|
||||
} else {
|
||||
Self::highlight_with(before, after, syntax.unwrap()).await?
|
||||
})
|
||||
|
|
@ -113,7 +113,7 @@ impl Highlighter {
|
|||
h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
|
||||
let indent = PREVIEW.indent();
|
||||
let indent = YAZI.preview.indent();
|
||||
let mut lines = Vec::with_capacity(after.len());
|
||||
for line in after {
|
||||
if ticket != INCR.current() {
|
||||
|
|
@ -158,11 +158,12 @@ impl Highlighter {
|
|||
}
|
||||
|
||||
fn line_height(s: &str, width: u16) -> usize {
|
||||
if PREVIEW.wrap != PreviewWrap::Yes {
|
||||
if YAZI.preview.wrap != PreviewWrap::Yes {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let pad = PREVIEW
|
||||
let pad = YAZI
|
||||
.preview
|
||||
.tab_size
|
||||
.checked_sub(1)
|
||||
.map(|n| s.bytes().filter(|&b| b == b'\t').count() * n as usize)
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ macro_rules! impl_file_methods {
|
|||
|
||||
Ok(match me.icon.get() {
|
||||
IconCache::Missing => {
|
||||
let matched = yazi_config::THEME.icons.matches(me);
|
||||
let matched = yazi_config::THEME.icon.matches(me);
|
||||
me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon));
|
||||
matched.map(Icon::from)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use mlua::{Function, Lua, Table};
|
||||
use twox_hash::XxHash3_128;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use super::Utils;
|
||||
use crate::{file::FileRef, url::Url};
|
||||
|
|
@ -9,7 +9,7 @@ impl Utils {
|
|||
pub(super) fn file_cache(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, t: Table| {
|
||||
let file: FileRef = t.raw_get("file")?;
|
||||
if file.url.parent() == Some(&PREVIEW.cache_dir) {
|
||||
if file.url.parent() == Some(&YAZI.preview.cache_dir) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ impl Utils {
|
|||
format!("{:x}", h.finish_128())
|
||||
};
|
||||
|
||||
Ok(Some(Url::from(PREVIEW.cache_dir.join(hex))))
|
||||
Ok(Some(Url::from(YAZI.preview.cache_dir.join(hex))))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use mlua::{AnyUserData, Function, IntoLuaMulti, Lua, Table, Value};
|
||||
use yazi_config::{PREVIEW, preview::PreviewWrap};
|
||||
use yazi_config::{YAZI, preview::PreviewWrap};
|
||||
use yazi_macro::emit;
|
||||
use yazi_shared::{errors::PeekError, event::Cmd};
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ impl Utils {
|
|||
lock.data = vec![Renderable::Text(Text {
|
||||
area,
|
||||
inner,
|
||||
wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO },
|
||||
wrap: if YAZI.preview.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO },
|
||||
})];
|
||||
|
||||
emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock)));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_config::open::Opener;
|
||||
use yazi_config::opener::OpenerRule;
|
||||
use yazi_shared::{event::CmdCow, url::Url};
|
||||
|
||||
// --- Open
|
||||
|
|
@ -18,7 +18,7 @@ impl From<CmdCow> for OpenDoOpt {
|
|||
|
||||
// --- Open with
|
||||
pub struct OpenWithOpt {
|
||||
pub opener: Cow<'static, Opener>,
|
||||
pub opener: Cow<'static, OpenerRule>,
|
||||
pub cwd: Url,
|
||||
pub targets: Vec<Url>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use std::{borrow::Cow, ffi::OsString};
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::open::Opener;
|
||||
use yazi_config::opener::OpenerRule;
|
||||
use yazi_shared::{event::CmdCow, url::Url};
|
||||
|
||||
// --- Exec
|
||||
pub struct ProcessExecOpt {
|
||||
pub cwd: Url,
|
||||
pub opener: Cow<'static, Opener>,
|
||||
pub opener: Cow<'static, OpenerRule>,
|
||||
pub args: Vec<OsString>,
|
||||
pub done: Option<oneshot::Sender<()>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ffi::OsString};
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::open::Opener;
|
||||
use yazi_config::opener::OpenerRule;
|
||||
use yazi_macro::emit;
|
||||
use yazi_shared::{event::Cmd, url::Url};
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ pub struct TasksProxy;
|
|||
|
||||
impl TasksProxy {
|
||||
#[inline]
|
||||
pub fn open_with(opener: Cow<'static, Opener>, cwd: Url, targets: Vec<Url>) {
|
||||
pub fn open_with(opener: Cow<'static, OpenerRule>, cwd: Url, targets: Vec<Url>) {
|
||||
emit!(Call(Cmd::new("tasks:open_with").with_any("option", OpenWithOpt {
|
||||
opener,
|
||||
cwd,
|
||||
|
|
@ -20,7 +20,7 @@ impl TasksProxy {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn process_exec(opener: Cow<'static, Opener>, cwd: Url, args: Vec<OsString>) {
|
||||
pub async fn process_exec(opener: Cow<'static, OpenerRule>, cwd: Url, args: Vec<OsString>) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
emit!(Call(Cmd::new("tasks:process_exec").with_any("option", ProcessExecOpt {
|
||||
cwd,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::VecDeque, path::Path};
|
|||
use anyhow::{Result, anyhow};
|
||||
use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
||||
use tracing::warn;
|
||||
use yazi_config::TASKS;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::{calculate_size, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path};
|
||||
use yazi_shared::url::Url;
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ impl File {
|
|||
// Operation not permitted (os error 1)
|
||||
// Attribute not found (os error 93)
|
||||
Err(e)
|
||||
if task.retry < TASKS.bizarre_retry
|
||||
if task.retry < YAZI.tasks.bizarre_retry
|
||||
&& matches!(e.raw_os_error(), Some(1) | Some(93)) =>
|
||||
{
|
||||
task.retry += 1;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use yazi_config::TASKS;
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use super::{Task, TaskStage};
|
||||
use crate::TaskKind;
|
||||
|
|
@ -32,7 +32,7 @@ impl Ongoing {
|
|||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
if TASKS.suppress_preload {
|
||||
if YAZI.tasks.suppress_preload {
|
||||
self.all.values().filter(|t| t.kind != TaskKind::Preload).count()
|
||||
} else {
|
||||
self.all.len()
|
||||
|
|
@ -44,7 +44,7 @@ impl Ongoing {
|
|||
|
||||
#[inline]
|
||||
pub fn values(&self) -> Box<dyn Iterator<Item = &Task> + '_> {
|
||||
if TASKS.suppress_preload {
|
||||
if YAZI.tasks.suppress_preload {
|
||||
Box::new(self.all.values().filter(|t| t.kind != TaskKind::Preload))
|
||||
} else {
|
||||
Box::new(self.all.values())
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
|||
use futures::{FutureExt, future::BoxFuture};
|
||||
use parking_lot::Mutex;
|
||||
use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle};
|
||||
use yazi_config::{TASKS, plugin::{Fetcher, Preloader}};
|
||||
use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
|
||||
use yazi_dds::Pump;
|
||||
use yazi_fs::{must_be_dir, remove_dir_clean, unique_name};
|
||||
use yazi_proxy::{MgrProxy, options::{PluginOpt, ProcessExecOpt}};
|
||||
|
|
@ -39,14 +39,16 @@ impl Scheduler {
|
|||
|
||||
micro: micro_tx,
|
||||
prog: prog_tx,
|
||||
handles: Vec::with_capacity(TASKS.micro_workers as usize + TASKS.macro_workers as usize + 1),
|
||||
handles: Vec::with_capacity(
|
||||
YAZI.tasks.micro_workers as usize + YAZI.tasks.macro_workers as usize + 1,
|
||||
),
|
||||
ongoing: Default::default(),
|
||||
};
|
||||
|
||||
for _ in 0..TASKS.micro_workers {
|
||||
for _ in 0..YAZI.tasks.micro_workers {
|
||||
scheduler.handles.push(scheduler.schedule_micro(micro_rx.clone()));
|
||||
}
|
||||
for _ in 0..TASKS.macro_workers {
|
||||
for _ in 0..YAZI.tasks.macro_workers {
|
||||
scheduler.handles.push(scheduler.schedule_macro(micro_rx.clone(), macro_rx.clone()));
|
||||
}
|
||||
scheduler.progress(prog_rx);
|
||||
|
|
|
|||
|
|
@ -4,47 +4,7 @@ use serde::{Deserialize, Serialize, Serializer, ser::SerializeMap};
|
|||
use super::Color;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
||||
#[serde(from = "StyleShadow")]
|
||||
pub struct Style {
|
||||
pub fg: Option<Color>,
|
||||
pub bg: Option<Color>,
|
||||
pub modifier: Modifier,
|
||||
}
|
||||
|
||||
impl Serialize for Style {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(3))?;
|
||||
map.serialize_entry("fg", &self.fg)?;
|
||||
map.serialize_entry("bg", &self.bg)?;
|
||||
map.serialize_entry("modifier", &self.modifier.bits())?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for ratatui::style::Style {
|
||||
fn from(value: Style) -> Self {
|
||||
ratatui::style::Style {
|
||||
fg: value.fg.map(Into::into),
|
||||
bg: value.bg.map(Into::into),
|
||||
underline_color: None,
|
||||
add_modifier: value.modifier,
|
||||
sub_modifier: Modifier::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
#[inline]
|
||||
pub fn derive(self, other: ratatui::style::Style) -> ratatui::style::Style {
|
||||
ratatui::style::Style::from(self).patch(other)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
pub struct StyleShadow {
|
||||
#[serde(default)]
|
||||
pub fg: Option<Color>,
|
||||
#[serde(default)]
|
||||
|
|
@ -69,8 +29,33 @@ pub struct StyleShadow {
|
|||
pub crossed: bool,
|
||||
}
|
||||
|
||||
impl From<StyleShadow> for Style {
|
||||
fn from(value: StyleShadow) -> Self {
|
||||
impl Serialize for Style {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(3))?;
|
||||
map.serialize_entry("fg", &self.fg)?;
|
||||
map.serialize_entry("bg", &self.bg)?;
|
||||
map.serialize_entry("modifier", &Modifier::from(*self).bits())?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for ratatui::style::Style {
|
||||
fn from(value: Style) -> Self {
|
||||
ratatui::style::Style {
|
||||
fg: value.fg.map(Into::into),
|
||||
bg: value.bg.map(Into::into),
|
||||
underline_color: None,
|
||||
add_modifier: value.into(),
|
||||
sub_modifier: Modifier::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for ratatui::style::Modifier {
|
||||
fn from(value: Style) -> Self {
|
||||
let mut modifier = Modifier::empty();
|
||||
if value.bold {
|
||||
modifier |= Modifier::BOLD;
|
||||
|
|
@ -99,7 +84,13 @@ impl From<StyleShadow> for Style {
|
|||
if value.crossed {
|
||||
modifier |= Modifier::CROSSED_OUT;
|
||||
}
|
||||
|
||||
Self { fg: value.fg, bg: value.bg, modifier }
|
||||
modifier
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
#[inline]
|
||||
pub fn derive(self, other: ratatui::style::Style) -> ratatui::style::Style {
|
||||
ratatui::style::Style::from(self).patch(other)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::ops::Range;
|
|||
|
||||
use ratatui::crossterm::cursor::SetCursorStyle;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_config::INPUT;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_plugin::CLIPBOARD;
|
||||
|
||||
use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
|
||||
|
|
@ -90,12 +90,12 @@ impl Input {
|
|||
use InputMode as M;
|
||||
|
||||
match self.mode() {
|
||||
M::Normal if INPUT.cursor_blink => SetCursorStyle::BlinkingBlock,
|
||||
M::Normal if !INPUT.cursor_blink => SetCursorStyle::SteadyBlock,
|
||||
M::Insert if INPUT.cursor_blink => SetCursorStyle::BlinkingBar,
|
||||
M::Insert if !INPUT.cursor_blink => SetCursorStyle::SteadyBar,
|
||||
M::Replace if INPUT.cursor_blink => SetCursorStyle::BlinkingUnderScore,
|
||||
M::Replace if !INPUT.cursor_blink => SetCursorStyle::SteadyUnderScore,
|
||||
M::Normal if YAZI.input.cursor_blink => SetCursorStyle::BlinkingBlock,
|
||||
M::Normal if !YAZI.input.cursor_blink => SetCursorStyle::SteadyBlock,
|
||||
M::Insert if YAZI.input.cursor_blink => SetCursorStyle::BlinkingBar,
|
||||
M::Insert if !YAZI.input.cursor_blink => SetCursorStyle::SteadyBar,
|
||||
M::Replace if YAZI.input.cursor_blink => SetCursorStyle::BlinkingUnderScore,
|
||||
M::Replace if !YAZI.input.cursor_blink => SetCursorStyle::SteadyUnderScore,
|
||||
M::Normal | M::Insert | M::Replace => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue