feat(terminal): port render state, highlights and the formatter
- render.rs ports render.zig: RenderState, the per-frame snapshot the renderer consumes (rows, cells, styles, graphemes, selection, highlights, cursor, colors), updated incrementally with Zig's full redraw triggers and per-page/per-row dirty tracking. Cells are stored struct-of-arrays; use the accessors. - highlight.rs ports highlight.zig (untracked, tracked, flattened). - formatter.rs ports formatter.zig: plain, VT and HTML output for terminal, screen, page list and page, with selection, rectangle, unwrap, trim, palette, codepoint maps, hyperlinks and pin/point maps. Screen's test dump helpers now delegate to it. Cargo.toml also picks up dependencies for the kitty graphics and search modules that are being ported concurrently. Test Plan: - cargo test -p ghostty-terminal --lib (1243 passed; formatter 100/100, render 15/15 Zig tests)
This commit is contained in:
1 parent
f43c0cd00f
commit
6a374b6953
13 files changed
+5510
-356
No files matched your search
@@ -0,0 +1,589 @@
|
||||
//! "Page plain ..." tests (formatter.zig lines 1580-3287).
|
||||
use super::*;
|
||||
|
||||
struct Out {
|
||||
s: String,
|
||||
st: TrailingState,
|
||||
map: Vec<Coordinate>,
|
||||
}
|
||||
|
||||
/// Runs a PageFormatter over the last page with a point map attached.
|
||||
fn run(t: &Terminal, opts: &Options, cfg: impl FnOnce(&mut PageFormatter<'_>)) -> Out {
|
||||
let page = last_page(t);
|
||||
let mut map = Vec::new();
|
||||
let mut buf = Vec::new();
|
||||
let st = {
|
||||
let mut f = PageFormatter::new(page, opts);
|
||||
f.point_map = Some(&mut map);
|
||||
cfg(&mut f);
|
||||
f.format_with_state(&mut buf).unwrap()
|
||||
};
|
||||
Out {
|
||||
s: String::from_utf8(buf).unwrap(),
|
||||
st,
|
||||
map,
|
||||
}
|
||||
}
|
||||
|
||||
fn c(x: u16, y: u32) -> Coordinate {
|
||||
Coordinate { x, y }
|
||||
}
|
||||
|
||||
/// `n` consecutive cells on row `y` starting at column `x0`.
|
||||
fn span(x0: u16, n: u16, y: u32) -> Vec<Coordinate> {
|
||||
(0..n).map(|i| c(x0 + i, y)).collect()
|
||||
}
|
||||
|
||||
fn setup(cols: u16, rows: u16, input: &str) -> Terminal {
|
||||
let mut t = term(cols, rows);
|
||||
feed(&mut t, input.as_bytes());
|
||||
// Verify we have only a single page
|
||||
let pages = &t.screen().pages;
|
||||
assert!(pages.first.is_some());
|
||||
assert_eq!(pages.first, pages.last);
|
||||
t
|
||||
}
|
||||
|
||||
fn unwrap_opts() -> Options {
|
||||
Options {
|
||||
unwrap: true,
|
||||
..Options::plain()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_single_line() {
|
||||
let t = setup(80, 24, "hello, world");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello, world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 12));
|
||||
assert_eq!(o.map, span(0, 12, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_single_line_soft_wrapped_unwrapped() {
|
||||
let t = setup(3, 5, "hello!");
|
||||
let o = run(&t, &unwrap_opts(), |_| {});
|
||||
assert_eq!(o.s, "hello!");
|
||||
let mut e = span(0, 3, 0);
|
||||
e.extend(span(0, 3, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_single_wide_char() {
|
||||
let t = setup(80, 24, "1A⚡");
|
||||
let page = last_page(&t);
|
||||
let opts = Options::plain();
|
||||
|
||||
// Full string
|
||||
let o = run(&t, &opts, |_| {});
|
||||
assert_eq!(o.s, "1A⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 4));
|
||||
assert_eq!(o.map.len(), o.s.len());
|
||||
for i in 2..o.s.len() {
|
||||
assert_eq!(o.map[i], c(2, 0));
|
||||
}
|
||||
|
||||
// Wide only (from start)
|
||||
let o = run(&t, &opts, |f| f.start_x = 2);
|
||||
assert_eq!(o.s, "⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 4));
|
||||
assert_eq!(o.map, vec![c(2, 0); o.s.len()]);
|
||||
|
||||
// Wide only (from tail)
|
||||
let o = run(&t, &opts, |f| f.start_x = 3);
|
||||
assert_eq!(o.s, "⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 4));
|
||||
assert_eq!(o.map, vec![c(2, 0); o.s.len()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_single_wide_char_soft_wrapped_unwrapped() {
|
||||
let t = setup(3, 24, "1A⚡");
|
||||
let page = last_page(&t);
|
||||
let opts = unwrap_opts();
|
||||
|
||||
// Full string
|
||||
let o = run(&t, &opts, |_| {});
|
||||
assert_eq!(o.s, "1A⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 2));
|
||||
assert_eq!(o.map.len(), o.s.len());
|
||||
for i in 2..o.s.len() {
|
||||
assert_eq!(o.map[i], c(0, 1));
|
||||
}
|
||||
|
||||
// Full string (ending on spacer head)
|
||||
let o = run(&t, &opts, |f| {
|
||||
f.end_x = Some(2);
|
||||
f.end_y = Some(0);
|
||||
});
|
||||
assert_eq!(o.s, "1A⚡");
|
||||
assert_eq!(o.map.len(), o.s.len());
|
||||
for i in 2..o.s.len() {
|
||||
assert_eq!(o.map[i], c(0, 1));
|
||||
}
|
||||
|
||||
// Wide only (from start)
|
||||
let o = run(&t, &opts, |f| f.start_x = 2);
|
||||
assert_eq!(o.s, "⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 2));
|
||||
assert_eq!(o.map, vec![c(0, 1); o.s.len()]);
|
||||
|
||||
// Wide only (from tail). In Zig the formatter carried start_x = 2 over
|
||||
// from the previous block; start_y = 1, start_x = 1.
|
||||
let o = run(&t, &opts, |f| {
|
||||
f.start_y = 1;
|
||||
f.start_x = 1;
|
||||
});
|
||||
assert_eq!(o.s, "⚡");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 2));
|
||||
assert_eq!(o.map, vec![c(0, 1); o.s.len()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_multiline() {
|
||||
let t = setup(80, 24, "hello\r\nworld");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello\nworld");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = span(0, 5, 0);
|
||||
e.push(c(4, 0)); // \n
|
||||
e.extend(span(0, 5, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_multiline_rectangle() {
|
||||
let t = setup(80, 24, "hello\r\nworld");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_x = 1;
|
||||
f.end_x = Some(3);
|
||||
f.rectangle = true;
|
||||
});
|
||||
assert_eq!(o.s, "ell\norl");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, 0);
|
||||
let mut e = span(1, 3, 0);
|
||||
e.push(c(3, 0)); // \n
|
||||
e.extend(span(1, 3, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_multi_blank_lines() {
|
||||
let t = setup(80, 24, "hello\r\n\r\n\r\nworld");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello\n\n\nworld");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 3));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = span(0, 5, 0);
|
||||
e.push(c(4, 0)); // \n after row 0
|
||||
e.push(c(0, 1)); // \n after blank row 1
|
||||
e.push(c(0, 2)); // \n after blank row 2
|
||||
e.extend(span(0, 5, 3));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_trailing_blank_lines() {
|
||||
let t = setup(80, 24, "hello\r\nworld\r\n\r\n");
|
||||
let page = last_page(&t);
|
||||
// We expect there to be no trailing newlines because we can't
|
||||
// differentiate trailing blank lines as being meaningful because the
|
||||
// page formatter can't see the cursor position.
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello\nworld");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = span(0, 5, 0);
|
||||
e.push(c(4, 0));
|
||||
e.extend(span(0, 5, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_trailing_whitespace() {
|
||||
let t = setup(80, 24, "hello \r\nworld ");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello\nworld");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = span(0, 5, 0);
|
||||
e.push(c(4, 0));
|
||||
e.extend(span(0, 5, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_trailing_whitespace_no_trim() {
|
||||
let t = setup(80, 24, "hello \r\nworld ");
|
||||
let page = last_page(&t);
|
||||
let opts = Options {
|
||||
trim: false,
|
||||
..Options::plain()
|
||||
};
|
||||
let o = run(&t, &opts, |_| {});
|
||||
assert_eq!(o.s, "hello \nworld ");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 7));
|
||||
let mut e = span(0, 8, 0);
|
||||
e.push(c(7, 0));
|
||||
e.extend(span(0, 7, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_with_prior_trailing_state_rows() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.trailing_state = Some(TrailingState { rows: 2, cells: 0 });
|
||||
});
|
||||
assert_eq!(o.s, "\n\nhello");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = vec![c(0, 0), c(0, 1)];
|
||||
e.extend(span(0, 5, 0));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_with_prior_trailing_state_cells_no_wrapped_line() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.trailing_state = Some(TrailingState { rows: 0, cells: 3 });
|
||||
});
|
||||
// Blank cells are reset when row is not a wrap continuation
|
||||
assert_eq!(o.s, "hello");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
assert_eq!(o.map, span(0, 5, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_with_prior_trailing_state_cells_with_wrap_continuation() {
|
||||
let mut t = setup(80, 24, "world");
|
||||
// Surgically modify the first row to be a wrap continuation
|
||||
{
|
||||
let pages = &mut t.screen_mut().pages;
|
||||
let id = pages.last.unwrap();
|
||||
pages
|
||||
.node_mut(id)
|
||||
.unwrap()
|
||||
.data
|
||||
.get_row_mut(0)
|
||||
.unwrap()
|
||||
.set_wrap_continuation(true);
|
||||
}
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &unwrap_opts(), |f| {
|
||||
f.trailing_state = Some(TrailingState { rows: 0, cells: 3 });
|
||||
});
|
||||
// Blank cells are preserved when row is a wrap continuation with unwrap enabled
|
||||
assert_eq!(o.s, " world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
// 3 spaces from prior trailing state (can't go back beyond (0,0)) + "world"
|
||||
let mut e = vec![c(0, 0); 3];
|
||||
e.extend(span(0, 5, 0));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_soft_wrapped_without_unwrap() {
|
||||
let t = setup(10, 24, "hello world test");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
// Without unwrap, wrapped lines show as separate lines
|
||||
assert_eq!(o.s, "hello worl\nd test");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 6));
|
||||
let mut e = span(0, 10, 0);
|
||||
e.push(c(9, 0)); // \n
|
||||
e.extend(span(0, 6, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_soft_wrapped_with_unwrap() {
|
||||
let t = setup(10, 24, "hello world test");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &unwrap_opts(), |_| {});
|
||||
// With unwrap, wrapped lines are joined together
|
||||
assert_eq!(o.s, "hello world test");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 6));
|
||||
let mut e = span(0, 10, 0);
|
||||
e.extend(span(0, 6, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_soft_wrapped_3_lines_without_unwrap() {
|
||||
let t = setup(10, 24, "hello world this is a test");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello worl\nd this is\na test");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 2));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 6));
|
||||
let mut e = span(0, 10, 0);
|
||||
e.push(c(9, 0)); // \n
|
||||
e.extend(span(0, 9, 1));
|
||||
e.push(c(8, 1)); // \n
|
||||
e.extend(span(0, 6, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_soft_wrapped_3_lines_with_unwrap() {
|
||||
let t = setup(10, 24, "hello world this is a test");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &unwrap_opts(), |_| {});
|
||||
assert_eq!(o.s, "hello world this is a test");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 2));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 6));
|
||||
let mut e = span(0, 10, 0);
|
||||
e.extend(span(0, 10, 1));
|
||||
e.extend(span(0, 6, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_y_subset() {
|
||||
let t = setup(80, 24, "hello\r\nworld\r\ntest");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| f.start_y = 1);
|
||||
assert_eq!(o.s, "world\ntest");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 2));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 4));
|
||||
let mut e = span(0, 5, 1);
|
||||
e.push(c(4, 1)); // \n
|
||||
e.extend(span(0, 4, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_y_subset() {
|
||||
let t = setup(80, 24, "hello\r\nworld\r\ntest");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| f.end_y = Some(1));
|
||||
assert_eq!(o.s, "hello\nworld");
|
||||
assert_eq!(o.st.rows, 1);
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = span(0, 5, 0);
|
||||
e.push(c(4, 0)); // \n
|
||||
e.extend(span(0, 5, 1));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_y_and_end_y_range() {
|
||||
let t = setup(80, 24, "hello\r\nworld\r\ntest\r\nfoo");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_y = 1;
|
||||
f.end_y = Some(2);
|
||||
});
|
||||
assert_eq!(o.s, "world\ntest");
|
||||
assert_eq!(o.st.rows, 1);
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 4));
|
||||
let mut e = span(0, 5, 1);
|
||||
e.push(c(4, 1)); // \n
|
||||
e.extend(span(0, 4, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_y_out_of_bounds() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let o = run(&t, &Options::plain(), |f| f.start_y = 30);
|
||||
assert_eq!(o.s, "");
|
||||
assert_eq!(o.st.rows, 0);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
assert_eq!(o.map.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_y_greater_than_rows() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| f.end_y = Some(30));
|
||||
// Should clamp to page.size.rows and work normally
|
||||
assert_eq!(o.s, "hello");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
assert_eq!(o.map, span(0, 5, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_y_less_than_start_y() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_y = 5;
|
||||
f.end_y = Some(2);
|
||||
});
|
||||
assert_eq!(o.s, "");
|
||||
assert_eq!(o.st.rows, 0);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
assert_eq!(o.map.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_x_on_first_row_only() {
|
||||
let t = setup(80, 24, "hello world");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| f.start_x = 6);
|
||||
assert_eq!(o.s, "world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 11));
|
||||
assert_eq!(o.map, span(6, 5, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_x_on_last_row_only() {
|
||||
let t = setup(80, 24, "first line\r\nsecond line\r\nthird line");
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.end_y = Some(2);
|
||||
f.end_x = Some(4);
|
||||
});
|
||||
assert_eq!(o.s, "first line\nsecond line\nthird");
|
||||
assert_eq!(o.st.rows, 1);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
let mut e = span(0, 10, 0);
|
||||
e.push(c(9, 0)); // \n
|
||||
e.extend(span(0, 11, 1));
|
||||
e.push(c(10, 1)); // \n
|
||||
e.extend(span(0, 5, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_x_and_end_x_multiline() {
|
||||
let t = setup(80, 24, "hello world\r\ntest case\r\nfoo bar");
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_x = 6;
|
||||
f.end_y = Some(2);
|
||||
f.end_x = Some(2);
|
||||
});
|
||||
// First row: "world" (start_x=6 to end of row)
|
||||
// Second row: "test case" (full row)
|
||||
// Third row: "foo" (start to end_x=2, inclusive)
|
||||
assert_eq!(o.s, "world\ntest case\nfoo");
|
||||
assert_eq!(o.st.rows, 1);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
let mut e = span(6, 5, 0);
|
||||
e.push(c(10, 0)); // \n
|
||||
e.extend(span(0, 9, 1));
|
||||
e.push(c(8, 1)); // \n
|
||||
e.extend(span(0, 3, 2));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_x_out_of_bounds() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let o = run(&t, &Options::plain(), |f| f.start_x = 100);
|
||||
assert_eq!(o.s, "");
|
||||
assert_eq!(o.st.rows, 0);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
assert_eq!(o.map.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_x_greater_than_cols() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| f.end_x = Some(100));
|
||||
assert_eq!(o.s, "hello");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
assert_eq!(o.map, span(0, 5, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_end_x_less_than_start_x_single_row() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_x = 10;
|
||||
f.end_y = Some(0);
|
||||
f.end_x = Some(5);
|
||||
});
|
||||
assert_eq!(o.s, "");
|
||||
assert_eq!(o.st.rows, 0);
|
||||
assert_eq!(o.st.cells, 0);
|
||||
assert_eq!(o.map.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_y_non_zero_ignores_trailing_state() {
|
||||
let t = setup(80, 24, "hello\r\nworld");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_y = 1;
|
||||
f.trailing_state = Some(TrailingState { rows: 5, cells: 10 });
|
||||
});
|
||||
// Should NOT output the 5 newlines from trailing_state because start_y is non-zero
|
||||
assert_eq!(o.s, "world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows - 1));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
assert_eq!(o.map, span(0, 5, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_x_non_zero_ignores_trailing_state() {
|
||||
let t = setup(80, 24, "hello world");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_x = 6;
|
||||
f.trailing_state = Some(TrailingState { rows: 2, cells: 8 });
|
||||
});
|
||||
// Should NOT output the 2 newlines or 8 spaces from trailing_state
|
||||
assert_eq!(o.s, "world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 11));
|
||||
assert_eq!(o.map, span(6, 5, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_start_y_and_start_x_zero_uses_trailing_state() {
|
||||
let t = setup(80, 24, "hello");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |f| {
|
||||
f.start_y = 0;
|
||||
f.start_x = 0;
|
||||
f.trailing_state = Some(TrailingState { rows: 2, cells: 0 });
|
||||
});
|
||||
// SHOULD output the 2 newlines from trailing_state
|
||||
assert_eq!(o.s, "\n\nhello");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 5));
|
||||
let mut e = vec![c(0, 0), c(0, 1)];
|
||||
e.extend(span(0, 5, 0));
|
||||
assert_eq!(o.map, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_plain_single_line_with_styling() {
|
||||
let t = setup(80, 24, "hello, \x1b[1mworld\x1b[0m");
|
||||
let page = last_page(&t);
|
||||
let o = run(&t, &Options::plain(), |_| {});
|
||||
assert_eq!(o.s, "hello, world");
|
||||
assert_eq!(o.st.rows, usize::from(page.size.rows));
|
||||
assert_eq!(o.st.cells, usize::from(page.size.cols - 12));
|
||||
assert_eq!(o.map, span(0, 12, 0));
|
||||
}
|
||||
Reference in new issue
Block a user