From adc11f303a14e57445a91a4e41d55aab9e3b7370 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sun, 11 Feb 2018 13:48:51 -0800 Subject: [PATCH] Add some tests for hyperlink parsing and handling --- term/src/csi.rs | 8 ++-- term/src/hyperlink.rs | 7 +++ term/src/line.rs | 1 - term/src/terminalstate.rs | 8 +++- term/src/test/mod.rs | 91 ++++++++++++++++++++++++++++++++------- 5 files changed, 95 insertions(+), 20 deletions(-) diff --git a/term/src/csi.rs b/term/src/csi.rs index 56dc54cc5..f83cdf833 100644 --- a/term/src/csi.rs +++ b/term/src/csi.rs @@ -30,7 +30,7 @@ pub enum DecPrivateMode { #[derive(Debug)] pub enum CSIAction { - SetPen(CellAttributes), + SetPenNoLink(CellAttributes), SetForegroundColor(color::ColorAttribute), SetBackgroundColor(color::ColorAttribute), SetIntensity(Intensity), @@ -54,6 +54,7 @@ pub enum CSIAction { SaveCursor, RestoreCursor, ScrollLines(i64), + SoftReset, } /// Constrol Sequence Initiator (CSI) Parser. @@ -183,12 +184,12 @@ impl<'a> CSIParser<'a> { // With no parameters, reset to default pen. // Note that this empty case is only possible for the initial // iteration. - Some(CSIAction::SetPen(CellAttributes::default())) + Some(CSIAction::SetPenNoLink(CellAttributes::default())) } &[0, _..] => { // Explicitly set to default pen self.advance_by(1, params); - Some(CSIAction::SetPen(CellAttributes::default())) + Some(CSIAction::SetPenNoLink(CellAttributes::default())) } &[38, 2, _colorspace, red, green, blue, _..] => { // ISO-8613-6 true color foreground @@ -495,6 +496,7 @@ impl<'a> Iterator for CSIParser<'a> { ('l', &[b'?'], Some(params)) => self.dec_reset_mode(params), ('m', &[], Some(params)) => self.sgr(params), ('n', &[], Some(params)) => self.dsr(params), + ('p', &[b'!'], Some(&[])) => Some(CSIAction::SoftReset), ('r', &[], Some(params)) => self.set_scroll_region(params), // SCOSC: Save Cursor diff --git a/term/src/hyperlink.rs b/term/src/hyperlink.rs index a091225ab..689d6b1a2 100644 --- a/term/src/hyperlink.rs +++ b/term/src/hyperlink.rs @@ -26,6 +26,13 @@ impl Hyperlink { id: (**id).into(), } } + + pub fn with_id(url: &str, id: &str) -> Self { + Self { + url: url.into(), + id: id.into(), + } + } } /// The spec says that the escape sequence is of the form: diff --git a/term/src/line.rs b/term/src/line.rs index dd83e7676..76d173b56 100644 --- a/term/src/line.rs +++ b/term/src/line.rs @@ -103,7 +103,6 @@ impl Line { clusters } - #[allow(dead_code)] pub fn from_text(s: &str, attrs: &CellAttributes) -> Line { let mut cells = Vec::new(); diff --git a/term/src/terminalstate.rs b/term/src/terminalstate.rs index ff9d5b1a8..d9f525834 100644 --- a/term/src/terminalstate.rs +++ b/term/src/terminalstate.rs @@ -777,8 +777,14 @@ impl TerminalState { fn perform_csi(&mut self, act: CSIAction) { debug!("{:?}", act); match act { - CSIAction::SetPen(pen) => { + CSIAction::SoftReset => { + self.pen = CellAttributes::default(); + // TODO: see https://vt100.net/docs/vt510-rm/DECSTR.html + } + CSIAction::SetPenNoLink(pen) => { + let link = self.pen.hyperlink.take(); self.pen = pen; + self.pen.hyperlink = link; } CSIAction::SetForegroundColor(color) => { self.pen.foreground = color; diff --git a/term/src/test/mod.rs b/term/src/test/mod.rs index b19ce092b..1942c5fed 100644 --- a/term/src/test/mod.rs +++ b/term/src/test/mod.rs @@ -98,6 +98,19 @@ impl TestTerm { }; self.print(format!("{}K", num)); } + + fn hyperlink(&mut self, link: &Rc) { + self.print(format!("\x1b]8;id={};{}\x1b\\", link.id, link.url)); + } + + fn hyperlink_off(&mut self) { + self.print("\x1b]8;;\x1b\\"); + } + + fn soft_reset(&mut self) { + self.print(CSI); + self.print("!p"); + } } impl Deref for TestTerm { @@ -190,7 +203,7 @@ bitflags! { struct Compare : u8{ const TEXT = 1; const ATTRS = 2; - const DIRTY = 3; + const DIRTY = 4; } } @@ -203,20 +216,6 @@ fn print_visible_lines(term: &Terminal) { } } -/// Asserts that the visible lines of the terminal have the -/// same cell contents. The cells must exactly match. -#[allow(dead_code)] -fn assert_visible_lines(term: &Terminal, expect_lines: &[Line]) { - print_visible_lines(&term); - let screen = term.screen(); - - assert_lines_equal( - screen.visible_lines(), - expect_lines, - Compare::ATTRS | Compare::TEXT, - ); -} - /// Asserts that the visible lines of the terminal have the /// same character contents as the expected lines. /// The other cell attributes are not compared; this is @@ -348,3 +347,65 @@ fn test_delete_lines() { assert_visible_contents(&term, &["111", "aaa", " ", "bbb", " "]); assert_dirty_lines!(&term, &[1, 2, 3, 4]); } + +#[test] +fn test_hyperlinks() { + let mut term = TestTerm::new(3, 5, 0); + let link = Rc::new(Hyperlink::with_id("http://example.com", "")); + term.hyperlink(&link); + term.print("hello"); + term.hyperlink_off(); + + let mut linked = CellAttributes::default(); + linked.hyperlink = Some(Rc::clone(&link)); + + assert_lines_equal( + term.screen().visible_lines(), + &[ + Line::from_text("hello", &linked), + " ".into(), + " ".into(), + ], + Compare::TEXT | Compare::ATTRS, + ); + + term.hyperlink(&link); + term.print("he"); + // Resetting pen should not reset the link + term.print("\x1b[m"); + term.print("y!!"); + + assert_lines_equal( + term.screen().visible_lines(), + &[ + Line::from_text("hello", &linked), + Line::from_text("hey!!", &linked), + " ".into(), + ], + Compare::TEXT | Compare::ATTRS, + ); + + let otherlink = Rc::new(Hyperlink::with_id("http://example.com/other", "w00t")); + + // Switching link and turning it off + term.hyperlink(&otherlink); + term.print("wo"); + // soft reset also disables hyperlink attribute + term.soft_reset(); + term.print("00t"); + + let mut partial_line: Line = "wo00t".into(); + partial_line.cells[0].attrs.hyperlink = Some(Rc::clone(&otherlink)); + partial_line.cells[1].attrs.hyperlink = Some(Rc::clone(&otherlink)); + + assert_lines_equal( + term.screen().visible_lines(), + &[ + Line::from_text("hello", &linked), + Line::from_text("hey!!", &linked), + partial_line, + ], + Compare::TEXT | Compare::ATTRS, + ); + +}