Skip to content

Commit 352309b

Browse files
committed
Revert "Remove code that tries to handle ANSI escape inputs"
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.
1 parent 6680f65 commit 352309b

File tree

6 files changed

+400
-120
lines changed

6 files changed

+400
-120
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
## Bugfixes
66

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

911
## Syntaxes

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub(crate) mod printer;
4646
pub mod style;
4747
pub(crate) mod syntax_mapping;
4848
mod terminal;
49+
mod vscreen;
4950
pub(crate) mod wrapping;
5051

5152
pub use pretty_printer::{Input, PrettyPrinter, Syntax};

src/preprocessor.rs

+27-18
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
11
use std::fmt::Write;
22

3+
use console::AnsiCodeIterator;
4+
35
/// Expand tabs like an ANSI-enabled expand(1).
4-
pub fn expand_tabs(mut text: &str, width: usize, cursor: &mut usize) -> String {
5-
let mut buffer = String::with_capacity(text.len() * 2);
6-
7-
while let Some(index) = text.find('\t') {
8-
// Add previous text.
9-
if index > 0 {
10-
*cursor += index;
11-
buffer.push_str(&text[0..index]);
12-
}
6+
pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String {
7+
let mut buffer = String::with_capacity(line.len() * 2);
8+
9+
for chunk in AnsiCodeIterator::new(line) {
10+
match chunk {
11+
(text, true) => buffer.push_str(text),
12+
(mut text, false) => {
13+
while let Some(index) = text.find('\t') {
14+
// Add previous text.
15+
if index > 0 {
16+
*cursor += index;
17+
buffer.push_str(&text[0..index]);
18+
}
1319

14-
// Add tab.
15-
let spaces = width - (*cursor % width);
16-
*cursor += spaces;
17-
buffer.push_str(&*" ".repeat(spaces));
20+
// Add tab.
21+
let spaces = width - (*cursor % width);
22+
*cursor += spaces;
23+
buffer.push_str(&*" ".repeat(spaces));
1824

19-
// Next.
20-
text = &text[index + 1..text.len()];
21-
}
25+
// Next.
26+
text = &text[index + 1..text.len()];
27+
}
2228

23-
*cursor += text.len();
24-
buffer.push_str(text);
29+
*cursor += text.len();
30+
buffer.push_str(text);
31+
}
32+
}
33+
}
2534

2635
buffer
2736
}

src/printer.rs

+138-102
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use ansi_term::Style;
66

77
use bytesize::ByteSize;
88

9+
use console::AnsiCodeIterator;
10+
911
use syntect::easy::HighlightLines;
1012
use syntect::highlighting::Color;
1113
use syntect::highlighting::Theme;
@@ -31,6 +33,7 @@ use crate::line_range::RangeCheckResult;
3133
use crate::preprocessor::{expand_tabs, replace_nonprintable};
3234
use crate::style::StyleComponent;
3335
use crate::terminal::{as_terminal_escaped, to_ansi_color};
36+
use crate::vscreen::AnsiStyle;
3437
use crate::wrapping::WrappingMode;
3538

3639
pub(crate) trait Printer {
@@ -119,6 +122,7 @@ pub(crate) struct InteractivePrinter<'a> {
119122
config: &'a Config<'a>,
120123
decorations: Vec<Box<dyn Decoration>>,
121124
panel_width: usize,
125+
ansi_style: AnsiStyle,
122126
content_type: Option<ContentType>,
123127
#[cfg(feature = "git")]
124128
pub line_changes: &'a Option<LineChanges>,
@@ -202,6 +206,7 @@ impl<'a> InteractivePrinter<'a> {
202206
config,
203207
decorations,
204208
content_type: input.reader.content_type,
209+
ansi_style: AnsiStyle::new(),
205210
#[cfg(feature = "git")]
206211
line_changes,
207212
highlighter_from_set,
@@ -480,7 +485,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
480485
self.config.highlighted_lines.0.check(line_number) == RangeCheckResult::InRange;
481486

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

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

509514
for &(style, region) in &regions {
510-
let text = &*self.preprocess(region, &mut cursor_total);
511-
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
512-
513-
write!(
514-
handle,
515-
"{}",
516-
as_terminal_escaped(
517-
style,
518-
text_trimmed,
519-
true_color,
520-
colored_output,
521-
italics,
522-
background_color
523-
)
524-
)?;
515+
let ansi_iterator = AnsiCodeIterator::new(region);
516+
for chunk in ansi_iterator {
517+
match chunk {
518+
// ANSI escape passthrough.
519+
(ansi, true) => {
520+
self.ansi_style.update(ansi);
521+
write!(handle, "{}", ansi)?;
522+
}
525523

526-
if text.len() != text_trimmed.len() {
527-
if let Some(background_color) = background_color {
528-
let ansi_style = Style {
529-
background: to_ansi_color(background_color, true_color),
530-
..Default::default()
531-
};
532-
533-
let width = if cursor_total <= cursor_max {
534-
cursor_max - cursor_total + 1
535-
} else {
536-
0
537-
};
538-
write!(handle, "{}", ansi_style.paint(" ".repeat(width)))?;
524+
// Regular text.
525+
(text, false) => {
526+
let text = &*self.preprocess(text, &mut cursor_total);
527+
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
528+
529+
write!(
530+
handle,
531+
"{}",
532+
as_terminal_escaped(
533+
style,
534+
&format!("{}{}", self.ansi_style, text_trimmed),
535+
true_color,
536+
colored_output,
537+
italics,
538+
background_color
539+
)
540+
)?;
541+
542+
if text.len() != text_trimmed.len() {
543+
if let Some(background_color) = background_color {
544+
let ansi_style = Style {
545+
background: to_ansi_color(background_color, true_color),
546+
..Default::default()
547+
};
548+
549+
let width = if cursor_total <= cursor_max {
550+
cursor_max - cursor_total + 1
551+
} else {
552+
0
553+
};
554+
write!(handle, "{}", ansi_style.paint(" ".repeat(width)))?;
555+
}
556+
write!(handle, "{}", &text[text_trimmed.len()..])?;
557+
}
558+
}
539559
}
540-
write!(handle, "{}", &text[text_trimmed.len()..])?;
541560
}
542561
}
543562

@@ -546,82 +565,98 @@ impl<'a> Printer for InteractivePrinter<'a> {
546565
}
547566
} else {
548567
for &(style, region) in &regions {
549-
let text = self.preprocess(
550-
region.trim_end_matches(|c| c == '\r' || c == '\n'),
551-
&mut cursor_total,
552-
);
553-
554-
let mut max_width = cursor_max - cursor;
555-
556-
// line buffer (avoid calling write! for every character)
557-
let mut line_buf = String::with_capacity(max_width * 4);
558-
559-
// Displayed width of line_buf
560-
let mut current_width = 0;
561-
562-
for c in text.chars() {
563-
// calculate the displayed width for next character
564-
let cw = c.width().unwrap_or(0);
565-
current_width += cw;
566-
567-
// if next character cannot be printed on this line,
568-
// flush the buffer.
569-
if current_width > max_width {
570-
// Generate wrap padding if not already generated.
571-
if panel_wrap.is_none() {
572-
panel_wrap = if self.panel_width > 0 {
573-
Some(format!(
574-
"{} ",
575-
self.decorations
576-
.iter()
577-
.map(|d| d.generate(line_number, true, self).text)
578-
.collect::<Vec<String>>()
579-
.join(" ")
580-
))
581-
} else {
582-
Some("".to_string())
583-
}
568+
let ansi_iterator = AnsiCodeIterator::new(region);
569+
for chunk in ansi_iterator {
570+
match chunk {
571+
// ANSI escape passthrough.
572+
(ansi, true) => {
573+
self.ansi_style.update(ansi);
574+
write!(handle, "{}", ansi)?;
584575
}
585576

586-
// It wraps.
587-
write!(
588-
handle,
589-
"{}\n{}",
590-
as_terminal_escaped(
591-
style,
592-
&line_buf,
593-
self.config.true_color,
594-
self.config.colored_output,
595-
self.config.use_italic_text,
596-
background_color
597-
),
598-
panel_wrap.clone().unwrap()
599-
)?;
600-
601-
cursor = 0;
602-
max_width = cursor_max;
603-
604-
line_buf.clear();
605-
current_width = cw;
606-
}
577+
// Regular text.
578+
(text, false) => {
579+
let text = self.preprocess(
580+
text.trim_end_matches(|c| c == '\r' || c == '\n'),
581+
&mut cursor_total,
582+
);
583+
584+
let mut max_width = cursor_max - cursor;
585+
586+
// line buffer (avoid calling write! for every character)
587+
let mut line_buf = String::with_capacity(max_width * 4);
588+
589+
// Displayed width of line_buf
590+
let mut current_width = 0;
591+
592+
for c in text.chars() {
593+
// calculate the displayed width for next character
594+
let cw = c.width().unwrap_or(0);
595+
current_width += cw;
596+
597+
// if next character cannot be printed on this line,
598+
// flush the buffer.
599+
if current_width > max_width {
600+
// Generate wrap padding if not already generated.
601+
if panel_wrap.is_none() {
602+
panel_wrap = if self.panel_width > 0 {
603+
Some(format!(
604+
"{} ",
605+
self.decorations
606+
.iter()
607+
.map(|d| d
608+
.generate(line_number, true, self)
609+
.text)
610+
.collect::<Vec<String>>()
611+
.join(" ")
612+
))
613+
} else {
614+
Some("".to_string())
615+
}
616+
}
617+
618+
// It wraps.
619+
write!(
620+
handle,
621+
"{}\n{}",
622+
as_terminal_escaped(
623+
style,
624+
&*format!("{}{}", self.ansi_style, line_buf),
625+
self.config.true_color,
626+
self.config.colored_output,
627+
self.config.use_italic_text,
628+
background_color
629+
),
630+
panel_wrap.clone().unwrap()
631+
)?;
632+
633+
cursor = 0;
634+
max_width = cursor_max;
635+
636+
line_buf.clear();
637+
current_width = cw;
638+
}
639+
640+
line_buf.push(c);
641+
}
607642

608-
line_buf.push(c);
643+
// flush the buffer
644+
cursor += current_width;
645+
write!(
646+
handle,
647+
"{}",
648+
as_terminal_escaped(
649+
style,
650+
&*format!("{}{}", self.ansi_style, line_buf),
651+
self.config.true_color,
652+
self.config.colored_output,
653+
self.config.use_italic_text,
654+
background_color
655+
)
656+
)?;
657+
}
658+
}
609659
}
610-
611-
// flush the buffer
612-
cursor += current_width;
613-
write!(
614-
handle,
615-
"{}",
616-
as_terminal_escaped(
617-
style,
618-
&line_buf,
619-
self.config.true_color,
620-
self.config.colored_output,
621-
self.config.use_italic_text,
622-
background_color
623-
)
624-
)?;
625660
}
626661

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

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

0 commit comments

Comments
 (0)