summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan P <32112321+eth-p@users.noreply.github.com>2020-10-31 23:14:00 -0700
committerGitHub <noreply@github.com>2020-10-31 23:14:00 -0700
commit61f947a580f385f66e96d60e2458e89f06940113 (patch)
treeefd7ab1b4aa89816d93f11c499fe1bb5169bb22c
parentc4bbf2886240148979f65d88f1adf548c3e5be4d (diff)
parent1614f80366a1fcc344df157d04bb5284ef09ee4c (diff)
Merge pull request #1276 from tommilligan/style-rule
style: add component 'rule' for horizontal file delimiter
-rw-r--r--CHANGELOG.md1
-rw-r--r--assets/manual/bat.1.in2
-rw-r--r--src/bin/bat/app.rs18
-rw-r--r--src/bin/bat/clap_app.rs5
-rw-r--r--src/pretty_printer.rs10
-rw-r--r--src/printer.rs24
-rw-r--r--src/style.rs7
-rw-r--r--tests/integration_tests.rs54
-rw-r--r--tests/snapshot_tests.rs10
-rwxr-xr-xtests/snapshots/generate_snapshots.py2
-rw-r--r--tests/snapshots/output/changes_grid_header_numbers_rule.snapshot.txt26
-rw-r--r--tests/snapshots/output/changes_grid_header_rule.snapshot.txt26
-rw-r--r--tests/snapshots/output/changes_grid_numbers_rule.snapshot.txt24
-rw-r--r--tests/snapshots/output/changes_grid_rule.snapshot.txt24
-rw-r--r--tests/snapshots/output/changes_header_numbers_rule.snapshot.txt23
-rw-r--r--tests/snapshots/output/changes_header_rule.snapshot.txt23
-rw-r--r--tests/snapshots/output/changes_numbers_rule.snapshot.txt22
-rw-r--r--tests/snapshots/output/changes_rule.snapshot.txt22
-rw-r--r--tests/snapshots/output/grid_header_numbers_rule.snapshot.txt26
-rw-r--r--tests/snapshots/output/grid_header_rule.snapshot.txt26
-rw-r--r--tests/snapshots/output/grid_numbers_rule.snapshot.txt24
-rw-r--r--tests/snapshots/output/grid_rule.snapshot.txt24
-rw-r--r--tests/snapshots/output/header_numbers_rule.snapshot.txt23
-rw-r--r--tests/snapshots/output/header_rule.snapshot.txt23
-rw-r--r--tests/snapshots/output/numbers_rule.snapshot.txt22
-rw-r--r--tests/snapshots/output/rule.snapshot.txt22
26 files changed, 499 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91a0ab46..ba6f7235 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
## Features
- Adjust pager configuration to comply with `--wrap=never`, see #1255 (@gahag)
+- Added a new `--style` value, `rule`, which adds a simple horizontal ruled line between files, see #1276 (@tommilligan)
## Bugfixes
diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in
index a6846b5d..65a17fe1 100644
--- a/assets/manual/bat.1.in
+++ b/assets/manual/bat.1.in
@@ -129,7 +129,7 @@ Configure which elements (line numbers, file headers, grid borders, Git modifica
of components to display (e.g. 'numbers,changes,grid') or a pre\-defined style ('full').
To set a default style, add the '\-\-style=".."' option to the configuration file or
export the BAT_STYLE environment variable (e.g.: export BAT_STYLE=".."). Possible
-values: *auto*, full, plain, changes, header, grid, numbers, snip.
+values: *auto*, full, plain, changes, header, grid, rule, numbers, snip.
.HP
\fB\-r\fR, \fB\-\-line\-range\fR <N:M>...
.IP
diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index 8249e3c9..8d09618c 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -288,8 +288,8 @@ impl App {
fn style_components(&self) -> Result<StyleComponents> {
let matches = &self.matches;
- Ok(StyleComponents(
- if matches.value_of("decorations") == Some("never") {
+ let mut styled_components =
+ StyleComponents(if matches.value_of("decorations") == Some("never") {
HashSet::new()
} else if matches.is_present("number") {
[StyleComponent::LineNumbers].iter().cloned().collect()
@@ -323,7 +323,17 @@ impl App {
acc.extend(components.iter().cloned());
acc
})
- },
- ))
+ });
+
+ // If `grid` is set, remove `rule` as it is a subset of `grid`, and print a warning.
+ if styled_components.grid() && styled_components.0.remove(&StyleComponent::Rule) {
+ use ansi_term::Colour::Yellow;
+ eprintln!(
+ "{}: Style 'rule' is a subset of style 'grid', 'rule' will not be visible.",
+ Yellow.paint("[bat warning]"),
+ );
+ }
+
+ Ok(styled_components)
}
}
diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs
index d04abd8a..4e642760 100644
--- a/src/bin/bat/clap_app.rs
+++ b/src/bin/bat/clap_app.rs
@@ -367,7 +367,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.validator(|val| {
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")]
"changes",
]
@@ -382,7 +382,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
})
.help(
"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(
"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 \
* grid: vertical/horizontal lines to separate side bar\n \
and the header from the content.\n \
+ * rule: horizontal lines to delimit files.\n \
* numbers: show line numbers in the side bar.\n \
* snip: draw separation lines between distinct line ranges.",
),
diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs
index c9530bf4..89745eec 100644
--- a/src/pretty_printer.rs
+++ b/src/pretty_printer.rs
@@ -23,6 +23,7 @@ struct ActiveStyleComponents {
header: bool,
vcs_modification_markers: bool,
grid: bool,
+ rule: bool,
line_numbers: bool,
snip: bool,
}
@@ -179,6 +180,12 @@ impl<'a> PrettyPrinter<'a> {
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
/// the `git` feature is not activated.
#[cfg_attr(
@@ -285,6 +292,9 @@ impl<'a> PrettyPrinter<'a> {
if self.active_style_components.grid {
style_components.push(StyleComponent::Grid);
}
+ if self.active_style_components.rule {
+ style_components.push(StyleComponent::Rule);
+ }
if self.active_style_components.header {
style_components.push(StyleComponent::Header);
}
diff --git a/src/printer.rs b/src/printer.rs
index 71bf78ee..e0829874 100644
--- a/src/printer.rs
+++ b/src/printer.rs
@@ -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<()> {
if self.panel_width == 0 {
- writeln!(
- handle,
- "{}",
- self.colors.grid.paint("─".repeat(self.config.term_width))
- )?;
+ self.print_horizontal_line_term(handle, self.colors.grid)?;
} else {
let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
@@ -251,6 +256,10 @@ impl<'a> Printer for InteractivePrinter<'a> {
input: &OpenedInput,
add_header_padding: bool,
) -> 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 Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
writeln!(
@@ -279,7 +288,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
.paint(if self.panel_width > 0 { "│ " } 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)?;
}
write!(handle, "{}", " ".repeat(self.panel_width))?;
@@ -603,6 +613,7 @@ const DEFAULT_GUTTER_COLOR: u8 = 238;
#[derive(Debug, Default)]
pub struct Colors {
pub grid: Style,
+ pub rule: Style,
pub filename: Style,
pub git_added: Style,
pub git_removed: Style,
@@ -624,6 +635,7 @@ impl Colors {
Colors {
grid: gutter_color.normal(),
+ rule: gutter_color.normal(),
filename: Style::new().bold(),
git_added: Green.normal(),
git_removed: Red.normal(),
diff --git a/src/style.rs b/src/style.rs
index 8d51cbde..65414206 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -9,6 +9,7 @@ pub enum StyleComponent {
#[cfg(feature = "git")]
Changes,
Grid,
+ Rule,
Header,
LineNumbers,
Snip,
@@ -29,6 +30,7 @@ impl StyleComponent {
#[cfg(feature = "git")]
StyleComponent::Changes => &[StyleComponent::Changes],
StyleComponent::Grid => &[StyleComponent::Grid],
+ StyleComponent::Rule => &[StyleComponent::Rule],
StyleComponent::Header => &[StyleComponent::Header],
StyleComponent::LineNumbers => &[StyleComponent::LineNumbers],
StyleComponent::Snip => &[StyleComponent::Snip],
@@ -54,6 +56,7 @@ impl FromStr for StyleComponent {
#[cfg(feature = "git")]
"changes" => Ok(StyleComponent::Changes),
"grid" => Ok(StyleComponent::Grid),
+ "rule" => Ok(StyleComponent::Rule),
"header" => Ok(StyleComponent::Header),
"numbers" => Ok(StyleComponent::LineNumbers),
"snip" => Ok(StyleComponent::Snip),
@@ -81,6 +84,10 @@ impl StyleComponents {
self.0.contains(&StyleComponent::Grid)
}
+ pub fn rule(&self) -> bool {
+ self.0.contains(&StyleComponent::Rule)
+ }
+
pub fn header(&self) -> bool {
self.0.contains(&StyleComponent::Header)
}
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 09ac8498..ac11efb4 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -581,6 +581,18 @@ fn empty_file_leads_to_empty_output_with_grid_enabled() {
}
#[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]
fn filename_basic() {
bat()
.arg("test.txt")
@@ -678,6 +690,48 @@ fn header_padding() {
.stderr("");
}
+#[test]
+fn header_padding_rule() {
+ bat()
+ .arg("--decorations=always")
+ .arg("--style=header,rule")
+ .arg("--terminal-width=80")
+ .arg("test.txt")
+ .arg("single-line.txt")
+ .assert()
+ .stdout(
+ "File: test.txt
+hello world
+────────────────────────────────────────────────────────────────────────────────
+File: single-line.txt
+Single Line
+",
+ )
+ .stderr("");
+}
+
+#[test]
+fn grid_overrides_rule() {
+ bat()
+ .arg("--decorations=always")
+ .arg("--style=grid,rule")
+ .arg("--terminal-width=80")
+ .arg("test.txt")
+ .arg("single-line.txt")
+ .assert()
+ .stdout(
+ "\
+────────────────────────────────────────────────────────────────────────────────
+hello world
+────────────────────────────────────────────────────────────────────────────────
+────────────────────────────────────────────────────────────────────────────────
+Single Line
+────────────────────────────────────────────────────────────────────────────────
+",
+ )
+ .stderr("\x1b[33m[bat warning]\x1b[0m: Style 'rule' is a subset of style 'grid', 'rule' will not be visible.\n");
+}
+
#[cfg(target_os = "linux")]
#[test]
fn file_with_invalid_utf8_filename() {
diff --git a/tests/snapshot_tests.rs b/tests/snapshot_tests.rs
index ad41edb2..8abb8414 100644
--- a/tests/snapshot_tests.rs
+++ b/tests/snapshot_tests.rs
@@ -19,17 +19,27 @@ snapshot_tests! {
grid: "grid",
header: "header",
numbers: "numbers",
+ rule: "rule",
changes_grid: "changes,grid",
changes_header: "changes,header",
changes_numbers: "changes,numbers",
+ changes_rule: "changes,rule",
grid_header: "grid,header",
grid_numbers: "grid,numbers",
+ grid_rule: "grid,rule",
header_numbers: "header,numbers",
+ header_rule: "header,rule",
changes_grid_header: "changes,grid,header",
changes_grid_numbers: "changes,grid,numbers",
+ changes_grid_rule: "changes,grid,rule",
changes_header_numbers: "changes,header,numbers",
+ changes_header_rule: "changes,header,rule",
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_rule: "changes,grid,header,rule",
+ changes_grid_header_numbers_rule: "changes,grid,header,numbers,rule",
full: "full",
plain: "plain",
}
diff --git a/tests/snapshots/generate_snapshots.py b/tests/snapshots/generate_snapshots.py
index fd17b285..bb7d69ba 100755
--- a/tests/snapshots/generate_snapshots.py
+++ b/tests/snapshots/generate_snapshots.py
@@ -7,7 +7,7 @@ import shutil
def generate_snapshots():
- single_styles = ["changes", "grid", "header", "numbers"]
+ single_styles = ["changes", "grid", "header", "numbers", "rule"]
collective_styles = ["full", "plain"]
for num in range(len(single_styles)):
diff --git a/tests/snapshots/output/changes_grid_header_numbers_rule.snapshot.txt b/tests/snapshots/output/changes_grid_header_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..cee4737f
--- /dev/null
+++ b/tests/snapshots/output/changes_grid_header_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 + │ }
+───────┴────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/changes_grid_header_rule.snapshot.txt b/tests/snapshots/output/changes_grid_header_rule.snapshot.txt
new file mode 100644
index 00000000..655b6e24
--- /dev/null
+++ b/tests/snapshots/output/changes_grid_header_rule.snapshot.txt
@@ -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: "�����"#;
+ │ }
+ │
+ │ fn area(rectangle: &Rectangle) -> u32 {
+ │ rectangle.width * rectangle.height
+ │ }
++ │
++ │ fn perimeter(rectangle: &Rectangle) -> u32 {
++ │ (rectangle.width + rectangle.height) * 2
++ │ }
+──┴─────────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/changes_grid_numbers_rule.snapshot.txt b/tests/snapshots/output/changes_grid_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..7fe7cccf
--- /dev/null
+++ b/tests/snapshots/output/changes_grid_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 + │ }
+───────┴────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/changes_grid_rule.snapshot.txt b/tests/snapshots/output/changes_grid_rule.snapshot.txt
new file mode 100644
index 00000000..04767b8b
--- /dev/null
+++ b/tests/snapshots/output/changes_grid_rule.snapshot.txt
@@ -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: "�����"#;
+ │ }
+ │
+ │ fn area(rectangle: &Rectangle) -> u32 {
+ │ rectangle.width * rectangle.height
+ │ }
++ │
++ │ fn perimeter(rectangle: &Rectangle) -> u32 {
++ │ (rectangle.width + rectangle.height) * 2
++ │ }
+──┴─────────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/changes_header_numbers_rule.snapshot.txt b/tests/snapshots/output/changes_header_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..77145eb9
--- /dev/null
+++ b/tests/snapshots/output/changes_header_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 + }
diff --git a/tests/snapshots/output/changes_header_rule.snapshot.txt b/tests/snapshots/output/changes_header_rule.snapshot.txt
new file mode 100644
index 00000000..82fe8c47
--- /dev/null
+++ b/tests/snapshots/output/changes_header_rule.snapshot.txt
@@ -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: "�����"#;
+ }
+
+ fn area(rectangle: &Rectangle) -> u32 {
+ rectangle.width * rectangle.height
+ }
++
++ fn perimeter(rectangle: &Rectangle) -> u32 {
++ (rectangle.width + rectangle.height) * 2
++ }
diff --git a/tests/snapshots/output/changes_numbers_rule.snapshot.txt b/tests/snapshots/output/changes_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..bdfe745b
--- /dev/null
+++ b/tests/snapshots/output/changes_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 + }
diff --git a/tests/snapshots/output/changes_rule.snapshot.txt b/tests/snapshots/output/changes_rule.snapshot.txt
new file mode 100644
index 00000000..28d87201
--- /dev/null
+++ b/tests/snapshots/output/changes_rule.snapshot.txt
@@ -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: "�����"#;
+ }
+
+ fn area(rectangle: &Rectangle) -> u32 {
+ rectangle.width * rectangle.height
+ }
++
++ fn perimeter(rectangle: &Rectangle) -> u32 {
++ (rectangle.width + rectangle.height) * 2
++ }
diff --git a/tests/snapshots/output/grid_header_numbers_rule.snapshot.txt b/tests/snapshots/output/grid_header_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..e9f1a9cf
--- /dev/null
+++ b/tests/snapshots/output/grid_header_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 │ }
+─────┴──────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/grid_header_rule.snapshot.txt b/tests/snapshots/output/grid_header_rule.snapshot.txt
new file mode 100644
index 00000000..50c16941
--- /dev/null
+++ b/tests/snapshots/output/grid_header_rule.snapshot.txt
@@ -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: "�����"#;
+}
+
+fn area(rectangle: &Rectangle) -> u32 {
+ rectangle.width * rectangle.height
+}
+
+fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+}
+────────────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/grid_numbers_rule.snapshot.txt b/tests/snapshots/output/grid_numbers_rule.snapshot.txt
new file mode 100644
index 00000000..09614667
--- /dev/null
+++ b/tests/snapshots/output/grid_numbers_rule.snapshot.txt
@@ -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: "�����"#;
+ 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 │ }
+─────┴──────────────────────────────────────────────────────────────────────────
diff --git a/tests/snapshots/output/grid_rule.snapshot.txt b/tests/snapshots/output/grid_rule.snapshot.txt
new file mode 100644
index 00000000..5b970581
--- /dev/null
+++ b/tests/snapshots/output/grid_rule.snapshot.txt
@@ -0,0 +1,24 @@
+────────────────────────────────────────────────────────────────────────────────
+struct Rectangle {
+ width: u32,
+ height: u32,
+}
+
+fn main() {
+ let rect1 = Rectangle { width: 30, height: 50 };
+
+ println!(
+ "The peri