From 6c2f9f2d18e50b77d1ad3732d0dec9133ca9a961 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 11 Aug 2021 21:43:14 -0700 Subject: [PATCH] term: restore line length pre-allocation A while back I made the line lengths lazily grown; the reduction in memory was nice, and it helped with render performance for really wide screens. Unfortunately, it puts a bunch of reallocation into the hot path of the parser and updating the terminal model when people run the inevitable `cat giant-file.txt` benchmark. This commit reinstates pre-allocating lines to match the physical terminal width, and tweaks the code a bit to take advantage of const Cell allocation and to avoid some clones (a really micro optimization). --- term/src/screen.rs | 16 +- term/src/terminalstate/performer.rs | 9 +- term/src/test/c1.rs | 10 +- term/src/test/csi.rs | 35 +++-- term/src/test/mod.rs | 225 ++++++++++++++++++++-------- termwiz/src/surface/line.rs | 19 +-- 6 files changed, 207 insertions(+), 107 deletions(-) diff --git a/term/src/screen.rs b/term/src/screen.rs index 7f7fc6167..e18f505b1 100644 --- a/term/src/screen.rs +++ b/term/src/screen.rs @@ -63,7 +63,7 @@ impl Screen { let mut lines = VecDeque::with_capacity(physical_rows + scrollback_size(config, allow_scrollback)); for _ in 0..physical_rows { - lines.push_back(Line::with_width(0)); + lines.push_back(Line::with_width(physical_cols)); } Screen { @@ -215,7 +215,7 @@ impl Screen { // lines than the viewport size, or we resized taller, // pad us back out to the viewport size while self.lines.len() < physical_rows { - self.lines.push_back(Line::with_width(0)); + self.lines.push_back(Line::with_width(self.physical_cols)); } let new_cursor_y; @@ -236,7 +236,7 @@ impl Screen { physical_rows.saturating_sub(new_cursor_y as usize); let actual_num_rows_after_cursor = self.lines.len().saturating_sub(cursor_y); for _ in actual_num_rows_after_cursor..required_num_rows_after_cursor { - self.lines.push_back(Line::with_width(0)); + self.lines.push_back(Line::with_width(self.physical_cols)); } } else { // Compute the new cursor location; this is logically the inverse @@ -595,7 +595,7 @@ impl Screen { for _ in 0..to_move { let mut line = self.lines.remove(remove_idx).unwrap(); // Make the line like a new one of the appropriate width - line.resize_and_clear(0, seqno); + line.resize_and_clear(self.physical_cols, seqno); line.update_last_change_seqno(seqno); if scroll_region.end as usize == self.physical_rows { self.lines.push_back(line); @@ -620,11 +620,12 @@ impl Screen { if scroll_region.end as usize == self.physical_rows { // It's cheaper to push() than it is insert() at the end for _ in 0..to_add { - self.lines.push_back(Line::with_width(0)); + self.lines.push_back(Line::with_width(self.physical_cols)); } } else { for _ in 0..to_add { - self.lines.insert(phys_scroll.end, Line::with_width(0)); + self.lines + .insert(phys_scroll.end, Line::with_width(self.physical_cols)); } } } @@ -672,7 +673,8 @@ impl Screen { } for _ in 0..num_rows { - self.lines.insert(phys_scroll.start, Line::with_width(0)); + self.lines + .insert(phys_scroll.start, Line::with_width(self.physical_cols)); } } diff --git a/term/src/terminalstate/performer.rs b/term/src/terminalstate/performer.rs index ad28c9877..3649c6e24 100644 --- a/term/src/terminalstate/performer.rs +++ b/term/src/terminalstate/performer.rs @@ -59,10 +59,7 @@ impl<'a> Performer<'a> { let seqno = self.seqno; let mut p = std::mem::take(&mut self.print); - let mut graphemes = - unicode_segmentation::UnicodeSegmentation::graphemes(p.as_str(), true).peekable(); - - while let Some(g) = graphemes.next() { + for g in unicode_segmentation::UnicodeSegmentation::graphemes(p.as_str(), true) { let g = if (self.shift_out && self.g1_charset == CharSet::DecLineDrawing) || (!self.shift_out && self.g0_charset == CharSet::DecLineDrawing) { @@ -138,7 +135,6 @@ impl<'a> Performer<'a> { // If we didn't do this, then we'd effectively filter them out from // the model, which seems like a lossy design choice. let print_width = grapheme_column_width(g).max(1); - let is_last = graphemes.peek().is_none(); let wrappable = x + print_width >= width; let cell = Cell::new_grapheme_with_width(g, print_width, pen); @@ -153,10 +149,9 @@ impl<'a> Performer<'a> { // Assign the cell log::trace!( - "print x={} y={} is_last={} print_width={} width={} cell={:?}", + "print x={} y={} print_width={} width={} cell={:?}", x, y, - is_last, print_width, width, cell diff --git a/term/src/test/c1.rs b/term/src/test/c1.rs index 87ee3d21c..19f2c5cee 100644 --- a/term/src/test/c1.rs +++ b/term/src/test/c1.rs @@ -7,12 +7,12 @@ fn test_ind() { let mut term = TestTerm::new(4, 4, 0); term.print("a\r\nb\x1bD"); term.assert_cursor_pos(1, 2, None, None); - assert_visible_contents(&term, file!(), line!(), &["a", "b", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["a ", "b ", " ", " "]); term.print("\x1bD"); term.assert_cursor_pos(1, 3, None, None); term.print("\x1bD"); term.assert_cursor_pos(1, 3, None, Some(term.current_seqno() - 1)); - assert_visible_contents(&term, file!(), line!(), &["b", "", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["b ", " ", " ", " "]); } #[test] @@ -24,7 +24,7 @@ fn test_nel() { term.assert_cursor_pos(0, 3, None, None); term.print("\x1bE"); term.assert_cursor_pos(0, 3, None, None); - assert_visible_contents(&term, file!(), line!(), &["b", "", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["b ", " ", " ", " "]); } #[test] @@ -57,7 +57,7 @@ fn test_hts() { fn test_ri() { let mut term = TestTerm::new(4, 2, 0); term.print("a\r\nb\r\nc\r\nd."); - assert_visible_contents(&term, file!(), line!(), &["a", "b", "c", "d."]); + assert_visible_contents(&term, file!(), line!(), &["a ", "b ", "c ", "d."]); term.assert_cursor_pos(1, 3, None, None); term.print("\x1bM"); term.assert_cursor_pos(1, 2, None, None); @@ -68,5 +68,5 @@ fn test_ri() { let seqno = term.current_seqno(); term.print("\x1bM"); term.assert_cursor_pos(1, 0, None, Some(seqno)); - assert_visible_contents(&term, file!(), line!(), &["", "a", "b", "c"]); + assert_visible_contents(&term, file!(), line!(), &[" ", "a ", "b ", "c "]); } diff --git a/term/src/test/csi.rs b/term/src/test/csi.rs index 22e82c39c..22302b453 100644 --- a/term/src/test/csi.rs +++ b/term/src/test/csi.rs @@ -25,7 +25,7 @@ fn test_rep() { term.print("h"); term.cup(1, 0); term.print("\x1b[2ba"); - assert_visible_contents(&term, file!(), line!(), &["hhha", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["hhha", " ", " "]); } #[test] @@ -34,7 +34,12 @@ fn test_irm() { term.print("foo"); term.cup(0, 0); term.print("\x1b[4hBAR"); - assert_visible_contents(&term, file!(), line!(), &["BARfoo", "", ""]); + assert_visible_contents( + &term, + file!(), + line!(), + &["BARfoo ", " ", " "], + ); } #[test] @@ -43,12 +48,12 @@ fn test_ich() { term.print("hey!wat?"); term.cup(1, 0); term.print("\x1b[2@"); - assert_visible_contents(&term, file!(), line!(), &["h e", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h e", "wat?", " "]); // check how we handle overflowing the width term.print("\x1b[12@"); - assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", " "]); term.print("\x1b[-12@"); - assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", " "]); } #[test] @@ -57,12 +62,12 @@ fn test_ech() { term.print("hey!wat?"); term.cup(1, 0); term.print("\x1b[2X"); - assert_visible_contents(&term, file!(), line!(), &["h !", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h !", "wat?", " "]); // check how we handle overflowing the width term.print("\x1b[12X"); - assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", " "]); term.print("\x1b[-12X"); - assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", ""]); + assert_visible_contents(&term, file!(), line!(), &["h ", "wat?", " "]); } #[test] @@ -71,14 +76,14 @@ fn test_dch() { term.print("hello world"); term.cup(1, 0); term.print("\x1b[P"); - assert_visible_contents(&term, file!(), line!(), &["hllo world"]); + assert_visible_contents(&term, file!(), line!(), &["hllo world "]); term.cup(4, 0); term.print("\x1b[2P"); - assert_visible_contents(&term, file!(), line!(), &["hlloorld"]); + assert_visible_contents(&term, file!(), line!(), &["hlloorld "]); term.print("\x1b[-2P"); - assert_visible_contents(&term, file!(), line!(), &["hlloorld"]); + assert_visible_contents(&term, file!(), line!(), &["hlloorld "]); } #[test] @@ -118,11 +123,11 @@ fn test_dl() { term.cup(0, 1); let seqno = term.current_seqno(); term.delete_lines(1); - assert_visible_contents(&term, file!(), line!(), &["a", "c", ""]); + assert_visible_contents(&term, file!(), line!(), &["a", "c", " "]); term.assert_cursor_pos(0, 1, None, Some(seqno)); term.cup(0, 0); term.delete_lines(2); - assert_visible_contents(&term, file!(), line!(), &["", "", ""]); + assert_visible_contents(&term, file!(), line!(), &[" ", " ", " "]); term.print("1\r\n2\r\n3"); term.cup(0, 1); term.delete_lines(-2); @@ -185,7 +190,7 @@ fn test_ed() { fn test_ed_erase_scrollback() { let mut term = TestTerm::new(3, 3, 3); term.print("abc\r\ndef\r\nghi\r\n111\r\n222\r\na\x1b[3J"); - assert_all_contents(&term, file!(), line!(), &["111", "222", "a"]); + assert_all_contents(&term, file!(), line!(), &["111", "222", "a "]); term.print("b"); - assert_all_contents(&term, file!(), line!(), &["111", "222", "ab"]); + assert_all_contents(&term, file!(), line!(), &["111", "222", "ab "]); } diff --git a/term/src/test/mod.rs b/term/src/test/mod.rs index a2c199f9b..b948c161f 100644 --- a/term/src/test/mod.rs +++ b/term/src/test/mod.rs @@ -327,7 +327,18 @@ fn test_semantic() { )); term.print("there"); - assert_visible_contents(&term, file!(), line!(), &["hello", "there", "", "", ""]); + assert_visible_contents( + &term, + file!(), + line!(), + &[ + "hello ", + "there ", + " ", + " ", + " ", + ], + ); term.cup(0, 2); term.print(format!( @@ -339,7 +350,13 @@ fn test_semantic() { &term, file!(), line!(), - &["hello", "there", "three", "", ""], + &[ + "hello ", + "there ", + "three ", + " ", + " ", + ], ); k9::snapshot!( @@ -349,8 +366,8 @@ fn test_semantic() { SemanticZone { start_y: 0, start_x: 0, - end_y: 2, - end_x: 4, + end_y: 4, + end_x: 9, semantic_type: Output, }, ] @@ -386,7 +403,7 @@ fn test_semantic() { let mut input = CellAttributes::default(); input.set_semantic_type(SemanticType::Input); - let mut prompt_line = Line::from_text("> ls -l", &output); + let mut prompt_line = Line::from_text("> ls -l ", &output); for i in 0..2 { prompt_line.cells_mut()[i] .attrs_mut() @@ -439,11 +456,11 @@ fn test_semantic() { line!(), &term.screen().visible_lines(), &[ - Line::from_text("hello", &output), - Line::from_text("there", &output), - Line::from_text("three", &output), + Line::from_text("hello ", &output), + Line::from_text("there ", &output), + Line::from_text("three ", &output), prompt_line, - Line::from_text("some file", &output), + Line::from_text("some file ", &output), ], Compare::TEXT | Compare::ATTRS, ); @@ -457,7 +474,18 @@ fn basic_output() { term.set_auto_wrap(false); term.print("hello, world!"); - assert_visible_contents(&term, file!(), line!(), &["", " hello, w!", "", "", ""]); + assert_visible_contents( + &term, + file!(), + line!(), + &[ + " ", + " hello, w!", + " ", + " ", + " ", + ], + ); term.set_auto_wrap(true); term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay); @@ -467,7 +495,13 @@ fn basic_output() { &term, file!(), line!(), - &[" ", " hello, wo", "rld!", "", ""], + &[ + " ", + " hello, wo", + "rld! ", + " ", + " ", + ], ); term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay); @@ -475,7 +509,13 @@ fn basic_output() { &term, file!(), line!(), - &[" ", " ", " ", "", ""], + &[ + " ", + " ", + " ", + " ", + " ", + ], ); term.cup(0, 2); @@ -486,7 +526,7 @@ fn basic_output() { &term, file!(), line!(), - &[" ", " ", "wo", "", ""], + &[" ", " ", "wo", " ", " "], ); term.erase_in_line(EraseInLine::EraseToStartOfLine); @@ -494,7 +534,13 @@ fn basic_output() { &term, file!(), line!(), - &[" ", " ", " ", "", ""], + &[ + " ", + " ", + " ", + " ", + " ", + ], ); } @@ -506,7 +552,7 @@ fn cursor_movement_damage() { let seqno = term.current_seqno(); term.print("fooo."); - assert_visible_contents(&term, file!(), line!(), &["foo", "o."]); + assert_visible_contents(&term, file!(), line!(), &["foo", "o. "]); term.assert_cursor_pos(2, 1, None, None); term.assert_dirty_lines(seqno, &[0, 1], None); @@ -553,7 +599,7 @@ fn scroll_up_within_left_and_right_margins() { &term, file!(), line!(), - &[&ones, &twos, &threes, &fours, &fives], + &["111 ", "222 ", "333 ", "44444", "555 "], ); term.set_mode("?69", true); // allow left/right margins to be set @@ -566,11 +612,11 @@ fn scroll_up_within_left_and_right_margins() { file!(), line!(), &[ - &ones, - &twos, + "111 ", + "222 ", &format!("3{}", "4".repeat(NUM_COLS + 1)), &format!("4{}", "5".repeat(NUM_COLS - 1)), - &format!("5{}", " ".repeat(NUM_COLS - 1)), + &format!("5{} ", " ".repeat(NUM_COLS - 1)), ], ); } @@ -599,7 +645,7 @@ fn scroll_down_within_left_and_right_margins() { &term, file!(), line!(), - &[&ones, &twos, &threes, &fours, &fives], + &["111 ", "222 ", "333 ", "44444", "555 "], ); term.set_mode("?69", true); // allow left/right margins to be set @@ -616,9 +662,9 @@ fn scroll_down_within_left_and_right_margins() { file!(), line!(), &[ - &ones, - &twos, - &format!("3{}", " ".repeat(NUM_COLS - 1)), + "111 ", + "222 ", + &format!("3{} ", " ".repeat(NUM_COLS - 1)), &format!("4{}", "3".repeat(NUM_COLS - 1)), &format!("5{}", "4".repeat(NUM_COLS + 1)), ], @@ -647,7 +693,12 @@ fn test_delete_lines() { let seqno = term.current_seqno(); term.assert_dirty_lines(seqno, &[], None); term.delete_lines(2); - assert_visible_contents(&term, file!(), line!(), &["111", "444", "555", "", ""]); + assert_visible_contents( + &term, + file!(), + line!(), + &["111", "444", "555", " ", " "], + ); term.assert_dirty_lines(seqno, &[1, 2, 3, 4], None); term.cup(0, 3); @@ -668,7 +719,12 @@ fn test_delete_lines() { print_all_lines(&term); term.delete_lines(2); - assert_visible_contents(&term, file!(), line!(), &["111", "aaa", "", "", "bbb"]); + assert_visible_contents( + &term, + file!(), + line!(), + &["111", "aaa", " ", " ", "bbb"], + ); term.assert_dirty_lines(seqno, &[1, 2, 3], None); // expand the scroll region to fill the screen @@ -678,7 +734,12 @@ fn test_delete_lines() { print_all_lines(&term); term.delete_lines(1); - assert_visible_contents(&term, file!(), line!(), &["aaa", "", "", "bbb", ""]); + assert_visible_contents( + &term, + file!(), + line!(), + &["aaa", " ", " ", "bbb", " "], + ); term.assert_dirty_lines(seqno, &[4], None); } @@ -692,7 +753,10 @@ fn test_dec_special_graphics() { &term, file!(), line!(), - &["ABC▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥DEF", "hello"], + &[ + "ABC▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥DEF ", + "hello ", + ], ); term = TestTerm::new(2, 50, 0); @@ -701,7 +765,10 @@ fn test_dec_special_graphics() { &term, file!(), line!(), - &["SO-ABC▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥DEF", "SI-hello"], + &[ + "SO-ABC▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥DEF ", + "SI-hello ", + ], ); } @@ -715,7 +782,12 @@ fn test_dec_double_width() { &term, file!(), line!(), - &["line1", "line2", "line3", "line4"], + &[ + "line1 ", + "line2 ", + "line3 ", + "line4 ", + ], ); let lines = term.screen().visible_lines(); @@ -736,35 +808,43 @@ fn test_resize_wrap() { &term, file!(), line!(), - &["111", "2222", "aa", "333", "", "", "", ""], + &[ + "111 ", "2222", "aa ", "333 ", " ", " ", " ", " ", + ], ); term.resize(LINES, 5, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222a", "a", "333", "", "", "", ""], + &["111 ", "2222a", "a", "333 ", " ", " ", " ", " "], ); term.resize(LINES, 6, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222aa", "333", "", "", "", "", ""], + &[ + "111 ", "2222aa", "333 ", " ", " ", " ", " ", " ", + ], ); term.resize(LINES, 7, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222aa", "333", "", "", "", "", ""], + &[ + "111 ", "2222aa", "333 ", " ", " ", " ", " ", " ", + ], ); term.resize(LINES, 8, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222aa", "333", "", "", "", "", ""], + &[ + "111 ", "2222aa", "333 ", " ", " ", " ", " ", " ", + ], ); // Resize smaller again @@ -773,28 +853,36 @@ fn test_resize_wrap() { &term, file!(), line!(), - &["111", "2222aa", "333", "", "", "", "", ""], + &[ + "111 ", "2222aa", "333 ", " ", " ", " ", " ", " ", + ], ); term.resize(LINES, 6, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222aa", "333", "", "", "", "", ""], + &[ + "111 ", "2222aa", "333 ", " ", " ", " ", " ", " ", + ], ); term.resize(LINES, 5, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222a", "a", "333", "", "", "", ""], + &[ + "111 ", "2222a", "a", "333 ", " ", " ", " ", " ", + ], ); term.resize(LINES, 4, 0, 0); assert_visible_contents( &term, file!(), line!(), - &["111", "2222", "aa", "333", "", "", "", ""], + &[ + "111 ", "2222", "aa", "333 ", " ", " ", " ", " ", + ], ); } @@ -803,9 +891,9 @@ fn test_resize_wrap_issue_971() { const LINES: usize = 4; let mut term = TestTerm::new(LINES, 4, 0); term.print("====\r\nSS\r\n"); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); term.resize(LINES, 6, 0, 0); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); } #[test] @@ -813,9 +901,9 @@ fn test_resize_wrap_sgc_issue_978() { const LINES: usize = 4; let mut term = TestTerm::new(LINES, 4, 0); term.print("\u{1b}(0qqqq\u{1b}(B\r\nSS\r\n"); - assert_visible_contents(&term, file!(), line!(), &["────", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["────", "SS ", " ", " "]); term.resize(LINES, 6, 0, 0); - assert_visible_contents(&term, file!(), line!(), &["────", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["────", "SS ", " ", " "]); } #[test] @@ -823,9 +911,9 @@ fn test_resize_wrap_dectcm_issue_978() { const LINES: usize = 4; let mut term = TestTerm::new(LINES, 4, 0); term.print("\u{1b}[?25l====\u{1b}[?25h\r\nSS\r\n"); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); term.resize(LINES, 6, 0, 0); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); } #[test] @@ -833,44 +921,44 @@ fn test_resize_wrap_escape_code_issue_978() { const LINES: usize = 4; let mut term = TestTerm::new(LINES, 4, 0); term.print("====\u{1b}[0m\r\nSS\r\n"); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); term.resize(LINES, 6, 0, 0); - assert_visible_contents(&term, file!(), line!(), &["====", "SS", "", ""]); + assert_visible_contents(&term, file!(), line!(), &["====", "SS ", " ", " "]); } #[test] fn test_scrollup() { let mut term = TestTerm::new(2, 1, 4); term.print("1\n"); - assert_all_contents(&term, file!(), line!(), &["1", ""]); + assert_all_contents(&term, file!(), line!(), &["1", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 0); term.print("2\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 1); term.print("3\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "3", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "3", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 2); term.print("4\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 3); term.print("5\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", "5", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", "5", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 4); term.print("6\n"); - assert_all_contents(&term, file!(), line!(), &["2", "3", "4", "5", "6", ""]); + assert_all_contents(&term, file!(), line!(), &["2", "3", "4", "5", "6", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 5); term.print("7\n"); - assert_all_contents(&term, file!(), line!(), &["3", "4", "5", "6", "7", ""]); + assert_all_contents(&term, file!(), line!(), &["3", "4", "5", "6", "7", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 6); term.print("8\n"); - assert_all_contents(&term, file!(), line!(), &["4", "5", "6", "7", "8", ""]); + assert_all_contents(&term, file!(), line!(), &["4", "5", "6", "7", "8", " "]); assert_eq!(term.screen().visible_row_to_stable_row(0), 7); } @@ -878,14 +966,14 @@ fn test_scrollup() { fn test_ri() { let mut term = TestTerm::new(3, 1, 10); term.print("1\n\u{8d}\n"); - assert_all_contents(&term, file!(), line!(), &["1", "", ""]); + assert_all_contents(&term, file!(), line!(), &["1", " ", " "]); } #[test] fn test_scroll_margins() { let mut term = TestTerm::new(3, 1, 10); term.print("1\n2\n3\n4\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "3", "4", " "]); let margins = CSI::Cursor(termwiz::escape::csi::Cursor::SetTopAndBottomMargins { top: OneBased::new(1), @@ -894,14 +982,19 @@ fn test_scroll_margins() { term.print(format!("{}", margins)); term.print("z\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "z", "4", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "z", "4", " "]); term.print("a\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "z", "a", "", ""]); + assert_all_contents(&term, file!(), line!(), &["1", "2", "z", "a", " ", " "]); term.cup(0, 1); term.print("W\n"); - assert_all_contents(&term, file!(), line!(), &["1", "2", "z", "a", "W", "", ""]); + assert_all_contents( + &term, + file!(), + line!(), + &["1", "2", "z", "a", "W", " ", " "], + ); } #[test] @@ -919,9 +1012,9 @@ fn test_emoji_with_modifier() { file!(), line!(), &[ - &format!("{}", waving_hand), - &format!("{}", waving_hand_dark_tone), - "", + &format!("{} ", waving_hand), + &format!("{} ", waving_hand_dark_tone), + " ", ], ); } @@ -941,7 +1034,11 @@ fn test_hyperlinks() { file!(), line!(), &term.screen().visible_lines(), - &[Line::from_text("hello", &linked), "".into(), "".into()], + &[ + Line::from_text("hello", &linked), + " ".into(), + " ".into(), + ], Compare::TEXT | Compare::ATTRS, ); @@ -958,7 +1055,7 @@ fn test_hyperlinks() { &[ Line::from_text_with_wrapped_last_col("hello", &linked), Line::from_text("hey!!", &linked), - "".into(), + " ".into(), ], Compare::TEXT | Compare::ATTRS, ); diff --git a/termwiz/src/surface/line.rs b/termwiz/src/surface/line.rs index 323de6bd0..404614a92 100644 --- a/termwiz/src/surface/line.rs +++ b/termwiz/src/surface/line.rs @@ -62,7 +62,7 @@ pub enum DoubleClickRange { impl Line { pub fn with_width(width: usize) -> Self { let mut cells = Vec::with_capacity(width); - cells.resize(width, Cell::default()); + cells.resize_with(width, Cell::blank); let bits = LineBits::NONE; Self { bits, @@ -99,16 +99,17 @@ impl Line { } pub fn resize_and_clear(&mut self, width: usize, seqno: SequenceNo) { - let blank = Cell::default(); - self.cells.clear(); - self.cells.resize(width, blank); + for c in &mut self.cells { + *c = Cell::blank(); + } + self.cells.resize_with(width, Cell::blank); self.cells.shrink_to_fit(); self.update_last_change_seqno(seqno); self.bits = LineBits::NONE; } pub fn resize(&mut self, width: usize, seqno: SequenceNo) { - self.cells.resize(width, Cell::default()); + self.cells.resize_with(width, Cell::blank); self.update_last_change_seqno(seqno); } @@ -116,7 +117,7 @@ impl Line { /// Returns the list of resultant line(s) pub fn wrap(mut self, width: usize, seqno: SequenceNo) -> Vec { if let Some(end_idx) = self.cells.iter().rposition(|c| c.str() != " ") { - self.cells.resize(end_idx + 1, Cell::default()); + self.cells.resize_with(end_idx + 1, Cell::blank); let mut lines: Vec<_> = self .cells @@ -448,7 +449,7 @@ impl Line { // if the line isn't wide enough, pad it out with the default attributes. if idx + width > self.cells.len() { - self.cells.resize(idx + width, Cell::default()); + self.cells.resize_with(idx + width, Cell::blank); } self.invalidate_implicit_hyperlinks(seqno); @@ -511,7 +512,7 @@ impl Line { } if x >= self.cells.len() { - self.cells.resize(x, Cell::default()); + self.cells.resize_with(x, Cell::blank); } // If we're inserting a wide cell, we should also insert the overlapped cells. @@ -557,7 +558,7 @@ impl Line { .iter() .rposition(|c| c.str() != " " || c.attrs() != &def_attr) { - self.cells.resize(end_idx + 1, Cell::default()); + self.cells.resize_with(end_idx + 1, Cell::blank); self.update_last_change_seqno(seqno); } }