From 0338bc10354d1ea491165b3b0f903b3bf9832c52 Mon Sep 17 00:00:00 2001 From: efanhome Date: Fri, 21 Aug 2026 11:19:48 +0800 Subject: [PATCH] fix(tui): truncate Chinese/multibyte chars without panic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old implementation sliced by byte index (&s[..max]), which cut inside multibyte UTF-8 characters like 性 (bytes 38..41) and panicked at runtime. Use unicode-width to compute display width per character and break on char boundaries instead. - Add unicode-width 0.2 dependency - Rewrite truncate() with char_indices() + UnicodeWidthChar - Use UnicodeWidthStr for width calculation --- Cargo.lock | 1 + Cargo.toml | 1 + src/tui/ui.rs | 14 ++++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb3d28b..9ad5c0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -809,6 +809,7 @@ dependencies = [ "serde_json", "tempfile", "toml", + "unicode-width 0.2.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d382369..5678652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ name = "disky" path = "src/main.rs" [dependencies] +unicode-width = "0.2" jwalk = "0.8" duckdb = { version = "1.1", features = ["bundled"] } clap = { version = "4", features = ["derive"] } diff --git a/src/tui/ui.rs b/src/tui/ui.rs index a4a74b4..13c9e27 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -7,6 +7,7 @@ use ratatui::{ widgets::{Block, Borders, List, ListItem, ListState, Paragraph}, Frame, }; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; pub fn render(frame: &mut Frame, app: &App, list_state: &mut ListState) { let area = frame.area(); @@ -163,9 +164,14 @@ fn size_bar(size: i64, max: i64, width: usize) -> String { } fn truncate(s: &str, max: usize) -> String { - if s.len() <= max { - s.to_string() - } else { - format!("{}…", &s[..max.saturating_sub(1)]) + let max = max.saturating_sub(1); // reserve 1 column for '…' + let mut width = 0; + for (byte_i, ch) in s.char_indices() { + let ch_w = ch.width().unwrap_or(0); + if width + ch_w > max { + return format!("{}…", &s[..byte_i]); + } + width += ch_w; } + s.to_string() }