1use alloc::vec;
2use alloc::vec::Vec;
3use core::ops::{Index, IndexMut};
4use core::{cmp, fmt};
5
6use unicode_segmentation::UnicodeSegmentation;
7
8use crate::buffer::{BufferDiff, Cell, CellWidth};
9use crate::layout::{Position, Rect};
10use crate::style::Style;
11use crate::text::{Line, Span};
12
13#[derive(Default, Clone, Eq, PartialEq, Hash)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67pub struct Buffer {
68 pub area: Rect,
70 pub content: Vec<Cell>,
73}
74
75impl Buffer {
76 #[must_use]
78 pub fn empty(area: Rect) -> Self {
79 Self::filled(area, Cell::EMPTY)
80 }
81
82 #[must_use]
84 pub fn filled(area: Rect, cell: Cell) -> Self {
85 let size = area.area() as usize;
86 let content = vec![cell; size];
87 Self { area, content }
88 }
89
90 #[must_use]
92 pub fn with_lines<'a, Iter>(lines: Iter) -> Self
93 where
94 Iter: IntoIterator,
95 Iter::Item: Into<Line<'a>>,
96 {
97 let lines = lines.into_iter().map(Into::into).collect::<Vec<_>>();
98 let height = lines.len() as u16;
99 let width = lines.iter().map(Line::width).max().unwrap_or_default() as u16;
100 let mut buffer = Self::empty(Rect::new(0, 0, width, height));
101 for (y, line) in lines.iter().enumerate() {
102 buffer.set_line(0, y as u16, line, width);
103 }
104 buffer
105 }
106
107 pub fn content(&self) -> &[Cell] {
109 &self.content
110 }
111
112 pub const fn area(&self) -> &Rect {
114 &self.area
115 }
116
117 #[track_caller]
129 #[deprecated = "use `Buffer[(x, y)]` instead. To avoid panicking, use `Buffer::cell((x, y))`. Both methods take `impl Into<Position>`."]
130 #[must_use]
131 pub fn get(&self, x: u16, y: u16) -> &Cell {
132 let i = self.index_of(x, y);
133 &self.content[i]
134 }
135
136 #[track_caller]
149 #[deprecated = "use `Buffer[(x, y)]` instead. To avoid panicking, use `Buffer::cell_mut((x, y))`. Both methods take `impl Into<Position>`."]
150 #[must_use]
151 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
152 let i = self.index_of(x, y);
153 &mut self.content[i]
154 }
155
156 #[must_use]
179 pub fn cell<P: Into<Position>>(&self, position: P) -> Option<&Cell> {
180 let position = position.into();
181 let index = self.index_of_opt(position)?;
182 self.content.get(index)
183 }
184
185 #[must_use]
210 pub fn cell_mut<P: Into<Position>>(&mut self, position: P) -> Option<&mut Cell> {
211 let position = position.into();
212 let index = self.index_of_opt(position)?;
213 self.content.get_mut(index)
214 }
215
216 #[track_caller]
248 #[must_use]
249 pub fn index_of(&self, x: u16, y: u16) -> usize {
250 self.index_of_opt(Position { x, y }).unwrap_or_else(|| {
251 panic!(
252 "index outside of buffer: the area is {area:?} but index is ({x}, {y})",
253 area = self.area,
254 )
255 })
256 }
257
258 #[must_use]
264 const fn index_of_opt(&self, position: Position) -> Option<usize> {
265 let area = self.area;
266 if !area.contains(position) {
267 return None;
268 }
269 let y = (position.y - self.area.y) as usize;
271 let x = (position.x - self.area.x) as usize;
272 let width = self.area.width as usize;
273 Some(y * width + x)
274 }
275
276 #[must_use]
309 pub fn pos_of(&self, index: usize) -> (u16, u16) {
310 debug_assert!(
311 index < self.content.len(),
312 "Trying to get the coords of a cell outside the buffer: i={index} len={}",
313 self.content.len()
314 );
315 let x = index % self.area.width as usize + self.area.x as usize;
316 let y = index / self.area.width as usize + self.area.y as usize;
317 (
318 u16::try_from(x).expect("x overflow. This should never happen as area.width is u16"),
319 u16::try_from(y).expect("y overflow. This should never happen as area.height is u16"),
320 )
321 }
322
323 pub fn set_string<T, S>(&mut self, x: u16, y: u16, string: T, style: S)
325 where
326 T: AsRef<str>,
327 S: Into<Style>,
328 {
329 self.set_stringn(x, y, string, usize::MAX, style);
330 }
331
332 pub fn set_stringn<T, S>(
337 &mut self,
338 mut x: u16,
339 y: u16,
340 string: T,
341 max_width: usize,
342 style: S,
343 ) -> (u16, u16)
344 where
345 T: AsRef<str>,
346 S: Into<Style>,
347 {
348 let max_width = max_width.try_into().unwrap_or(u16::MAX);
349 let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
350 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
351 .filter(|symbol| !symbol.contains(char::is_control))
352 .map(|symbol| (symbol, symbol.cell_width()))
353 .filter(|(_symbol, width)| *width > 0)
354 .map_while(|(symbol, width)| {
355 remaining_width = remaining_width.checked_sub(width)?;
356 Some((symbol, width))
357 });
358 let style = style.into();
359 for (symbol, width) in graphemes {
360 self[(x, y)].set_symbol(symbol).set_style(style);
361 let next_symbol = x + width;
362 x += 1;
363 while x < next_symbol {
365 self[(x, y)].reset();
366 x += 1;
367 }
368 }
369 (x, y)
370 }
371
372 pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, max_width: u16) -> (u16, u16) {
374 let mut remaining_width = max_width;
375 let mut x = x;
376 for span in line {
377 if remaining_width == 0 {
378 break;
379 }
380 let pos = self.set_stringn(
381 x,
382 y,
383 span.content.as_ref(),
384 remaining_width as usize,
385 line.style.patch(span.style),
386 );
387 let w = pos.0.saturating_sub(x);
388 x = pos.0;
389 remaining_width = remaining_width.saturating_sub(w);
390 }
391 (x, y)
392 }
393
394 pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, max_width: u16) -> (u16, u16) {
396 self.set_stringn(x, y, &span.content, max_width as usize, span.style)
397 }
398
399 pub fn set_style<S: Into<Style>>(&mut self, area: Rect, style: S) {
406 let style = style.into();
407 let area = self.area.intersection(area);
408 for y in area.top()..area.bottom() {
409 for x in area.left()..area.right() {
410 self[(x, y)].set_style(style);
411 }
412 }
413 }
414
415 pub fn resize(&mut self, area: Rect) {
418 let length = area.area() as usize;
419 if self.content.len() > length {
420 self.content.truncate(length);
421 } else {
422 self.content.resize(length, Cell::EMPTY);
423 }
424 self.area = area;
425 }
426
427 pub fn reset(&mut self) {
429 for cell in &mut self.content {
430 cell.reset();
431 }
432 }
433
434 pub fn merge(&mut self, other: &Self) {
436 let area = self.area.union(other.area);
437 self.content.resize(area.area() as usize, Cell::EMPTY);
438
439 let size = self.area.area() as usize;
441 for i in (0..size).rev() {
442 let (x, y) = self.pos_of(i);
443 let k = ((y - area.y) * area.width + x - area.x) as usize;
445 if i != k {
446 self.content[k] = self.content[i].clone();
447 self.content[i].reset();
448 }
449 }
450
451 let size = other.area.area() as usize;
454 for i in 0..size {
455 let (x, y) = other.pos_of(i);
456 let k = ((y - area.y) * area.width + x - area.x) as usize;
458 self.content[k] = other.content[i].clone();
459 }
460 self.area = area;
461 }
462
463 pub fn diff<'a>(&self, other: &'a Self) -> Vec<(u16, u16, &'a Cell)> {
472 self.diff_iter(other).collect()
473 }
474
475 pub fn diff_iter<'prev, 'next>(&'prev self, other: &'next Self) -> BufferDiff<'prev, 'next> {
507 BufferDiff::new(self, other)
508 }
509}
510
511impl<P: Into<Position>> Index<P> for Buffer {
512 type Output = Cell;
513
514 fn index(&self, position: P) -> &Self::Output {
535 let position = position.into();
536 let index = self.index_of(position.x, position.y);
537 &self.content[index]
538 }
539}
540
541impl<P: Into<Position>> IndexMut<P> for Buffer {
542 fn index_mut(&mut self, position: P) -> &mut Self::Output {
563 let position = position.into();
564 let index = self.index_of(position.x, position.y);
565 &mut self.content[index]
566 }
567}
568
569impl fmt::Debug for Buffer {
570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578 f.write_fmt(format_args!("Buffer {{\n area: {:?}", self.area))?;
579
580 if self.area.is_empty() {
581 return f.write_str("\n}");
582 }
583
584 f.write_str(",\n content: [\n")?;
585 let mut last_style = None;
586 let mut styles = vec![];
587 for (y, line) in self.content.chunks(self.area.width as usize).enumerate() {
588 let mut overwritten = vec![];
589 let mut skip: u16 = 0;
590 f.write_str(" \"")?;
591 for (x, c) in line.iter().enumerate() {
592 let sym = c.symbol();
593 if skip == 0 {
594 f.write_str(sym)?;
595 } else {
596 overwritten.push((x, sym));
597 }
598 skip = cmp::max(skip, c.cell_width()).saturating_sub(1);
599 #[cfg(feature = "underline-color")]
600 {
601 let style = (c.fg, c.bg, c.underline_color, c.modifier);
602 if last_style != Some(style) {
603 last_style = Some(style);
604 styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
605 }
606 }
607 #[cfg(not(feature = "underline-color"))]
608 {
609 let style = (c.fg, c.bg, c.modifier);
610 if last_style != Some(style) {
611 last_style = Some(style);
612 styles.push((x, y, c.fg, c.bg, c.modifier));
613 }
614 }
615 }
616 f.write_str("\",")?;
617 if !overwritten.is_empty() {
618 f.write_fmt(format_args!(
619 " // hidden by multi-width symbols: {overwritten:?}"
620 ))?;
621 }
622 f.write_str("\n")?;
623 }
624 f.write_str(" ],\n styles: [\n")?;
625 for s in styles {
626 #[cfg(feature = "underline-color")]
627 f.write_fmt(format_args!(
628 " x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
629 s.0, s.1, s.2, s.3, s.4, s.5
630 ))?;
631 #[cfg(not(feature = "underline-color"))]
632 f.write_fmt(format_args!(
633 " x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
634 s.0, s.1, s.2, s.3, s.4
635 ))?;
636 }
637 f.write_str(" ]\n}")?;
638 Ok(())
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 #[cfg(not(feature = "std"))]
645 extern crate std;
646
647 use alloc::format;
648 use alloc::string::{String, ToString};
649 use core::iter;
650 use core::num::NonZeroU16;
651 use std::{dbg, println};
652
653 use itertools::Itertools;
654 use rstest::{fixture, rstest};
655
656 use super::*;
657 use crate::buffer::CellDiffOption;
658 use crate::style::{Color, Modifier, Stylize};
659
660 #[test]
661 fn debug_empty_buffer() {
662 let buffer = Buffer::empty(Rect::ZERO);
663 let result = format!("{buffer:?}");
664 println!("{result}");
665 let expected = "Buffer {\n area: Rect { x: 0, y: 0, width: 0, height: 0 }\n}";
666 assert_eq!(result, expected);
667 }
668
669 #[cfg(feature = "underline-color")]
670 #[test]
671 fn debug_grapheme_override() {
672 let buffer = Buffer::with_lines(["a🦀b"]);
673 let result = format!("{buffer:?}");
674 println!("{result}");
675 let expected = indoc::indoc!(
676 r#"
677 Buffer {
678 area: Rect { x: 0, y: 0, width: 4, height: 1 },
679 content: [
680 "a🦀b", // hidden by multi-width symbols: [(2, " ")]
681 ],
682 styles: [
683 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
684 ]
685 }"#
686 );
687 assert_eq!(result, expected);
688 }
689
690 #[test]
691 fn debug_some_example() {
692 let mut buffer = Buffer::empty(Rect::new(0, 0, 12, 2));
693 buffer.set_string(0, 0, "Hello World!", Style::default());
694 buffer.set_string(
695 0,
696 1,
697 "G'day World!",
698 Style::default()
699 .fg(Color::Green)
700 .bg(Color::Yellow)
701 .add_modifier(Modifier::BOLD),
702 );
703 let result = format!("{buffer:?}");
704 println!("{result}");
705 #[cfg(feature = "underline-color")]
706 let expected = indoc::indoc!(
707 r#"
708 Buffer {
709 area: Rect { x: 0, y: 0, width: 12, height: 2 },
710 content: [
711 "Hello World!",
712 "G'day World!",
713 ],
714 styles: [
715 x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
716 x: 0, y: 1, fg: Green, bg: Yellow, underline: Reset, modifier: BOLD,
717 ]
718 }"#
719 );
720 #[cfg(not(feature = "underline-color"))]
721 let expected = indoc::indoc!(
722 r#"
723 Buffer {
724 area: Rect { x: 0, y: 0, width: 12, height: 2 },
725 content: [
726 "Hello World!",
727 "G'day World!",
728 ],
729 styles: [
730 x: 0, y: 0, fg: Reset, bg: Reset, modifier: NONE,
731 x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
732 ]
733 }"#
734 );
735
736 assert_eq!(result, expected);
737 }
738
739 #[test]
740 fn it_translates_to_and_from_coordinates() {
741 let rect = Rect::new(200, 100, 50, 80);
742 let buf = Buffer::empty(rect);
743
744 assert_eq!(buf.pos_of(0), (200, 100));
746 assert_eq!(buf.index_of(200, 100), 0);
747
748 assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
750 assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
751 }
752
753 #[test]
754 #[should_panic(expected = "outside the buffer")]
755 fn pos_of_panics_on_out_of_bounds() {
756 let rect = Rect::new(0, 0, 10, 10);
757 let buf = Buffer::empty(rect);
758
759 let _ = buf.pos_of(100);
761 }
762
763 #[rstest]
764 #[case::left(9, 10)]
765 #[case::top(10, 9)]
766 #[case::right(20, 10)]
767 #[case::bottom(10, 20)]
768 #[should_panic(
769 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
770 )]
771 fn index_of_panics_on_out_of_bounds(#[case] x: u16, #[case] y: u16) {
772 let _ = Buffer::empty(Rect::new(10, 10, 10, 10)).index_of(x, y);
773 }
774
775 #[test]
776 fn test_cell() {
777 let buf = Buffer::with_lines(["Hello", "World"]);
778
779 let mut expected = Cell::default();
780 expected.set_symbol("H");
781
782 assert_eq!(buf.cell((0, 0)), Some(&expected));
783 assert_eq!(buf.cell((10, 10)), None);
784 assert_eq!(buf.cell(Position::new(0, 0)), Some(&expected));
785 assert_eq!(buf.cell(Position::new(10, 10)), None);
786 }
787
788 #[test]
789 fn test_cell_mut() {
790 let mut buf = Buffer::with_lines(["Hello", "World"]);
791
792 let mut expected = Cell::default();
793 expected.set_symbol("H");
794
795 assert_eq!(buf.cell_mut((0, 0)), Some(&mut expected));
796 assert_eq!(buf.cell_mut((10, 10)), None);
797 assert_eq!(buf.cell_mut(Position::new(0, 0)), Some(&mut expected));
798 assert_eq!(buf.cell_mut(Position::new(10, 10)), None);
799 }
800
801 #[test]
802 fn index() {
803 let buf = Buffer::with_lines(["Hello", "World"]);
804
805 let mut expected = Cell::default();
806 expected.set_symbol("H");
807
808 assert_eq!(buf[(0, 0)], expected);
809 }
810
811 #[rstest]
812 #[case::left(9, 10)]
813 #[case::top(10, 9)]
814 #[case::right(20, 10)]
815 #[case::bottom(10, 20)]
816 #[should_panic(
817 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
818 )]
819 fn index_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
820 let rect = Rect::new(10, 10, 10, 10);
821 let buf = Buffer::empty(rect);
822 let _ = buf[(x, y)];
823 }
824
825 #[test]
826 fn index_mut() {
827 let mut buf = Buffer::with_lines(["Cat", "Dog"]);
828 buf[(0, 0)].set_symbol("B");
829 buf[Position::new(0, 1)].set_symbol("L");
830 assert_eq!(buf, Buffer::with_lines(["Bat", "Log"]));
831 }
832
833 #[rstest]
834 #[case::left(9, 10)]
835 #[case::top(10, 9)]
836 #[case::right(20, 10)]
837 #[case::bottom(10, 20)]
838 #[should_panic(
839 expected = "index outside of buffer: the area is Rect { x: 10, y: 10, width: 10, height: 10 } but index is"
840 )]
841 fn index_mut_out_of_bounds_panics(#[case] x: u16, #[case] y: u16) {
842 let mut buf = Buffer::empty(Rect::new(10, 10, 10, 10));
843 buf[(x, y)].set_symbol("A");
844 }
845
846 #[test]
847 fn set_string() {
848 let area = Rect::new(0, 0, 5, 1);
849 let mut buffer = Buffer::empty(area);
850
851 buffer.set_stringn(0, 0, "aaa", 0, Style::default());
853 assert_eq!(buffer, Buffer::with_lines([" "]));
854
855 buffer.set_string(0, 0, "aaa", Style::default());
856 assert_eq!(buffer, Buffer::with_lines(["aaa "]));
857
858 buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
860 assert_eq!(buffer, Buffer::with_lines(["bbbb "]));
861
862 buffer.set_string(0, 0, "12345", Style::default());
863 assert_eq!(buffer, Buffer::with_lines(["12345"]));
864
865 buffer.set_string(0, 0, "123456", Style::default());
867 assert_eq!(buffer, Buffer::with_lines(["12345"]));
868
869 buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
871 buffer.set_string(0, 0, "12345", Style::default());
872 buffer.set_string(0, 1, "67890", Style::default());
873 assert_eq!(buffer, Buffer::with_lines(["12345", "67890"]));
874 }
875
876 #[test]
877 fn set_string_multi_width_overwrite() {
878 let area = Rect::new(0, 0, 5, 1);
879 let mut buffer = Buffer::empty(area);
880
881 buffer.set_string(0, 0, "aaaaa", Style::default());
883 buffer.set_string(0, 0, "称号", Style::default());
884 assert_eq!(buffer, Buffer::with_lines(["称号a"]));
885 }
886
887 #[test]
888 fn set_string_zero_width() {
889 assert_eq!("\u{200B}".cell_width(), 0);
890
891 let area = Rect::new(0, 0, 1, 1);
892 let mut buffer = Buffer::empty(area);
893
894 let s = "\u{200B}a";
896 buffer.set_stringn(0, 0, s, 1, Style::default());
897 assert_eq!(buffer, Buffer::with_lines(["a"]));
898
899 let s = "a\u{200B}";
901 buffer.set_stringn(0, 0, s, 1, Style::default());
902 assert_eq!(buffer, Buffer::with_lines(["a"]));
903 }
904
905 #[test]
906 fn set_string_double_width() {
907 let area = Rect::new(0, 0, 5, 1);
908 let mut buffer = Buffer::empty(area);
909 buffer.set_string(0, 0, "コン", Style::default());
910 assert_eq!(buffer, Buffer::with_lines(["コン "]));
911
912 buffer.set_string(0, 0, "コンピ", Style::default());
914 assert_eq!(buffer, Buffer::with_lines(["コン "]));
915 }
916
917 #[test]
918 fn set_string_halfwidth_katakana_with_dakuten() {
919 let area = Rect::new(0, 0, 5, 1);
920
921 let mut buffer = Buffer::empty(area);
923 buffer.set_string(0, 0, "ガ", Style::default());
924 let mut expected = Buffer::empty(area);
925 expected.set_string(0, 0, "ガ", Style::default());
926 assert_eq!(buffer, expected);
927
928 let mut buffer = Buffer::empty(area);
930 buffer.set_string(0, 0, "カ", Style::default());
931 assert_eq!(buffer.content[0].symbol(), "カ");
932 assert_eq!(buffer.content[1].symbol(), " ");
933
934 let mut buffer = Buffer::empty(area);
936 buffer.set_string(0, 0, "ガ", Style::default());
937 assert_eq!(buffer.content[0].symbol(), "ガ");
939 assert_eq!(buffer.content[1].symbol(), " "); assert_eq!(buffer.content[2].symbol(), " ");
941
942 let mut buffer = Buffer::empty(area);
944 buffer.set_string(0, 0, "パ", Style::default());
945 assert_eq!(buffer.content[0].symbol(), "パ");
946 assert_eq!(buffer.content[1].symbol(), " "); assert_eq!(buffer.content[2].symbol(), " ");
948
949 let mut buffer = Buffer::empty(area);
951 buffer.set_string(0, 0, "ガギ", Style::default());
952 assert_eq!(buffer.content[0].symbol(), "ガ"); assert_eq!(buffer.content[1].symbol(), " "); assert_eq!(buffer.content[2].symbol(), "ギ"); assert_eq!(buffer.content[3].symbol(), " "); assert_eq!(buffer.content[4].symbol(), " ");
957
958 let mut buffer = Buffer::empty(area);
960 buffer.set_string(0, 0, "ガギグ", Style::default());
961 assert_eq!(buffer.content[0].symbol(), "ガ");
962 assert_eq!(buffer.content[1].symbol(), " ");
963 assert_eq!(buffer.content[2].symbol(), "ギ");
964 assert_eq!(buffer.content[3].symbol(), " ");
965 assert_eq!(buffer.content[4].symbol(), " ");
966 }
967
968 #[test]
969 fn set_string_combining_vs_halfwidth_dakuten() {
970 let area = Rect::new(0, 0, 5, 1);
971
972 let mut buffer1 = Buffer::empty(area);
974 let (x1, _) = buffer1.set_stringn(0, 0, "ガ", usize::MAX, Style::default());
975 assert_eq!(buffer1.content[0].symbol(), "ガ");
977 assert_eq!(buffer1.content[0].cell_width(), 1);
978 assert_eq!(x1, 1);
979
980 let mut buffer2 = Buffer::empty(area);
982 let (x2, _) = buffer2.set_stringn(0, 0, "ガ", usize::MAX, Style::default());
983 assert_eq!(buffer2.content[0].symbol(), "ガ");
985 assert_eq!(buffer2.content[0].cell_width(), 2);
986 assert_eq!(x2, 2);
987 }
988
989 #[fixture]
990 fn small_one_line_buffer() -> Buffer {
991 Buffer::empty(Rect::new(0, 0, 5, 1))
992 }
993
994 #[rstest]
995 #[case::empty("", " ")]
996 #[case::one("1", "1 ")]
997 #[case::full("12345", "12345")]
998 #[case::overflow("123456", "12345")]
999 fn set_line_raw(
1000 mut small_one_line_buffer: Buffer,
1001 #[case] content: &str,
1002 #[case] expected: &str,
1003 ) {
1004 let line = Line::raw(content);
1005 small_one_line_buffer.set_line(0, 0, &line, 5);
1006
1007 let mut expected_buffer = Buffer::empty(small_one_line_buffer.area);
1010 expected_buffer.set_string(0, 0, expected, Style::default());
1011 assert_eq!(small_one_line_buffer, expected_buffer);
1012 }
1013
1014 #[rstest]
1015 #[case::empty("", " ")]
1016 #[case::one("1", "1 ")]
1017 #[case::full("12345", "12345")]
1018 #[case::overflow("123456", "12345")]
1019 fn set_line_styled(
1020 mut small_one_line_buffer: Buffer,
1021 #[case] content: &str,
1022 #[case] expected: &str,
1023 ) {
1024 let color = Color::Blue;
1025 let line = Line::styled(content, color);
1026 small_one_line_buffer.set_line(0, 0, &line, 5);
1027
1028 let actual_contents = small_one_line_buffer
1030 .content
1031 .iter()
1032 .map(Cell::symbol)
1033 .join("");
1034 let actual_styles = small_one_line_buffer
1035 .content
1036 .iter()
1037 .map(|c| c.fg)
1038 .collect_vec();
1039
1040 let expected_styles = iter::repeat_n(color, content.len().min(5))
1043 .chain(iter::repeat_n(
1044 Color::default(),
1045 5_usize.saturating_sub(content.len()),
1046 ))
1047 .collect_vec();
1048 assert_eq!(actual_contents, expected);
1049 assert_eq!(actual_styles, expected_styles);
1050 }
1051
1052 #[test]
1053 fn set_style() {
1054 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
1055 buffer.set_style(Rect::new(0, 1, 5, 1), Style::new().red());
1056 #[rustfmt::skip]
1057 let expected = Buffer::with_lines([
1058 "aaaaa".into(),
1059 "bbbbb".red(),
1060 "ccccc".into(),
1061 ]);
1062 assert_eq!(buffer, expected);
1063 }
1064
1065 #[test]
1066 fn set_style_does_not_panic_when_out_of_area() {
1067 let mut buffer = Buffer::with_lines(["aaaaa", "bbbbb", "ccccc"]);
1068 buffer.set_style(Rect::new(0, 1, 10, 3), Style::new().red());
1069 #[rustfmt::skip]
1070 let expected = Buffer::with_lines([
1071 "aaaaa".into(),
1072 "bbbbb".red(),
1073 "ccccc".red(),
1074 ]);
1075 assert_eq!(buffer, expected);
1076 }
1077
1078 #[test]
1079 fn with_lines() {
1080 #[rustfmt::skip]
1081 let buffer = Buffer::with_lines([
1082 "┌────────┐",
1083 "│コンピュ│",
1084 "│ーa 上で│",
1085 "└────────┘",
1086 ]);
1087 assert_eq!(buffer.area.x, 0);
1088 assert_eq!(buffer.area.y, 0);
1089 assert_eq!(buffer.area.width, 10);
1090 assert_eq!(buffer.area.height, 4);
1091 }
1092
1093 #[test]
1094 fn diff_empty_empty() {
1095 let area = Rect::new(0, 0, 40, 40);
1096 let prev = Buffer::empty(area);
1097 let next = Buffer::empty(area);
1098 let diff = prev.diff(&next);
1099 assert_eq!(diff, []);
1100 }
1101
1102 #[test]
1103 fn diff_empty_filled() {
1104 let area = Rect::new(0, 0, 40, 40);
1105 let prev = Buffer::empty(area);
1106 let next = Buffer::filled(area, Cell::new("a"));
1107 let diff = prev.diff_iter(&next);
1108 assert_eq!(diff.count(), 40 * 40);
1109 }
1110
1111 #[test]
1112 fn diff_filled_filled() {
1113 let area = Rect::new(0, 0, 40, 40);
1114 let prev = Buffer::filled(area, Cell::new("a"));
1115 let next = Buffer::filled(area, Cell::new("a"));
1116 let diff = prev.diff(&next);
1117 assert_eq!(diff, []);
1118 }
1119
1120 #[test]
1121 fn diff_single_width() {
1122 let prev = Buffer::with_lines([
1123 " ",
1124 "┌Title─┐ ",
1125 "│ │ ",
1126 "│ │ ",
1127 "└──────┘ ",
1128 ]);
1129 let next = Buffer::with_lines([
1130 " ",
1131 "┌TITLE─┐ ",
1132 "│ │ ",
1133 "│ │ ",
1134 "└──────┘ ",
1135 ]);
1136 let diff = prev.diff(&next);
1137 assert_eq!(
1138 diff,
1139 [
1140 (2, 1, &Cell::new("I")),
1141 (3, 1, &Cell::new("T")),
1142 (4, 1, &Cell::new("L")),
1143 (5, 1, &Cell::new("E")),
1144 ]
1145 );
1146 }
1147
1148 #[test]
1149 fn diff_multi_width() {
1150 #[rustfmt::skip]
1151 let prev = Buffer::with_lines([
1152 "┌Title─┐ ",
1153 "└──────┘ ",
1154 ]);
1155 #[rustfmt::skip]
1156 let next = Buffer::with_lines([
1157 "┌称号──┐ ",
1158 "└──────┘ ",
1159 ]);
1160 let diff = prev.diff(&next);
1161 assert_eq!(
1162 diff,
1163 [
1164 (1, 0, &Cell::new("称")),
1165 (3, 0, &Cell::new("号")),
1167 (5, 0, &Cell::new("─")),
1169 ]
1170 );
1171 }
1172
1173 #[test]
1174 fn diff_multi_width_offset() {
1175 let prev = Buffer::with_lines(["┌称号──┐"]);
1176 let next = Buffer::with_lines(["┌─称号─┐"]);
1177
1178 let diff = prev.diff(&next);
1179 assert_eq!(
1180 diff,
1181 [
1182 (1, 0, &Cell::new("─")),
1183 (2, 0, &Cell::new("称")),
1184 (4, 0, &Cell::new("号")),
1185 ]
1186 );
1187 }
1188
1189 #[test]
1190 fn merge_diff_idempotent() {
1191 let mut prev = Buffer::with_lines(["123"]);
1192 let next = Buffer::with_lines(["456"]);
1193 prev.merge(&next);
1194
1195 let diff = prev.diff(&next);
1196 assert_eq!(diff, []);
1197 }
1198
1199 #[test]
1200 fn merge_diff_forcedwidth() {
1201 let mut prev = Buffer::with_lines(["123"]);
1202 let mut next = Buffer::empty(Rect::new(0, 0, 3, 1));
1203 next.cell_mut((0, 0))
1204 .unwrap()
1205 .set_symbol("456")
1206 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(3).unwrap()));
1207 prev.merge(&next);
1208
1209 let diff = prev.diff(&next);
1210 assert_eq!(diff, []);
1211 }
1212
1213 #[test]
1214 fn merge_diff_link() {
1215 let mut prev = Buffer::with_lines(["_".repeat(4)]);
1216 let mut next = Buffer::empty(Rect::new(0, 0, 4, 1));
1217 next.cell_mut((0, 0))
1219 .unwrap()
1220 .set_symbol("\x1b]8;;http://example.com\x1b\\link\x1b]8;;\x1b\\")
1221 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(4).unwrap()));
1222 prev.merge(&next);
1223
1224 let diff = prev.diff(&next);
1225 assert_eq!(diff, []);
1226 }
1227
1228 #[test]
1229 fn merge_diff_split_link() {
1230 let mut prev = Buffer::with_lines(["_".repeat(8)]);
1231 let mut next = Buffer::empty(Rect::new(0, 0, 8, 1));
1232 next.cell_mut((0, 0))
1234 .unwrap()
1235 .set_symbol("\x1b]8;;http://example.com\x1b\\🔗")
1236 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(2).unwrap()));
1237 next.cell_mut((2, 0)).unwrap().set_symbol("l");
1239 next.cell_mut((3, 0)).unwrap().set_symbol("i");
1240 next.cell_mut((4, 0)).unwrap().set_symbol("n");
1241 next.cell_mut((5, 0)).unwrap().set_symbol("k");
1242 next.cell_mut((7, 0))
1244 .unwrap()
1245 .set_symbol("🔗\x1b]8;;\x1b\\")
1246 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(2).unwrap()));
1247 prev.merge(&next);
1248
1249 let diff = prev.diff(&next);
1250 assert_eq!(diff, []);
1251 }
1252
1253 #[test]
1254 fn merge_diff_image_sequences() {
1255 let mut prev = Buffer::empty(Rect::new(0, 0, 20, 1));
1256
1257 let mut kitty_image_placeholder_start = String::new();
1258 kitty_image_placeholder_start.push_str("\x1b[s");
1264 kitty_image_placeholder_start.push_str("\x1b[38;2;0;0;0m");
1267 kitty_image_placeholder_start.push_str("\u{10EEEE}\u{305}\u{305}\u{305}");
1271
1272 prev.cell_mut((0, 0))
1273 .unwrap()
1274 .set_symbol(&kitty_image_placeholder_start)
1275 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(1).unwrap()));
1276
1277 prev.cell_mut((1, 0)).unwrap().set_char('\u{10EEEE}');
1279 prev.cell_mut((2, 0)).unwrap().set_char('\u{10EEEE}');
1280 prev.cell_mut((3, 0))
1283 .unwrap()
1284 .set_symbol("\u{10EEEE}\x1b[u")
1285 .set_diff_option(CellDiffOption::ForcedWidth(NonZeroU16::new(1).unwrap()));
1286
1287 let mut buffer = Buffer::filled(Rect::new(0, 0, 20, 1), Cell::new("x"));
1288 buffer.merge(&prev);
1289
1290 let diff = buffer.diff(&prev);
1291 assert_eq!(diff, []);
1292 }
1293
1294 #[test]
1295 fn diff_skip() {
1296 let prev = Buffer::with_lines(["123"]);
1297 let mut next = Buffer::with_lines(["456"]);
1298 for i in 1..3 {
1299 next.content[i].set_diff_option(CellDiffOption::Skip);
1300 }
1301
1302 let diff = prev.diff(&next);
1303 assert_eq!(diff, [(0, 0, &Cell::new("4"))],);
1304 }
1305
1306 #[rstest]
1307 #[case(Rect::new(0, 0, 2, 2), Rect::new(0, 2, 2, 2), ["11", "11", "22", "22"])]
1308 #[case(Rect::new(2, 2, 2, 2), Rect::new(0, 0, 2, 2), ["22 ", "22 ", " 11", " 11"])]
1309 fn merge<'line, Lines>(#[case] one: Rect, #[case] two: Rect, #[case] expected: Lines)
1310 where
1311 Lines: IntoIterator,
1312 Lines::Item: Into<Line<'line>>,
1313 {
1314 let mut one = Buffer::filled(one, Cell::new("1"));
1315 let two = Buffer::filled(two, Cell::new("2"));
1316 one.merge(&two);
1317 assert_eq!(one, Buffer::with_lines(expected));
1318 }
1319
1320 #[test]
1321 fn merge_with_offset() {
1322 let mut one = Buffer::filled(
1323 Rect {
1324 x: 3,
1325 y: 3,
1326 width: 2,
1327 height: 2,
1328 },
1329 Cell::new("1"),
1330 );
1331 let two = Buffer::filled(
1332 Rect {
1333 x: 1,
1334 y: 1,
1335 width: 3,
1336 height: 4,
1337 },
1338 Cell::new("2"),
1339 );
1340 one.merge(&two);
1341 let mut expected = Buffer::with_lines(["222 ", "222 ", "2221", "2221"]);
1342 expected.area = Rect {
1343 x: 1,
1344 y: 1,
1345 width: 4,
1346 height: 4,
1347 };
1348 assert_eq!(one, expected);
1349 }
1350
1351 #[rstest]
1352 #[case(CellDiffOption::None, CellDiffOption::Skip, [CellDiffOption::None, CellDiffOption::None, CellDiffOption::Skip, CellDiffOption::Skip, CellDiffOption::Skip, CellDiffOption::Skip])]
1353 #[case(CellDiffOption::Skip, CellDiffOption::None, [CellDiffOption::Skip, CellDiffOption::Skip, CellDiffOption::None, CellDiffOption::None, CellDiffOption::None, CellDiffOption::None])]
1354 fn merge_skip(
1355 #[case] skip_one: CellDiffOption,
1356 #[case] skip_two: CellDiffOption,
1357 #[case] expected: [CellDiffOption; 6],
1358 ) {
1359 let mut one = {
1360 let area = Rect {
1361 x: 0,
1362 y: 0,
1363 width: 2,
1364 height: 2,
1365 };
1366 let mut cell = Cell::new("1");
1367 cell.diff_option = skip_one;
1368 Buffer::filled(area, cell)
1369 };
1370 let two = {
1371 let area = Rect {
1372 x: 0,
1373 y: 1,
1374 width: 2,
1375 height: 2,
1376 };
1377 let mut cell = Cell::new("2");
1378 cell.diff_option = skip_two;
1379 Buffer::filled(area, cell)
1380 };
1381 one.merge(&two);
1382 let skipped = one
1383 .content()
1384 .iter()
1385 .map(|c| c.diff_option)
1386 .collect::<Vec<_>>();
1387 assert_eq!(skipped, expected);
1388 }
1389
1390 #[test]
1391 fn with_lines_accepts_into_lines() {
1392 use crate::style::Stylize;
1393 let mut buf = Buffer::empty(Rect::new(0, 0, 3, 2));
1394 buf.set_string(0, 0, "foo", Style::new().red());
1395 buf.set_string(0, 1, "bar", Style::new().blue());
1396 assert_eq!(buf, Buffer::with_lines(["foo".red(), "bar".blue()]));
1397 }
1398
1399 #[test]
1400 fn control_sequence_rendered_full() {
1401 let text = "I \x1b[0;36mwas\x1b[0m here!";
1402
1403 let mut buffer = Buffer::filled(Rect::new(0, 0, 25, 3), Cell::new("x"));
1404 buffer.set_string(1, 1, text, Style::new());
1405
1406 let expected = Buffer::with_lines([
1407 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1408 "xI [0;36mwas[0m here!xxxx",
1409 "xxxxxxxxxxxxxxxxxxxxxxxxx",
1410 ]);
1411 assert_eq!(buffer, expected);
1412 }
1413
1414 #[test]
1415 fn control_sequence_rendered_partially() {
1416 let text = "I \x1b[0;36mwas\x1b[0m here!";
1417
1418 let mut buffer = Buffer::filled(Rect::new(0, 0, 11, 3), Cell::new("x"));
1419 buffer.set_string(1, 1, text, Style::new());
1420
1421 #[rustfmt::skip]
1422 let expected = Buffer::with_lines([
1423 "xxxxxxxxxxx",
1424 "xI [0;36mwa",
1425 "xxxxxxxxxxx",
1426 ]);
1427 assert_eq!(buffer, expected);
1428 }
1429
1430 #[rstest]
1434 #[case::shrug("🤷", "🤷xxxxx")]
1436 #[case::polarbear("🐻❄️", "🐻❄️xxxxx")]
1440 #[case::eye_speechbubble("👁️🗨️", "👁️🗨️xxxxx")]
1444 #[case::keyboard_emoji("⌨️", "⌨️xxxxx")]
1447 fn renders_emoji(#[case] input: &str, #[case] expected: &str) {
1448 use unicode_width::UnicodeWidthChar;
1449
1450 dbg!(input);
1451 dbg!(input.len());
1452 dbg!(
1453 input
1454 .graphemes(true)
1455 .map(|symbol| (
1456 symbol,
1457 symbol.escape_unicode().to_string(),
1458 symbol.cell_width()
1459 ))
1460 .collect::<Vec<_>>()
1461 );
1462 dbg!(
1463 input
1464 .chars()
1465 .map(|char| (
1466 char,
1467 char.escape_unicode().to_string(),
1468 char.width(),
1469 char.is_control()
1470 ))
1471 .collect::<Vec<_>>()
1472 );
1473
1474 let mut buffer = Buffer::filled(Rect::new(0, 0, 7, 1), Cell::new("x"));
1475 buffer.set_string(0, 0, input, Style::new());
1476
1477 let expected = Buffer::with_lines([expected]);
1478 assert_eq!(buffer, expected);
1479 }
1480
1481 #[test]
1486 fn index_pos_of_u16_max() {
1487 let buffer = Buffer::empty(Rect::new(0, 0, 256, 256 + 1));
1488 assert_eq!(buffer.index_of(255, 255), 65535);
1489 assert_eq!(buffer.pos_of(65535), (255, 255));
1490
1491 assert_eq!(buffer.index_of(0, 256), 65536);
1492 assert_eq!(buffer.pos_of(65536), (0, 256)); assert_eq!(buffer.index_of(1, 256), 65537);
1495 assert_eq!(buffer.pos_of(65537), (1, 256)); assert_eq!(buffer.index_of(255, 256), 65791);
1498 assert_eq!(buffer.pos_of(65791), (255, 256)); }
1500
1501 #[test]
1502 fn diff_clears_trailing_cell_for_wide_grapheme() {
1503 let prev = Buffer::with_lines(["ab"]); assert_eq!(prev.area.width, 2);
1506
1507 let mut next = Buffer::with_lines([" "]); next.set_string(0, 0, "⌨️", Style::new());
1509
1510 let expected_next = Buffer::with_lines(["⌨️"]);
1513 assert_eq!(next, expected_next);
1514
1515 let diff = prev.diff(&next);
1519 assert!(
1520 diff.iter()
1521 .any(|(x, y, c)| *x == 0 && *y == 0 && c.symbol() == "⌨️")
1522 );
1523 assert!(
1526 diff.iter()
1527 .any(|(x, y, c)| *x == 1 && *y == 0 && c.symbol() == " ")
1528 );
1529 }
1530
1531 #[test]
1542 fn diff_ignores_style_only_changes_in_trailing_cells() {
1543 use crate::style::Color;
1544
1545 let mut prev = Buffer::empty(Rect::new(0, 0, 3, 1));
1547 prev.content[0].set_symbol(" ");
1548 prev.content[0].set_fg(Color::LightBlue); prev.content[1].set_symbol(" ");
1550 prev.content[1].set_fg(Color::LightBlue);
1551 prev.content[2].set_symbol("x");
1552
1553 let mut next = Buffer::empty(Rect::new(0, 0, 3, 1));
1556 next.content[0].set_symbol("⚠️"); next.content[0].set_fg(Color::Reset); next.content[1].set_symbol(" "); next.content[1].set_fg(Color::Reset);
1560 next.content[2].set_symbol("x");
1561
1562 let diff = prev.diff(&next);
1563
1564 assert!(
1566 diff.iter().any(|(x, y, _)| *x == 0 && *y == 0),
1567 "Diff should include first cell (0,0) because the symbol changed"
1568 );
1569
1570 assert!(
1574 !diff.iter().any(|(x, y, _)| *x == 1 && *y == 0),
1575 "Diff should not include trailing cell (1,0) when only style changed. \
1576 Found updates: {:?}",
1577 diff.iter()
1578 .map(|(x, y, c)| (x, y, c.symbol(), c.fg))
1579 .collect::<Vec<_>>()
1580 );
1581 }
1582}