mirror of
https://github.com/sharkdp/bat
synced 2026-08-26 14:13:37 +01:00
Merge pull request #3816 from adnrivera/fix/ignored-suffix-firstline
Fix --ignored-suffix to fall back to first-line detection
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
- Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres)
|
- Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres)
|
||||||
|
|
||||||
## Bugfixes
|
## Bugfixes
|
||||||
|
- Fix `--ignored-suffix` not falling back to first-line/shebang detection when the ignored suffix is also a registered extension (e.g. `--ignored-suffix .txt` on a shebang script), see #2745 and #3816 (@adnrivera)
|
||||||
- Fix `capacity overflow` panic when printing a snip separator at `--terminal-width=1` with multiple line ranges. Closes #3803, see #3804 (@leeewee)
|
- Fix `capacity overflow` panic when printing a snip separator at `--terminal-width=1` with multiple line ranges. Closes #3803, see #3804 (@leeewee)
|
||||||
- Pass `--no-paging` to `bat` invocations inside the bash / zsh / fish / PowerShell shell completion scripts so that shell-level pager wiring (e.g. `LESSOPEN='|-bat -f -pp %s'`) cannot inject ANSI escape sequences into the completion candidates. Closes #3760 (@mvanhorn)
|
- Pass `--no-paging` to `bat` invocations inside the bash / zsh / fish / PowerShell shell completion scripts so that shell-level pager wiring (e.g. `LESSOPEN='|-bat -f -pp %s'`) cannot inject ANSI escape sequences into the completion candidates. Closes #3760 (@mvanhorn)
|
||||||
- Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit)
|
- Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit)
|
||||||
|
|||||||
+50
-8
@@ -347,15 +347,15 @@ impl HighlightingAssets {
|
|||||||
file_name: &OsStr,
|
file_name: &OsStr,
|
||||||
ignored_suffixes: &IgnoredSuffixes,
|
ignored_suffixes: &IgnoredSuffixes,
|
||||||
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||||
let mut syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
|
let stripped =
|
||||||
if syntax.is_none() {
|
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
|
||||||
syntax =
|
self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes)
|
||||||
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
|
.map(Some)
|
||||||
// Note: recursion
|
})?;
|
||||||
self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes)
|
match stripped {
|
||||||
})?;
|
Some(syntax) => Ok(syntax),
|
||||||
|
None => self.find_syntax_by_extension(Path::new(file_name).extension()),
|
||||||
}
|
}
|
||||||
Ok(syntax)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_first_line_syntax(
|
fn get_first_line_syntax(
|
||||||
@@ -686,6 +686,48 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn syntax_detection_ignored_suffix_falls_back_to_first_line() {
|
||||||
|
let mut test = SyntaxDetectionTest::new();
|
||||||
|
|
||||||
|
// By default a `.txt` file uses Plain Text, even with a shebang: the
|
||||||
|
// `.txt` extension wins and first-line detection is not reached.
|
||||||
|
assert_eq!(
|
||||||
|
test.syntax_for_file_with_content("test.txt", "#!/usr/bin/env bash"),
|
||||||
|
"Plain Text"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Once `.txt` is an ignored suffix it is stripped before detection. The
|
||||||
|
// stripped name (`test`) has no extension, so detection falls back to the
|
||||||
|
// first line -- even though `.txt` is itself a registered extension that
|
||||||
|
// would otherwise match as Plain Text. See #2745.
|
||||||
|
test.syntax_mapping.insert_ignored_suffix(".txt");
|
||||||
|
assert_eq!(
|
||||||
|
test.syntax_for_file_with_content("test.txt", "#!/usr/bin/env bash"),
|
||||||
|
"Bourne Again Shell (bash)"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
test.syntax_for_file_with_content("test.txt", "<?php"),
|
||||||
|
"PHP"
|
||||||
|
);
|
||||||
|
|
||||||
|
// A `.txt` file without a recognizable first line keeps no syntax (the
|
||||||
|
// caller renders it as plain text); we must not resurrect the shadowed
|
||||||
|
// `.txt` -> Plain Text match here.
|
||||||
|
assert_eq!(
|
||||||
|
test.syntax_for_file_with_content("notes.txt", "just some prose"),
|
||||||
|
"!no syntax!"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stripping that exposes a real extension still works: `.dev` is ignored,
|
||||||
|
// and the remaining `.json` extension is detected as usual.
|
||||||
|
test.syntax_mapping.insert_ignored_suffix(".dev");
|
||||||
|
assert_eq!(
|
||||||
|
test.syntax_for_file_with_content("config.json.dev", ""),
|
||||||
|
"JSON"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn syntax_detection_is_case_insensitive() {
|
fn syntax_detection_is_case_insensitive() {
|
||||||
let mut test = SyntaxDetectionTest::new();
|
let mut test = SyntaxDetectionTest::new();
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
NAME="${0##*/}"
|
||||||
|
for i in {1..3}; do
|
||||||
|
echo "hello $NAME $i"
|
||||||
|
done
|
||||||
@@ -4248,3 +4248,37 @@ fn tcl_shebang_detection_expect() {
|
|||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignored_suffix_enables_first_line_detection() {
|
||||||
|
// A shebang shell script saved with a `.txt` extension is Plain Text by
|
||||||
|
// default (the extension wins). With `--ignored-suffix .txt` the suffix is
|
||||||
|
// stripped before detection, so it falls back to the first line and is
|
||||||
|
// highlighted exactly as if its language were forced to bash. See #2745.
|
||||||
|
let fixture = "regression_tests/issue_2745.txt";
|
||||||
|
let common = ["--color=always", "--decorations=never", "--style=plain"];
|
||||||
|
|
||||||
|
let stdout = |args: &[&str]| -> Vec<u8> {
|
||||||
|
let assert = bat()
|
||||||
|
.args(common)
|
||||||
|
.args(args)
|
||||||
|
.arg(fixture)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
assert.get_output().stdout.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let forced_bash = stdout(&["--language", "bash"]);
|
||||||
|
let with_ignored_suffix = stdout(&["--ignored-suffix", ".txt"]);
|
||||||
|
let default = stdout(&[]);
|
||||||
|
|
||||||
|
// The fixture really is being highlighted (forcing bash is not a no-op).
|
||||||
|
assert!(
|
||||||
|
forced_bash.windows(2).any(|w| w == b"\x1b["),
|
||||||
|
"forced-bash output should contain ANSI color codes"
|
||||||
|
);
|
||||||
|
// With the ignored suffix, detection matches forced bash highlighting...
|
||||||
|
assert_eq!(with_ignored_suffix, forced_bash);
|
||||||
|
// ...while the default (extension wins) stays plain and differs.
|
||||||
|
assert_ne!(default, forced_bash);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user