From 665238f5318bee40f254fe43aa158e61bdd25392 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 5 Nov 2019 10:26:19 -0500 Subject: contents_formatted should return a Vec the overall terminal escape sequence byte stream is not necessarily utf8-safe, even if individual cell contents are --- src/attrs.rs | 19 ++++++++++++++----- src/grid.rs | 23 ++++++++++++++++------- src/row.rs | 10 +++++----- src/screen.rs | 2 +- tests/escape.rs | 12 ++++++------ tests/init.rs | 2 +- tests/split-escapes.rs | 2 +- tests/window_contents.rs | 16 ++++++++-------- 8 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index f1b59a6..95a5e76 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -87,11 +87,11 @@ impl Attrs { } } - pub fn escape_code_diff(&self, other: &Self) -> String { + pub fn escape_code_diff(&self, other: &Self) -> Vec { let mut opts = vec![]; if self != other && self == &Self::default() { - return "\x1b[m".to_string(); + return b"\x1b[m".to_vec(); } if self.fgcolor != other.fgcolor { @@ -159,8 +159,17 @@ impl Attrs { opts.push(if self.inverse() { 7 } else { 27 }); } - let strs: Vec<_> = - opts.iter().map(std::string::ToString::to_string).collect(); - format!("\x1b[{}m", strs.join(";")) + let mut bytes = b"\x1b[".to_vec(); + let mut first = true; + for opt in opts { + if first { + first = false; + } else { + bytes.push(b';'); + } + bytes.extend(opt.to_string().as_bytes()); + } + bytes.push(b'm'); + bytes } } diff --git a/src/grid.rs b/src/grid.rs index a5bca31..50942f4 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -139,18 +139,27 @@ impl Grid { col_start: u16, row_end: u16, col_end: u16, - ) -> String { - let mut contents = String::new(); + ) -> Vec { + let mut contents = vec![]; let mut prev_attrs = crate::attrs::Attrs::default(); let row_start = row_start as usize; let row_end = row_end as usize; for row in self.rows().skip(row_start).take(row_end - row_start + 1) { - let (new_contents, new_attrs) = - &row.contents_formatted(col_start, col_end, prev_attrs); - contents += new_contents; - prev_attrs = *new_attrs; + let (mut new_contents, new_attrs) = + row.contents_formatted(col_start, col_end, prev_attrs); + contents.append(&mut new_contents); + prev_attrs = new_attrs; } - contents.trim_end().to_string() + + let mut idx = None; + for (i, b) in contents.iter().enumerate().rev() { + if !(*b as char).is_whitespace() { + idx = Some(i + 1); + break; + } + } + contents.truncate(idx.unwrap_or(0)); + contents } pub fn erase_all(&mut self) { diff --git a/src/row.rs b/src/row.rs index 220cc0c..4246d33 100644 --- a/src/row.rs +++ b/src/row.rs @@ -91,9 +91,9 @@ impl Row { col_start: u16, col_end: u16, attrs: crate::attrs::Attrs, - ) -> (String, crate::attrs::Attrs) { + ) -> (Vec, crate::attrs::Attrs) { let mut prev_was_wide = false; - let mut contents = String::new(); + let mut contents = vec![]; let mut prev_attrs = attrs; if let Some(max_col) = self.max_col() { for col in col_start..=(col_end.min(max_col)) { @@ -106,7 +106,7 @@ impl Row { let attrs = cell.attrs(); if &prev_attrs != attrs { - contents += &attrs.escape_code_diff(&prev_attrs); + contents.append(&mut attrs.escape_code_diff(&prev_attrs)); prev_attrs = *attrs; } @@ -116,13 +116,13 @@ impl Row { } else { cell_contents }; - contents += cell_contents; + contents.extend(cell_contents.as_bytes()); prev_was_wide = cell.is_wide(); } } if !self.wrapped { - contents += "\r\n"; + contents.extend(b"\r\n"); } (contents, prev_attrs) } diff --git a/src/screen.rs b/src/screen.rs index 63d7205..a1bf53b 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -151,7 +151,7 @@ impl Screen { col_start: u16, row_end: u16, col_end: u16, - ) -> String { + ) -> Vec { self.grid() .contents_formatted(row_start, col_start, row_end, col_end) } diff --git a/tests/escape.rs b/tests/escape.rs index d2dd5b1..2c7d745 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -26,7 +26,7 @@ fn ris() { assert_eq!(cell.contents(), ""); assert_eq!(parser.screen().contents(0, 0, 23, 79), ""); - assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), ""); + assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), b""); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); @@ -64,7 +64,7 @@ fn ris() { assert_eq!(parser.screen().contents(0, 0, 23, 79), "foo"); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "f\x1b[31;47;1;3;4moo" + b"f\x1b[31;47;1;3;4moo" ); assert_eq!(parser.screen().title(), "window title"); @@ -100,7 +100,7 @@ fn ris() { assert_eq!(cell.contents(), ""); assert_eq!(parser.screen().contents(0, 0, 23, 79), ""); - assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), ""); + assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), b""); // title and icon name don't change with reset assert_eq!(parser.screen().title(), "window title"); @@ -168,20 +168,20 @@ fn decsc() { assert_eq!(parser.screen().cursor_position(), (4, 3)); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "\r\n\r\n\r\n\r\n\x1b[31mfoo" + b"\r\n\r\n\r\n\r\n\x1b[31mfoo" ); parser.process(b"\x1b[32m\x1b[?6lbar"); assert_eq!(parser.screen().cursor_position(), (0, 3)); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "\x1b[32mbar\r\n\r\n\r\n\r\n\x1b[31mfoo" + b"\x1b[32mbar\r\n\r\n\r\n\r\n\x1b[31mfoo" ); parser.process(b"\x1b8\x1b[Hz"); assert_eq!(parser.screen().cursor_position(), (4, 1)); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "\x1b[32mbar\r\n\r\n\r\n\r\n\x1b[31mzoo" + b"\x1b[32mbar\r\n\r\n\r\n\r\n\x1b[31mzoo" ); } diff --git a/tests/init.rs b/tests/init.rs index cf9d146..61395e4 100644 --- a/tests/init.rs +++ b/tests/init.rs @@ -16,7 +16,7 @@ fn init() { assert!(cell.is_none()); assert_eq!(parser.screen().contents(0, 0, 23, 79), ""); - assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), ""); + assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), b""); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); diff --git a/tests/split-escapes.rs b/tests/split-escapes.rs index 36a9eaa..157ff29 100644 --- a/tests/split-escapes.rs +++ b/tests/split-escapes.rs @@ -7,7 +7,7 @@ fn get_file_contents(name: &str) -> Vec { buf } -fn write_to_parser(chunks: &mut Vec>) -> (String, String) { +fn write_to_parser(chunks: &mut Vec>) -> (String, Vec) { let mut parser = vt100::Parser::new(37, 193); for chunk in chunks.iter_mut() { parser.process(&chunk); diff --git a/tests/window_contents.rs b/tests/window_contents.rs index 898351e..5fe0cda 100644 --- a/tests/window_contents.rs +++ b/tests/window_contents.rs @@ -2,7 +2,7 @@ fn formatted() { let mut parser = vt100::Parser::new(24, 80); compare_formatted(&parser); - assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), ""); + assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), b""); parser.process(b"foobar"); compare_formatted(&parser); @@ -10,7 +10,7 @@ fn formatted() { assert!(!parser.screen().cell(0, 3).unwrap().bold()); assert!(!parser.screen().cell(0, 4).unwrap().bold()); assert!(!parser.screen().cell(0, 5).unwrap().bold()); - assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), "foobar"); + assert_eq!(parser.screen().contents_formatted(0, 0, 23, 79), b"foobar"); parser.process(b"\x1b[1;4H\x1b[1;7m\x1b[33mb"); compare_formatted(&parser); @@ -20,7 +20,7 @@ fn formatted() { assert!(!parser.screen().cell(0, 5).unwrap().bold()); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "foo\x1b[33;1;7mb\x1b[mar" + b"foo\x1b[33;1;7mb\x1b[mar" ); parser.process(b"\x1b[1;5H\x1b[22;42ma"); @@ -31,27 +31,27 @@ fn formatted() { assert!(!parser.screen().cell(0, 5).unwrap().bold()); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "foo\x1b[33;1;7mb\x1b[42;22ma\x1b[mr" + b"foo\x1b[33;1;7mb\x1b[42;22ma\x1b[mr" ); parser.process(b"\x1b[1;6H\x1b[35mr\r\nquux"); compare_formatted(&parser); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\nquux" + &b"foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\nquux"[..] ); parser.process(b"\x1b[2;1H\x1b[45mquux"); compare_formatted(&parser); assert_eq!( parser.screen().contents_formatted(0, 0, 23, 79), - "foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mquux" + &b"foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mquux"[..] ); parser .process(b"\x1b[2;2H\x1b[38;2;123;213;231mu\x1b[38;5;254mu\x1b[39mx"); compare_formatted(&parser); - assert_eq!(parser.screen().contents_formatted(0, 0 ,23, 79), "foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mq\x1b[38;2;123;213;231mu\x1b[38;5;254mu\x1b[39mx"); + assert_eq!(parser.screen().contents_formatted(0, 0 ,23, 79), &b"foo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mq\x1b[38;2;123;213;231mu\x1b[38;5;254mu\x1b[39mx"[..]); } fn compare_formatted(parser: &vt100::Parser) { @@ -59,7 +59,7 @@ fn compare_formatted(parser: &vt100::Parser) { let contents = parser.screen().contents_formatted(0, 0, rows - 1, cols - 1); let mut parser2 = vt100::Parser::new(rows, cols); - parser2.process(contents.as_bytes()); + parser2.process(&contents); compare_cells(parser, &parser2); } -- cgit v1.2.3-54-g00ecf