From 054421268fff33003b892eb7bcef1a831c6c308d Mon Sep 17 00:00:00 2001 From: Ethan P Date: Sun, 16 Apr 2023 20:18:40 -0700 Subject: [PATCH] Strip OSC sequences before printing This commit strips OSC (Operating System Command) sequences before printing lines. Eventually when time permits, I want to add back support for printing OSC sequences (and improve it to treat hyperlinks like an attribute). Until then, this should help prevent garbled output :) --- src/printer.rs | 8 +++++--- src/vscreen.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index 257cc766..45fd5336 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -33,7 +33,7 @@ use crate::line_range::RangeCheckResult; use crate::preprocessor::{expand_tabs, replace_nonprintable}; use crate::style::StyleComponent; use crate::terminal::{as_terminal_escaped, to_ansi_color}; -use crate::vscreen::AnsiStyle; +use crate::vscreen::{strip_problematic_sequences, AnsiStyle}; use crate::wrapping::WrappingMode; pub enum OutputHandle<'a> { @@ -581,7 +581,8 @@ impl<'a> Printer for InteractivePrinter<'a> { let italics = self.config.use_italic_text; for &(style, region) in ®ions { - let ansi_iterator = AnsiCodeIterator::new(region); + let text = strip_problematic_sequences(region); + let ansi_iterator = AnsiCodeIterator::new(&text); for chunk in ansi_iterator { match chunk { // ANSI escape passthrough. @@ -634,7 +635,8 @@ impl<'a> Printer for InteractivePrinter<'a> { } } else { for &(style, region) in ®ions { - let ansi_iterator = AnsiCodeIterator::new(region); + let text = strip_problematic_sequences(region); + let ansi_iterator = AnsiCodeIterator::new(&text); for chunk in ansi_iterator { match chunk { // ANSI escape passthrough. diff --git a/src/vscreen.rs b/src/vscreen.rs index ecf2bd3c..ccd0bfe8 100644 --- a/src/vscreen.rs +++ b/src/vscreen.rs @@ -458,9 +458,54 @@ impl<'a> Iterator for EscapeSequenceOffsetsIterator<'a> { } } +/// Strips problematic ANSI escape sequences from a string. +/// +/// Ideally, this will be replaced with something that uses [[Attributes]] to create a table of char offsets +/// -> absolute styles and style deltas. Something like that would let us simplify the printer (and support +/// re-printing OSC hyperlink commands). +pub fn strip_problematic_sequences(text: &str) -> String { + use EscapeSequenceOffsets::*; + + let mut buffer = String::with_capacity(text.len()); + for seq in EscapeSequenceOffsetsIterator::new(text) { + match seq { + Text { start, end } => buffer.push_str(&text[start..end]), + Unknown { start, end } => buffer.push_str(&text[start..end]), + + NF { + start_sequence: start, + start: _, + end, + } => buffer.push_str(&text[start..end]), + + CSI { + start_sequence: start, + start_parameters: _, + start_intermediates: _, + start_final_byte: _, + end, + } => buffer.push_str(&text[start..end]), + + OSC { + start_sequence: _, + start_command: _, + start_terminator: _, + end: _, + } => { + // TODO(eth-p): Support re-printing hyperlinks. + // In the meantime, strip these. + } + } + } + + buffer +} + #[cfg(test)] mod tests { - use crate::vscreen::{EscapeSequenceOffsets, EscapeSequenceOffsetsIterator}; + use crate::vscreen::{ + strip_problematic_sequences, EscapeSequenceOffsets, EscapeSequenceOffsetsIterator, + }; #[test] fn test_escape_sequence_offsets_iterator_parses_text() { @@ -683,4 +728,12 @@ mod tests { ); assert_eq!(iter.next(), None); } + + #[test] + fn test_strip_problematic_sequences() { + assert_eq!( + strip_problematic_sequences("text\x1B[33m\x1B]OSC\x1B\\\x1B(0"), + "text\x1B[33m\x1B(0" + ); + } }