Skip to content

Commit

Permalink
Revert "Remove code that tries to handle ANSI escape inputs"
Browse files Browse the repository at this point in the history
This reverts commit 8174e02. Turns out
it is needed for a common use case, see
#2307.

It is not a clean revert, because I adjust CHANGELOG.md and also add a
comment to the test. I also had to resolve a small `use` conflict.
  • Loading branch information
Enselic committed Sep 10, 2022
1 parent 6680f65 commit 352309b
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 120 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## Bugfixes

- Bring back pre-processing of ANSI escape characters to so that some common `bat` use cases starts working again. See #2308 (@Enselic)

## Other

## Syntaxes
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub(crate) mod printer;
pub mod style;
pub(crate) mod syntax_mapping;
mod terminal;
mod vscreen;
pub(crate) mod wrapping;

pub use pretty_printer::{Input, PrettyPrinter, Syntax};
Expand Down
45 changes: 27 additions & 18 deletions src/preprocessor.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
use std::fmt::Write;

use console::AnsiCodeIterator;

/// Expand tabs like an ANSI-enabled expand(1).
pub fn expand_tabs(mut text: &str, width: usize, cursor: &mut usize) -> String {
let mut buffer = String::with_capacity(text.len() * 2);

while let Some(index) = text.find('\t') {
// Add previous text.
if index > 0 {
*cursor += index;
buffer.push_str(&text[0..index]);
}
pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String {
let mut buffer = String::with_capacity(line.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') {
// Add previous text.
if index > 0 {
*cursor += index;
buffer.push_str(&text[0..index]);
}

// Add tab.
let spaces = width - (*cursor % width);
*cursor += spaces;
buffer.push_str(&*" ".repeat(spaces));
// Add tab.
let spaces = width - (*cursor % width);
*cursor += spaces;
buffer.push_str(&*" ".repeat(spaces));

// Next.
text = &text[index + 1..text.len()];
}
// Next.
text = &text[index + 1..text.len()];
}

*cursor += text.len();
buffer.push_str(text);
*cursor += text.len();
buffer.push_str(text);
}
}
}

buffer
}
Expand Down
240 changes: 138 additions & 102 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use ansi_term::Style;

use bytesize::ByteSize;

use console::AnsiCodeIterator;

use syntect::easy::HighlightLines;
use syntect::highlighting::Color;
use syntect::highlighting::Theme;
Expand All @@ -31,6 +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::wrapping::WrappingMode;

pub(crate) trait Printer {
Expand Down Expand Up @@ -119,6 +122,7 @@ pub(crate) struct InteractivePrinter<'a> {
config: &'a Config<'a>,
decorations: Vec<Box<dyn Decoration>>,
panel_width: usize,
ansi_style: AnsiStyle,
content_type: Option<ContentType>,
#[cfg(feature = "git")]
pub line_changes: &'a Option<LineChanges>,
Expand Down Expand Up @@ -202,6 +206,7 @@ impl<'a> InteractivePrinter<'a> {
config,
decorations,
content_type: input.reader.content_type,
ansi_style: AnsiStyle::new(),
#[cfg(feature = "git")]
line_changes,
highlighter_from_set,
Expand Down Expand Up @@ -480,7 +485,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
self.config.highlighted_lines.0.check(line_number) == RangeCheckResult::InRange;

if highlight_this_line && self.config.theme == "ansi" {
write!(handle, "\x1B[4m")?;
self.ansi_style.update("^[4m");
}

let background_color = self
Expand All @@ -507,37 +512,51 @@ impl<'a> Printer for InteractivePrinter<'a> {
let italics = self.config.use_italic_text;

for &(style, region) in &regions {
let text = &*self.preprocess(region, &mut cursor_total);
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');

write!(
handle,
"{}",
as_terminal_escaped(
style,
text_trimmed,
true_color,
colored_output,
italics,
background_color
)
)?;
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)?;
}

if text.len() != text_trimmed.len() {
if let Some(background_color) = background_color {
let ansi_style = Style {
background: to_ansi_color(background_color, true_color),
..Default::default()
};

let width = if cursor_total <= cursor_max {
cursor_max - cursor_total + 1
} else {
0
};
write!(handle, "{}", ansi_style.paint(" ".repeat(width)))?;
// Regular text.
(text, false) => {
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),
true_color,
colored_output,
italics,
background_color
)
)?;

if text.len() != text_trimmed.len() {
if let Some(background_color) = background_color {
let ansi_style = Style {
background: to_ansi_color(background_color, true_color),
..Default::default()
};

let width = if cursor_total <= cursor_max {
cursor_max - cursor_total + 1
} else {
0
};
write!(handle, "{}", ansi_style.paint(" ".repeat(width)))?;
}
write!(handle, "{}", &text[text_trimmed.len()..])?;
}
}
}
write!(handle, "{}", &text[text_trimmed.len()..])?;
}
}

