1use crate::{
2 layout::Rect,
3 style::{Color, Modifier, Style},
4 text::{Span, Spans},
5};
6use std::cmp::min;
7use unicode_segmentation::UnicodeSegmentation;
8use unicode_width::UnicodeWidthStr;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Cell {
13 pub symbol: String,
14 pub fg: Color,
15 pub bg: Color,
16 pub modifier: Modifier,
17}
18
19impl Cell {
20 pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
21 self.symbol.clear();
22 self.symbol.push_str(symbol);
23 self
24 }
25
26 pub fn set_char(&mut self, ch: char) -> &mut Cell {
27 self.symbol.clear();
28 self.symbol.push(ch);
29 self
30 }
31
32 pub fn set_fg(&mut self, color: Color) -> &mut Cell {
33 self.fg = color;
34 self
35 }
36
37 pub fn set_bg(&mut self, color: Color) -> &mut Cell {
38 self.bg = color;
39 self
40 }
41
42 pub fn set_style(&mut self, style: Style) -> &mut Cell {
43 if let Some(c) = style.fg {
44 self.fg = c;
45 }
46 if let Some(c) = style.bg {
47 self.bg = c;
48 }
49 self.modifier.insert(style.add_modifier);
50 self.modifier.remove(style.sub_modifier);
51 self
52 }
53
54 pub fn style(&self) -> Style {
55 Style::default()
56 .fg(self.fg)
57 .bg(self.bg)
58 .add_modifier(self.modifier)
59 }
60
61 pub fn reset(&mut self) {
62 self.symbol.clear();
63 self.symbol.push(' ');
64 self.fg = Color::Reset;
65 self.bg = Color::Reset;
66 self.modifier = Modifier::empty();
67 }
68}
69
70impl Default for Cell {
71 fn default() -> Cell {
72 Cell {
73 symbol: " ".into(),
74 fg: Color::Reset,
75 bg: Color::Reset,
76 modifier: Modifier::empty(),
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Default)]
109pub struct Buffer {
110 pub area: Rect,
112 pub content: Vec<Cell>,
115}
116
117impl Buffer {
118 pub fn empty(area: Rect) -> Buffer {
120 let cell: Cell = Default::default();
121 Buffer::filled(area, &cell)
122 }
123
124 pub fn filled(area: Rect, cell: &Cell) -> Buffer {
126 let size = area.area() as usize;
127 let mut content = Vec::with_capacity(size);
128 for _ in 0..size {
129 content.push(cell.clone());
130 }
131 Buffer { area, content }
132 }
133
134 pub fn with_lines<S>(lines: Vec<S>) -> Buffer
136 where
137 S: AsRef<str>,
138 {
139 let height = lines.len() as u16;
140 let width = lines
141 .iter()
142 .map(|i| i.as_ref().width() as u16)
143 .max()
144 .unwrap_or_default();
145 let mut buffer = Buffer::empty(Rect {
146 x: 0,
147 y: 0,
148 width,
149 height,
150 });
151 for (y, line) in lines.iter().enumerate() {
152 buffer.set_string(0, y as u16, line, Style::default());
153 }
154 buffer
155 }
156
157 pub fn content(&self) -> &[Cell] {
159 &self.content
160 }
161
162 pub fn area(&self) -> &Rect {
164 &self.area
165 }
166
167 pub fn get(&self, x: u16, y: u16) -> &Cell {
169 let i = self.index_of(x, y);
170 &self.content[i]
171 }
172
173 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
175 let i = self.index_of(x, y);
176 &mut self.content[i]
177 }
178
179 pub fn index_of(&self, x: u16, y: u16) -> usize {
208 debug_assert!(
209 x >= self.area.left()
210 && x < self.area.right()
211 && y >= self.area.top()
212 && y < self.area.bottom(),
213 "Trying to access position outside the buffer: x={}, y={}, area={:?}",
214 x,
215 y,
216 self.area
217 );
218 ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
219 }
220
221 pub fn pos_of(&self, i: usize) -> (u16, u16) {
249 debug_assert!(
250 i < self.content.len(),
251 "Trying to get the coords of a cell outside the buffer: i={} len={}",
252 i,
253 self.content.len()
254 );
255 (
256 self.area.x + i as u16 % self.area.width,
257 self.area.y + i as u16 / self.area.width,
258 )
259 }
260
261 pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
263 where
264 S: AsRef<str>,
265 {
266 self.set_stringn(x, y, string, usize::MAX, style);
267 }
268
269 pub fn set_stringn<S>(
272 &mut self,
273 x: u16,
274 y: u16,
275 string: S,
276 width: usize,
277 style: Style,
278 ) -> (u16, u16)
279 where
280 S: AsRef<str>,
281 {
282 let mut index = self.index_of(x, y);
283 let mut x_offset = x as usize;
284 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
285 let max_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
286 for s in graphemes {
287 let width = s.width();
288 if width == 0 || s.chars().all(|c| c.is_control()) {
289 continue;
290 }
291 if width > max_offset.saturating_sub(x_offset) {
294 break;
295 }
296
297 self.content[index].set_symbol(s);
298 self.content[index].set_style(style);
299 for i in index + 1..index + width {
301 self.content[i].reset();
302 }
303 index += width;
304 x_offset += width;
305 }
306 (x_offset as u16, y)
307 }
308
309 pub fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -> (u16, u16) {
310 let mut remaining_width = width;
311 let mut x = x;
312 for span in &spans.0 {
313 if remaining_width == 0 {
314 break;
315 }
316 let pos = self.set_stringn(
317 x,
318 y,
319 span.content.as_ref(),
320 remaining_width as usize,
321 span.style,
322 );
323 let w = pos.0.saturating_sub(x);
324 x = pos.0;
325 remaining_width = remaining_width.saturating_sub(w);
326 }
327 (x, y)
328 }
329
330 pub fn set_span<'a>(&mut self, x: u16, y: u16, span: &Span<'a>, width: u16) -> (u16, u16) {
331 self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
332 }
333
334 pub fn set_style(&mut self, area: Rect, style: Style) {
335 for y in area.top()..area.bottom() {
336 for x in area.left()..area.right() {
337 self.get_mut(x, y).set_style(style);
338 }
339 }
340 }
341
342 pub fn resize(&mut self, area: Rect) {
345 let length = area.area() as usize;
346 if self.content.len() > length {
347 self.content.truncate(length);
348 } else {
349 self.content.resize(length, Default::default());
350 }
351 self.area = area;
352 }
353
354 pub fn reset(&mut self) {
356 for c in &mut self.content {
357 c.reset();
358 }
359 }
360
361 pub fn merge(&mut self, other: &Buffer) {
363 let area = self.area.union(other.area);
364 let cell: Cell = Default::default();
365 self.content.resize(area.area() as usize, cell.clone());
366
367 let size = self.area.area() as usize;
369 for i in (0..size).rev() {
370 let (x, y) = self.pos_of(i);
371 let k = ((y - area.y) * area.width + x - area.x) as usize;
373 if i != k {
374 self.content[k] = self.content[i].clone();
375 self.content[i] = cell.clone();
376 }
377 }
378
379 let size = other.area.area() as usize;
382 for i in 0..size {
383 let (x, y) = other.pos_of(i);
384 let k = ((y - area.y) * area.width + x - area.x) as usize;
386 self.content[k] = other.content[i].clone();
387 }
388 self.area = area;
389 }
390
391 pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(u16, u16, &'a Cell)> {
420 let previous_buffer = &self.content;
421 let next_buffer = &other.content;
422 let width = self.area.width;
423
424 let mut updates: Vec<(u16, u16, &Cell)> = vec![];
425 let mut invalidated: usize = 0;
427 let mut to_skip: usize = 0;
430 for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
431 if (current != previous || invalidated > 0) && to_skip == 0 {
432 let x = i as u16 % width;
433 let y = i as u16 / width;
434 updates.push((x, y, &next_buffer[i]));
435 }
436
437 to_skip = current.symbol.width().saturating_sub(1);
438
439 let affected_width = std::cmp::max(current.symbol.width(), previous.symbol.width());
440 invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
441 }
442 updates
443 }
444}
445
446#[macro_export]
450macro_rules! assert_buffer_eq {
451 ($actual_expr:expr, $expected_expr:expr) => {
452 match (&$actual_expr, &$expected_expr) {
453 (actual, expected) => {
454 if actual.area != expected.area {
455 panic!(
456 indoc::indoc!(
457 "
458 buffer areas not equal
459 expected: {:?}
460 actual: {:?}"
461 ),
462 expected, actual
463 );
464 }
465 let diff = expected.diff(&actual);
466 if !diff.is_empty() {
467 let nice_diff = diff
468 .iter()
469 .enumerate()
470 .map(|(i, (x, y, cell))| {
471 let expected_cell = expected.get(*x, *y);
472 indoc::formatdoc! {"
473 {i}: at ({x}, {y})
474 expected: {expected_cell:?}
475 actual: {cell:?}
476 "}
477 })
478 .collect::<Vec<String>>()
479 .join("\n");
480 panic!(
481 indoc::indoc!(
482 "
483 buffer contents not equal
484 expected: {:?}
485 actual: {:?}
486 diff:
487 {}"
488 ),
489 expected, actual, nice_diff
490 );
491 }
492 assert_eq!(actual, expected, "buffers not equal");
495 }
496 }
497 };
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503
504 fn cell(s: &str) -> Cell {
505 let mut cell = Cell::default();
506 cell.set_symbol(s);
507 cell
508 }
509
510 #[test]
511 fn it_translates_to_and_from_coordinates() {
512 let rect = Rect::new(200, 100, 50, 80);
513 let buf = Buffer::empty(rect);
514
515 assert_eq!(buf.pos_of(0), (200, 100));
517 assert_eq!(buf.index_of(200, 100), 0);
518
519 assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
521 assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
522 }
523
524 #[test]
525 #[should_panic(expected = "outside the buffer")]
526 fn pos_of_panics_on_out_of_bounds() {
527 let rect = Rect::new(0, 0, 10, 10);
528 let buf = Buffer::empty(rect);
529
530 buf.pos_of(100);
532 }
533
534 #[test]
535 #[should_panic(expected = "outside the buffer")]
536 fn index_of_panics_on_out_of_bounds() {
537 let rect = Rect::new(0, 0, 10, 10);
538 let buf = Buffer::empty(rect);
539
540 buf.index_of(10, 0);
542 }
543
544 #[test]
545 fn buffer_set_string() {
546 let area = Rect::new(0, 0, 5, 1);
547 let mut buffer = Buffer::empty(area);
548
549 buffer.set_stringn(0, 0, "aaa", 0, Style::default());
551 assert_eq!(buffer, Buffer::with_lines(vec![" "]));
552
553 buffer.set_string(0, 0, "aaa", Style::default());
554 assert_eq!(buffer, Buffer::with_lines(vec!["aaa "]));
555
556 buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
558 assert_eq!(buffer, Buffer::with_lines(vec!["bbbb "]));
559
560 buffer.set_string(0, 0, "12345", Style::default());
561 assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
562
563 buffer.set_string(0, 0, "123456", Style::default());
565 assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
566 }
567
568 #[test]
569 fn buffer_set_string_zero_width() {
570 let area = Rect::new(0, 0, 1, 1);
571 let mut buffer = Buffer::empty(area);
572
573 let s = "\u{1}a";
575 buffer.set_stringn(0, 0, s, 1, Style::default());
576 assert_eq!(buffer, Buffer::with_lines(vec!["a"]));
577
578 let s = "a\u{1}";
580 buffer.set_stringn(0, 0, s, 1, Style::default());
581 assert_eq!(buffer, Buffer::with_lines(vec!["a"]));
582 }
583
584 #[test]
585 fn buffer_set_string_double_width() {
586 let area = Rect::new(0, 0, 5, 1);
587 let mut buffer = Buffer::empty(area);
588 buffer.set_string(0, 0, "コン", Style::default());
589 assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
590
591 buffer.set_string(0, 0, "コンピ", Style::default());
593 assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
594 }
595
596 #[test]
597 fn buffer_with_lines() {
598 let buffer =
599 Buffer::with_lines(vec!["┌────────┐", "│コンピュ│", "│ーa 上で│", "└────────┘"]);
600 assert_eq!(buffer.area.x, 0);
601 assert_eq!(buffer.area.y, 0);
602 assert_eq!(buffer.area.width, 10);
603 assert_eq!(buffer.area.height, 4);
604 }
605
606 #[test]
607 fn buffer_diffing_empty_empty() {
608 let area = Rect::new(0, 0, 40, 40);
609 let prev = Buffer::empty(area);
610 let next = Buffer::empty(area);
611 let diff = prev.diff(&next);
612 assert_eq!(diff, vec![]);
613 }
614
615 #[test]
616 fn buffer_diffing_empty_filled() {
617 let area = Rect::new(0, 0, 40, 40);
618 let prev = Buffer::empty(area);
619 let next = Buffer::filled(area, Cell::default().set_symbol("a"));
620 let diff = prev.diff(&next);
621 assert_eq!(diff.len(), 40 * 40);
622 }
623
624 #[test]
625 fn buffer_diffing_filled_filled() {
626 let area = Rect::new(0, 0, 40, 40);
627 let prev = Buffer::filled(area, Cell::default().set_symbol("a"));
628 let next = Buffer::filled(area, Cell::default().set_symbol("a"));
629 let diff = prev.diff(&next);
630 assert_eq!(diff, vec![]);
631 }
632
633 #[test]
634 fn buffer_diffing_single_width() {
635 let prev = Buffer::with_lines(vec![
636 " ",
637 "┌Title─┐ ",
638 "│ │ ",
639 "│ │ ",
640 "└──────┘ ",
641 ]);
642 let next = Buffer::with_lines(vec![
643 " ",
644 "┌TITLE─┐ ",
645 "│ │ ",
646 "│ │ ",
647 "└──────┘ ",
648 ]);
649 let diff = prev.diff(&next);
650 assert_eq!(
651 diff,
652 vec![
653 (2, 1, &cell("I")),
654 (3, 1, &cell("T")),
655 (4, 1, &cell("L")),
656 (5, 1, &cell("E")),
657 ]
658 );
659 }
660
661 #[test]
662 #[rustfmt::skip]
663 fn buffer_diffing_multi_width() {
664 let prev = Buffer::with_lines(vec![
665 "┌Title─┐ ",
666 "└──────┘ ",
667 ]);
668 let next = Buffer::with_lines(vec![
669 "┌称号──┐ ",
670 "└──────┘ ",
671 ]);
672 let diff = prev.diff(&next);
673 assert_eq!(
674 diff,
675 vec![
676 (1, 0, &cell("称")),
677 (3, 0, &cell("号")),
679 (5, 0, &cell("─")),
681 ]
682 );
683 }
684
685 #[test]
686 fn buffer_diffing_multi_width_offset() {
687 let prev = Buffer::with_lines(vec!["┌称号──┐"]);
688 let next = Buffer::with_lines(vec!["┌─称号─┐"]);
689
690 let diff = prev.diff(&next);
691 assert_eq!(
692 diff,
693 vec![(1, 0, &cell("─")), (2, 0, &cell("称")), (4, 0, &cell("号")),]
694 );
695 }
696
697 #[test]
698 fn buffer_merge() {
699 let mut one = Buffer::filled(
700 Rect {
701 x: 0,
702 y: 0,
703 width: 2,
704 height: 2,
705 },
706 Cell::default().set_symbol("1"),
707 );
708 let two = Buffer::filled(
709 Rect {
710 x: 0,
711 y: 2,
712 width: 2,
713 height: 2,
714 },
715 Cell::default().set_symbol("2"),
716 );
717 one.merge(&two);
718 assert_eq!(one, Buffer::with_lines(vec!["11", "11", "22", "22"]));
719 }
720
721 #[test]
722 fn buffer_merge2() {
723 let mut one = Buffer::filled(
724 Rect {
725 x: 2,
726 y: 2,
727 width: 2,
728 height: 2,
729 },
730 Cell::default().set_symbol("1"),
731 );
732 let two = Buffer::filled(
733 Rect {
734 x: 0,
735 y: 0,
736 width: 2,
737 height: 2,
738 },
739 Cell::default().set_symbol("2"),
740 );
741 one.merge(&two);
742 assert_eq!(
743 one,
744 Buffer::with_lines(vec!["22 ", "22 ", " 11", " 11"])
745 );
746 }
747
748 #[test]
749 fn buffer_merge3() {
750 let mut one = Buffer::filled(
751 Rect {
752 x: 3,
753 y: 3,
754 width: 2,
755 height: 2,
756 },
757 Cell::default().set_symbol("1"),
758 );
759 let two = Buffer::filled(
760 Rect {
761 x: 1,
762 y: 1,
763 width: 3,
764 height: 4,
765 },
766 Cell::default().set_symbol("2"),
767 );
768 one.merge(&two);
769 let mut merged = Buffer::with_lines(vec!["222 ", "222 ", "2221", "2221"]);
770 merged.area = Rect {
771 x: 1,
772 y: 1,
773 width: 4,
774 height: 4,
775 };
776 assert_eq!(one, merged);
777 }
778}