From e85e57949df612e22463c06fc31fb0b7957ea3c7 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 12 Nov 2019 15:37:31 -0500 Subject: split formatted calculations into multiple methods different applications want to be able to apply different things, so this gives more control --- CHANGELOG.md | 16 ++++ src/screen.rs | 153 +++++++++++++++++++++++++++++++------ src/term.rs | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++- tests/control.rs | 13 +++- tests/escape.rs | 23 ++++-- tests/mode.rs | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/osc.rs | 77 +++++++++++++++++++ 7 files changed, 692 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0971274..85eea08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## Unreleased + +### Added + +* `Screen::input_mode_formatted` and `Screen::input_mode_diff` give escape + codes to set the current terminal input modes. +* `Screen::title_formatted` and `Screen::title_diff` give escape codes to set + the terminal window title. +* `Screen::bells_diff` gives escape codes to trigger any audible or visual + bells which have been seen since the previous state. + +### Changed + +* `Screen::contents_diff` no longer includes audible or visual bells (see + `Screen::bells_diff` instead). + ## [0.5.1] - 2019-11-12 ### Fixed diff --git a/src/screen.rs b/src/screen.rs index d4613f0..3128f78 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -169,14 +169,11 @@ impl Screen { }) } - /// Returns the formatted contents of the terminal. + /// Returns the formatted visible contents of the terminal. /// /// Formatting information will be included inline as terminal escape /// codes. The result will be suitable for feeding directly to a raw - /// terminal parser, and will result in the same visual output. Internal - /// terminal modes (such as application keypad mode or alternate screen - /// mode) will not be included here, but modes that affect the visible - /// output (such as hidden cursor mode) will. + /// terminal parser, and will result in the same visual output. pub fn contents_formatted(&self) -> Vec { let mut contents = vec![]; self.write_contents_formatted(&mut contents); @@ -189,16 +186,16 @@ impl Screen { self.attrs.write_escape_code_diff(contents, &prev_attrs); } - /// Returns the formatted contents of the terminal by row, restricted to - /// the given subset of columns. + /// Returns the formatted visible contents of the terminal by row, + /// restricted to the given subset of columns. /// /// Formatting information will be included inline as terminal escape /// codes. The result will be suitable for feeding directly to a raw - /// terminal parser, and will result in the same visual output. Internal - /// terminal modes (such as application keypad mode or alternate screen - /// mode) will not be included here. + /// terminal parser, and will result in the same visual output. /// - /// CRLF at the end of lines will not be included. + /// You are responsible for positioning the cursor before printing each + /// row, and final cursor position after displaying each row is + /// unspecified. pub fn rows_formatted( &self, start: u16, @@ -220,8 +217,9 @@ impl Screen { }) } - /// Returns a terminal byte stream sufficient to turn the screen described - /// by `prev` into the screen described by `self`. + /// Returns a terminal byte stream sufficient to turn the visible contents + /// of the screen described by `prev` into the visible contents of the + /// screen described by `self`. /// /// The result of rendering `prev.contents_formatted()` followed by /// `self.contents_diff(prev)` should be equivalent to the result of @@ -246,22 +244,16 @@ impl Screen { prev.attrs, ); self.attrs.write_escape_code_diff(contents, &prev_attrs); - if self.audible_bell_count != prev.audible_bell_count { - crate::term::AudibleBell::default().write_buf(contents); - } - if self.visual_bell_count != prev.visual_bell_count { - crate::term::VisualBell::default().write_buf(contents); - } } /// Returns a sequence of terminal byte streams sufficient to turn the - /// subset of each row from `prev` (as described by `start` and `width`) - /// into the corresponding row subset in `self`. + /// visible contents of the subset of each row from `prev` (as described + /// by `start` and `width`) into the visible contents of the corresponding + /// row subset in `self`. /// - /// You must handle the initial row positioning yourself - each row diff - /// expects to start out positioned at the start of that row. Internal - /// terminal modes (such as application keypad mode or alternate screen - /// mode) will not be included here. + /// You are responsible for positioning the cursor before printing each + /// row, and final cursor position after displaying each row is + /// unspecified. pub fn rows_diff<'a>( &'a self, prev: &'a Self, @@ -289,6 +281,117 @@ impl Screen { }) } + pub fn input_mode_formatted(&self) -> Vec { + let mut contents = vec![]; + self.write_input_mode_formatted(&mut contents); + contents + } + + fn write_input_mode_formatted(&self, contents: &mut Vec) { + crate::term::ApplicationKeypad::new( + self.mode(Mode::ApplicationKeypad), + ) + .write_buf(contents); + crate::term::ApplicationCursor::new( + self.mode(Mode::ApplicationCursor), + ) + .write_buf(contents); + crate::term::BracketedPaste::new(self.mode(Mode::BracketedPaste)) + .write_buf(contents); + crate::term::MouseProtocolMode::new( + self.mouse_protocol_mode, + MouseProtocolMode::None, + ) + .write_buf(contents); + crate::term::MouseProtocolEncoding::new( + self.mouse_protocol_encoding, + MouseProtocolEncoding::Default, + ) + .write_buf(contents); + } + + pub fn input_mode_diff(&self, prev: &Self) -> Vec { + let mut contents = vec![]; + self.write_input_mode_diff(&mut contents, prev); + contents + } + + fn write_input_mode_diff(&self, contents: &mut Vec, prev: &Self) { + if self.mode(Mode::ApplicationKeypad) + != prev.mode(Mode::ApplicationKeypad) + { + crate::term::ApplicationKeypad::new( + self.mode(Mode::ApplicationKeypad), + ) + .write_buf(contents); + } + if self.mode(Mode::ApplicationCursor) + != prev.mode(Mode::ApplicationCursor) + { + crate::term::ApplicationCursor::new( + self.mode(Mode::ApplicationCursor), + ) + .write_buf(contents); + } + if self.mode(Mode::BracketedPaste) != prev.mode(Mode::BracketedPaste) + { + crate::term::BracketedPaste::new(self.mode(Mode::BracketedPaste)) + .write_buf(contents); + } + crate::term::MouseProtocolMode::new( + self.mouse_protocol_mode, + prev.mouse_protocol_mode, + ) + .write_buf(contents); + crate::term::MouseProtocolEncoding::new( + self.mouse_protocol_encoding, + prev.mouse_protocol_encoding, + ) + .write_buf(contents); + } + + pub fn title_formatted(&self) -> Vec { + let mut contents = vec![]; + self.write_title_formatted(&mut contents); + contents + } + + fn write_title_formatted(&self, contents: &mut Vec) { + crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "") + .write_buf(contents); + } + + pub fn title_diff(&self, prev: &Self) -> Vec { + let mut contents = vec![]; + self.write_title_diff(&mut contents, prev); + contents + } + + fn write_title_diff(&self, contents: &mut Vec, prev: &Self) { + crate::term::ChangeTitle::new( + &self.icon_name, + &self.title, + &prev.icon_name, + &prev.title, + ) + .write_buf(contents); + } + + pub fn bells_diff(&self, prev: &Self) -> Vec { + let mut contents = vec![]; + self.write_bells_diff(&mut contents, prev); + contents + } + + fn write_bells_diff(&self, contents: &mut Vec, prev: &Self) { + if self.audible_bell_count != prev.audible_bell_count { + crate::term::AudibleBell::default().write_buf(contents); + } + if self.visual_bell_count != prev.visual_bell_count { + crate::term::VisualBell::default().write_buf(contents); + } + } + /// Returns the `Cell` object at the given location in the terminal, if it /// exists. pub fn cell(&self, row: u16, col: u16) -> Option<&crate::cell::Cell> { diff --git a/src/term.rs b/src/term.rs index c988702..d2f7c9b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -299,18 +299,18 @@ impl BufWrite for EraseChar { #[derive(Default, Debug)] #[must_use = "this struct does nothing unless you call write_buf"] pub struct HideCursor { - hide: bool, + state: bool, } impl HideCursor { - pub fn new(hide: bool) -> Self { - Self { hide } + pub fn new(state: bool) -> Self { + Self { state } } } impl BufWrite for HideCursor { fn write_buf(&self, buf: &mut Vec) { - if self.hide { + if self.state { buf.extend_from_slice(b"\x1b[?25l") } else { buf.extend_from_slice(b"\x1b[?25h") @@ -364,3 +364,215 @@ impl BufWrite for VisualBell { buf.extend_from_slice(b"\x1bg"); } } + +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ChangeTitle<'a> { + icon_name: &'a str, + title: &'a str, + prev_icon_name: &'a str, + prev_title: &'a str, +} + +impl<'a> ChangeTitle<'a> { + pub fn new( + icon_name: &'a str, + title: &'a str, + prev_icon_name: &'a str, + prev_title: &'a str, + ) -> Self { + Self { + icon_name, + title, + prev_icon_name, + prev_title, + } + } +} + +impl<'a> BufWrite for ChangeTitle<'a> { + fn write_buf(&self, buf: &mut Vec) { + if self.icon_name == self.title + && (self.icon_name != self.prev_icon_name + || self.title != self.prev_title) + { + buf.extend_from_slice(b"\x1b]0;"); + buf.extend_from_slice(self.icon_name.as_bytes()); + buf.push(b'\x07'); + } else { + if self.icon_name != self.prev_icon_name { + buf.extend_from_slice(b"\x1b]1;"); + buf.extend_from_slice(self.icon_name.as_bytes()); + buf.push(b'\x07'); + } + if self.title != self.prev_title { + buf.extend_from_slice(b"\x1b]2;"); + buf.extend_from_slice(self.title.as_bytes()); + buf.push(b'\x07'); + } + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ApplicationKeypad { + state: bool, +} + +impl ApplicationKeypad { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for ApplicationKeypad { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b=") + } else { + buf.extend_from_slice(b"\x1b>") + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ApplicationCursor { + state: bool, +} + +impl ApplicationCursor { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for ApplicationCursor { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b[?1h") + } else { + buf.extend_from_slice(b"\x1b[?1l") + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct BracketedPaste { + state: bool, +} + +impl BracketedPaste { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for BracketedPaste { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b[?2004h") + } else { + buf.extend_from_slice(b"\x1b[?2004l") + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MouseProtocolMode { + mode: crate::screen::MouseProtocolMode, + prev: crate::screen::MouseProtocolMode, +} + +impl MouseProtocolMode { + pub fn new( + mode: crate::screen::MouseProtocolMode, + prev: crate::screen::MouseProtocolMode, + ) -> Self { + Self { mode, prev } + } +} + +impl BufWrite for MouseProtocolMode { + fn write_buf(&self, buf: &mut Vec) { + if self.mode == self.prev { + return; + } + + match self.mode { + crate::screen::MouseProtocolMode::None => match self.prev { + crate::screen::MouseProtocolMode::None => {} + crate::screen::MouseProtocolMode::Press => { + buf.extend_from_slice(b"\x1b[?9l"); + } + crate::screen::MouseProtocolMode::PressRelease => { + buf.extend_from_slice(b"\x1b[?1000l"); + } + crate::screen::MouseProtocolMode::ButtonMotion => { + buf.extend_from_slice(b"\x1b[?1002l"); + } + crate::screen::MouseProtocolMode::AnyMotion => { + buf.extend_from_slice(b"\x1b[?1003l"); + } + }, + crate::screen::MouseProtocolMode::Press => { + buf.extend_from_slice(b"\x1b[?9h"); + } + crate::screen::MouseProtocolMode::PressRelease => { + buf.extend_from_slice(b"\x1b[?1000h"); + } + crate::screen::MouseProtocolMode::ButtonMotion => { + buf.extend_from_slice(b"\x1b[?1002h"); + } + crate::screen::MouseProtocolMode::AnyMotion => { + buf.extend_from_slice(b"\x1b[?1003h"); + } + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MouseProtocolEncoding { + encoding: crate::screen::MouseProtocolEncoding, + prev: crate::screen::MouseProtocolEncoding, +} + +impl MouseProtocolEncoding { + pub fn new( + encoding: crate::screen::MouseProtocolEncoding, + prev: crate::screen::MouseProtocolEncoding, + ) -> Self { + Self { encoding, prev } + } +} + +impl BufWrite for MouseProtocolEncoding { + fn write_buf(&self, buf: &mut Vec) { + if self.encoding == self.prev { + return; + } + + match self.encoding { + crate::screen::MouseProtocolEncoding::Default => { + match self.prev { + crate::screen::MouseProtocolEncoding::Default => {} + crate::screen::MouseProtocolEncoding::Utf8 => { + buf.extend_from_slice(b"\x1b[?1005l"); + } + crate::screen::MouseProtocolEncoding::Sgr => { + buf.extend_from_slice(b"\x1b[?1006l"); + } + } + } + crate::screen::MouseProtocolEncoding::Utf8 => { + buf.extend_from_slice(b"\x1b[?1005h"); + } + crate::screen::MouseProtocolEncoding::Sgr => { + buf.extend_from_slice(b"\x1b[?1006h"); + } + } + } +} diff --git a/tests/control.rs b/tests/control.rs index 8372cea..e0dc276 100644 --- a/tests/control.rs +++ b/tests/control.rs @@ -9,27 +9,32 @@ fn bel() { parser.process(b"\x07"); assert_eq!(parser.screen().audible_bell_count(), 1); assert_eq!(parser.screen().audible_bell_count(), 1); - assert_eq!(parser.screen().contents_diff(&screen), b"\x07"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x07"); let screen = parser.screen().clone(); parser.process(b"\x07"); assert_eq!(parser.screen().audible_bell_count(), 2); - assert_eq!(parser.screen().contents_diff(&screen), b"\x07"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x07"); let screen = parser.screen().clone(); parser.process(b"\x07\x07\x07"); assert_eq!(parser.screen().audible_bell_count(), 5); - assert_eq!(parser.screen().contents_diff(&screen), b"\x07"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x07"); let screen = parser.screen().clone(); parser.process(b"foo"); assert_eq!(parser.screen().audible_bell_count(), 5); assert_eq!(parser.screen().contents_diff(&screen), b"foo"); + assert_eq!(parser.screen().bells_diff(&screen), b""); let screen = parser.screen().clone(); parser.process(b"ba\x07r"); assert_eq!(parser.screen().audible_bell_count(), 6); - assert_eq!(parser.screen().contents_diff(&screen), b"bar\x07"); + assert_eq!(parser.screen().contents_diff(&screen), b"bar"); + assert_eq!(parser.screen().bells_diff(&screen), b"\x07"); } #[test] diff --git a/tests/escape.rs b/tests/escape.rs index 96ee762..67d6a87 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -68,6 +68,10 @@ fn ris() { &b"\x1b[?25l\x1b[m\x1b[H\x1b[Jf\x1b[31;47;1;3;4moo\x1b[21;21H\x1b[7m" [..] ); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]1;window icon name\x07\x1b]2;window title\x07"[..] + ); assert_eq!(parser.screen().title(), "window title"); assert_eq!(parser.screen().icon_name(), "window icon name"); @@ -96,7 +100,11 @@ fn ris() { assert_eq!(parser.screen().contents(), ""); assert_eq!( parser.screen().contents_formatted(), - b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + &b"\x1b[?25h\x1b[m\x1b[H\x1b[J"[..] + ); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]1;window icon name\x07\x1b]2;window title\x07"[..] ); // title and icon name don't change with reset @@ -130,27 +138,32 @@ fn vb() { parser.process(b"\x1bg"); assert_eq!(parser.screen().visual_bell_count(), 1); assert_eq!(parser.screen().visual_bell_count(), 1); - assert_eq!(parser.screen().contents_diff(&screen), b"\x1bg"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x1bg"); let screen = parser.screen().clone(); parser.process(b"\x1bg"); assert_eq!(parser.screen().visual_bell_count(), 2); - assert_eq!(parser.screen().contents_diff(&screen), b"\x1bg"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x1bg"); let screen = parser.screen().clone(); parser.process(b"\x1bg\x1bg\x1bg"); assert_eq!(parser.screen().visual_bell_count(), 5); - assert_eq!(parser.screen().contents_diff(&screen), b"\x1bg"); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!(parser.screen().bells_diff(&screen), b"\x1bg"); let screen = parser.screen().clone(); parser.process(b"foo"); assert_eq!(parser.screen().visual_bell_count(), 5); assert_eq!(parser.screen().contents_diff(&screen), b"foo"); + assert_eq!(parser.screen().bells_diff(&screen), b""); let screen = parser.screen().clone(); parser.process(b"ba\x1bgr"); assert_eq!(parser.screen().visual_bell_count(), 6); - assert_eq!(parser.screen().contents_diff(&screen), b"bar\x1bg"); + assert_eq!(parser.screen().contents_diff(&screen), b"bar"); + assert_eq!(parser.screen().bells_diff(&screen), b"\x1bg"); } #[test] diff --git a/tests/mode.rs b/tests/mode.rs index 5e222fe..e2404c6 100644 --- a/tests/mode.rs +++ b/tests/mode.rs @@ -15,7 +15,16 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1l\x1b[?2004l" + ); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1h"); assert!(!parser.screen().application_keypad()); @@ -30,7 +39,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?9h"); assert!(!parser.screen().application_keypad()); @@ -45,7 +65,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?9h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?9h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?25l"); assert!(!parser.screen().application_keypad()); @@ -60,7 +91,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b"\x1b[?25l"); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?9h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1000h"); assert!(!parser.screen().application_keypad()); @@ -75,7 +117,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?1000h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1000h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1002h"); assert!(!parser.screen().application_keypad()); @@ -90,7 +143,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?1002h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1002h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1003h"); assert!(!parser.screen().application_keypad()); @@ -105,7 +169,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?1003h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1003h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1005h"); assert!(!parser.screen().application_keypad()); @@ -120,7 +195,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Utf8 ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?1003h\x1b[?1005h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1005h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1006h"); assert!(!parser.screen().application_keypad()); @@ -135,7 +221,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004l\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1006h"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?2004h"); assert!(!parser.screen().application_keypad()); @@ -150,7 +247,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1h\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?2004h"); + let screen = parser.screen().clone(); parser.process(b"\x1b="); assert!(parser.screen().application_keypad()); @@ -165,7 +273,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1h\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b="); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1l"); assert!(parser.screen().application_keypad()); @@ -180,7 +299,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1l"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?9l"); assert!(parser.screen().application_keypad()); @@ -195,7 +325,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25l\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?25h"); assert!(parser.screen().application_keypad()); @@ -210,7 +351,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b"\x1b[?25h"); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1000l"); assert!(parser.screen().application_keypad()); @@ -225,7 +377,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1002l"); assert!(parser.screen().application_keypad()); @@ -240,7 +403,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1003h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1003l"); assert!(parser.screen().application_keypad()); @@ -255,7 +429,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1003l"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1005l"); assert!(parser.screen().application_keypad()); @@ -270,7 +455,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Sgr ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h\x1b[?1006h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b""); + let screen = parser.screen().clone(); parser.process(b"\x1b[?1006l"); assert!(parser.screen().application_keypad()); @@ -285,7 +481,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004h" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?1006l"); + let screen = parser.screen().clone(); parser.process(b"\x1b[?2004l"); assert!(parser.screen().application_keypad()); @@ -300,7 +507,18 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b=\x1b[?1l\x1b[?2004l" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b[?2004l"); + let screen = parser.screen().clone(); parser.process(b"\x1b>"); assert!(!parser.screen().application_keypad()); @@ -315,6 +533,16 @@ fn modes() { parser.screen().mouse_protocol_encoding(), vt100::MouseProtocolEncoding::Default ); + assert_eq!( + parser.screen().contents_formatted(), + b"\x1b[?25h\x1b[m\x1b[H\x1b[J" + ); + assert_eq!(parser.screen().contents_diff(&screen), b""); + assert_eq!( + parser.screen().input_mode_formatted(), + b"\x1b>\x1b[?1l\x1b[?2004l" + ); + assert_eq!(parser.screen().input_mode_diff(&screen), b"\x1b>"); } #[test] diff --git a/tests/osc.rs b/tests/osc.rs index f9bcc67..7fd7e82 100644 --- a/tests/osc.rs +++ b/tests/osc.rs @@ -5,12 +5,26 @@ fn title() { let mut parser = vt100::Parser::default(); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + + let screen = parser.screen().clone(); parser.process(b"\x1b]2;it's a title\x07"); assert_eq!(parser.screen().title(), "it's a title"); assert_eq!(parser.screen().icon_name(), ""); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]2;it's a title\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]2;it's a title\x07"[..] + ); + + let screen = parser.screen().clone(); parser.process(b"\x1b]2;\x07"); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + assert_eq!(parser.screen().title_formatted(), &b""[..]); + assert_eq!(parser.screen().title_diff(&screen), &b"\x1b]0;\x07"[..]); } #[test] @@ -18,12 +32,26 @@ fn icon_name() { let mut parser = vt100::Parser::default(); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + + let screen = parser.screen().clone(); parser.process(b"\x1b]1;it's an icon name\x07"); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), "it's an icon name"); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]1;it's an icon name\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]1;it's an icon name\x07"[..] + ); + + let screen = parser.screen().clone(); parser.process(b"\x1b]1;\x07"); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + assert_eq!(parser.screen().title_formatted(), &b""[..]); + assert_eq!(parser.screen().title_diff(&screen), &b"\x1b]0;\x07"[..]); } #[test] @@ -31,12 +59,61 @@ fn title_icon_name() { let mut parser = vt100::Parser::default(); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + + let screen = parser.screen().clone(); parser.process(b"\x1b]0;it's both\x07"); assert_eq!(parser.screen().title(), "it's both"); assert_eq!(parser.screen().icon_name(), "it's both"); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]0;it's both\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]0;it's both\x07"[..] + ); + + let screen = parser.screen().clone(); parser.process(b"\x1b]0;\x07"); assert_eq!(parser.screen().title(), ""); assert_eq!(parser.screen().icon_name(), ""); + assert_eq!(parser.screen().title_formatted(), &b""[..]); + assert_eq!(parser.screen().title_diff(&screen), &b"\x1b]0;\x07"[..]); + + let screen = parser.screen().clone(); + parser.process(b"\x1b]2;it's a title\x07\x1b]1;it's an icon name\x07"); + assert_eq!(parser.screen().title(), "it's a title"); + assert_eq!(parser.screen().icon_name(), "it's an icon name"); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]1;it's an icon name\x07\x1b]2;it's a title\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]1;it's an icon name\x07\x1b]2;it's a title\x07"[..] + ); + + let screen = parser.screen().clone(); + parser.process(b"\x1b]1;it's a title\x07"); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]0;it's a title\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]0;it's a title\x07"[..] + ); + + let screen = parser.screen().clone(); + parser.process(b"\x1b]1;it's an icon name\x07"); + assert_eq!( + parser.screen().title_formatted(), + &b"\x1b]1;it's an icon name\x07\x1b]2;it's a title\x07"[..] + ); + assert_eq!( + parser.screen().title_diff(&screen), + &b"\x1b]1;it's an icon name\x07"[..] + ); } #[test] -- cgit v1.2.3