1use std::{
2 fmt,
3 ops::{Index, IndexMut},
4};
5
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthStr;
8
9use crate::{
10 buffer::Cell,
11 layout::{Position, Rect},
12 style::Style,
13 text::{Line, Span},
14};
15
16#[derive(Default, Clone, Eq, PartialEq, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct Buffer {
71 pub area: Rect,
73 pub content: Vec<Cell>,
76}
77
78impl Buffer {
79 #[must_use]
81 pub fn empty(area: Rect) -> Self {
82 Self::filled(area, Cell::EMPTY)
83 }
84
85 #[must_use]
87 pub fn filled(area: Rect, cell: Cell) -> Self {
88 let size = area.area() as usize;
89 let content = vec![cell; size];
90 Self { area, content }
91 }
92
93 #[must_use]
95 pub fn with_lines<'a, Iter>(lines: Iter) -> Self
96 where
97 Iter: IntoIterator,
98 Iter::Item: Into<Line<'a>>,
99 {
100 let lines = lines.into_iter().map(Into::into).collect::<Vec<_>>();
101 let height = lines.len() as u16;
102 let width = lines.iter().map(Line::width).max().unwrap_or_default() as u16;
103 let mut buffer = Self::empty(Rect::new(0, 0, width, height));
104 for (y, line) in lines.iter().enumerate() {
105 buffer.set_line(0, y as u16, line, width);
106 }
107 buffer
108 }
109
110 pub fn content(&self) -> &[Cell] {
112 &self.content
113 }
114
115 pub const fn area(&self) -> &Rect {
117 &self.area
118 }
119
120 #[track_caller]
132 #[deprecated(note = "Use Buffer[] or Buffer::cell instead")]
133 #[must_use]
134 pub fn get(&self, x: u16, y: u16) -> &Cell {
135 let i = self.index_of(x, y);
136 &self.content[i]
137 }
138
139 #[track_caller]
152 #[deprecated(note = "Use Buffer[] or Buffer::cell_mut instead")]
153 #[must_use]
154 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
155 let i = self.index_of(x, y);
156 &mut self.content[i]
157 }
158
159 #[must_use]
184 pub fn cell<P: Into<Position>>(&self, position: P) -> Option<&Cell> {
185 let position = position.into();
186 let index = self.index_of_opt(position)?;
187 self.content.get(index)
188 }
189
190 #[must_use]
217 pub fn cell_mut<P: Into<Position>>(&mut self, position: P) -> Option<&mut Cell> {
218 let position = position.into();
219 let index = self.index_of_opt(position)?;
220 self.content.get_mut(index)
221 }
222
223 #[track_caller]
250 #[must_use]
251 pub fn index_of(&self, x: u16, y: u16) -> usize {
252 self.index_of_opt(Position { x, y }).unwrap_or_else(|| {
253 panic!(
254 "index outside of buffer: the area is {area:?} but index is ({x}, {y})",
255 area = self.area,
256 )
257 })
258 }
259
260 #[must_use]
266 const fn index_of_opt(&self, position: Position) -> Option<usize> {
267 let area = self.area;
268 if !area.contains(position) {
269 return None;
270 }
271 let y = (position.y - self.area.y) as usize;
273 let x = (position.x - self.area.x) as usize;
274 let width = self.area.width as usize;
275 Some(y * width + x)
276 }
277
278 #[must_use]
306 pub fn pos_of(&self, i: usize) -> (u16, u16) {
307 debug_assert!(
308 i < self.content.len(),
309 "Trying to get the coords of a cell outside the buffer: i={i} len={}",
310 self.content.len()
311 );
312 (
313 self.area.x + (i as u16) % self.area.width,
314 self.area.y + (i as u16) / self.area.width,
315 )
316 }
317
318 pub fn set_string<T, S>(&mut self, x: u16, y: u16, string: T, style: S)
320 where
321 T: AsRef<str>,
322 S: Into<Style>,
323 {
324 self.set_stringn(x, y, string, usize::MAX, style);
325 }
326
327 pub fn set_stringn<T, S>(
332 &mut self,
333 mut x: u16,
334 y: u16,
335 string: T,
336 max_width: usize,
337 style: S,
338 ) -> (u16, u16)
339 where
340 T: AsRef<str>,
341 S: Into<Style>,
342 {
343 let max_width = max_width.try_into().unwrap_or(u16::MAX);
344 let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
345 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
346 .filter(|symbol| !symbol.contains(|char: char| char.is_control()))
347 .map(|symbol| (symbol, symbol.width() as u16))
348 .filter(|(_symbol, width)| *width > 0)
349 .map_while(|(symbol, width)| {
350 remaining_width = remaining_width.checked_sub(width)?;
351 Some((symbol, width))
352 });
353 let style = style.into();
354 for (symbol, width) in graphemes {
355 self[(x, y)].set_symbol(symbol).set_style(style);
356 let next_symbol = x + width;
357 x += 1;
358 while x < next_symbol {
360 self[(x, y)].reset();
361 x += 1;
362 }
363 }
364 (x, y)
365 }
366
367 pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, max_width: u16) -> (u16, u16) {
369 let mut remaining_width = max_width;
370 let mut x = x;
371 for span in line {
372 if remaining_width == 0 {
373 break;
374 }
375 let pos = self.set_stringn(
376 x,
377 y,
378 span.content.as_ref(),
379 remaining_width as usize,
380 line.style.patch(span.style),
381 );
382 let w = pos.0.saturating_sub(x);
383 x = pos.0;
384 remaining_width = remaining_width.saturating_sub(w);
385 }
386 (x, y)
387 }
388
389 pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, max_width: u16) -> (u16, u16) {
391 self.set_stringn(x, y, &span.content, max_width as usize, span.style)
392 }
393
394 pub fn set_style<S: Into<Style>>(&mut self, area: Rect, style: S) {
401 let style = style.into();
402 let area = self.area.intersection(area);
403 for y in area.top()..area.bottom() {
404 for x in area.left()..area.right() {
405 self[(x, y)].set_style(style);
406 }
407 }
408 }
409
410 pub fn resize(&mut self, area: Rect) {
413 let length = area.area() as usize;
414 if self.content.len() > length {
415 self.content.truncate(length);
416 } else {
417 self.content.resize(length, Cell::EMPTY);
418 }
419 self.area = area;
420 }
421
422 pub fn reset(&mut self) {
424 for cell in &mut self.content {
425 cell.reset();
426 }
427 }
428
429 pub fn merge(&mut self, other: &Self) {
431 let area = self.area.union(other.area);
432 self.content.resize(area.area() as usize, Cell::EMPTY);
433
434 let size = self.area.area() as usize;
436 for i in (0..size).rev() {
437 let (x, y) = self.pos_of(i);
438 let k = ((y - area.y) * area.width + x - area.x) as usize;
440 if i != k {
441 self.content[k] = self.content[i].clone();
442 self.content[i].reset();
443 }
444 }
445
446 let size = other.area.area() as usize;
449 for i in 0..size {
450 let (x, y) = other.pos_of(i);
451 let k = ((y - area.y) * area.width + x - area.x) as usize;
453 self.content[k] = other.content[i].clone();
454 }
455 self.area = area;
456 }
457
458 pub fn diff<'a>(&self, other: &'a Self) -> Vec<(u16, u16, &'a Cell)> {
487 let previous_buffer = &self.content;
488 let next_buffer = &other.content;
489
490 let mut updates: Vec<(u16, u16, &Cell)> = vec![];
491 let mut invalidated: usize = 0;
493 let mut to_skip: usize = 0;
496 for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
497 if !current.skip && (current != previous || invalidated > 0) && to_skip == 0 {
498 let (x, y) = self.pos_of(i);
499 updates.push((x, y, &next_buffer[i]));
500 }
501
502 to_skip = current.symbol().width().saturating_sub(1);
503
504 let affected_width = std::cmp::max(current.symbol().width(), previous.symbol().width());
505 invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
506 }
507 updates
508 }
509}
510
511impl<P: Into<Position>> Index<P> for Buffer {
512 type Output = Cell;
513
514 fn index(&self, position: P) -> &Self::Output {
537 let position = position.into();
538 let index = self.index_of(position.x, position.y);
539 &self.content[index]
540 }
541}
542
543impl<P: Into<Position>> IndexMut<P> for Buffer {
544 fn index_mut(&mut self, position: P) -> &mut Self::Output {
567 let position = position.into();
568 let index = self.index_of(position.x, position.y);
569 &mut self.content[index]
570 }
571}
572
573impl fmt::Debug for Buffer {
574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582 f.write_fmt(format_args!("Buffer {{\n area: {:?}", &self.area))?;
583
584 if self.area.is_empty() {
585 return f.write_str("\n}");
586 }
587
588 f.write_str(",\n content: [\n")?;
589 let mut last_style = None;
590 let mut styles = vec![];
591 for (y, line) in self.content.chunks(self.area.width as usize).enumerate() {
592 let mut overwritten = vec![];
593 let mut skip: usize = 0;
594 f.write_str(" \"")?;
595 for (x, c) in line.iter().enumerate() {
596 if skip == 0 {
597 f.write_str(c.symbol())?;
598 } else {
599 overwritten.push((x, c.symbol()));
600 }
601 skip = std::cmp::max(skip, c.symbol().width()).saturating_sub(1);
602 #[cfg(feature = "underline-color")]
603 {
604 let style = (c.fg, c.bg, c.underline_color, c.modifier);
605 if last_style != Some(style) {
606 last_style = Some(style);
607 styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
608 }
609 }
610 #[cfg(not(feature = "underline-color"))]
611 {
612 let style = (c.fg, c.bg, c.modifier);
613 if last_style != Some(style) {
614 last_style = Some(style);
615 styles.push((x, y, c.fg, c.bg, c.modifier));
616 }
617 }
618 }
619 f.write_str("\",")?;
620 if !overwritten.is_empty() {
621 f.write_fmt(format_args!(
622 " // hidden by multi-width symbols: {overwritten:?}"
623 ))?;
624 }
625 f.write_str("\n")?;
626 }
627 f.write_str(" ],\n styles: [\n")?;
628 for s in styles {
629 #[cfg(feature = "underline-color")]
630 f.write_fmt(format_args!(
631 " x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
632 s.0, s.1, s.2, s.3, s.4, s.5
633 ))?;
634 #[cfg(not(feature = "underline-color"))]
635 f.write_fmt(format_args!(
636 " x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
637 s.0, s.1, s.2, s.3, s.4
638 ))?;
639 }
640 f.write_str(" ]\n}")?;
641 Ok(())
642 }
643}
644
645#[cfg(test)]
646mod tests {
647 use std::iter;
648
649 use itertools::Itertools;
650 use rstest::{fixture, rstest};
651
652 use super::*;
653 use crate::style::{Color, Modifier, Stylize};
654
655 #[test]
656 fn debug_empty_buffer() {
657 let buffer = Buffer::empty(Rect::ZERO);
658 let result = format!("{buffer:?}");
659 println!("{result}");
660 let expected = "Buffer {\n area: Rect { x: 0, y: 0, width: 0, height: 0 }\n}";
661 assert_eq!(result, expected);
662 }
663
664 #[cfg(feature = "underline-color")]
665 #[test]
666 fn debug_grapheme_override() {
667 let buffer = Buffer::with_lines(["a🦀b"]);
668 let result = format!("{buffer:?}");
669 println!("{result}");
670 let expected = indoc::indoc!(
671 r#"
672 Buffer {
673 area: Rect { x: 0, y: 0, width: 4, height: 1 },
674 content: [
675 "a🦀b", // hidden by multi-width symbols: [(2, " ")]
676 ],
677 styles: [
678 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
679 ]
680 }"#
681 );
682 assert_eq!(result, expected);
683 }
684
685 #[test]
686 fn debug_some_example() {
687 let mut buffer = Buffer::empty(Rect::new(0, 0, 12, 2));
688 buffer.set_string(0, 0, "Hello World!", Style::default());
689 buffer.set_string(
690 0,
691 1,
692 "G'day World!",
693 Style::default()
694 .fg(Color::Green)
695 .bg(Color::Yellow)
696 .add_modifier(Modifier::BOLD),
697 );
698 let result = format!("{buffer:?}");
699 println!("{result}");
700 #[cfg(feature = "underline-color")]
701 let expected = indoc::indoc!(
702 r#"
703 Buffer {
704 area: Rect { x: 0, y: 0, width: 12, height: 2 },
705 content: [
706 "Hello World!",
707 "G'day World!",
708 ],
709 styles: [
710 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
711 x: 0, y: 1, fg: Green, bg: Yellow, underline: Reset, modifier: BOLD,
712 ]
713 }"#
714 );
715 #[cfg(not(feature = "underline-color"))]
716 let expected = indoc::indoc!(
717 r#"
718 Buffer {
719 area: Rect { x: 0, y: 0, width: 12, height: 2 },
720 content: [
721 "Hello World!",
722 "G'day World!",
723 ],
724 styles: [
725 x: 0, y: 0, fg: Reset, bg: Reset, modifier: NONE,
726 x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
727 ]
728 }"#
729 );
730
731 assert_eq!(result, expected);
732 }
733
734 #[test]
735 fn it_translates_to_and_from_coordinates() {
736 let rect = Rect::new(200, 100, 50, 80);
737 let buf = Buffer::empty(rect);
738
739 assert_eq!(buf.pos_of(0), (200, 100));
741 assert_eq!(buf.index_of(200, 100), 0);
742
743 assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
745 assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
746 }
747
748 #[test]
749 #[should_panic(expected = "outside the buffer")]
750 fn pos_of_panics_on_out_of_bounds() {
751 let rect = Rect::new(0, 0, 10, 10);
752 let buf = Buffer::empty(rect);
753
754 let _ = buf.pos_of(100);
756 }
757
758 #[rstest]
759 #[case::left(9, 10)]
760 #[case::top(10, 9)]
761 #[case::right(20, 10)]
762 #[case::bottom(10, 20)]
763 #[should_panic(
764 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
765 )]
766 fn index_of_panics_on_out_of_bounds(#[case] x: u16, #[case] y: u16) {
767 let _ = Buffer::empty(Rect::new(10, 10, 10, 10)).index_of(x, y);
768 }
769
770 #[test]
771 fn test_cell() {
772 let buf = Buffer::with_lines(["Hello", "World"]);
773
774 let mut expected = Cell::default();
775 expected.set_symbol("H");
776
777 assert_eq!(buf.cell((0, 0)), Some(&expected));
778 assert_eq!(buf.cell((10, 10)), None);
779 assert_eq!(buf.cell(Position::new(0, 0)), Some(&expected));
780 assert_eq!(buf.cell(Position::new(10, 10)), None);
781 }
782
783 #[test]
784 fn test_cell_mut() {
785 let mut buf = Buffer::with_lines(["Hello", "World"]);
786
787 let mut expected = Cell::default();
788 expected.set_symbol("H");
789
790 assert_eq!(buf.cell_mut((0, 0)), Some(&mut expected));
791 assert_eq!(buf.cell_mut((10, 10)), None);
792 assert_eq!(buf.cell_mut(Position::new(0, 0)), Some(&mut expected));
793 assert_eq!(buf.cell_mut(Position::new(10, 10)), None);
794 }
795
796 #[test]
797 fn index() {
798 let buf = Buffer::with_lines(["Hello", "World"]);
799
800 let mut expected = Cell::default();
801 expected.set_symbol("H");
802
803 assert_eq!(buf[(0, 0)], expected);
804 }
805
806 #[rstest]
807 #[case::left(9, 10)]
808 #[case::top(10, 9)]
809 #[case::right(20, 10)]
810 #[case::bottom(10, 20)]
811 #[should_panic(
812 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
813 )]
814 fn index_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
815 let rect = Rect::new(10, 10, 10, 10);
816 let buf = Buffer::empty(rect);
817 let _ = buf[(x, y)];
818 }
819
820 #[test]
821 fn index_mut() {
822 let mut buf = Buffer::with_lines(["Cat", "Dog"]);
823 buf[(0, 0)].set_symbol("B");
824 buf[Position::new(0, 1)].set_symbol("L");
825 assert_eq!(buf, Buffer::with_lines(["Bat", "Log"]));
826 }
827
828 #[rstest]
829 #[case::left(9, 10)]
830 #[case::top(10, 9)]
831 #[case::right(20, 10)]
832 #[case::bottom(10, 20)]
833 #[should_panic(
834 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
835 )]
836 fn index_mut_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
837 let mut buf = Buffer::empty(Rect::new(10, 10, 10, 10));
838 buf[(x, y)].set_symbol("A");
839 }
840
841 #[test]
842 fn set_string() {
843 let area = Rect::new(0, 0, 5, 1);
844 let mut buffer = Buffer::empty(area);
845
846 buffer.set_stringn(0, 0, "aaa", 0, Style::default());
848 assert_eq!(buffer, Buffer::with_lines([" "]));
849
850 buffer.set_string(0, 0, "aaa", Style::default());
851 assert_eq!(buffer, Buffer::with_lines(["aaa "]));
852
853 buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
855 assert_eq!(buffer, Buffer::with_lines(["bbbb "]));
856
857 buffer.set_string(0, 0, "12345", Style::default());
858 assert_eq!(buffer, Buffer::with_lines(["12345"]));
859
860 buffer.set_string(0, 0, "123456", Style::default());
862 assert_eq!(buffer, Buffer::with_lines(["12345"]));
863
864 buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
866 buffer.set_string(0, 0, "12345", Style::default());
867 buffer.set_string(0, 1, "67890", Style::default());
868 assert_eq!(buffer, Buffer::with_lines(["12345", "67890"]));
869 }
870
871 #[test]
872 fn set_string_multi_width_overwrite() {
873 let area = Rect::new(0, 0, 5, 1);
874 let mut buffer = Buffer::empty(area);
875
876 buffer.set_string(0, 0, "aaaaa", Style::default());
878 buffer.set_string(0, 0, "称号", Style::default());
879 assert_eq!(buffer, Buffer::with_lines(["称号a"]));
880 }
881
882 #[test]
883 fn set_string_zero_width() {
884 assert_eq!("\u{200B}".width(), 0);
885
886 let area = Rect::new(0, 0, 1, 1);
887 let mut buffer = Buffer::empty(area);
888
889 let s = "\u{200B}a";
891 buffer.set_stringn(0, 0, s, 1, Style::default());
892 assert_eq!(buffer, Buffer::with_lines(["a"]));
893
894 let s = "a\u{200B}";
896 buffer.set_stringn(0, 0, s, 1, Style::default());
897 assert_eq!(buffer, Buffer::with_lines(["a"]));
898 }
899
900 #[test]
901 fn set_string_double_width() {
902 let area = Rect::new(0, 0, 5, 1);
903 let mut buffer = Buffer::empty(area);
904 buffer.set_string(0, 0, "コン", Style::default());
905 assert_eq!(buffer, Buffer::with_lines(["コン "]));
906
907 buffer.set_string(0, 0, "コンピ", Style::default());
909 assert_eq!(buffer, Buffer::with_lines(["コン "]));
910 }
911
912 #[fixture]
913 fn small_one_line_buffer() -> Buffer {
914 Buffer::empty(Rect::new(0, 0, 5, 1))
915 }
916
917 #[rstest]
918 #[case::empty("", " ")]
919 #[case::one("1", "1 ")]
920 #[case::full("12345", "12345")]
921 #[case::overflow("123456", "12345")]
922 fn set_line_raw(
923 mut small_one_line_buffer: Buffer,
924 #[case] content: &str,
925 #[case] expected: &str,
926 ) {
927 let line = Line::raw(content);
928 small_one_line_buffer.set_line(0, 0, &line, 5);
929
930 let mut expected_buffer = Buffer::empty(small_one_line_buffer.area);
933 expected_buffer.set_string(0, 0, expected, Style::default());
934 assert_eq!(small_one_line_buffer, expected_buffer);
935 }
936
937 #[rstest]
938 #[case::empty("", " ")]
939 #[case::one("1", "1 ")]
940 #[case::full("12345", "12345")]
941 #[case::overflow("123456", "12345")]
942 fn set_line_styled(
943 mut small_one_line_buffer: Buffer,
944 #[case] content: &str,
945 #[case] expected: &str,
946 ) {
947 let color = Color::Blue;
948 let line = Line::styled(content, color);
949 small_one_line_buffer.set_line(0, 0, &line, 5);
950
951 let actual_contents = small_one_line_buffer
953 .content
954 .iter()
955 .map(Cell::symbol)
956 .join("");
957 let actual_styles = small_one_line_buffer
958 .content
959 .iter()
960 .map(|c| c.fg)
961 .collect_vec();
962
963 let expected_styles = iter::repeat(color)
966 .take(content.len().min(5))
967 .chain(iter::repeat(Color::default()).take(5_usize.saturating_sub(content.len())))
968 .collect_vec();
969 assert_eq!(actual_contents, expected);
970 assert_eq!(actual_styles, expected_styles);
971 }
972
973 #[test]
974 fn set_style() {
975 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
976 buffer.set_style(Rect::new(0, 1, 5, 1), Style::new().red());
977 #[rustfmt::skip]
978 let expected = Buffer::with_lines([
979 "aaaaa".into(),
980 "bbbbb".red(),
981 "ccccc".into(),
982 ]);
983 assert_eq!(buffer, expected);
984 }
985
986 #[test]
987 fn set_style_does_not_panic_when_out_of_area() {
988 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
989 buffer.set_style(Rect::new(0, 1, 10, 3), Style::new().red());
990 #[rustfmt::skip]
991 let expected = Buffer::with_lines([
992 "aaaaa".into(),
993 "bbbbb".red(),
994 "ccccc".red(),
995 ]);
996 assert_eq!(buffer, expected);
997 }
998
999 #[test]
1000 fn with_lines() {
1001 #[rustfmt::skip]
1002 let buffer = Buffer::with_lines([
1003 "┌────────┐",
1004 "│コンピュ│",
1005 "│ーa 上で│",
1006 "└────────┘",
1007 ]);
1008 assert_eq!(buffer.area.x, 0);
1009 assert_eq!(buffer.area.y, 0);
1010 assert_eq!(buffer.area.width, 10);
1011 assert_eq!(buffer.area.height, 4);
1012 }
1013
1014 #[test]
1015 fn diff_empty_empty() {
1016 let area = Rect::new(0, 0, 40, 40);
1017 let prev = Buffer::empty(area);
1018 let next = Buffer::empty(area);
1019 let diff = prev.diff(&next);
1020 assert_eq!(diff, []);
1021 }
1022
1023 #[test]
1024 fn diff_empty_filled() {
1025 let area = Rect::new(0, 0, 40, 40);
1026 let prev = Buffer::empty(area);
1027 let next = Buffer::filled(area, Cell::new("a"));
1028 let diff = prev.diff(&next);
1029 assert_eq!(diff.len(), 40 * 40);
1030 }
1031
1032 #[test]
1033 fn diff_filled_filled() {
1034 let area = Rect::new(0, 0, 40, 40);
1035 let prev = Buffer::filled(area, Cell::new("a"));
1036 let next = Buffer::filled(area, Cell::new("a"));
1037 let diff = prev.diff(&next);
1038 assert_eq!(diff, []);
1039 }
1040
1041 #[test]
1042 fn diff_single_width() {
1043 let prev = Buffer::with_lines([
1044 " ",
1045 "┌Title─┐ ",
1046 "│ │ ",
1047 "│ │ ",
1048 "└──────┘ ",
1049 ]);
1050 let next = Buffer::with_lines([
1051 " ",
1052 "┌TITLE─┐ ",
1053 "│ │ ",
1054 "│ │ ",
1055 "└──────┘ ",
1056 ]);
1057 let diff = prev.diff(&next);
1058 assert_eq!(
1059 diff,
1060 [
1061 (2, 1, &Cell::new("I")),
1062 (3, 1, &Cell::new("T")),
1063 (4, 1, &Cell::new("L")),
1064 (5, 1, &Cell::new("E")),
1065 ]
1066 );
1067 }
1068
1069 #[test]
1070 fn diff_multi_width() {
1071 #[rustfmt::skip]
1072 let prev = Buffer::with_lines([
1073 "┌Title─┐ ",
1074 "└──────┘ ",
1075 ]);
1076 #[rustfmt::skip]
1077 let next = Buffer::with_lines([
1078 "┌称号──┐ ",
1079 "└──────┘ ",
1080 ]);
1081 let diff = prev.diff(&next);
1082 assert_eq!(
1083 diff,
1084 [
1085 (1, 0, &Cell::new("称")),
1086 (3, 0, &Cell::new("号")),
1088 (5, 0, &Cell::new("─")),
1090 ]
1091 );
1092 }
1093
1094 #[test]
1095 fn diff_multi_width_offset() {
1096 let prev = Buffer::with_lines(["┌称号──┐"]);
1097 let next = Buffer::with_lines(["┌─称号─┐"]);
1098
1099 let diff = prev.diff(&next);
1100 assert_eq!(
1101 diff,
1102 [
1103 (1, 0, &Cell::new("─")),
1104 (2, 0, &Cell::new("称")),
1105 (4, 0, &Cell::new("号")),
1106 ]
1107 );
1108 }
1109
1110 #[test]
1111 fn diff_skip() {
1112 let prev = Buffer::with_lines(["123"]);
1113 let mut next = Buffer::with_lines(["456"]);
1114 for i in 1..3 {
1115 next.content[i].set_skip(true);
1116 }
1117
1118 let diff = prev.diff(&next);
1119 assert_eq!(diff, [(0, 0, &Cell::new("4"))],);
1120 }
1121
1122 #[rstest]
1123 #[case(Rect::new(0, 0, 2, 2), Rect::new(0, 2, 2, 2), ["11", "11", "22", "22"])]
1124 #[case(Rect::new(2, 2, 2, 2), Rect::new(0, 0, 2, 2), ["22 ", "22 ", " 11", " 11"])]
1125 fn merge<'line, Lines>(#[case] one: Rect, #[case] two: Rect, #[case] expected: Lines)
1126 where
1127 Lines: IntoIterator,
1128 Lines::Item: Into<Line<'line>>,
1129 {
1130 let mut one = Buffer::filled(one, Cell::new("1"));
1131 let two = Buffer::filled(two, Cell::new("2"));
1132 one.merge(&two);
1133 assert_eq!(one, Buffer::with_lines(expected));
1134 }
1135
1136 #[test]
1137 fn merge_with_offset() {
1138 let mut one = Buffer::filled(
1139 Rect {
1140 x: 3,
1141 y: 3,
1142 width: 2,
1143 height: 2,
1144 },
1145 Cell::new("1"),
1146 );
1147 let two = Buffer::filled(
1148 Rect {
1149 x: 1,
1150 y: 1,
1151 width: 3,
1152 height: 4,
1153 },
1154 Cell::new("2"),
1155 );
1156 one.merge(&two);
1157 let mut expected = Buffer::with_lines(["222 ", "222 ", "2221", "2221"]);
1158 expected.area = Rect {
1159 x: 1,
1160 y: 1,
1161 width: 4,
1162 height: 4,
1163 };
1164 assert_eq!(one, expected);
1165 }
1166
1167 #[rstest]
1168 #[case(false, true, [false, false, true, true, true, true])]
1169 #[case(true, false, [true, true, false, false, false, false])]
1170 fn merge_skip(#[case] skip_one: bool, #[case] skip_two: bool, #[case] expected: [bool; 6]) {
1171 let mut one = {
1172 let area = Rect {
1173 x: 0,
1174 y: 0,
1175 width: 2,
1176 height: 2,
1177 };
1178 let mut cell = Cell::new("1");
1179 cell.skip = skip_one;
1180 Buffer::filled(area, cell)
1181 };
1182 let two = {
1183 let area = Rect {
1184 x: 0,
1185 y: 1,
1186 width: 2,
1187 height: 2,
1188 };
1189 let mut cell = Cell::new("2");
1190 cell.skip = skip_two;
1191 Buffer::filled(area, cell)
1192 };
1193 one.merge(&two);
1194 let skipped = one.content().iter().map(|c| c.skip).collect::<Vec<_>>();
1195 assert_eq!(skipped, expected);
1196 }
1197
1198 #[test]
1199 fn with_lines_accepts_into_lines() {
1200 use crate::style::Stylize;
1201 let mut buf = Buffer::empty(Rect::new(0, 0, 3, 2));
1202 buf.set_string(0, 0, "foo", Style::new().red());
1203 buf.set_string(0, 1, "bar", Style::new().blue());
1204 assert_eq!(buf, Buffer::with_lines(["foo".red(), "bar".blue()]));
1205 }
1206
1207 #[test]
1208 fn control_sequence_rendered_full() {
1209 let text = "I \x1b[0;36mwas\x1b[0m here!";
1210
1211 let mut buffer = Buffer::filled(Rect::new(0, 0, 25, 3), Cell::new("x"));
1212 buffer.set_string(1, 1, text, Style::new());
1213
1214 let expected = Buffer::with_lines([
1215 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1216 "xI [0;36mwas[0m here!xxxx",
1217 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1218 ]);
1219 assert_eq!(buffer, expected);
1220 }
1221
1222 #[test]
1223 fn control_sequence_rendered_partially() {
1224 let text = "I \x1b[0;36mwas\x1b[0m here!";
1225
1226 let mut buffer = Buffer::filled(Rect::new(0, 0, 11, 3), Cell::new("x"));
1227 buffer.set_string(1, 1, text, Style::new());
1228
1229 #[rustfmt::skip]
1230 let expected = Buffer::with_lines([
1231 "xxxxxxxxxxx",
1232 "xI [0;36mwa",
1233 "xxxxxxxxxxx",
1234 ]);
1235 assert_eq!(buffer, expected);
1236 }
1237
1238 #[rstest]
1242 #[case::shrug("🤷", "🤷xxxxx")]
1244 #[case::polarbear("🐻❄️", "🐻❄️xxxxx")]
1248 #[case::eye_speechbubble("👁️🗨️", "👁️🗨️xxxxx")]
1252 fn renders_emoji(#[case] input: &str, #[case] expected: &str) {
1253 use unicode_width::UnicodeWidthChar;
1254
1255 dbg!(input);
1256 dbg!(input.len());
1257 dbg!(input
1258 .graphemes(true)
1259 .map(|symbol| (symbol, symbol.escape_unicode().to_string(), symbol.width()))
1260 .collect::<Vec<_>>());
1261 dbg!(input
1262 .chars()
1263 .map(|char| (
1264 char,
1265 char.escape_unicode().to_string(),
1266 char.width(),
1267 char.is_control()
1268 ))
1269 .collect::<Vec<_>>());
1270
1271 let mut buffer = Buffer::filled(Rect::new(0, 0, 7, 1), Cell::new("x"));
1272 buffer.set_string(0, 0, input, Style::new());
1273
1274 let expected = Buffer::with_lines([expected]);
1275 assert_eq!(buffer, expected);
1276 }
1277}