1use alloc::vec;
2use alloc::vec::Vec;
3use core::ops::{Index, IndexMut};
4use core::{cmp, fmt};
5
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthStr;
8
9use crate::buffer::Cell;
10use crate::layout::{Position, Rect};
11use crate::style::Style;
12use crate::text::{Line, Span};
13
14#[derive(Default, Clone, Eq, PartialEq, Hash)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66pub struct Buffer {
67 pub area: Rect,
69 pub content: Vec<Cell>,
72}
73
74impl Buffer {
75 #[must_use]
77 pub fn empty(area: Rect) -> Self {
78 Self::filled(area, Cell::EMPTY)
79 }
80
81 #[must_use]
83 pub fn filled(area: Rect, cell: Cell) -> Self {
84 let size = area.area() as usize;
85 let content = vec![cell; size];
86 Self { area, content }
87 }
88
89 #[must_use]
91 pub fn with_lines<'a, Iter>(lines: Iter) -> Self
92 where
93 Iter: IntoIterator,
94 Iter::Item: Into<Line<'a>>,
95 {
96 let lines = lines.into_iter().map(Into::into).collect::<Vec<_>>();
97 let height = lines.len() as u16;
98 let width = lines.iter().map(Line::width).max().unwrap_or_default() as u16;
99 let mut buffer = Self::empty(Rect::new(0, 0, width, height));
100 for (y, line) in lines.iter().enumerate() {
101 buffer.set_line(0, y as u16, line, width);
102 }
103 buffer
104 }
105
106 pub fn content(&self) -> &[Cell] {
108 &self.content
109 }
110
111 pub const fn area(&self) -> &Rect {
113 &self.area
114 }
115
116 #[track_caller]
128 #[deprecated = "use `Buffer[(x, y)]` instead. To avoid panicking, use `Buffer::cell((x, y))`. Both methods take `impl Into<Position>`."]
129 #[must_use]
130 pub fn get(&self, x: u16, y: u16) -> &Cell {
131 let i = self.index_of(x, y);
132 &self.content[i]
133 }
134
135 #[track_caller]
148 #[deprecated = "use `Buffer[(x, y)]` instead. To avoid panicking, use `Buffer::cell_mut((x, y))`. Both methods take `impl Into<Position>`."]
149 #[must_use]
150 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
151 let i = self.index_of(x, y);
152 &mut self.content[i]
153 }
154
155 #[must_use]
178 pub fn cell<P: Into<Position>>(&self, position: P) -> Option<&Cell> {
179 let position = position.into();
180 let index = self.index_of_opt(position)?;
181 self.content.get(index)
182 }
183
184 #[must_use]
209 pub fn cell_mut<P: Into<Position>>(&mut self, position: P) -> Option<&mut Cell> {
210 let position = position.into();
211 let index = self.index_of_opt(position)?;
212 self.content.get_mut(index)
213 }
214
215 #[track_caller]
247 #[must_use]
248 pub fn index_of(&self, x: u16, y: u16) -> usize {
249 self.index_of_opt(Position { x, y }).unwrap_or_else(|| {
250 panic!(
251 "index outside of buffer: the area is {area:?} but index is ({x}, {y})",
252 area = self.area,
253 )
254 })
255 }
256
257 #[must_use]
263 const fn index_of_opt(&self, position: Position) -> Option<usize> {
264 let area = self.area;
265 if !area.contains(position) {
266 return None;
267 }
268 let y = (position.y - self.area.y) as usize;
270 let x = (position.x - self.area.x) as usize;
271 let width = self.area.width as usize;
272 Some(y * width + x)
273 }
274
275 #[must_use]
308 pub fn pos_of(&self, index: usize) -> (u16, u16) {
309 debug_assert!(
310 index < self.content.len(),
311 "Trying to get the coords of a cell outside the buffer: i={index} len={}",
312 self.content.len()
313 );
314 let x = index % self.area.width as usize + self.area.x as usize;
315 let y = index / self.area.width as usize + self.area.y as usize;
316 (
317 u16::try_from(x).expect("x overflow. This should never happen as area.width is u16"),
318 u16::try_from(y).expect("y overflow. This should never happen as area.height is u16"),
319 )
320 }
321
322 pub fn set_string<T, S>(&mut self, x: u16, y: u16, string: T, style: S)
324 where
325 T: AsRef<str>,
326 S: Into<Style>,
327 {
328 self.set_stringn(x, y, string, usize::MAX, style);
329 }
330
331 pub fn set_stringn<T, S>(
336 &mut self,
337 mut x: u16,
338 y: u16,
339 string: T,
340 max_width: usize,
341 style: S,
342 ) -> (u16, u16)
343 where
344 T: AsRef<str>,
345 S: Into<Style>,
346 {
347 let max_width = max_width.try_into().unwrap_or(u16::MAX);
348 let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
349 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
350 .filter(|symbol| !symbol.contains(char::is_control))
351 .map(|symbol| (symbol, symbol.width() as u16))
352 .filter(|(_symbol, width)| *width > 0)
353 .map_while(|(symbol, width)| {
354 remaining_width = remaining_width.checked_sub(width)?;
355 Some((symbol, width))
356 });
357 let style = style.into();
358 for (symbol, width) in graphemes {
359 self[(x, y)].set_symbol(symbol).set_style(style);
360 let next_symbol = x + width;
361 x += 1;
362 while x < next_symbol {
364 self[(x, y)].reset();
365 x += 1;
366 }
367 }
368 (x, y)
369 }
370
371 pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, max_width: u16) -> (u16, u16) {
373 let mut remaining_width = max_width;
374 let mut x = x;
375 for span in line {
376 if remaining_width == 0 {
377 break;
378 }
379 let pos = self.set_stringn(
380 x,
381 y,
382 span.content.as_ref(),
383 remaining_width as usize,
384 line.style.patch(span.style),
385 );
386 let w = pos.0.saturating_sub(x);
387 x = pos.0;
388 remaining_width = remaining_width.saturating_sub(w);
389 }
390 (x, y)
391 }
392
393 pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, max_width: u16) -> (u16, u16) {
395 self.set_stringn(x, y, &span.content, max_width as usize, span.style)
396 }
397
398 pub fn set_style<S: Into<Style>>(&mut self, area: Rect, style: S) {
405 let style = style.into();
406 let area = self.area.intersection(area);
407 for y in area.top()..area.bottom() {
408 for x in area.left()..area.right() {
409 self[(x, y)].set_style(style);
410 }
411 }
412 }
413
414 pub fn resize(&mut self, area: Rect) {
417 let length = area.area() as usize;
418 if self.content.len() > length {
419 self.content.truncate(length);
420 } else {
421 self.content.resize(length, Cell::EMPTY);
422 }
423 self.area = area;
424 }
425
426 pub fn reset(&mut self) {
428 for cell in &mut self.content {
429 cell.reset();
430 }
431 }
432
433 pub fn merge(&mut self, other: &Self) {
435 let area = self.area.union(other.area);
436 self.content.resize(area.area() as usize, Cell::EMPTY);
437
438 let size = self.area.area() as usize;
440 for i in (0..size).rev() {
441 let (x, y) = self.pos_of(i);
442 let k = ((y - area.y) * area.width + x - area.x) as usize;
444 if i != k {
445 self.content[k] = self.content[i].clone();
446 self.content[i].reset();
447 }
448 }
449
450 let size = other.area.area() as usize;
453 for i in 0..size {
454 let (x, y) = other.pos_of(i);
455 let k = ((y - area.y) * area.width + x - area.x) as usize;
457 self.content[k] = other.content[i].clone();
458 }
459 self.area = area;
460 }
461
462 pub fn diff<'a>(&self, other: &'a Self) -> Vec<(u16, u16, &'a Cell)> {
491 let previous_buffer = &self.content;
492 let next_buffer = &other.content;
493
494 let mut updates: Vec<(u16, u16, &Cell)> = vec![];
495 let mut invalidated: usize = 0;
497 let mut to_skip: usize = 0;
500 for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
501 if !current.skip && (current != previous || invalidated > 0) && to_skip == 0 {
502 let (x, y) = self.pos_of(i);
503 updates.push((x, y, &next_buffer[i]));
504
505 let symbol = current.symbol();
511 let cell_width = symbol.width();
512 let contains_vs16 = symbol.chars().any(|c| c == '\u{FE0F}');
517 if cell_width > 1 && contains_vs16 {
518 for k in 1..cell_width {
519 let j = i + k;
520 if j >= next_buffer.len() || j >= previous_buffer.len() {
522 break;
523 }
524 let prev_trailing = &previous_buffer[j];
525 let next_trailing = &next_buffer[j];
526 if !next_trailing.skip && prev_trailing != next_trailing {
527 let (tx, ty) = self.pos_of(j);
528 updates.push((tx, ty, next_trailing));
533 }
534 }
535 }
536 }
537
538 to_skip = current.symbol().width().saturating_sub(1);
539
540 let affected_width = cmp::max(current.symbol().width(), previous.symbol().width());
541 invalidated = cmp::max(affected_width, invalidated).saturating_sub(1);
542 }
543 updates
544 }
545}
546
547impl<P: Into<Position>> Index<P> for Buffer {
548 type Output = Cell;
549
550 fn index(&self, position: P) -> &Self::Output {
571 let position = position.into();
572 let index = self.index_of(position.x, position.y);
573 &self.content[index]
574 }
575}
576
577impl<P: Into<Position>> IndexMut<P> for Buffer {
578 fn index_mut(&mut self, position: P) -> &mut Self::Output {
599 let position = position.into();
600 let index = self.index_of(position.x, position.y);
601 &mut self.content[index]
602 }
603}
604
605impl fmt::Debug for Buffer {
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614 f.write_fmt(format_args!("Buffer {{\n area: {:?}", &self.area))?;
615
616 if self.area.is_empty() {
617 return f.write_str("\n}");
618 }
619
620 f.write_str(",\n content: [\n")?;
621 let mut last_style = None;
622 let mut styles = vec![];
623 for (y, line) in self.content.chunks(self.area.width as usize).enumerate() {
624 let mut overwritten = vec![];
625 let mut skip: usize = 0;
626 f.write_str(" \"")?;
627 for (x, c) in line.iter().enumerate() {
628 if skip == 0 {
629 f.write_str(c.symbol())?;
630 } else {
631 overwritten.push((x, c.symbol()));
632 }
633 skip = cmp::max(skip, c.symbol().width()).saturating_sub(1);
634 #[cfg(feature = "underline-color")]
635 {
636 let style = (c.fg, c.bg, c.underline_color, c.modifier);
637 if last_style != Some(style) {
638 last_style = Some(style);
639 styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
640 }
641 }
642 #[cfg(not(feature = "underline-color"))]
643 {
644 let style = (c.fg, c.bg, c.modifier);
645 if last_style != Some(style) {
646 last_style = Some(style);
647 styles.push((x, y, c.fg, c.bg, c.modifier));
648 }
649 }
650 }
651 f.write_str("\",")?;
652 if !overwritten.is_empty() {
653 f.write_fmt(format_args!(
654 " // hidden by multi-width symbols: {overwritten:?}"
655 ))?;
656 }
657 f.write_str("\n")?;
658 }
659 f.write_str(" ],\n styles: [\n")?;
660 for s in styles {
661 #[cfg(feature = "underline-color")]
662 f.write_fmt(format_args!(
663 " x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
664 s.0, s.1, s.2, s.3, s.4, s.5
665 ))?;
666 #[cfg(not(feature = "underline-color"))]
667 f.write_fmt(format_args!(
668 " x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
669 s.0, s.1, s.2, s.3, s.4
670 ))?;
671 }
672 f.write_str(" ]\n}")?;
673 Ok(())
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use alloc::format;
680 use alloc::string::ToString;
681 use core::iter;
682 use std::{dbg, println};
683
684 use itertools::Itertools;
685 use rstest::{fixture, rstest};
686
687 use super::*;
688 use crate::style::{Color, Modifier, Stylize};
689
690 #[test]
691 fn debug_empty_buffer() {
692 let buffer = Buffer::empty(Rect::ZERO);
693 let result = format!("{buffer:?}");
694 println!("{result}");
695 let expected = "Buffer {\n area: Rect { x: 0, y: 0, width: 0, height: 0 }\n}";
696 assert_eq!(result, expected);
697 }
698
699 #[cfg(feature = "underline-color")]
700 #[test]
701 fn debug_grapheme_override() {
702 let buffer = Buffer::with_lines(["a🦀b"]);
703 let result = format!("{buffer:?}");
704 println!("{result}");
705 let expected = indoc::indoc!(
706 r#"
707 Buffer {
708 area: Rect { x: 0, y: 0, width: 4, height: 1 },
709 content: [
710 "a🦀b", // hidden by multi-width symbols: [(2, " ")]
711 ],
712 styles: [
713 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
714 ]
715 }"#
716 );
717 assert_eq!(result, expected);
718 }
719
720 #[test]
721 fn debug_some_example() {
722 let mut buffer = Buffer::empty(Rect::new(0, 0, 12, 2));
723 buffer.set_string(0, 0, "Hello World!", Style::default());
724 buffer.set_string(
725 0,
726 1,
727 "G'day World!",
728 Style::default()
729 .fg(Color::Green)
730 .bg(Color::Yellow)
731 .add_modifier(Modifier::BOLD),
732 );
733 let result = format!("{buffer:?}");
734 println!("{result}");
735 #[cfg(feature = "underline-color")]
736 let expected = indoc::indoc!(
737 r#"
738 Buffer {
739 area: Rect { x: 0, y: 0, width: 12, height: 2 },
740 content: [
741 "Hello World!",
742 "G'day World!",
743 ],
744 styles: [
745 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
746 x: 0, y: 1, fg: Green, bg: Yellow, underline: Reset, modifier: BOLD,
747 ]
748 }"#
749 );
750 #[cfg(not(feature = "underline-color"))]
751 let expected = indoc::indoc!(
752 r#"
753 Buffer {
754 area: Rect { x: 0, y: 0, width: 12, height: 2 },
755 content: [
756 "Hello World!",
757 "G'day World!",
758 ],
759 styles: [
760 x: 0, y: 0, fg: Reset, bg: Reset, modifier: NONE,
761 x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
762 ]
763 }"#
764 );
765
766 assert_eq!(result, expected);
767 }
768
769 #[test]
770 fn it_translates_to_and_from_coordinates() {
771 let rect = Rect::new(200, 100, 50, 80);
772 let buf = Buffer::empty(rect);
773
774 assert_eq!(buf.pos_of(0), (200, 100));
776 assert_eq!(buf.index_of(200, 100), 0);
777
778 assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
780 assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
781 }
782
783 #[test]
784 #[should_panic(expected = "outside the buffer")]
785 fn pos_of_panics_on_out_of_bounds() {
786 let rect = Rect::new(0, 0, 10, 10);
787 let buf = Buffer::empty(rect);
788
789 let _ = buf.pos_of(100);
791 }
792
793 #[rstest]
794 #[case::left(9, 10)]
795 #[case::top(10, 9)]
796 #[case::right(20, 10)]
797 #[case::bottom(10, 20)]
798 #[should_panic(
799 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
800 )]
801 fn index_of_panics_on_out_of_bounds(#[case] x: u16, #[case] y: u16) {
802 let _ = Buffer::empty(Rect::new(10, 10, 10, 10)).index_of(x, y);
803 }
804
805 #[test]
806 fn test_cell() {
807 let buf = Buffer::with_lines(["Hello", "World"]);
808
809 let mut expected = Cell::default();
810 expected.set_symbol("H");
811
812 assert_eq!(buf.cell((0, 0)), Some(&expected));
813 assert_eq!(buf.cell((10, 10)), None);
814 assert_eq!(buf.cell(Position::new(0, 0)), Some(&expected));
815 assert_eq!(buf.cell(Position::new(10, 10)), None);
816 }
817
818 #[test]
819 fn test_cell_mut() {
820 let mut buf = Buffer::with_lines(["Hello", "World"]);
821
822 let mut expected = Cell::default();
823 expected.set_symbol("H");
824
825 assert_eq!(buf.cell_mut((0, 0)), Some(&mut expected));
826 assert_eq!(buf.cell_mut((10, 10)), None);
827 assert_eq!(buf.cell_mut(Position::new(0, 0)), Some(&mut expected));
828 assert_eq!(buf.cell_mut(Position::new(10, 10)), None);
829 }
830
831 #[test]
832 fn index() {
833 let buf = Buffer::with_lines(["Hello", "World"]);
834
835 let mut expected = Cell::default();
836 expected.set_symbol("H");
837
838 assert_eq!(buf[(0, 0)], expected);
839 }
840
841 #[rstest]
842 #[case::left(9, 10)]
843 #[case::top(10, 9)]
844 #[case::right(20, 10)]
845 #[case::bottom(10, 20)]
846 #[should_panic(
847 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
848 )]
849 fn index_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
850 let rect = Rect::new(10, 10, 10, 10);
851 let buf = Buffer::empty(rect);
852 let _ = buf[(x, y)];
853 }
854
855 #[test]
856 fn index_mut() {
857 let mut buf = Buffer::with_lines(["Cat", "Dog"]);
858 buf[(0, 0)].set_symbol("B");
859 buf[Position::new(0, 1)].set_symbol("L");
860 assert_eq!(buf, Buffer::with_lines(["Bat", "Log"]));
861 }
862
863 #[rstest]
864 #[case::left(9, 10)]
865 #[case::top(10, 9)]
866 #[case::right(20, 10)]
867 #[case::bottom(10, 20)]
868 #[should_panic(
869 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
870 )]
871 fn index_mut_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
872 let mut buf = Buffer::empty(Rect::new(10, 10, 10, 10));
873 buf[(x, y)].set_symbol("A");
874 }
875
876 #[test]
877 fn set_string() {
878 let area = Rect::new(0, 0, 5, 1);
879 let mut buffer = Buffer::empty(area);
880
881 buffer.set_stringn(0, 0, "aaa", 0, Style::default());
883 assert_eq!(buffer, Buffer::with_lines([" "]));
884
885 buffer.set_string(0, 0, "aaa", Style::default());
886 assert_eq!(buffer, Buffer::with_lines(["aaa "]));
887
888 buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
890 assert_eq!(buffer, Buffer::with_lines(["bbbb "]));
891
892 buffer.set_string(0, 0, "12345", Style::default());
893 assert_eq!(buffer, Buffer::with_lines(["12345"]));
894
895 buffer.set_string(0, 0, "123456", Style::default());
897 assert_eq!(buffer, Buffer::with_lines(["12345"]));
898
899 buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
901 buffer.set_string(0, 0, "12345", Style::default());
902 buffer.set_string(0, 1, "67890", Style::default());
903 assert_eq!(buffer, Buffer::with_lines(["12345", "67890"]));
904 }
905
906 #[test]
907 fn set_string_multi_width_overwrite() {
908 let area = Rect::new(0, 0, 5, 1);
909 let mut buffer = Buffer::empty(area);
910
911 buffer.set_string(0, 0, "aaaaa", Style::default());
913 buffer.set_string(0, 0, "称号", Style::default());
914 assert_eq!(buffer, Buffer::with_lines(["称号a"]));
915 }
916
917 #[test]
918 fn set_string_zero_width() {
919 assert_eq!("\u{200B}".width(), 0);
920
921 let area = Rect::new(0, 0, 1, 1);
922 let mut buffer = Buffer::empty(area);
923
924 let s = "\u{200B}a";
926 buffer.set_stringn(0, 0, s, 1, Style::default());
927 assert_eq!(buffer, Buffer::with_lines(["a"]));
928
929 let s = "a\u{200B}";
931 buffer.set_stringn(0, 0, s, 1, Style::default());
932 assert_eq!(buffer, Buffer::with_lines(["a"]));
933 }
934
935 #[test]
936 fn set_string_double_width() {
937 let area = Rect::new(0, 0, 5, 1);
938 let mut buffer = Buffer::empty(area);
939 buffer.set_string(0, 0, "コン", Style::default());
940 assert_eq!(buffer, Buffer::with_lines(["コン "]));
941
942 buffer.set_string(0, 0, "コンピ", Style::default());
944 assert_eq!(buffer, Buffer::with_lines(["コン "]));
945 }
946
947 #[fixture]
948 fn small_one_line_buffer() -> Buffer {
949 Buffer::empty(Rect::new(0, 0, 5, 1))
950 }
951
952 #[rstest]
953 #[case::empty("", " ")]
954 #[case::one("1", "1 ")]
955 #[case::full("12345", "12345")]
956 #[case::overflow("123456", "12345")]
957 fn set_line_raw(
958 mut small_one_line_buffer: Buffer,
959 #[case] content: &str,
960 #[case] expected: &str,
961 ) {
962 let line = Line::raw(content);
963 small_one_line_buffer.set_line(0, 0, &line, 5);
964
965 let mut expected_buffer = Buffer::empty(small_one_line_buffer.area);
968 expected_buffer.set_string(0, 0, expected, Style::default());
969 assert_eq!(small_one_line_buffer, expected_buffer);
970 }
971
972 #[rstest]
973 #[case::empty("", " ")]
974 #[case::one("1", "1 ")]
975 #[case::full("12345", "12345")]
976 #[case::overflow("123456", "12345")]
977 fn set_line_styled(
978 mut small_one_line_buffer: Buffer,
979 #[case] content: &str,
980 #[case] expected: &str,
981 ) {
982 let color = Color::Blue;
983 let line = Line::styled(content, color);
984 small_one_line_buffer.set_line(0, 0, &line, 5);
985
986 let actual_contents = small_one_line_buffer
988 .content
989 .iter()
990 .map(Cell::symbol)
991 .join("");
992 let actual_styles = small_one_line_buffer
993 .content
994 .iter()
995 .map(|c| c.fg)
996 .collect_vec();
997
998 let expected_styles = iter::repeat_n(color, content.len().min(5))
1001 .chain(iter::repeat_n(
1002 Color::default(),
1003 5_usize.saturating_sub(content.len()),
1004 ))
1005 .collect_vec();
1006 assert_eq!(actual_contents, expected);
1007 assert_eq!(actual_styles, expected_styles);
1008 }
1009
1010 #[test]
1011 fn set_style() {
1012 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
1013 buffer.set_style(Rect::new(0, 1, 5, 1), Style::new().red());
1014 #[rustfmt::skip]
1015 let expected = Buffer::with_lines([
1016 "aaaaa".into(),
1017 "bbbbb".red(),
1018 "ccccc".into(),
1019 ]);
1020 assert_eq!(buffer, expected);
1021 }
1022
1023 #[test]
1024 fn set_style_does_not_panic_when_out_of_area() {
1025 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
1026 buffer.set_style(Rect::new(0, 1, 10, 3), Style::new().red());
1027 #[rustfmt::skip]
1028 let expected = Buffer::with_lines([
1029 "aaaaa".into(),
1030 "bbbbb".red(),
1031 "ccccc".red(),
1032 ]);
1033 assert_eq!(buffer, expected);
1034 }
1035
1036 #[test]
1037 fn with_lines() {
1038 #[rustfmt::skip]
1039 let buffer = Buffer::with_lines([
1040 "┌────────┐",
1041 "│コンピュ│",
1042 "│ーa 上で│",
1043 "└────────┘",
1044 ]);
1045 assert_eq!(buffer.area.x, 0);
1046 assert_eq!(buffer.area.y, 0);
1047 assert_eq!(buffer.area.width, 10);
1048 assert_eq!(buffer.area.height, 4);
1049 }
1050
1051 #[test]
1052 fn diff_empty_empty() {
1053 let area = Rect::new(0, 0, 40, 40);
1054 let prev = Buffer::empty(area);
1055 let next = Buffer::empty(area);
1056 let diff = prev.diff(&next);
1057 assert_eq!(diff, []);
1058 }
1059
1060 #[test]
1061 fn diff_empty_filled() {
1062 let area = Rect::new(0, 0, 40, 40);
1063 let prev = Buffer::empty(area);
1064 let next = Buffer::filled(area, Cell::new("a"));
1065 let diff = prev.diff(&next);
1066 assert_eq!(diff.len(), 40 * 40);
1067 }
1068
1069 #[test]
1070 fn diff_filled_filled() {
1071 let area = Rect::new(0, 0, 40, 40);
1072 let prev = Buffer::filled(area, Cell::new("a"));
1073 let next = Buffer::filled(area, Cell::new("a"));
1074 let diff = prev.diff(&next);
1075 assert_eq!(diff, []);
1076 }
1077
1078 #[test]
1079 fn diff_single_width() {
1080 let prev = Buffer::with_lines([
1081 " ",
1082 "┌Title─┐ ",
1083 "│ │ ",
1084 "│ │ ",
1085 "└──────┘ ",
1086 ]);
1087 let next = Buffer::with_lines([
1088 " ",
1089 "┌TITLE─┐ ",
1090 "│ │ ",
1091 "│ │ ",
1092 "└──────┘ ",
1093 ]);
1094 let diff = prev.diff(&next);
1095 assert_eq!(
1096 diff,
1097 [
1098 (2, 1, &Cell::new("I")),
1099 (3, 1, &Cell::new("T")),
1100 (4, 1, &Cell::new("L")),
1101 (5, 1, &Cell::new("E")),
1102 ]
1103 );
1104 }
1105
1106 #[test]
1107 fn diff_multi_width() {
1108 #[rustfmt::skip]
1109 let prev = Buffer::with_lines([
1110 "┌Title─┐ ",
1111 "└──────┘ ",
1112 ]);
1113 #[rustfmt::skip]
1114 let next = Buffer::with_lines([
1115 "┌称号──┐ ",
1116 "└──────┘ ",
1117 ]);
1118 let diff = prev.diff(&next);
1119 assert_eq!(
1120 diff,
1121 [
1122 (1, 0, &Cell::new("称")),
1123 (3, 0, &Cell::new("号")),
1125 (5, 0, &Cell::new("─")),
1127 ]
1128 );
1129 }
1130
1131 #[test]
1132 fn diff_multi_width_offset() {
1133 let prev = Buffer::with_lines(["┌称号──┐"]);
1134 let next = Buffer::with_lines(["┌─称号─┐"]);
1135
1136 let diff = prev.diff(&next);
1137 assert_eq!(
1138 diff,
1139 [
1140 (1, 0, &Cell::new("─")),
1141 (2, 0, &Cell::new("称")),
1142 (4, 0, &Cell::new("号")),
1143 ]
1144 );
1145 }
1146
1147 #[test]
1148 fn diff_skip() {
1149 let prev = Buffer::with_lines(["123"]);
1150 let mut next = Buffer::with_lines(["456"]);
1151 for i in 1..3 {
1152 next.content[i].set_skip(true);
1153 }
1154
1155 let diff = prev.diff(&next);
1156 assert_eq!(diff, [(0, 0, &Cell::new("4"))],);
1157 }
1158
1159 #[rstest]
1160 #[case(Rect::new(0, 0, 2, 2), Rect::new(0, 2, 2, 2), ["11", "11", "22", "22"])]
1161 #[case(Rect::new(2, 2, 2, 2), Rect::new(0, 0, 2, 2), ["22 ", "22 ", " 11", " 11"])]
1162 fn merge<'line, Lines>(#[case] one: Rect, #[case] two: Rect, #[case] expected: Lines)
1163 where
1164 Lines: IntoIterator,
1165 Lines::Item: Into<Line<'line>>,
1166 {
1167 let mut one = Buffer::filled(one, Cell::new("1"));
1168 let two = Buffer::filled(two, Cell::new("2"));
1169 one.merge(&two);
1170 assert_eq!(one, Buffer::with_lines(expected));
1171 }
1172
1173 #[test]
1174 fn merge_with_offset() {
1175 let mut one = Buffer::filled(
1176 Rect {
1177 x: 3,
1178 y: 3,
1179 width: 2,
1180 height: 2,
1181 },
1182 Cell::new("1"),
1183 );
1184 let two = Buffer::filled(
1185 Rect {
1186 x: 1,
1187 y: 1,
1188 width: 3,
1189 height: 4,
1190 },
1191 Cell::new("2"),
1192 );
1193 one.merge(&two);
1194 let mut expected = Buffer::with_lines(["222 ", "222 ", "2221", "2221"]);
1195 expected.area = Rect {
1196 x: 1,
1197 y: 1,
1198 width: 4,
1199 height: 4,
1200 };
1201 assert_eq!(one, expected);
1202 }
1203
1204 #[rstest]
1205 #[case(false, true, [false, false, true, true, true, true])]
1206 #[case(true, false, [true, true, false, false, false, false])]
1207 fn merge_skip(#[case] skip_one: bool, #[case] skip_two: bool, #[case] expected: [bool; 6]) {
1208 let mut one = {
1209 let area = Rect {
1210 x: 0,
1211 y: 0,
1212 width: 2,
1213 height: 2,
1214 };
1215 let mut cell = Cell::new("1");
1216 cell.skip = skip_one;
1217 Buffer::filled(area, cell)
1218 };
1219 let two = {
1220 let area = Rect {
1221 x: 0,
1222 y: 1,
1223 width: 2,
1224 height: 2,
1225 };
1226 let mut cell = Cell::new("2");
1227 cell.skip = skip_two;
1228 Buffer::filled(area, cell)
1229 };
1230 one.merge(&two);
1231 let skipped = one.content().iter().map(|c| c.skip).collect::<Vec<_>>();
1232 assert_eq!(skipped, expected);
1233 }
1234
1235 #[test]
1236 fn with_lines_accepts_into_lines() {
1237 use crate::style::Stylize;
1238 let mut buf = Buffer::empty(Rect::new(0, 0, 3, 2));
1239 buf.set_string(0, 0, "foo", Style::new().red());
1240 buf.set_string(0, 1, "bar", Style::new().blue());
1241 assert_eq!(buf, Buffer::with_lines(["foo".red(), "bar".blue()]));
1242 }
1243
1244 #[test]
1245 fn control_sequence_rendered_full() {
1246 let text = "I \x1b[0;36mwas\x1b[0m here!";
1247
1248 let mut buffer = Buffer::filled(Rect::new(0, 0, 25, 3), Cell::new("x"));
1249 buffer.set_string(1, 1, text, Style::new());
1250
1251 let expected = Buffer::with_lines([
1252 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1253 "xI [0;36mwas[0m here!xxxx",
1254 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1255 ]);
1256 assert_eq!(buffer, expected);
1257 }
1258
1259 #[test]
1260 fn control_sequence_rendered_partially() {
1261 let text = "I \x1b[0;36mwas\x1b[0m here!";
1262
1263 let mut buffer = Buffer::filled(Rect::new(0, 0, 11, 3), Cell::new("x"));
1264 buffer.set_string(1, 1, text, Style::new());
1265
1266 #[rustfmt::skip]
1267 let expected = Buffer::with_lines([
1268 "xxxxxxxxxxx",
1269 "xI [0;36mwa",
1270 "xxxxxxxxxxx",
1271 ]);
1272 assert_eq!(buffer, expected);
1273 }
1274
1275 #[rstest]
1279 #[case::shrug("🤷", "🤷xxxxx")]
1281 #[case::polarbear("🐻❄️", "🐻❄️xxxxx")]
1285 #[case::eye_speechbubble("👁️🗨️", "👁️🗨️xxxxx")]
1289 #[case::keyboard_emoji("⌨️", "⌨️xxxxx")]
1292 fn renders_emoji(#[case] input: &str, #[case] expected: &str) {
1293 use unicode_width::UnicodeWidthChar;
1294
1295 dbg!(input);
1296 dbg!(input.len());
1297 dbg!(
1298 input
1299 .graphemes(true)
1300 .map(|symbol| (symbol, symbol.escape_unicode().to_string(), symbol.width()))
1301 .collect::<Vec<_>>()
1302 );
1303 dbg!(
1304 input
1305 .chars()
1306 .map(|char| (
1307 char,
1308 char.escape_unicode().to_string(),
1309 char.width(),
1310 char.is_control()
1311 ))
1312 .collect::<Vec<_>>()
1313 );
1314
1315 let mut buffer = Buffer::filled(Rect::new(0, 0, 7, 1), Cell::new("x"));
1316 buffer.set_string(0, 0, input, Style::new());
1317
1318 let expected = Buffer::with_lines([expected]);
1319 assert_eq!(buffer, expected);
1320 }
1321
1322 #[test]
1327 fn index_pos_of_u16_max() {
1328 let buffer = Buffer::empty(Rect::new(0, 0, 256, 256 + 1));
1329 assert_eq!(buffer.index_of(255, 255), 65535);
1330 assert_eq!(buffer.pos_of(65535), (255, 255));
1331
1332 assert_eq!(buffer.index_of(0, 256), 65536);
1333 assert_eq!(buffer.pos_of(65536), (0, 256)); assert_eq!(buffer.index_of(1, 256), 65537);
1336 assert_eq!(buffer.pos_of(65537), (1, 256)); assert_eq!(buffer.index_of(255, 256), 65791);
1339 assert_eq!(buffer.pos_of(65791), (255, 256)); }
1341
1342 #[test]
1343 fn diff_clears_trailing_cell_for_wide_grapheme() {
1344 let prev = Buffer::with_lines(["ab"]); assert_eq!(prev.area.width, 2);
1347
1348 let mut next = Buffer::with_lines([" "]); next.set_string(0, 0, "⌨️", Style::new());
1350
1351 let expected_next = Buffer::with_lines(["⌨️"]);
1354 assert_eq!(next, expected_next);
1355
1356 let diff = prev.diff(&next);
1360 assert!(
1361 diff.iter()
1362 .any(|(x, y, c)| *x == 0 && *y == 0 && c.symbol() == "⌨️")
1363 );
1364 assert!(
1367 diff.iter()
1368 .any(|(x, y, c)| *x == 1 && *y == 0 && c.symbol() == " ")
1369 );
1370 }
1371}