mirror of
https://github.com/sharkdp/bat.git
synced 2025-03-15 15:18:45 +00:00
Merge branch 'sharkdp:master' into master
This commit is contained in:
commit
31db954393
@ -29,5 +29,5 @@ jobs:
|
|||||||
ADDED=$(git diff -U0 "origin/${PR_BASE}" HEAD -- CHANGELOG.md | grep -P '^\+[^\+].+$')
|
ADDED=$(git diff -U0 "origin/${PR_BASE}" HEAD -- CHANGELOG.md | grep -P '^\+[^\+].+$')
|
||||||
echo "Added lines in CHANGELOG.md:"
|
echo "Added lines in CHANGELOG.md:"
|
||||||
echo "$ADDED"
|
echo "$ADDED"
|
||||||
echo "Grepping for PR info:"
|
echo "Grepping for PR info (see CONTRIBUTING.md):"
|
||||||
grep "#${PR_NUMBER}\\b.*@${PR_SUBMITTER}\\b" <<< "$ADDED"
|
grep "#${PR_NUMBER}\\b.*@${PR_SUBMITTER}\\b" <<< "$ADDED"
|
||||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- Set terminal title to file names when Paging is not Paging::Never #2807 (@Oliver-Looney)
|
||||||
|
- `bat --squeeze-blank`/`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-blank`, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815)
|
||||||
|
- `PrettyPrinter::squeeze_empty_lines` to support line squeezing for bat as a library, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815)
|
||||||
|
|
||||||
## Bugfixes
|
## Bugfixes
|
||||||
|
|
||||||
- Fix long file name wrapping in header, see #2835 (@FilipRazek)
|
- Fix long file name wrapping in header, see #2835 (@FilipRazek)
|
||||||
@ -24,12 +29,19 @@
|
|||||||
- Pull in fix for unsafe-libyaml security advisory, see #2812 (@dtolnay)
|
- Pull in fix for unsafe-libyaml security advisory, see #2812 (@dtolnay)
|
||||||
- Update git-version dependency to use Syn v2, see #2816 (@dtolnay)
|
- Update git-version dependency to use Syn v2, see #2816 (@dtolnay)
|
||||||
- Update git2 dependency to v0.18.2, see #2852 (@eth-p)
|
- Update git2 dependency to v0.18.2, see #2852 (@eth-p)
|
||||||
|
- Improve performance when color output disabled, see #2397 and #2857 (@eth-p)
|
||||||
|
- Relax syntax mapping rule restrictions to allow brace expansion #2865 (@cyqsimon)
|
||||||
- Apply clippy fixes #2864 (@cyqsimon)
|
- Apply clippy fixes #2864 (@cyqsimon)
|
||||||
|
|
||||||
## Syntaxes
|
## Syntaxes
|
||||||
|
|
||||||
- `cmd-help`: scope subcommands followed by other terms, and other misc improvements, see #2819 (@victor-gp)
|
- `cmd-help`: scope subcommands followed by other terms, and other misc improvements, see #2819 (@victor-gp)
|
||||||
- Upgrade JQ syntax, see #2820 (@dependabot[bot])
|
- Upgrade JQ syntax, see #2820 (@dependabot[bot])
|
||||||
|
- Add syntax mapping for quadman quadlets #2866 (@cyqsimon)
|
||||||
|
- Map containers .conf files to TOML syntax #2867 (@cyqsimon)
|
||||||
|
- Associate `xsh` files with `xonsh` syntax that is Python, see #2840 (@anki-code).
|
||||||
|
- Added auto detect syntax for `.jsonc` #2795 (@mxaddict)
|
||||||
|
- Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict)
|
||||||
|
|
||||||
## Themes
|
## Themes
|
||||||
|
|
||||||
|
@ -6,21 +6,42 @@ Thank you for considering to contribute to `bat`!
|
|||||||
|
|
||||||
## Add an entry to the changelog
|
## Add an entry to the changelog
|
||||||
|
|
||||||
If your contribution changes the behavior of `bat` (as opposed to a typo-fix
|
Keeping the [`CHANGELOG.md`](CHANGELOG.md) file up-to-date makes the release
|
||||||
in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file
|
process much easier and therefore helps to get your changes into a new `bat`
|
||||||
and describe your changes. This makes the release process much easier and
|
release faster. However, not every change to the repository requires a
|
||||||
therefore helps to get your changes into a new `bat` release faster.
|
changelog entry. Below are a few examples of that.
|
||||||
|
|
||||||
|
Please update the changelog if your contribution contains changes regarding
|
||||||
|
any of the following:
|
||||||
|
- the behavior of `bat`
|
||||||
|
- syntax mappings
|
||||||
|
- syntax definitions
|
||||||
|
- themes
|
||||||
|
- the build system, linting, or CI workflows
|
||||||
|
|
||||||
|
A changelog entry is not necessary when:
|
||||||
|
- updating documentation
|
||||||
|
- fixing typos
|
||||||
|
|
||||||
|
>[!NOTE]
|
||||||
|
> For PRs, a CI workflow verifies that a suitable changelog entry is
|
||||||
|
> added. If such an entry is missing, the workflow will fail. If your
|
||||||
|
> changes do not need an entry to the changelog (see above), that
|
||||||
|
> workflow failure can be disregarded.
|
||||||
|
|
||||||
|
|
||||||
|
### Changelog entry format
|
||||||
|
|
||||||
The top of the `CHANGELOG` contains a *"unreleased"* section with a few
|
The top of the `CHANGELOG` contains a *"unreleased"* section with a few
|
||||||
subsections (Features, Bugfixes, …). Please add your entry to the subsection
|
subsections (Features, Bugfixes, …). Please add your entry to the subsection
|
||||||
that best describes your change.
|
that best describes your change.
|
||||||
|
|
||||||
Entries follow this format:
|
Entries must follow this format:
|
||||||
```
|
```
|
||||||
- Short description of what has been changed, see #123 (@user)
|
- Short description of what has been changed, see #123 (@user)
|
||||||
```
|
```
|
||||||
Here, `#123` is the number of the original issue and/or your pull request.
|
Please replace `#123` with the number of your pull request (not issue) and
|
||||||
Please replace `@user` by your GitHub username.
|
`@user` by your GitHub username.
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
@ -53,14 +53,16 @@ struct Matcher(Vec<MatcherSegment>);
|
|||||||
///
|
///
|
||||||
/// Note that this implementation is rather strict: it will greedily interpret
|
/// Note that this implementation is rather strict: it will greedily interpret
|
||||||
/// every valid environment variable replacement as such, then immediately
|
/// every valid environment variable replacement as such, then immediately
|
||||||
/// hard-error if it finds a '$', '{', or '}' anywhere in the remaining text
|
/// hard-error if it finds a '$' anywhere in the remaining text segments.
|
||||||
/// segments.
|
|
||||||
///
|
///
|
||||||
/// The reason for this strictness is I currently cannot think of a valid reason
|
/// The reason for this strictness is I currently cannot think of a valid reason
|
||||||
/// why you would ever need '$', '{', or '}' as plaintext in a glob pattern.
|
/// why you would ever need '$' as plaintext in a glob pattern. Therefore any
|
||||||
/// Therefore any such occurrences are likely human errors.
|
/// such occurrences are likely human errors.
|
||||||
///
|
///
|
||||||
/// If we later discover some edge cases, it's okay to make it more permissive.
|
/// If we later discover some edge cases, it's okay to make it more permissive.
|
||||||
|
///
|
||||||
|
/// Revision history:
|
||||||
|
/// - 2024-02-20: allow `{` and `}` (glob brace expansion)
|
||||||
impl FromStr for Matcher {
|
impl FromStr for Matcher {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
@ -106,7 +108,7 @@ impl FromStr for Matcher {
|
|||||||
if non_empty_segments
|
if non_empty_segments
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(Seg::text)
|
.filter_map(Seg::text)
|
||||||
.any(|t| t.contains(['$', '{', '}']))
|
.any(|t| t.contains('$'))
|
||||||
{
|
{
|
||||||
bail!(r#"Invalid matcher: "{s}""#);
|
bail!(r#"Invalid matcher: "{s}""#);
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,12 @@ Options:
|
|||||||
--list-themes
|
--list-themes
|
||||||
Display a list of supported themes for syntax highlighting.
|
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>
|
--style <components>
|
||||||
Configure which elements (line numbers, file headers, grid borders, Git modifications, ..)
|
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
|
to display in addition to the file contents. The argument is a comma-separated list of
|
||||||
@ -123,6 +129,9 @@ Options:
|
|||||||
set a default style, add the '--style=".."' option to the configuration file or export the
|
set a default style, add the '--style=".."' option to the configuration file or export the
|
||||||
BAT_STYLE environment variable (e.g.: export BAT_STYLE="..").
|
BAT_STYLE environment variable (e.g.: export BAT_STYLE="..").
|
||||||
|
|
||||||
|
By default, the following components are enabled:
|
||||||
|
changes, grid, header-filename, numbers, snip
|
||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
|
|
||||||
* default: enables recommended style components (default).
|
* default: enables recommended style components (default).
|
||||||
@ -160,6 +169,9 @@ Options:
|
|||||||
--acknowledgements
|
--acknowledgements
|
||||||
Show acknowledgements.
|
Show acknowledgements.
|
||||||
|
|
||||||
|
--set-terminal-title
|
||||||
|
Sets terminal title to filenames when using a pager.
|
||||||
|
|
||||||
-h, --help
|
-h, --help
|
||||||
Print help (see a summary with '-h')
|
Print help (see a summary with '-h')
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ Options:
|
|||||||
Set the color theme for syntax highlighting.
|
Set the color theme for syntax highlighting.
|
||||||
--list-themes
|
--list-themes
|
||||||
Display all supported highlighting themes.
|
Display all supported highlighting themes.
|
||||||
|
-s, --squeeze-blank
|
||||||
|
Squeeze consecutive empty lines.
|
||||||
--style <components>
|
--style <components>
|
||||||
Comma-separated list of style elements to display (*default*, auto, full, plain, changes,
|
Comma-separated list of style elements to display (*default*, auto, full, plain, changes,
|
||||||
header, header-filename, header-filesize, grid, rule, numbers, snip).
|
header, header-filename, header-filesize, grid, rule, numbers, snip).
|
||||||
|
@ -13,6 +13,6 @@ fn main() {
|
|||||||
|
|
||||||
println!("Themes:");
|
println!("Themes:");
|
||||||
for theme in printer.themes() {
|
for theme in printer.themes() {
|
||||||
println!("- {}", theme);
|
println!("- {theme}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,7 @@ fn asset_from_contents<T: serde::de::DeserializeOwned>(
|
|||||||
} else {
|
} else {
|
||||||
bincode::deserialize_from(contents)
|
bincode::deserialize_from(contents)
|
||||||
}
|
}
|
||||||
.map_err(|_| format!("Could not parse {}", description).into())
|
.map_err(|_| format!("Could not parse {description}").into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn asset_from_cache<T: serde::de::DeserializeOwned>(
|
fn asset_from_cache<T: serde::de::DeserializeOwned>(
|
||||||
@ -396,7 +396,7 @@ fn asset_from_cache<T: serde::de::DeserializeOwned>(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
asset_from_contents(&contents[..], description, compressed)
|
asset_from_contents(&contents[..], description, compressed)
|
||||||
.map_err(|_| format!("Could not parse cached {}", description).into())
|
.map_err(|_| format!("Could not parse cached {description}").into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -466,7 +466,7 @@ mod tests {
|
|||||||
let file_path = self.temp_dir.path().join(file_name);
|
let file_path = self.temp_dir.path().join(file_name);
|
||||||
{
|
{
|
||||||
let mut temp_file = File::create(&file_path).unwrap();
|
let mut temp_file = File::create(&file_path).unwrap();
|
||||||
writeln!(temp_file, "{}", first_line).unwrap();
|
writeln!(temp_file, "{first_line}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let input = Input::ordinary_file(&file_path);
|
let input = Input::ordinary_file(&file_path);
|
||||||
@ -514,8 +514,7 @@ mod tests {
|
|||||||
|
|
||||||
if !consistent {
|
if !consistent {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Inconsistent syntax detection:\nFor File: {}\nFor Reader: {}",
|
"Inconsistent syntax detection:\nFor File: {as_file}\nFor Reader: {as_reader}"
|
||||||
as_file, as_reader
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ fn print_unlinked_contexts(syntax_set: &SyntaxSet) {
|
|||||||
if !missing_contexts.is_empty() {
|
if !missing_contexts.is_empty() {
|
||||||
println!("Some referenced contexts could not be found!");
|
println!("Some referenced contexts could not be found!");
|
||||||
for context in missing_contexts {
|
for context in missing_contexts {
|
||||||
println!("- {}", context);
|
println!("- {context}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ pub(crate) fn asset_to_contents<T: serde::Serialize>(
|
|||||||
} else {
|
} else {
|
||||||
bincode::serialize_into(&mut contents, asset)
|
bincode::serialize_into(&mut contents, asset)
|
||||||
}
|
}
|
||||||
.map_err(|_| format!("Could not serialize {}", description))?;
|
.map_err(|_| format!("Could not serialize {description}"))?;
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ fn handle_license(path: &Path) -> Result<Option<String>> {
|
|||||||
} else if license_not_needed_in_acknowledgements(&license_text) {
|
} else if license_not_needed_in_acknowledgements(&license_text) {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("ERROR: License is of unknown type: {:?}", path).into())
|
Err(format!("ERROR: License is of unknown type: {path:?}").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ fn append_to_acknowledgements(
|
|||||||
relative_path: &str,
|
relative_path: &str,
|
||||||
license_text: &str,
|
license_text: &str,
|
||||||
) {
|
) {
|
||||||
write!(acknowledgements, "## {}\n\n{}", relative_path, license_text).ok();
|
write!(acknowledgements, "## {relative_path}\n\n{license_text}").ok();
|
||||||
|
|
||||||
// Make sure the last char is a newline to not mess up formatting later
|
// Make sure the last char is a newline to not mess up formatting later
|
||||||
if acknowledgements
|
if acknowledgements
|
||||||
|
@ -88,7 +88,7 @@ impl TryFrom<ThemeSet> for LazyThemeSet {
|
|||||||
let lazy_theme = LazyTheme {
|
let lazy_theme = LazyTheme {
|
||||||
serialized: crate::assets::build_assets::asset_to_contents(
|
serialized: crate::assets::build_assets::asset_to_contents(
|
||||||
&theme,
|
&theme,
|
||||||
&format!("theme {}", name),
|
&format!("theme {name}"),
|
||||||
COMPRESS_LAZY_THEMES,
|
COMPRESS_LAZY_THEMES,
|
||||||
)?,
|
)?,
|
||||||
deserialized: OnceCell::new(),
|
deserialized: OnceCell::new(),
|
||||||
|
@ -289,6 +289,17 @@ impl App {
|
|||||||
use_custom_assets: !self.matches.get_flag("no-custom-assets"),
|
use_custom_assets: !self.matches.get_flag("no-custom-assets"),
|
||||||
#[cfg(feature = "lessopen")]
|
#[cfg(feature = "lessopen")]
|
||||||
use_lessopen: self.matches.get_flag("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
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ pub fn assets_from_cache_or_binary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clear_asset(path: PathBuf, description: &str) {
|
fn clear_asset(path: PathBuf, description: &str) {
|
||||||
print!("Clearing {} ... ", description);
|
print!("Clearing {description} ... ");
|
||||||
match fs::remove_file(&path) {
|
match fs::remove_file(&path) {
|
||||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||||
println!("skipped (not present)");
|
println!("skipped (not present)");
|
||||||
|
@ -387,6 +387,21 @@ pub fn build_app(interactive_output: bool) -> Command {
|
|||||||
.help("Display all supported highlighting themes.")
|
.help("Display all supported highlighting themes.")
|
||||||
.long_help("Display a list of supported themes for syntax highlighting."),
|
.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(
|
||||||
Arg::new("style")
|
Arg::new("style")
|
||||||
.long("style")
|
.long("style")
|
||||||
@ -415,7 +430,7 @@ pub fn build_app(interactive_output: bool) -> Command {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(invalid) = invalid_vals.next() {
|
if let Some(invalid) = invalid_vals.next() {
|
||||||
Err(format!("Unknown style, '{}'", invalid))
|
Err(format!("Unknown style, '{invalid}'"))
|
||||||
} else {
|
} else {
|
||||||
Ok(val.to_owned())
|
Ok(val.to_owned())
|
||||||
}
|
}
|
||||||
@ -432,6 +447,8 @@ pub fn build_app(interactive_output: bool) -> Command {
|
|||||||
pre-defined style ('full'). To set a default style, add the \
|
pre-defined style ('full'). To set a default style, add the \
|
||||||
'--style=\"..\"' option to the configuration file or export the \
|
'--style=\"..\"' option to the configuration file or export the \
|
||||||
BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\").\n\n\
|
BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\").\n\n\
|
||||||
|
By default, the following components are enabled:\n \
|
||||||
|
changes, grid, header-filename, numbers, snip\n\n\
|
||||||
Possible values:\n\n \
|
Possible values:\n\n \
|
||||||
* default: enables recommended style components (default).\n \
|
* default: enables recommended style components (default).\n \
|
||||||
* full: enables all available components.\n \
|
* full: enables all available components.\n \
|
||||||
@ -567,6 +584,13 @@ pub fn build_app(interactive_output: bool) -> Command {
|
|||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
.hide_short_help(true)
|
.hide_short_help(true)
|
||||||
.help("Show acknowledgements."),
|
.help("Show acknowledgements."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("set-terminal-title")
|
||||||
|
.long("set-terminal-title")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.hide_short_help(true)
|
||||||
|
.help("Sets terminal title to filenames when using a pager."),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if the current directory contains a file name cache. Otherwise,
|
// Check if the current directory contains a file name cache. Otherwise,
|
||||||
|
@ -222,16 +222,37 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result<
|
|||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
for theme in assets.themes() {
|
for theme in assets.themes() {
|
||||||
writeln!(stdout, "{}", theme)?;
|
writeln!(stdout, "{theme}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_terminal_title_to(new_terminal_title: String) {
|
||||||
|
let osc_command_for_setting_terminal_title = "\x1b]0;";
|
||||||
|
let osc_end_command = "\x07";
|
||||||
|
print!("{osc_command_for_setting_terminal_title}{new_terminal_title}{osc_end_command}");
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_new_terminal_title(inputs: &Vec<Input>) -> String {
|
||||||
|
let mut new_terminal_title = "bat: ".to_string();
|
||||||
|
for (index, input) in inputs.iter().enumerate() {
|
||||||
|
new_terminal_title += input.description().title();
|
||||||
|
if index < inputs.len() - 1 {
|
||||||
|
new_terminal_title += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_terminal_title
|
||||||
|
}
|
||||||
|
|
||||||
fn run_controller(inputs: Vec<Input>, config: &Config, cache_dir: &Path) -> Result<bool> {
|
fn run_controller(inputs: Vec<Input>, config: &Config, cache_dir: &Path) -> Result<bool> {
|
||||||
let assets = assets_from_cache_or_binary(config.use_custom_assets, cache_dir)?;
|
let assets = assets_from_cache_or_binary(config.use_custom_assets, cache_dir)?;
|
||||||
let controller = Controller::new(config, &assets);
|
let controller = Controller::new(config, &assets);
|
||||||
|
if config.paging_mode != PagingMode::Never && config.set_terminal_title {
|
||||||
|
set_terminal_title_to(get_new_terminal_title(&inputs));
|
||||||
|
}
|
||||||
controller.run(inputs, None)
|
controller.run(inputs, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +94,12 @@ pub struct Config<'a> {
|
|||||||
// Whether or not to use $LESSOPEN if set
|
// Whether or not to use $LESSOPEN if set
|
||||||
#[cfg(feature = "lessopen")]
|
#[cfg(feature = "lessopen")]
|
||||||
pub use_lessopen: bool,
|
pub use_lessopen: bool,
|
||||||
|
|
||||||
|
// 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"))]
|
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||||
|
@ -56,7 +56,7 @@ impl Decoration for LineNumberDecoration {
|
|||||||
|
|
||||||
self.cached_wrap.clone()
|
self.cached_wrap.clone()
|
||||||
} else {
|
} else {
|
||||||
let plain: String = format!("{:4}", line_number);
|
let plain: String = format!("{line_number:4}");
|
||||||
DecorationText {
|
DecorationText {
|
||||||
width: plain.len(),
|
width: plain.len(),
|
||||||
text: self.color.paint(plain).to_string(),
|
text: self.color.paint(plain).to_string(),
|
||||||
|
@ -197,7 +197,7 @@ impl<'a> Input<'a> {
|
|||||||
InputKind::StdIn => {
|
InputKind::StdIn => {
|
||||||
if let Some(stdout) = stdout_identifier {
|
if let Some(stdout) = stdout_identifier {
|
||||||
let input_identifier = Identifier::try_from(clircle::Stdio::Stdin)
|
let input_identifier = Identifier::try_from(clircle::Stdio::Stdin)
|
||||||
.map_err(|e| format!("Stdin: Error identifying file: {}", e))?;
|
.map_err(|e| format!("Stdin: Error identifying file: {e}"))?;
|
||||||
if stdout.surely_conflicts_with(&input_identifier) {
|
if stdout.surely_conflicts_with(&input_identifier) {
|
||||||
return Err("IO circle detected. The input from stdin is also an output. Aborting to avoid infinite loop.".into());
|
return Err("IO circle detected. The input from stdin is also an output. Aborting to avoid infinite loop.".into());
|
||||||
}
|
}
|
||||||
|
@ -230,6 +230,12 @@ impl<'a> PrettyPrinter<'a> {
|
|||||||
self
|
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
|
/// Specify the highlighting theme
|
||||||
pub fn theme(&mut self, theme: impl AsRef<str>) -> &mut Self {
|
pub fn theme(&mut self, theme: impl AsRef<str>) -> &mut Self {
|
||||||
self.config.theme = theme.as_ref().to_owned();
|
self.config.theme = theme.as_ref().to_owned();
|
||||||
|
120
src/printer.rs
120
src/printer.rs
@ -9,6 +9,7 @@ use bytesize::ByteSize;
|
|||||||
|
|
||||||
use syntect::easy::HighlightLines;
|
use syntect::easy::HighlightLines;
|
||||||
use syntect::highlighting::Color;
|
use syntect::highlighting::Color;
|
||||||
|
use syntect::highlighting::FontStyle;
|
||||||
use syntect::highlighting::Theme;
|
use syntect::highlighting::Theme;
|
||||||
use syntect::parsing::SyntaxSet;
|
use syntect::parsing::SyntaxSet;
|
||||||
|
|
||||||
@ -48,6 +49,22 @@ const ANSI_UNDERLINE_DISABLE: EscapeSequence = EscapeSequence::CSI {
|
|||||||
final_byte: "m",
|
final_byte: "m",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EMPTY_SYNTECT_STYLE: syntect::highlighting::Style = syntect::highlighting::Style {
|
||||||
|
foreground: Color {
|
||||||
|
r: 127,
|
||||||
|
g: 127,
|
||||||
|
b: 127,
|
||||||
|
a: 255,
|
||||||
|
},
|
||||||
|
background: Color {
|
||||||
|
r: 127,
|
||||||
|
g: 127,
|
||||||
|
b: 127,
|
||||||
|
a: 255,
|
||||||
|
},
|
||||||
|
font_style: FontStyle::empty(),
|
||||||
|
};
|
||||||
|
|
||||||
pub enum OutputHandle<'a> {
|
pub enum OutputHandle<'a> {
|
||||||
IoWrite(&'a mut dyn io::Write),
|
IoWrite(&'a mut dyn io::Write),
|
||||||
FmtWrite(&'a mut dyn fmt::Write),
|
FmtWrite(&'a mut dyn fmt::Write),
|
||||||
@ -84,11 +101,15 @@ pub(crate) trait Printer {
|
|||||||
|
|
||||||
pub struct SimplePrinter<'a> {
|
pub struct SimplePrinter<'a> {
|
||||||
config: &'a Config<'a>,
|
config: &'a Config<'a>,
|
||||||
|
consecutive_empty_lines: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SimplePrinter<'a> {
|
impl<'a> SimplePrinter<'a> {
|
||||||
pub fn new(config: &'a Config) -> Self {
|
pub fn new(config: &'a Config) -> Self {
|
||||||
SimplePrinter { config }
|
SimplePrinter {
|
||||||
|
config,
|
||||||
|
consecutive_empty_lines: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +138,21 @@ impl<'a> Printer for SimplePrinter<'a> {
|
|||||||
_line_number: usize,
|
_line_number: usize,
|
||||||
line_buffer: &[u8],
|
line_buffer: &[u8],
|
||||||
) -> Result<()> {
|
) -> 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 !out_of_range {
|
||||||
if self.config.show_nonprintable {
|
if self.config.show_nonprintable {
|
||||||
let line = replace_nonprintable(
|
let line = replace_nonprintable(
|
||||||
@ -124,7 +160,7 @@ impl<'a> Printer for SimplePrinter<'a> {
|
|||||||
self.config.tab_width,
|
self.config.tab_width,
|
||||||
self.config.nonprintable_notation,
|
self.config.nonprintable_notation,
|
||||||
);
|
);
|
||||||
write!(handle, "{}", line)?;
|
write!(handle, "{line}")?;
|
||||||
} else {
|
} else {
|
||||||
match handle {
|
match handle {
|
||||||
OutputHandle::IoWrite(handle) => handle.write_all(line_buffer)?,
|
OutputHandle::IoWrite(handle) => handle.write_all(line_buffer)?,
|
||||||
@ -170,6 +206,7 @@ pub(crate) struct InteractivePrinter<'a> {
|
|||||||
pub line_changes: &'a Option<LineChanges>,
|
pub line_changes: &'a Option<LineChanges>,
|
||||||
highlighter_from_set: Option<HighlighterFromSet<'a>>,
|
highlighter_from_set: Option<HighlighterFromSet<'a>>,
|
||||||
background_color_highlight: Option<Color>,
|
background_color_highlight: Option<Color>,
|
||||||
|
consecutive_empty_lines: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InteractivePrinter<'a> {
|
impl<'a> InteractivePrinter<'a> {
|
||||||
@ -222,11 +259,13 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
panel_width = 0;
|
panel_width = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let highlighter_from_set = if input
|
// Get the highlighter for the output.
|
||||||
|
let is_printing_binary = input
|
||||||
.reader
|
.reader
|
||||||
.content_type
|
.content_type
|
||||||
.map_or(false, |c| c.is_binary() && !config.show_nonprintable)
|
.map_or(false, |c| c.is_binary() && !config.show_nonprintable);
|
||||||
{
|
|
||||||
|
let highlighter_from_set = if is_printing_binary || config.colored_output == false {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// Determine the type of syntax for highlighting
|
// Determine the type of syntax for highlighting
|
||||||
@ -253,6 +292,7 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
line_changes,
|
line_changes,
|
||||||
highlighter_from_set,
|
highlighter_from_set,
|
||||||
background_color_highlight,
|
background_color_highlight,
|
||||||
|
consecutive_empty_lines: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +333,7 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
" ".repeat(self.panel_width - 1 - text_truncated.len())
|
" ".repeat(self.panel_width - 1 - text_truncated.len())
|
||||||
);
|
);
|
||||||
if self.config.style_components.grid() {
|
if self.config.style_components.grid() {
|
||||||
format!("{} │ ", text_filled)
|
format!("{text_filled} │ ")
|
||||||
} else {
|
} else {
|
||||||
text_filled
|
text_filled
|
||||||
}
|
}
|
||||||
@ -328,7 +368,7 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
content: &str,
|
content: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.print_header_component_indent(handle)?;
|
self.print_header_component_indent(handle)?;
|
||||||
writeln!(handle, "{}", content)
|
writeln!(handle, "{content}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_header_multiline_component(
|
fn print_header_multiline_component(
|
||||||
@ -346,6 +386,31 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
self.print_header_component_with_indent(handle, content)
|
self.print_header_component_with_indent(handle, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn highlight_regions_for_line<'b>(
|
||||||
|
&mut self,
|
||||||
|
line: &'b str,
|
||||||
|
) -> Result<Vec<(syntect::highlighting::Style, &'b str)>> {
|
||||||
|
let highlighter_from_set = match self.highlighter_from_set {
|
||||||
|
Some(ref mut highlighter_from_set) => highlighter_from_set,
|
||||||
|
_ => return Ok(vec![(EMPTY_SYNTECT_STYLE, line)]),
|
||||||
|
};
|
||||||
|
|
||||||
|
// skip syntax highlighting on long lines
|
||||||
|
let too_long = line.len() > 1024 * 16;
|
||||||
|
|
||||||
|
let for_highlighting: &str = if too_long { "\n" } else { &line };
|
||||||
|
|
||||||
|
let mut highlighted_line = highlighter_from_set
|
||||||
|
.highlighter
|
||||||
|
.highlight_line(for_highlighting, highlighter_from_set.syntax_set)?;
|
||||||
|
|
||||||
|
if too_long {
|
||||||
|
highlighted_line[0].1 = &line;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(highlighted_line)
|
||||||
|
}
|
||||||
|
|
||||||
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
|
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
|
||||||
if self.config.tab_width > 0 {
|
if self.config.tab_width > 0 {
|
||||||
return expand_tabs(text, self.config.tab_width, cursor);
|
return expand_tabs(text, self.config.tab_width, cursor);
|
||||||
@ -429,7 +494,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
description
|
description
|
||||||
.kind()
|
.kind()
|
||||||
.map(|kind| format!("{}: ", kind))
|
.map(|kind| format!("{kind}: "))
|
||||||
.unwrap_or_else(|| "".into()),
|
.unwrap_or_else(|| "".into()),
|
||||||
self.colors.header_value.paint(description.title()),
|
self.colors.header_value.paint(description.title()),
|
||||||
mode
|
mode
|
||||||
@ -487,7 +552,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
"{}",
|
"{}",
|
||||||
self.colors
|
self.colors
|
||||||
.grid
|
.grid
|
||||||
.paint(format!("{}{}{}{}", panel, snip_left, title, snip_right))
|
.paint(format!("{panel}{snip_left}{title}{snip_right}"))
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -528,34 +593,23 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let regions = {
|
let regions = self.highlight_regions_for_line(&line)?;
|
||||||
let highlighter_from_set = match self.highlighter_from_set {
|
|
||||||
Some(ref mut highlighter_from_set) => highlighter_from_set,
|
|
||||||
_ => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// skip syntax highlighting on long lines
|
|
||||||
let too_long = line.len() > 1024 * 16;
|
|
||||||
|
|
||||||
let for_highlighting: &str = if too_long { "\n" } else { &line };
|
|
||||||
|
|
||||||
let mut highlighted_line = highlighter_from_set
|
|
||||||
.highlighter
|
|
||||||
.highlight_line(for_highlighting, highlighter_from_set.syntax_set)?;
|
|
||||||
|
|
||||||
if too_long {
|
|
||||||
highlighted_line[0].1 = &line;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlighted_line
|
|
||||||
};
|
|
||||||
|
|
||||||
if out_of_range {
|
if out_of_range {
|
||||||
return Ok(());
|
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: usize = 0;
|
||||||
let mut cursor_max: usize = self.config.term_width;
|
let mut cursor_max: usize = self.config.term_width;
|
||||||
let mut cursor_total: usize = 0;
|
let mut cursor_total: usize = 0;
|
||||||
|
@ -80,7 +80,7 @@ impl FromStr for StyleComponent {
|
|||||||
"full" => Ok(StyleComponent::Full),
|
"full" => Ok(StyleComponent::Full),
|
||||||
"default" => Ok(StyleComponent::Default),
|
"default" => Ok(StyleComponent::Default),
|
||||||
"plain" => Ok(StyleComponent::Plain),
|
"plain" => Ok(StyleComponent::Plain),
|
||||||
_ => Err(format!("Unknown style '{}'", s).into()),
|
_ => Err(format!("Unknown style '{s}'").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
[mappings]
|
||||||
|
"INI" = ["**/.aws/credentials", "**/.aws/config"]
|
@ -1,3 +1,3 @@
|
|||||||
# JSON Lines is a simple variation of JSON #2535
|
# JSON Lines is a simple variation of JSON #2535
|
||||||
[mappings]
|
[mappings]
|
||||||
"JSON" = ["*.jsonl"]
|
"JSON" = ["*.jsonl", "*.jsonc"]
|
3
src/syntax_mapping/builtins/common/xonsh.toml
Normal file
3
src/syntax_mapping/builtins/common/xonsh.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Xonsh shell (https://xon.sh/)
|
||||||
|
[mappings]
|
||||||
|
"Python" = ["*.xsh", "*.xonshrc"]
|
8
src/syntax_mapping/builtins/linux/50-containers.toml
Normal file
8
src/syntax_mapping/builtins/linux/50-containers.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# see https://github.com/containers/image/tree/main/docs
|
||||||
|
[mappings]
|
||||||
|
"TOML" = [
|
||||||
|
"/usr/share/containers/**/*.conf",
|
||||||
|
"/etc/containers/**/*.conf",
|
||||||
|
"${HOME}/.config/containers/**/*.conf",
|
||||||
|
"${XDG_CONFIG_HOME}/containers/**/*.conf",
|
||||||
|
]
|
7
src/syntax_mapping/builtins/linux/50-podman-quadlet.toml
Normal file
7
src/syntax_mapping/builtins/linux/50-podman-quadlet.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# see `man quadlet`
|
||||||
|
[mappings]
|
||||||
|
"INI" = [
|
||||||
|
"**/containers/systemd/*.{container,volume,network,kube,image}",
|
||||||
|
"**/containers/systemd/users/*.{container,volume,network,kube,image}",
|
||||||
|
"**/containers/systemd/users/*/*.{container,volume,network,kube,image}",
|
||||||
|
]
|
@ -73,7 +73,7 @@ fn internal_suffixes() {
|
|||||||
let file_names = ignored_suffixes
|
let file_names = ignored_suffixes
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|suffix| format!("test.json{}", suffix));
|
.map(|suffix| format!("test.json{suffix}"));
|
||||||
for file_name_str in file_names {
|
for file_name_str in file_names {
|
||||||
let file_name = OsStr::new(&file_name_str);
|
let file_name = OsStr::new(&file_name_str);
|
||||||
let expected_stripped_file_name = OsStr::new("test.json");
|
let expected_stripped_file_name = OsStr::new("test.json");
|
||||||
@ -95,7 +95,7 @@ fn external_suffixes() {
|
|||||||
let file_names = ignored_suffixes
|
let file_names = ignored_suffixes
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|suffix| format!("test.json{}", suffix));
|
.map(|suffix| format!("test.json{suffix}"));
|
||||||
for file_name_str in file_names {
|
for file_name_str in file_names {
|
||||||
let file_name = OsStr::new(&file_name_str);
|
let file_name = OsStr::new(&file_name_str);
|
||||||
let expected_stripped_file_name = OsStr::new("test.json");
|
let expected_stripped_file_name = OsStr::new("test.json");
|
||||||
|
@ -221,11 +221,11 @@ impl Attributes {
|
|||||||
8 => match parameters.next() {
|
8 => match parameters.next() {
|
||||||
Some(5) /* 256-color */ => format!("\x1B[{};5;{}m", color, join(";", 1, parameters)),
|
Some(5) /* 256-color */ => format!("\x1B[{};5;{}m", color, join(";", 1, parameters)),
|
||||||
Some(2) /* 24-bit color */ => format!("\x1B[{};2;{}m", color, join(";", 3, parameters)),
|
Some(2) /* 24-bit color */ => format!("\x1B[{};2;{}m", color, join(";", 3, parameters)),
|
||||||
Some(c) => format!("\x1B[{};{}m", color, c),
|
Some(c) => format!("\x1B[{color};{c}m"),
|
||||||
_ => "".to_owned(),
|
_ => "".to_owned(),
|
||||||
},
|
},
|
||||||
9 => "".to_owned(),
|
9 => "".to_owned(),
|
||||||
_ => format!("\x1B[{}m", color),
|
_ => format!("\x1B[{color}m"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +435,7 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some((_, tc)) => {
|
Some((_, tc)) => {
|
||||||
panic!("this should not be reached: char {:?}", tc)
|
panic!("this should not be reached: char {tc:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
tests/examples/empty_lines.txt
vendored
Normal file
30
tests/examples/empty_lines.txt
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
line 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
line 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
line 20
|
||||||
|
line 21
|
||||||
|
|
||||||
|
|
||||||
|
line 24
|
||||||
|
|
||||||
|
line 26
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
line 30
|
@ -208,6 +208,70 @@ fn line_range_multiple() {
|
|||||||
.stdout("line 1\nline 2\nline 4\n");
|
.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]
|
#[test]
|
||||||
#[cfg_attr(any(not(feature = "git"), target_os = "windows"), ignore)]
|
#[cfg_attr(any(not(feature = "git"), target_os = "windows"), ignore)]
|
||||||
fn short_help() {
|
fn short_help() {
|
||||||
@ -936,6 +1000,18 @@ fn env_var_bat_paging() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_set_terminal_title() {
|
||||||
|
bat()
|
||||||
|
.arg("--paging=always")
|
||||||
|
.arg("--set-terminal-title")
|
||||||
|
.arg("test.txt")
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout("\u{1b}]0;bat: test.txt\x07hello world\n")
|
||||||
|
.stderr("");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diagnostic_sanity_check() {
|
fn diagnostic_sanity_check() {
|
||||||
bat()
|
bat()
|
||||||
@ -1682,7 +1758,7 @@ fn do_not_panic_regression_tests() {
|
|||||||
] {
|
] {
|
||||||
bat()
|
bat()
|
||||||
.arg("--color=always")
|
.arg("--color=always")
|
||||||
.arg(&format!("regression_tests/{}", filename))
|
.arg(&format!("regression_tests/{filename}"))
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
}
|
}
|
||||||
@ -1695,7 +1771,7 @@ fn do_not_detect_different_syntax_for_stdin_and_files() {
|
|||||||
let cmd_for_file = bat()
|
let cmd_for_file = bat()
|
||||||
.arg("--color=always")
|
.arg("--color=always")
|
||||||
.arg("--map-syntax=*.js:Markdown")
|
.arg("--map-syntax=*.js:Markdown")
|
||||||
.arg(&format!("--file-name={}", file))
|
.arg(&format!("--file-name={file}"))
|
||||||
.arg("--style=plain")
|
.arg("--style=plain")
|
||||||
.arg(file)
|
.arg(file)
|
||||||
.assert()
|
.assert()
|
||||||
@ -1705,7 +1781,7 @@ fn do_not_detect_different_syntax_for_stdin_and_files() {
|
|||||||
.arg("--color=always")
|
.arg("--color=always")
|
||||||
.arg("--map-syntax=*.js:Markdown")
|
.arg("--map-syntax=*.js:Markdown")
|
||||||
.arg("--style=plain")
|
.arg("--style=plain")
|
||||||
.arg(&format!("--file-name={}", file))
|
.arg(&format!("--file-name={file}"))
|
||||||
.pipe_stdin(Path::new(EXAMPLES_DIR).join(file))
|
.pipe_stdin(Path::new(EXAMPLES_DIR).join(file))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.assert()
|
.assert()
|
||||||
@ -1724,7 +1800,7 @@ fn no_first_line_fallback_when_mapping_to_invalid_syntax() {
|
|||||||
bat()
|
bat()
|
||||||
.arg("--color=always")
|
.arg("--color=always")
|
||||||
.arg("--map-syntax=*.invalid-syntax:InvalidSyntax")
|
.arg("--map-syntax=*.invalid-syntax:InvalidSyntax")
|
||||||
.arg(&format!("--file-name={}", file))
|
.arg(&format!("--file-name={file}"))
|
||||||
.arg("--style=plain")
|
.arg("--style=plain")
|
||||||
.arg(file)
|
.arg(file)
|
||||||
.assert()
|
.assert()
|
||||||
@ -1922,7 +1998,7 @@ fn ansi_passthrough_emit() {
|
|||||||
.arg("--paging=never")
|
.arg("--paging=never")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--terminal-width=80")
|
.arg("--terminal-width=80")
|
||||||
.arg(format!("--wrap={}", wrapping))
|
.arg(format!("--wrap={wrapping}"))
|
||||||
.arg("--decorations=always")
|
.arg("--decorations=always")
|
||||||
.arg("--style=plain")
|
.arg("--style=plain")
|
||||||
.write_stdin("\x1B[33mColor\nColor \x1B[m\nPlain\n")
|
.write_stdin("\x1B[33mColor\nColor \x1B[m\nPlain\n")
|
||||||
|
@ -30,8 +30,7 @@ fn no_duplicate_extensions() {
|
|||||||
for extension in &syntax.file_extensions {
|
for extension in &syntax.file_extensions {
|
||||||
assert!(
|
assert!(
|
||||||
KNOWN_EXCEPTIONS.contains(&extension.as_str()) || extensions.insert(extension),
|
KNOWN_EXCEPTIONS.contains(&extension.as_str()) || extensions.insert(extension),
|
||||||
"File extension / pattern \"{}\" appears twice in the syntax set",
|
"File extension / pattern \"{extension}\" appears twice in the syntax set"
|
||||||
extension
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ impl BatTester {
|
|||||||
"--color=never",
|
"--color=never",
|
||||||
"--decorations=always",
|
"--decorations=always",
|
||||||
"--terminal-width=80",
|
"--terminal-width=80",
|
||||||
&format!("--style={}", style),
|
&format!("--style={style}"),
|
||||||
])
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("bat failed");
|
.expect("bat failed");
|
||||||
@ -40,7 +40,7 @@ impl BatTester {
|
|||||||
.replace("tests/snapshots/", "");
|
.replace("tests/snapshots/", "");
|
||||||
|
|
||||||
let mut expected = String::new();
|
let mut expected = String::new();
|
||||||
let mut file = File::open(format!("tests/snapshots/output/{}.snapshot.txt", name))
|
let mut file = File::open(format!("tests/snapshots/output/{name}.snapshot.txt"))
|
||||||
.expect("snapshot file missing");
|
.expect("snapshot file missing");
|
||||||
file.read_to_string(&mut expected)
|
file.read_to_string(&mut expected)
|
||||||
.expect("could not read snapshot file");
|
.expect("could not read snapshot file");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user