1use compact_str::CompactString;
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthStr as _;
8
9use crate::prelude::*;
10
11
12
13#[derive(Clone, Default, Eq, Hash, PartialEq)]
14pub struct Buffer {
15 pub area: Rect,
17 pub content: Vec<Cell>,
20}
21
22impl Buffer {
23 #[must_use]
25 pub fn empty(area: Rect) -> Self {
26 Self::filled(area, Cell::EMPTY)
27 }
28
29 #[must_use]
31 pub fn filled(area: Rect, cell: Cell) -> Self {
32 let size = area.area() as usize;
33 let content = vec![cell; size];
34 Self { area, content }
35 }
36
37 pub fn content(&self) -> &[Cell] {
39 &self.content
40 }
41
42 pub const fn area(&self) -> &Rect {
44 &self.area
45 }
46
47 #[track_caller]
49 pub fn get(&self, x: u16, y: u16) -> &Cell {
50 let i = self.index_of(x, y);
51 &self.content[i]
52 }
53
54 #[track_caller]
56 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
57 let i = self.index_of(x, y);
58 &mut self.content[i]
59 }
60
61 #[track_caller]
69 pub fn index_of(&self, x: u16, y: u16) -> usize {
70 debug_assert!(
71 x >= self.area.left()
72 && x < self.area.right()
73 && y >= self.area.top()
74 && y < self.area.bottom(),
75 "Trying to access position outside the buffer: x={x}, y={y}, area={:?}",
76 self.area
77 );
78 ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
79 }
80
81 pub fn pos_of(&self, i: usize) -> (u16, u16) {
89 debug_assert!(
90 i < self.content.len(),
91 "Trying to get the coords of a cell outside the buffer: i={i} len={}",
92 self.content.len()
93 );
94 (
95 self.area.x + (i as u16) % self.area.width,
96 self.area.y + (i as u16) / self.area.width,
97 )
98 }
99
100 pub fn set_string<T, S>(&mut self, x: u16, y: u16, string: T, style: S)
102 where
103 T: AsRef<str>,
104 S: Into<Style>,
105 {
106 self.set_stringn(x, y, string, usize::MAX, style);
107 }
108
109 pub fn set_stringn<T, S>(
114 &mut self,
115 mut x: u16,
116 y: u16,
117 string: T,
118 max_width: usize,
119 style: S,
120 ) -> (u16, u16)
121 where
122 T: AsRef<str>,
123 S: Into<Style>,
124 {
125 let max_width = max_width.try_into().unwrap_or(u16::MAX);
126 let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
127 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
128 .map(|symbol| (symbol, symbol.width() as u16))
129 .filter(|(_symbol, width)| *width > 0)
130 .map_while(|(symbol, width)| {
131 remaining_width = remaining_width.checked_sub(width)?;
132 Some((symbol, width))
133 });
134 let style = style.into();
135 for (symbol, width) in graphemes {
136 self.get_mut(x, y).set_symbol(symbol).set_style(style);
137 let next_symbol = x + width;
138 x += 1;
139 while x < next_symbol {
141 self.get_mut(x, y).reset();
142 x += 1;
143 }
144 }
145 (x, y)
146 }
147
148 pub fn set_style<S: Into<Style>>(&mut self, area: Rect, style: S) {
158 let style = style.into();
159 let area = self.area.intersection(area);
160 for y in area.top()..area.bottom() {
161 for x in area.left()..area.right() {
162 self.get_mut(x, y).set_style(style);
163 }
164 }
165 }
166
167 pub fn resize(&mut self, area: Rect) {
170 let length = area.area() as usize;
171 if self.content.len() > length {
172 self.content.truncate(length);
173 } else {
174 self.content.resize(length, Cell::EMPTY);
175 }
176 self.area = area;
177 }
178
179 pub fn reset(&mut self) {
181 for cell in &mut self.content {
182 cell.reset();
183 }
184 }
185
186 pub fn merge(&mut self, other: &Self) {
188 let area = self.area.union(other.area);
189 self.content.resize(area.area() as usize, Cell::EMPTY);
190
191 let size = self.area.area() as usize;
193 for i in (0..size).rev() {
194 let (x, y) = self.pos_of(i);
195 let k = ((y - area.y) * area.width + x - area.x) as usize;
197 if i != k {
198 self.content[k] = self.content[i].clone();
199 self.content[i].reset();
200 }
201 }
202
203 let size = other.area.area() as usize;
206 for i in 0..size {
207 let (x, y) = other.pos_of(i);
208 let k = ((y - area.y) * area.width + x - area.x) as usize;
210 self.content[k] = other.content[i].clone();
211 }
212 self.area = area;
213 }
214
215 pub fn diff<'a>(&self, other: &'a Self) -> Vec<(u16, u16, &'a Cell)> {
244 let prev_buf = &self.content;
245 let next_buf = &other.content;
246
247 let mut updates: Vec<(u16, u16, &Cell)> = vec![];
248 let mut invalidated: usize = 0;
250 let mut to_skip: usize = 0;
253 for (i, (current, previous)) in next_buf.iter().zip(prev_buf.iter()).enumerate() {
254 if !current.skip && (current != previous || invalidated > 0) && to_skip == 0 {
255 let (x, y) = self.pos_of(i);
256 updates.push((x, y, &next_buf[i]));
257 }
258
259 to_skip = current.symbol().width().saturating_sub(1);
260
261 let affected_width = std::cmp::max(
262 current.symbol().width(),
263 previous.symbol().width(),
264 );
265 invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
266 }
267 updates
268 }
269}
270
271impl std::fmt::Debug for Buffer {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 f.write_fmt(format_args!("Buffer {{\n area: {:?}", &self.area))?;
281
282 if self.area.is_empty() {
283 return f.write_str("\n}");
284 }
285
286 f.write_str(",\n content: [\n")?;
287 let mut last_style = None;
288 let mut styles = vec![];
289 for (y, line) in self.content.chunks(self.area.width as usize).enumerate() {
290 let mut overwritten = vec![];
291 let mut skip: usize = 0;
292 f.write_str(" \"")?;
293 for (x, c) in line.iter().enumerate() {
294 if skip == 0 {
295 f.write_str(c.symbol())?;
296 } else {
297 overwritten.push((x, c.symbol()));
298 }
299 skip = std::cmp::max(skip, c.symbol().width()).saturating_sub(1);
300 #[cfg(feature = "underline-color")]
301 {
302 let style = (c.fg, c.bg, c.underline_color, c.modifier);
303 if last_style != Some(style) {
304 last_style = Some(style);
305 styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
306 }
307 }
308 #[cfg(not(feature = "underline-color"))]
309 {
310 let style = (c.fg, c.bg, c.modifier);
311 if last_style != Some(style) {
312 last_style = Some(style);
313 styles.push((x, y, c.fg, c.bg, c.modifier));
314 }
315 }
316 }
317 f.write_str("\",")?;
318 if !overwritten.is_empty() {
319 f.write_fmt(format_args!(
320 " // hidden by multi-width symbols: {overwritten:?}"
321 ))?;
322 }
323 f.write_str("\n")?;
324 }
325 f.write_str(" ],\n styles: [\n")?;
326 for s in styles {
327 #[cfg(feature = "underline-color")]
328 f.write_fmt(format_args!(
329 " x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
330 s.0, s.1, s.2, s.3, s.4, s.5
331 ))?;
332 #[cfg(not(feature = "underline-color"))]
333 f.write_fmt(format_args!(
334 " x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
335 s.0, s.1, s.2, s.3, s.4
336 ))?;
337 }
338 f.write_str(" ]\n}")?;
339 Ok(())
340 }
341}
342
343
344
345#[derive(Clone, Debug, Eq, Hash, PartialEq)]
347pub struct Cell {
348 symbol: CompactString,
357
358 pub fg: Color,
360
361 pub bg: Color,
363
364 #[cfg(feature = "underline-color")]
366 pub underline_color: Color,
367
368 pub modifier: Modifier,
370
371 pub skip: bool,
373}
374
375impl Cell {
376 pub const EMPTY: Self = Self::new(" ");
378
379 pub const fn new(symbol: &'static str) -> Self {
386 Self {
387 symbol: CompactString::new_inline(symbol),
388 fg: Color::Reset,
389 bg: Color::Reset,
390 #[cfg(feature = "underline-color")]
391 underline_color: Color::Reset,
392 modifier: Modifier::empty(),
393 skip: false,
394 }
395 }
396
397 #[must_use]
399 pub fn symbol(&self) -> &str {
400 self.symbol.as_str()
401 }
402
403 pub fn set_symbol(&mut self, symbol: &str) -> &mut Self {
405 self.symbol = CompactString::new(symbol);
406 self
407 }
408
409 pub(crate) fn append_symbol(&mut self, symbol: &str) -> &mut Self {
413 self.symbol.push_str(symbol);
414 self
415 }
416
417 pub fn set_char(&mut self, ch: char) -> &mut Self {
419 let mut buf = [0; 4];
420 self.symbol = CompactString::new(ch.encode_utf8(&mut buf));
421 self
422 }
423
424 pub fn set_fg(&mut self, color: Color) -> &mut Self {
426 self.fg = color;
427 self
428 }
429
430 pub fn set_bg(&mut self, color: Color) -> &mut Self {
432 self.bg = color;
433 self
434 }
435
436 pub fn set_style<S: Into<Style>>(&mut self, style: S) -> &mut Self {
441 let style = style.into();
442 if let Some(c) = style.fg {
443 self.fg = c;
444 }
445 if let Some(c) = style.bg {
446 self.bg = c;
447 }
448 #[cfg(feature = "underline-color")]
449 if let Some(c) = style.underline_color {
450 self.underline_color = c;
451 }
452 self.modifier.insert(style.add_modifier);
453 self.modifier.remove(style.sub_modifier);
454 self
455 }
456
457 #[must_use]
459 pub const fn style(&self) -> Style {
460 Style {
461 color_mode: ColorMode::overwrite(),
462 fg: Some(self.fg),
463 bg: Some(self.bg),
464 #[cfg(feature = "underline-color")]
465 underline_color: Some(self.underline_color),
466 add_modifier: self.modifier,
467 sub_modifier: Modifier::empty(),
468 }
469 }
470
471 pub fn set_skip(&mut self, skip: bool) -> &mut Self {
476 self.skip = skip;
477 self
478 }
479
480 pub fn reset(&mut self) {
482 self.symbol = CompactString::new_inline(" ");
483 self.fg = Color::Reset;
484 self.bg = Color::Reset;
485 #[cfg(feature = "underline-color")]
486 {
487 self.underline_color = Color::Reset;
488 }
489 self.modifier = Modifier::empty();
490 self.skip = false;
491 }
492}
493
494impl Default for Cell {
495 fn default() -> Self {
496 Self::EMPTY
497 }
498}