diff --git a/src/printer.rs b/src/printer.rs index 6d495777..f413fdc3 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -598,12 +598,12 @@ impl<'a> Printer for InteractivePrinter<'a> { match chunk { // Regular text. EscapeSequence::Text(text) => { - let text = &*self.preprocess(text, &mut cursor_total); + let text = self.preprocess(text, &mut cursor_total); let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n'); write!( handle, - "{}", + "{}{}", as_terminal_escaped( style, &format!("{}{}", self.ansi_style, text_trimmed), @@ -611,9 +611,11 @@ impl<'a> Printer for InteractivePrinter<'a> { colored_output, italics, background_color - ) + ), + self.ansi_style.to_reset_sequence(), )?; + // Pad the rest of the line. if text.len() != text_trimmed.len() { if let Some(background_color) = background_color { let ansi_style = Style { @@ -693,7 +695,7 @@ impl<'a> Printer for InteractivePrinter<'a> { // It wraps. write!( handle, - "{}\n{}", + "{}{}\n{}", as_terminal_escaped( style, &format!("{}{}", self.ansi_style, line_buf), @@ -702,6 +704,7 @@ impl<'a> Printer for InteractivePrinter<'a> { self.config.use_italic_text, background_color ), + self.ansi_style.to_reset_sequence(), panel_wrap.clone().unwrap() )?; diff --git a/src/vscreen.rs b/src/vscreen.rs index 7e2b8cd1..c902d42b 100644 --- a/src/vscreen.rs +++ b/src/vscreen.rs @@ -23,6 +23,13 @@ impl AnsiStyle { } } } + + pub fn to_reset_sequence(&mut self) -> String { + match &mut self.attributes { + Some(a) => a.to_reset_sequence(), + None => String::new(), + } + } } impl Display for AnsiStyle { @@ -35,6 +42,8 @@ impl Display for AnsiStyle { } struct Attributes { + has_sgr_sequences: bool, + foreground: String, background: String, underlined: String, @@ -67,16 +76,18 @@ struct Attributes { strike: String, /// The hyperlink sequence. - /// FORMAT: \x1B]8;;\e\\ + /// FORMAT: \x1B]8;{ID};{URL}\e\\ /// /// `\e\\` may be replaced with BEL `\x07`. - /// Setting both and to an empty string represents no hyperlink. + /// Setting both {ID} and {URL} to an empty string represents no hyperlink. hyperlink: String, } impl Attributes { pub fn new() -> Self { Attributes { + has_sgr_sequences: false, + foreground: "".to_owned(), background: "".to_owned(), underlined: "".to_owned(), @@ -135,6 +146,8 @@ impl Attributes { } fn sgr_reset(&mut self) { + self.has_sgr_sequences = false; + self.foreground.clear(); self.background.clear(); self.underlined.clear(); @@ -152,6 +165,7 @@ impl Attributes { .map(|p| p.parse::()) .map(|p| p.unwrap_or(0)); // Treat errors as 0. + self.has_sgr_sequences = true; while let Some(p) = iter.next() { match p { 0 => self.sgr_reset(), @@ -214,6 +228,28 @@ impl Attributes { _ => format!("\x1B[{}m", color), } } + + /// Gets an ANSI escape sequence to reset all the known attributes. + pub fn to_reset_sequence(&self) -> String { + let mut buf = String::with_capacity(17); + + // TODO: Enable me in a later pull request. + // if self.has_sgr_sequences { + // buf.push_str("\x1B[m"); + // } + + if !self.hyperlink.is_empty() { + buf.push_str("\x1B]8;;\x1B\\"); // Disable hyperlink. + } + + // TODO: Enable me in a later pull request. + // if !self.charset.is_empty() { + // // https://espterm.github.io/docs/VT100%20escape%20codes.html + // buf.push_str("\x1B(B\x1B)B"); // setusg0 and setusg1 + // } + + buf + } } impl Display for Attributes { diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index bb4c4668..ecc37ed7 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1966,7 +1966,7 @@ fn ansi_hyperlink_emitted_when_wrapped() { .write_stdin("\x1B]8;;http://example.com/\x1B\\Hyperlinks..........Wrap across lines.\n") .assert() .success() - .stdout("\x1B]8;;http://example.com/\x1B\\\x1B]8;;http://example.com/\x1B\\Hyperlinks..........\n\x1B]8;;http://example.com/\x1B\\Wrap across lines.\n") + .stdout("\x1B]8;;http://example.com/\x1B\\\x1B]8;;http://example.com/\x1B\\Hyperlinks..........\x1B]8;;\x1B\\\n\x1B]8;;http://example.com/\x1B\\Wrap across lines.\n") // FIXME: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ should not be emitted twice. .stderr(""); }