From 9a0ef6bb20fc405fa58ee52e22e3db58468519c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Wed, 14 May 2025 16:31:21 +0800 Subject: [PATCH] fix: icon rules mixing (#2755) --- yazi-config/src/theme/icon.rs | 6 +- yazi-plugin/src/utils/text.rs | 121 ++++++++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 24 deletions(-) diff --git a/yazi-config/src/theme/icon.rs b/yazi-config/src/theme/icon.rs index 9d42115f..6196b10a 100644 --- a/yazi-config/src/theme/icon.rs +++ b/yazi-config/src/theme/icon.rs @@ -101,13 +101,13 @@ impl Icon { 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(), + self.append_dirs.0.into_iter().chain(self.dirs.0).chain(self.prepend_dirs.0).collect(), ), files: StrIcons( - self.prepend_files.0.into_iter().chain(self.files.0).chain(self.append_files.0).collect(), + self.append_files.0.into_iter().chain(self.files.0).chain(self.prepend_files.0).collect(), ), exts: StrIcons( - self.prepend_exts.0.into_iter().chain(self.exts.0).chain(self.append_exts.0).collect(), + self.append_exts.0.into_iter().chain(self.exts.0).chain(self.prepend_exts.0).collect(), ), conds: CondIcons( self.prepend_conds.0.into_iter().chain(self.conds.0).chain(self.append_conds.0).collect(), diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index 07f69c3e..0e016606 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -25,17 +25,20 @@ impl Utils { } pub(super) fn truncate(lua: &Lua) -> mlua::Result { - fn idx_and_width(it: impl Iterator, max: usize) -> (usize, usize) { - let mut width = 0; + fn traverse( + it: impl Iterator, + max: usize, + ) -> (Option, usize, bool) { + let (mut adv, mut last) = (0, 0); let idx = it - .take_while(|(_, c)| { - width += c.width().unwrap_or(0); - width <= max + .take_while(|&(_, c)| { + last = adv; + adv += c.width().unwrap_or(0); + adv <= max }) .map(|(i, _)| i) - .last() - .unwrap(); - (idx, width) + .last(); + (idx, last, adv > max) } lua.create_function(|lua, (s, t): (mlua::String, Table)| { @@ -53,25 +56,28 @@ impl Utils { let lossy = String::from_utf8_lossy(&b); let rtl = t.raw_get("rtl").unwrap_or(false); - let (idx, width) = if rtl { - idx_and_width(lossy.char_indices().rev(), max) + let (idx, width, remain) = if rtl { + traverse(lossy.char_indices().rev(), max) } else { - idx_and_width(lossy.char_indices(), max) + traverse(lossy.char_indices(), max) }; - if width <= max { - return Ok(s); - } else if rtl && idx == 0 { - return Ok(s); - } else if !rtl && lossy[idx..].chars().nth(1).is_none() { + let Some(idx) = idx else { return lua.create_string("…") }; + if !remain { return Ok(s); } - let result: Vec<_> = if rtl { - let i = lossy[idx..].char_indices().nth(1).map(|(i, _)| idx + i).unwrap_or(lossy.len()); - "…".bytes().chain(lossy[i..].bytes()).collect() - } else { - lossy[..idx].bytes().chain("…".bytes()).collect() + let result: Vec<_> = match (rtl, width == max) { + (false, false) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + lossy[..idx + len].bytes().chain("…".bytes()).collect() + } + (false, true) => lossy[..idx].bytes().chain("…".bytes()).collect(), + (true, false) => "…".bytes().chain(lossy[idx..].bytes()).collect(), + (true, true) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + "…".bytes().chain(lossy[idx + len..].bytes()).collect() + } }; lua.create_string(result) }) @@ -88,3 +94,76 @@ impl Utils { }) } } + +#[cfg(test)] +mod tests { + use mlua::Value; + + use super::*; + + fn truncate(s: &str, max: usize, rtl: bool) -> String { + let lua = Lua::new(); + let t = lua + .create_table_from([("max", Value::Integer(max as i64)), ("rtl", Value::Boolean(rtl))]) + .unwrap(); + + Utils::truncate(&lua).unwrap().call((s, t)).unwrap() + } + + #[test] + fn test_truncate() { + assert_eq!(truncate("你好,world", 0, false), ""); + assert_eq!(truncate("你好,world", 1, false), "…"); + assert_eq!(truncate("你好,world", 2, false), "…"); + + assert_eq!(truncate("你好,世界", 3, false), "你…"); + assert_eq!(truncate("你好,世界", 4, false), "你…"); + assert_eq!(truncate("你好,世界", 5, false), "你好…"); + + assert_eq!(truncate("Hello, world", 5, false), "Hell…"); + assert_eq!(truncate("Ni好,世界", 3, false), "Ni…"); + } + + #[test] + fn test_truncate_rtl() { + assert_eq!(truncate("world,你好", 0, true), ""); + assert_eq!(truncate("world,你好", 1, true), "…"); + assert_eq!(truncate("world,你好", 2, true), "…"); + + assert_eq!(truncate("你好,世界", 3, true), "…界"); + assert_eq!(truncate("你好,世界", 4, true), "…界"); + assert_eq!(truncate("你好,世界", 5, true), "…世界"); + + assert_eq!(truncate("Hello, world", 5, true), "…orld"); + assert_eq!(truncate("你好,Shi界", 3, true), "…界"); + } + + #[test] + fn test_truncate_oboe() { + assert_eq!(truncate("Hello, world", 11, false), "Hello, wor…"); + assert_eq!(truncate("你好,世界", 9, false), "你好,世…"); + assert_eq!(truncate("你好,世Jie", 9, false), "你好,世…"); + + assert_eq!(truncate("Hello, world", 11, true), "…llo, world"); + assert_eq!(truncate("你好,世界", 9, true), "…好,世界"); + assert_eq!(truncate("Ni好,世界", 9, true), "…好,世界"); + } + + #[test] + fn test_truncate_exact() { + assert_eq!(truncate("Hello, world", 12, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 12, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, true), "你好,世界"); + } + + #[test] + fn test_truncate_overflow() { + assert_eq!(truncate("Hello, world", 13, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 13, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, true), "你好,世界"); + } +}