Merge branch 'master' into master

This commit is contained in:
John Cavanaugh
2025-08-19 20:34:08 -07:00
committed by GitHub
27 changed files with 222 additions and 94 deletions

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- crate_metadata
- ensure_cargo_fmt
- lint
- min_version
- license_checks
- test_with_new_syntaxes_and_themes
@@ -35,7 +35,7 @@ jobs:
name: Extract crate metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Extract crate information
id: crate_metadata
run: |
@@ -51,15 +51,16 @@ jobs:
homepage: ${{ steps.crate_metadata.outputs.homepage }}
msrv: ${{ steps.crate_metadata.outputs.msrv }}
ensure_cargo_fmt:
name: Ensure 'cargo fmt' has been run
lint:
name: Ensure code quality
runs-on: ubuntu-latest
steps:
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- uses: actions/checkout@v4
components: rustfmt,clippy
- uses: actions/checkout@v5
- run: cargo fmt -- --check
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
min_version:
name: Minimum supported rust version
@@ -67,15 +68,11 @@ jobs:
needs: crate_metadata
steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
components: clippy
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }}
- name: Run tests
run: cargo test --locked ${{ env.MSRV_FEATURES }}
@@ -83,7 +80,7 @@ jobs:
name: License checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true # we especially want to perform license checks on submodules
- run: tests/scripts/license-checks.sh
@@ -93,7 +90,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true # we need all syntax and theme submodules
- name: Install Rust toolchain
@@ -122,7 +119,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Prepare environment variables
run: |
echo "BAT_SYSTEM_CONFIG_PREFIX=$GITHUB_WORKSPACE/tests/examples/system_config" >> $GITHUB_ENV
@@ -138,7 +135,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Check documentation
@@ -153,7 +150,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- run: cargo install cargo-audit --locked
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- run: cargo audit
build:
@@ -181,7 +178,7 @@ jobs:
BUILD_CMD: cargo
steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install prerequisites
shell: bash

View File

@@ -18,6 +18,7 @@
- Add missing mappings for various bash/zsh files, see PR #3262 (@AdamGaskins)
- Send all bat errors to stderr by default, see #3336 (@JerryImMouse)
- Make --map-syntax target case insensitive to match --language, see #3206 (@keith-hall)
- Correctly determine the end of the line in UTF16LE/BE input #3369 (@keith-hall)
## Other

8
Cargo.lock generated
View File

@@ -906,9 +906,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libgit2-sys"
@@ -996,9 +996,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.29.0"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.6.0",
"cfg-if",

View File

@@ -101,7 +101,7 @@ tempfile = "3.16.0"
serde = { version = "1.0", features = ["derive"] }
[target.'cfg(unix)'.dev-dependencies]
nix = { version = "0.29", default-features = false, features = ["term"] }
nix = { version = "0.30", default-features = false, features = ["term"] }
[build-dependencies]
anyhow = "1.0.97"

View File

