Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tab expansion support. #302

Merged
merged 17 commits into from
Sep 12, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
396 changes: 211 additions & 185 deletions src/app.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod decorations;
mod diff;
mod line_range;
mod output;
mod preprocessor;
mod printer;
mod style;
mod terminal;
Expand Down
34 changes: 34 additions & 0 deletions src/preprocessor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use console::AnsiCodeIterator;

/// Expand tabs like an ANSI-enabled expand(1).
pub fn expand(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));

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

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

buffer
}
39 changes: 24 additions & 15 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineN
use diff::get_git_diff;
use diff::LineChanges;
use errors::*;
use preprocessor::expand;
use style::OutputWrap;
use terminal::{as_terminal_escaped, to_ansi_color};

Expand Down Expand Up @@ -152,6 +153,14 @@ impl<'a> InteractivePrinter<'a> {

Ok(())
}

fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
if self.config.tab_width > 0 {
expand(text, self.config.tab_width, cursor)
} else {
text.to_string()
}
}
}

impl<'a> Printer for InteractivePrinter<'a> {
Expand Down Expand Up @@ -204,7 +213,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
let line = String::from_utf8_lossy(&line_buffer);
let line = String::from_utf8_lossy(&line_buffer).to_string();
let regions = self.highlighter.highlight(line.as_ref());

if out_of_range {
Expand All @@ -213,6 +222,7 @@ impl<'a> Printer for InteractivePrinter<'a> {

let mut cursor: usize = 0;
let mut cursor_max: usize = self.config.term_width;
let mut cursor_total: usize = 0;
let mut panel_wrap: Option<String> = None;

// Line decorations.
Expand All @@ -234,19 +244,14 @@ impl<'a> Printer for InteractivePrinter<'a> {
let true_color = self.config.true_color;
let colored_output = self.config.colored_output;

write!(
handle,
"{}",
regions
.iter()
.map(|&(style, text)| as_terminal_escaped(
style,
text,
true_color,
colored_output,
)).collect::<Vec<_>>()
.join("")
)?;
for &(style, region) in regions.iter() {
let text = &*self.preprocess(region, &mut cursor_total);
write!(
handle,
"{}",
as_terminal_escaped(style, &*text, true_color, colored_output,)
)?;
}

if line.bytes().next_back() != Some(b'\n') {
write!(handle, "\n")?;
Expand All @@ -273,7 +278,11 @@ impl<'a> Printer for InteractivePrinter<'a> {

// Regular text.
(text, false) => {
let text = text.trim_right_matches(|c| c == '\r' || c == '\n');
let text = self.preprocess(
text.trim_right_matches(|c| c == '\r' || c == '\n'),
&mut cursor_total,
);

let mut chars = text.chars();
let mut remaining = text.chars().count();

Expand Down
25 changes: 18 additions & 7 deletions tests/snapshots/generate_snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ def generate_snapshots():

for num in range(len(single_styles)):
for grouped in itertools.combinations(single_styles, num + 1):
generate_snapshot(",".join(grouped))
generate_style_snapshot(",".join(grouped))

for style in collective_styles:
generate_snapshot(style)

def generate_snapshot(option):
command = "../../target/debug/bat --style={0} sample.rs > output/{0}.snapshot.txt".format(
option
generate_style_snapshot(style)

generate_snapshot("tabs_passthrough", "--tabs=0 --style=full --wrap=never")
generate_snapshot("tabs_passthrough_wrapped", "--tabs=0 --style=full --wrap=character")
generate_snapshot("tabs_4", "--tabs=4 --style=full --wrap=never")
generate_snapshot("tabs_4_wrapped", "--tabs=4 --style=full --wrap=character")
generate_snapshot("tabs_8", "--tabs=8 --style=full --wrap=never")
generate_snapshot("tabs_8_wrapped", "--tabs=8 --style=full --wrap=character")

def generate_style_snapshot(style):
generate_snapshot(style.replace(",","_"), "--style={}".format(style))

def generate_snapshot(name, arguments):
command = "../../target/debug/bat --decorations=always {1} sample.rs > output/{0}.snapshot.txt".format(
name,
arguments
)
print("generating snapshot for {}".format(option))
print("generating snapshot for {}".format(name))
subprocess.call(command, shell=True)

def build_bat():
Expand Down
16 changes: 15 additions & 1 deletion tests/snapshots/output/changes.snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,21 @@ _ fn main() {
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }
+
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
~ */
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,22 @@ _ │ fn main() {
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
+ │
│ // Tab alignment:
│ /*
│ Indent
│ 1 2 3 4
│ 1 ?
│ 22 ?
│ 333 ?
│ 4444 ?
│ 55555 ?
│ 666666 ?
│ 7777777 ?
│ 88888888 ?
~ │ */
──┴─────────────────────────────────────────────────────────────────────────────
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,22 @@ _ │ fn main() {
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
+ │
│ // Tab alignment:
│ /*
│ Indent
│ 1 2 3 4
│ 1 ?
│ 22 ?
│ 333 ?
│ 4444 ?
│ 55555 ?
│ 666666 ?
│ 7777777 ?
│ 88888888 ?
~ │ */
──┴─────────────────────────────────────────────────────────────────────────────
40 changes: 40 additions & 0 deletions tests/snapshots/output/changes_grid_header_numbers.snapshot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: "�����"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,22 @@
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
19
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,21 @@ _ fn main() {
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }
+
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
~ */
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,21 @@
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
19
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
23 +
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 ~ */
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,21 @@
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
19
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
23 +
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 ~ */
16 changes: 15 additions & 1 deletion tests/snapshots/output/full.snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,22 @@
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
19
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
Loading