intelli_shell/utils/
widget.rs1use ratatui::{style::Style, text::Span};
2use unicode_width::UnicodeWidthChar;
3
4pub fn truncate_spans<'a>(spans: &[Span<'a>], max_width: u16) -> (Vec<Span<'a>>, u16) {
6 let mut current_width: u16 = 0;
7 let mut truncated_spans: Vec<Span<'a>> = Vec::new();
8
9 for span in spans {
10 let span_width = span.width() as u16;
11 if current_width.saturating_add(span_width) <= max_width {
12 current_width = current_width.saturating_add(span_width);
13 truncated_spans.push(span.clone());
14 } else {
15 let remaining_width = max_width.saturating_sub(current_width);
16 if remaining_width > 0 {
17 let mut content = String::new();
18 let mut content_width: u16 = 0;
19 for c in span.content.as_ref().chars() {
20 let char_width = UnicodeWidthChar::width(c).unwrap_or(0) as u16;
21 if content_width.saturating_add(char_width) <= remaining_width {
22 content.push(c);
23 content_width = content_width.saturating_add(char_width);
24 } else {
25 break;
26 }
27 }
28 if !content.is_empty() {
29 truncated_spans.push(Span::styled(content, span.style));
30 current_width = current_width.saturating_add(content_width);
31 }
32 }
33 break;
34 }
35 }
36 (truncated_spans, current_width)
37}
38
39pub fn truncate_spans_with_ellipsis<'a>(spans: &[Span<'a>], max_width: u16) -> (Vec<Span<'a>>, u16) {
41 let original_width = spans.iter().map(|s| s.width()).sum::<usize>() as u16;
42 if original_width <= max_width {
43 return (spans.to_vec(), original_width);
44 }
45 if max_width == 0 {
46 return (Vec::new(), 0);
47 }
48
49 let target_width = max_width.saturating_sub(1);
51 let (mut truncated_spans, mut current_width) = truncate_spans(spans, target_width);
52
53 let ellipsis_style = truncated_spans
55 .last()
56 .map_or_else(|| spans.first().map_or(Style::default(), |s| s.style), |s| s.style);
57
58 truncated_spans.push(Span::styled("…", ellipsis_style));
59 current_width += 1;
60
61 (truncated_spans, current_width)
62}