@@ -711,7 +711,7 @@ sidebar. Calling `bat` with `--tabs=0` will override it and let tabs be consumed
### Dark mode
If you make use of the dark mode feature in macOS, you might want to configure `bat` to use a different
If you make use of the dark mode feature in **macOS**, you might want to configure `bat` to use a different
theme based on the OS theme. The following snippet uses the `default` theme when in the _dark mode_
and the `GitHub` theme when in the _light mode_.
@@ -719,6 +719,34 @@ and the `GitHub` theme when in the _light mode_.
alias cat="bat --theme auto:system --theme-dark default --theme-light GitHub"
```
The same dark mode feature is now available in **GNOME** and affects the `org.gnome.desktop.interface color-scheme` setting. The following code converts the above to use said setting.
```bash
# .bashrc
sys_color_scheme_is_dark() {
condition=$(gsettings get org.gnome.desktop.interface color-scheme)
condition=$(echo "$condition" | tr -d "[:space:]'")
if [ $condition == "prefer-dark" ]; then
return 0
else
return 1
fi
}
bat_alias_wrapper() {
#get color scheme
sys_color_scheme_is_dark
if [[ $? -eq 0 ]]; then
# bat command with dark color scheme
bat --theme=default "$@"
else
# bat command with light color scheme
bat --theme=GitHub "$@"
fi
}
alias cat='bat_alias_wrapper'
```
## Configuration file

View File

@@ -1,5 +1,5 @@
// Output the square of a number.
fn print_square(num: f64) {
let result = f64::powf(num, 2.0);
println!("The square of {:.2} is {:.2}.", num, result);
println!("The square of {num:.2} is {result:.2}.");
}

View File

@@ -152,7 +152,7 @@ impl HighlightingAssets {
&self,
path: impl AsRef<Path>,
mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet> {
) -> Result<SyntaxReferenceInSet<'_>> {
let path = path.as_ref();
let syntax_match = mapping.get_syntax_for(path);
@@ -191,11 +191,11 @@ impl HighlightingAssets {
Some(theme) => theme,
None => {
if theme == "ansi-light" || theme == "ansi-dark" {
bat_warning!("Theme '{}' is deprecated, using 'ansi' instead.", theme);
bat_warning!("Theme '{theme}' is deprecated, using 'ansi' instead.");
return self.get_theme("ansi");
}
if !theme.is_empty() {
bat_warning!("Unknown theme '{}', using default.", theme)
bat_warning!("Unknown theme '{theme}', using default.")
}
self.get_theme_set()
.get(
@@ -212,7 +212,7 @@ impl HighlightingAssets {
language: Option<&str>,
input: &mut OpenedInput,
mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet> {
) -> Result<SyntaxReferenceInSet<'_>> {
if let Some(language) = language {
let syntax_set = self.get_syntax_set()?;
return syntax_set
@@ -244,14 +244,17 @@ impl HighlightingAssets {
pub(crate) fn find_syntax_by_name(
&self,
syntax_name: &str,
) -> Result<Option<SyntaxReferenceInSet>> {
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_extension(&self, e: Option<&OsStr>) -> Result<Option<SyntaxReferenceInSet>> {
fn find_syntax_by_extension(
&self,
e: Option<&OsStr>,
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let syntax_set = self.get_syntax_set()?;
let extension = e.and_then(|x| x.to_str()).unwrap_or_default();
Ok(syntax_set
@@ -259,7 +262,7 @@ impl HighlightingAssets {
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_token(&self, token: &str) -> Result<Option<SyntaxReferenceInSet>> {
fn find_syntax_by_token(&self, token: &str) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_token(token)
@@ -270,7 +273,7 @@ impl HighlightingAssets {
&self,
file_name: &OsStr,
ignored_suffixes: &IgnoredSuffixes,
) -> Result<Option<SyntaxReferenceInSet>> {
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let mut syntax = self.find_syntax_by_extension(Some(file_name))?;
if syntax.is_none() {
syntax =
@@ -286,7 +289,7 @@ impl HighlightingAssets {
&self,
file_name: &OsStr,
ignored_suffixes: &IgnoredSuffixes,
) -> Result<Option<SyntaxReferenceInSet>> {
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let mut syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
if syntax.is_none() {
syntax =
@@ -301,7 +304,7 @@ impl HighlightingAssets {
fn get_first_line_syntax(
&self,
reader: &mut InputReader,
) -> Result<Option<SyntaxReferenceInSet>> {
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let syntax_set = self.get_syntax_set()?;
Ok(String::from_utf8(reader.first_line.clone())
.ok()
@@ -354,8 +357,7 @@ fn asset_from_cache<T: serde::de::DeserializeOwned>(
) -> Result<T> {
let contents = fs::read(path).map_err(|_| {
format!(
"Could not load cached {} '{}'",
description,
"Could not load cached {description} '{}'",
path.to_string_lossy()
)
})?;

View File

@@ -40,15 +40,15 @@ impl AssetsMetadata {
/// Load metadata about the stored cache file from the given folder.
///
/// There are several possibilities:
/// - We find a metadata.yaml file and are able to parse it
/// => return the contained information
/// - We find a metadata.yaml file and but are not able to parse it
/// => return a SerdeYamlError
/// - We do not find a metadata.yaml file but a syntaxes.bin or themes.bin file
/// => assume that these were created by an old version of bat and return
/// AssetsMetadata::default() without version information
/// - We do not find a metadata.yaml file and no cached assets
/// => no user provided assets are available, return None
/// - We find a `metadata.yaml` file and are able to parse it
/// - return the contained information
/// - We find a `metadata.yaml` file, but are not able to parse it
/// - return a [`Error::SerdeYamlError`]
/// - We do not find a `metadata.yaml` file but a `syntaxes.bin` or `themes.bin` file
/// - assume that these were created by an old version of bat and return
/// [`AssetsMetadata::default()`] without version information
/// - We do not find a `metadata.yaml` file and no cached assets
/// - no user provided assets are available, return `None`
pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
match Self::try_load_from_folder(path) {
Ok(metadata) => Ok(Some(metadata)),

View File

@@ -47,9 +47,8 @@ fn build_theme_set(source_dir: &Path, include_integrated_assets: bool) -> Result
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
println!(
"Failed to load one or more themes from '{}' (reason: '{}')",
"Failed to load one or more themes from '{}' (reason: '{err}')",
theme_dir.to_string_lossy(),
err,
);
}
} else {
@@ -162,15 +161,10 @@ fn asset_to_cache<T: serde::Serialize>(
description: &str,
compressed: bool,
) -> Result<()> {
print!("Writing {} to {} ... ", description, path.to_string_lossy());
print!("Writing {description} to {} ... ", path.to_string_lossy());
let contents = asset_to_contents(asset, description, compressed)?;
std::fs::write(path, &contents[..]).map_err(|_| {
format!(
"Could not save {} to {}",
description,
path.to_string_lossy()
)
})?;
std::fs::write(path, &contents[..])
.map_err(|_| format!("Could not save {description} to {}", path.to_string_lossy()))?;
println!("okay");
Ok(())
}

View File

@@ -96,7 +96,7 @@ impl App {
Ok(clap_app::build_app(interactive_output).get_matches_from(args))
}
pub fn config(&self, inputs: &[Input]) -> Result<Config> {
pub fn config(&self, inputs: &[Input]) -> Result<Config<'_>> {
let style_components = self.style_components()?;
let extra_plain = self.matches.get_count("plain") > 1;
@@ -338,7 +338,7 @@ impl App {
})
}
pub fn inputs(&self) -> Result<Vec<Input>> {
pub fn inputs(&self) -> Result<Vec<Input<'_>>> {
let filenames: Option<Vec<&Path>> = self
.matches
.get_many::<PathBuf>("file-name")

View File

@@ -50,7 +50,7 @@ fn clear_asset(path: PathBuf, description: &str) {
println!("skipped (not present)");
}
Err(err) => {
println!("could not remove the cache file {:?}: {}", &path, err);
println!("could not remove the cache file {path:?}: {err}");
}
Ok(_) => println!("okay"),
}

View File

@@ -16,7 +16,7 @@ static VERSION: Lazy<String> = Lazy::new(|| {
if git_version.is_empty() {
crate_version!().to_string()
} else {
format!("{} ({})", crate_version!(), git_version)
format!("{} ({git_version})", crate_version!())
}
});

View File

@@ -88,9 +88,8 @@ pub fn generate_config_file() -> bat::error::Result<()> {
fs::write(&config_file, default_config).map_err(|e| {
format!(
"Failed to create config file at '{}': {}",
"Failed to create config file at '{}': {e}",
config_file.to_string_lossy(),
e
)
})?;

View File

@@ -5,7 +5,7 @@ pub fn new_file_input<'a>(file: &'a Path, name: Option<&'a Path>) -> Input<'a> {
named(Input::ordinary_file(file), name.or(Some(file)))
}
pub fn new_stdin_input(name: Option<&Path>) -> Input {
pub fn new_stdin_input(name: Option<&Path>) -> Input<'_> {
named(Input::stdin(), name)
}

View File

@@ -162,7 +162,7 @@ pub fn get_languages(config: &Config, cache_dir: &Path) -> Result<String> {
};
for lang in languages {
write!(result, "{:width$}{}", lang.name, separator, width = longest).ok();
write!(result, "{:width$}{separator}", lang.name, width = longest).ok();
// Number of characters on this line so far, wrap before `desired_width`
let mut num_chars = 0;
@@ -173,7 +173,7 @@ pub fn get_languages(config: &Config, cache_dir: &Path) -> Result<String> {
let new_chars = word.len() + comma_separator.len();
if num_chars + new_chars >= desired_width {
num_chars = 0;
write!(result, "\n{:width$}{}", "", separator, width = longest).ok();
write!(result, "\n{:width$}{separator}", "", width = longest).ok();
}
num_chars += new_chars;
@@ -224,9 +224,8 @@ pub fn list_themes(
if config.colored_output {
writeln!(
writer,
"Theme: {}{}\n",
"Theme: {}{default_theme_info}\n",
Style::new().bold().paint(theme.to_string()),
default_theme_info
)?;
config.theme = theme.to_string();
Controller::new(&config, &assets)
@@ -363,7 +362,7 @@ fn run() -> Result<bool> {
"fish" => println!("{}", completions::FISH_COMPLETION),
"ps1" => println!("{}", completions::PS1_COMPLETION),
"zsh" => println!("{}", completions::ZSH_COMPLETION),
_ => unreachable!("No completion for shell '{}' available.", shell),
_ => unreachable!("No completion for shell '{shell}' available."),
}
return Ok(true);
}

View File

@@ -60,18 +60,16 @@ pub fn default_error_handler(error: &Error, output: &mut dyn Write) {
Error::SerdeYamlError(_) => {
writeln!(
output,
"{}: Error while parsing metadata.yaml file: {}",
"{}: Error while parsing metadata.yaml file: {error}",
Red.paint("[bat error]"),
error
)
.ok();
}
_ => {
writeln!(
&mut std::io::stderr().lock(),
"{}: {}",
"{}: {error}",
Red.paint("[bat error]"),
error
)
.ok();
}

View File

@@ -217,14 +217,14 @@ impl<'a> Input<'a> {
metadata: self.metadata,
reader: {
let mut file = File::open(&path)
.map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?;
.map_err(|e| format!("'{}': {e}", path.to_string_lossy()))?;
if file.metadata()?.is_dir() {
return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
}
if let Some(stdout) = stdout_identifier {
let input_identifier = Identifier::try_from(file).map_err(|e| {
format!("{}: Error identifying file: {}", path.to_string_lossy(), e)
format!("{}: Error identifying file: {e}", path.to_string_lossy())
})?;
if stdout.surely_conflicts_with(&input_identifier) {
return Err(format!(
@@ -267,7 +267,9 @@ impl<'a> InputReader<'a> {
};
if content_type == Some(ContentType::UTF_16LE) {
reader.read_until(0x00, &mut first_line).ok();
read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A).ok();
} else if content_type == Some(ContentType::UTF_16BE) {
read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00).ok();
}
InputReader {
@@ -283,16 +285,44 @@ impl<'a> InputReader<'a> {
return Ok(true);
}
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
if self.content_type == Some(ContentType::UTF_16LE) {
let _ = self.inner.read_until(0x00, buf);
return read_utf16_line(&mut self.inner, buf, 0x00, 0x0A);
}
if self.content_type == Some(ContentType::UTF_16BE) {
return read_utf16_line(&mut self.inner, buf, 0x0A, 0x00);
}
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
Ok(res)
}
}
fn read_utf16_line<R: BufRead>(
reader: &mut R,
buf: &mut Vec<u8>,
read_until_char: u8,
preceded_by_char: u8,
) -> io::Result<bool> {
loop {
let mut temp = Vec::new();
let n = reader.read_until(read_until_char, &mut temp)?;
if n == 0 {
// EOF reached
break;
}
buf.extend_from_slice(&temp);
if buf.len() >= 2
&& buf[buf.len() - 2] == preceded_by_char
&& buf[buf.len() - 1] == read_until_char
{
// end of line found
break;
}
// end of line not found, keep going
}
Ok(!buf.is_empty())
}
#[test]
fn basic() {
let content = b"#!/bin/bash\necho hello";
@@ -350,3 +380,53 @@ fn utf16le() {
assert!(!res.unwrap());
assert!(buffer.is_empty());
}
#[test]
fn utf16le_issue3367() {
let content = b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00\
\x6F\x00\x20\x00\x62\x00\x61\x00\x72\x00\x0A\x00\
\x68\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x20\x00\x77\x00\x6F\x00\x72\x00\x6C\x00\x64\x00";
let mut reader = InputReader::new(&content[..]);
assert_eq!(
b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00",
&reader.first_line[..]
);
let mut buffer = vec![];
let res = reader.read_line(&mut buffer);
assert!(res.is_ok());
assert!(res.unwrap());
assert_eq!(
b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00",
&buffer[..]
);
buffer.clear();
let res = reader.read_line(&mut buffer);
assert!(res.is_ok());
assert!(res.unwrap());
assert_eq!(
b"\x6F\x00\x20\x00\x62\x00\x61\x00\x72\x00\x0A\x00",
&buffer[..]
);
buffer.clear();
let res = reader.read_line(&mut buffer);
assert!(res.is_ok());
assert!(res.unwrap());
assert_eq!(
b"\x68\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x20\x00\x77\x00\x6F\x00\x72\x00\x6C\x00\x64\x00",
&buffer[..]
);
buffer.clear();
let res = reader.read_line(&mut buffer);
assert!(res.is_ok());
assert!(!res.unwrap());
assert!(buffer.is_empty());
}

View File

@@ -39,7 +39,7 @@ impl LessOpenPreprocessor {
// Note that $LESSCLOSE has no such requirement
if lessopen.match_indices("%s").count() != 1 {
let error_msg = "LESSOPEN ignored: must contain exactly one %s";
bat_warning!("{}", error_msg);
bat_warning!("{error_msg}");
return Err(error_msg.into());
}
@@ -110,7 +110,7 @@ impl LessOpenPreprocessor {
if self.preprocess_stdin {
if let Some(stdout) = stdout_identifier {
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) {
return Err("IO circle detected. The input from stdin is also an output. Aborting to avoid infinite loop.".into());
}

View File

@@ -329,7 +329,7 @@ impl<'a> InteractivePrinter<'a> {
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);
let hline = format!("{}{grid_char}{hline}", "".repeat(self.panel_width));
writeln!(handle, "{}", self.colors.grid.paint(hline))?;
}
@@ -343,8 +343,7 @@ impl<'a> InteractivePrinter<'a> {
let text_truncated: String = text.chars().take(self.panel_width - 1).collect();
let text_filled: String = format!(
"{}{}",
text_truncated,
"{text_truncated}{}",
" ".repeat(self.panel_width - 1 - text_truncated.len())
);
if self.config.style_components.grid() {
@@ -400,7 +399,7 @@ impl<'a> InteractivePrinter<'a> {
while content_graphemes.len() > content_width {
let (content_line, remaining) = content_graphemes.split_at(content_width);
self.print_header_component_with_indent(handle, content_line.join("").as_str())?;
content_graphemes = remaining.iter().cloned().collect();
content_graphemes = remaining.to_vec();
}
self.print_header_component_with_indent(handle, content_graphemes.join("").as_str())
}
@@ -513,13 +512,12 @@ impl Printer for InteractivePrinter<'_> {
.try_for_each(|component| match component {
StyleComponent::HeaderFilename => {
let header_filename = format!(
"{}{}{}",
"{}{}{mode}",
description
.kind()
.map(|kind| format!("{kind}: "))
.unwrap_or_else(|| "".into()),
self.colors.header_value.paint(description.title()),
mode
);
self.print_header_multiline_component(handle, &header_filename)
}
@@ -701,7 +699,7 @@ impl Printer for InteractivePrinter<'_> {
"{}{}",
as_terminal_escaped(
style,
&format!("{}{}", self.ansi_style, text_trimmed),
&format!("{}{text_trimmed}", self.ansi_style),
true_color,
colored_output,
italics,
@@ -791,7 +789,7 @@ impl Printer for InteractivePrinter<'_> {
"{}{}\n{}",
as_terminal_escaped(
style,
&format!("{}{}", self.ansi_style, line_buf),
&format!("{}{line_buf}", self.ansi_style),
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,
@@ -818,7 +816,7 @@ impl Printer for InteractivePrinter<'_> {
"{}",
as_terminal_escaped(
style,
&format!("{}{}", self.ansi_style, line_buf),
&format!("{}{line_buf}", self.ansi_style),
self.config.true_color,
self.config.colored_output,
self.config.use_italic_text,

View File

@@ -212,15 +212,15 @@ impl Attributes {
}
fn update_with_charset(&mut self, kind: char, set: impl Iterator<Item = char>) -> bool {
self.charset = format!("\x1B{}{}", kind, set.take(1).collect::<String>());
self.charset = format!("\x1B{kind}{}", set.take(1).collect::<String>());
true
}
fn parse_color(color: u16, parameters: &mut dyn Iterator<Item = u16>) -> String {
match color % 10 {
8 => match parameters.next() {
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(5) /* 256-color */ => format!("\x1B[{color};5;{}m", join(";", 1, parameters)),
Some(2) /* 24-bit color */ => format!("\x1B[{color};2;{}m", join(";", 3, parameters)),
Some(c) => format!("\x1B[{color};{c}m"),
_ => "".to_owned(),
},

Binary file not shown.

BIN
tests/examples/test_UTF-16BE.txt vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1452,6 +1452,38 @@ fn utf16() {
.assert()
.success()
.stdout("hello world\n");
bat()
.arg("--plain")
.arg("--decorations=always")
.arg("test_UTF-16BE.txt")
.assert()
.success()
.stdout("hello world\nthis is a test\n");
}
#[test]
fn utf16le() {
bat()
.arg("--decorations=always")
.arg("--style=numbers")
.arg("--color=never")
.arg("test_UTF-16LE-complicated.txt")
.assert()
.success()
.stdout(" 1 上一伊刀\n 2 foo bar\n 3 hello world\n");
}
#[test]
fn utf16be() {
bat()
.arg("--decorations=always")
.arg("--style=numbers")
.arg("--color=never")
.arg("test_UTF-16BE-complicated.txt")
.assert()
.success()
.stdout(" 1 上一伊刀\n 2 foo bar\n 3 hello world\n");
}
// Regression test for https://github.com/sharkdp/bat/issues/1922

View File

@@ -75,7 +75,7 @@ fn create_sample_directory() -> Result<TempDir, git2::Error> {
// Copy over `sample.rs`
let sample_path = temp_dir.path().join("sample.rs");
println!("{:?}", &sample_path);
println!("{sample_path:?}");
fs::copy("tests/snapshots/sample.rs", &sample_path).expect("successful copy");
// Commit

View File

@@ -19,7 +19,7 @@ fn get_mocked_pagers_dir() -> PathBuf {
pub fn from(base: &str) -> String {
let mut cmd_and_args = shell_words::split(base).unwrap();
let suffix = if cfg!(windows) { ".bat" } else { "" };
let mut out_cmd = format!("{}{}", cmd_and_args.first().unwrap(), suffix);
let mut out_cmd = format!("{}{suffix}", cmd_and_args.first().unwrap());
if (cmd_and_args.len() > 1) {
out_cmd.push(' ');