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

Add -s/--squeeze and --squeeze-limit (based on #1441). #2665

Merged
merged 11 commits into from
Feb 24, 2024
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
## Features

- Set terminal title to file names when Paging is not Paging::Never #2807 (@Oliver-Looney)
- `bat --squeeze`/`bat -s` will now squeeze consecutive empty lines, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815)
- `bat --squeeze-limit` to set the maximum number of empty consecutive when using `--squeeze`, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815)
sharkdp marked this conversation as resolved.
Show resolved Hide resolved
- `PrettyPrinter::squeeze_empty_lines` to support line squeezing for bat as a library, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815)

## Bugfixes

Expand Down
6 changes: 6 additions & 0 deletions doc/long-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ Options:
--list-themes
Display a list of supported themes for syntax highlighting.

-s, --squeeze-blank
Squeeze consecutive empty lines into a single empty line.

--squeeze-limit <squeeze-limit>
Set the maximum number of consecutive empty lines to be printed.

--style <components>
Configure which elements (line numbers, file headers, grid borders, Git modifications, ..)
to display in addition to the file contents. The argument is a comma-separated list of
Expand Down
2 changes: 2 additions & 0 deletions doc/short-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Options:
Set the color theme for syntax highlighting.
--list-themes
Display all supported highlighting themes.
-s, --squeeze-blank
Squeeze consecutive empty lines.
--style <components>
Comma-separated list of style elements to display (*default*, auto, full, plain, changes,
header, header-filename, header-filesize, grid, rule, numbers, snip).
Expand Down
10 changes: 10 additions & 0 deletions src/bin/bat/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,16 @@ impl App {
#[cfg(feature = "lessopen")]
use_lessopen: self.matches.get_flag("lessopen"),
set_terminal_title: self.matches.get_flag("set-terminal-title"),
squeeze_lines: if self.matches.get_flag("squeeze-blank") {
Some(
self.matches
.get_one::<usize>("squeeze-limit")
.map(|limit| limit.to_owned())
.unwrap_or(1),
)
} else {
None
},
})
}

Expand Down
15 changes: 15 additions & 0 deletions src/bin/bat/clap_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,21 @@ pub fn build_app(interactive_output: bool) -> Command {
.help("Display all supported highlighting themes.")
.long_help("Display a list of supported themes for syntax highlighting."),
)
.arg(
Arg::new("squeeze-blank")
.long("squeeze-blank")
.short('s')
.action(ArgAction::SetTrue)
.help("Squeeze consecutive empty lines.")
.long_help("Squeeze consecutive empty lines into a single empty line.")
)
.arg(
Arg::new("squeeze-limit")
.long("squeeze-limit")
.value_parser(|s: &str| s.parse::<usize>().map_err(|_| "Requires a non-negative number".to_owned()))
.long_help("Set the maximum number of consecutive empty lines to be printed.")
.hide_short_help(true)
)
.arg(
Arg::new("style")
.long("style")
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ pub struct Config<'a> {

// Weather or not to set terminal title when using a pager
pub set_terminal_title: bool,

/// The maximum number of consecutive empty lines to display
pub squeeze_lines: Option<usize>,
}

#[cfg(all(feature = "minimal-application", feature = "paging"))]
Expand Down
6 changes: 6 additions & 0 deletions src/pretty_printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ impl<'a> PrettyPrinter<'a> {
self
}

/// Specify the maximum number of consecutive empty lines to print.
pub fn squeeze_empty_lines(&mut self, maximum: Option<usize>) -> &mut Self {
self.config.squeeze_lines = maximum;
self
}

