summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan P. <eth-p+git@hidden.email>2024-06-10 21:17:28 -0700
committerEthan P. <eth-p+git@hidden.email>2024-06-17 18:27:33 -0700
commit9e8176b1c69235b91c14aaa56c5862ed7dff3a9b (patch)
treeaed1124af0fdf1fbc43996c7d9ccd8b04ffd4da2
parent70ff93d2386433723acbbe76711d299dfe9dca99 (diff)
Add `--strip-ansi=auto` option
When using `auto`, escape sequences will be stripped unless printing plain text.
-rw-r--r--doc/long-help.txt5
-rw-r--r--src/bin/bat/app.rs1
-rw-r--r--src/bin/bat/clap_app.rs8
-rw-r--r--src/preprocessor.rs1
-rw-r--r--src/printer.rs40
-rw-r--r--tests/integration_tests.rs70
6 files changed, 107 insertions, 18 deletions
diff --git a/doc/long-help.txt b/doc/long-help.txt
index 93f56968..d9cdce39 100644
--- a/doc/long-help.txt
+++ b/doc/long-help.txt
@@ -123,8 +123,9 @@ Options:
Set the maximum number of consecutive empty lines to be printed.
--strip-ansi <when>
- Specify when to strip ANSI escape sequences from the input. Possible values: always,
- *never*.
+ Specify when to strip ANSI escape sequences from the input. The automatic mode will remove
+ escape sequences unless the syntax highlighting language is plain text. Possible values:
+ auto, always, *never*.
--style <components>
Configure which elements (line numbers, file headers, grid borders, Git modifications, ..)
diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index 62ffdd6d..0d2600b2 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -250,6 +250,7 @@ impl App {
{
Some("never") => StripAnsiMode::Never,
Some("always") => StripAnsiMode::Always,
+ Some("auto") => StripAnsiMode::Auto,
_ => unreachable!("other values for --strip-ansi are not allowed"),
},
theme: self
diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs
index 32c7c077..e70b1a5b 100644
--- a/src/bin/bat/clap_app.rs
+++ b/src/bin/bat/clap_app.rs
@@ -407,11 +407,13 @@ pub fn build_app(interactive_output: bool) -> Command {
.long("strip-ansi")
.overrides_with("strip-ansi")
.value_name("when")
- .value_parser(["always", "never"])
+ .value_parser(["auto", "always", "never"])
.default_value("never")
.hide_default_value(true)
- .help("Strip colors from the input (always, *never*)")
- .long_help("Specify when to strip ANSI escape sequences from the input. Possible values: always, *never*.")
+ .help("Strip colors from the input (auto, always, *never*)")
+ .long_help("Specify when to strip ANSI escape sequences from the input. \
+ The automatic mode will remove escape sequences unless the syntax highlighting \
+ language is plain text. Possible values: auto, always, *never*.")
.hide_short_help(true)
)
.arg(
diff --git a/src/preprocessor.rs b/src/preprocessor.rs
index 707946f9..dc2aa66e 100644
--- a/src/preprocessor.rs
+++ b/src/preprocessor.rs
@@ -154,6 +154,7 @@ pub enum StripAnsiMode {
#[default]
Never,
Always,
+ Auto,
}
#[test]
diff --git a/src/printer.rs b/src/printer.rs
index d76e6e0a..e9bea3fd 100644
--- a/src/printer.rs
+++ b/src/printer.rs
@@ -268,26 +268,40 @@ impl<'a> InteractivePrinter<'a> {
.content_type
.map_or(false, |c| c.is_binary() && !config.show_nonprintable);
- let highlighter_from_set = if is_printing_binary || !config.colored_output {
- None
- } else {
- // Determine the type of syntax for highlighting
- let syntax_in_set =
- match assets.get_syntax(config.language, input, &config.syntax_mapping) {
- Ok(syntax_in_set) => syntax_in_set,
- Err(Error::UndetectedSyntax(_)) => assets
- .find_syntax_by_name("Plain Text")?
- .expect("A plain text syntax is available"),
- Err(e) => return Err(e),
- };
+ let needs_to_match_syntax = !is_printing_binary
+ && (config.colored_output || config.strip_ansi == StripAnsiMode::Auto);
- Some(HighlighterFromSet::new(syntax_in_set, theme))
+ let (is_plain_text, highlighter_from_set) = if needs_to_match_syntax {
+ // Determine the type of syntax for highlighting
+ const PLAIN_TEXT_SYNTAX: &str = "Plain Text";
+ match assets.get_syntax(config.language, input, &config.syntax_mapping) {
+ Ok(syntax_in_set) => (
+ syntax_in_set.syntax.name == PLAIN_TEXT_SYNTAX,
+ Some(HighlighterFromSet::new(syntax_in_set, theme)),
+ ),
+
+ Err(Error::UndetectedSyntax(_)) => (
+ true,
+ Some(
+ assets
+ .find_syntax_by_name(PLAIN_TEXT_SYNTAX)?
+ .map(|s| HighlighterFromSet::new(s, theme))
+ .expect("A plain text syntax is available"),
+ ),
+ ),
+
+ Err(e) => return Err(e),
+ }
+ } else {
+ (false, None)
};
// Determine when to strip ANSI sequences
let strip_ansi = match config.strip_ansi {
_ if config.show_nonprintable => false,
StripAnsiMode::Always => true,
+ StripAnsiMode::Auto if is_plain_text => false, // Plain text may already contain escape sequences.
+ StripAnsiMode::Auto => true,
_ => false,
};
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index bc86cb9b..d6009361 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -2740,3 +2740,73 @@ fn strip_ansi_does_not_strip_when_show_nonprintable() {
assert!(output.contains("␛"))
}
+
+#[test]
+fn strip_ansi_auto_strips_ansi_when_detected_syntax_by_filename() {
+ bat()
+ .arg("--style=plain")
+ .arg("--decorations=always")
+ .arg("--color=never")
+ .arg("--strip-ansi=auto")
+ .arg("--file-name=test.rs")
+ .write_stdin("fn \x1B[33mYellow\x1B[m() -> () {}")
+ .assert()
+ .success()
+ .stdout("fn Yellow() -> () {}");
+}
+
+#[test]
+fn strip_ansi_auto_strips_ansi_when_provided_syntax_by_option() {
+ bat()
+ .arg("--style=plain")
+ .arg("--decorations=always")
+ .arg("--color=never")
+ .arg("--strip-ansi=auto")
+ .arg("--language=rust")
+ .write_stdin("fn \x1B[33mYellow\x1B[m() -> () {}")
+ .assert()
+ .success()
+ .stdout("fn Yellow() -> () {}");
+}
+
+#[test]
+fn strip_ansi_auto_does_not_strip_when_plain_text_by_filename() {
+ let output = String::from_utf8(
+ bat()
+ .arg("--style=plain")
+ .arg("--decorations=always")
+ .arg("--color=never")
+ .arg("--strip-ansi=auto")
+ .arg("--file-name=ansi.txt")
+ .write_stdin("\x1B[33mYellow\x1B[m")
+ .assert()
+ .success()
+ .get_output()
+ .stdout
+ .clone(),
+ )
+ .expect("valid utf8");
+
+ assert!(output.contains("\x1B[33mYellow"))
+}
+
+#[test]
+fn strip_ansi_auto_does_not_strip_ansi_when_plain_text_by_option() {
+ let output = String::from_utf8(
+ bat()
+ .arg("--style=plain")
+ .arg("--decorations=always")
+ .arg("--color=never")
+ .arg("--strip-ansi=auto")
+ .arg("--language=txt")
+ .write_stdin("\x1B[33mYellow\x1B[m")
+ .assert()
+ .success()
+ .get_output()
+ .stdout
+ .clone(),
+ )
+ .expect("valid utf8");
+
+ assert!(output.contains("\x1B[33mYellow"))
+}