Merge branch 'master' into fix-binary-detection-across-lines-3554
This commit is contained in:
17 files changed
+398
-28
No files matched your search
+2
-2
@@ -363,11 +363,11 @@ impl HighlightingAssets {
|
||||
reader: &mut InputReader,
|
||||
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||
let syntax_set = self.get_syntax_set()?;
|
||||
Ok(String::from_utf8(reader.first_line.clone())
|
||||
Ok(std::str::from_utf8(&reader.first_line)
|
||||
.ok()
|
||||
.and_then(|l| {
|
||||
// Strip UTF-8 BOM if present
|
||||
let line = l.strip_prefix('\u{feff}').unwrap_or(&l);
|
||||
let line = l.strip_prefix('\u{feff}').unwrap_or(l);
|
||||
syntax_set.find_syntax_by_first_line(line)
|
||||
})
|
||||
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
|
||||
|
||||
+43
-5
@@ -59,6 +59,10 @@ pub struct App {
|
||||
/// (not from config file or environment variables).
|
||||
/// This is used to honor the flag when piping output, similar to `cat -n`.
|
||||
number_from_cli: bool,
|
||||
/// True if -b / --number-nonblank was passed on the command line
|
||||
/// (not from config file or environment variables).
|
||||
/// This is used to honor the flag when piping output, similar to `cat -b`.
|
||||
number_nonblank_from_cli: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -81,12 +85,12 @@ impl App {
|
||||
return true;
|
||||
}
|
||||
// Handle combined short flags
|
||||
// Only count -n if it's the LAST flag in the combined form (so -p doesn't override it)
|
||||
// or if -p is not present in the combined form
|
||||
// Only count -n if its last occurrence comes after the last -p,
|
||||
// or if -p is not present in the combined form.
|
||||
if arg_str.starts_with('-') && !arg_str.starts_with("--") && arg_str.len() > 2 {
|
||||
let chars: Vec<char> = arg_str.chars().skip(1).collect();
|
||||
let n_pos = chars.iter().position(|&c| c == 'n');
|
||||
let p_pos = chars.iter().position(|&c| c == 'p');
|
||||
let n_pos = chars.iter().rposition(|&c| c == 'n');
|
||||
let p_pos = chars.iter().rposition(|&c| c == 'p');
|
||||
// -n is in the combined flag and either:
|
||||
// - -p is not present, OR
|
||||
// - -n comes after -p (so -n takes effect)
|
||||
@@ -99,6 +103,29 @@ impl App {
|
||||
false
|
||||
});
|
||||
|
||||
// Check if the -b / --number-nonblank option was passed on the command line
|
||||
// (before merging with config file and environment variables).
|
||||
// This is needed to honor the -b flag when piping output, similar to `cat -b`.
|
||||
// The same combined-flag logic applies as for -n above.
|
||||
let number_nonblank_from_cli = wild::args_os().any(|arg| {
|
||||
let arg_str = arg.to_string_lossy();
|
||||
if arg_str == "-b" || arg_str == "--number-nonblank" {
|
||||
return true;
|
||||
}
|
||||
// Handle combined short flags by comparing the last -b and -p occurrences.
|
||||
if arg_str.starts_with('-') && !arg_str.starts_with("--") && arg_str.len() > 2 {
|
||||
let chars: Vec<char> = arg_str.chars().skip(1).collect();
|
||||
let b_pos = chars.iter().rposition(|&c| c == 'b');
|
||||
let p_pos = chars.iter().rposition(|&c| c == 'p');
|
||||
if let Some(b) = b_pos {
|
||||
if p_pos.is_none() || b > p_pos.unwrap() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
|
||||
let matches = Self::matches(interactive_output)?;
|
||||
|
||||
if matches.get_flag("help") {
|
||||
@@ -139,6 +166,7 @@ impl App {
|
||||
matches,
|
||||
interactive_output,
|
||||
number_from_cli,
|
||||
number_nonblank_from_cli,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -454,7 +482,8 @@ impl App {
|
||||
.map(|s| s.as_str())
|
||||
== Some("always")
|
||||
|| self.matches.get_flag("force-colorization")
|
||||
|| self.number_from_cli),
|
||||
|| self.number_from_cli
|
||||
|| self.number_nonblank_from_cli),
|
||||
tab_width: self
|
||||
.matches
|
||||
.get_one::<String>("tabs")
|
||||
@@ -495,6 +524,8 @@ impl App {
|
||||
),
|
||||
quiet_empty: self.matches.get_flag("quiet-empty"),
|
||||
unbuffered: self.matches.get_flag("unbuffered"),
|
||||
number_nonblank: self.matches.get_flag("number-nonblank")
|
||||
|| self.number_nonblank_from_cli,
|
||||
theme: theme(self.theme_options()).to_string(),
|
||||
visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default()
|
||||
&& self.matches.get_flag("diff")
|
||||
@@ -613,6 +644,13 @@ impl App {
|
||||
])));
|
||||
}
|
||||
|
||||
// Only line numbers for non-blank lines if `--number-nonblank`.
|
||||
if self.matches.get_flag("number-nonblank") || self.number_nonblank_from_cli {
|
||||
return Some(StyleComponents(HashSet::from([
|
||||
StyleComponent::LineNumbers,
|
||||
])));
|
||||
}
|
||||
|
||||
// Plain if `--plain` is specified at least once.
|
||||
if self.matches.get_count("plain") > 0 {
|
||||
let mut components = HashSet::from([StyleComponent::Plain]);
|
||||
|
||||
@@ -97,6 +97,7 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
Arg::new("plain")
|
||||
.overrides_with("plain")
|
||||
.overrides_with("number")
|
||||
.overrides_with("number-nonblank")
|
||||
.short('p')
|
||||
.long("plain")
|
||||
.action(ArgAction::Count)
|
||||
@@ -277,6 +278,23 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
'--style=numbers'",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("number-nonblank")
|
||||
.long("number-nonblank")
|
||||
.overrides_with("number-nonblank")
|
||||
.short('b')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Show line numbers for non-blank lines only (alias for '--style=numbers').")
|
||||
.long_help(
|
||||
"Only show line numbers for non-blank lines, no other decorations. This is an \
|
||||
alias for '--style=numbers'. Non-blank lines are lines that contain any \
|
||||
character before the line ending, including spaces and tabs. When used \
|
||||
together with --number (-n), --number-nonblank (-b) takes precedence.\n\n\
|
||||
Example:\n \
|
||||
printf 'alpha\\n\\nbeta\\n' | bat -b\n \
|
||||
numbers 'alpha' and 'beta', but skips the empty line.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("color")
|
||||
.long("color")
|
||||
|
||||
@@ -119,6 +119,10 @@ pub struct Config<'a> {
|
||||
|
||||
/// Whether or not to use unbuffered input reading for streaming use cases
|
||||
pub unbuffered: bool,
|
||||
|
||||
/// Only number non-blank lines (like `cat -b`). Has no effect if `style_components` doesn't
|
||||
/// include `LineNumbers`.
|
||||
pub number_nonblank: bool,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
|
||||
+15
-4
@@ -276,10 +276,21 @@ impl Controller<'_> {
|
||||
}
|
||||
if !reached_eof {
|
||||
if reader.read_line(&mut current_line_buffer)? {
|
||||
// Fill the buffer
|
||||
buffered_lines
|
||||
.push_back((mem::take(&mut current_line_buffer), current_line_number));
|
||||
current_line_number += 1;
|
||||
// Fill the buffer. In number-nonblank mode, don't advance the
|
||||
// line counter for empty lines (content-free lines that contain
|
||||
// only the line terminator).
|
||||
if self.config.number_nonblank
|
||||
&& current_line_buffer
|
||||
.iter()
|
||||
.all(|&b| b == b'\r' || b == b'\n')
|
||||
{
|
||||
buffered_lines
|
||||
.push_back((mem::take(&mut current_line_buffer), current_line_number));
|
||||
} else {
|
||||
buffered_lines
|
||||
.push_back((mem::take(&mut current_line_buffer), current_line_number));
|
||||
current_line_number += 1;
|
||||
}
|
||||
} else {
|
||||
// No more data to read
|
||||
reached_eof = true;
|
||||
|
||||
@@ -45,6 +45,14 @@ impl Decoration for LineNumberDecoration {
|
||||
continuation: bool,
|
||||
_printer: &InteractivePrinter,
|
||||
) -> DecorationText {
|
||||
if line_number == 0 {
|
||||
// Blank line in number-nonblank mode: show empty space instead of a number.
|
||||
return DecorationText {
|
||||
text: self.color.paint(" ".repeat(self.width())).to_string(),
|
||||
width: self.width(),
|
||||
};
|
||||
}
|
||||
|
||||
if continuation {
|
||||
if line_number >= self.cached_wrap_invalid_at {
|
||||
let new_width = self.cached_wrap.width + 1;
|
||||
|
||||
+8
-1
@@ -738,10 +738,17 @@ impl Printer for InteractivePrinter<'_> {
|
||||
|
||||
// Line decorations.
|
||||
if self.panel_width > 0 {
|
||||
let display_line_number =
|
||||
if self.config.number_nonblank && line.trim_end_matches(['\r', '\n']).is_empty() {
|
||||
0
|
||||
} else {
|
||||
line_number
|
||||
};
|
||||
|
||||
let decorations = self
|
||||
.decorations
|
||||
.iter()
|
||||
.map(|d| d.generate(line_number, false, self));
|
||||
.map(|d| d.generate(display_line_number, false, self));
|
||||
|
||||
for deco in decorations {
|
||||
write!(handle, "{} ", deco.text)?;
|
||||
|
||||
Reference in new issue
Block a user