test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
use std::{
|
|
|
|
cmp::min,
|
|
|
|
fmt::{Debug, Formatter, Result},
|
|
|
|
};
|
2023-06-11 22:07:15 -07:00
|
|
|
|
2016-10-16 00:38:20 +02:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2018-12-14 17:33:05 +00:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
2016-10-16 00:38:20 +02:00
|
|
|
|
2023-06-11 22:07:15 -07:00
|
|
|
use crate::{
|
|
|
|
layout::Rect,
|
|
|
|
style::{Color, Modifier, Style},
|
2023-09-19 02:58:52 -07:00
|
|
|
text::{Line, Span},
|
2023-06-11 22:07:15 -07:00
|
|
|
};
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// A buffer cell
|
2023-08-11 10:16:48 +08:00
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
2023-08-22 14:51:06 +02:00
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
2016-10-26 14:32:45 +02:00
|
|
|
pub struct Cell {
|
2023-11-12 14:43:51 +09:00
|
|
|
#[deprecated(
|
|
|
|
since = "0.24.1",
|
|
|
|
note = "This field will be hidden at next major version. Use `Cell::symbol` method to get \
|
|
|
|
the value. Use `Cell::set_symbol` to update the field. Use `Cell::default` to \
|
|
|
|
create `Cell` instance"
|
|
|
|
)]
|
2016-10-26 19:19:46 +02:00
|
|
|
pub symbol: String,
|
2020-07-11 19:08:28 +02:00
|
|
|
pub fg: Color,
|
|
|
|
pub bg: Color,
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
pub underline_color: Color,
|
2020-07-11 19:08:28 +02:00
|
|
|
pub modifier: Modifier,
|
2023-08-25 10:20:36 +01:00
|
|
|
pub skip: bool,
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2023-11-12 14:43:51 +09:00
|
|
|
#[allow(deprecated)] // For Cell::symbol
|
2016-10-26 14:32:45 +02:00
|
|
|
impl Cell {
|
2023-11-12 14:43:51 +09:00
|
|
|
pub fn symbol(&self) -> &str {
|
|
|
|
self.symbol.as_str()
|
|
|
|
}
|
|
|
|
|
2016-11-06 18:49:57 +01:00
|
|
|
pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
|
|
|
|
self.symbol.clear();
|
|
|
|
self.symbol.push_str(symbol);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_char(&mut self, ch: char) -> &mut Cell {
|
|
|
|
self.symbol.clear();
|
|
|
|
self.symbol.push(ch);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_fg(&mut self, color: Color) -> &mut Cell {
|
2020-07-11 19:08:28 +02:00
|
|
|
self.fg = color;
|
2016-11-06 18:49:57 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_bg(&mut self, color: Color) -> &mut Cell {
|
2020-07-11 19:08:28 +02:00
|
|
|
self.bg = color;
|
2016-11-06 18:49:57 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_style(&mut self, style: Style) -> &mut Cell {
|
2020-07-11 19:08:28 +02:00
|
|
|
if let Some(c) = style.fg {
|
|
|
|
self.fg = c;
|
|
|
|
}
|
|
|
|
if let Some(c) = style.bg {
|
|
|
|
self.bg = c;
|
|
|
|
}
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
if let Some(c) = style.underline_color {
|
|
|
|
self.underline_color = c;
|
|
|
|
}
|
2020-07-11 19:08:28 +02:00
|
|
|
self.modifier.insert(style.add_modifier);
|
|
|
|
self.modifier.remove(style.sub_modifier);
|
2016-11-06 18:49:57 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
pub fn style(&self) -> Style {
|
|
|
|
Style::default()
|
|
|
|
.fg(self.fg)
|
|
|
|
.bg(self.bg)
|
|
|
|
.underline_color(self.underline_color)
|
|
|
|
.add_modifier(self.modifier)
|
|
|
|
}
|
|
|
|
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(not(feature = "underline-color"))]
|
2020-09-27 16:38:12 +02:00
|
|
|
pub fn style(&self) -> Style {
|
|
|
|
Style::default()
|
|
|
|
.fg(self.fg)
|
|
|
|
.bg(self.bg)
|
|
|
|
.add_modifier(self.modifier)
|
|
|
|
}
|
|
|
|
|
2023-08-25 10:20:36 +01:00
|
|
|
/// Sets the cell to be skipped when copying (diffing) the buffer to the screen.
|
|
|
|
///
|
|
|
|
/// This is helpful when it is necessary to prevent the buffer from overwriting a cell that is
|
|
|
|
/// covered by an image from some terminal graphics protocol (Sixel / iTerm / Kitty ...).
|
|
|
|
pub fn set_skip(&mut self, skip: bool) -> &mut Cell {
|
|
|
|
self.skip = skip;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn reset(&mut self) {
|
2016-10-26 19:19:46 +02:00
|
|
|
self.symbol.clear();
|
|
|
|
self.symbol.push(' ');
|
2020-07-11 19:08:28 +02:00
|
|
|
self.fg = Color::Reset;
|
|
|
|
self.bg = Color::Reset;
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
{
|
|
|
|
self.underline_color = Color::Reset;
|
|
|
|
}
|
2020-07-11 19:08:28 +02:00
|
|
|
self.modifier = Modifier::empty();
|
2023-08-25 10:20:36 +01:00
|
|
|
self.skip = false;
|
2016-10-26 14:32:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Cell {
|
|
|
|
fn default() -> Cell {
|
2023-11-12 14:43:51 +09:00
|
|
|
#[allow(deprecated)] // For Cell::symbol
|
2016-10-09 19:46:53 +02:00
|
|
|
Cell {
|
2016-10-26 14:32:45 +02:00
|
|
|
symbol: " ".into(),
|
2020-07-11 19:08:28 +02:00
|
|
|
fg: Color::Reset,
|
|
|
|
bg: Color::Reset,
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
underline_color: Color::Reset,
|
2020-07-11 19:08:28 +02:00
|
|
|
modifier: Modifier::empty(),
|
2023-08-25 10:20:36 +01:00
|
|
|
skip: false,
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// A buffer that maps to the desired content of the terminal after the draw call
|
|
|
|
///
|
|
|
|
/// No widget in the library interacts directly with the terminal. Instead each of them is required
|
|
|
|
/// to draw their state to an intermediate buffer. It is basically a grid where each cell contains
|
|
|
|
/// a grapheme, a foreground color and a background color. This grid will then be used to output
|
|
|
|
/// the appropriate escape sequences and characters to draw the UI as the user has defined it.
|
|
|
|
///
|
|
|
|
/// # Examples:
|
|
|
|
///
|
|
|
|
/// ```
|
2023-09-11 18:01:57 -07:00
|
|
|
/// use ratatui::{prelude::*, buffer::Cell};
|
2016-11-03 23:59:04 +01:00
|
|
|
///
|
|
|
|
/// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5});
|
2016-11-06 18:49:57 +01:00
|
|
|
/// buf.get_mut(0, 2).set_symbol("x");
|
2023-11-12 14:43:51 +09:00
|
|
|
/// assert_eq!(buf.get(0, 2).symbol(), "x");
|
2023-11-13 23:29:01 +09:00
|
|
|
///
|
2018-09-07 22:24:52 +02:00
|
|
|
/// buf.set_string(3, 0, "string", Style::default().fg(Color::Red).bg(Color::White));
|
2023-11-13 23:29:01 +09:00
|
|
|
/// let cell = buf.get_mut(5, 0);
|
|
|
|
/// assert_eq!(cell.symbol(), "r");
|
|
|
|
/// assert_eq!(cell.fg, Color::Red);
|
|
|
|
/// assert_eq!(cell.bg, Color::White);
|
|
|
|
///
|
2016-11-06 18:49:57 +01:00
|
|
|
/// buf.get_mut(5, 0).set_char('x');
|
2023-11-12 14:43:51 +09:00
|
|
|
/// assert_eq!(buf.get(5, 0).symbol(), "x");
|
2016-11-03 23:59:04 +01:00
|
|
|
/// ```
|
2023-08-11 10:16:48 +08:00
|
|
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
2023-08-22 14:51:06 +02:00
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
2016-10-26 14:32:45 +02:00
|
|
|
pub struct Buffer {
|
2016-11-03 23:59:04 +01:00
|
|
|
/// The area represented by this buffer
|
2016-10-26 14:32:45 +02:00
|
|
|
pub area: Rect,
|
2016-11-03 23:59:04 +01:00
|
|
|
/// The content of the buffer. The length of this Vec should always be equal to area.width *
|
|
|
|
/// area.height
|
2016-10-26 14:32:45 +02:00
|
|
|
pub content: Vec<Cell>,
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2016-10-26 14:32:45 +02:00
|
|
|
impl Buffer {
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Returns a Buffer with all cells set to the default one
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn empty(area: Rect) -> Buffer {
|
2023-05-21 23:45:37 -04:00
|
|
|
let cell = Cell::default();
|
2017-12-26 17:09:04 +01:00
|
|
|
Buffer::filled(area, &cell)
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Returns a Buffer with all cells initialized with the attributes of the given Cell
|
2017-12-26 17:09:04 +01:00
|
|
|
pub fn filled(area: Rect, cell: &Cell) -> Buffer {
|
2016-10-09 19:46:53 +02:00
|
|
|
let size = area.area() as usize;
|
|
|
|
let mut content = Vec::with_capacity(size);
|
|
|
|
for _ in 0..size {
|
|
|
|
content.push(cell.clone());
|
|
|
|
}
|
2018-08-13 00:27:56 +02:00
|
|
|
Buffer { area, content }
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2018-11-04 20:16:10 +01:00
|
|
|
/// Returns a Buffer containing the given lines
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
pub fn with_lines<'a, S>(lines: Vec<S>) -> Buffer
|
2018-11-04 20:16:10 +01:00
|
|
|
where
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
S: Into<Line<'a>>,
|
2018-11-04 20:16:10 +01:00
|
|
|
{
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
let lines = lines.into_iter().map(Into::into).collect::<Vec<_>>();
|
2018-11-04 20:16:10 +01:00
|
|
|
let height = lines.len() as u16;
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
let width = lines.iter().map(Line::width).max().unwrap_or_default() as u16;
|
|
|
|
let mut buffer = Buffer::empty(Rect::new(0, 0, width, height));
|
2019-02-03 22:07:58 +01:00
|
|
|
for (y, line) in lines.iter().enumerate() {
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
buffer.set_line(0, y as u16, line, width);
|
2018-11-04 20:16:10 +01:00
|
|
|
}
|
|
|
|
buffer
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Returns the content of the buffer as a slice
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn content(&self) -> &[Cell] {
|
2016-10-09 19:46:53 +02:00
|
|
|
&self.content
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Returns the area covered by this buffer
|
2016-10-09 19:46:53 +02:00
|
|
|
pub fn area(&self) -> &Rect {
|
|
|
|
&self.area
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Returns a reference to Cell at the given coordinates
|
2016-11-06 18:49:57 +01:00
|
|
|
pub fn get(&self, x: u16, y: u16) -> &Cell {
|
2016-10-26 14:32:45 +02:00
|
|
|
let i = self.index_of(x, y);
|
|
|
|
&self.content[i]
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2016-11-06 18:49:57 +01:00
|
|
|
/// Returns a mutable reference to Cell at the given coordinates
|
|
|
|
pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
|
|
|
|
let i = self.index_of(x, y);
|
|
|
|
&mut self.content[i]
|
|
|
|
}
|
|
|
|
|
2023-03-05 17:55:36 +05:30
|
|
|
/// Returns the index in the `Vec<Cell>` for the given global (x, y) coordinates.
|
2018-03-22 21:27:13 +01:00
|
|
|
///
|
|
|
|
/// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2023-09-11 18:01:57 -07:00
|
|
|
/// # use ratatui::prelude::*;
|
2018-03-22 21:27:13 +01:00
|
|
|
/// let rect = Rect::new(200, 100, 10, 10);
|
|
|
|
/// let buffer = Buffer::empty(rect);
|
|
|
|
/// // Global coordinates to the top corner of this buffer's area
|
|
|
|
/// assert_eq!(buffer.index_of(200, 100), 0);
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics when given an coordinate that is outside of this Buffer's area.
|
|
|
|
///
|
|
|
|
/// ```should_panic
|
2023-09-11 18:01:57 -07:00
|
|
|
/// # use ratatui::prelude::*;
|
2018-03-22 21:27:13 +01:00
|
|
|
/// let rect = Rect::new(200, 100, 10, 10);
|
|
|
|
/// let buffer = Buffer::empty(rect);
|
|
|
|
/// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
|
|
|
|
/// // starts at (200, 100).
|
|
|
|
/// buffer.index_of(0, 0); // Panics
|
|
|
|
/// ```
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn index_of(&self, x: u16, y: u16) -> usize {
|
2017-09-11 07:58:37 +02:00
|
|
|
debug_assert!(
|
2018-08-12 19:44:52 +02:00
|
|
|
x >= self.area.left()
|
|
|
|
&& x < self.area.right()
|
|
|
|
&& y >= self.area.top()
|
2017-10-30 22:28:18 +01:00
|
|
|
&& y < self.area.bottom(),
|
2023-05-21 23:46:02 -04:00
|
|
|
"Trying to access position outside the buffer: x={x}, y={y}, area={:?}",
|
2017-09-11 07:58:37 +02:00
|
|
|
self.area
|
|
|
|
);
|
2016-11-07 22:57:13 +01:00
|
|
|
((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2018-03-22 21:27:13 +01:00
|
|
|
/// Returns the (global) coordinates of a cell given its index
|
|
|
|
///
|
|
|
|
/// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2023-09-11 18:01:57 -07:00
|
|
|
/// # use ratatui::prelude::*;
|
2018-03-22 21:27:13 +01:00
|
|
|
/// let rect = Rect::new(200, 100, 10, 10);
|
|
|
|
/// let buffer = Buffer::empty(rect);
|
|
|
|
/// assert_eq!(buffer.pos_of(0), (200, 100));
|
|
|
|
/// assert_eq!(buffer.pos_of(14), (204, 101));
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics when given an index that is outside the Buffer's content.
|
|
|
|
///
|
|
|
|
/// ```should_panic
|
2023-09-11 18:01:57 -07:00
|
|
|
/// # use ratatui::prelude::*;
|
2018-03-22 21:27:13 +01:00
|
|
|
/// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
|
|
|
|
/// let buffer = Buffer::empty(rect);
|
|
|
|
/// // Index 100 is the 101th cell, which lies outside of the area of this Buffer.
|
|
|
|
/// buffer.pos_of(100); // Panics
|
|
|
|
/// ```
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn pos_of(&self, i: usize) -> (u16, u16) {
|
2017-09-11 07:58:37 +02:00
|
|
|
debug_assert!(
|
2018-03-22 21:27:13 +01:00
|
|
|
i < self.content.len(),
|
2023-05-21 23:46:02 -04:00
|
|
|
"Trying to get the coords of a cell outside the buffer: i={i} len={}",
|
2017-09-11 07:58:37 +02:00
|
|
|
self.content.len()
|
|
|
|
);
|
|
|
|
(
|
2023-05-21 23:45:37 -04:00
|
|
|
self.area.x + (i as u16) % self.area.width,
|
|
|
|
self.area.y + (i as u16) / self.area.width,
|
2017-09-11 07:58:37 +02:00
|
|
|
)
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Print a string, starting at the position (x, y)
|
2018-09-07 22:24:52 +02:00
|
|
|
pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
|
|
|
|
where
|
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
2016-11-06 18:49:57 +01:00
|
|
|
self.set_stringn(x, y, string, usize::MAX, style);
|
2016-10-22 12:51:41 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Print at most the first n characters of a string if enough space is available
|
|
|
|
/// until the end of the line
|
2019-12-15 21:38:18 +01:00
|
|
|
pub fn set_stringn<S>(
|
|
|
|
&mut self,
|
|
|
|
x: u16,
|
|
|
|
y: u16,
|
|
|
|
string: S,
|
|
|
|
width: usize,
|
|
|
|
style: Style,
|
|
|
|
) -> (u16, u16)
|
2018-09-07 22:24:52 +02:00
|
|
|
where
|
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
2016-10-26 14:32:45 +02:00
|
|
|
let mut index = self.index_of(x, y);
|
2018-12-14 17:33:05 +00:00
|
|
|
let mut x_offset = x as usize;
|
2018-09-07 22:24:52 +02:00
|
|
|
let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
|
2018-12-14 17:33:05 +00:00
|
|
|
let max_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
|
|
|
|
for s in graphemes {
|
|
|
|
let width = s.width();
|
2020-05-09 12:56:44 +02:00
|
|
|
if width == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
2018-12-14 17:33:05 +00:00
|
|
|
// `x_offset + width > max_offset` could be integer overflow on 32-bit machines if we
|
2023-02-14 11:26:07 +02:00
|
|
|
// change dimensions to usize or u32 and someone resizes the terminal to 1x2^32.
|
2018-12-14 17:33:05 +00:00
|
|
|
if width > max_offset.saturating_sub(x_offset) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.content[index].set_symbol(s);
|
|
|
|
self.content[index].set_style(style);
|
|
|
|
// Reset following cells if multi-width (they would be hidden by the grapheme),
|
|
|
|
for i in index + 1..index + width {
|
|
|
|
self.content[i].reset();
|
|
|
|
}
|
|
|
|
index += width;
|
|
|
|
x_offset += width;
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
2019-12-15 21:38:18 +01:00
|
|
|
(x_offset as u16, y)
|
|
|
|
}
|
|
|
|
|
2023-05-18 11:21:43 -07:00
|
|
|
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) {
|
|
|
|
let mut remaining_width = width;
|
|
|
|
let mut x = x;
|
|
|
|
for span in &line.spans {
|
|
|
|
if remaining_width == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let pos = self.set_stringn(
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
span.content.as_ref(),
|
2020-05-10 15:44:30 +02:00
|
|
|
remaining_width as usize,
|
2020-07-11 19:08:28 +02:00
|
|
|
span.style,
|
2020-05-10 15:44:30 +02:00
|
|
|
);
|
|
|
|
let w = pos.0.saturating_sub(x);
|
|
|
|
x = pos.0;
|
|
|
|
remaining_width = remaining_width.saturating_sub(w);
|
|
|
|
}
|
|
|
|
(x, y)
|
|
|
|
}
|
|
|
|
|
2023-02-15 18:29:50 +05:30
|
|
|
pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, width: u16) -> (u16, u16) {
|
2020-07-11 19:08:28 +02:00
|
|
|
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
|
2020-05-10 15:44:30 +02:00
|
|
|
}
|
|
|
|
|
2020-07-11 19:08:28 +02:00
|
|
|
#[deprecated(
|
|
|
|
since = "0.10.0",
|
|
|
|
note = "You should use styling capabilities of `Buffer::set_style`"
|
|
|
|
)]
|
2019-12-15 21:38:18 +01:00
|
|
|
pub fn set_background(&mut self, area: Rect, color: Color) {
|
|
|
|
for y in area.top()..area.bottom() {
|
|
|
|
for x in area.left()..area.right() {
|
|
|
|
self.get_mut(x, y).set_bg(color);
|
|
|
|
}
|
|
|
|
}
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
|
|
|
|
2020-07-11 19:08:28 +02:00
|
|
|
pub fn set_style(&mut self, area: Rect, style: Style) {
|
|
|
|
for y in area.top()..area.bottom() {
|
|
|
|
for x in area.left()..area.right() {
|
|
|
|
self.get_mut(x, y).set_style(style);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Resize the buffer so that the mapped area matches the given area and that the buffer
|
|
|
|
/// length is equal to area.width * area.height
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn resize(&mut self, area: Rect) {
|
|
|
|
let length = area.area() as usize;
|
|
|
|
if self.content.len() > length {
|
|
|
|
self.content.truncate(length);
|
|
|
|
} else {
|
2023-05-21 23:45:37 -04:00
|
|
|
self.content.resize(length, Cell::default());
|
2016-10-21 19:02:19 +02:00
|
|
|
}
|
2016-10-26 14:32:45 +02:00
|
|
|
self.area = area;
|
2016-10-21 19:02:19 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Reset all cells in the buffer
|
2016-10-26 14:32:45 +02:00
|
|
|
pub fn reset(&mut self) {
|
|
|
|
for c in &mut self.content {
|
|
|
|
c.reset();
|
2016-10-20 12:01:09 +02:00
|
|
|
}
|
2016-10-14 19:44:52 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:59:04 +01:00
|
|
|
/// Merge an other buffer into this one
|
2017-12-26 17:09:04 +01:00
|
|
|
pub fn merge(&mut self, other: &Buffer) {
|
2018-08-13 00:27:56 +02:00
|
|
|
let area = self.area.union(other.area);
|
2023-05-21 23:45:37 -04:00
|
|
|
let cell = Cell::default();
|
2016-10-09 19:46:53 +02:00
|
|
|
self.content.resize(area.area() as usize, cell.clone());
|
|
|
|
|
|
|
|
// Move original content to the appropriate space
|
|
|
|
let size = self.area.area() as usize;
|
|
|
|
for i in (0..size).rev() {
|
2016-10-26 14:32:45 +02:00
|
|
|
let (x, y) = self.pos_of(i);
|
2016-10-09 19:46:53 +02:00
|
|
|
// New index in content
|
2019-03-10 17:20:50 +01:00
|
|
|
let k = ((y - area.y) * area.width + x - area.x) as usize;
|
2016-10-09 19:46:53 +02:00
|
|
|
if i != k {
|
2019-03-10 17:20:50 +01:00
|
|
|
self.content[k] = self.content[i].clone();
|
2016-10-09 19:46:53 +02:00
|
|
|
self.content[i] = cell.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push content of the other buffer into this one (may erase previous
|
|
|
|
// data)
|
|
|
|
let size = other.area.area() as usize;
|
|
|
|
for i in 0..size {
|
2016-10-26 14:32:45 +02:00
|
|
|
let (x, y) = other.pos_of(i);
|
2016-10-09 19:46:53 +02:00
|
|
|
// New index in content
|
2019-03-10 17:20:50 +01:00
|
|
|
let k = ((y - area.y) * area.width + x - area.x) as usize;
|
2016-10-09 19:46:53 +02:00
|
|
|
self.content[k] = other.content[i].clone();
|
|
|
|
}
|
|
|
|
self.area = area;
|
|
|
|
}
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
/// Builds a minimal sequence of coordinates and Cells necessary to update the UI from
|
|
|
|
/// self to other.
|
|
|
|
///
|
|
|
|
/// We're assuming that buffers are well-formed, that is no double-width cell is followed by
|
|
|
|
/// a non-blank cell.
|
|
|
|
///
|
|
|
|
/// # Multi-width characters handling:
|
|
|
|
///
|
|
|
|
/// ```text
|
|
|
|
/// (Index:) `01`
|
|
|
|
/// Prev: `コ`
|
|
|
|
/// Next: `aa`
|
|
|
|
/// Updates: `0: a, 1: a'
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ```text
|
|
|
|
/// (Index:) `01`
|
|
|
|
/// Prev: `a `
|
|
|
|
/// Next: `コ`
|
|
|
|
/// Updates: `0: コ` (double width symbol at index 0 - skip index 1)
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ```text
|
|
|
|
/// (Index:) `012`
|
|
|
|
/// Prev: `aaa`
|
|
|
|
/// Next: `aコ`
|
|
|
|
/// Updates: `0: a, 1: コ` (double width symbol at index 1 - skip index 2)
|
|
|
|
/// ```
|
|
|
|
pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(u16, u16, &'a Cell)> {
|
|
|
|
let previous_buffer = &self.content;
|
|
|
|
let next_buffer = &other.content;
|
|
|
|
|
|
|
|
let mut updates: Vec<(u16, u16, &Cell)> = vec![];
|
2023-02-14 11:26:07 +02:00
|
|
|
// Cells invalidated by drawing/replacing preceding multi-width characters:
|
2018-12-14 17:33:05 +00:00
|
|
|
let mut invalidated: usize = 0;
|
2023-06-04 03:34:05 -07:00
|
|
|
// Cells from the current buffer to skip due to preceding multi-width characters taking
|
2023-08-25 10:20:36 +01:00
|
|
|
// their place (the skipped cells should be blank anyway), or due to per-cell-skipping:
|
2018-12-14 17:33:05 +00:00
|
|
|
let mut to_skip: usize = 0;
|
|
|
|
for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
|
2023-08-25 10:20:36 +01:00
|
|
|
if !current.skip && (current != previous || invalidated > 0) && to_skip == 0 {
|
2023-04-17 13:23:50 +01:00
|
|
|
let (x, y) = self.pos_of(i);
|
2018-12-14 17:33:05 +00:00
|
|
|
updates.push((x, y, &next_buffer[i]));
|
|
|
|
}
|
|
|
|
|
2023-11-12 14:43:51 +09:00
|
|
|
to_skip = current.symbol().width().saturating_sub(1);
|
2018-12-14 17:33:05 +00:00
|
|
|
|
2023-11-12 14:43:51 +09:00
|
|
|
let affected_width = std::cmp::max(current.symbol().width(), previous.symbol().width());
|
2018-12-14 17:33:05 +00:00
|
|
|
invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
|
|
|
|
}
|
|
|
|
updates
|
|
|
|
}
|
2016-10-09 19:46:53 +02:00
|
|
|
}
|
2018-03-22 21:27:13 +01:00
|
|
|
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
/// Assert that two buffers are equal by comparing their areas and content.
|
|
|
|
///
|
|
|
|
/// On panic, displays the areas or the content and a diff of the contents.
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! assert_buffer_eq {
|
|
|
|
($actual_expr:expr, $expected_expr:expr) => {
|
|
|
|
match (&$actual_expr, &$expected_expr) {
|
|
|
|
(actual, expected) => {
|
|
|
|
if actual.area != expected.area {
|
|
|
|
panic!(
|
|
|
|
indoc::indoc!(
|
|
|
|
"
|
|
|
|
buffer areas not equal
|
|
|
|
expected: {:?}
|
|
|
|
actual: {:?}"
|
|
|
|
),
|
|
|
|
expected, actual
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let diff = expected.diff(&actual);
|
|
|
|
if !diff.is_empty() {
|
|
|
|
let nice_diff = diff
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, (x, y, cell))| {
|
|
|
|
let expected_cell = expected.get(*x, *y);
|
2023-06-02 10:03:34 -04:00
|
|
|
indoc::formatdoc! {"
|
|
|
|
{i}: at ({x}, {y})
|
|
|
|
expected: {expected_cell:?}
|
|
|
|
actual: {cell:?}
|
|
|
|
"}
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
})
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join("\n");
|
|
|
|
panic!(
|
|
|
|
indoc::indoc!(
|
|
|
|
"
|
|
|
|
buffer contents not equal
|
|
|
|
expected: {:?}
|
|
|
|
actual: {:?}
|
|
|
|
diff:
|
|
|
|
{}"
|
|
|
|
),
|
|
|
|
expected, actual, nice_diff
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// shouldn't get here, but this guards against future behavior
|
|
|
|
// that changes equality but not area or content
|
|
|
|
assert_eq!(actual, expected, "buffers not equal");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for Buffer {
|
|
|
|
/// Writes a debug representation of the buffer to the given formatter.
|
|
|
|
///
|
|
|
|
/// The format is like a pretty printed struct, with the following fields:
|
2023-06-02 10:03:34 -04:00
|
|
|
/// * `area`: displayed as `Rect { x: 1, y: 2, width: 3, height: 4 }`
|
|
|
|
/// * `content`: displayed as a list of strings representing the content of the buffer
|
|
|
|
/// * `styles`: displayed as a list of: `{ x: 1, y: 2, fg: Color::Red, bg: Color::Blue,
|
2023-06-04 03:34:05 -07:00
|
|
|
/// modifier: Modifier::BOLD }` only showing a value when there is a change in style.
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
|
|
f.write_fmt(format_args!(
|
|
|
|
"Buffer {{\n area: {:?},\n content: [\n",
|
|
|
|
&self.area
|
|
|
|
))?;
|
|
|
|
let mut last_style = None;
|
|
|
|
let mut styles = vec![];
|
|
|
|
for (y, line) in self.content.chunks(self.area.width as usize).enumerate() {
|
|
|
|
let mut overwritten = vec![];
|
|
|
|
let mut skip: usize = 0;
|
|
|
|
f.write_str(" \"")?;
|
|
|
|
for (x, c) in line.iter().enumerate() {
|
|
|
|
if skip == 0 {
|
2023-11-12 14:43:51 +09:00
|
|
|
f.write_str(c.symbol())?;
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
} else {
|
2023-11-12 14:43:51 +09:00
|
|
|
overwritten.push((x, c.symbol()));
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
}
|
2023-11-12 14:43:51 +09:00
|
|
|
skip = std::cmp::max(skip, c.symbol().width()).saturating_sub(1);
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
{
|
|
|
|
let style = (c.fg, c.bg, c.underline_color, c.modifier);
|
|
|
|
if last_style != Some(style) {
|
|
|
|
last_style = Some(style);
|
|
|
|
styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
|
|
|
|
}
|
|
|
|
}
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(not(feature = "underline-color"))]
|
2023-07-15 11:57:15 +02:00
|
|
|
{
|
|
|
|
let style = (c.fg, c.bg, c.modifier);
|
|
|
|
if last_style != Some(style) {
|
|
|
|
last_style = Some(style);
|
|
|
|
styles.push((x, y, c.fg, c.bg, c.modifier));
|
|
|
|
}
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !overwritten.is_empty() {
|
|
|
|
f.write_fmt(format_args!(
|
2023-05-07 23:59:56 +02:00
|
|
|
"// hidden by multi-width symbols: {overwritten:?}"
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
))?;
|
|
|
|
}
|
|
|
|
f.write_str("\",\n")?;
|
|
|
|
}
|
|
|
|
f.write_str(" ],\n styles: [\n")?;
|
|
|
|
for s in styles {
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
f.write_fmt(format_args!(
|
|
|
|
" x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
|
|
|
|
s.0, s.1, s.2, s.3, s.4, s.5
|
|
|
|
))?;
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(not(feature = "underline-color"))]
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
f.write_fmt(format_args!(
|
|
|
|
" x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
|
|
|
|
s.0, s.1, s.2, s.3, s.4
|
|
|
|
))?;
|
|
|
|
}
|
|
|
|
f.write_str(" ]\n}")?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-22 21:27:13 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2018-12-14 17:33:05 +00:00
|
|
|
fn cell(s: &str) -> Cell {
|
|
|
|
let mut cell = Cell::default();
|
|
|
|
cell.set_symbol(s);
|
|
|
|
cell
|
|
|
|
}
|
|
|
|
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
#[test]
|
|
|
|
fn it_implements_debug() {
|
|
|
|
let mut buf = Buffer::empty(Rect::new(0, 0, 12, 2));
|
|
|
|
buf.set_string(0, 0, "Hello World!", Style::default());
|
|
|
|
buf.set_string(
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
"G'day World!",
|
|
|
|
Style::default()
|
|
|
|
.fg(Color::Green)
|
|
|
|
.bg(Color::Yellow)
|
|
|
|
.add_modifier(Modifier::BOLD),
|
|
|
|
);
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(feature = "underline-color")]
|
2023-07-15 11:57:15 +02:00
|
|
|
assert_eq!(
|
|
|
|
format!("{buf:?}"),
|
|
|
|
indoc::indoc!(
|
|
|
|
"
|
|
|
|
Buffer {
|
|
|
|
area: Rect { x: 0, y: 0, width: 12, height: 2 },
|
|
|
|
content: [
|
|
|
|
\"Hello World!\",
|
|
|
|
\"G'day World!\",
|
|
|
|
],
|
|
|
|
styles: [
|
|
|
|
x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
|
|
|
x: 0, y: 1, fg: Green, bg: Yellow, underline: Reset, modifier: BOLD,
|
|
|
|
]
|
|
|
|
}"
|
|
|
|
)
|
|
|
|
);
|
2023-10-18 13:52:43 -07:00
|
|
|
#[cfg(not(feature = "underline-color"))]
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_eq!(
|
2023-05-07 23:59:56 +02:00
|
|
|
format!("{buf:?}"),
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
indoc::indoc!(
|
|
|
|
"
|
|
|
|
Buffer {
|
|
|
|
area: Rect { x: 0, y: 0, width: 12, height: 2 },
|
|
|
|
content: [
|
|
|
|
\"Hello World!\",
|
|
|
|
\"G'day World!\",
|
|
|
|
],
|
|
|
|
styles: [
|
2023-06-01 06:37:59 -07:00
|
|
|
x: 0, y: 0, fg: Reset, bg: Reset, modifier: NONE,
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
|
|
|
|
]
|
|
|
|
}"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn assert_buffer_eq_does_not_panic_on_equal_buffers() {
|
|
|
|
let buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
|
|
let other_buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
|
|
assert_buffer_eq!(buffer, other_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[should_panic]
|
|
|
|
#[test]
|
|
|
|
fn assert_buffer_eq_panics_on_unequal_area() {
|
|
|
|
let buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
|
|
let other_buffer = Buffer::empty(Rect::new(0, 0, 6, 1));
|
|
|
|
assert_buffer_eq!(buffer, other_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[should_panic]
|
|
|
|
#[test]
|
|
|
|
fn assert_buffer_eq_panics_on_unequal_style() {
|
|
|
|
let buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
|
|
let mut other_buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
|
|
other_buffer.set_string(0, 0, " ", Style::default().fg(Color::Red));
|
|
|
|
assert_buffer_eq!(buffer, other_buffer);
|
|
|
|
}
|
|
|
|
|
2018-03-22 21:27:13 +01:00
|
|
|
#[test]
|
|
|
|
fn it_translates_to_and_from_coordinates() {
|
|
|
|
let rect = Rect::new(200, 100, 50, 80);
|
|
|
|
let buf = Buffer::empty(rect);
|
|
|
|
|
|
|
|
// First cell is at the upper left corner.
|
|
|
|
assert_eq!(buf.pos_of(0), (200, 100));
|
|
|
|
assert_eq!(buf.index_of(200, 100), 0);
|
|
|
|
|
|
|
|
// Last cell is in the lower right.
|
|
|
|
assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
|
|
|
|
assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "outside the buffer")]
|
|
|
|
fn pos_of_panics_on_out_of_bounds() {
|
|
|
|
let rect = Rect::new(0, 0, 10, 10);
|
|
|
|
let buf = Buffer::empty(rect);
|
|
|
|
|
|
|
|
// There are a total of 100 cells; zero-indexed means that 100 would be the 101st cell.
|
|
|
|
buf.pos_of(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "outside the buffer")]
|
|
|
|
fn index_of_panics_on_out_of_bounds() {
|
|
|
|
let rect = Rect::new(0, 0, 10, 10);
|
|
|
|
let buf = Buffer::empty(rect);
|
|
|
|
|
|
|
|
// width is 10; zero-indexed means that 10 would be the 11th cell.
|
|
|
|
buf.index_of(10, 0);
|
|
|
|
}
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_set_string() {
|
|
|
|
let area = Rect::new(0, 0, 5, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
|
|
|
|
|
|
|
// Zero-width
|
|
|
|
buffer.set_stringn(0, 0, "aaa", 0, Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec![" "]));
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
buffer.set_string(0, 0, "aaa", Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["aaa "]));
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
// Width limit:
|
|
|
|
buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["bbbb "]));
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
buffer.set_string(0, 0, "12345", Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["12345"]));
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
// Width truncation:
|
|
|
|
buffer.set_string(0, 0, "123456", Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["12345"]));
|
|
|
|
|
|
|
|
// multi-line
|
|
|
|
buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
|
|
|
|
buffer.set_string(0, 0, "12345", Style::default());
|
|
|
|
buffer.set_string(0, 1, "67890", Style::default());
|
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["12345", "67890"]));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_set_string_multi_width_overwrite() {
|
|
|
|
let area = Rect::new(0, 0, 5, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
|
|
|
|
|
|
|
// multi-width overwrite
|
|
|
|
buffer.set_string(0, 0, "aaaaa", Style::default());
|
|
|
|
buffer.set_string(0, 0, "称号", Style::default());
|
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["称号a"]));
|
2018-12-14 17:33:05 +00:00
|
|
|
}
|
|
|
|
|
2020-05-09 12:56:44 +02:00
|
|
|
#[test]
|
|
|
|
fn buffer_set_string_zero_width() {
|
|
|
|
let area = Rect::new(0, 0, 1, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
|
|
|
|
|
|
|
// Leading grapheme with zero width
|
|
|
|
let s = "\u{1}a";
|
|
|
|
buffer.set_stringn(0, 0, s, 1, Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["a"]));
|
2020-05-09 12:56:44 +02:00
|
|
|
|
|
|
|
// Trailing grapheme with zero with
|
|
|
|
let s = "a\u{1}";
|
|
|
|
buffer.set_stringn(0, 0, s, 1, Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["a"]));
|
2020-05-09 12:56:44 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 17:33:05 +00:00
|
|
|
#[test]
|
|
|
|
fn buffer_set_string_double_width() {
|
|
|
|
let area = Rect::new(0, 0, 5, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
|
|
|
buffer.set_string(0, 0, "コン", Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["コン "]));
|
2018-12-14 17:33:05 +00:00
|
|
|
|
|
|
|
// Only 1 space left.
|
|
|
|
buffer.set_string(0, 0, "コンピ", Style::default());
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["コン "]));
|
2018-12-14 17:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_with_lines() {
|
2019-11-05 08:08:14 +01:00
|
|
|
let buffer =
|
|
|
|
Buffer::with_lines(vec!["┌────────┐", "│コンピュ│", "│ーa 上で│", "└────────┘"]);
|
2018-12-14 17:33:05 +00:00
|
|
|
assert_eq!(buffer.area.x, 0);
|
|
|
|
assert_eq!(buffer.area.y, 0);
|
|
|
|
assert_eq!(buffer.area.width, 10);
|
|
|
|
assert_eq!(buffer.area.height, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_empty_empty() {
|
|
|
|
let area = Rect::new(0, 0, 40, 40);
|
|
|
|
let prev = Buffer::empty(area);
|
|
|
|
let next = Buffer::empty(area);
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(diff, vec![]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_empty_filled() {
|
|
|
|
let area = Rect::new(0, 0, 40, 40);
|
|
|
|
let prev = Buffer::empty(area);
|
|
|
|
let next = Buffer::filled(area, Cell::default().set_symbol("a"));
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(diff.len(), 40 * 40);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_filled_filled() {
|
|
|
|
let area = Rect::new(0, 0, 40, 40);
|
|
|
|
let prev = Buffer::filled(area, Cell::default().set_symbol("a"));
|
|
|
|
let next = Buffer::filled(area, Cell::default().set_symbol("a"));
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(diff, vec![]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_single_width() {
|
|
|
|
let prev = Buffer::with_lines(vec![
|
|
|
|
" ",
|
|
|
|
"┌Title─┐ ",
|
|
|
|
"│ │ ",
|
|
|
|
"│ │ ",
|
|
|
|
"└──────┘ ",
|
|
|
|
]);
|
|
|
|
let next = Buffer::with_lines(vec![
|
|
|
|
" ",
|
|
|
|
"┌TITLE─┐ ",
|
|
|
|
"│ │ ",
|
|
|
|
"│ │ ",
|
|
|
|
"└──────┘ ",
|
|
|
|
]);
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(
|
|
|
|
diff,
|
|
|
|
vec![
|
|
|
|
(2, 1, &cell("I")),
|
|
|
|
(3, 1, &cell("T")),
|
|
|
|
(4, 1, &cell("L")),
|
|
|
|
(5, 1, &cell("E")),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[rustfmt::skip]
|
|
|
|
fn buffer_diffing_multi_width() {
|
|
|
|
let prev = Buffer::with_lines(vec![
|
|
|
|
"┌Title─┐ ",
|
|
|
|
"└──────┘ ",
|
|
|
|
]);
|
|
|
|
let next = Buffer::with_lines(vec![
|
|
|
|
"┌称号──┐ ",
|
|
|
|
"└──────┘ ",
|
|
|
|
]);
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(
|
|
|
|
diff,
|
|
|
|
vec![
|
|
|
|
(1, 0, &cell("称")),
|
|
|
|
// Skipped "i"
|
|
|
|
(3, 0, &cell("号")),
|
|
|
|
// Skipped "l"
|
|
|
|
(5, 0, &cell("─")),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_multi_width_offset() {
|
|
|
|
let prev = Buffer::with_lines(vec!["┌称号──┐"]);
|
|
|
|
let next = Buffer::with_lines(vec!["┌─称号─┐"]);
|
|
|
|
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(
|
|
|
|
diff,
|
2019-11-05 08:08:14 +01:00
|
|
|
vec![(1, 0, &cell("─")), (2, 0, &cell("称")), (4, 0, &cell("号")),]
|
2018-12-14 17:33:05 +00:00
|
|
|
);
|
|
|
|
}
|
2019-03-10 17:20:50 +01:00
|
|
|
|
2023-08-25 10:20:36 +01:00
|
|
|
#[test]
|
|
|
|
fn buffer_diffing_skip() {
|
|
|
|
let prev = Buffer::with_lines(vec!["123"]);
|
|
|
|
let mut next = Buffer::with_lines(vec!["456"]);
|
|
|
|
for i in 1..3 {
|
|
|
|
next.content[i].set_skip(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
let diff = prev.diff(&next);
|
|
|
|
assert_eq!(diff, vec![(0, 0, &cell("4"))],);
|
|
|
|
}
|
|
|
|
|
2019-03-10 17:20:50 +01:00
|
|
|
#[test]
|
|
|
|
fn buffer_merge() {
|
|
|
|
let mut one = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("1"),
|
|
|
|
);
|
|
|
|
let two = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 2,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("2"),
|
|
|
|
);
|
|
|
|
one.merge(&two);
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(one, Buffer::with_lines(vec!["11", "11", "22", "22"]));
|
2019-03-10 17:20:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_merge2() {
|
|
|
|
let mut one = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 2,
|
|
|
|
y: 2,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("1"),
|
|
|
|
);
|
|
|
|
let two = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("2"),
|
|
|
|
);
|
|
|
|
one.merge(&two);
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(
|
2019-03-10 17:20:50 +01:00
|
|
|
one,
|
|
|
|
Buffer::with_lines(vec!["22 ", "22 ", " 11", " 11"])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_merge3() {
|
|
|
|
let mut one = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 3,
|
|
|
|
y: 3,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("1"),
|
|
|
|
);
|
|
|
|
let two = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 1,
|
|
|
|
y: 1,
|
|
|
|
width: 3,
|
|
|
|
height: 4,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("2"),
|
|
|
|
);
|
|
|
|
one.merge(&two);
|
|
|
|
let mut merged = Buffer::with_lines(vec!["222 ", "222 ", "2221", "2221"]);
|
|
|
|
merged.area = Rect {
|
|
|
|
x: 1,
|
|
|
|
y: 1,
|
|
|
|
width: 4,
|
|
|
|
height: 4,
|
|
|
|
};
|
test(buffer): add `assert_buffer_eq!` and Debug implementation (#161)
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests
2023-05-07 12:39:51 -07:00
|
|
|
assert_buffer_eq!(one, merged);
|
2019-03-10 17:20:50 +01:00
|
|
|
}
|
2023-08-25 10:20:36 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_merge_skip() {
|
|
|
|
let mut one = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("1"),
|
|
|
|
);
|
|
|
|
let two = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 1,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("2").set_skip(true),
|
|
|
|
);
|
|
|
|
one.merge(&two);
|
|
|
|
let skipped: Vec<bool> = one.content().iter().map(|c| c.skip).collect();
|
|
|
|
assert_eq!(skipped, vec![false, false, true, true, true, true]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn buffer_merge_skip2() {
|
|
|
|
let mut one = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("1").set_skip(true),
|
|
|
|
);
|
|
|
|
let two = Buffer::filled(
|
|
|
|
Rect {
|
|
|
|
x: 0,
|
|
|
|
y: 1,
|
|
|
|
width: 2,
|
|
|
|
height: 2,
|
|
|
|
},
|
|
|
|
Cell::default().set_symbol("2"),
|
|
|
|
);
|
|
|
|
one.merge(&two);
|
|
|
|
let skipped: Vec<bool> = one.content().iter().map(|c| c.skip).collect();
|
|
|
|
assert_eq!(skipped, vec![true, true, false, false, false, false]);
|
|
|
|
}
|
test(buffer): allow with_lines to accept Vec<Into<Line>> (#494)
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
2023-09-12 11:53:43 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn with_lines_accepts_into_lines() {
|
|
|
|
use crate::style::Stylize;
|
|
|
|
let mut buf = Buffer::empty(Rect::new(0, 0, 3, 2));
|
|
|
|
buf.set_string(0, 0, "foo", Style::new().red());
|
|
|
|
buf.set_string(0, 1, "bar", Style::new().blue());
|
|
|
|
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
|
|
|
|
}
|
2023-11-12 14:43:51 +09:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cell_symbol_field() {
|
|
|
|
let mut cell = Cell::default();
|
|
|
|
assert_eq!(cell.symbol(), " ");
|
|
|
|
cell.set_symbol("あ"); // Multi-byte character
|
|
|
|
assert_eq!(cell.symbol(), "あ");
|
|
|
|
cell.set_symbol("👨👩👧👦"); // Multiple code units combined with ZWJ
|
|
|
|
assert_eq!(cell.symbol(), "👨👩👧👦");
|
|
|
|
}
|
2018-03-22 21:27:13 +01:00
|
|
|
}
|