Expand All @@ -546,82 +565,98 @@ impl<'a> Printer for InteractivePrinter<'a> {
}
} else {
for &(style, region) in &regions {
let text = self.preprocess(
region.trim_end_matches(|c| c == '\r' || c == '\n'),
&mut cursor_total,
);

let mut max_width = cursor_max - cursor;

// line buffer (avoid calling write! for every character)
let mut line_buf = String::with_capacity(max_width * 4);

// Displayed width of line_buf
let mut current_width = 0;

for c in text.chars() {
// calculate the displayed width for next character
let cw = c.width().unwrap_or(0);
current_width += cw;

// if next character cannot be printed on this line,
// flush the buffer.
if current_width > max_width {
// Generate wrap padding if not already generated.
if panel_wrap.is_none() {
panel_wrap = if self.panel_width > 0 {
Some(format!(
"{} ",
self.decorations
.iter()
.map(|d| d.generate(line_number, true, self).text)
.collect::<Vec<String>>()
.join(" ")
))
} else {
Some("".to_string())
}
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)?;
}

// It wraps.
write!(
handle,
"{}\n{}",
as_terminal_escaped(
style,
&line_buf,
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,
background_color
),
panel_wrap.clone().unwrap()
)?;

cursor = 0;
max_width = cursor_max;

line_buf.clear();
current_width = cw;
}
// Regular text.
(text, false) => {
let text = self.preprocess(
text.trim_end_matches(|c| c == '\r' || c == '\n'),
&mut cursor_total,
);

let mut max_width = cursor_max - cursor;

// line buffer (avoid calling write! for every character)
let mut line_buf = String::with_capacity(max_width * 4);

// Displayed width of line_buf
let mut current_width = 0;

for c in text.chars() {
// calculate the displayed width for next character
let cw = c.width().unwrap_or(0);
current_width += cw;

// if next character cannot be printed on this line,
// flush the buffer.
if current_width > max_width {
// Generate wrap padding if not already generated.
if panel_wrap.is_none() {
panel_wrap = if self.panel_width > 0 {
Some(format!(
"{} ",
self.decorations
.iter()
.map(|d| d
.generate(line_number, true, self)
.text)
.collect::<Vec<String>>()
.join(" ")
))
} else {
Some("".to_string())
}
}

// It wraps.
write!(
handle,
"{}\n{}",
as_terminal_escaped(
style,
&*format!("{}{}", self.ansi_style, line_buf),
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,
background_color
),
panel_wrap.clone().unwrap()
)?;

cursor = 0;
max_width = cursor_max;

line_buf.clear();
current_width = cw;
}

line_buf.push(c);
}

line_buf.push(c);
// flush the buffer
cursor += current_width;
write!(
handle,
"{}",
as_terminal_escaped(
style,
&*format!("{}{}", self.ansi_style, line_buf),
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,
background_color
)
)?;
}
}
}

// flush the buffer
cursor += current_width;
write!(
handle,
"{}",
as_terminal_escaped(
style,
&line_buf,
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,
background_color
)
)?;
}

if let Some(background_color) = background_color {
Expand All @@ -640,6 +675,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
}

if highlight_this_line && self.config.theme == "ansi" {
self.ansi_style.update("^[24m");
write!(handle, "\x1B[24m")?;
}

Expand Down
Loading

0 comments on commit 352309b

Please sign in to comment.