style: add component 'rule' for horizontal file delimiter

This commit is contained in:
Tom Milligan 2020-10-06 16:57:47 +01:00
parent 33128d75f2
commit aa4000cb0d
No known key found for this signature in database
GPG Key ID: BFF10895C45328D2
23 changed files with 441 additions and 9 deletions

View File

@ -367,7 +367,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.validator(|val| { .validator(|val| {
let mut invalid_vals = val.split(',').filter(|style| { let mut invalid_vals = val.split(',').filter(|style| {
!&[ !&[
"auto", "full", "plain", "header", "grid", "numbers", "snip", "auto", "full", "plain", "header", "grid", "rule", "numbers", "snip",
#[cfg(feature = "git")] #[cfg(feature = "git")]
"changes", "changes",
] ]
@ -382,7 +382,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
}) })
.help( .help(
"Comma-separated list of style elements to display \ "Comma-separated list of style elements to display \
(*auto*, full, plain, changes, header, grid, numbers, snip).", (*auto*, full, plain, changes, header, grid, rule, numbers, snip).",
) )
.long_help( .long_help(
"Configure which elements (line numbers, file headers, grid \ "Configure which elements (line numbers, file headers, grid \
@ -400,6 +400,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
* header: show filenames before the content.\n \ * header: show filenames before the content.\n \
* grid: vertical/horizontal lines to separate side bar\n \ * grid: vertical/horizontal lines to separate side bar\n \
and the header from the content.\n \ and the header from the content.\n \
* rule: horizontal lines to delimit files.\n \
* numbers: show line numbers in the side bar.\n \ * numbers: show line numbers in the side bar.\n \
* snip: draw separation lines between distinct line ranges.", * snip: draw separation lines between distinct line ranges.",
), ),

View File

@ -23,6 +23,7 @@ struct ActiveStyleComponents {
header: bool, header: bool,
vcs_modification_markers: bool, vcs_modification_markers: bool,
grid: bool, grid: bool,
rule: bool,
line_numbers: bool, line_numbers: bool,
snip: bool, snip: bool,
} }
@ -179,6 +180,12 @@ impl<'a> PrettyPrinter<'a> {
self self
} }
/// Whether to paint a horizontal rule to delimit files
pub fn rule(&mut self, yes: bool) -> &mut Self {
self.active_style_components.rule = yes;
self
}
/// Whether to show modification markers for VCS changes. This has no effect if /// Whether to show modification markers for VCS changes. This has no effect if
/// the `git` feature is not activated. /// the `git` feature is not activated.
#[cfg_attr( #[cfg_attr(
@ -285,6 +292,9 @@ impl<'a> PrettyPrinter<'a> {
if self.active_style_components.grid { if self.active_style_components.grid {
style_components.push(StyleComponent::Grid); style_components.push(StyleComponent::Grid);
} }
if self.active_style_components.rule {
style_components.push(StyleComponent::Rule);
}
if self.active_style_components.header { if self.active_style_components.header {
style_components.push(StyleComponent::Header); style_components.push(StyleComponent::Header);
} }

View File

@ -200,13 +200,18 @@ impl<'a> InteractivePrinter<'a> {
}) })
} }
fn print_horizontal_line_term(&mut self, handle: &mut dyn Write, style: Style) -> Result<()> {
writeln!(
handle,
"{}",
style.paint("".repeat(self.config.term_width))
)?;
Ok(())
}
fn print_horizontal_line(&mut self, handle: &mut dyn Write, grid_char: char) -> Result<()> { fn print_horizontal_line(&mut self, handle: &mut dyn Write, grid_char: char) -> Result<()> {
if self.panel_width == 0 { if self.panel_width == 0 {
writeln!( self.print_horizontal_line_term(handle, self.colors.grid)?;
handle,
"{}",
self.colors.grid.paint("".repeat(self.config.term_width))
)?;
} else { } else {
let hline = "".repeat(self.config.term_width - (self.panel_width + 1)); let hline = "".repeat(self.config.term_width - (self.panel_width + 1));
let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline); let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline);
@ -251,6 +256,10 @@ impl<'a> Printer for InteractivePrinter<'a> {
input: &OpenedInput, input: &OpenedInput,
add_header_padding: bool, add_header_padding: bool,
) -> Result<()> { ) -> Result<()> {
if add_header_padding && self.config.style_components.rule() {
self.print_horizontal_line_term(handle, self.colors.rule)?;
}
if !self.config.style_components.header() { if !self.config.style_components.header() {
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
writeln!( writeln!(
@ -279,7 +288,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
.paint(if self.panel_width > 0 { "" } else { "" }), .paint(if self.panel_width > 0 { "" } else { "" }),
)?; )?;
} else { } else {
if add_header_padding { // Only pad space between files, if we haven't already drawn a horizontal rule
if add_header_padding && !self.config.style_components.rule() {
writeln!(handle)?; writeln!(handle)?;
} }
write!(handle, "{}", " ".repeat(self.panel_width))?; write!(handle, "{}", " ".repeat(self.panel_width))?;
@ -603,6 +613,7 @@ const DEFAULT_GUTTER_COLOR: u8 = 238;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Colors { pub struct Colors {
pub grid: Style, pub grid: Style,
pub rule: Style,
pub filename: Style, pub filename: Style,
pub git_added: Style, pub git_added: Style,
pub git_removed: Style, pub git_removed: Style,
@ -624,6 +635,7 @@ impl Colors {
Colors { Colors {
grid: gutter_color.normal(), grid: gutter_color.normal(),
rule: gutter_color.normal(),
filename: Style::new().bold(), filename: Style::new().bold(),
git_added: Green.normal(), git_added: Green.normal(),
git_removed: Red.normal(), git_removed: Red.normal(),

View File

@ -9,6 +9,7 @@ pub enum StyleComponent {
#[cfg(feature = "git")] #[cfg(feature = "git")]
Changes, Changes,
Grid, Grid,
Rule,
Header, Header,
LineNumbers, LineNumbers,
Snip, Snip,
@ -29,6 +30,7 @@ impl StyleComponent {
#[cfg(feature = "git")] #[cfg(feature = "git")]
StyleComponent::Changes => &[StyleComponent::Changes], StyleComponent::Changes => &[StyleComponent::Changes],
StyleComponent::Grid => &[StyleComponent::Grid], StyleComponent::Grid => &[StyleComponent::Grid],
StyleComponent::Rule => &[StyleComponent::Rule],
StyleComponent::Header => &[StyleComponent::Header], StyleComponent::Header => &[StyleComponent::Header],
StyleComponent::LineNumbers => &[StyleComponent::LineNumbers], StyleComponent::LineNumbers => &[StyleComponent::LineNumbers],
StyleComponent::Snip => &[StyleComponent::Snip], StyleComponent::Snip => &[StyleComponent::Snip],
@ -54,6 +56,7 @@ impl FromStr for StyleComponent {
#[cfg(feature = "git")] #[cfg(feature = "git")]
"changes" => Ok(StyleComponent::Changes), "changes" => Ok(StyleComponent::Changes),
"grid" => Ok(StyleComponent::Grid), "grid" => Ok(StyleComponent::Grid),
"rule" => Ok(StyleComponent::Rule),
"header" => Ok(StyleComponent::Header), "header" => Ok(StyleComponent::Header),
"numbers" => Ok(StyleComponent::LineNumbers), "numbers" => Ok(StyleComponent::LineNumbers),
"snip" => Ok(StyleComponent::Snip), "snip" => Ok(StyleComponent::Snip),
@ -81,6 +84,10 @@ impl StyleComponents {
self.0.contains(&StyleComponent::Grid) self.0.contains(&StyleComponent::Grid)
} }
pub fn rule(&self) -> bool {
self.0.contains(&StyleComponent::Rule)
}
pub fn header(&self) -> bool { pub fn header(&self) -> bool {
self.0.contains(&StyleComponent::Header) self.0.contains(&StyleComponent::Header)
} }

View File

@ -570,6 +570,18 @@ fn empty_file_leads_to_empty_output_with_grid_enabled() {
.stdout(""); .stdout("");
} }
#[test]
fn empty_file_leads_to_empty_output_with_rule_enabled() {
bat()
.arg("empty.txt")
.arg("--style=rule")
.arg("--decorations=always")
.arg("--terminal-width=80")
.assert()
.success()
.stdout("");
}
#[test] #[test]
fn filename_basic() { fn filename_basic() {
bat() bat()

View File

@ -19,17 +19,27 @@ snapshot_tests! {
grid: "grid", grid: "grid",
header: "header", header: "header",
numbers: "numbers", numbers: "numbers",
rule: "rule",
changes_grid: "changes,grid", changes_grid: "changes,grid",
changes_header: "changes,header", changes_header: "changes,header",
changes_numbers: "changes,numbers", changes_numbers: "changes,numbers",
changes_rule: "changes,rule",
grid_header: "grid,header", grid_header: "grid,header",
grid_numbers: "grid,numbers", grid_numbers: "grid,numbers",
grid_rule: "grid,rule",
header_numbers: "header,numbers", header_numbers: "header,numbers",
header_rule: "header,rule",
changes_grid_header: "changes,grid,header", changes_grid_header: "changes,grid,header",
changes_grid_numbers: "changes,grid,numbers", changes_grid_numbers: "changes,grid,numbers",
changes_grid_rule: "changes,grid,rule",
changes_header_numbers: "changes,header,numbers", changes_header_numbers: "changes,header,numbers",
changes_header_rule: "changes,header,rule",
grid_header_numbers: "grid,header,numbers", grid_header_numbers: "grid,header,numbers",
grid_header_rule: "grid,header,rule",
header_numbers_rule: "header,numbers,rule",
changes_grid_header_numbers: "changes,grid,header,numbers", changes_grid_header_numbers: "changes,grid,header,numbers",
changes_grid_header_rule: "changes,grid,header,rule",
changes_grid_header_numbers_rule: "changes,grid,header,numbers,rule",
full: "full", full: "full",
plain: "plain", plain: "plain",
} }

View File

@ -7,7 +7,7 @@ import shutil
def generate_snapshots(): def generate_snapshots():
single_styles = ["changes", "grid", "header", "numbers"] single_styles = ["changes", "grid", "header", "numbers", "rule"]
collective_styles = ["full", "plain"] collective_styles = ["full", "plain"]
for num in range(len(single_styles)): for num in range(len(single_styles)):

View File

@ -0,0 +1,26 @@
───────┬────────────────────────────────────────────────────────────────────────
│ 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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,26 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ File: sample.rs
──┼─────────────────────────────────────────────────────────────────────────────
│ struct Rectangle {
│ width: u32,
│ height: u32,
│ }
_ │ fn main() {
│ let rect1 = Rectangle { width: 30, height: 50 };
│ println!(
~ │ "The perimeter of the rectangle is {} pixels.",
~ │ perimeter(&rect1)
│ );
+ │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
│ }
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+ │
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
──┴─────────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,24 @@
───────┬────────────────────────────────────────────────────────────────────────
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,24 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ struct Rectangle {
│ width: u32,
│ height: u32,
│ }
_ │ fn main() {
│ let rect1 = Rectangle { width: 30, height: 50 };
│ println!(
~ │ "The perimeter of the rectangle is {} pixels.",
~ │ perimeter(&rect1)
│ );
+ │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
│ }
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+ │
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
──┴─────────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,23 @@
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 + }

View File

@ -0,0 +1,23 @@
File: sample.rs
struct Rectangle {
width: u32,
height: u32,
}
_ fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
~ "The perimeter of the rectangle is {} pixels.",
~ perimeter(&rect1)
);
+ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }

View File

@ -0,0 +1,22 @@
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 + }

View File

@ -0,0 +1,22 @@
struct Rectangle {
width: u32,
height: u32,
}
_ fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
~ "The perimeter of the rectangle is {} pixels.",
~ perimeter(&rect1)
);
+ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }

View File

@ -0,0 +1,26 @@
─────┬──────────────────────────────────────────────────────────────────────────
│ 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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,26 @@
────────────────────────────────────────────────────────────────────────────────
File: sample.rs
────────────────────────────────────────────────────────────────────────────────
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The perimeter of the rectangle is {} pixels.",
perimeter(&rect1)
);
println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
────────────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,24 @@
─────┬──────────────────────────────────────────────────────────────────────────
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,24 @@
────────────────────────────────────────────────────────────────────────────────
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The perimeter of the rectangle is {} pixels.",
perimeter(&rect1)
);
println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
────────────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,23 @@
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 }

View File

@ -0,0 +1,23 @@
File: sample.rs
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The perimeter of the rectangle is {} pixels.",
perimeter(&rect1)
);
println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}

View File

@ -0,0 +1,22 @@
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: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
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 }

View File

@ -0,0 +1,22 @@
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The perimeter of the rectangle is {} pixels.",
perimeter(&rect1)
);
println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}