From a0ab614108e26181a9cb92010592331bea582284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Mon, 16 Jun 2025 20:18:19 +0800 Subject: [PATCH] fix: proper invalid UTF-8 path support (#2884) --- .github/workflows/draft.yml | 9 +- Cargo.lock | 2 + scripts/build.sh | 5 +- yazi-binding/Cargo.toml | 2 + yazi-binding/src/color.rs | 20 ++ .../src/bindings => yazi-binding/src}/icon.rs | 11 +- yazi-binding/src/lib.rs | 2 +- yazi-binding/src/macros.rs | 68 +++++ yazi-binding/src/style.rs | 45 +++ yazi-config/src/color.rs | 21 ++ yazi-config/src/icon.rs | 7 + yazi-config/src/lib.rs | 2 +- yazi-config/src/mgr/mgr.rs | 4 +- .../src/theme => yazi-config/src}/style.rs | 17 +- yazi-config/src/theme/filetype.rs | 3 +- yazi-config/src/theme/icon.rs | 4 +- yazi-config/src/theme/theme.rs | 62 ++-- yazi-core/src/cmp/commands/trigger.rs | 34 +-- yazi-core/src/mgr/commands/tab_create.rs | 22 +- yazi-core/src/tab/commands/cd.rs | 21 +- yazi-core/src/tab/commands/reveal.rs | 4 +- yazi-fm/src/lives/file.rs | 3 +- yazi-fm/src/notify/notify.rs | 4 +- yazi-fs/src/file.rs | 14 +- yazi-fs/src/mounts/linux.rs | 2 +- yazi-fs/src/path.rs | 23 +- yazi-plugin/preset/components/status.lua | 12 +- yazi-plugin/preset/components/tabs.lua | 4 +- yazi-plugin/preset/plugins/fzf.lua | 2 +- yazi-plugin/preset/plugins/zoxide.lua | 2 +- yazi-plugin/src/bindings/mod.rs | 2 +- yazi-plugin/src/config/theme.rs | 285 ++++++++++++++++-- yazi-plugin/src/elements/elements.rs | 2 +- yazi-plugin/src/elements/gauge.rs | 2 +- yazi-plugin/src/elements/line.rs | 2 +- yazi-plugin/src/elements/mod.rs | 2 +- yazi-plugin/src/elements/span.rs | 2 +- yazi-plugin/src/elements/style.rs | 66 ---- yazi-plugin/src/elements/table.rs | 3 +- yazi-plugin/src/elements/text.rs | 2 +- yazi-plugin/src/macros.rs | 71 +---- yazi-proxy/src/options/notify.rs | 12 +- yazi-proxy/src/tab.rs | 2 +- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/lib.rs | 2 +- yazi-shared/src/shell/windows.rs | 2 +- yazi-shared/src/theme/color.rs | 35 --- yazi-shared/src/theme/icon.rs | 15 - yazi-shared/src/theme/mod.rs | 1 - yazi-shared/src/url/urn.rs | 3 +- 50 files changed, 577 insertions(+), 367 deletions(-) create mode 100644 yazi-binding/src/color.rs rename {yazi-plugin/src/bindings => yazi-binding/src}/icon.rs (65%) create mode 100644 yazi-binding/src/style.rs create mode 100644 yazi-config/src/color.rs create mode 100644 yazi-config/src/icon.rs rename {yazi-shared/src/theme => yazi-config/src}/style.rs (80%) delete mode 100644 yazi-plugin/src/elements/style.rs delete mode 100644 yazi-shared/src/theme/color.rs delete mode 100644 yazi-shared/src/theme/icon.rs delete mode 100644 yazi-shared/src/theme/mod.rs diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index f0adf3b2..0cea3b43 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -109,16 +109,15 @@ jobs: matrix: include: - target: x86_64-unknown-linux-musl - image: rust-musl-cross:x86_64-musl - target: aarch64-unknown-linux-musl - image: rust-musl-cross:aarch64-musl container: - image: docker://ghcr.io/rust-cross/${{ matrix.image }} + image: docker://ghcr.io/cross-rs/${{ matrix.target }}:edge steps: - uses: actions/checkout@v4 - - name: Add musl target - run: rustup target add ${{ matrix.target }} + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 diff --git a/Cargo.lock b/Cargo.lock index f87219c2..7bc7cb20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3445,7 +3445,9 @@ version = "25.6.11" dependencies = [ "mlua", "paste", + "ratatui", "serde_json", + "yazi-config", "yazi-fs", "yazi-macro", "yazi-shared", diff --git a/scripts/build.sh b/scripts/build.sh index d91d82d0..4a8609fe 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -17,8 +17,7 @@ cp yazi-boot/completions/* "$ARTIFACT_NAME/completions" cp README.md LICENSE "$ARTIFACT_NAME" # Zip the artifact -if ! command -v zip &> /dev/null -then - sudo apt-get update && sudo apt-get install -yq zip +if ! command -v zip &> /dev/null; then + apt-get update && apt-get install -yq zip fi zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME" diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index eb17e4da..954915b9 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -9,6 +9,7 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] +yazi-config = { path = "../yazi-config", version = "25.6.11" } yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" } @@ -16,4 +17,5 @@ yazi-shared = { path = "../yazi-shared", version = "25.6.11" } # External dependencies mlua = { workspace = true } paste = { workspace = true } +ratatui = { workspace = true } serde_json = { workspace = true } diff --git a/yazi-binding/src/color.rs b/yazi-binding/src/color.rs new file mode 100644 index 00000000..6a14dd2a --- /dev/null +++ b/yazi-binding/src/color.rs @@ -0,0 +1,20 @@ +use std::str::FromStr; + +use mlua::{ExternalError, ExternalResult, UserData, Value}; + +#[derive(Clone, Copy, Default)] +pub struct Color(pub ratatui::style::Color); + +impl TryFrom for Color { + type Error = mlua::Error; + + fn try_from(value: Value) -> Result { + Ok(Self(match value { + Value::String(s) => ratatui::style::Color::from_str(&s.to_str()?).into_lua_err()?, + Value::UserData(ud) => ud.borrow::()?.0, + _ => Err("expected a Color".into_lua_err())?, + })) + } +} + +impl UserData for Color {} diff --git a/yazi-plugin/src/bindings/icon.rs b/yazi-binding/src/icon.rs similarity index 65% rename from yazi-plugin/src/bindings/icon.rs rename to yazi-binding/src/icon.rs index eff21620..cef7fe07 100644 --- a/yazi-plugin/src/bindings/icon.rs +++ b/yazi-binding/src/icon.rs @@ -1,25 +1,24 @@ use std::ops::Deref; use mlua::{UserData, UserDataFields, Value}; -use yazi_binding::cached_field; -use crate::elements::Style; +use crate::{Style, cached_field}; pub struct Icon { - inner: &'static yazi_shared::theme::Icon, + inner: &'static yazi_config::Icon, v_text: Option, v_style: Option, } impl Deref for Icon { - type Target = yazi_shared::theme::Icon; + type Target = yazi_config::Icon; fn deref(&self) -> &Self::Target { self.inner } } -impl From<&'static yazi_shared::theme::Icon> for Icon { - fn from(icon: &'static yazi_shared::theme::Icon) -> Self { +impl From<&'static yazi_config::Icon> for Icon { + fn from(icon: &'static yazi_config::Icon) -> Self { Self { inner: icon, v_text: None, v_style: None } } } diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index 83b2fa91..575ff39e 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -1,3 +1,3 @@ mod macros; -yazi_macro::mod_flat!(error id stage url urn); +yazi_macro::mod_flat!(color error icon id stage style url urn); diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 8495d63d..efb021c5 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -17,3 +17,71 @@ macro_rules! cached_field { }); }; } + +#[macro_export] +macro_rules! impl_style_shorthands { + ($methods:ident, $($field:tt).+) => { + $methods.add_function_mut("fg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| { + match value { + mlua::Value::Nil => { + ud.borrow::()?.$($field).+.fg.map($crate::Color).into_lua(lua) + }, + _ => { + ud.borrow_mut::()?.$($field).+.fg = Some($crate::Color::try_from(value)?.0); + ud.into_lua(lua) + } + } + }); + $methods.add_function_mut("bg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| { + match value { + mlua::Value::Nil => { + ud.borrow::()?.$($field).+.bg.map($crate::Color).into_lua(lua) + } + _ => { + ud.borrow_mut::()?.$($field).+.bg = Some($crate::Color::try_from(value)?.0); + ud.into_lua(lua) + } + } + }); + $methods.add_function_mut("bold", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::BOLD; + Ok(ud) + }); + $methods.add_function_mut("dim", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::DIM; + Ok(ud) + }); + $methods.add_function_mut("italic", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::ITALIC; + Ok(ud) + }); + $methods.add_function_mut("underline", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::UNDERLINED; + Ok(ud) + }); + $methods.add_function_mut("blink", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::SLOW_BLINK; + Ok(ud) + }); + $methods.add_function_mut("blink_rapid", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::RAPID_BLINK; + Ok(ud) + }); + $methods.add_function_mut("reverse", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::REVERSED; + Ok(ud) + }); + $methods.add_function_mut("hidden", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::HIDDEN; + Ok(ud) + }); + $methods.add_function_mut("crossed", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::CROSSED_OUT; + Ok(ud) + }); + $methods.add_function_mut("reset", |_, ud: mlua::AnyUserData| { + ud.borrow_mut::()?.$($field).+.add_modifier = ratatui::style::Modifier::empty(); + Ok(ud) + }); + }; +} diff --git a/yazi-binding/src/style.rs b/yazi-binding/src/style.rs new file mode 100644 index 00000000..c81c8cf1 --- /dev/null +++ b/yazi-binding/src/style.rs @@ -0,0 +1,45 @@ +use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; + +#[derive(Clone, Copy, Default)] +pub struct Style(pub ratatui::style::Style); + +impl Style { + pub fn compose(lua: &Lua) -> mlua::Result { + let new = lua.create_function(|_, (_, value): (Table, Value)| Self::try_from(value))?; + + let style = lua.create_table()?; + style.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?)); + + style.into_lua(lua) + } +} + +impl TryFrom for Style { + type Error = mlua::Error; + + fn try_from(value: Value) -> Result { + Ok(Self(match value { + Value::Nil => Default::default(), + Value::UserData(ud) => ud.borrow::()?.0, + _ => Err("expected a Style or nil".into_lua_err())?, + })) + } +} + +impl From for Style { + fn from(value: yazi_config::Style) -> Self { Self(value.into()) } +} + +impl UserData for Style { + fn add_methods>(methods: &mut M) { + crate::impl_style_shorthands!(methods, 0); + + methods.add_function_mut("patch", |_, (ud, value): (AnyUserData, Value)| { + { + let mut me = ud.borrow_mut::()?; + me.0 = me.0.patch(Self::try_from(value)?.0); + } + Ok(ud) + }) + } +} diff --git a/yazi-config/src/color.rs b/yazi-config/src/color.rs new file mode 100644 index 00000000..fbdbe23b --- /dev/null +++ b/yazi-config/src/color.rs @@ -0,0 +1,21 @@ +use std::str::FromStr; + +use serde::Deserialize; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Color(ratatui::style::Color); + +impl<'de> Deserialize<'de> for Color { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + ratatui::style::Color::from_str(&String::deserialize(deserializer)?) + .map_err(serde::de::Error::custom) + .map(Self) + } +} + +impl From for ratatui::style::Color { + fn from(value: Color) -> Self { value.0 } +} diff --git a/yazi-config/src/icon.rs b/yazi-config/src/icon.rs new file mode 100644 index 00000000..d19cdc21 --- /dev/null +++ b/yazi-config/src/icon.rs @@ -0,0 +1,7 @@ +use crate::Style; + +#[derive(Clone, Debug)] +pub struct Icon { + pub text: String, + pub style: Style, +} diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index db702d4f..9fab9163 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -2,7 +2,7 @@ yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which); -yazi_macro::mod_flat!(layout pattern platform preset priority yazi); +yazi_macro::mod_flat!(color icon layout pattern platform preset priority style yazi); use std::io::{Read, Write}; diff --git a/yazi-config/src/mgr/mgr.rs b/yazi-config/src/mgr/mgr.rs index c8a65f98..32c1cc5b 100644 --- a/yazi-config/src/mgr/mgr.rs +++ b/yazi-config/src/mgr/mgr.rs @@ -1,11 +1,11 @@ use anyhow::{Result, bail}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use yazi_codegen::DeserializeOver2; use yazi_fs::{CWD, SortBy}; use super::{MgrRatio, MouseEvents}; -#[derive(Debug, Deserialize, DeserializeOver2, Serialize)] +#[derive(Debug, Deserialize, DeserializeOver2)] pub struct Mgr { pub ratio: MgrRatio, diff --git a/yazi-shared/src/theme/style.rs b/yazi-config/src/style.rs similarity index 80% rename from yazi-shared/src/theme/style.rs rename to yazi-config/src/style.rs index 4e9db592..9063f805 100644 --- a/yazi-shared/src/theme/style.rs +++ b/yazi-config/src/style.rs @@ -1,7 +1,7 @@ use ratatui::style::Modifier; -use serde::{Deserialize, Serialize, Serializer, ser::SerializeMap}; +use serde::Deserialize; -use super::Color; +use crate::Color; #[derive(Clone, Copy, Debug, Default, Deserialize)] pub struct Style { @@ -29,19 +29,6 @@ pub struct Style { pub crossed: bool, } -impl Serialize for Style { - fn serialize(&self, serializer: S) -> Result - 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