mirror of
https://github.com/sharkdp/bat.git
synced 2025-02-23 13:10:49 +01:00
Remove code that tries to handle ANSI escape inputs
Syntax highlighting is broken when input contains ANSI escape characters anyway, so there is not much point in trying to handle ANSI escapes in input.
This commit is contained in:
parent
a6297b282c
commit
8174e02279
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
- Relaxed glibc requirements on amd64, see #2106 and #2194 (@sharkdp)
|
- Relaxed glibc requirements on amd64, see #2106 and #2194 (@sharkdp)
|
||||||
- Improved fish completions. See #2275 (@zgracem)
|
- Improved fish completions. See #2275 (@zgracem)
|
||||||
|
- Stop pre-processing ANSI escape characters. Syntax highlighting on ANSI escaped input is not supported. See #2185 and #2189 (@Enselic)
|
||||||
|
|
||||||
## Syntaxes
|
## Syntaxes
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ pub(crate) mod printer;
|
|||||||
pub mod style;
|
pub mod style;
|
||||||
pub(crate) mod syntax_mapping;
|
pub(crate) mod syntax_mapping;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod vscreen;
|
|
||||||
pub(crate) mod wrapping;
|
pub(crate) mod wrapping;
|
||||||
|
|
||||||
pub use pretty_printer::{Input, PrettyPrinter};
|
pub use pretty_printer::{Input, PrettyPrinter};
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
use console::AnsiCodeIterator;
|
|
||||||
|
|
||||||
/// Expand tabs like an ANSI-enabled expand(1).
|
/// Expand tabs like an ANSI-enabled expand(1).
|
||||||
pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String {
|
pub fn expand_tabs(mut text: &str, width: usize, cursor: &mut usize) -> String {
|
||||||
let mut buffer = String::with_capacity(line.len() * 2);
|
let mut buffer = String::with_capacity(text.len() * 2);
|
||||||
|
|
||||||
for chunk in AnsiCodeIterator::new(line) {
|
|
||||||
match chunk {
|
|
||||||
(text, true) => buffer.push_str(text),
|
|
||||||
(mut text, false) => {
|
|
||||||
while let Some(index) = text.find('\t') {
|
while let Some(index) = text.find('\t') {
|
||||||
// Add previous text.
|
// Add previous text.
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
@ -26,9 +20,6 @@ pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String {
|
|||||||
|
|
||||||
*cursor += text.len();
|
*cursor += text.len();
|
||||||
buffer.push_str(text);
|
buffer.push_str(text);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@ use ansi_term::Style;
|
|||||||
|
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
|
|
||||||
use console::AnsiCodeIterator;
|
|
||||||
|
|
||||||
use syntect::easy::HighlightLines;
|
use syntect::easy::HighlightLines;
|
||||||
use syntect::highlighting::Color;
|
use syntect::highlighting::Color;
|
||||||
use syntect::highlighting::Theme;
|
use syntect::highlighting::Theme;
|
||||||
@ -33,7 +31,6 @@ use crate::line_range::RangeCheckResult;
|
|||||||
use crate::preprocessor::{expand_tabs, replace_nonprintable};
|
use crate::preprocessor::{expand_tabs, replace_nonprintable};
|
||||||
use crate::style::StyleComponent;
|
use crate::style::StyleComponent;
|
||||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
||||||
use crate::vscreen::AnsiStyle;
|
|
||||||
use crate::wrapping::WrappingMode;
|
use crate::wrapping::WrappingMode;
|
||||||
|
|
||||||
pub(crate) trait Printer {
|
pub(crate) trait Printer {
|
||||||
@ -122,7 +119,6 @@ pub(crate) struct InteractivePrinter<'a> {
|
|||||||
config: &'a Config<'a>,
|
config: &'a Config<'a>,
|
||||||
decorations: Vec<Box<dyn Decoration>>,
|
decorations: Vec<Box<dyn Decoration>>,
|
||||||
panel_width: usize,
|
panel_width: usize,
|
||||||
ansi_style: AnsiStyle,
|
|
||||||
content_type: Option<ContentType>,
|
content_type: Option<ContentType>,
|
||||||
#[cfg(feature = "git")]
|
#[cfg(feature = "git")]
|
||||||
pub line_changes: &'a Option<LineChanges>,
|
pub line_changes: &'a Option<LineChanges>,
|
||||||
@ -206,7 +202,6 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
config,
|
config,
|
||||||
decorations,
|
decorations,
|
||||||
content_type: input.reader.content_type,
|
content_type: input.reader.content_type,
|
||||||
ansi_style: AnsiStyle::new(),
|
|
||||||
#[cfg(feature = "git")]
|
#[cfg(feature = "git")]
|
||||||
line_changes,
|
line_changes,
|
||||||
highlighter_from_set,
|
highlighter_from_set,
|
||||||
@ -476,7 +471,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
self.config.highlighted_lines.0.check(line_number) == RangeCheckResult::InRange;
|
self.config.highlighted_lines.0.check(line_number) == RangeCheckResult::InRange;
|
||||||
|
|
||||||
if highlight_this_line && self.config.theme == "ansi" {
|
if highlight_this_line && self.config.theme == "ansi" {
|
||||||
self.ansi_style.update("^[4m");
|
write!(handle, "\x1B[4m")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let background_color = self
|
let background_color = self
|
||||||
@ -503,18 +498,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
let italics = self.config.use_italic_text;
|
let italics = self.config.use_italic_text;
|
||||||
|
|
||||||
for &(style, region) in ®ions {
|
for &(style, region) in ®ions {
|
||||||
let ansi_iterator = AnsiCodeIterator::new(region);
|
let text = &*self.preprocess(region, &mut cursor_total);
|
||||||
for chunk in ansi_iterator {
|
|
||||||
match chunk {
|
|
||||||
// ANSI escape passthrough.
|
|
||||||
(ansi, true) => {
|
|
||||||
self.ansi_style.update(ansi);
|
|
||||||
write!(handle, "{}", ansi)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular text.
|
|
||||||
(text, false) => {
|
|
||||||
let text = &*self.preprocess(text, &mut cursor_total);
|
|
||||||
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
|
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
@ -522,7 +506,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{}",
|
"{}",
|
||||||
as_terminal_escaped(
|
as_terminal_escaped(
|
||||||
style,
|
style,
|
||||||
&format!("{}{}", self.ansi_style, text_trimmed),
|
text_trimmed,
|
||||||
true_color,
|
true_color,
|
||||||
colored_output,
|
colored_output,
|
||||||
italics,
|
italics,
|
||||||
@ -547,28 +531,14 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
write!(handle, "{}", &text[text_trimmed.len()..])?;
|
write!(handle, "{}", &text[text_trimmed.len()..])?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.config.style_components.plain() && line.bytes().next_back() != Some(b'\n') {
|
if !self.config.style_components.plain() && line.bytes().next_back() != Some(b'\n') {
|
||||||
writeln!(handle)?;
|
writeln!(handle)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for &(style, region) in ®ions {
|
for &(style, region) in ®ions {
|
||||||
let ansi_iterator = AnsiCodeIterator::new(region);
|
|
||||||
for chunk in ansi_iterator {
|
|
||||||
match chunk {
|
|
||||||
// ANSI escape passthrough.
|
|
||||||
(ansi, true) => {
|
|
||||||
self.ansi_style.update(ansi);
|
|
||||||
write!(handle, "{}", ansi)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular text.
|
|
||||||
(text, false) => {
|
|
||||||
let text = self.preprocess(
|
let text = self.preprocess(
|
||||||
text.trim_end_matches(|c| c == '\r' || c == '\n'),
|
region.trim_end_matches(|c| c == '\r' || c == '\n'),
|
||||||
&mut cursor_total,
|
&mut cursor_total,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -595,9 +565,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{} ",
|
"{} ",
|
||||||
self.decorations
|
self.decorations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| d
|
.map(|d| d.generate(line_number, true, self).text)
|
||||||
.generate(line_number, true, self)
|
|
||||||
.text)
|
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(" ")
|
.join(" ")
|
||||||
))
|
))
|
||||||
@ -612,7 +580,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{}\n{}",
|
"{}\n{}",
|
||||||
as_terminal_escaped(
|
as_terminal_escaped(
|
||||||
style,
|
style,
|
||||||
&*format!("{}{}", self.ansi_style, line_buf),
|
&line_buf,
|
||||||
self.config.true_color,
|
self.config.true_color,
|
||||||
self.config.colored_output,
|
self.config.colored_output,
|
||||||
self.config.use_italic_text,
|
self.config.use_italic_text,
|
||||||
@ -638,7 +606,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{}",
|
"{}",
|
||||||
as_terminal_escaped(
|
as_terminal_escaped(
|
||||||
style,
|
style,
|
||||||
&*format!("{}{}", self.ansi_style, line_buf),
|
&line_buf,
|
||||||
self.config.true_color,
|
self.config.true_color,
|
||||||
self.config.colored_output,
|
self.config.colored_output,
|
||||||
self.config.use_italic_text,
|
self.config.use_italic_text,
|
||||||
@ -646,9 +614,6 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(background_color) = background_color {
|
if let Some(background_color) = background_color {
|
||||||
let ansi_style = Style {
|
let ansi_style = Style {
|
||||||
@ -666,7 +631,6 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if highlight_this_line && self.config.theme == "ansi" {
|
if highlight_this_line && self.config.theme == "ansi" {
|
||||||
self.ansi_style.update("^[24m");
|
|
||||||
write!(handle, "\x1B[24m")?;
|
write!(handle, "\x1B[24m")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
212
src/vscreen.rs
212
src/vscreen.rs
@ -1,212 +0,0 @@
|
|||||||
use std::fmt::{Display, Formatter};
|
|
||||||
|
|
||||||
// Wrapper to avoid unnecessary branching when input doesn't have ANSI escape sequences.
|
|
||||||
pub struct AnsiStyle {
|
|
||||||
attributes: Option<Attributes>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnsiStyle {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
AnsiStyle { attributes: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, sequence: &str) -> bool {
|
|
||||||
match &mut self.attributes {
|
|
||||||
Some(a) => a.update(sequence),
|
|
||||||
None => {
|
|
||||||
self.attributes = Some(Attributes::new());
|
|
||||||
self.attributes.as_mut().unwrap().update(sequence)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AnsiStyle {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self.attributes {
|
|
||||||
Some(ref a) => a.fmt(f),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Attributes {
|
|
||||||
foreground: String,
|
|
||||||
background: String,
|
|
||||||
underlined: String,
|
|
||||||
|
|
||||||
/// The character set to use.
|
|
||||||
/// REGEX: `\^[()][AB0-3]`
|
|
||||||
charset: String,
|
|
||||||
|
|
||||||
/// A buffer for unknown sequences.
|
|
||||||
unknown_buffer: String,
|
|
||||||
|
|
||||||
/// ON: ^[1m
|
|
||||||
/// OFF: ^[22m
|
|
||||||
bold: String,
|
|
||||||
|
|
||||||
/// ON: ^[2m
|
|
||||||
/// OFF: ^[22m
|
|
||||||
dim: String,
|
|
||||||
|
|
||||||
/// ON: ^[4m
|
|
||||||
/// OFF: ^[24m
|
|
||||||
underline: String,
|
|
||||||
|
|
||||||
/// ON: ^[3m
|
|
||||||
/// OFF: ^[23m
|
|
||||||
italic: String,
|
|
||||||
|
|
||||||
/// ON: ^[9m
|
|
||||||
/// OFF: ^[29m
|
|
||||||
strike: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attributes {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Attributes {
|
|
||||||
foreground: "".to_owned(),
|
|
||||||
background: "".to_owned(),
|
|
||||||
underlined: "".to_owned(),
|
|
||||||
charset: "".to_owned(),
|
|
||||||
unknown_buffer: "".to_owned(),
|
|
||||||
bold: "".to_owned(),
|
|
||||||
dim: "".to_owned(),
|
|
||||||
underline: "".to_owned(),
|
|
||||||
italic: "".to_owned(),
|
|
||||||
strike: "".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the attributes with an escape sequence.
|
|
||||||
/// Returns `false` if the sequence is unsupported.
|
|
||||||
pub fn update(&mut self, sequence: &str) -> bool {
|
|
||||||
let mut chars = sequence.char_indices().skip(1);
|
|
||||||
|
|
||||||
if let Some((_, t)) = chars.next() {
|
|
||||||
match t {
|
|
||||||
'(' => self.update_with_charset('(', chars.map(|(_, c)| c)),
|
|
||||||
')' => self.update_with_charset(')', chars.map(|(_, c)| c)),
|
|
||||||
'[' => {
|
|
||||||
if let Some((i, last)) = chars.last() {
|
|
||||||
// SAFETY: Always starts with ^[ and ends with m.
|
|
||||||
self.update_with_csi(last, &sequence[2..i])
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.update_with_unsupported(sequence),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sgr_reset(&mut self) {
|
|
||||||
self.foreground.clear();
|
|
||||||
self.background.clear();
|
|
||||||
self.underlined.clear();
|
|
||||||
self.bold.clear();
|
|
||||||
self.dim.clear();
|
|
||||||
self.underline.clear();
|
|
||||||
self.italic.clear();
|
|
||||||
self.strike.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_with_sgr(&mut self, parameters: &str) -> bool {
|
|
||||||
let mut iter = parameters
|
|
||||||
.split(';')
|
|
||||||
.map(|p| if p.is_empty() { "0" } else { p })
|
|
||||||
.map(|p| p.parse::<u16>())
|
|
||||||
.map(|p| p.unwrap_or(0)); // Treat errors as 0.
|
|
||||||
|
|
||||||
while let Some(p) = iter.next() {
|
|
||||||
match p {
|
|
||||||
0 => self.sgr_reset(),
|
|
||||||
1 => self.bold = format!("\x1B[{}m", parameters),
|
|
||||||
2 => self.dim = format!("\x1B[{}m", parameters),
|
|
||||||
3 => self.italic = format!("\x1B[{}m", parameters),
|
|
||||||
4 => self.underline = format!("\x1B[{}m", parameters),
|
|
||||||
23 => self.italic.clear(),
|
|
||||||
24 => self.underline.clear(),
|
|
||||||
22 => {
|
|
||||||
self.bold.clear();
|
|
||||||
self.dim.clear();
|
|
||||||
}
|
|
||||||
30..=39 => self.foreground = Self::parse_color(p, &mut iter),
|
|
||||||
40..=49 => self.background = Self::parse_color(p, &mut iter),
|
|
||||||
58..=59 => self.underlined = Self::parse_color(p, &mut iter),
|
|
||||||
90..=97 => self.foreground = Self::parse_color(p, &mut iter),
|
|
||||||
100..=107 => self.foreground = Self::parse_color(p, &mut iter),
|
|
||||||
_ => {
|
|
||||||
// Unsupported SGR sequence.
|
|
||||||
// Be compatible and pretend one just wasn't was provided.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_with_csi(&mut self, finalizer: char, sequence: &str) -> bool {
|
|
||||||
if finalizer == 'm' {
|
|
||||||
self.update_with_sgr(sequence)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_with_unsupported(&mut self, sequence: &str) -> bool {
|
|
||||||
self.unknown_buffer.push_str(sequence);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_with_charset(&mut self, kind: char, set: impl Iterator<Item = char>) -> bool {
|
|
||||||
self.charset = format!("\x1B{}{}", kind, set.take(1).collect::<String>());
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_color(color: u16, parameters: &mut dyn Iterator<Item = u16>) -> String {
|
|
||||||
match color % 10 {
|
|
||||||
8 => match parameters.next() {
|
|
||||||
Some(5) /* 256-color */ => format!("\x1B[{};5;{}m", color, join(";", 1, parameters)),
|
|
||||||
Some(2) /* 24-bit color */ => format!("\x1B[{};2;{}m", color, join(";", 3, parameters)),
|
|
||||||
Some(c) => format!("\x1B[{};{}m", color, c),
|
|
||||||
_ => "".to_owned(),
|
|
||||||
},
|
|
||||||
9 => "".to_owned(),
|
|
||||||
_ => format!("\x1B[{}m", color),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Attributes {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}{}{}{}{}{}{}{}{}",
|
|
||||||
self.foreground,
|
|
||||||
self.background,
|
|
||||||
self.underlined,
|
|
||||||
self.charset,
|
|
||||||
self.bold,
|
|
||||||
self.dim,
|
|
||||||
self.underline,
|
|
||||||
self.italic,
|
|
||||||
self.strike,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn join(
|
|
||||||
delimiter: &str,
|
|
||||||
limit: usize,
|
|
||||||
iterator: &mut dyn Iterator<Item = impl ToString>,
|
|
||||||
) -> String {
|
|
||||||
iterator
|
|
||||||
.take(limit)
|
|
||||||
.map(|i| i.to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(delimiter)
|
|
||||||
}
|
|
@ -1419,25 +1419,6 @@ fn ansi_highlight_underline() {
|
|||||||
.stderr("");
|
.stderr("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that ANSI passthrough is emitted properly for both wrapping and non-wrapping printer.
|
|
||||||
#[test]
|
|
||||||
fn ansi_passthrough_emit() {
|
|
||||||
for wrapping in &["never", "character"] {
|
|
||||||
bat()
|
|
||||||
.arg("--paging=never")
|
|
||||||
.arg("--color=never")
|
|
||||||
.arg("--terminal-width=80")
|
|
||||||
.arg(format!("--wrap={}", wrapping))
|
|
||||||
.arg("--decorations=always")
|
|
||||||
.arg("--style=plain")
|
|
||||||
.write_stdin("\x1B[33mColor\nColor \x1B[m\nPlain\n")
|
|
||||||
.assert()
|
|
||||||
.success()
|
|
||||||
.stdout("\x1B[33m\x1B[33mColor\n\x1B[33mColor \x1B[m\nPlain\n")
|
|
||||||
.stderr("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignored_suffix_arg() {
|
fn ignored_suffix_arg() {
|
||||||
bat()
|
bat()
|
||||||
|
Loading…
Reference in New Issue
Block a user