@akiojin/gwt 9.0.4 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +106 -146
- package/README.md +103 -143
- package/bin/gwt.cjs +1 -1
- package/package.json +5 -5
- package/rustfmt.toml +0 -2
- package/scripts/check-release-flow.sh +2 -8
- package/scripts/postinstall.js +17 -7
- package/scripts/run-local-backend-tests-on-commit.sh +6 -12
- package/scripts/test-all.sh +1 -5
- package/scripts/check-e2e-coverage-threshold.mjs +0 -238
- package/scripts/run-local-e2e-coverage-on-commit.sh +0 -69
- package/scripts/run-local-e2e-on-commit.sh +0 -60
- package/scripts/verify-ci-node-toolchain.sh +0 -76
- package/scripts/voice-eval.sh +0 -48
- package/vendor/ratatui-core/src/backend/test.rs +0 -1077
- package/vendor/ratatui-core/src/backend.rs +0 -405
- package/vendor/ratatui-core/src/buffer/assert.rs +0 -71
- package/vendor/ratatui-core/src/buffer/buffer.rs +0 -1388
- package/vendor/ratatui-core/src/buffer/cell.rs +0 -377
- package/vendor/ratatui-core/src/buffer.rs +0 -9
- package/vendor/ratatui-core/src/layout/alignment.rs +0 -89
- package/vendor/ratatui-core/src/layout/constraint.rs +0 -526
- package/vendor/ratatui-core/src/layout/direction.rs +0 -63
- package/vendor/ratatui-core/src/layout/flex.rs +0 -212
- package/vendor/ratatui-core/src/layout/layout.rs +0 -2838
- package/vendor/ratatui-core/src/layout/margin.rs +0 -79
- package/vendor/ratatui-core/src/layout/offset.rs +0 -66
- package/vendor/ratatui-core/src/layout/position.rs +0 -253
- package/vendor/ratatui-core/src/layout/rect/iter.rs +0 -356
- package/vendor/ratatui-core/src/layout/rect/ops.rs +0 -136
- package/vendor/ratatui-core/src/layout/rect.rs +0 -1114
- package/vendor/ratatui-core/src/layout/size.rs +0 -147
- package/vendor/ratatui-core/src/layout.rs +0 -333
- package/vendor/ratatui-core/src/lib.rs +0 -82
- package/vendor/ratatui-core/src/style/anstyle.rs +0 -348
- package/vendor/ratatui-core/src/style/color.rs +0 -788
- package/vendor/ratatui-core/src/style/palette/material.rs +0 -608
- package/vendor/ratatui-core/src/style/palette/tailwind.rs +0 -653
- package/vendor/ratatui-core/src/style/palette.rs +0 -6
- package/vendor/ratatui-core/src/style/palette_conversion.rs +0 -82
- package/vendor/ratatui-core/src/style/stylize.rs +0 -668
- package/vendor/ratatui-core/src/style.rs +0 -1069
- package/vendor/ratatui-core/src/symbols/bar.rs +0 -51
- package/vendor/ratatui-core/src/symbols/block.rs +0 -51
- package/vendor/ratatui-core/src/symbols/border.rs +0 -709
- package/vendor/ratatui-core/src/symbols/braille.rs +0 -21
- package/vendor/ratatui-core/src/symbols/half_block.rs +0 -3
- package/vendor/ratatui-core/src/symbols/line.rs +0 -259
- package/vendor/ratatui-core/src/symbols/marker.rs +0 -82
- package/vendor/ratatui-core/src/symbols/merge.rs +0 -748
- package/vendor/ratatui-core/src/symbols/pixel.rs +0 -30
- package/vendor/ratatui-core/src/symbols/scrollbar.rs +0 -46
- package/vendor/ratatui-core/src/symbols/shade.rs +0 -5
- package/vendor/ratatui-core/src/symbols.rs +0 -15
- package/vendor/ratatui-core/src/terminal/frame.rs +0 -192
- package/vendor/ratatui-core/src/terminal/terminal.rs +0 -926
- package/vendor/ratatui-core/src/terminal/viewport.rs +0 -58
- package/vendor/ratatui-core/src/terminal.rs +0 -40
- package/vendor/ratatui-core/src/text/grapheme.rs +0 -84
- package/vendor/ratatui-core/src/text/line.rs +0 -1678
- package/vendor/ratatui-core/src/text/masked.rs +0 -149
- package/vendor/ratatui-core/src/text/span.rs +0 -904
- package/vendor/ratatui-core/src/text/text.rs +0 -1434
- package/vendor/ratatui-core/src/text.rs +0 -64
- package/vendor/ratatui-core/src/widgets/stateful_widget.rs +0 -193
- package/vendor/ratatui-core/src/widgets/widget.rs +0 -174
- package/vendor/ratatui-core/src/widgets.rs +0 -9
|
@@ -1,1077 +0,0 @@
|
|
|
1
|
-
//! This module provides the `TestBackend` implementation for the [`Backend`] trait.
|
|
2
|
-
//! It is used in the integration tests to verify the correctness of the library.
|
|
3
|
-
|
|
4
|
-
use alloc::string::String;
|
|
5
|
-
use alloc::vec;
|
|
6
|
-
use core::fmt::{self, Write};
|
|
7
|
-
use core::iter;
|
|
8
|
-
|
|
9
|
-
use unicode_width::UnicodeWidthStr;
|
|
10
|
-
|
|
11
|
-
use crate::backend::{Backend, ClearType, WindowSize};
|
|
12
|
-
use crate::buffer::{Buffer, Cell};
|
|
13
|
-
use crate::layout::{Position, Rect, Size};
|
|
14
|
-
|
|
15
|
-
/// A [`Backend`] implementation used for integration testing that renders to an memory buffer.
|
|
16
|
-
///
|
|
17
|
-
/// Note: that although many of the integration and unit tests in ratatui are written using this
|
|
18
|
-
/// backend, it is preferable to write unit tests for widgets directly against the buffer rather
|
|
19
|
-
/// than using this backend. This backend is intended for integration tests that test the entire
|
|
20
|
-
/// terminal UI.
|
|
21
|
-
///
|
|
22
|
-
/// # Example
|
|
23
|
-
///
|
|
24
|
-
/// ```rust,ignore
|
|
25
|
-
/// use ratatui::backend::{Backend, TestBackend};
|
|
26
|
-
///
|
|
27
|
-
/// let mut backend = TestBackend::new(10, 2);
|
|
28
|
-
/// backend.clear()?;
|
|
29
|
-
/// backend.assert_buffer_lines([" "; 2]);
|
|
30
|
-
/// # Result::Ok(())
|
|
31
|
-
/// ```
|
|
32
|
-
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
33
|
-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
34
|
-
pub struct TestBackend {
|
|
35
|
-
buffer: Buffer,
|
|
36
|
-
scrollback: Buffer,
|
|
37
|
-
cursor: bool,
|
|
38
|
-
pos: (u16, u16),
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/// Returns a string representation of the given buffer for debugging purpose.
|
|
42
|
-
///
|
|
43
|
-
/// This function is used to visualize the buffer content in a human-readable format.
|
|
44
|
-
/// It iterates through the buffer content and appends each cell's symbol to the view string.
|
|
45
|
-
/// If a cell is hidden by a multi-width symbol, it is added to the overwritten vector and
|
|
46
|
-
/// displayed at the end of the line.
|
|
47
|
-
fn buffer_view(buffer: &Buffer) -> String {
|
|
48
|
-
let mut view = String::with_capacity(buffer.content.len() + buffer.area.height as usize * 3);
|
|
49
|
-
for cells in buffer.content.chunks(buffer.area.width as usize) {
|
|
50
|
-
let mut overwritten = vec![];
|
|
51
|
-
let mut skip: usize = 0;
|
|
52
|
-
view.push('"');
|
|
53
|
-
for (x, c) in cells.iter().enumerate() {
|
|
54
|
-
if skip == 0 {
|
|
55
|
-
view.push_str(c.symbol());
|
|
56
|
-
} else {
|
|
57
|
-
overwritten.push((x, c.symbol()));
|
|
58
|
-
}
|
|
59
|
-
skip = core::cmp::max(skip, c.symbol().width()).saturating_sub(1);
|
|
60
|
-
}
|
|
61
|
-
view.push('"');
|
|
62
|
-
if !overwritten.is_empty() {
|
|
63
|
-
write!(&mut view, " Hidden by multi-width symbols: {overwritten:?}").unwrap();
|
|
64
|
-
}
|
|
65
|
-
view.push('\n');
|
|
66
|
-
}
|
|
67
|
-
view
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
impl TestBackend {
|
|
71
|
-
/// Creates a new `TestBackend` with the specified width and height.
|
|
72
|
-
pub fn new(width: u16, height: u16) -> Self {
|
|
73
|
-
Self {
|
|
74
|
-
buffer: Buffer::empty(Rect::new(0, 0, width, height)),
|
|
75
|
-
scrollback: Buffer::empty(Rect::new(0, 0, width, 0)),
|
|
76
|
-
cursor: false,
|
|
77
|
-
pos: (0, 0),
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// Creates a new `TestBackend` with the specified lines as the initial screen state.
|
|
82
|
-
///
|
|
83
|
-
/// The backend's screen size is determined from the initial lines.
|
|
84
|
-
#[must_use]
|
|
85
|
-
pub fn with_lines<'line, Lines>(lines: Lines) -> Self
|
|
86
|
-
where
|
|
87
|
-
Lines: IntoIterator,
|
|
88
|
-
Lines::Item: Into<crate::text::Line<'line>>,
|
|
89
|
-
{
|
|
90
|
-
let buffer = Buffer::with_lines(lines);
|
|
91
|
-
let scrollback = Buffer::empty(Rect {
|
|
92
|
-
width: buffer.area.width,
|
|
93
|
-
..Rect::ZERO
|
|
94
|
-
});
|
|
95
|
-
Self {
|
|
96
|
-
buffer,
|
|
97
|
-
scrollback,
|
|
98
|
-
cursor: false,
|
|
99
|
-
pos: (0, 0),
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/// Returns a reference to the internal buffer of the `TestBackend`.
|
|
104
|
-
pub const fn buffer(&self) -> &Buffer {
|
|
105
|
-
&self.buffer
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/// Returns a reference to the internal scrollback buffer of the `TestBackend`.
|
|
109
|
-
///
|
|
110
|
-
/// The scrollback buffer represents the part of the screen that is currently hidden from view,
|
|
111
|
-
/// but that could be accessed by scrolling back in the terminal's history. This would normally
|
|
112
|
-
/// be done using the terminal's scrollbar or an equivalent keyboard shortcut.
|
|
113
|
-
///
|
|
114
|
-
/// The scrollback buffer starts out empty. Lines are appended when they scroll off the top of
|
|
115
|
-
/// the main buffer. This happens when lines are appended to the bottom of the main buffer
|
|
116
|
-
/// using [`Backend::append_lines`].
|
|
117
|
-
///
|
|
118
|
-
/// The scrollback buffer has a maximum height of [`u16::MAX`]. If lines are appended to the
|
|
119
|
-
/// bottom of the scrollback buffer when it is at its maximum height, a corresponding number of
|
|
120
|
-
/// lines will be removed from the top.
|
|
121
|
-
pub const fn scrollback(&self) -> &Buffer {
|
|
122
|
-
&self.scrollback
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/// Resizes the `TestBackend` to the specified width and height.
|
|
126
|
-
pub fn resize(&mut self, width: u16, height: u16) {
|
|
127
|
-
self.buffer.resize(Rect::new(0, 0, width, height));
|
|
128
|
-
let scrollback_height = self.scrollback.area.height;
|
|
129
|
-
self.scrollback
|
|
130
|
-
.resize(Rect::new(0, 0, width, scrollback_height));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/// Asserts that the `TestBackend`'s buffer is equal to the expected buffer.
|
|
134
|
-
///
|
|
135
|
-
/// This is a shortcut for `assert_eq!(self.buffer(), &expected)`.
|
|
136
|
-
///
|
|
137
|
-
/// # Panics
|
|
138
|
-
///
|
|
139
|
-
/// When they are not equal, a panic occurs with a detailed error message showing the
|
|
140
|
-
/// differences between the expected and actual buffers.
|
|
141
|
-
#[expect(deprecated)]
|
|
142
|
-
#[track_caller]
|
|
143
|
-
pub fn assert_buffer(&self, expected: &Buffer) {
|
|
144
|
-
// TODO: use assert_eq!()
|
|
145
|
-
crate::assert_buffer_eq!(&self.buffer, expected);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected buffer.
|
|
149
|
-
///
|
|
150
|
-
/// This is a shortcut for `assert_eq!(self.scrollback(), &expected)`.
|
|
151
|
-
///
|
|
152
|
-
/// # Panics
|
|
153
|
-
///
|
|
154
|
-
/// When they are not equal, a panic occurs with a detailed error message showing the
|
|
155
|
-
/// differences between the expected and actual buffers.
|
|
156
|
-
#[track_caller]
|
|
157
|
-
pub fn assert_scrollback(&self, expected: &Buffer) {
|
|
158
|
-
assert_eq!(&self.scrollback, expected);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/// Asserts that the `TestBackend`'s scrollback buffer is empty.
|
|
162
|
-
///
|
|
163
|
-
/// # Panics
|
|
164
|
-
///
|
|
165
|
-
/// When the scrollback buffer is not equal, a panic occurs with a detailed error message
|
|
166
|
-
/// showing the differences between the expected and actual buffers.
|
|
167
|
-
pub fn assert_scrollback_empty(&self) {
|
|
168
|
-
let expected = Buffer {
|
|
169
|
-
area: Rect {
|
|
170
|
-
width: self.scrollback.area.width,
|
|
171
|
-
..Rect::ZERO
|
|
172
|
-
},
|
|
173
|
-
content: vec![],
|
|
174
|
-
};
|
|
175
|
-
self.assert_scrollback(&expected);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/// Asserts that the `TestBackend`'s buffer is equal to the expected lines.
|
|
179
|
-
///
|
|
180
|
-
/// This is a shortcut for `assert_eq!(self.buffer(), &Buffer::with_lines(expected))`.
|
|
181
|
-
///
|
|
182
|
-
/// # Panics
|
|
183
|
-
///
|
|
184
|
-
/// When they are not equal, a panic occurs with a detailed error message showing the
|
|
185
|
-
/// differences between the expected and actual buffers.
|
|
186
|
-
#[track_caller]
|
|
187
|
-
pub fn assert_buffer_lines<'line, Lines>(&self, expected: Lines)
|
|
188
|
-
where
|
|
189
|
-
Lines: IntoIterator,
|
|
190
|
-
Lines::Item: Into<crate::text::Line<'line>>,
|
|
191
|
-
{
|
|
192
|
-
self.assert_buffer(&Buffer::with_lines(expected));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected lines.
|
|
196
|
-
///
|
|
197
|
-
/// This is a shortcut for `assert_eq!(self.scrollback(), &Buffer::with_lines(expected))`.
|
|
198
|
-
///
|
|
199
|
-
/// # Panics
|
|
200
|
-
///
|
|
201
|
-
/// When they are not equal, a panic occurs with a detailed error message showing the
|
|
202
|
-
/// differences between the expected and actual buffers.
|
|
203
|
-
#[track_caller]
|
|
204
|
-
pub fn assert_scrollback_lines<'line, Lines>(&self, expected: Lines)
|
|
205
|
-
where
|
|
206
|
-
Lines: IntoIterator,
|
|
207
|
-
Lines::Item: Into<crate::text::Line<'line>>,
|
|
208
|
-
{
|
|
209
|
-
self.assert_scrollback(&Buffer::with_lines(expected));
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/// Asserts that the `TestBackend`'s cursor position is equal to the expected one.
|
|
213
|
-
///
|
|
214
|
-
/// This is a shortcut for `assert_eq!(self.get_cursor_position().unwrap(), expected)`.
|
|
215
|
-
///
|
|
216
|
-
/// # Panics
|
|
217
|
-
///
|
|
218
|
-
/// When they are not equal, a panic occurs with a detailed error message showing the
|
|
219
|
-
/// differences between the expected and actual position.
|
|
220
|
-
#[track_caller]
|
|
221
|
-
pub fn assert_cursor_position<P: Into<Position>>(&mut self, position: P) {
|
|
222
|
-
let actual = self.get_cursor_position().unwrap();
|
|
223
|
-
assert_eq!(actual, position.into());
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
impl fmt::Display for TestBackend {
|
|
228
|
-
/// Formats the `TestBackend` for display by calling the `buffer_view` function
|
|
229
|
-
/// on its internal buffer.
|
|
230
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
231
|
-
write!(f, "{}", buffer_view(&self.buffer))
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
type Result<T, E = core::convert::Infallible> = core::result::Result<T, E>;
|
|
236
|
-
|
|
237
|
-
impl Backend for TestBackend {
|
|
238
|
-
type Error = core::convert::Infallible;
|
|
239
|
-
|
|
240
|
-
fn draw<'a, I>(&mut self, content: I) -> Result<()>
|
|
241
|
-
where
|
|
242
|
-
I: Iterator<Item = (u16, u16, &'a Cell)>,
|
|
243
|
-
{
|
|
244
|
-
for (x, y, c) in content {
|
|
245
|
-
self.buffer[(x, y)] = c.clone();
|
|
246
|
-
}
|
|
247
|
-
Ok(())
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
fn hide_cursor(&mut self) -> Result<()> {
|
|
251
|
-
self.cursor = false;
|
|
252
|
-
Ok(())
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
fn show_cursor(&mut self) -> Result<()> {
|
|
256
|
-
self.cursor = true;
|
|
257
|
-
Ok(())
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
fn get_cursor_position(&mut self) -> Result<Position> {
|
|
261
|
-
Ok(self.pos.into())
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> Result<()> {
|
|
265
|
-
self.pos = position.into().into();
|
|
266
|
-
Ok(())
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
fn clear(&mut self) -> Result<()> {
|
|
270
|
-
self.buffer.reset();
|
|
271
|
-
Ok(())
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
fn clear_region(&mut self, clear_type: ClearType) -> Result<()> {
|
|
275
|
-
let region = match clear_type {
|
|
276
|
-
ClearType::All => return self.clear(),
|
|
277
|
-
ClearType::AfterCursor => {
|
|
278
|
-
let index = self.buffer.index_of(self.pos.0, self.pos.1) + 1;
|
|
279
|
-
&mut self.buffer.content[index..]
|
|
280
|
-
}
|
|
281
|
-
ClearType::BeforeCursor => {
|
|
282
|
-
let index = self.buffer.index_of(self.pos.0, self.pos.1);
|
|
283
|
-
&mut self.buffer.content[..index]
|
|
284
|
-
}
|
|
285
|
-
ClearType::CurrentLine => {
|
|
286
|
-
let line_start_index = self.buffer.index_of(0, self.pos.1);
|
|
287
|
-
let line_end_index = self.buffer.index_of(self.buffer.area.width - 1, self.pos.1);
|
|
288
|
-
&mut self.buffer.content[line_start_index..=line_end_index]
|
|
289
|
-
}
|
|
290
|
-
ClearType::UntilNewLine => {
|
|
291
|
-
let index = self.buffer.index_of(self.pos.0, self.pos.1);
|
|
292
|
-
let line_end_index = self.buffer.index_of(self.buffer.area.width - 1, self.pos.1);
|
|
293
|
-
&mut self.buffer.content[index..=line_end_index]
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
for cell in region {
|
|
297
|
-
cell.reset();
|
|
298
|
-
}
|
|
299
|
-
Ok(())
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/// Inserts n line breaks at the current cursor position.
|
|
303
|
-
///
|
|
304
|
-
/// After the insertion, the cursor x position will be incremented by 1 (unless it's already
|
|
305
|
-
/// at the end of line). This is a common behaviour of terminals in raw mode.
|
|
306
|
-
///
|
|
307
|
-
/// If the number of lines to append is fewer than the number of lines in the buffer after the
|
|
308
|
-
/// cursor y position then the cursor is moved down by n rows.
|
|
309
|
-
///
|
|
310
|
-
/// If the number of lines to append is greater than the number of lines in the buffer after
|
|
311
|
-
/// the cursor y position then that number of empty lines (at most the buffer's height in this
|
|
312
|
-
/// case but this limit is instead replaced with scrolling in most backend implementations) will
|
|
313
|
-
/// be added after the current position and the cursor will be moved to the last row.
|
|
314
|
-
fn append_lines(&mut self, line_count: u16) -> Result<()> {
|
|
315
|
-
let Position { x: cur_x, y: cur_y } = self.get_cursor_position()?;
|
|
316
|
-
let Rect { width, height, .. } = self.buffer.area;
|
|
317
|
-
|
|
318
|
-
// the next column ensuring that we don't go past the last column
|
|
319
|
-
let new_cursor_x = cur_x.saturating_add(1).min(width.saturating_sub(1));
|
|
320
|
-
|
|
321
|
-
let max_y = height.saturating_sub(1);
|
|
322
|
-
let lines_after_cursor = max_y.saturating_sub(cur_y);
|
|
323
|
-
|
|
324
|
-
if line_count > lines_after_cursor {
|
|
325
|
-
// We need to insert blank lines at the bottom and scroll the lines from the top into
|
|
326
|
-
// scrollback.
|
|
327
|
-
let scroll_by: usize = (line_count - lines_after_cursor).into();
|
|
328
|
-
let width: usize = self.buffer.area.width.into();
|
|
329
|
-
let cells_to_scrollback = self.buffer.content.len().min(width * scroll_by);
|
|
330
|
-
|
|
331
|
-
append_to_scrollback(
|
|
332
|
-
&mut self.scrollback,
|
|
333
|
-
self.buffer.content.splice(
|
|
334
|
-
0..cells_to_scrollback,
|
|
335
|
-
iter::repeat_with(Default::default).take(cells_to_scrollback),
|
|
336
|
-
),
|
|
337
|
-
);
|
|
338
|
-
self.buffer.content.rotate_left(cells_to_scrollback);
|
|
339
|
-
append_to_scrollback(
|
|
340
|
-
&mut self.scrollback,
|
|
341
|
-
iter::repeat_with(Default::default).take(width * scroll_by - cells_to_scrollback),
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
let new_cursor_y = cur_y.saturating_add(line_count).min(max_y);
|
|
346
|
-
self.set_cursor_position(Position::new(new_cursor_x, new_cursor_y))?;
|
|
347
|
-
|
|
348
|
-
Ok(())
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
fn size(&self) -> Result<Size> {
|
|
352
|
-
Ok(self.buffer.area.as_size())
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
fn window_size(&mut self) -> Result<WindowSize> {
|
|
356
|
-
// Some arbitrary window pixel size, probably doesn't need much testing.
|
|
357
|
-
const WINDOW_PIXEL_SIZE: Size = Size {
|
|
358
|
-
width: 640,
|
|
359
|
-
height: 480,
|
|
360
|
-
};
|
|
361
|
-
Ok(WindowSize {
|
|
362
|
-
columns_rows: self.buffer.area.as_size(),
|
|
363
|
-
pixels: WINDOW_PIXEL_SIZE,
|
|
364
|
-
})
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
fn flush(&mut self) -> Result<()> {
|
|
368
|
-
Ok(())
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
#[cfg(feature = "scrolling-regions")]
|
|
372
|
-
fn scroll_region_up(&mut self, region: core::ops::Range<u16>, scroll_by: u16) -> Result<()> {
|
|
373
|
-
let width: usize = self.buffer.area.width.into();
|
|
374
|
-
let cell_region_start = width * region.start.min(self.buffer.area.height) as usize;
|
|
375
|
-
let cell_region_end = width * region.end.min(self.buffer.area.height) as usize;
|
|
376
|
-
let cell_region_len = cell_region_end - cell_region_start;
|
|
377
|
-
let cells_to_scroll_by = width * scroll_by as usize;
|
|
378
|
-
|
|
379
|
-
// Deal with the simple case where nothing needs to be copied into scrollback.
|
|
380
|
-
if cell_region_start > 0 {
|
|
381
|
-
if cells_to_scroll_by >= cell_region_len {
|
|
382
|
-
// The scroll amount is large enough to clear the whole region.
|
|
383
|
-
self.buffer.content[cell_region_start..cell_region_end].fill_with(Default::default);
|
|
384
|
-
} else {
|
|
385
|
-
// Scroll up by rotating, then filling in the bottom with empty cells.
|
|
386
|
-
self.buffer.content[cell_region_start..cell_region_end]
|
|
387
|
-
.rotate_left(cells_to_scroll_by);
|
|
388
|
-
self.buffer.content[cell_region_end - cells_to_scroll_by..cell_region_end]
|
|
389
|
-
.fill_with(Default::default);
|
|
390
|
-
}
|
|
391
|
-
return Ok(());
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// The rows inserted into the scrollback will first come from the buffer, and if that is
|
|
395
|
-
// insufficient, will then be blank rows.
|
|
396
|
-
let cells_from_region = cell_region_len.min(cells_to_scroll_by);
|
|
397
|
-
append_to_scrollback(
|
|
398
|
-
&mut self.scrollback,
|
|
399
|
-
self.buffer.content.splice(
|
|
400
|
-
0..cells_from_region,
|
|
401
|
-
iter::repeat_with(Default::default).take(cells_from_region),
|
|
402
|
-
),
|
|
403
|
-
);
|
|
404
|
-
if cells_to_scroll_by < cell_region_len {
|
|
405
|
-
// Rotate the remaining cells to the front of the region.
|
|
406
|
-
self.buffer.content[cell_region_start..cell_region_end].rotate_left(cells_from_region);
|
|
407
|
-
} else {
|
|
408
|
-
// Splice cleared out the region. Insert empty rows in scrollback.
|
|
409
|
-
append_to_scrollback(
|
|
410
|
-
&mut self.scrollback,
|
|
411
|
-
iter::repeat_with(Default::default).take(cells_to_scroll_by - cell_region_len),
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
Ok(())
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
#[cfg(feature = "scrolling-regions")]
|
|
418
|
-
fn scroll_region_down(&mut self, region: core::ops::Range<u16>, scroll_by: u16) -> Result<()> {
|
|
419
|
-
let width: usize = self.buffer.area.width.into();
|
|
420
|
-
let cell_region_start = width * region.start.min(self.buffer.area.height) as usize;
|
|
421
|
-
let cell_region_end = width * region.end.min(self.buffer.area.height) as usize;
|
|
422
|
-
let cell_region_len = cell_region_end - cell_region_start;
|
|
423
|
-
let cells_to_scroll_by = width * scroll_by as usize;
|
|
424
|
-
|
|
425
|
-
if cells_to_scroll_by >= cell_region_len {
|
|
426
|
-
// The scroll amount is large enough to clear the whole region.
|
|
427
|
-
self.buffer.content[cell_region_start..cell_region_end].fill_with(Default::default);
|
|
428
|
-
} else {
|
|
429
|
-
// Scroll up by rotating, then filling in the top with empty cells.
|
|
430
|
-
self.buffer.content[cell_region_start..cell_region_end]
|
|
431
|
-
.rotate_right(cells_to_scroll_by);
|
|
432
|
-
self.buffer.content[cell_region_start..cell_region_start + cells_to_scroll_by]
|
|
433
|
-
.fill_with(Default::default);
|
|
434
|
-
}
|
|
435
|
-
Ok(())
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/// Append the provided cells to the bottom of a scrollback buffer. The number of cells must be a
|
|
440
|
-
/// multiple of the buffer's width. If the scrollback buffer ends up larger than 65535 lines tall,
|
|
441
|
-
/// then lines will be removed from the top to get it down to size.
|
|
442
|
-
fn append_to_scrollback(scrollback: &mut Buffer, cells: impl IntoIterator<Item = Cell>) {
|
|
443
|
-
scrollback.content.extend(cells);
|
|
444
|
-
let width = scrollback.area.width as usize;
|
|
445
|
-
let new_height = (scrollback.content.len() / width).min(u16::MAX as usize);
|
|
446
|
-
let keep_from = scrollback
|
|
447
|
-
.content
|
|
448
|
-
.len()
|
|
449
|
-
.saturating_sub(width * u16::MAX as usize);
|
|
450
|
-
scrollback.content.drain(0..keep_from);
|
|
451
|
-
scrollback.area.height = new_height as u16;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
#[cfg(test)]
|
|
455
|
-
mod tests {
|
|
456
|
-
use alloc::format;
|
|
457
|
-
|
|
458
|
-
use itertools::Itertools as _;
|
|
459
|
-
|
|
460
|
-
use super::*;
|
|
461
|
-
|
|
462
|
-
#[test]
|
|
463
|
-
fn new() {
|
|
464
|
-
assert_eq!(
|
|
465
|
-
TestBackend::new(10, 2),
|
|
466
|
-
TestBackend {
|
|
467
|
-
buffer: Buffer::with_lines([" "; 2]),
|
|
468
|
-
scrollback: Buffer::empty(Rect::new(0, 0, 10, 0)),
|
|
469
|
-
cursor: false,
|
|
470
|
-
pos: (0, 0),
|
|
471
|
-
}
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
#[test]
|
|
475
|
-
fn test_buffer_view() {
|
|
476
|
-
let buffer = Buffer::with_lines(["aaaa"; 2]);
|
|
477
|
-
assert_eq!(buffer_view(&buffer), "\"aaaa\"\n\"aaaa\"\n");
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
#[test]
|
|
481
|
-
fn buffer_view_with_overwrites() {
|
|
482
|
-
let multi_byte_char = "👨👩👧👦"; // renders 2 wide
|
|
483
|
-
let buffer = Buffer::with_lines([multi_byte_char]);
|
|
484
|
-
assert_eq!(
|
|
485
|
-
buffer_view(&buffer),
|
|
486
|
-
format!(
|
|
487
|
-
r#""{multi_byte_char}" Hidden by multi-width symbols: [(1, " ")]
|
|
488
|
-
"#,
|
|
489
|
-
)
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
#[test]
|
|
494
|
-
fn buffer() {
|
|
495
|
-
let backend = TestBackend::new(10, 2);
|
|
496
|
-
backend.assert_buffer_lines([" "; 2]);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
#[test]
|
|
500
|
-
fn resize() {
|
|
501
|
-
let mut backend = TestBackend::new(10, 2);
|
|
502
|
-
backend.resize(5, 5);
|
|
503
|
-
backend.assert_buffer_lines([" "; 5]);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
#[test]
|
|
507
|
-
fn assert_buffer() {
|
|
508
|
-
let backend = TestBackend::new(10, 2);
|
|
509
|
-
backend.assert_buffer_lines([" "; 2]);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
#[test]
|
|
513
|
-
#[should_panic = "buffer contents not equal"]
|
|
514
|
-
fn assert_buffer_panics() {
|
|
515
|
-
let backend = TestBackend::new(10, 2);
|
|
516
|
-
backend.assert_buffer_lines(["aaaaaaaaaa"; 2]);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
#[test]
|
|
520
|
-
#[should_panic = "assertion `left == right` failed"]
|
|
521
|
-
fn assert_scrollback_panics() {
|
|
522
|
-
let backend = TestBackend::new(10, 2);
|
|
523
|
-
backend.assert_scrollback_lines(["aaaaaaaaaa"; 2]);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
#[test]
|
|
527
|
-
fn display() {
|
|
528
|
-
let backend = TestBackend::new(10, 2);
|
|
529
|
-
assert_eq!(format!("{backend}"), "\" \"\n\" \"\n");
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
#[test]
|
|
533
|
-
fn draw() {
|
|
534
|
-
let mut backend = TestBackend::new(10, 2);
|
|
535
|
-
let cell = Cell::new("a");
|
|
536
|
-
backend.draw([(0, 0, &cell)].into_iter()).unwrap();
|
|
537
|
-
backend.draw([(0, 1, &cell)].into_iter()).unwrap();
|
|
538
|
-
backend.assert_buffer_lines(["a "; 2]);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
#[test]
|
|
542
|
-
fn hide_cursor() {
|
|
543
|
-
let mut backend = TestBackend::new(10, 2);
|
|
544
|
-
backend.hide_cursor().unwrap();
|
|
545
|
-
assert!(!backend.cursor);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
#[test]
|
|
549
|
-
fn show_cursor() {
|
|
550
|
-
let mut backend = TestBackend::new(10, 2);
|
|
551
|
-
backend.show_cursor().unwrap();
|
|
552
|
-
assert!(backend.cursor);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
#[test]
|
|
556
|
-
fn get_cursor_position() {
|
|
557
|
-
let mut backend = TestBackend::new(10, 2);
|
|
558
|
-
assert_eq!(backend.get_cursor_position().unwrap(), Position::ORIGIN);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
#[test]
|
|
562
|
-
fn assert_cursor_position() {
|
|
563
|
-
let mut backend = TestBackend::new(10, 2);
|
|
564
|
-
backend.assert_cursor_position(Position::ORIGIN);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
#[test]
|
|
568
|
-
fn set_cursor_position() {
|
|
569
|
-
let mut backend = TestBackend::new(10, 10);
|
|
570
|
-
backend
|
|
571
|
-
.set_cursor_position(Position { x: 5, y: 5 })
|
|
572
|
-
.unwrap();
|
|
573
|
-
assert_eq!(backend.pos, (5, 5));
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
#[test]
|
|
577
|
-
fn clear() {
|
|
578
|
-
let mut backend = TestBackend::new(4, 2);
|
|
579
|
-
let cell = Cell::new("a");
|
|
580
|
-
backend.draw([(0, 0, &cell)].into_iter()).unwrap();
|
|
581
|
-
backend.draw([(0, 1, &cell)].into_iter()).unwrap();
|
|
582
|
-
backend.clear().unwrap();
|
|
583
|
-
backend.assert_buffer_lines([" ", " "]);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
#[test]
|
|
587
|
-
fn clear_region_all() {
|
|
588
|
-
let mut backend = TestBackend::with_lines([
|
|
589
|
-
"aaaaaaaaaa",
|
|
590
|
-
"aaaaaaaaaa",
|
|
591
|
-
"aaaaaaaaaa",
|
|
592
|
-
"aaaaaaaaaa",
|
|
593
|
-
"aaaaaaaaaa",
|
|
594
|
-
]);
|
|
595
|
-
|
|
596
|
-
backend.clear_region(ClearType::All).unwrap();
|
|
597
|
-
backend.assert_buffer_lines([
|
|
598
|
-
" ",
|
|
599
|
-
" ",
|
|
600
|
-
" ",
|
|
601
|
-
" ",
|
|
602
|
-
" ",
|
|
603
|
-
]);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
#[test]
|
|
607
|
-
fn clear_region_after_cursor() {
|
|
608
|
-
let mut backend = TestBackend::with_lines([
|
|
609
|
-
"aaaaaaaaaa",
|
|
610
|
-
"aaaaaaaaaa",
|
|
611
|
-
"aaaaaaaaaa",
|
|
612
|
-
"aaaaaaaaaa",
|
|
613
|
-
"aaaaaaaaaa",
|
|
614
|
-
]);
|
|
615
|
-
|
|
616
|
-
backend
|
|
617
|
-
.set_cursor_position(Position { x: 3, y: 2 })
|
|
618
|
-
.unwrap();
|
|
619
|
-
backend.clear_region(ClearType::AfterCursor).unwrap();
|
|
620
|
-
backend.assert_buffer_lines([
|
|
621
|
-
"aaaaaaaaaa",
|
|
622
|
-
"aaaaaaaaaa",
|
|
623
|
-
"aaaa ",
|
|
624
|
-
" ",
|
|
625
|
-
" ",
|
|
626
|
-
]);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
#[test]
|
|
630
|
-
fn clear_region_before_cursor() {
|
|
631
|
-
let mut backend = TestBackend::with_lines([
|
|
632
|
-
"aaaaaaaaaa",
|
|
633
|
-
"aaaaaaaaaa",
|
|
634
|
-
"aaaaaaaaaa",
|
|
635
|
-
"aaaaaaaaaa",
|
|
636
|
-
"aaaaaaaaaa",
|
|
637
|
-
]);
|
|
638
|
-
|
|
639
|
-
backend
|
|
640
|
-
.set_cursor_position(Position { x: 5, y: 3 })
|
|
641
|
-
.unwrap();
|
|
642
|
-
backend.clear_region(ClearType::BeforeCursor).unwrap();
|
|
643
|
-
backend.assert_buffer_lines([
|
|
644
|
-
" ",
|
|
645
|
-
" ",
|
|
646
|
-
" ",
|
|
647
|
-
" aaaaa",
|
|
648
|
-
"aaaaaaaaaa",
|
|
649
|
-
]);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
#[test]
|
|
653
|
-
fn clear_region_current_line() {
|
|
654
|
-
let mut backend = TestBackend::with_lines([
|
|
655
|
-
"aaaaaaaaaa",
|
|
656
|
-
"aaaaaaaaaa",
|
|
657
|
-
"aaaaaaaaaa",
|
|
658
|
-
"aaaaaaaaaa",
|
|
659
|
-
"aaaaaaaaaa",
|
|
660
|
-
]);
|
|
661
|
-
|
|
662
|
-
backend
|
|
663
|
-
.set_cursor_position(Position { x: 3, y: 1 })
|
|
664
|
-
.unwrap();
|
|
665
|
-
backend.clear_region(ClearType::CurrentLine).unwrap();
|
|
666
|
-
backend.assert_buffer_lines([
|
|
667
|
-
"aaaaaaaaaa",
|
|
668
|
-
" ",
|
|
669
|
-
"aaaaaaaaaa",
|
|
670
|
-
"aaaaaaaaaa",
|
|
671
|
-
"aaaaaaaaaa",
|
|
672
|
-
]);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
#[test]
|
|
676
|
-
fn clear_region_until_new_line() {
|
|
677
|
-
let mut backend = TestBackend::with_lines([
|
|
678
|
-
"aaaaaaaaaa",
|
|
679
|
-
"aaaaaaaaaa",
|
|
680
|
-
"aaaaaaaaaa",
|
|
681
|
-
"aaaaaaaaaa",
|
|
682
|
-
"aaaaaaaaaa",
|
|
683
|
-
]);
|
|
684
|
-
|
|
685
|
-
backend
|
|
686
|
-
.set_cursor_position(Position { x: 3, y: 0 })
|
|
687
|
-
.unwrap();
|
|
688
|
-
backend.clear_region(ClearType::UntilNewLine).unwrap();
|
|
689
|
-
backend.assert_buffer_lines([
|
|
690
|
-
"aaa ",
|
|
691
|
-
"aaaaaaaaaa",
|
|
692
|
-
"aaaaaaaaaa",
|
|
693
|
-
"aaaaaaaaaa",
|
|
694
|
-
"aaaaaaaaaa",
|
|
695
|
-
]);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
#[test]
|
|
699
|
-
fn append_lines_not_at_last_line() {
|
|
700
|
-
let mut backend = TestBackend::with_lines([
|
|
701
|
-
"aaaaaaaaaa",
|
|
702
|
-
"bbbbbbbbbb",
|
|
703
|
-
"cccccccccc",
|
|
704
|
-
"dddddddddd",
|
|
705
|
-
"eeeeeeeeee",
|
|
706
|
-
]);
|
|
707
|
-
|
|
708
|
-
backend.set_cursor_position(Position::ORIGIN).unwrap();
|
|
709
|
-
|
|
710
|
-
// If the cursor is not at the last line in the terminal the addition of a
|
|
711
|
-
// newline simply moves the cursor down and to the right
|
|
712
|
-
|
|
713
|
-
backend.append_lines(1).unwrap();
|
|
714
|
-
backend.assert_cursor_position(Position { x: 1, y: 1 });
|
|
715
|
-
|
|
716
|
-
backend.append_lines(1).unwrap();
|
|
717
|
-
backend.assert_cursor_position(Position { x: 2, y: 2 });
|
|
718
|
-
|
|
719
|
-
backend.append_lines(1).unwrap();
|
|
720
|
-
backend.assert_cursor_position(Position { x: 3, y: 3 });
|
|
721
|
-
|
|
722
|
-
backend.append_lines(1).unwrap();
|
|
723
|
-
backend.assert_cursor_position(Position { x: 4, y: 4 });
|
|
724
|
-
|
|
725
|
-
// As such the buffer should remain unchanged
|
|
726
|
-
backend.assert_buffer_lines([
|
|
727
|
-
"aaaaaaaaaa",
|
|
728
|
-
"bbbbbbbbbb",
|
|
729
|
-
"cccccccccc",
|
|
730
|
-
"dddddddddd",
|
|
731
|
-
"eeeeeeeeee",
|
|
732
|
-
]);
|
|
733
|
-
backend.assert_scrollback_empty();
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
#[test]
|
|
737
|
-
fn append_lines_at_last_line() {
|
|
738
|
-
let mut backend = TestBackend::with_lines([
|
|
739
|
-
"aaaaaaaaaa",
|
|
740
|
-
"bbbbbbbbbb",
|
|
741
|
-
"cccccccccc",
|
|
742
|
-
"dddddddddd",
|
|
743
|
-
"eeeeeeeeee",
|
|
744
|
-
]);
|
|
745
|
-
|
|
746
|
-
// If the cursor is at the last line in the terminal the addition of a
|
|
747
|
-
// newline will scroll the contents of the buffer
|
|
748
|
-
backend
|
|
749
|
-
.set_cursor_position(Position { x: 0, y: 4 })
|
|
750
|
-
.unwrap();
|
|
751
|
-
|
|
752
|
-
backend.append_lines(1).unwrap();
|
|
753
|
-
|
|
754
|
-
backend.assert_buffer_lines([
|
|
755
|
-
"bbbbbbbbbb",
|
|
756
|
-
"cccccccccc",
|
|
757
|
-
"dddddddddd",
|
|
758
|
-
"eeeeeeeeee",
|
|
759
|
-
" ",
|
|
760
|
-
]);
|
|
761
|
-
backend.assert_scrollback_lines(["aaaaaaaaaa"]);
|
|
762
|
-
|
|
763
|
-
// It also moves the cursor to the right, as is common of the behaviour of
|
|
764
|
-
// terminals in raw-mode
|
|
765
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
#[test]
|
|
769
|
-
fn append_multiple_lines_not_at_last_line() {
|
|
770
|
-
let mut backend = TestBackend::with_lines([
|
|
771
|
-
"aaaaaaaaaa",
|
|
772
|
-
"bbbbbbbbbb",
|
|
773
|
-
"cccccccccc",
|
|
774
|
-
"dddddddddd",
|
|
775
|
-
"eeeeeeeeee",
|
|
776
|
-
]);
|
|
777
|
-
|
|
778
|
-
backend.set_cursor_position(Position::ORIGIN).unwrap();
|
|
779
|
-
|
|
780
|
-
// If the cursor is not at the last line in the terminal the addition of multiple
|
|
781
|
-
// newlines simply moves the cursor n lines down and to the right by 1
|
|
782
|
-
|
|
783
|
-
backend.append_lines(4).unwrap();
|
|
784
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
785
|
-
|
|
786
|
-
// As such the buffer should remain unchanged
|
|
787
|
-
backend.assert_buffer_lines([
|
|
788
|
-
"aaaaaaaaaa",
|
|
789
|
-
"bbbbbbbbbb",
|
|
790
|
-
"cccccccccc",
|
|
791
|
-
"dddddddddd",
|
|
792
|
-
"eeeeeeeeee",
|
|
793
|
-
]);
|
|
794
|
-
backend.assert_scrollback_empty();
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
#[test]
|
|
798
|
-
fn append_multiple_lines_past_last_line() {
|
|
799
|
-
let mut backend = TestBackend::with_lines([
|
|
800
|
-
"aaaaaaaaaa",
|
|
801
|
-
"bbbbbbbbbb",
|
|
802
|
-
"cccccccccc",
|
|
803
|
-
"dddddddddd",
|
|
804
|
-
"eeeeeeeeee",
|
|
805
|
-
]);
|
|
806
|
-
|
|
807
|
-
backend
|
|
808
|
-
.set_cursor_position(Position { x: 0, y: 3 })
|
|
809
|
-
.unwrap();
|
|
810
|
-
|
|
811
|
-
backend.append_lines(3).unwrap();
|
|
812
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
813
|
-
|
|
814
|
-
backend.assert_buffer_lines([
|
|
815
|
-
"cccccccccc",
|
|
816
|
-
"dddddddddd",
|
|
817
|
-
"eeeeeeeeee",
|
|
818
|
-
" ",
|
|
819
|
-
" ",
|
|
820
|
-
]);
|
|
821
|
-
backend.assert_scrollback_lines(["aaaaaaaaaa", "bbbbbbbbbb"]);
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
#[test]
|
|
825
|
-
fn append_multiple_lines_where_cursor_at_end_appends_height_lines() {
|
|
826
|
-
let mut backend = TestBackend::with_lines([
|
|
827
|
-
"aaaaaaaaaa",
|
|
828
|
-
"bbbbbbbbbb",
|
|
829
|
-
"cccccccccc",
|
|
830
|
-
"dddddddddd",
|
|
831
|
-
"eeeeeeeeee",
|
|
832
|
-
]);
|
|
833
|
-
|
|
834
|
-
backend
|
|
835
|
-
.set_cursor_position(Position { x: 0, y: 4 })
|
|
836
|
-
.unwrap();
|
|
837
|
-
|
|
838
|
-
backend.append_lines(5).unwrap();
|
|
839
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
840
|
-
|
|
841
|
-
backend.assert_buffer_lines([
|
|
842
|
-
" ",
|
|
843
|
-
" ",
|
|
844
|
-
" ",
|
|
845
|
-
" ",
|
|
846
|
-
" ",
|
|
847
|
-
]);
|
|
848
|
-
backend.assert_scrollback_lines([
|
|
849
|
-
"aaaaaaaaaa",
|
|
850
|
-
"bbbbbbbbbb",
|
|
851
|
-
"cccccccccc",
|
|
852
|
-
"dddddddddd",
|
|
853
|
-
"eeeeeeeeee",
|
|
854
|
-
]);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
#[test]
|
|
858
|
-
fn append_multiple_lines_where_cursor_appends_height_lines() {
|
|
859
|
-
let mut backend = TestBackend::with_lines([
|
|
860
|
-
"aaaaaaaaaa",
|
|
861
|
-
"bbbbbbbbbb",
|
|
862
|
-
"cccccccccc",
|
|
863
|
-
"dddddddddd",
|
|
864
|
-
"eeeeeeeeee",
|
|
865
|
-
]);
|
|
866
|
-
|
|
867
|
-
backend.set_cursor_position(Position::ORIGIN).unwrap();
|
|
868
|
-
|
|
869
|
-
backend.append_lines(5).unwrap();
|
|
870
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
871
|
-
|
|
872
|
-
backend.assert_buffer_lines([
|
|
873
|
-
"bbbbbbbbbb",
|
|
874
|
-
"cccccccccc",
|
|
875
|
-
"dddddddddd",
|
|
876
|
-
"eeeeeeeeee",
|
|
877
|
-
" ",
|
|
878
|
-
]);
|
|
879
|
-
backend.assert_scrollback_lines(["aaaaaaaaaa"]);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
#[test]
|
|
883
|
-
fn append_multiple_lines_where_cursor_at_end_appends_more_than_height_lines() {
|
|
884
|
-
let mut backend = TestBackend::with_lines([
|
|
885
|
-
"aaaaaaaaaa",
|
|
886
|
-
"bbbbbbbbbb",
|
|
887
|
-
"cccccccccc",
|
|
888
|
-
"dddddddddd",
|
|
889
|
-
"eeeeeeeeee",
|
|
890
|
-
]);
|
|
891
|
-
|
|
892
|
-
backend
|
|
893
|
-
.set_cursor_position(Position { x: 0, y: 4 })
|
|
894
|
-
.unwrap();
|
|
895
|
-
|
|
896
|
-
backend.append_lines(8).unwrap();
|
|
897
|
-
backend.assert_cursor_position(Position { x: 1, y: 4 });
|
|
898
|
-
|
|
899
|
-
backend.assert_buffer_lines([
|
|
900
|
-
" ",
|
|
901
|
-
" ",
|
|
902
|
-
" ",
|
|
903
|
-
" ",
|
|
904
|
-
" ",
|
|
905
|
-
]);
|
|
906
|
-
backend.assert_scrollback_lines([
|
|
907
|
-
"aaaaaaaaaa",
|
|
908
|
-
"bbbbbbbbbb",
|
|
909
|
-
"cccccccccc",
|
|
910
|
-
"dddddddddd",
|
|
911
|
-
"eeeeeeeeee",
|
|
912
|
-
" ",
|
|
913
|
-
" ",
|
|
914
|
-
" ",
|
|
915
|
-
]);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
#[test]
|
|
919
|
-
fn append_lines_truncates_beyond_u16_max() -> Result<()> {
|
|
920
|
-
let mut backend = TestBackend::new(10, 5);
|
|
921
|
-
|
|
922
|
-
// Fill the scrollback with 65535 + 10 lines.
|
|
923
|
-
let row_count = u16::MAX as usize + 10;
|
|
924
|
-
for row in 0..=row_count {
|
|
925
|
-
if row > 4 {
|
|
926
|
-
backend.set_cursor_position(Position { x: 0, y: 4 })?;
|
|
927
|
-
backend.append_lines(1)?;
|
|
928
|
-
}
|
|
929
|
-
let cells = format!("{row:>10}").chars().map(Cell::from).collect_vec();
|
|
930
|
-
let content = cells
|
|
931
|
-
.iter()
|
|
932
|
-
.enumerate()
|
|
933
|
-
.map(|(column, cell)| (column as u16, 4.min(row) as u16, cell));
|
|
934
|
-
backend.draw(content)?;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// check that the buffer contains the last 5 lines appended
|
|
938
|
-
backend.assert_buffer_lines([
|
|
939
|
-
" 65541",
|
|
940
|
-
" 65542",
|
|
941
|
-
" 65543",
|
|
942
|
-
" 65544",
|
|
943
|
-
" 65545",
|
|
944
|
-
]);
|
|
945
|
-
|
|
946
|
-
// TODO: ideally this should be something like:
|
|
947
|
-
// let lines = (6..=65545).map(|row| format!("{row:>10}"));
|
|
948
|
-
// backend.assert_scrollback_lines(lines);
|
|
949
|
-
// but there's some truncation happening in Buffer::with_lines that needs to be fixed
|
|
950
|
-
assert_eq!(
|
|
951
|
-
Buffer {
|
|
952
|
-
area: Rect::new(0, 0, 10, 5),
|
|
953
|
-
content: backend.scrollback.content[0..10 * 5].to_vec(),
|
|
954
|
-
},
|
|
955
|
-
Buffer::with_lines([
|
|
956
|
-
" 6",
|
|
957
|
-
" 7",
|
|
958
|
-
" 8",
|
|
959
|
-
" 9",
|
|
960
|
-
" 10",
|
|
961
|
-
]),
|
|
962
|
-
"first 5 lines of scrollback should have been truncated"
|
|
963
|
-
);
|
|
964
|
-
|
|
965
|
-
assert_eq!(
|
|
966
|
-
Buffer {
|
|
967
|
-
area: Rect::new(0, 0, 10, 5),
|
|
968
|
-
content: backend.scrollback.content[10 * 65530..10 * 65535].to_vec(),
|
|
969
|
-
},
|
|
970
|
-
Buffer::with_lines([
|
|
971
|
-
" 65536",
|
|
972
|
-
" 65537",
|
|
973
|
-
" 65538",
|
|
974
|
-
" 65539",
|
|
975
|
-
" 65540",
|
|
976
|
-
]),
|
|
977
|
-
"last 5 lines of scrollback should have been appended"
|
|
978
|
-
);
|
|
979
|
-
|
|
980
|
-
// These checks come after the content checks as otherwise we won't see the failing content
|
|
981
|
-
// when these checks fail.
|
|
982
|
-
// Make sure the scrollback is the right size.
|
|
983
|
-
assert_eq!(backend.scrollback.area.width, 10);
|
|
984
|
-
assert_eq!(backend.scrollback.area.height, 65535);
|
|
985
|
-
assert_eq!(backend.scrollback.content.len(), 10 * 65535);
|
|
986
|
-
Ok(())
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
#[test]
|
|
990
|
-
fn size() {
|
|
991
|
-
let backend = TestBackend::new(10, 2);
|
|
992
|
-
assert_eq!(backend.size().unwrap(), Size::new(10, 2));
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
#[test]
|
|
996
|
-
fn flush() {
|
|
997
|
-
let mut backend = TestBackend::new(10, 2);
|
|
998
|
-
backend.flush().unwrap();
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
#[cfg(feature = "scrolling-regions")]
|
|
1002
|
-
mod scrolling_regions {
|
|
1003
|
-
use rstest::rstest;
|
|
1004
|
-
|
|
1005
|
-
use super::*;
|
|
1006
|
-
|
|
1007
|
-
const A: &str = "aaaa";
|
|
1008
|
-
const B: &str = "bbbb";
|
|
1009
|
-
const C: &str = "cccc";
|
|
1010
|
-
const D: &str = "dddd";
|
|
1011
|
-
const E: &str = "eeee";
|
|
1012
|
-
const S: &str = " ";
|
|
1013
|
-
|
|
1014
|
-
#[rstest]
|
|
1015
|
-
#[case([A, B, C, D, E], 0..5, 0, [], [A, B, C, D, E])]
|
|
1016
|
-
#[case([A, B, C, D, E], 0..5, 2, [A, B], [C, D, E, S, S])]
|
|
1017
|
-
#[case([A, B, C, D, E], 0..5, 5, [A, B, C, D, E], [S, S, S, S, S])]
|
|
1018
|
-
#[case([A, B, C, D, E], 0..5, 7, [A, B, C, D, E, S, S], [S, S, S, S, S])]
|
|
1019
|
-
#[case([A, B, C, D, E], 0..3, 0, [], [A, B, C, D, E])]
|
|
1020
|
-
#[case([A, B, C, D, E], 0..3, 2, [A, B], [C, S, S, D, E])]
|
|
1021
|
-
#[case([A, B, C, D, E], 0..3, 3, [A, B, C], [S, S, S, D, E])]
|
|
1022
|
-
#[case([A, B, C, D, E], 0..3, 4, [A, B, C, S], [S, S, S, D, E])]
|
|
1023
|
-
#[case([A, B, C, D, E], 1..4, 0, [], [A, B, C, D, E])]
|
|
1024
|
-
#[case([A, B, C, D, E], 1..4, 2, [], [A, D, S, S, E])]
|
|
1025
|
-
#[case([A, B, C, D, E], 1..4, 3, [], [A, S, S, S, E])]
|
|
1026
|
-
#[case([A, B, C, D, E], 1..4, 4, [], [A, S, S, S, E])]
|
|
1027
|
-
#[case([A, B, C, D, E], 0..0, 0, [], [A, B, C, D, E])]
|
|
1028
|
-
#[case([A, B, C, D, E], 0..0, 2, [S, S], [A, B, C, D, E])]
|
|
1029
|
-
#[case([A, B, C, D, E], 2..2, 0, [], [A, B, C, D, E])]
|
|
1030
|
-
#[case([A, B, C, D, E], 2..2, 2, [], [A, B, C, D, E])]
|
|
1031
|
-
fn scroll_region_up<const L: usize, const M: usize, const N: usize>(
|
|
1032
|
-
#[case] initial_screen: [&'static str; L],
|
|
1033
|
-
#[case] range: core::ops::Range<u16>,
|
|
1034
|
-
#[case] scroll_by: u16,
|
|
1035
|
-
#[case] expected_scrollback: [&'static str; M],
|
|
1036
|
-
#[case] expected_buffer: [&'static str; N],
|
|
1037
|
-
) {
|
|
1038
|
-
let mut backend = TestBackend::with_lines(initial_screen);
|
|
1039
|
-
backend.scroll_region_up(range, scroll_by).unwrap();
|
|
1040
|
-
if expected_scrollback.is_empty() {
|
|
1041
|
-
backend.assert_scrollback_empty();
|
|
1042
|
-
} else {
|
|
1043
|
-
backend.assert_scrollback_lines(expected_scrollback);
|
|
1044
|
-
}
|
|
1045
|
-
backend.assert_buffer_lines(expected_buffer);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
#[rstest]
|
|
1049
|
-
#[case([A, B, C, D, E], 0..5, 0, [A, B, C, D, E])]
|
|
1050
|
-
#[case([A, B, C, D, E], 0..5, 2, [S, S, A, B, C])]
|
|
1051
|
-
#[case([A, B, C, D, E], 0..5, 5, [S, S, S, S, S])]
|
|
1052
|
-
#[case([A, B, C, D, E], 0..5, 7, [S, S, S, S, S])]
|
|
1053
|
-
#[case([A, B, C, D, E], 0..3, 0, [A, B, C, D, E])]
|
|
1054
|
-
#[case([A, B, C, D, E], 0..3, 2, [S, S, A, D, E])]
|
|
1055
|
-
#[case([A, B, C, D, E], 0..3, 3, [S, S, S, D, E])]
|
|
1056
|
-
#[case([A, B, C, D, E], 0..3, 4, [S, S, S, D, E])]
|
|
1057
|
-
#[case([A, B, C, D, E], 1..4, 0, [A, B, C, D, E])]
|
|
1058
|
-
#[case([A, B, C, D, E], 1..4, 2, [A, S, S, B, E])]
|
|
1059
|
-
#[case([A, B, C, D, E], 1..4, 3, [A, S, S, S, E])]
|
|
1060
|
-
#[case([A, B, C, D, E], 1..4, 4, [A, S, S, S, E])]
|
|
1061
|
-
#[case([A, B, C, D, E], 0..0, 0, [A, B, C, D, E])]
|
|
1062
|
-
#[case([A, B, C, D, E], 0..0, 2, [A, B, C, D, E])]
|
|
1063
|
-
#[case([A, B, C, D, E], 2..2, 0, [A, B, C, D, E])]
|
|
1064
|
-
#[case([A, B, C, D, E], 2..2, 2, [A, B, C, D, E])]
|
|
1065
|
-
fn scroll_region_down<const M: usize, const N: usize>(
|
|
1066
|
-
#[case] initial_screen: [&'static str; M],
|
|
1067
|
-
#[case] range: core::ops::Range<u16>,
|
|
1068
|
-
#[case] scroll_by: u16,
|
|
1069
|
-
#[case] expected_buffer: [&'static str; N],
|
|
1070
|
-
) {
|
|
1071
|
-
let mut backend = TestBackend::with_lines(initial_screen);
|
|
1072
|
-
backend.scroll_region_down(range, scroll_by).unwrap();
|
|
1073
|
-
backend.assert_scrollback_empty();
|
|
1074
|
-
backend.assert_buffer_lines(expected_buffer);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|