/// Specify the highlighting theme
pub fn theme(&mut self, theme: impl AsRef<str>) -> &mut Self {
self.config.theme = theme.as_ref().to_owned();
Expand Down
35 changes: 34 additions & 1 deletion src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,15 @@ pub(crate) trait Printer {

pub struct SimplePrinter<'a> {
config: &'a Config<'a>,
consecutive_empty_lines: usize,
}

impl<'a> SimplePrinter<'a> {
pub fn new(config: &'a Config) -> Self {
SimplePrinter { config }
SimplePrinter {
config,
consecutive_empty_lines: 0,
}
}
}

Expand Down Expand Up @@ -134,6 +138,21 @@ impl<'a> Printer for SimplePrinter<'a> {
_line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
// Skip squeezed lines.
if let Some(squeeze_limit) = self.config.squeeze_lines {
if String::from_utf8_lossy(line_buffer)
.trim_end_matches(|c| c == '\r' || c == '\n')
.is_empty()
{
self.consecutive_empty_lines += 1;
if self.consecutive_empty_lines > squeeze_limit {
return Ok(());
}
} else {
self.consecutive_empty_lines = 0;
}
}

if !out_of_range {
if self.config.show_nonprintable {
let line = replace_nonprintable(
Expand Down Expand Up @@ -187,6 +206,7 @@ pub(crate) struct InteractivePrinter<'a> {
pub line_changes: &'a Option<LineChanges>,
highlighter_from_set: Option<HighlighterFromSet<'a>>,
background_color_highlight: Option<Color>,
consecutive_empty_lines: usize,
}

impl<'a> InteractivePrinter<'a> {
Expand Down Expand Up @@ -272,6 +292,7 @@ impl<'a> InteractivePrinter<'a> {
line_changes,
highlighter_from_set,
background_color_highlight,
consecutive_empty_lines: 0,
})
}

Expand Down Expand Up @@ -577,6 +598,18 @@ impl<'a> Printer for InteractivePrinter<'a> {
return Ok(());
}

// Skip squeezed lines.
if let Some(squeeze_limit) = self.config.squeeze_lines {
if line.trim_end_matches(|c| c == '\r' || c == '\n').is_empty() {
self.consecutive_empty_lines += 1;
if self.consecutive_empty_lines > squeeze_limit {
return Ok(());
}
} else {
self.consecutive_empty_lines = 0;
}
}

let mut cursor: usize = 0;
let mut cursor_max: usize = self.config.term_width;
let mut cursor_total: usize = 0;
Expand Down
30 changes: 30 additions & 0 deletions tests/examples/empty_lines.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
line 1



line 5














line 20
line 21


line 24

line 26



line 30
64 changes: 64 additions & 0 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,70 @@ fn line_range_multiple() {
.stdout("line 1\nline 2\nline 4\n");
}

#[test]
fn squeeze_blank() {
bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.assert()
.success()
.stdout("line 1\n\nline 5\n\nline 20\nline 21\n\nline 24\n\nline 26\n\nline 30\n");
}

#[test]
fn squeeze_blank_line_numbers() {
bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.arg("--decorations=always")
.arg("--number")
.assert()
.success()
.stdout(" 1 line 1\n 2 \n 5 line 5\n 6 \n 20 line 20\n 21 line 21\n 22 \n 24 line 24\n 25 \n 26 line 26\n 27 \n 30 line 30\n");
}

#[test]
fn squeeze_limit() {
bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.arg("--squeeze-limit=2")
.assert()
.success()
.stdout("line 1\n\n\nline 5\n\n\nline 20\nline 21\n\n\nline 24\n\nline 26\n\n\nline 30\n");

bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.arg("--squeeze-limit=5")
.assert()
.success()
.stdout("line 1\n\n\n\nline 5\n\n\n\n\n\nline 20\nline 21\n\n\nline 24\n\nline 26\n\n\n\nline 30\n");
}

#[test]
fn squeeze_limit_line_numbers() {
bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.arg("--squeeze-limit=2")
.arg("--decorations=always")
.arg("--number")
.assert()
.success()
.stdout(" 1 line 1\n 2 \n 3 \n 5 line 5\n 6 \n 7 \n 20 line 20\n 21 line 21\n 22 \n 23 \n 24 line 24\n 25 \n 26 line 26\n 27 \n 28 \n 30 line 30\n");

bat()
.arg("empty_lines.txt")
.arg("--squeeze-blank")
.arg("--squeeze-limit=5")
.arg("--decorations=always")
.arg("--number")
.assert()
.success()
.stdout(" 1 line 1\n 2 \n 3 \n 4 \n 5 line 5\n 6 \n 7 \n 8 \n 9 \n 10 \n 20 line 20\n 21 line 21\n 22 \n 23 \n 24 line 24\n 25 \n 26 line 26\n 27 \n 28 \n 29 \n 30 line 30\n");
}

#[test]
#[cfg_attr(any(not(feature = "git"), target_os = "windows"), ignore)]
fn short_help() {
Expand Down