vtcode_tui/core_tui/session/utils/
line_truncation.rs1use ratatui::prelude::*;
2use unicode_width::UnicodeWidthStr;
3
4const ELLIPSIS: char = '…';
6
7pub(crate) fn truncate_line_with_ellipsis_if_overflow(
22 line: Line<'static>,
23 max_width: usize,
24) -> Line<'static> {
25 let total_width: usize = line.spans.iter().map(|s| s.width()).sum();
26 if total_width <= max_width {
27 return line;
29 }
30
31 let available_width = max_width.saturating_sub(ELLIPSIS.len_utf8());
33 if available_width == 0 {
34 return Line::from(Span::raw(ELLIPSIS.to_string()));
36 }
37
38 let mut truncated_line = truncate_line_to_width(line, available_width);
39 truncated_line.spans.push(Span::raw(ELLIPSIS.to_string()));
40 truncated_line
41}
42
43fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> {
49 let total: usize = line.spans.iter().map(|s| s.width()).sum();
50 if total <= max_width {
51 return line;
52 }
53
54 let mut remaining = max_width;
55 let mut truncated_spans: Vec<Span<'static>> = Vec::with_capacity(line.spans.len());
56 for span in line.spans {
57 let span_width = span.width();
58 if span_width <= remaining {
59 remaining -= span_width;
60 truncated_spans.push(span);
61 } else {
62 let mut chars_width = 0usize;
64 let mut byte_end = 0usize;
65 for ch in span.content.chars() {
66 let cw = UnicodeWidthStr::width(ch.encode_utf8(&mut [0u8; 4]) as &str);
67 if chars_width + cw > remaining {
68 break;
69 }
70 chars_width += cw;
71 byte_end += ch.len_utf8();
72 }
73 if byte_end > 0 {
74 let fragment: String = span.content[..byte_end].to_string();
75 truncated_spans.push(Span::styled(fragment, span.style));
76 }
77 break;
78 }
79 }
80 Line::from(truncated_spans)
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_no_truncation_when_fits() {
89 let line = Line::from("Hello");
90 let result = truncate_line_with_ellipsis_if_overflow(line.clone(), 10);
91 assert_eq!(result.spans.len(), 1);
92 assert_eq!(result.spans[0].content, "Hello");
93 }
94
95 #[test]
96 fn test_truncation_with_ellipsis() {
97 let line = Line::from("Hello World");
98 let result = truncate_line_with_ellipsis_if_overflow(line, 8);
99 let result_text: String = result.spans.iter().map(|s| s.content.as_ref()).collect();
100 assert!(result_text.ends_with(ELLIPSIS));
101 assert!(result_text.len() < "Hello World".len());
102 }
103
104 #[test]
105 fn test_ellipsis_only_when_very_narrow() {
106 let line = Line::from("Hello");
107 let result = truncate_line_with_ellipsis_if_overflow(line, 1);
108 assert_eq!(result.spans[0].content, ELLIPSIS.to_string());
109 }
110}