1use anstyle::Style;
4
5use crate::cells::{prefix_len, text_width};
6use crate::line::{Line, Piece};
7
8pub const ELLIPSIS: &str = "…";
10
11#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
16#[non_exhaustive]
17pub enum Overflow {
18 #[default]
21 KeepWords,
22 Fold,
25 Ellipsis,
27}
28
29impl Line {
30 pub(crate) fn minimum_width(&self, overflow: Overflow) -> u16 {
33 let pieces = self.pieces();
34 match overflow {
35 Overflow::KeepWords => pieces
36 .split(|piece| piece.space)
37 .map(total_width)
38 .max()
39 .unwrap_or(0),
40 Overflow::Fold => pieces
41 .iter()
42 .filter(|piece| !piece.space)
43 .map(|piece| piece.width)
44 .max()
45 .unwrap_or(0),
46 Overflow::Ellipsis => u16::from(self.width() > 0),
47 }
48 }
49
50 #[must_use]
58 pub fn truncate(&self, width: u16) -> Self {
59 if self.width() <= width {
60 return self.clone();
61 }
62 let Some(budget) = width.checked_sub(ELLIPSIS_WIDTH) else {
63 return Self::new();
64 };
65 let mut cut = Self::new();
66 let mut used: u16 = 0;
67 let mut style = Style::new();
68 for span in self.spans() {
69 style = span.style();
70 let span_width = span.width();
71 if used.saturating_add(span_width) <= budget {
72 cut.push(span.clone());
73 used = used.saturating_add(span_width);
74 } else {
75 let room = budget.saturating_sub(used);
76 let kept = &span.text()[..prefix_len(span.text(), room)];
77 let gap = usize::from(room.saturating_sub(text_width(kept)));
78 cut.append(kept, style);
79 cut.append(&" ".repeat(gap), style);
80 break;
81 }
82 }
83 cut.append(ELLIPSIS, style);
84 cut
85 }
86
87 #[must_use]
97 pub fn fit(&self, width: u16, overflow: Overflow) -> Vec<Self> {
98 match overflow {
99 Overflow::KeepWords | Overflow::Fold => self.wrap(width, overflow),
100 Overflow::Ellipsis => vec![self.truncate(width)],
101 }
102 }
103
104 fn wrap(&self, width: u16, overflow: Overflow) -> Vec<Self> {
107 let pieces = self.pieces();
108 let mut wrapper = Wrapper::new(width.max(1), overflow);
109 let mut rest = pieces.as_slice();
110 while !rest.is_empty() {
111 let space_len = rest.iter().take_while(|piece| piece.space).count();
112 let (space, tail) = rest.split_at(space_len);
113 let word_len = tail.iter().take_while(|piece| !piece.space).count();
114 let (word, next) = tail.split_at(word_len);
115 if word.is_empty() {
116 break;
117 }
118 wrapper.place(space, word);
119 rest = next;
120 }
121 wrapper.finish()
122 }
123}
124
125const ELLIPSIS_WIDTH: u16 = 1;
126
127fn total_width(pieces: &[Piece<'_>]) -> u16 {
128 pieces
129 .iter()
130 .fold(0, |total, piece| total.saturating_add(piece.width))
131}
132
133struct Wrapper<'a> {
140 width: u16,
141 overflow: Overflow,
142 lines: Vec<Line>,
143 current: Vec<Piece<'a>>,
144 current_width: u16,
145}
146
147impl<'a> Wrapper<'a> {
148 fn new(width: u16, overflow: Overflow) -> Self {
149 Self {
150 width,
151 overflow,
152 lines: Vec::new(),
153 current: Vec::new(),
154 current_width: 0,
155 }
156 }
157
158 fn place(&mut self, space: &[Piece<'a>], word: &[Piece<'a>]) {
159 let space_width = total_width(space);
160 let word_width = total_width(word);
161 if self.current.is_empty() {
162 let indented =
163 self.lines.is_empty() && space_width.saturating_add(word_width) <= self.width;
164 if indented {
165 self.extend(space);
166 }
167 self.put_word(word, word_width);
168 } else if self
169 .current_width
170 .saturating_add(space_width)
171 .saturating_add(word_width)
172 <= self.width
173 {
174 self.extend(space);
175 self.extend(word);
176 } else {
177 self.break_line();
178 self.put_word(word, word_width);
179 }
180 }
181
182 fn put_word(&mut self, word: &[Piece<'a>], word_width: u16) {
185 if self.overflow == Overflow::Fold && word_width > self.width {
186 for piece in word {
187 let overflows = self.current_width.saturating_add(piece.width) > self.width;
188 if overflows && !self.current.is_empty() {
189 self.break_line();
190 }
191 self.push(*piece);
192 }
193 } else {
194 self.extend(word);
195 }
196 }
197
198 fn extend(&mut self, pieces: &[Piece<'a>]) {
199 for piece in pieces {
200 self.push(*piece);
201 }
202 }
203
204 fn push(&mut self, piece: Piece<'a>) {
205 self.current_width = self.current_width.saturating_add(piece.width);
206 self.current.push(piece);
207 }
208
209 fn break_line(&mut self) {
210 self.lines.push(Line::from_pieces(&self.current));
211 self.current.clear();
212 self.current_width = 0;
213 }
214
215 fn finish(mut self) -> Vec<Line> {
216 if !self.current.is_empty() || self.lines.is_empty() {
217 self.break_line();
218 }
219 self.lines
220 }
221}