tau_term_screen/
two_line_elision.rs1use crate::{Cell, StyledText, layout_lines};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct TwoLineElision {
12 pub prefix: StyledText,
14 pub first: StyledText,
16 pub last: StyledText,
18 pub first_omissions: Vec<StyledText>,
21 pub last_omissions: Vec<StyledText>,
24 pub labels: Vec<StyledText>,
26 pub unabridged: Option<StyledText>,
28}
29
30impl TwoLineElision {
31 pub(crate) fn is_empty(&self) -> bool {
33 self.prefix.is_empty()
34 && self.first.is_empty()
35 && self.last.is_empty()
36 && self.first_omissions.iter().all(StyledText::is_empty)
37 && self.last_omissions.iter().all(StyledText::is_empty)
38 && self.labels.iter().all(StyledText::is_empty)
39 && self.unabridged.as_ref().is_none_or(StyledText::is_empty)
40 }
41
42 pub(crate) fn layout(&self, width: usize) -> Vec<Vec<Cell>> {
44 let configured_prefix = self.prefix.to_cells();
45 let prefix = if cells_width(&configured_prefix) < width {
46 configured_prefix
47 } else {
48 Vec::new()
49 };
50 if let Some(unabridged) = &self.unabridged {
51 let mut complete = self.prefix.clone();
52 for span in unabridged.spans() {
53 complete.push(span.clone());
54 }
55 let lines = layout_lines().content(&complete).width(width).call();
56 if lines.len() <= 2 {
57 return lines;
58 }
59 }
60
61 let first_marker = select(
62 &self.first_omissions,
63 width.saturating_sub(cells_width(&prefix)),
64 );
65 let label = select(&self.labels, width);
66 let last_marker = select(
67 &self.last_omissions,
68 width.saturating_sub(cells_width(&label)),
69 );
70
71 let mut first_row = prefix;
72 let first_budget = width
73 .saturating_sub(cells_width(&first_row))
74 .saturating_sub(cells_width(&first_marker));
75 first_row.extend(cell_prefix(&self.first.to_cells(), first_budget));
76 first_row.extend(first_marker);
77
78 let last_budget = width
79 .saturating_sub(cells_width(&last_marker))
80 .saturating_sub(cells_width(&label));
81 let mut last_row = last_marker;
82 last_row.extend(cell_suffix(&self.last.to_cells(), last_budget));
83 last_row.extend(label);
84 vec![first_row, last_row]
85 }
86}
87
88fn select(options: &[StyledText], width: usize) -> Vec<Cell> {
89 for option in options {
90 let cells = option.to_cells();
91 if cells_width(&cells) <= width {
92 return cells;
93 }
94 }
95 Vec::new()
96}
97
98fn cell_prefix(cells: &[Cell], width: usize) -> Vec<Cell> {
99 let mut used = 0;
100 cells
101 .iter()
102 .take_while(|cell| {
103 let next = used + cell.col_width();
104 let fits = next <= width;
105 if fits {
106 used = next;
107 }
108 fits
109 })
110 .cloned()
111 .collect()
112}
113
114fn cell_suffix(cells: &[Cell], width: usize) -> Vec<Cell> {
115 let mut used = 0;
116 let mut start = cells.len();
117 for (index, cell) in cells.iter().enumerate().rev() {
118 let next = used + cell.col_width();
119 if width < next {
120 start = index + 1;
121 while start < cells.len() && cells[start].col_width() == 0 {
122 start += 1;
123 }
124 break;
125 }
126 used = next;
127 start = index;
128 }
129 cells[start..].to_vec()
130}
131
132fn cells_width(cells: &[Cell]) -> usize {
133 cells.iter().map(Cell::col_width).sum()
134}