From d8a8a343543b568602c2077f2a093f97beddbe3d Mon Sep 17 00:00:00 2001 From: OnlyYu1996 <1158673577@qq.com> Date: Sun, 17 May 2026 05:33:16 +0800 Subject: [PATCH] fix(exec): truncate output on utf8 boundaries --- src/cortex-exec/src/output.rs | 45 ++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/cortex-exec/src/output.rs b/src/cortex-exec/src/output.rs index f7b26852b..18f7ae38a 100644 --- a/src/cortex-exec/src/output.rs +++ b/src/cortex-exec/src/output.rs @@ -169,7 +169,14 @@ fn truncate(s: &str, max_len: usize) -> String { if s.len() <= max_len { s.to_string() } else { - format!("{}...", &s[..max_len]) + let end = s + .char_indices() + .map(|(idx, _)| idx) + .take_while(|idx| *idx <= max_len) + .last() + .unwrap_or(0); + + format!("{}...", &s[..end]) } } @@ -178,3 +185,39 @@ impl Default for OutputWriter { Self::new(OutputFormat::Text) } } + +#[cfg(test)] +mod tests { + use super::truncate; + + #[test] + fn truncate_leaves_short_text_unchanged() { + assert_eq!(truncate("short", 100), "short"); + } + + #[test] + fn truncate_ascii_text() { + assert_eq!(truncate("hello world", 5), "hello..."); + } + + #[test] + fn truncate_multibyte_text_on_char_boundary() { + assert_eq!( + truncate( + "\u{65e5}\u{672c}\u{8a9e}\u{30d5}\u{30a1}\u{30a4}\u{30eb}", + 10 + ), + "\u{65e5}\u{672c}\u{8a9e}..." + ); + } + + #[test] + fn truncate_multibyte_text_before_first_char_boundary() { + assert_eq!(truncate("\u{65e5}\u{672c}\u{8a9e}", 1), "..."); + } + + #[test] + fn truncate_emoji_text_on_char_boundary() { + assert_eq!(truncate("abcd\u{1f600}efgh", 6), "abcd..."); + } +}