From ba97230b9854da12cb6b861d9a7823c667e51d21 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sun, 1 Feb 2026 13:34:33 +0100 Subject: [PATCH 001/130] feat: Map BUILD to Python (Starlark) for Bazel (fixes #3575) See #3575. --- src/syntax_mapping/builtins/common/50-bazel.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/syntax_mapping/builtins/common/50-bazel.toml diff --git a/src/syntax_mapping/builtins/common/50-bazel.toml b/src/syntax_mapping/builtins/common/50-bazel.toml new file mode 100644 index 00000000..5f44ae2d --- /dev/null +++ b/src/syntax_mapping/builtins/common/50-bazel.toml @@ -0,0 +1,2 @@ +[mappings] +"Python" = ["BUILD"] From 5a4a7de9338be2b888674259ee29db3ff6b9b6fe Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sun, 1 Feb 2026 13:40:51 +0100 Subject: [PATCH 002/130] docs: Document mapping BUILD to Python (Starlark) for Bazel in CHANGELOG.md (see #3576) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9934329f..bfc516c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,10 @@ - Add `--quiet-empty` (`-E`) flag to suppress output when input is empty. Closes #1936, see #3563 (@NORMAL-EX) - Improve native man pages and command help syntax highlighting by stripping overstriking, see #3517 (@akirk) +- Map BUILD to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes + - `--help` now correctly honors `--pager=builtin`. See #3516 (@keith-hall) - `--help` now correctly honors custom themes. See #3524 (@keith-hall) - Fixed test compatibility with future Cargo build directory changes, see #3550 (@nmacl) From 652d50e92edb239ecb594bf4a3e9d5526983b019 Mon Sep 17 00:00:00 2001 From: zachvalenta Date: Sun, 8 Mar 2026 16:09:42 -0400 Subject: [PATCH 003/130] Makefile syntax for justfiles --- CHANGELOG.md | 1 + src/syntax_mapping/builtins/common/50-just.toml | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/syntax_mapping/builtins/common/50-just.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d30114d..5d1ee483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Features +- Map `justfile`, `Justfile`, `.justfile`, and `*.justfile` to Makefile syntax highlighting, see #XXXX (@zachvalenta) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) diff --git a/src/syntax_mapping/builtins/common/50-just.toml b/src/syntax_mapping/builtins/common/50-just.toml new file mode 100644 index 00000000..5ca19c85 --- /dev/null +++ b/src/syntax_mapping/builtins/common/50-just.toml @@ -0,0 +1,2 @@ +[mappings] +"Makefile" = ["justfile", "Justfile", ".justfile", "*.justfile"] From 56fe0fa2260675c2603607ac35c4c367e5d8d797 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Sat, 14 Mar 2026 09:18:56 +0200 Subject: [PATCH 004/130] Add case-sensitive glob support to syntax mapping to allow us to map `BUILD` case sensitively to Python for Skylark --- build/syntax_mapping.rs | 40 +++++++++++++-- src/syntax_mapping.rs | 50 +++++++++++++++++-- src/syntax_mapping/builtin.rs | 9 ++-- src/syntax_mapping/builtins/README.md | 6 +-- .../builtins/common/50-bazel.toml | 2 +- 5 files changed, 91 insertions(+), 16 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index 64be4bb9..f17b1cf5 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -51,7 +51,13 @@ impl ToTokens for MappingTarget { /// A single matcher. /// /// Codegen converts this into a `Lazy>`. -struct Matcher(Vec); +struct Matcher { + segments: Vec, + /// Whether the glob pattern should be matched case-insensitively. + /// + /// Defaults to `true` (case-insensitive) for backwards compatibility. + case_insensitive: bool, +} /// Parse a matcher. /// /// Note that this implementation is rather strict: it will greedily interpret @@ -116,18 +122,24 @@ impl FromStr for Matcher { bail!(r#"Invalid matcher: "{s}""#); } - Ok(Self(non_empty_segments)) + Ok(Self { + segments: non_empty_segments, + case_insensitive: true, + }) } } impl ToTokens for Matcher { fn to_tokens(&self, tokens: &mut TokenStream) { - let t = match self.0.as_slice() { + let case_insensitive = self.case_insensitive; + let t = match self.segments.as_slice() { [] => unreachable!("0-length matcher should never be created"), [MatcherSegment::Text(text)] => { - quote! { Lazy::new(|| Some(build_matcher_fixed(#text))) } + quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case_insensitive))) } } // parser logic ensures that this case can only happen when there are dynamic segments - segs @ [_, ..] => quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ])) }, + segs @ [_, ..] => { + quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case_insensitive)) } + } }; tokens.append_all(t); } @@ -175,7 +187,12 @@ impl MatcherSegment { /// A struct that models a single .toml file in /src/syntax_mapping/builtins/. #[derive(Clone, Debug, Deserialize)] struct MappingDefModel { + #[serde(default)] mappings: IndexMap>, + /// Case-sensitive mappings. Unlike `mappings`, these glob patterns are + /// matched case-sensitively. + #[serde(default)] + case_sensitive_mappings: IndexMap>, } impl MappingDefModel { fn into_mapping_list(self) -> MappingList { @@ -188,6 +205,19 @@ impl MappingDefModel { .map(|matcher| (matcher, target.clone())) .collect::>() }) + .chain( + self.case_sensitive_mappings + .into_iter() + .flat_map(|(target, matchers)| { + matchers + .into_iter() + .map(|mut matcher| { + matcher.case_insensitive = false; + (matcher, target.clone()) + }) + .collect::>() + }), + ) .collect(); MappingList(list) } diff --git a/src/syntax_mapping.rs b/src/syntax_mapping.rs index 0cd2d655..8fc4008a 100644 --- a/src/syntax_mapping.rs +++ b/src/syntax_mapping.rs @@ -17,9 +17,9 @@ use ignored_suffixes::IgnoredSuffixes; mod builtin; pub mod ignored_suffixes; -fn make_glob_matcher(from: &str) -> Result { +fn make_glob_matcher(from: &str, case_insensitive: bool) -> Result { let matcher = GlobBuilder::new(from) - .case_insensitive(true) + .case_insensitive(case_insensitive) .literal_separator(true) .build()? .compile_matcher(); @@ -97,7 +97,14 @@ impl<'a> SyntaxMapping<'a> { } pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { - let matcher = make_glob_matcher(from)?; + let matcher = make_glob_matcher(from, true)?; + self.custom_mappings.push((matcher, to)); + Ok(()) + } + + /// Like [`Self::insert`], but the glob pattern is matched case-sensitively. + pub fn insert_case_sensitive(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { + let matcher = make_glob_matcher(from, false)?; self.custom_mappings.push((matcher, to)); Ok(()) } @@ -261,4 +268,41 @@ mod tests { Some(MappingTarget::MapTo("alpha")) ); } + + #[test] + fn case_sensitive_custom_mappings_work() { + let mut map = SyntaxMapping::new(); + map.insert_case_sensitive("MY_SPECIAL_FILE", MappingTarget::MapTo("Python")) + .ok(); + + // Exact case matches + assert_eq!( + map.get_syntax_for("/path/to/MY_SPECIAL_FILE"), + Some(MappingTarget::MapTo("Python")) + ); + // Different case should NOT match the case-sensitive rule + assert_eq!(map.get_syntax_for("/path/to/my_special_file"), None); + assert_eq!(map.get_syntax_for("/path/to/My_Special_File"), None); + } + + #[test] + fn builtin_mappings_build_is_case_sensitive() { + let map = SyntaxMapping::new(); + + // "BUILD" (uppercase) should map to Python via case-sensitive builtin + assert_eq!( + map.get_syntax_for("/path/to/BUILD"), + Some(MappingTarget::MapTo("Python")) + ); + // "build" (lowercase) should still map to MapToUnknown + assert_eq!( + map.get_syntax_for("/path/to/build"), + Some(MappingTarget::MapToUnknown) + ); + // Mixed case should NOT match the Python rule + assert_eq!( + map.get_syntax_for("/path/to/Build"), + Some(MappingTarget::MapToUnknown) + ); + } } diff --git a/src/syntax_mapping/builtin.rs b/src/syntax_mapping/builtin.rs index 1822be57..52f1d47b 100644 --- a/src/syntax_mapping/builtin.rs +++ b/src/syntax_mapping/builtin.rs @@ -53,8 +53,9 @@ include!(concat!( /// A failure to compile is a fatal error. /// /// Used internally by `Lazy>`'s lazy evaluation closure. -fn build_matcher_fixed(from: &str) -> GlobMatcher { - make_glob_matcher(from).expect("A builtin fixed glob matcher failed to compile") +fn build_matcher_fixed(from: &str, case_insensitive: bool) -> GlobMatcher { + make_glob_matcher(from, case_insensitive) + .expect("A builtin fixed glob matcher failed to compile") } /// Join a list of matcher segments to create a glob string, replacing all @@ -64,7 +65,7 @@ fn build_matcher_fixed(from: &str) -> GlobMatcher { /// to compile. /// /// Used internally by `Lazy>`'s lazy evaluation closure. -fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option { +fn build_matcher_dynamic(segs: &[MatcherSegment], case_insensitive: bool) -> Option { // join segments let mut buf = String::new(); for seg in segs { @@ -77,7 +78,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option { } } // compile glob matcher - let matcher = make_glob_matcher(&buf).ok()?; + let matcher = make_glob_matcher(&buf, case_insensitive).ok()?; Some(matcher) } diff --git a/src/syntax_mapping/builtins/README.md b/src/syntax_mapping/builtins/README.md index 29cf43ee..7152fd33 100644 --- a/src/syntax_mapping/builtins/README.md +++ b/src/syntax_mapping/builtins/README.md @@ -20,9 +20,9 @@ syntax mappings defined by all TOML files, and embed them into the binary. ## File syntax -Each TOML file should contain a single section named `mappings`, with each of -its keys being a language identifier (first column of `bat -L`; also referred to -as "target"). +Each TOML file should contain a single section named `mappings` and/or a single +section named `case_sensitive_mappings`, with each of its keys being a language +identifier (first column of `bat -L`; also referred to as "target"). The value of each key should be an array of strings, with each item being a glob matcher. We will call each of these items a "rule". diff --git a/src/syntax_mapping/builtins/common/50-bazel.toml b/src/syntax_mapping/builtins/common/50-bazel.toml index 5f44ae2d..2b0e17e2 100644 --- a/src/syntax_mapping/builtins/common/50-bazel.toml +++ b/src/syntax_mapping/builtins/common/50-bazel.toml @@ -1,2 +1,2 @@ -[mappings] +[case_sensitive_mappings] "Python" = ["BUILD"] From 2a29802dd57a202589e555e896432575225fe76b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 03:35:38 +0000 Subject: [PATCH 005/130] Refactor: string-or-struct matcher syntax, Case enum, remove case_sensitive_mappings table Co-authored-by: keith-hall <11882719+keith-hall@users.noreply.github.com> --- build/syntax_mapping.rs | 83 +++++++++++++------ src/syntax_mapping.rs | 15 +++- src/syntax_mapping/builtin.rs | 10 +-- .../builtins/common/50-bazel.toml | 4 +- 4 files changed, 76 insertions(+), 36 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index f17b1cf5..17722f67 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -47,16 +47,34 @@ impl ToTokens for MappingTarget { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, DeserializeFromStr)] +/// Whether a glob pattern should be matched case-sensitively or case-insensitively. +/// +/// Mirrors the runtime `Case` type in `src/syntax_mapping.rs`. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +enum Case { + Sensitive, + Insensitive, +} +impl ToTokens for Case { + fn to_tokens(&self, tokens: &mut TokenStream) { + let t = match self { + Self::Sensitive => quote! { Case::Sensitive }, + Self::Insensitive => quote! { Case::Insensitive }, + }; + tokens.append_all(t); + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] /// A single matcher. /// /// Codegen converts this into a `Lazy>`. struct Matcher { segments: Vec, - /// Whether the glob pattern should be matched case-insensitively. + /// Whether the glob pattern should be matched case-sensitively. /// - /// Defaults to `true` (case-insensitive) for backwards compatibility. - case_insensitive: bool, + /// Defaults to `Case::Insensitive` for backwards compatibility. + case: Case, } /// Parse a matcher. /// @@ -124,21 +142,53 @@ impl FromStr for Matcher { Ok(Self { segments: non_empty_segments, - case_insensitive: true, + case: Case::Insensitive, }) } } + +/// Helper type for deserializing a `Matcher` from either a plain string or a +/// `{ glob = "...", case_sensitive = true }` struct. +#[derive(Deserialize)] +#[serde(untagged)] +enum RawMatcher { + Simple(String), + Full { + glob: String, + #[serde(default)] + case_sensitive: bool, + }, +} + +impl<'de> serde::Deserialize<'de> for Matcher { + fn deserialize>(deserializer: D) -> Result { + let raw = RawMatcher::deserialize(deserializer)?; + match raw { + RawMatcher::Simple(s) => Matcher::from_str(&s).map_err(serde::de::Error::custom), + RawMatcher::Full { glob, case_sensitive } => { + let mut matcher = + Matcher::from_str(&glob).map_err(serde::de::Error::custom)?; + matcher.case = if case_sensitive { + Case::Sensitive + } else { + Case::Insensitive + }; + Ok(matcher) + } + } + } +} impl ToTokens for Matcher { fn to_tokens(&self, tokens: &mut TokenStream) { - let case_insensitive = self.case_insensitive; + let case = &self.case; let t = match self.segments.as_slice() { [] => unreachable!("0-length matcher should never be created"), [MatcherSegment::Text(text)] => { - quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case_insensitive))) } + quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case))) } } // parser logic ensures that this case can only happen when there are dynamic segments segs @ [_, ..] => { - quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case_insensitive)) } + quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case)) } } }; tokens.append_all(t); @@ -189,10 +239,6 @@ impl MatcherSegment { struct MappingDefModel { #[serde(default)] mappings: IndexMap>, - /// Case-sensitive mappings. Unlike `mappings`, these glob patterns are - /// matched case-sensitively. - #[serde(default)] - case_sensitive_mappings: IndexMap>, } impl MappingDefModel { fn into_mapping_list(self) -> MappingList { @@ -205,19 +251,6 @@ impl MappingDefModel { .map(|matcher| (matcher, target.clone())) .collect::>() }) - .chain( - self.case_sensitive_mappings - .into_iter() - .flat_map(|(target, matchers)| { - matchers - .into_iter() - .map(|mut matcher| { - matcher.case_insensitive = false; - (matcher, target.clone()) - }) - .collect::>() - }), - ) .collect(); MappingList(list) } diff --git a/src/syntax_mapping.rs b/src/syntax_mapping.rs index 8fc4008a..584a5cb6 100644 --- a/src/syntax_mapping.rs +++ b/src/syntax_mapping.rs @@ -17,9 +17,16 @@ use ignored_suffixes::IgnoredSuffixes; mod builtin; pub mod ignored_suffixes; -fn make_glob_matcher(from: &str, case_insensitive: bool) -> Result { +/// Whether a glob pattern should be matched case-sensitively or case-insensitively. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Case { + Sensitive, + Insensitive, +} + +fn make_glob_matcher(from: &str, case: Case) -> Result { let matcher = GlobBuilder::new(from) - .case_insensitive(case_insensitive) + .case_insensitive(matches!(case, Case::Insensitive)) .literal_separator(true) .build()? .compile_matcher(); @@ -97,14 +104,14 @@ impl<'a> SyntaxMapping<'a> { } pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { - let matcher = make_glob_matcher(from, true)?; + let matcher = make_glob_matcher(from, Case::Insensitive)?; self.custom_mappings.push((matcher, to)); Ok(()) } /// Like [`Self::insert`], but the glob pattern is matched case-sensitively. pub fn insert_case_sensitive(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { - let matcher = make_glob_matcher(from, false)?; + let matcher = make_glob_matcher(from, Case::Sensitive)?; self.custom_mappings.push((matcher, to)); Ok(()) } diff --git a/src/syntax_mapping/builtin.rs b/src/syntax_mapping/builtin.rs index 52f1d47b..41007be5 100644 --- a/src/syntax_mapping/builtin.rs +++ b/src/syntax_mapping/builtin.rs @@ -3,7 +3,7 @@ use std::env; use globset::GlobMatcher; use once_cell::sync::Lazy; -use crate::syntax_mapping::{make_glob_matcher, MappingTarget}; +use crate::syntax_mapping::{make_glob_matcher, Case, MappingTarget}; // Static syntax mappings generated from /src/syntax_mapping/builtins/ by the // build script (/build/syntax_mapping.rs). @@ -53,8 +53,8 @@ include!(concat!( /// A failure to compile is a fatal error. /// /// Used internally by `Lazy>`'s lazy evaluation closure. -fn build_matcher_fixed(from: &str, case_insensitive: bool) -> GlobMatcher { - make_glob_matcher(from, case_insensitive) +fn build_matcher_fixed(from: &str, case: Case) -> GlobMatcher { + make_glob_matcher(from, case) .expect("A builtin fixed glob matcher failed to compile") } @@ -65,7 +65,7 @@ fn build_matcher_fixed(from: &str, case_insensitive: bool) -> GlobMatcher { /// to compile. /// /// Used internally by `Lazy>`'s lazy evaluation closure. -fn build_matcher_dynamic(segs: &[MatcherSegment], case_insensitive: bool) -> Option { +fn build_matcher_dynamic(segs: &[MatcherSegment], case: Case) -> Option { // join segments let mut buf = String::new(); for seg in segs { @@ -78,7 +78,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment], case_insensitive: bool) -> Opt } } // compile glob matcher - let matcher = make_glob_matcher(&buf, case_insensitive).ok()?; + let matcher = make_glob_matcher(&buf, case).ok()?; Some(matcher) } diff --git a/src/syntax_mapping/builtins/common/50-bazel.toml b/src/syntax_mapping/builtins/common/50-bazel.toml index 2b0e17e2..2ced1399 100644 --- a/src/syntax_mapping/builtins/common/50-bazel.toml +++ b/src/syntax_mapping/builtins/common/50-bazel.toml @@ -1,2 +1,2 @@ -[case_sensitive_mappings] -"Python" = ["BUILD"] +[mappings] +"Python" = [{ glob = "BUILD", case_sensitive = true }] From c6e661d80bc9654583771f2ccd00aa1bf4a3e28a Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Wed, 18 Mar 2026 22:25:07 +0200 Subject: [PATCH 006/130] cargo fmt --- build/syntax_mapping.rs | 8 +++++--- src/syntax_mapping/builtin.rs | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index 17722f67..11d778e3 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -165,9 +165,11 @@ impl<'de> serde::Deserialize<'de> for Matcher { let raw = RawMatcher::deserialize(deserializer)?; match raw { RawMatcher::Simple(s) => Matcher::from_str(&s).map_err(serde::de::Error::custom), - RawMatcher::Full { glob, case_sensitive } => { - let mut matcher = - Matcher::from_str(&glob).map_err(serde::de::Error::custom)?; + RawMatcher::Full { + glob, + case_sensitive, + } => { + let mut matcher = Matcher::from_str(&glob).map_err(serde::de::Error::custom)?; matcher.case = if case_sensitive { Case::Sensitive } else { diff --git a/src/syntax_mapping/builtin.rs b/src/syntax_mapping/builtin.rs index 41007be5..79d298c3 100644 --- a/src/syntax_mapping/builtin.rs +++ b/src/syntax_mapping/builtin.rs @@ -54,8 +54,7 @@ include!(concat!( /// /// Used internally by `Lazy>`'s lazy evaluation closure. fn build_matcher_fixed(from: &str, case: Case) -> GlobMatcher { - make_glob_matcher(from, case) - .expect("A builtin fixed glob matcher failed to compile") + make_glob_matcher(from, case).expect("A builtin fixed glob matcher failed to compile") } /// Join a list of matcher segments to create a glob string, replacing all From 618d7340bb63fa6168615585075c6debb7240a82 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:34:12 -0700 Subject: [PATCH 007/130] fix: consistent .deb MUSL package names Fix two inconsistencies in MUSL .deb package naming: 1. Change dpkg_arch for aarch64-unknown-linux-musl from `arm64` to `musl-linux-arm64` to match the convention used by other MUSL targets. 2. Change the DPKG_BASENAME case pattern from `*-musl)` to `*-musl*)` so it also matches `musleabihf`, giving the arm target the `bat-musl` prefix like all other MUSL packages. After this fix, all MUSL .deb packages follow the consistent pattern: `bat-musl_VERSION_musl-linux-ARCH.deb` Closes #3482 --- .github/workflows/CICD.yml | 4 ++-- CHANGELOG.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index f1bc3887..e1b82424 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -161,7 +161,7 @@ jobs: fail-fast: false matrix: job: - - { target: aarch64-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: arm64, use-cross: true } + - { target: aarch64-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-arm64, use-cross: true } - { target: aarch64-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: arm64, use-cross: true } - { target: arm-unknown-linux-gnueabihf , os: ubuntu-latest , dpkg_arch: armhf, use-cross: true } - { target: arm-unknown-linux-musleabihf, os: ubuntu-latest , dpkg_arch: musl-linux-armhf, use-cross: true } @@ -335,7 +335,7 @@ jobs: DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }} DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl - case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac; + case ${{ matrix.job.target }} in *-musl*) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac; DPKG_VERSION=${{ needs.crate_metadata.outputs.version }} DPKG_ARCH="${{ matrix.job.dpkg_arch }}" DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de41716..ea1475bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir) ## Bugfixes +- Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix), see #3482 (@mvanhorn) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) - Fix syntax highlighting for symlinked files when the symlink name has no extension but the target does. Closes #1001, see #3621 (@Xavrir) - Report error when pager is missing instead of silently falling back, see #3588 (@IMaloney) From 2a3ed948ecc1f98dc7347ddf170ffcd384139ca1 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:35:48 -0700 Subject: [PATCH 008/130] feat: preserve change markers when combining --diff with --plain When --plain is set (via CLI or config file), --diff output loses all visual markers, making it indistinguishable from plain text. The diff line filtering still works, but without Changes markers or Snip separators the output is not useful. Automatically include Changes and Snip style components when --diff is active alongside --plain. This preserves the --plain intent (no grid, no header, no line numbers) while keeping diff output readable. Closes #3630 --- CHANGELOG.md | 1 + src/bin/bat/app.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de41716..a9508f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Features +- Preserve `--diff` change markers and snip separators when `--plain` is set, see #3630 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index dddb5559..a580ff02 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -590,7 +590,16 @@ impl App { // Plain if `--plain` is specified at least once. if self.matches.get_count("plain") > 0 { - return Some(StyleComponents(HashSet::from([StyleComponent::Plain]))); + let mut components = HashSet::from([StyleComponent::Plain]); + // When --diff is active, preserve change markers and snip separators + // so that diff output remains visually useful. + if self.matches.try_contains_id("diff").unwrap_or_default() + && self.matches.get_flag("diff") + { + components.insert(StyleComponent::Changes); + components.insert(StyleComponent::Snip); + } + return Some(StyleComponents(components)); } // Default behavior. From 99c8e15c27c7fc03c9b50aa494f7d0db14885040 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:37:45 -0700 Subject: [PATCH 009/130] fix: update changelog entry with PR number for CI check --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea1475bb..c3c893b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ - Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir) ## Bugfixes -- Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix), see #3482 (@mvanhorn) +- Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) - Fix syntax highlighting for symlinked files when the symlink name has no extension but the target does. Closes #1001, see #3621 (@Xavrir) - Report error when pager is missing instead of silently falling back, see #3588 (@IMaloney) From 5e140558b13f6b87e1dc74ac67cabeb48fc773e4 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:38:03 -0700 Subject: [PATCH 010/130] fix: update changelog entry with PR number for CI check --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9508f39..457b5cf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ## Features -- Preserve `--diff` change markers and snip separators when `--plain` is set, see #3630 (@mvanhorn) +- Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) From 169dc7c45b0c5aacd386c46eb40c9e8abf6cad8b Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:33:33 -0700 Subject: [PATCH 011/130] test: add integration tests for --diff combined with --plain --- tests/integration_tests.rs | 146 +++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index cfbad253..9a8a7f41 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3965,3 +3965,149 @@ fn word_wrap_short_line_no_wrap() { .success() .stdout("Single Line\n"); } + +#[cfg(unix)] +#[cfg(feature = "git")] +fn setup_diff_test_repo() -> tempfile::TempDir { + use std::process::Command; + + let dir = tempfile::tempdir().expect("can create temporary directory"); + let repo = dir.path(); + + // Initialize a git repo and commit a file + Command::new("git") + .args(["init"]) + .current_dir(repo) + .output() + .expect("git init"); + + Command::new("git") + .args(["config", "user.email", "test@test.com"]) + .current_dir(repo) + .output() + .expect("git config email"); + + Command::new("git") + .args(["config", "user.name", "Test"]) + .current_dir(repo) + .output() + .expect("git config name"); + + std::fs::write(repo.join("test.txt"), "line 1\nline 2\nline 3\n") + .expect("can write test file"); + + Command::new("git") + .args(["add", "test.txt"]) + .current_dir(repo) + .output() + .expect("git add"); + + Command::new("git") + .args(["commit", "-m", "initial"]) + .current_dir(repo) + .output() + .expect("git commit"); + + // Modify the file so --diff has something to show + std::fs::write(repo.join("test.txt"), "line 1\nline 2 modified\nline 3\nline 4 added\n") + .expect("can write modified test file"); + + dir +} + +#[cfg(unix)] +#[cfg(feature = "git")] +#[test] +fn diff_plain_preserves_change_markers() { + let repo = setup_diff_test_repo(); + + // With --diff --plain, output should contain the change marker column + // but not other decorations like line numbers or grid + let output = bat() + .current_dir(repo.path()) + .arg("--diff") + .arg("--plain") + .arg("--color=never") + .arg("--decorations=always") + .arg("test.txt") + .assert() + .success() + .get_output() + .stdout + .clone(); + + let stdout = std::str::from_utf8(&output).expect("valid utf-8"); + + // The output should contain the modified and added lines + assert!( + stdout.contains("line 2 modified"), + "diff plain output should contain modified line, got: {stdout}" + ); + assert!( + stdout.contains("line 4 added"), + "diff plain output should contain added line, got: {stdout}" + ); + + // Should NOT contain line numbers (a decoration that --plain disables) + assert!( + !stdout.contains(" 1"), + "diff plain output should not contain line numbers, got: {stdout}" + ); +} + +#[cfg(unix)] +#[cfg(feature = "git")] +#[test] +fn diff_plain_does_not_show_grid_or_header() { + let repo = setup_diff_test_repo(); + + let output = bat() + .current_dir(repo.path()) + .arg("--diff") + .arg("--plain") + .arg("--color=never") + .arg("--decorations=always") + .arg("--terminal-width=80") + .arg("test.txt") + .assert() + .success() + .get_output() + .stdout + .clone(); + + let stdout = std::str::from_utf8(&output).expect("valid utf-8"); + + // Grid lines use box-drawing characters + assert!( + !stdout.contains('─'), + "diff plain output should not contain grid lines, got: {stdout}" + ); + assert!( + !stdout.contains('│'), + "diff plain output should not contain grid separators, got: {stdout}" + ); + + // Header shows "File: " + assert!( + !stdout.contains("File:"), + "diff plain output should not contain file header, got: {stdout}" + ); +} + +#[cfg(unix)] +#[cfg(feature = "git")] +#[test] +fn plain_without_diff_still_works() { + let repo = setup_diff_test_repo(); + + // --plain without --diff should output file content with no decorations at all + bat() + .current_dir(repo.path()) + .arg("--plain") + .arg("--color=never") + .arg("--decorations=always") + .arg("test.txt") + .assert() + .success() + .stdout("line 1\nline 2 modified\nline 3\nline 4 added\n"); +} From fc94a0ec49dcea3d5c022d38299f82b1f894f844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Thu, 19 Mar 2026 13:26:14 +0300 Subject: [PATCH 012/130] fix: account for caret notation width in text wrapping Control characters displayed in caret notation (e.g. ^@ for NUL) occupy 2 terminal columns, but the width calculation treated them as 0-width. Add a char_width() helper that returns 2 for control characters, fixing incorrect line wrapping with --binary=as-text. Fixes #3631 --- CHANGELOG.md | 1 + src/printer.rs | 18 ++++++++++++----- .../examples/regression_tests/issue_3631.txt | Bin 0 -> 24 bytes tests/integration_tests.rs | 19 ++++++++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 tests/examples/regression_tests/issue_3631.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de41716..34bd719e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir) ## Bugfixes +- Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3631 (@eyupcanakman) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) - Fix syntax highlighting for symlinked files when the symlink name has no extension but the target does. Closes #1001, see #3621 (@Xavrir) - Report error when pager is missing instead of silently falling back, see #3588 (@IMaloney) diff --git a/src/printer.rs b/src/printer.rs index 6a57fb62..c58c914d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -37,6 +37,16 @@ use crate::wrapping::WrappingMode; use crate::BinaryBehavior; use crate::StripAnsiMode; +// Return the displayed width of a character. +// +// Control characters (0x00..=0x1F and 0x7F) are rendered by the terminal +// in caret notation (e.g. ^@, ^A, ..., ^?), which occupies two columns. +// UnicodeWidthChar::width() returns None for these, so we map them to 2 +// here instead of the previous default of 0. +fn char_width(c: char) -> usize { + c.width().unwrap_or(if c.is_control() { 2 } else { 0 }) +} + const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI { raw_sequence: "\x1B[4m", parameters: "4", @@ -793,7 +803,7 @@ impl Printer for InteractivePrinter<'_> { for c in text.chars() { // calculate the displayed width for next character - let cw = c.width().unwrap_or(0); + let cw = char_width(c); current_width += cw; // Track whitespace positions for word wrapping. @@ -868,10 +878,8 @@ impl Printer for InteractivePrinter<'_> { if let Some(rs) = rest_start { // Word wrap: carry remainder to next line. let remainder = line_buf[rs..].to_string(); - let rem_width: usize = remainder - .chars() - .map(|ch| ch.width().unwrap_or(0)) - .sum(); + let rem_width: usize = + remainder.chars().map(char_width).sum(); line_buf.clear(); line_buf.push_str(&remainder); current_width = rem_width + cw; diff --git a/tests/examples/regression_tests/issue_3631.txt b/tests/examples/regression_tests/issue_3631.txt new file mode 100644 index 0000000000000000000000000000000000000000..8449691fdeab44ee83cf8b8059c2aff05c2e76b5 GIT binary patch literal 24 OcmZQzzyz*-E?fWtr~u*s literal 0 HcmV?d00001 diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index cfbad253..c6ea83e9 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -2681,6 +2681,25 @@ fn binary_as_text() { .stderr(""); } +#[test] +fn binary_as_text_control_char_width() { + // Control characters are displayed as caret notation (e.g. ^@) by the + // terminal, occupying 2 columns each. With 20 NUL bytes (40 columns) + + // "END" (3 columns) = 43 columns, wrapping at terminal width 40 must + // produce 2 lines, not 1. See #3631. + bat() + .arg("--binary=as-text") + .arg("--wrap=character") + .arg("--terminal-width=40") + .arg("--decorations=always") + .arg("--style=plain") + .arg("--color=never") + .arg("regression_tests/issue_3631.txt") + .assert() + .success() + .stdout(predicate::function(|s: &str| s.lines().count() == 2)); +} + #[test] fn no_strip_overstrike_for_plain_text() { // Overstrike is preserved for plain text files (no syntax highlighting) From 1f540752ef21f64aafa46007bb2bf7f328ad5230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Thu, 19 Mar 2026 19:24:26 +0300 Subject: [PATCH 013/130] fix: reference PR number in CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34bd719e..180f9ad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ - Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir) ## Bugfixes -- Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3631 (@eyupcanakman) +- Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) - Fix syntax highlighting for symlinked files when the symlink name has no extension but the target does. Closes #1001, see #3621 (@Xavrir) - Report error when pager is missing instead of silently falling back, see #3588 (@IMaloney) From 3767f15c2abf07ef1edfa499fa27cfff03977393 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Fri, 20 Mar 2026 23:11:23 +0200 Subject: [PATCH 014/130] improvements from PR review fix documentation about syntax mappings --- src/syntax_mapping/builtins/README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/syntax_mapping/builtins/README.md b/src/syntax_mapping/builtins/README.md index 7152fd33..220e45df 100644 --- a/src/syntax_mapping/builtins/README.md +++ b/src/syntax_mapping/builtins/README.md @@ -20,12 +20,10 @@ syntax mappings defined by all TOML files, and embed them into the binary. ## File syntax -Each TOML file should contain a single section named `mappings` and/or a single -section named `case_sensitive_mappings`, with each of its keys being a language +Each TOML file should contain a single section named `mappings`, with each of its keys being a language identifier (first column of `bat -L`; also referred to as "target"). -The value of each key should be an array of strings, with each item being a glob -matcher. We will call each of these items a "rule". +The value of each key should be an array of "rules". The rules are expected to be objects with a `glob` string and a `case_sensitive` boolean. For simplification, a rule can be just a glob string, which is shorthand for the default case insensitive mode. For example, if `foo-application` uses both TOML and YAML configuration files, we could write something like this: @@ -98,6 +96,15 @@ like this: ] ``` +### Case sensitivity + +By default, all glob patterns are matched case-insensitively. To match a pattern case-sensitively, use the object form of the rule with the `case_sensitive` option: + +```toml +[mappings] +"Python" = [{ glob = "BUILD", case_sensitive = true }] +``` + ## Ordering At compile time, all TOML files applicable to the target are processed in From 7a6f442c8666bcda1649786ee48256f09b21363e Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Fri, 20 Mar 2026 23:35:27 +0200 Subject: [PATCH 015/130] improvements from PR review - prefer to use `Default::default` because it's semantically clearer why we've chosen to use a particular value --- build/syntax_mapping.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index 11d778e3..bc4601fc 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -50,9 +50,10 @@ impl ToTokens for MappingTarget { /// Whether a glob pattern should be matched case-sensitively or case-insensitively. /// /// Mirrors the runtime `Case` type in `src/syntax_mapping.rs`. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] enum Case { Sensitive, + #[default] Insensitive, } impl ToTokens for Case { @@ -65,7 +66,7 @@ impl ToTokens for Case { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] /// A single matcher. /// /// Codegen converts this into a `Lazy>`. From a19593b383f208106d019cbca3546cb0748eb377 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Fri, 20 Mar 2026 23:53:30 +0200 Subject: [PATCH 016/130] improvements from PR review 1. Added `Deserialize` derive and `#[serde(try_from = "RawMatcher")]` to the `Matcher` struct. With `try_from`, serde generates a `Deserialize` impl that first deserializes into `RawMatcher`, then calls `TryFrom for Matcher`. It never attempts to deserialize `Matcher`'s fields directly, so `Case` and `MatcherSegment` don't need `Deserialize` impls. 2. Replaced the manual `impl<'de> serde::Deserialize<'de> for Matcher` with a standard `impl TryFrom for Matcher`. The logic is identical - the conversion is fallible because `Matcher::from_str` returns `Result<_, anyhow::Error>`, so `try_from` (not `from`) is the correct choice, avoiding any panics. The net effect: same behavior, same error handling, but using the idiomatic serde pattern instead of a manual deserializer impl. The `RawMatcher` intermediate type and its `#[serde(untagged)]` derive remain unchanged. --- build/syntax_mapping.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index bc4601fc..c7133be9 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -66,7 +66,8 @@ impl ToTokens for Case { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize)] +#[serde(try_from = "RawMatcher")] /// A single matcher. /// /// Codegen converts this into a `Lazy>`. @@ -161,16 +162,17 @@ enum RawMatcher { }, } -impl<'de> serde::Deserialize<'de> for Matcher { - fn deserialize>(deserializer: D) -> Result { - let raw = RawMatcher::deserialize(deserializer)?; +impl TryFrom for Matcher { + type Error = anyhow::Error; + + fn try_from(raw: RawMatcher) -> Result { match raw { - RawMatcher::Simple(s) => Matcher::from_str(&s).map_err(serde::de::Error::custom), + RawMatcher::Simple(s) => Matcher::from_str(&s), RawMatcher::Full { glob, case_sensitive, } => { - let mut matcher = Matcher::from_str(&glob).map_err(serde::de::Error::custom)?; + let mut matcher = Matcher::from_str(&glob)?; matcher.case = if case_sensitive { Case::Sensitive } else { From e60875ac125f6698775679fa9a8beb495299b4e5 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:27:10 -0700 Subject: [PATCH 017/130] fix: gate Changes component on git feature flag When bat is built without the `git` feature, `StyleComponent::Changes` is not available. Add `#[cfg(feature = "git")]` guard so `--diff --plain` works in both configurations. Co-Authored-By: Claude Opus 4.6 --- src/bin/bat/app.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index a580ff02..f95bbb91 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -596,6 +596,7 @@ impl App { if self.matches.try_contains_id("diff").unwrap_or_default() && self.matches.get_flag("diff") { + #[cfg(feature = "git")] components.insert(StyleComponent::Changes); components.insert(StyleComponent::Snip); } From e86797fbf4acf3685f96539e9d3118f86186217d Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:46:54 -0700 Subject: [PATCH 018/130] style: auto-format integration tests Co-Authored-By: Claude Opus 4.6 --- tests/integration_tests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 9a8a7f41..b0165126 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3993,8 +3993,7 @@ fn setup_diff_test_repo() -> tempfile::TempDir { .output() .expect("git config name"); - std::fs::write(repo.join("test.txt"), "line 1\nline 2\nline 3\n") - .expect("can write test file"); + std::fs::write(repo.join("test.txt"), "line 1\nline 2\nline 3\n").expect("can write test file"); Command::new("git") .args(["add", "test.txt"]) @@ -4009,8 +4008,11 @@ fn setup_diff_test_repo() -> tempfile::TempDir { .expect("git commit"); // Modify the file so --diff has something to show - std::fs::write(repo.join("test.txt"), "line 1\nline 2 modified\nline 3\nline 4 added\n") - .expect("can write modified test file"); + std::fs::write( + repo.join("test.txt"), + "line 1\nline 2 modified\nline 3\nline 4 added\n", + ) + .expect("can write modified test file"); dir } From 0b4c886efc31d0adf8a99303006620f289146375 Mon Sep 17 00:00:00 2001 From: Sungjoon Moon Date: Thu, 25 Dec 2025 20:19:30 +0000 Subject: [PATCH 019/130] ci: Use git version of cross for better target support --- .github/workflows/CICD.yml | 4 +--- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index e1b82424..4c3db0c8 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -195,9 +195,7 @@ jobs: - name: Install cross if: matrix.job.use-cross - uses: taiki-e/install-action@v2 - with: - tool: cross + run: cargo install cross --git https://github.com/cross-rs/cross --rev 588b3c99db52b5a9c5906fab96cfadcf1bde7863 - name: Overwrite build command env variable if: matrix.job.use-cross diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2c2f84..08b116ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Fixed test compatibility with future Cargo build directory changes, see #3550 (@nmacl) ## Other +- Use git version of cross. See #3533 (@OctopusET) - Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX) From ca3ef28d56a2b010f2ddf8fa01be64aec905ab4f Mon Sep 17 00:00:00 2001 From: Sim-hu Date: Tue, 24 Mar 2026 19:20:18 +0900 Subject: [PATCH 020/130] fix: use correct Debian architecture name for i686 .deb package The i686 .deb package declared its architecture as "i686", which Debian does not recognize. Debian uses "i386" for 32-bit x86, matching the convention already used by the other targets (arm64, armhf, amd64). Closes #3611 --- .github/workflows/CICD.yml | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 4c3db0c8..f881cada 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -166,7 +166,7 @@ jobs: - { target: arm-unknown-linux-gnueabihf , os: ubuntu-latest , dpkg_arch: armhf, use-cross: true } - { target: arm-unknown-linux-musleabihf, os: ubuntu-latest , dpkg_arch: musl-linux-armhf, use-cross: true } - { target: i686-pc-windows-msvc , os: windows-2025 , } - - { target: i686-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: i686, use-cross: true } + - { target: i686-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: i386, use-cross: true } - { target: i686-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-i686, use-cross: true } - { target: x86_64-apple-darwin , os: macos-15-intel, } - { target: aarch64-apple-darwin , os: macos-latest , } diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b116ff..b9611731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes +- Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611 - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) From b8d462ba87f4ec4c5e3d6862f2dfd5865b97b481 Mon Sep 17 00:00:00 2001 From: Sim-hu Date: Tue, 24 Mar 2026 19:24:36 +0900 Subject: [PATCH 021/130] fix: add PR number and author to changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9611731..217d3baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes -- Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611 +- Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) - Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir) From 87e043b91ce47f95829ad74ac06e04d0a163dccf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 18:46:46 +0000 Subject: [PATCH 022/130] Add Home/End key bindings to builtin minus pager Co-authored-by: keith-hall <11882719+keith-hall@users.noreply.github.com> --- src/output.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/output.rs b/src/output.rs index 0205f48e..8054be46 100644 --- a/src/output.rs +++ b/src/output.rs @@ -23,6 +23,18 @@ pub struct BuiltinPager { impl BuiltinPager { fn new() -> Self { let pager = minus::Pager::new(); + + let mut input_register = minus::input::HashedEventRegister::default(); + input_register.add_key_events(&["home"], |_, _| { + minus::input::InputEvent::UpdateUpperMark(0) + }); + input_register.add_key_events(&["end"], |_, _| { + minus::input::InputEvent::UpdateUpperMark(usize::MAX) + }); + pager + .set_input_classifier(Box::new(input_register)) + .expect("failed to set input classifier on newly created pager"); + let handle = { let pager = pager.clone(); Some(spawn(move || { From eef71074070adebb33cc3144db0bbf154c669927 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 24 Mar 2026 21:38:07 +0200 Subject: [PATCH 023/130] update changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b116ff..52d732f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,5 @@ # unreleased -- Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l) -- Fixed syntax tests path, see #3610 (@foxfromworld) ## Features @@ -27,11 +25,13 @@ - `--help` now correctly honors `--pager=builtin`. See #3516 (@keith-hall) - `--help` now correctly honors custom themes. See #3524 (@keith-hall) - Fixed test compatibility with future Cargo build directory changes, see #3550 (@nmacl) +- Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l) +- Fixed syntax tests path, see #3610 (@foxfromworld) ## Other - Use git version of cross. See #3533 (@OctopusET) - - Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX) +- Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall) ## Syntaxes From 3e789f5241dfedc789de7a8b7e2bd704858514d2 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:49:50 +0800 Subject: [PATCH 024/130] Imprv: cleanup matcher glob parsing logic - Data flow is now strictly linear from `RawMatcher` to `Matcher` - I've also hoisted `RawMatcher` in front of `Matcher` to signal this - Re-type `RawMatcher.case_sensitive` from `bool` to `Option` - This moves all parser logic away from `RawMatcher`, making it a more faithful representation of the data - Favour default consts in `Matcher::try_from` to `Default` impl on `Case` - Because the default choice of casing is a design decision of the logic, not an intrinsic property of the type --- build/syntax_mapping.rs | 153 ++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 78 deletions(-) diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs index c7133be9..b0db01eb 100644 --- a/build/syntax_mapping.rs +++ b/build/syntax_mapping.rs @@ -47,13 +47,24 @@ impl ToTokens for MappingTarget { } } +/// Helper type for deserializing a `Matcher` from either a plain string or a +/// `{ glob = "...", case_sensitive = true }` struct. +#[derive(Deserialize)] +#[serde(untagged)] +enum RawMatcher { + Simple(String), + Full { + glob: String, + case_sensitive: Option, + }, +} + /// Whether a glob pattern should be matched case-sensitively or case-insensitively. /// /// Mirrors the runtime `Case` type in `src/syntax_mapping.rs`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] enum Case { Sensitive, - #[default] Insensitive, } impl ToTokens for Case { @@ -66,11 +77,11 @@ impl ToTokens for Case { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize)] -#[serde(try_from = "RawMatcher")] /// A single matcher. /// /// Codegen converts this into a `Lazy>`. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)] +#[serde(try_from = "RawMatcher")] struct Matcher { segments: Vec, /// Whether the glob pattern should be matched case-sensitively. @@ -78,7 +89,7 @@ struct Matcher { /// Defaults to `Case::Insensitive` for backwards compatibility. case: Case, } -/// Parse a matcher. +/// Parse the glob pattern of a matcher. /// /// Note that this implementation is rather strict: it will greedily interpret /// every valid environment variable replacement as such, then immediately @@ -92,93 +103,79 @@ struct Matcher { /// /// Revision history: /// - 2024-02-20: allow `{` and `}` (glob brace expansion) -impl FromStr for Matcher { - type Err = anyhow::Error; - fn from_str(s: &str) -> Result { - use MatcherSegment as Seg; - static VAR_REGEX: Lazy = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap()); +fn parse_glob(s: &str) -> Result, anyhow::Error> { + use MatcherSegment as Seg; + static VAR_REGEX: Lazy = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap()); - let mut segments = vec![]; - let mut text_start = 0; - for capture in VAR_REGEX.captures_iter(s) { - let match_0 = capture.get(0).unwrap(); + let mut segments = vec![]; + let mut text_start = 0; + for capture in VAR_REGEX.captures_iter(s) { + let match_0 = capture.get(0).unwrap(); - // text before this var - let text_end = match_0.start(); - segments.push(Seg::Text(s[text_start..text_end].into())); - text_start = match_0.end(); + // text before this var + let text_end = match_0.start(); + segments.push(Seg::Text(s[text_start..text_end].into())); + text_start = match_0.end(); - // this var - segments.push(Seg::Env(capture.get(1).unwrap().as_str().into())); - } - // possible trailing text - segments.push(Seg::Text(s[text_start..].into())); - - // cleanup empty text segments - let non_empty_segments = segments - .into_iter() - .filter(|seg| seg.text().map(|t| !t.is_empty()).unwrap_or(true)) - .collect_vec(); - - // sanity check - if non_empty_segments - .windows(2) - .any(|segs| segs[0].is_text() && segs[1].is_text()) - { - unreachable!("Parsed into consecutive text segments: {non_empty_segments:?}"); - } - - // guard empty case - if non_empty_segments.is_empty() { - bail!(r#"Parsed an empty matcher: "{s}""#); - } - - // guard variable syntax leftover fragments - if non_empty_segments - .iter() - .filter_map(Seg::text) - .any(|t| t.contains('$')) - { - bail!(r#"Invalid matcher: "{s}""#); - } - - Ok(Self { - segments: non_empty_segments, - case: Case::Insensitive, - }) + // this var + segments.push(Seg::Env(capture.get(1).unwrap().as_str().into())); } -} + // possible trailing text + segments.push(Seg::Text(s[text_start..].into())); -/// Helper type for deserializing a `Matcher` from either a plain string or a -/// `{ glob = "...", case_sensitive = true }` struct. -#[derive(Deserialize)] -#[serde(untagged)] -enum RawMatcher { - Simple(String), - Full { - glob: String, - #[serde(default)] - case_sensitive: bool, - }, -} + // cleanup empty text segments + let non_empty_segments = segments + .into_iter() + .filter(|seg| seg.text().map(|t| !t.is_empty()).unwrap_or(true)) + .collect_vec(); + // sanity check + if non_empty_segments + .windows(2) + .any(|segs| segs[0].is_text() && segs[1].is_text()) + { + unreachable!("Parsed into consecutive text segments: {non_empty_segments:?}"); + } + + // guard empty case + if non_empty_segments.is_empty() { + bail!(r#"Parsed an empty matcher: "{s}""#); + } + + // guard variable syntax leftover fragments + if non_empty_segments + .iter() + .filter_map(Seg::text) + .any(|t| t.contains('$')) + { + bail!(r#"Invalid matcher: "{s}""#); + } + + Ok(non_empty_segments) +} impl TryFrom for Matcher { type Error = anyhow::Error; - fn try_from(raw: RawMatcher) -> Result { - match raw { - RawMatcher::Simple(s) => Matcher::from_str(&s), + const DEFAULT_CASE: Case = Case::Insensitive; + match &raw { + RawMatcher::Simple(s) => { + let segments = parse_glob(s)?; + Ok(Self { + segments, + case: DEFAULT_CASE, + }) + } RawMatcher::Full { glob, case_sensitive, } => { - let mut matcher = Matcher::from_str(&glob)?; - matcher.case = if case_sensitive { - Case::Sensitive - } else { - Case::Insensitive + let segments = parse_glob(glob)?; + let case = match case_sensitive { + None => DEFAULT_CASE, + Some(false) => Case::Insensitive, + Some(true) => Case::Sensitive, }; - Ok(matcher) + Ok(Self { segments, case }) } } } From b511b928f440ed1f5fcba305a47d93bd68f379a0 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:00:46 +0800 Subject: [PATCH 025/130] Write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd7770b..251e699d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Use git version of cross. See #3533 (@OctopusET) - Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX) - Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall) +- Builtin syntax mapping: cleanup matcher glob parsing logic #3652 (@cyqsimon) ## Syntaxes From 7ff1fb3278b756e73a71911d1ae9a702af1c221f Mon Sep 17 00:00:00 2001 From: Weixie Cui Date: Thu, 26 Mar 2026 11:10:58 +0800 Subject: [PATCH 026/130] fix: warn when $LESSCLOSE fails, not when it succeeds The Drop cleanup for Preprocessed inverted the exit-status check, so bat emitted a warning when LESSCLOSE exited successfully and stayed silent on failure. Align the condition with the intended behavior. --- src/lessopen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lessopen.rs b/src/lessopen.rs index 966bc2c0..81ae3cff 100644 --- a/src/lessopen.rs +++ b/src/lessopen.rs @@ -261,7 +261,7 @@ impl Drop for Preprocessed { } }; - if lessclose_output.status.success() { + if !lessclose_output.status.success() { bat_warning!("$LESSCLOSE exited with nonzero exit code",) }; } From 5c3b8040dbcbe6c39b84746ceab944fd97db859d Mon Sep 17 00:00:00 2001 From: Weixie Cui Date: Thu, 26 Mar 2026 11:11:19 +0800 Subject: [PATCH 027/130] fix: add changelog entry for LESSCLOSE warning fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251e699d..99831cf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes +- Fix inverted `$LESSCLOSE` warning so bat warns on nonzero exit, not on success. See #3654 (@cuiweixie) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) From 89f3d2d31afe5487c89a7d14b48717b56758f243 Mon Sep 17 00:00:00 2001 From: Claw Explorer Date: Thu, 26 Mar 2026 12:18:52 -0400 Subject: [PATCH 028/130] docs: add instructions for removing fish help abbreviations Add documentation showing how to erase the fish abbreviations for --help and -h, since the dash-prefixed names make removal non-obvious. The key is using -- before the abbreviation name to prevent fish from interpreting it as a flag. Closes #3536 --- CHANGELOG.md | 4 ++++ README.md | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251e699d..cd21680c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # unreleased +## Other + +- Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536 + ## Features - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) diff --git a/README.md b/README.md index d5a68440..140c3022 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,15 @@ abbr -a --position anywhere -- --help '--help | bat -plhelp' abbr -a --position anywhere -- -h '-h | bat -plhelp' ``` +To remove these abbreviations later: + +```fish +abbr -e -- --help +abbr -e -- -h +``` + +The `--` before the abbreviation name is required because `--help` and `-h` start with dashes, which would otherwise be interpreted as flags to `abbr` itself. + This way, you can keep on using `cp --help`, but get colorized help pages. Be aware that in some cases, `-h` may not be a shorthand of `--help` (for example with `ls`). In cases where you need to use `-h` From 652489251b5e8eeefea0c339adf293e5f8341db7 Mon Sep 17 00:00:00 2001 From: Claw Explorer Date: Sat, 28 Mar 2026 16:34:28 -0400 Subject: [PATCH 029/130] docs: move removal instructions into a TIP callout Address review feedback from @keith-hall: wrap the abbreviation removal instructions in a GitHub callout (> [!TIP]) to visually separate them from the main setup flow. Also reorder so the 'This way, you can keep on using...' sentence follows directly after the abbreviation creation, improving readability. --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 140c3022..d6ef3381 100644 --- a/README.md +++ b/README.md @@ -244,17 +244,16 @@ abbr -a --position anywhere -- --help '--help | bat -plhelp' abbr -a --position anywhere -- -h '-h | bat -plhelp' ``` -To remove these abbreviations later: - -```fish -abbr -e -- --help -abbr -e -- -h -``` - -The `--` before the abbreviation name is required because `--help` and `-h` start with dashes, which would otherwise be interpreted as flags to `abbr` itself. - This way, you can keep on using `cp --help`, but get colorized help pages. +> [!TIP] +> To remove these abbreviations later, run: +> ```fish +> abbr -e -- --help +> abbr -e -- -h +> ``` +> The `--` before the abbreviation name is required because `--help` and `-h` start with dashes, which would otherwise be interpreted as flags to `abbr` itself. + Be aware that in some cases, `-h` may not be a shorthand of `--help` (for example with `ls`). In cases where you need to use `-h` as a command argument you can prepend `\` to the argument (eg. `ls \-h`) to escape the aliasing defined above. From abab6533e4d1ff26c1a4f706b8f54235683bd997 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 31 Mar 2026 22:22:31 +0300 Subject: [PATCH 030/130] Update --diagnostic output with BAT_THEME_LIGHT/DARK env vars --- src/bin/bat/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 7ca08b9d..5a771cd6 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -307,6 +307,8 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { "BAT_STYLE", "BAT_TABS", "BAT_THEME", + bat::theme::env::BAT_THEME_DARK, + bat::theme::env::BAT_THEME_LIGHT, "COLORTERM", "LANG", "LC_ALL", From ba223289ce7325ddb0c95d22b4bf74e2fb7ee3e5 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 31 Mar 2026 22:23:10 +0300 Subject: [PATCH 031/130] Update --diagnostic output with detected terminal color scheme type --- src/bin/bat/main.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 5a771cd6..3cdf0531 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -285,7 +285,29 @@ fn run_controller(inputs: Vec, config: &Config, cache_dir: &Path) -> Resu #[cfg(feature = "bugreport")] fn invoke_bugreport(app: &App, cache_dir: &Path) { - use bugreport::{bugreport, collector::*, format::Markdown}; + use bugreport::{bugreport, collector::*, format::Markdown, report::ReportEntry}; + + struct ColorSchemeCollector; + + impl Collector for ColorSchemeCollector { + fn description(&self) -> &str { + "Detected terminal color scheme" + } + + fn collect( + &mut self, + _: &bugreport::CrateInfo, + ) -> std::result::Result { + let color_scheme = + bat::theme::color_scheme(bat::theme::DetectColorScheme::Always); + let text = match color_scheme { + Some(bat::theme::ColorScheme::Dark) => "dark", + Some(bat::theme::ColorScheme::Light) => "light", + None => "not detected", + }; + Ok(ReportEntry::Text(text.to_string())) + } + } let pager = bat::config::get_pager_executable( app.matches.get_one::("pager").map(|s| s.as_str()), ) @@ -328,6 +350,7 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { custom_assets_metadata, )) .info(DirectoryEntries::new("Custom assets", cache_dir)) + .info(ColorSchemeCollector) .info(CompileTimeInformation::default()); #[cfg(feature = "paging")] From 3e4169b8c3cc3b5d5824ff821279c5ce6872467a Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 31 Mar 2026 22:35:21 +0300 Subject: [PATCH 032/130] cargo fmt --- src/bin/bat/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 3cdf0531..a4a2c176 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -298,8 +298,7 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { &mut self, _: &bugreport::CrateInfo, ) -> std::result::Result { - let color_scheme = - bat::theme::color_scheme(bat::theme::DetectColorScheme::Always); + let color_scheme = bat::theme::color_scheme(bat::theme::DetectColorScheme::Always); let text = match color_scheme { Some(bat::theme::ColorScheme::Dark) => "dark", Some(bat::theme::ColorScheme::Light) => "light", From 31b0a357190eec0b9df23b5f6ee2115402ab3f78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 02:03:59 +0000 Subject: [PATCH 033/130] build(deps): bump unicode-segmentation from 1.12.0 to 1.13.2 Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.12.0 to 1.13.2. - [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.12.0...v1.13.2) --- updated-dependencies: - dependency-name: unicode-segmentation dependency-version: 1.13.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e777a2e..baedc438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1808,9 +1808,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" diff --git a/Cargo.toml b/Cargo.toml index 6c783336..4a8ecdc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,7 @@ bytesize = { version = "2.3.1" } encoding_rs = "0.8.35" execute = { version = "0.2.15", optional = true } terminal-colorsaurus = "1.0" -unicode-segmentation = "1.12.0" +unicode-segmentation = "1.13.2" itertools = "0.14.0" [dependencies.git2] From f820ad1050654b36d1244f06c534dc9f73665087 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 02:41:02 +0000 Subject: [PATCH 034/130] build(deps): bump unicode-width from 0.2.1 to 0.2.2 Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.2.1 to 0.2.2. - [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.2.1...v0.2.2) --- updated-dependencies: - dependency-name: unicode-width dependency-version: 0.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index baedc438..db7f3f36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1814,9 +1814,9 @@ checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unsafe-libyaml" diff --git a/Cargo.toml b/Cargo.toml index 4a8ecdc9..99a97871 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ minus = { version = "5.6", optional = true, features = [ "dynamic_output", "search", ] } -unicode-width = "0.2.1" +unicode-width = "0.2.2" globset = "0.4" serde = "1.0" serde_derive = "1.0" From de9ca4ef7b5e4a9b949684c2e0cc6872ec41a8eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:13:41 +0000 Subject: [PATCH 035/130] build(deps): bump toml from 0.9.8 to 1.1.1+spec-1.1.0 Bumps [toml](https://github.com/toml-rs/toml) from 0.9.8 to 1.1.1+spec-1.1.0. - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.8...toml-v1.1.1) --- updated-dependencies: - dependency-name: toml dependency-version: 1.1.1+spec-1.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db7f3f36..f7c76d47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1406,9 +1406,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -1757,9 +1757,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee" dependencies = [ "indexmap", "serde_core", @@ -1772,27 +1772,27 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "typenum" @@ -2186,9 +2186,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" [[package]] name = "wit-bindgen-rt" diff --git a/Cargo.toml b/Cargo.toml index 99a97871..68add205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,7 +119,7 @@ serde = "1.0" serde_derive = "1.0" serde_with = { version = "3.17.0", default-features = false, features = ["macros"] } syn = { version = "2.0.104", features = ["full"] } -toml = { version = "0.9.8", features = ["preserve_order"] } +toml = { version = "1.1.1", features = ["preserve_order"] } walkdir = "2.5" [build-dependencies.clap] From 57e99e9bcdcd4d58946c687d8b7d3684d5072890 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:38:08 +0000 Subject: [PATCH 036/130] build(deps): bump tempfile from 3.23.0 to 3.27.0 Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.23.0 to 3.27.0. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.23.0...v3.27.0) --- updated-dependencies: - dependency-name: tempfile dependency-version: 3.27.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7c76d47..62f29a10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,9 +951,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1324,14 +1324,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] @@ -1603,14 +1603,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.61.2", ] diff --git a/Cargo.toml b/Cargo.toml index 68add205..4d85a6e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ expect-test = "1.5.0" serial_test = { version = "2.0.0", default-features = false } predicates = "3.1.3" wait-timeout = "0.2.1" -tempfile = "3.23.0" +tempfile = "3.27.0" serde = { version = "1.0", features = ["derive"] } [target.'cfg(unix)'.dev-dependencies] From 956f0aeb08f8e4cefa59279e15eeb5be1ed41721 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:54:15 +0000 Subject: [PATCH 037/130] build(deps): bump quote from 1.0.40 to 1.0.45 Bumps [quote](https://github.com/dtolnay/quote) from 1.0.40 to 1.0.45. - [Release notes](https://github.com/dtolnay/quote/releases) - [Commits](https://github.com/dtolnay/quote/compare/1.0.40...1.0.45) --- updated-dependencies: - dependency-name: quote dependency-version: 1.0.45 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62f29a10..202a5295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,9 +1235,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] diff --git a/Cargo.toml b/Cargo.toml index 4d85a6e3..b63c4e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,7 +113,7 @@ itertools = "0.14.0" once_cell = "1.20" prettyplease = "0.2.37" proc-macro2 = "1.0.106" -quote = "1.0.40" +quote = "1.0.45" regex = "1.12.2" serde = "1.0" serde_derive = "1.0" From c64b6761feded76d519c91dda256e0779a4842b4 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Tue, 7 Apr 2026 23:04:24 +0200 Subject: [PATCH 038/130] Add .NET slnx extension This is the new XML-based format for solution files. --- CHANGELOG.md | 1 + src/syntax_mapping/builtins/common/50-dotnet-xml.toml | 2 +- tests/syntax-tests/highlighted/XML/solution.slnx | 7 +++++++ tests/syntax-tests/source/XML/solution.slnx | 7 +++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/syntax-tests/highlighted/XML/solution.slnx create mode 100644 tests/syntax-tests/source/XML/solution.slnx diff --git a/CHANGELOG.md b/CHANGELOG.md index cd21680c..c67cf148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## Other - Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536 +- Add .NET slnx extension, see #3682 (@ltrzesniewski) ## Features diff --git a/src/syntax_mapping/builtins/common/50-dotnet-xml.toml b/src/syntax_mapping/builtins/common/50-dotnet-xml.toml index 1e3a860a..70319f83 100644 --- a/src/syntax_mapping/builtins/common/50-dotnet-xml.toml +++ b/src/syntax_mapping/builtins/common/50-dotnet-xml.toml @@ -1,2 +1,2 @@ [mappings] -"XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets"] +"XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets", "*.slnx"] diff --git a/tests/syntax-tests/highlighted/XML/solution.slnx b/tests/syntax-tests/highlighted/XML/solution.slnx new file mode 100644 index 00000000..7946e72b --- /dev/null +++ b/tests/syntax-tests/highlighted/XML/solution.slnx @@ -0,0 +1,7 @@ +<Solution> + <Folder Name="/Build/"> + <File Path="Directory.Build.props" /> + <File Path="projectname.targets" /> +  + <Project Path="console.csproj" /> + diff --git a/tests/syntax-tests/source/XML/solution.slnx b/tests/syntax-tests/source/XML/solution.slnx new file mode 100644 index 00000000..e836bb51 --- /dev/null +++ b/tests/syntax-tests/source/XML/solution.slnx @@ -0,0 +1,7 @@ + + + + + + + From 2459aa94047279f9d7e6290010c6e8547b870962 Mon Sep 17 00:00:00 2001 From: Asish Kumar Date: Fri, 10 Apr 2026 14:35:33 +0000 Subject: [PATCH 039/130] Detect ZIP archives as binary content --- CHANGELOG.md | 1 + src/input.rs | 41 +++++++++++++++++++++++++++++++++----- tests/integration_tests.rs | 18 +++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c67cf148..faf869a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes +- Treat ZIP archives as binary content based on their magic header, see #0000 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) diff --git a/src/input.rs b/src/input.rs index 29846abe..30f13f98 100644 --- a/src/input.rs +++ b/src/input.rs @@ -261,11 +261,7 @@ impl<'a> InputReader<'a> { let mut first_line = vec![]; reader.read_until(b'\n', &mut first_line).ok(); - let content_type = if first_line.is_empty() { - None - } else { - Some(content_inspector::inspect(&first_line[..])) - }; + let content_type = inspect_content_type(&first_line); if content_type == Some(ContentType::UTF_16LE) { read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A).ok(); @@ -319,6 +315,25 @@ impl<'a> InputReader<'a> { } } +fn inspect_content_type(first_line: &[u8]) -> Option { + if first_line.is_empty() { + return None; + } + + let content_type = content_inspector::inspect(first_line); + if content_type == ContentType::UTF_8 && has_zip_signature(first_line) { + Some(ContentType::BINARY) + } else { + Some(content_type) + } +} + +fn has_zip_signature(bytes: &[u8]) -> bool { + [b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08"] + .into_iter() + .any(|signature| bytes.starts_with(signature)) +} + fn read_utf16_line( reader: &mut R, buf: &mut Vec, @@ -374,6 +389,22 @@ fn basic() { assert!(buffer.is_empty()); } +#[test] +fn zip_magic_headers_are_treated_as_binary() { + for content in [b"PK\x03\x04hello", b"PK\x05\x06hello", b"PK\x07\x08hello"] { + let reader = InputReader::new(&content[..]); + assert_eq!(Some(ContentType::BINARY), reader.content_type); + } +} + +#[test] +fn non_zip_pk_prefix_is_not_treated_as_binary() { + assert_eq!( + Some(ContentType::UTF_8), + inspect_content_type(b"PK\x03\x03hello") + ); +} + #[test] fn utf16le() { let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00"; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 96da50d7..d7e44301 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -2093,6 +2093,24 @@ fn header_binary() { .stderr(""); } +#[test] +fn header_zip_file_is_binary() { + let tmp_dir = tempdir().expect("can create temporary directory"); + let tmp_path = tmp_dir.path().join("test.zip"); + std::fs::write(&tmp_path, b"PK\x03\x04hello").expect("can write temporary file"); + + bat() + .arg(&tmp_path) + .arg("--decorations=always") + .arg("--style=header") + .arg("-r=0:0") + .arg("--file-name=test.zip") + .assert() + .success() + .stdout("File: test.zip \n") + .stderr(""); +} + #[test] fn header_full_binary() { bat() From b3aec318cf1fc16edefe8a90d78e3eb2f62100f8 Mon Sep 17 00:00:00 2001 From: Asish Kumar Date: Fri, 10 Apr 2026 14:37:00 +0000 Subject: [PATCH 040/130] Update changelog entry for #3686 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf869a5..796aac21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) ## Bugfixes -- Treat ZIP archives as binary content based on their magic header, see #0000 (@officialasishkumar) +- Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) - Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman) From a9137fab05f8204804c5cfecf480f4c8f2d7ab82 Mon Sep 17 00:00:00 2001 From: Asish Kumar Date: Fri, 10 Apr 2026 14:35:33 +0000 Subject: [PATCH 041/130] Support BAT_WIDTH as an alias for --terminal-width Signed-off-by: Asish Kumar --- CHANGELOG.md | 1 + doc/long-help.txt | 3 ++- src/bin/bat/clap_app.rs | 8 ++++-- src/bin/bat/config.rs | 1 + src/bin/bat/main.rs | 1 + tests/integration_tests.rs | 52 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 796aac21..0539669b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) +- Support configuring `--terminal-width` via `BAT_WIDTH`, see #0000 (@officialasishkumar) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) - Added an initial `flake.nix` for a ready made development environment; see #3578 (@vorburger) - Add `--quiet-empty` (`-E`) flag to suppress output when input is empty. Closes #1936, see #3563 (@NORMAL-EX) diff --git a/doc/long-help.txt b/doc/long-help.txt index 82878acd..7d3cc0e5 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -77,7 +77,8 @@ Options: --terminal-width Explicitly set the width of the terminal instead of determining it automatically. If prefixed with '+' or '-', the value will be treated as an offset to the actual terminal - width. See also: '--wrap'. + width. This can also be configured via the BAT_WIDTH environment variable (e.g. export + BAT_WIDTH="100"). See also: '--wrap'. -n, --number Only show line numbers, no other decorations. This is an alias for '--style=numbers' diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 3636f081..6dfdf829 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -240,6 +240,7 @@ pub fn build_app(interactive_output: bool) -> Command { .arg( Arg::new("terminal-width") .long("terminal-width") + .overrides_with("terminal-width") .value_name("width") .hide_short_help(true) .allow_hyphen_values(true) @@ -255,10 +256,13 @@ pub fn build_app(interactive_output: bool) -> Command { }) .map_err(|e| e.to_string()) }) - .help( + .help("Explicitly set the width of the terminal instead of determining it automatically.") + .long_help( "Explicitly set the width of the terminal instead of determining it \ automatically. If prefixed with '+' or '-', the value will be treated \ - as an offset to the actual terminal width. See also: '--wrap'.", + as an offset to the actual terminal width. This can also be configured \ + via the BAT_WIDTH environment variable (e.g. export BAT_WIDTH=\"100\"). \ + See also: '--wrap'.", ), ) .arg( diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 77d691c3..3e2fb72e 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -153,6 +153,7 @@ fn get_args_from_str(content: &str) -> Result, shell_words::ParseE pub fn get_args_from_env_vars() -> Vec { [ ("--tabs", "BAT_TABS"), + ("--terminal-width", "BAT_WIDTH"), ("--theme", bat::theme::env::BAT_THEME), ("--theme-dark", bat::theme::env::BAT_THEME_DARK), ("--theme-light", bat::theme::env::BAT_THEME_LIGHT), diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index a4a2c176..f3fa01f4 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -328,6 +328,7 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { "BAT_STYLE", "BAT_TABS", "BAT_THEME", + "BAT_WIDTH", bat::theme::env::BAT_THEME_DARK, bat::theme::env::BAT_THEME_LIGHT, "COLORTERM", diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d7e44301..8ae4cfdd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1047,6 +1047,57 @@ fn tabs_4_arg_overrides_env_noconfig() { ); } +#[test] +fn terminal_width_env_var_is_respected() { + let tmp_dir = tempdir().expect("can create temporary directory"); + let tmp_path = tmp_dir.path().join("long.txt"); + std::fs::write( + &tmp_path, + "0123456789abcdef0123456789abcdef0123456789abcdef\n", + ) + .expect("can write temporary file"); + + bat() + .env("BAT_WIDTH", "20") + .arg(&tmp_path) + .arg("--paging=never") + .arg("--color=never") + .arg("--style=numbers") + .arg("--decorations=always") + .arg("--wrap=character") + .assert() + .success() + .stdout(" 1 0123456789abcde\n f0123456789abcd\n ef0123456789abc\n def\n") + .stderr(""); +} + +#[test] +fn terminal_width_arg_overrides_env() { + let tmp_dir = tempdir().expect("can create temporary directory"); + let tmp_path = tmp_dir.path().join("long.txt"); + std::fs::write( + &tmp_path, + "0123456789abcdef0123456789abcdef0123456789abcdef\n", + ) + .expect("can write temporary file"); + + bat() + .env("BAT_WIDTH", "20") + .arg(&tmp_path) + .arg("--paging=never") + .arg("--color=never") + .arg("--style=numbers") + .arg("--decorations=always") + .arg("--wrap=character") + .arg("--terminal-width=10") + .assert() + .success() + .stdout( + " 1 01234\n 56789\n abcde\n f0123\n 45678\n 9abcd\n ef012\n 34567\n 89abc\n def\n", + ) + .stderr(""); +} + #[test] fn fail_non_existing() { bat().arg("non-existing-file").assert().failure(); @@ -1474,6 +1525,7 @@ fn diagnostic_sanity_check() { .assert() .success() .stdout(predicate::str::contains("BAT_PAGER=")) + .stdout(predicate::str::contains("BAT_WIDTH=")) .stderr(""); } From 01174b31f5276d5142fdcb4f74a8815ff6c4ea4f Mon Sep 17 00:00:00 2001 From: Asish Kumar Date: Fri, 10 Apr 2026 14:36:49 +0000 Subject: [PATCH 042/130] Update changelog entry for #3687 Signed-off-by: Asish Kumar --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0539669b..ee313425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) -- Support configuring `--terminal-width` via `BAT_WIDTH`, see #0000 (@officialasishkumar) +- Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) - Added an initial `flake.nix` for a ready made development environment; see #3578 (@vorburger) - Add `--quiet-empty` (`-E`) flag to suppress output when input is empty. Closes #1936, see #3563 (@NORMAL-EX) From db647c98134e4a2aaeded2deee6d32f1dc7a9c27 Mon Sep 17 00:00:00 2001 From: orbisai0security Date: Sun, 12 Apr 2026 11:43:12 +0000 Subject: [PATCH 043/130] fix: V-001 security vulnerability Automated security fix generated by Orbis Security AI --- tests/snapshots/generate_snapshots.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/snapshots/generate_snapshots.py b/tests/snapshots/generate_snapshots.py index bb7d69ba..f8c14541 100755 --- a/tests/snapshots/generate_snapshots.py +++ b/tests/snapshots/generate_snapshots.py @@ -23,18 +23,19 @@ def generate_style_snapshot(style): def generate_snapshot(name, arguments): - command = "cargo run -- --paging=never --color=never --decorations=always " - command += "{args} sample.rs > output/{name}.snapshot.txt".format( - name=name, - args=arguments - ) + output_file = "output/{name}.snapshot.txt".format(name=name) + command = [ + "cargo", "run", "--", "--paging=never", "--color=never", + "--decorations=always", arguments, "sample.rs" + ] print("generating snapshot for {}".format(name)) - subprocess.call(command, shell=True) + with open(output_file, "w") as f: + subprocess.call(command, stdout=f) def build_bat(): print("building bat") - subprocess.call("cargo build", cwd="../..", shell=True) + subprocess.call(["cargo", "build"], cwd="../..") def prepare_output_dir(): @@ -49,7 +50,7 @@ def modify_sample_file(): def undo_sample_file_modification(): print("undoing sample.rs modifications") - subprocess.call("git checkout -- sample.rs", shell=True) + subprocess.call(["git", "checkout", "--", "sample.rs"]) build_bat() From 6876a782daf2055b3ab743885b3868e3c268b66e Mon Sep 17 00:00:00 2001 From: Kira Security Bot Date: Sun, 12 Apr 2026 12:37:43 +0000 Subject: [PATCH 044/130] Apply code changes: @orbisai0security can you address code review comm... --- tests/snapshots/generate_snapshots.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) mode change 100755 => 100644 tests/snapshots/generate_snapshots.py diff --git a/tests/snapshots/generate_snapshots.py b/tests/snapshots/generate_snapshots.py old mode 100755 new mode 100644 index f8c14541..db7839e9 --- a/tests/snapshots/generate_snapshots.py +++ b/tests/snapshots/generate_snapshots.py @@ -4,6 +4,7 @@ import itertools import subprocess import pathlib import shutil +from typing import Iterable def generate_snapshots(): @@ -19,14 +20,14 @@ def generate_snapshots(): def generate_style_snapshot(style): - generate_snapshot(style.replace(",", "_"), "--style={}".format(style)) + generate_snapshot(style.replace(",", "_"), ["--style={}".format(style)]) -def generate_snapshot(name, arguments): +def generate_snapshot(name: str, arguments: Iterable[str]): output_file = "output/{name}.snapshot.txt".format(name=name) command = [ "cargo", "run", "--", "--paging=never", "--color=never", - "--decorations=always", arguments, "sample.rs" + "--decorations=always", *arguments, "sample.rs" ] print("generating snapshot for {}".format(name)) with open(output_file, "w") as f: From e89c515e9af81cb1058f3074cf01c08b952e69ed Mon Sep 17 00:00:00 2001 From: Jan Larres Date: Mon, 13 Apr 2026 17:52:03 +1200 Subject: [PATCH 045/130] [Python] Support uv as script runner in shebang --- CHANGELOG.md | 1 + assets/patches/Python.sublime-syntax.patch | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee313425..4fc4e54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Improve native man pages and command help syntax highlighting by stripping overstriking, see #3517 (@akirk) - Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir) - Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger) +- Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes - Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) diff --git a/assets/patches/Python.sublime-syntax.patch b/assets/patches/Python.sublime-syntax.patch index 36a50894..bacb9db9 100644 --- a/assets/patches/Python.sublime-syntax.patch +++ b/assets/patches/Python.sublime-syntax.patch @@ -2,6 +2,15 @@ diff --git syntaxes/01_Packages/Python/Python.sublime-syntax syntaxes/01_Package index 2acd86d8..86257f7b 100644 --- syntaxes/01_Packages/Python/Python.sublime-syntax +++ syntaxes/01_Packages/Python/Python.sublime-syntax +@@ -25,7 +31,7 @@ file_extensions: + - wscript + - bazel + - bzl +-first_line_match: ^#!\s*/.*\bpython(\d(\.\d)?)?\b ++first_line_match: ^#!\s*/.*\b(python(\d(\.\d)?)?|uv)\b + scope: source.python + + variables: @@ -988,10 +988,6 @@ contexts: - match: \} scope: punctuation.section.mapping-or-set.end.python From 52763e0205f00b9b4f388e9f994aebe0fa0b41b5 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:04:26 -0700 Subject: [PATCH 046/130] feat: add tclsh, wish, and expect shebang detection for Tcl syntax Add first_line_match to the Tcl syntax definition via a patch file, enabling automatic Tcl highlighting for scripts with tclsh, wish, or expect shebangs. Fixes #3513 --- CHANGELOG.md | 1 + assets/patches/Tcl.sublime-syntax.patch | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 assets/patches/Tcl.sublime-syntax.patch diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc4e54e..3682bb82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ## Features - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) +- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3513 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar) diff --git a/assets/patches/Tcl.sublime-syntax.patch b/assets/patches/Tcl.sublime-syntax.patch new file mode 100644 index 00000000..487a1841 --- /dev/null +++ b/assets/patches/Tcl.sublime-syntax.patch @@ -0,0 +1,12 @@ +diff --git syntaxes/01_Packages/TCL/Tcl.sublime-syntax syntaxes/01_Packages/TCL/Tcl.sublime-syntax +index 1234567..abcdefg 100644 +--- syntaxes/01_Packages/TCL/Tcl.sublime-syntax ++++ syntaxes/01_Packages/TCL/Tcl.sublime-syntax +@@ -3,6 +3,7 @@ + # http://www.sublimetext.com/docs/3/syntax.html + name: Tcl + file_extensions: + - tcl ++first_line_match: ^\#!.*\b(tclsh|wish|expect)\b + scope: source.tcl + variables: From 1f89178fce7dde0f6241091fff241768317a9c46 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:06:42 -0700 Subject: [PATCH 047/130] fix: reference PR number in changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3682bb82..79b65708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,12 @@ ## Features +<<<<<<< HEAD - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) - Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3513 (@mvanhorn) +======= +- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3647 (@mvanhorn) +>>>>>>> 785fff9 (fix: reference PR number in changelog entry) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar) From e070d105b57f854d982212265e2e52d6ea6d24ec Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 26 Mar 2026 22:22:54 -0700 Subject: [PATCH 048/130] test: add shebang regression tests and move changelog to Syntaxes section Add extensionless regression test files for tclsh, wish, and expect shebangs so syntect bumps don't silently break first-line detection. Move changelog entry from Features to Syntaxes per reviewer suggestion. Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 6 ++-- .../regression_tests/issue_3647_expect | 2 ++ .../regression_tests/issue_3647_tclsh | 2 ++ .../examples/regression_tests/issue_3647_wish | 2 ++ tests/integration_tests.rs | 31 +++++++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 tests/examples/regression_tests/issue_3647_expect create mode 100644 tests/examples/regression_tests/issue_3647_tclsh create mode 100644 tests/examples/regression_tests/issue_3647_wish diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b65708..99222693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,9 @@ <<<<<<< HEAD - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) -- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3513 (@mvanhorn) -======= - Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3647 (@mvanhorn) ->>>>>>> 785fff9 (fix: reference PR number in changelog entry) +======= +>>>>>>> 29c913f (test: add shebang regression tests and move changelog to Syntaxes section) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar) @@ -50,6 +49,7 @@ ## Syntaxes +- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3647 (@mvanhorn) - Change the URL of Zig submodule from GitHub to Codeberg, see #3519 (@sorairolake) - Don't color strings inside CSV files, to make it easier to tell which column they belong to, see #3521 (@keith-hall) - Add syntax highlighting support for COBOL, see #3584 (@adukhan99) diff --git a/tests/examples/regression_tests/issue_3647_expect b/tests/examples/regression_tests/issue_3647_expect new file mode 100644 index 00000000..3786df69 --- /dev/null +++ b/tests/examples/regression_tests/issue_3647_expect @@ -0,0 +1,2 @@ +#!/usr/bin/expect -f +set timeout 30 diff --git a/tests/examples/regression_tests/issue_3647_tclsh b/tests/examples/regression_tests/issue_3647_tclsh new file mode 100644 index 00000000..0f4b3c5c --- /dev/null +++ b/tests/examples/regression_tests/issue_3647_tclsh @@ -0,0 +1,2 @@ +#!/usr/bin/env tclsh +puts "Hello from tclsh" diff --git a/tests/examples/regression_tests/issue_3647_wish b/tests/examples/regression_tests/issue_3647_wish new file mode 100644 index 00000000..8bfe4baf --- /dev/null +++ b/tests/examples/regression_tests/issue_3647_wish @@ -0,0 +1,2 @@ +#!/usr/bin/wish +button .b -text "Click" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8ae4cfdd..9f18f598 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4201,4 +4201,35 @@ fn plain_without_diff_still_works() { .assert() .success() .stdout("line 1\nline 2 modified\nline 3\nline 4 added\n"); +#[test] +fn tcl_shebang_detection_tclsh() { + bat() + .arg("--color=always") + .arg("--style=plain") + .arg("--decorations=always") + .arg("regression_tests/issue_3647_tclsh") + .assert() + .success(); +} + +#[test] +fn tcl_shebang_detection_wish() { + bat() + .arg("--color=always") + .arg("--style=plain") + .arg("--decorations=always") + .arg("regression_tests/issue_3647_wish") + .assert() + .success(); +} + +#[test] +fn tcl_shebang_detection_expect() { + bat() + .arg("--color=always") + .arg("--style=plain") + .arg("--decorations=always") + .arg("regression_tests/issue_3647_expect") + .assert() + .success(); } From cafad6b03668abc1ab41e1e10e72fd449ac609e4 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:18:30 -0700 Subject: [PATCH 049/130] test: add highlighted outputs for Tcl shebang regression tests Generate highlighted test outputs for tclsh, wish, and expect shebang detection files to prevent regressions. --- CHANGELOG.md | 4 ---- tests/integration_tests.rs | 2 ++ tests/syntax-tests/highlighted/Tcl/expect_shebang | 7 +++++++ tests/syntax-tests/highlighted/Tcl/tclsh_shebang | 7 +++++++ tests/syntax-tests/highlighted/Tcl/wish_shebang | 5 +++++ 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 tests/syntax-tests/highlighted/Tcl/expect_shebang create mode 100644 tests/syntax-tests/highlighted/Tcl/tclsh_shebang create mode 100644 tests/syntax-tests/highlighted/Tcl/wish_shebang diff --git a/CHANGELOG.md b/CHANGELOG.md index 99222693..8049cba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,7 @@ ## Features -<<<<<<< HEAD - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) -- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3647 (@mvanhorn) -======= ->>>>>>> 29c913f (test: add shebang regression tests and move changelog to Syntaxes section) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 9f18f598..432faed8 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4201,6 +4201,8 @@ fn plain_without_diff_still_works() { .assert() .success() .stdout("line 1\nline 2 modified\nline 3\nline 4 added\n"); +} + #[test] fn tcl_shebang_detection_tclsh() { bat() diff --git a/tests/syntax-tests/highlighted/Tcl/expect_shebang b/tests/syntax-tests/highlighted/Tcl/expect_shebang new file mode 100644 index 00000000..22f2101e --- /dev/null +++ b/tests/syntax-tests/highlighted/Tcl/expect_shebang @@ -0,0 +1,7 @@ +#!/usr/bin/expect -f +# Expect script detected via expect shebang +set timeout 30 +spawn ssh user@host +expect "password:" +send "secret\r" +expect eof diff --git a/tests/syntax-tests/highlighted/Tcl/tclsh_shebang b/tests/syntax-tests/highlighted/Tcl/tclsh_shebang new file mode 100644 index 00000000..de50978f --- /dev/null +++ b/tests/syntax-tests/highlighted/Tcl/tclsh_shebang @@ -0,0 +1,7 @@ +#!/usr/bin/env tclsh +# Tcl script detected via tclsh shebang +puts "Hello from tclsh" +set x 42 +if {$x > 0} { + puts "positive" +} diff --git a/tests/syntax-tests/highlighted/Tcl/wish_shebang b/tests/syntax-tests/highlighted/Tcl/wish_shebang new file mode 100644 index 00000000..134b185d --- /dev/null +++ b/tests/syntax-tests/highlighted/Tcl/wish_shebang @@ -0,0 +1,5 @@ +#!/usr/bin/wish +# Tk script detected via wish shebang +package require Tk +button .b -text "Click" -command {puts "clicked"} +pack .b From 77ea750e662d6dd21196acec1cae32517fe57dac Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sat, 18 Apr 2026 06:58:45 -0700 Subject: [PATCH 050/130] test(Tcl): add shebang source files for regression test CI reported FileNotFoundError for Tcl/{tclsh,expect,wish}_shebang in tests/syntax-tests/source/Tcl/. The highlighted files existed but the source files had been omitted, so create_highlighted_versions.py had nothing to read from. Source files match the shebang regression test inputs at tests/examples/regression_tests/issue_3647_*. --- tests/syntax-tests/source/Tcl/expect_shebang | 7 +++++++ tests/syntax-tests/source/Tcl/tclsh_shebang | 7 +++++++ tests/syntax-tests/source/Tcl/wish_shebang | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 tests/syntax-tests/source/Tcl/expect_shebang create mode 100644 tests/syntax-tests/source/Tcl/tclsh_shebang create mode 100644 tests/syntax-tests/source/Tcl/wish_shebang diff --git a/tests/syntax-tests/source/Tcl/expect_shebang b/tests/syntax-tests/source/Tcl/expect_shebang new file mode 100644 index 00000000..ef288ba8 --- /dev/null +++ b/tests/syntax-tests/source/Tcl/expect_shebang @@ -0,0 +1,7 @@ +#!/usr/bin/expect -f +# Expect script detected via expect shebang +set timeout 30 +spawn ssh user@host +expect "password:" +send "secret\r" +expect eof diff --git a/tests/syntax-tests/source/Tcl/tclsh_shebang b/tests/syntax-tests/source/Tcl/tclsh_shebang new file mode 100644 index 00000000..08faa4d6 --- /dev/null +++ b/tests/syntax-tests/source/Tcl/tclsh_shebang @@ -0,0 +1,7 @@ +#!/usr/bin/env tclsh +# Tcl script detected via tclsh shebang +puts "Hello from tclsh" +set x 42 +if {$x > 0} { + puts "positive" +} diff --git a/tests/syntax-tests/source/Tcl/wish_shebang b/tests/syntax-tests/source/Tcl/wish_shebang new file mode 100644 index 00000000..690e73ef --- /dev/null +++ b/tests/syntax-tests/source/Tcl/wish_shebang @@ -0,0 +1,5 @@ +#!/usr/bin/wish +# Tk script detected via wish shebang +package require Tk +button .b -text "Click" -command {puts "clicked"} +pack .b From 0ecdeb28d43c7a259c59cc35dd5e1f0c2a266d3e Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sat, 18 Apr 2026 09:42:26 -0700 Subject: [PATCH 051/130] test(Tcl): regenerate shebang golden files to match patched syntax The Tcl.sublime-syntax.patch adds first_line_match so bat recognizes tclsh/wish/expect shebang files as Tcl. With the patch applied, the leading '#!/...' and '# comment' lines get tokenized as Tcl comments and rendered in Monokai-Extended's comment color instead of default foreground. The goldens were generated against unpatched bat in the original commit, so CI's 'Run tests with updated syntaxes and themes' job (which runs assets/create.sh before the regression test) disagreed. Regenerate against the patched build so the regression test passes. --- tests/syntax-tests/highlighted/Tcl/expect_shebang | 14 +++++++------- tests/syntax-tests/highlighted/Tcl/tclsh_shebang | 12 ++++++------ tests/syntax-tests/highlighted/Tcl/wish_shebang | 10 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/syntax-tests/highlighted/Tcl/expect_shebang b/tests/syntax-tests/highlighted/Tcl/expect_shebang index 22f2101e..871eed6c 100644 --- a/tests/syntax-tests/highlighted/Tcl/expect_shebang +++ b/tests/syntax-tests/highlighted/Tcl/expect_shebang @@ -1,7 +1,7 @@ -#!/usr/bin/expect -f -# Expect script detected via expect shebang -set timeout 30 -spawn ssh user@host -expect "password:" -send "secret\r" -expect eof +#!/usr/bin/expect -f +# Expect script detected via expect shebang +set timeout 30 +spawn ssh user@host +expect "password:" +send "secret\r" +expect eof diff --git a/tests/syntax-tests/highlighted/Tcl/tclsh_shebang b/tests/syntax-tests/highlighted/Tcl/tclsh_shebang index de50978f..5461f5f8 100644 --- a/tests/syntax-tests/highlighted/Tcl/tclsh_shebang +++ b/tests/syntax-tests/highlighted/Tcl/tclsh_shebang @@ -1,7 +1,7 @@ -#!/usr/bin/env tclsh -# Tcl script detected via tclsh shebang -puts "Hello from tclsh" -set x 42 -if {$x > 0} { - puts "positive" +#!/usr/bin/env tclsh +# Tcl script detected via tclsh shebang +puts "Hello from tclsh" +set x 42 +if {$x > 0} { + puts "positive" } diff --git a/tests/syntax-tests/highlighted/Tcl/wish_shebang b/tests/syntax-tests/highlighted/Tcl/wish_shebang index 134b185d..94fb971f 100644 --- a/tests/syntax-tests/highlighted/Tcl/wish_shebang +++ b/tests/syntax-tests/highlighted/Tcl/wish_shebang @@ -1,5 +1,5 @@ -#!/usr/bin/wish -# Tk script detected via wish shebang -package require Tk -button .b -text "Click" -command {puts "clicked"} -pack .b +#!/usr/bin/wish +# Tk script detected via wish shebang +package require Tk +button .b -text "Click" -command {puts "clicked"} +pack .b From ea7fafca1e6191dbfc4c55f039930e350b4412b2 Mon Sep 17 00:00:00 2001 From: Barry <100205797+barry3406@users.noreply.github.com> Date: Sun, 19 Apr 2026 14:19:00 -0700 Subject: [PATCH 052/130] windows: statically link the CRT to remove vcruntime dependency Add a .cargo/config.toml that sets target-feature=+crt-static for the i686, x86_64, and aarch64 MSVC targets so bat.exe no longer depends on the vcruntime DLL. Mirrors the fix applied to fd in sharkdp/fd#1891. Closes #3634 --- .cargo/config.toml | 11 +++++++++++ CHANGELOG.md | 1 + 2 files changed, 12 insertions(+) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..67a37e28 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,11 @@ +# On Windows MSVC, statically link the C runtime so that the resulting EXE does +# not depend on the vcruntime DLL. +# +# See: https://github.com/sharkdp/bat/issues/3634 + +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] +[target.i686-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] +[target.aarch64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 8049cba3..5a37d298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ - Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX) - Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall) - Builtin syntax mapping: cleanup matcher glob parsing logic #3652 (@cyqsimon) +- Statically link the CRT for MSVC builds via Cargo config to avoid runtime DLL dependencies. Closes #3634, see #3692 (@barry3406) ## Syntaxes From 71c894e843522fd1804283b7bd347cb2c31fad21 Mon Sep 17 00:00:00 2001 From: curious-rabbit Date: Mon, 20 Apr 2026 14:25:49 +0200 Subject: [PATCH 053/130] santize filenames --- CHANGELOG.md | 1 + src/assets.rs | 16 +++++++---- src/bin/bat/main.rs | 4 ++- src/input.rs | 16 +++++------ src/lib.rs | 1 + src/preprocessor.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++ src/printer.rs | 12 ++++++--- 7 files changed, 98 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc4e54e..18a322a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- Sanitize control characters in filenames before displaying them in the file header, error messages, and the terminal title, preventing ANSI escape injection via crafted filenames. Closes #3054, see #3691 (@curious-rabbit) - Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) diff --git a/src/assets.rs b/src/assets.rs index 29247bd7..9483f032 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -158,7 +158,9 @@ impl HighlightingAssets { let syntax_match = mapping.get_syntax_for(path); if let Some(MappingTarget::MapToUnknown) = syntax_match { - return Err(Error::UndetectedSyntax(path.to_string_lossy().into())); + return Err(Error::UndetectedSyntax( + crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()), + )); } if let Some(MappingTarget::MapTo(syntax_name)) = syntax_match { @@ -175,13 +177,17 @@ impl HighlightingAssets { ) { (Some(syntax), _) => Ok(syntax), - (_, Some(MappingTarget::MapExtensionToUnknown)) => { - Err(Error::UndetectedSyntax(path.to_string_lossy().into())) - } + (_, Some(MappingTarget::MapExtensionToUnknown)) => Err(Error::UndetectedSyntax( + crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()), + )), _ => self .get_syntax_for_file_extension(file_name, &mapping.ignored_suffixes)? - .ok_or_else(|| Error::UndetectedSyntax(path.to_string_lossy().into())), + .ok_or_else(|| { + Error::UndetectedSyntax(crate::preprocessor::sanitize_for_terminal( + &path.to_string_lossy(), + )) + }), } } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index f3fa01f4..71ed8527 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -259,7 +259,9 @@ pub fn list_themes( 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}"); + // Prevent BEL/ESC/C1 bytes in the title from terminating or nesting the OSC. + let safe_title = bat::sanitize_for_terminal(&new_terminal_title); + print!("{osc_command_for_setting_terminal_title}{safe_title}{osc_end_command}"); io::stdout().flush().unwrap(); } diff --git a/src/input.rs b/src/input.rs index 30f13f98..231e2a34 100644 --- a/src/input.rs +++ b/src/input.rs @@ -216,20 +216,20 @@ impl<'a> Input<'a> { description, metadata: self.metadata, reader: { - let mut file = File::open(&path) - .map_err(|e| format!("'{}': {e}", path.to_string_lossy()))?; + let path_display = + crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()); + let mut file = + File::open(&path).map_err(|e| format!("'{path_display}': {e}"))?; if file.metadata()?.is_dir() { - return Err(format!("'{}' is a directory.", path.to_string_lossy()).into()); + return Err(format!("'{path_display}' is a directory.").into()); } if let Some(stdout) = stdout_identifier { - let input_identifier = Identifier::try_from(file).map_err(|e| { - format!("{}: Error identifying file: {e}", path.to_string_lossy()) - })?; + let input_identifier = Identifier::try_from(file) + .map_err(|e| format!("{path_display}: Error identifying file: {e}"))?; if stdout.surely_conflicts_with(&input_identifier) { return Err(format!( - "IO circle detected. The input from '{}' is also an output. Aborting to avoid infinite loop.", - path.to_string_lossy() + "IO circle detected. The input from '{path_display}' is also an output. Aborting to avoid infinite loop.", ) .into()); } diff --git a/src/lib.rs b/src/lib.rs index 4c60f10e..685214dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ mod vscreen; pub(crate) mod wrapping; pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation}; +pub use preprocessor::sanitize_for_terminal; pub use preprocessor::StripAnsiMode; pub use pretty_printer::{Input, PrettyPrinter, Syntax}; pub use syntax_mapping::{MappingTarget, SyntaxMapping}; diff --git a/src/preprocessor.rs b/src/preprocessor.rs index 6b4e2935..a34f9f9e 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -149,6 +149,35 @@ pub fn strip_ansi(line: &str) -> String { buffer } +/// Escape C0, DEL, and C1 control characters so a string from an untrusted +/// filename or path can be safely written to the terminal. +pub fn sanitize_for_terminal(input: &str) -> String { + if !input + .chars() + .any(|c| matches!(c, '\x00'..='\x08' | '\x0A'..='\x1F' | '\x7F'..='\u{9F}')) + { + return input.to_owned(); + } + + let mut out = String::with_capacity(input.len() + 8); + for c in input.chars() { + match c { + '\t' => out.push('\t'), + '\x00'..='\x1F' => { + out.push('^'); + out.push(char::from_u32(0x40 + c as u32).unwrap_or('?')); + } + '\x7F' => out.push_str("^?"), + '\u{80}'..='\u{9F}' => { + use std::fmt::Write as _; + let _ = write!(out, "\\u{{{:x}}}", c as u32); + } + other => out.push(other), + } + } + out +} + /// Strips overstrike sequences (backspace formatting) from input. /// /// Overstrike formatting is used by man pages and some help output: @@ -261,3 +290,40 @@ fn test_strip_overstrike() { // Unicode with overstrike assert_eq!(strip_overstrike("ä\x08äöü", 2), "äöü"); } + +#[test] +fn test_sanitize_for_terminal_passthrough() { + assert_eq!(sanitize_for_terminal(""), ""); + assert_eq!(sanitize_for_terminal("hello.txt"), "hello.txt"); + assert_eq!(sanitize_for_terminal("résumé.pdf"), "résumé.pdf"); + assert_eq!(sanitize_for_terminal("日本語.md"), "日本語.md"); + assert_eq!( + sanitize_for_terminal("path/with spaces/file.log"), + "path/with spaces/file.log" + ); + assert_eq!(sanitize_for_terminal("a\tb"), "a\tb"); +} + +#[test] +fn test_sanitize_for_terminal_c0_controls() { + assert_eq!( + sanitize_for_terminal("\x1b[31mINJECTED\x1b[0m.txt"), + "^[[31mINJECTED^[[0m.txt" + ); + assert_eq!(sanitize_for_terminal("bad\x07rest"), "bad^Grest"); + assert_eq!(sanitize_for_terminal("\x00\x08\n\r\x7F"), "^@^H^J^M^?"); + assert_eq!(sanitize_for_terminal("\u{9b}31m"), "\\u{9b}31m"); + assert_eq!( + sanitize_for_terminal("\u{9d}0;pwned\x07"), + "\\u{9d}0;pwned^G" + ); +} + +#[test] +fn test_sanitize_for_terminal_idempotent_on_sanitized() { + let dirty = "\x1b]0;pwned\x07file.txt"; + let clean = sanitize_for_terminal(dirty); + assert_eq!(sanitize_for_terminal(&clean), clean); + assert!(!clean.contains('\x1b')); + assert!(!clean.contains('\x07')); +} diff --git a/src/printer.rs b/src/printer.rs index c58c914d..17f7aa30 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -29,7 +29,9 @@ use crate::error::*; use crate::input::OpenedInput; use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult}; use crate::output::OutputHandle; -use crate::preprocessor::{expand_tabs, replace_nonprintable, strip_ansi, strip_overstrike}; +use crate::preprocessor::{ + expand_tabs, replace_nonprintable, sanitize_for_terminal, strip_ansi, strip_overstrike, +}; use crate::style::StyleComponent; use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator}; @@ -489,7 +491,7 @@ impl Printer for InteractivePrinter<'_> { (but will be present if the output of 'bat' is piped). You can use 'bat -A' \ to show the binary file contents.", Yellow.paint("[bat warning]"), - input.description.summary(), + sanitize_for_terminal(&input.description.summary()), )?; } else if self.config.style_components.grid() { self.print_horizontal_line(handle, '┬')?; @@ -543,9 +545,11 @@ impl Printer for InteractivePrinter<'_> { "{}{}{mode}", description .kind() - .map(|kind| format!("{kind}: ")) + .map(|kind| format!("{}: ", sanitize_for_terminal(kind))) .unwrap_or_else(|| "".into()), - self.colors.header_value.paint(description.title()), + self.colors + .header_value + .paint(sanitize_for_terminal(description.title())), ); self.print_header_multiline_component(handle, &header_filename) } From c3df0e6a883f128974e4ccb7d71555cf4166045b Mon Sep 17 00:00:00 2001 From: Ish West Date: Sat, 25 Apr 2026 17:37:36 +0200 Subject: [PATCH 054/130] Fixed a bug with hardcoded terminal probing when `--list-themes` is called --- src/bin/bat/app.rs | 2 +- src/bin/bat/main.rs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index f95bbb91..3124cdcd 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -645,7 +645,7 @@ impl App { Ok(styled_components) } - fn theme_options(&self) -> ThemeOptions { + pub(crate) fn theme_options(&self) -> ThemeOptions { Self::theme_options_from_matches(&self.matches) } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index f3fa01f4..5184cf99 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -17,7 +17,6 @@ use std::path::Path; use std::process; use bat::output::{OutputHandle, OutputType}; -use bat::theme::DetectColorScheme; use nu_ansi_term::Color::Green; use nu_ansi_term::Style; @@ -39,7 +38,7 @@ use bat::{ error::*, input::Input, style::{StyleComponent, StyleComponents}, - theme::{color_scheme, default_theme, ColorScheme}, + theme::{default_theme, theme, ColorScheme, ThemeOptions}, MappingTarget, PagingMode, }; @@ -197,7 +196,7 @@ pub fn list_themes( cfg: &Config, config_dir: &Path, cache_dir: &Path, - detect_color_scheme: DetectColorScheme, + theme_options: ThemeOptions, ) -> Result<()> { let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?; let mut config = cfg.clone(); @@ -206,7 +205,7 @@ pub fn list_themes( config.language = Some("Rust"); config.style_components = StyleComponents(style); - let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default()); + let default_theme_name = theme(theme_options).to_string(); let mut buf = String::new(); let mut handle = OutputHandle::FmtWrite(&mut buf); @@ -426,7 +425,7 @@ fn run() -> Result { }; run_controller(inputs, &plain_config, cache_dir) } else if app.matches.get_flag("list-themes") { - list_themes(&config, config_dir, cache_dir, DetectColorScheme::default())?; + list_themes(&config, config_dir, cache_dir, app.theme_options())?; Ok(true) } else if app.matches.get_flag("config-file") { println!("{}", config_file().to_string_lossy()); From 044d445adc5b8136bee14ab21830e43836edffd5 Mon Sep 17 00:00:00 2001 From: Ish West Date: Sat, 25 Apr 2026 21:17:02 +0200 Subject: [PATCH 055/130] Updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a37d298..91171c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value (regression introduced in bc42149a). (@optimistiCli) - Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) From 4144059e7a8c7dcc0fcdd40431f6e05cb4ee40cf Mon Sep 17 00:00:00 2001 From: Ish West Date: Sat, 25 Apr 2026 21:30:03 +0200 Subject: [PATCH 056/130] Fixed CHANGELOG.md entry format --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91171c92..bd76e2e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes -- Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value (regression introduced in bc42149a). (@optimistiCli) +- Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli) - Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) From f39d63b85d5b4bc675c718ef7832d786ed38a9a1 Mon Sep 17 00:00:00 2001 From: YoshKoz <77861115+YoshKoz@users.noreply.github.com> Date: Sun, 19 Apr 2026 23:19:53 +0200 Subject: [PATCH 057/130] fix(zsh): use newline-splitting for language completions to fix word-splitting on names with spaces Language names like "HTML (Jinja2)" and "Apache Conf" contain spaces, which caused $() command substitution to word-split them into garbage tokens. Use ${(f)"$(...)"} (split on newlines only) to match the pattern already used for theme completions on line 100. Fixes #2897 --- assets/completions/bat.zsh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 3c3f81d1..f8de56e4 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -90,7 +90,7 @@ _{{PROJECT_EXECUTABLE}}_main() { languages) local IFS=$'\n' local -a languages - languages=( $({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }') ) + languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }')"} ) _describe 'language' languages && ret=0 ;; From 5722311b2e96f6c6751e848a99c265e386ab43f3 Mon Sep 17 00:00:00 2001 From: Yoshi Tacke <77861115+YoshKoz@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:06:00 +0200 Subject: [PATCH 058/130] docs(changelog): add bugfix entry for zsh completion fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a37d298..8eabb7d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - Fixed test compatibility with future Cargo build directory changes, see #3550 (@nmacl) - Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l) - Fixed syntax tests path, see #3610 (@foxfromworld) +- Fix zsh tab completion word-splitting language names containing spaces (e.g. `HTML (Jinja2)`, `Apache Conf`), see #3693 (@YoshKoz) ## Other - Use git version of cross. See #3533 (@OctopusET) From 64567c4819ff76cec66a74e87c9e47b1f6f9b609 Mon Sep 17 00:00:00 2001 From: lawrence3699 Date: Tue, 28 Apr 2026 01:11:06 +1000 Subject: [PATCH 059/130] Propagate initial input read errors --- CHANGELOG.md | 1 + src/input.rs | 44 +++++++++++++++++++++++++++++++++++--------- src/lessopen.rs | 4 ++-- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eabb7d4..066b1161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- Report initial input read errors instead of treating them as empty input. Closes #3002, see #3706 (@lawrence3699) - Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar) - Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu) - Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn) diff --git a/src/input.rs b/src/input.rs index 30f13f98..a3420123 100644 --- a/src/input.rs +++ b/src/input.rs @@ -207,7 +207,7 @@ impl<'a> Input<'a> { kind: OpenedInputKind::StdIn, description, metadata: self.metadata, - reader: InputReader::new(stdin), + reader: InputReader::try_new(stdin)?, }) } @@ -236,14 +236,14 @@ impl<'a> Input<'a> { file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happened..."); } - InputReader::new(BufReader::new(file)) + InputReader::try_new(BufReader::new(file))? }, }), InputKind::CustomReader(reader) => Ok(OpenedInput { description, kind: OpenedInputKind::CustomReader, metadata: self.metadata, - reader: InputReader::new(BufReader::new(reader)), + reader: InputReader::try_new(BufReader::new(reader))?, }), } } @@ -257,24 +257,29 @@ pub(crate) struct InputReader<'a> { } impl<'a> InputReader<'a> { - pub(crate) fn new(mut reader: R) -> InputReader<'a> { + #[cfg(test)] + pub(crate) fn new(reader: R) -> InputReader<'a> { + Self::try_new(reader).expect("reading the first line failed") + } + + pub(crate) fn try_new(mut reader: R) -> io::Result> { let mut first_line = vec![]; - reader.read_until(b'\n', &mut first_line).ok(); + reader.read_until(b'\n', &mut first_line)?; let content_type = inspect_content_type(&first_line); if content_type == Some(ContentType::UTF_16LE) { - read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A).ok(); + read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A)?; } else if content_type == Some(ContentType::UTF_16BE) { - read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00).ok(); + read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00)?; } - InputReader { + Ok(InputReader { inner: Box::new(reader), first_line, content_type, unbuffered: false, - } + }) } pub(crate) fn read_line(&mut self, buf: &mut Vec) -> io::Result { @@ -405,6 +410,27 @@ fn non_zip_pk_prefix_is_not_treated_as_binary() { ); } +#[test] +fn input_open_returns_initial_read_errors() { + struct FailingRead; + + impl Read for FailingRead { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Err(io::Error::other("initial read failed")) + } + } + + let input = Input::from_reader(Box::new(FailingRead)); + let result = input.open(io::empty(), None); + + assert!(result.is_err()); + assert!(result + .err() + .unwrap() + .to_string() + .contains("initial read failed")); +} + #[test] fn utf16le() { let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00"; diff --git a/src/lessopen.rs b/src/lessopen.rs index 966bc2c0..116206ba 100644 --- a/src/lessopen.rs +++ b/src/lessopen.rs @@ -155,7 +155,7 @@ impl LessOpenPreprocessor { Ok(OpenedInput { kind, - reader: InputReader::new(BufReader::new( + reader: InputReader::try_new(BufReader::new( if matches!(self.kind, LessOpenKind::TempFile) { let lessopen_string = match String::from_utf8(lessopen_stdout) { Ok(string) => string, @@ -192,7 +192,7 @@ impl LessOpenPreprocessor { .map(|s| s.replacen("%s", &path_str, 1).replacen("%s", "-", 1)), } }, - )), + ))?, metadata: input.metadata, description: input.description, }) From bceb260e91919aebe48f4230f69d5c3f0ebee1e9 Mon Sep 17 00:00:00 2001 From: guille Date: Fri, 24 Apr 2026 16:20:49 +0200 Subject: [PATCH 060/130] Improve Kotlin syntax --- .gitmodules | 6 +- CHANGELOG.md | 1 + assets/syntaxes/02_Extra/Kotlin | 2 +- .../syntaxes/02_Extra/Kotlin.sublime-syntax | 398 ------------------ tests/syntax-tests/highlighted/Kotlin/test.kt | 104 ++--- 5 files changed, 57 insertions(+), 454 deletions(-) delete mode 100644 assets/syntaxes/02_Extra/Kotlin.sublime-syntax diff --git a/.gitmodules b/.gitmodules index b64280c6..20e0bbb4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -49,9 +49,6 @@ [submodule "assets/themes/zenburn"] path = assets/themes/zenburn url = https://github.com/colinta/zenburn.git -[submodule "assets/syntaxes/Kotlin"] - path = assets/syntaxes/02_Extra/Kotlin - url = https://github.com/vkostyukov/kotlin-sublime-package [submodule "assets/syntaxes/Elm"] path = assets/syntaxes/02_Extra/Elm url = https://github.com/elm-community/SublimeElmLanguageSupport @@ -281,3 +278,6 @@ [submodule "assets/syntaxes/02_Extra/COBOL"] path = assets/syntaxes/02_Extra/COBOL url = https://github.com/adukhan99/sublime_cobol.git +[submodule "assets/syntaxes/02_Extra/Kotlin"] + path = assets/syntaxes/02_Extra/Kotlin + url = https://github.com/guille/sublime-kotlin diff --git a/CHANGELOG.md b/CHANGELOG.md index 066b1161..341dbf42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Fixed manpage syntax so that ANSI escape codes don't get incorrectly highlighted and thus broken, see #3586 (@BlueElectivire) - Map several Google Cloud CLI config files to their appropriate syntax #3635 (@victor-gp) - Map all ignore dotfiles to Git Ignore syntax #3636 (@victor-gp) +- Improved Kotlin syntax, see #3698 (@guille) ## Themes diff --git a/assets/syntaxes/02_Extra/Kotlin b/assets/syntaxes/02_Extra/Kotlin index aeeed278..c3536941 160000 --- a/assets/syntaxes/02_Extra/Kotlin +++ b/assets/syntaxes/02_Extra/Kotlin @@ -1 +1 @@ -Subproject commit aeeed2780b04aea3d293c547c24cae27cafef0c5 +Subproject commit c353694169c047bd746c93c214289fc52d152cae diff --git a/assets/syntaxes/02_Extra/Kotlin.sublime-syntax b/assets/syntaxes/02_Extra/Kotlin.sublime-syntax deleted file mode 100644 index eab59192..00000000 --- a/assets/syntaxes/02_Extra/Kotlin.sublime-syntax +++ /dev/null @@ -1,398 +0,0 @@ -%YAML 1.2 ---- -# http://www.sublimetext.com/docs/3/syntax.html -name: Kotlin -file_extensions: - - kt - - kts -scope: source.Kotlin -contexts: - main: - - include: comments - - match: '^\s*(package)\b(?:\s*([^ ;$]+)\s*)?' - captures: - 1: keyword.other.kotlin - 2: entity.name.package.kotlin - - include: imports - - include: statements - classes: - - match: (?" - pop: true - - include: generics - - match: \( - push: - - match: \) - pop: true - - include: parameters - - match: (:) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: "(?={|$)" - pop: true - - match: \w+ - scope: entity.other.inherited-class.kotlin - - match: \( - push: - - match: \) - pop: true - - include: expressions - - match: '\{' - push: - - match: '\}' - pop: true - - include: statements - comments: - - match: /\* - captures: - 0: punctuation.definition.comment.kotlin - push: - - meta_scope: comment.block.kotlin - - match: \*/ - captures: - 0: punctuation.definition.comment.kotlin - pop: true - - match: \s*((//).*$\n?) - captures: - 1: comment.line.double-slash.kotlin - 2: punctuation.definition.comment.kotlin - constants: - - match: \b(true|false|null|this|super)\b - scope: constant.language.kotlin - - match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFf])?\b' - scope: constant.numeric.kotlin - - match: '\b([A-Z][A-Z0-9_]+)\b' - scope: constant.other.kotlin - expressions: - - match: \( - push: - - match: \) - pop: true - - include: expressions - - include: types - - include: strings - - include: constants - - include: comments - - include: keywords - functions: - - match: (?=\s*\b(?:fun)\b) - push: - - match: '(?=$|\})' - pop: true - - match: \b(fun)\b - captures: - 1: keyword.other.kotlin - push: - - match: (?=\() - pop: true - - match: < - push: - - match: ">" - pop: true - - include: generics - - match: '([\.<\?>\w]+\.)?(\w+)' - captures: - 2: entity.name.function.kotlin - - match: \( - push: - - match: \) - pop: true - - include: parameters - - match: (:) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: "(?={|=|$)" - pop: true - - include: types - - match: '\{' - push: - - match: '(?=\})' - pop: true - - include: statements - - match: (=) - captures: - 1: keyword.operator.assignment.kotlin - push: - - match: (?=$) - pop: true - - include: expressions - generics: - - match: (:) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: (?=,|>) - pop: true - - include: types - - include: keywords - - match: \w+ - scope: storage.type.generic.kotlin - getters-and-setters: - - match: \b(get)\b\s*\(\s*\) - captures: - 1: entity.name.function.kotlin - push: - - match: '\}|(?=\bset\b)|$' - pop: true - - match: (=) - captures: - 1: keyword.operator.assignment.kotlin - push: - - match: (?=$|\bset\b) - pop: true - - include: expressions - - match: '\{' - push: - - match: '\}' - pop: true - - include: expressions - - match: \b(set)\b\s*(?=\() - captures: - 1: entity.name.function.kotlin - push: - - match: '\}|(?=\bget\b)|$' - pop: true - - match: \( - push: - - match: \) - pop: true - - include: parameters - - match: (=) - captures: - 1: keyword.operator.assignment.kotlin - push: - - match: (?=$|\bset\b) - pop: true - - include: expressions - - match: '\{' - push: - - match: '\}' - pop: true - - include: expressions - imports: - - match: '^\s*(import)\s+[^ $]+\s+(as)?' - captures: - 1: keyword.other.kotlin - 2: keyword.other.kotlin - keywords: - - match: \b(var|val|public|private|protected|abstract|final|sealed|enum|open|attribute|annotation|override|inline|vararg|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof|reified|suspend)\b - scope: storage.modifier.kotlin - - match: \b(try|catch|finally|throw)\b - scope: keyword.control.catch-exception.kotlin - - match: \b(if|else|while|for|do|return|when|where|break|continue)\b - scope: keyword.control.kotlin - - match: \b(in|is|!in|!is|as|as\?|assert)\b - scope: keyword.operator.kotlin - - match: (==|!=|===|!==|<=|>=|<|>) - scope: keyword.operator.comparison.kotlin - - match: (=) - scope: keyword.operator.assignment.kotlin - - match: (::) - scope: keyword.operator.kotlin - - match: (:) - scope: keyword.operator.declaration.kotlin - - match: \b(by)\b - scope: keyword.other.by.kotlin - - match: (\?\.) - scope: keyword.operator.safenav.kotlin - - match: (\.) - scope: keyword.operator.dot.kotlin - - match: (\?:) - scope: keyword.operator.elvis.kotlin - - match: (\-\-|\+\+) - scope: keyword.operator.increment-decrement.kotlin - - match: (\+=|\-=|\*=|\/=) - scope: keyword.operator.arithmetic.assign.kotlin - - match: (\.\.) - scope: keyword.operator.range.kotlin - - match: (\-|\+|\*|\/|%) - scope: keyword.operator.arithmetic.kotlin - - match: (!|&&|\|\|) - scope: keyword.operator.logical.kotlin - - match: (;) - scope: punctuation.terminator.kotlin - namespaces: - - match: \b(namespace)\b - scope: keyword.other.kotlin - - match: '\{' - push: - - match: '\}' - pop: true - - include: statements - parameters: - - match: (:) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: (?=,|\)|=) - pop: true - - include: types - - match: (=) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: (?=,|\)) - pop: true - - include: expressions - - include: keywords - - match: \w+ - scope: variable.parameter.function.kotlin - statements: - - include: namespaces - - include: typedefs - - include: classes - - include: functions - - include: variables - - include: getters-and-setters - - include: expressions - strings: - - match: '"""' - captures: - 0: punctuation.definition.string.begin.kotlin - push: - - meta_scope: string.quoted.third.kotlin - - match: '"""' - captures: - 0: punctuation.definition.string.end.kotlin - pop: true - - match: '(\$\w+|\$\{[^\}]+\})' - scope: variable.parameter.template.kotlin - - match: \\. - scope: constant.character.escape.kotlin - - match: '"' - captures: - 0: punctuation.definition.string.begin.kotlin - push: - - meta_scope: string.quoted.double.kotlin - - match: '"' - captures: - 0: punctuation.definition.string.end.kotlin - pop: true - - match: '(\$\w+|\$\{[^\}]+\})' - scope: variable.parameter.template.kotlin - - match: \\. - scope: constant.character.escape.kotlin - - match: "'" - captures: - 0: punctuation.definition.string.begin.kotlin - push: - - meta_scope: string.quoted.single.kotlin - - match: "'" - captures: - 0: punctuation.definition.string.end.kotlin - pop: true - - match: \\. - scope: constant.character.escape.kotlin - - match: "`" - captures: - 0: punctuation.definition.string.begin.kotlin - push: - - meta_scope: string.quoted.single.kotlin - - match: "`" - captures: - 0: punctuation.definition.string.end.kotlin - pop: true - typedefs: - - match: (?=\s*(?:type)) - push: - - match: (?=$) - pop: true - - match: \b(type)\b - scope: keyword.other.kotlin - - match: < - push: - - match: ">" - pop: true - - include: generics - - include: expressions - types: - - match: \b(Nothing|Any|Unit|String|CharSequence|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b - scope: storage.type.buildin.kotlin - - match: \b(IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b - scope: storage.type.buildin.array.kotlin - - match: \b(Array|Collection|List|Map|Set|MutableList|MutableMap|MutableSet|Sequence)<\b - captures: - 1: storage.type.buildin.collection.kotlin - push: - - match: ">" - pop: true - - include: types - - include: keywords - - match: \w+< - push: - - match: ">" - pop: true - - include: types - - include: keywords - - match: '\{' - push: - - match: '\}' - pop: true - - include: statements - - match: \( - push: - - match: \) - pop: true - - include: types - - match: (->) - scope: keyword.operator.declaration.kotlin - variables: - - match: (?=\s*\b(?:var|val)\b) - push: - - match: (?=:|=|(\b(by)\b)|$) - pop: true - - match: \b(var|val)\b - captures: - 1: keyword.other.kotlin - push: - - match: (?=:|=|(\b(by)\b)|$) - pop: true - - match: < - push: - - match: ">" - pop: true - - include: generics - - match: '([\.<\?>\w]+\.)?(\w+)' - captures: - 2: entity.name.variable.kotlin - - match: (:) - captures: - 1: keyword.operator.declaration.kotlin - push: - - match: (?==|$) - pop: true - - include: types - - include: getters-and-setters - - match: \b(by)\b - captures: - 1: keyword.other.kotlin - push: - - match: (?=$) - pop: true - - include: expressions - - match: (=) - captures: - 1: keyword.operator.assignment.kotlin - push: - - match: (?=$) - pop: true - - include: expressions - - include: getters-and-setters diff --git a/tests/syntax-tests/highlighted/Kotlin/test.kt b/tests/syntax-tests/highlighted/Kotlin/test.kt index dcb1560e..bb17aba2 100644 --- a/tests/syntax-tests/highlighted/Kotlin/test.kt +++ b/tests/syntax-tests/highlighted/Kotlin/test.kt @@ -1,85 +1,85 @@ -import kotlin.math.* +import kotlin.math.* -data class Example( - val name: String, - val numbers: List<Int?> +data class Example( + val name: String, + val numbers: List<Int?> ) -fun interface JokeInterface { - fun isFunny(): Boolean +fun interface JokeInterface { + fun isFunny(): Boolean } -abstract class AbstractJoke : JokeInterface { - override fun isFunny() = false - abstract fun content(): String +abstract class AbstractJoke : JokeInterface { + override fun isFunny() = false + abstract fun content(): String } -class Joke : AbstractJoke() { - override fun isFunny(): Boolean { +class Joke : AbstractJoke() { + override fun isFunny(): Boolean {  return true - } - override fun content(): String = "content of joke here, haha" + } + override fun content(): String = "content of joke here, haha" } -class DelegatedJoke(val joke: Joke) : JokeInterface by joke { - val number: Long = 123L +class DelegatedJoke(val joke: Joke) : JokeInterface by joke { + val number: Long = 123L - companion object { - const val someConstant = "some constant text" - } + companion object { + const val someConstant = "some constant text" + } } -object SomeSingleton +object SomeSingleton -sealed class Shape { - abstract fun area(): Double +sealed class Shape { + abstract fun area(): Double } -data class Square(val sideLength: Double) : Shape() { - override fun area(): Double = sideLength.pow(2) +data class Square(val sideLength: Double) : Shape() { + override fun area(): Double = sideLength.pow(2) } -object Point : Shape() { - override fun area() = .0 +object Point : Shape() { + override fun area() = .0 } -class Circle(val radius: Double) : Shape() { - override fun area(): Double { - return PI * radius * radius - } +class Circle(val radius: Double) : Shape() { + override fun area(): Double { + return PI * radius * radius + } } -fun String.extensionMethod() = "test" +fun String.extensionMethod() = "test" -fun main() { - val name = """ +fun main() { + val name = """  multiline  string    some numbers: 123123 42 - """.trimIndent() - val example = Example(name = name, numbers = listOf(512, 42, null, -1)) + """.trimIndent() + val example = Example(name = name, numbers = listOf(512, 42, null, -1)) - example.numbers - .filterNotNull() - .forEach { println(it) } + example.numbers + .filterNotNull() + .forEach { println(it) } - setOf(Joke(), DelegatedJoke(Joke()).joke) - .filter(JokeInterface::isFunny) - .map(AbstractJoke::content) - .forEachIndexed { index: Int, joke -> - println("I heard a funny joke(#${index + 1}): $joke") - } + setOf(Joke(), DelegatedJoke(Joke()).joke) + .filter(JokeInterface::isFunny) + .map(AbstractJoke::content) + .forEachIndexed { index: Int, joke -> + println("I heard a funny joke(#${index + 1}): $joke") + } - listOf(Square(12.3), Point, Circle(5.2)) - .associateWith(Shape::area) - .toList() - .sortedBy { it.second } - .forEach { - println("${it.first}: ${it.second}") - } + listOf(Square(12.3), Point, Circle(5.2)) + .associateWith(Shape::area) + .toList() + .sortedBy { it.second } + .forEach { + println("${it.first}: ${it.second}") + } - println("some string".extensionMethod()) + println("some string".extensionMethod()) - require(SomeSingleton::class.simpleName == "SomeSingletonName") { "something does not seem right..." } + require(SomeSingleton::class.simpleName == "SomeSingletonName") { "something does not seem right..." } } From 492c387ce7fdc02ea7c0aceabef3c2e1b15f09c4 Mon Sep 17 00:00:00 2001 From: guille Date: Fri, 24 Apr 2026 20:52:52 +0200 Subject: [PATCH 061/130] CHANGELOG: reference PR not issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341dbf42..637dc209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ - Fixed manpage syntax so that ANSI escape codes don't get incorrectly highlighted and thus broken, see #3586 (@BlueElectivire) - Map several Google Cloud CLI config files to their appropriate syntax #3635 (@victor-gp) - Map all ignore dotfiles to Git Ignore syntax #3636 (@victor-gp) -- Improved Kotlin syntax, see #3698 (@guille) +- Improved Kotlin syntax, see #3699 (@guille) ## Themes From de64d3a0ebe6f70637d215d6042e7eb9239755e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 19:28:44 +0000 Subject: [PATCH 062/130] build(deps): bump assets/syntaxes/02_Extra/Idris2 Bumps [assets/syntaxes/02_Extra/Idris2](https://github.com/buzden/sublime-syntax-idris2) from `bbfe50e` to `4d8eb35`. - [Commits](https://github.com/buzden/sublime-syntax-idris2/compare/bbfe50e023e0edc74f5e0c003eb946528d49279f...4d8eb35a38254d422030e77b4933530008dd3c6e) --- updated-dependencies: - dependency-name: assets/syntaxes/02_Extra/Idris2 dependency-version: 4d8eb35a38254d422030e77b4933530008dd3c6e dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- assets/syntaxes/02_Extra/Idris2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/syntaxes/02_Extra/Idris2 b/assets/syntaxes/02_Extra/Idris2 index bbfe50e0..4d8eb35a 160000 --- a/assets/syntaxes/02_Extra/Idris2 +++ b/assets/syntaxes/02_Extra/Idris2 @@ -1 +1 @@ -Subproject commit bbfe50e023e0edc74f5e0c003eb946528d49279f +Subproject commit 4d8eb35a38254d422030e77b4933530008dd3c6e From ebf469b99af6c3d4eab784b2d6d29255062c0470 Mon Sep 17 00:00:00 2001 From: David Peter Date: Thu, 30 Apr 2026 10:16:33 +0200 Subject: [PATCH 063/130] Update README --- README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/README.md b/README.md index d6ef3381..e360a777 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,6 @@ [Русский]

-### Sponsors - -A special *thank you* goes to our biggest sponsors:
- -

- - Warp -
- Warp, the intelligent terminal -
- Available on MacOS, Linux, Windows -
-

- ### Syntax highlighting `bat` supports syntax highlighting for a large number of programming and markup From 10b4f07a87bb6bccd9d962bf9e7bb3f04b338128 Mon Sep 17 00:00:00 2001 From: David Peter Date: Thu, 30 Apr 2026 10:18:58 +0200 Subject: [PATCH 064/130] Remove sponsors --- README.md | 6 ------ doc/sponsors.md | 2 -- doc/sponsors/graphite-logo.jpeg | Bin 28195 -> 0 bytes doc/sponsors/warp-logo.png | Bin 132621 -> 0 bytes doc/sponsors/warp-pack-header.png | Bin 37674 -> 0 bytes 5 files changed, 8 deletions(-) delete mode 100644 doc/sponsors/graphite-logo.jpeg delete mode 100644 doc/sponsors/warp-logo.png delete mode 100644 doc/sponsors/warp-pack-header.png diff --git a/README.md b/README.md index e360a777..375ec6d7 100644 --- a/README.md +++ b/README.md @@ -194,12 +194,6 @@ Note that the [Manpage syntax](assets/syntaxes/02_Extra/Manpage.sublime-syntax) The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.md) script is a wrapper that will format code and print it with `bat`. -#### `Warp` - - - Warp - - #### Highlighting `--help` messages You can use `bat` to colorize help text: `$ cp --help | bat -plhelp` diff --git a/doc/sponsors.md b/doc/sponsors.md index 24509077..641e5205 100644 --- a/doc/sponsors.md +++ b/doc/sponsors.md @@ -10,5 +10,3 @@ No issue will have a different priority based on sponsorship status of the reporter. Contributions from anybody are most welcomed, please see our [`CONTRIBUTING.md`](../CONTRIBUTING.md) guide. - -If you want to see our biggest sponsors, check the top of [`README.md`](../README.md#sponsors). diff --git a/doc/sponsors/graphite-logo.jpeg b/doc/sponsors/graphite-logo.jpeg deleted file mode 100644 index 00443818dd32d97031c57dec5dcd83a9ad8777ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28195 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 DCAoA} diff --git a/doc/sponsors/warp-logo.png b/doc/sponsors/warp-logo.png deleted file mode 100644 index f99dd38ceea805656daea5cd80c3525dbd307b71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132621 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¯>*xI^)PTW9xaQE(z0zaJ zv?Lote?cvbaKBu}->HUg(0DNc_z>x=?o)`Fzxct4zheqW`b${Zw0@Ly@>XuLX9XV1 z!e_V^e}IDIG2ZvYGS8M$@(63?NL=+7@TWfQxFy!>oCZrv3WrmRT#@DU3#T3BiIU?l zRDRged>y{1k~^MfBHKU!Q;9NWw?#5M!J%iKU+B-opOsC2RLvX);GV<;<>Kvd9vcOZ zU@LH8#g;$HyIyw5l7hC3CDuSd5%^g2RG1D6*cV;1&L%=5uDWYWBd*;%y&lT+x<_z- z{Gf~lTQc?HLMomYBII8wI_!uq3`34z-%l+`;Z~EGV(uFhlPE@d(gev+sE&HmK>4_! z-wA;0=#i(qLD1l7(RDu522tDaj-s<=fkawJ;Bj3?uPGv03i7Y$HdY=HkoMre!wa{82P#eRN;=Tos{a_wY4}i zfq*_gUNXsF0TMLwlE?}G94TL(ft@4_7VS4kKJ1@gzXDVy76@XEIl;fQTz71%?rhZP z8BaMF_L^tZ8kZhHlpQickzS#9hZLbdxhsR)W0J7LR_yE~ZCCN7UtQV?v4$lV8)X2k zkZ`j6@7^rYYJ06NPtg5>hQive`Eekfs{b+nOr13}y!u7h1^b=z&>rU?wxz6cuJ| zAvjpkq#0)J`V4Po6S2b%yl_-OtQBxlj?_}Knfq3%M~b~OO6#O*L~7ay3X+c7-Mp)6tZ)F^ZhI#>NRsu>wY>h ze#B=uOWEjR9srQcToV{6t+b`DUy`0dBdTi;jG}y0`)!|WmVcOIm6DUQ=o5+VW>~W4 zM=ouh9?bc3Qm4<_UoCUE!U}?B$8mUp7m&)pmtv2a{bn7&yXX+V(KQ#xTq2KgCx;vZq5Mh!av4ZF-CPjCn#5rwOD#oJT+*T!gARJ9C%UL zVoZ{91bLqDDol4pLhY?%N40%XoJ^S-?`x7@CSAbxx}}V%0azDsS==Rg2D|PtphV9_ z8&|txV5D&&j?$Lw%UrC-Zhx(~=98Q2?%Rfomit9IKaUi_!x>#Bq?0lKt7I69N^0jE1xq^5{OLu0RRM`9&=lbpY%ZN99l~%*PkL4qp1H;sp zO6ZdKn%|=z8kM3Y4YPumFl&bradO#vx|3OC#u{Qhr>(UhFMT7adu>Nr-kI@MJTwbs z_1wY&k;7lr#lPFFHV4etK*t$rCjwZ^CRf6u&)>Ia52l)KgTq!xMm#^*=eM(Q_0N?( zAkJ%ISX5SM`zGhOb2O#B*jT*qnjj+Ro521mw_w)TLCs*tW`4;0GdA+xi-lXJtXMVN z1GC_302TdPiVMt-HM@+SPeo)oYK?NW4_=V{HfaJrY;~eiMy*mv-%olF2p{Tkvdc$f zKKxkllWBVo7AzfWX}gx>lz-Im8@hB_h<%HOeUxsfRqXpQ{P?R!Qynl}cJk9r6&`D! zxj4vf1$YS0`ILEe4wqzE^?G~SwnttXc#*o}McDn#XECasXlf^l;zz?S(WQ9sV!7}Z z;nMGc8*o5twc|5=7kqc@Z}e7oSp{--tjl(O_$kjZ26#Lfur7x#in_Py?xv9459vL} z%thsLly1J@^Qu{c6~s&2dIiYUBK6h~AoV3!jFxc}nzsvkT(+#tTbPbFofbxLyDA=l z$z0Jm7ly6tFh_Em^>x{XKP@TaJG^Ew7IV^sNCuntxe-PqC(D}gHNLWD)=1!5i;o7>k8FB;EJD zaZ>HzJJ}oI4?lb*ECq~Rl5{%yXWU!t7EDp=%Qep0#I?)K7g(_eC*Nt&xVqqcYYLNt zE7*|%@++m^$_8L265<3)Ly!Yd=-qNc+Drpd`jG3H&7nX0CzA%owxx!w&^z%}vpLCX zvJTs{S_p%bSrL?ht!%p{>xW8v7G8@>s)t*`(+@GEyssJWMnVq!0D zX*w1S#>T&TFZ7}JP~k&F>}xf;;nenV>9i+;Uf zhEuelAErrcO}tlwzJ@+%MF%P!Bt;4d6*uH$a>$yfRuZWhPI^YWS1liibiUYPhPzH! z`Q|PB#+Z!KAQE^<%R>=qK%eXSwO6Zedqob09DVyCV?nC+@uruE!gd$-#2}@$WOx>X4nkUVMqUT@JU^& zjph>HOL$OuSXKR)0C%Uy2#2Y2>CiOy3(8+79f`UEQAO9Sfp2SY64hSmeQ@orCh?&& zoT`yahU4z%Lh&D$Mpe=+wQsIVj>;Bt7HWrlGZ+%-03mNX#>FtiTS zgBfwt0eoWwVt7@#boy2{+`V%bTP zvnt{l7j5YN@m$>-64#LKo0J=)O^`Z0U*$vTB9*9Fy&W?x3otxO#&FX^AE{%zx$`P% zPy+5XhlX>-x!OGO4f$OG_{p5#-FWXI%afql7yUmnra#8WVsrq($UtMO>`YJh<*gMH zwLJ;qH9NO(fR2Nw9vyGjM*@0s7x7Bp7IK0IJ{2vm6T{hn)TgRkTd+%5};r>|a;^_Jyk@pF}Ha!!;_(db?di2~6-VR|PE@6||ZAXhy8OC0A(xvDkkT$zJEKDmZg+*cb6Kjz~BKYpNUgC zZ~V}HWBPS=NZ~8X%g7Jgqak%Hr4xwub#|d{#r-UD>8Bmg#ra@BP)33_mcgb;X*e?$ z=I6^oiGf-%bQ^Zxna}k_Xl8Yo&4K5RUx!(}ze2yA2O4kKEg4`?*ig4)*^fi4VU6b{n?~m_@K74 zdYXzQD=Ut)DDfN`v7aX&BAQ&M+y)_L_e0o+z5WeQoYucVo z8#eDJ+P-jqq!*X zn;l-j1cpybY-#e8zrXY!=Z&b^{ppX^8mv&I?fRlSm1kgw7D@S=qsBy21qdCS6l3yB zNd#uPuWmo8FfOH6pO`UGO6;?PlREV~3h_pHCQw{894uWEaZu!SMmioMSON`A>U6Fa zZrS?nO^6RFQq#Z0^^-Kh*j_N=(-*@c*~#??TcGfkm(VMT^zaEU0P{&h?(oYIlTLm< z35%7PC0N|2AVl)IRh}A9|9pF)(f*XY2|zv^JGqT;J*aWCwoa7kGQ&M-Oqp*EgV7X| zt66}fmhOblayj~#@(^`(T?#?8k8fLnI79pBLkG1N%fam~!7YsmnqOVX7OjejhblCZ z@u&1j>n_&gHuNK)P6tCaOTi=$O=78Y3t?fq;)6e>_#Rds3hXy00C9ccb#k-#9?24T zaRW-5T>aS&B~BQY##fg5l}_zMD)Nary`8Yny=65`HJ#wTqH8_FU%MWfkG(EDsg03Y zI-mJ!zHjE*X-Ez1b+5#OO+6`Zo*EWBNH>WoIDPk^yE6gpP-5=! zwTMQ`e*YxdN)T&kvs7ruitm{R!DGw|9p^l0{MaWiYc)=ROe@C$xuQ{%QNGAPNlZkI zlC@KN?Y7gq`dFACG=9}mVgk2+u!~!b~-is@o-4XTj+Bm*RQVT>(#sOq1PVbZ@Y|LYKF3Pev?sX*KpM82IuMi zE=J?pc^53@Nt&%1;~lqppHm8NswrI3#}4{l5FJAV(>nHVS8Jlj|hqp)&Y3hpE4$PqEgE`6L- z*!ArUinLowscVJO)~UD9cp83KDO0L)LrgS046z>oh2%8IKG|=*#$+CVUvbTPOzeYB z77$TMBW~OKpm2`aoh#QZlc88T#K%!R^e#|{A8mccrefljqL)gG38G~b8o&mgF$tL< zY5P`e02-SxfwJqkbkyo^vB(E4HPw)&O6+pb7UZp-!A1air4>ouS^6K{ z4(x;u8B8yW#^&RrskPjbbj0`NWQnL%8EeQK0$kahTd8WwkM;EEB4Q|foz&f63Q>*X zBrkk4ol}w8)^h{oHU!lVts((J**}(!y|MVc9+^1*LPB45*o;g+QDQ*0lbaju51sn zKKJtEZSM~-Cfg?BL5|uc9$y<1HLrpyj+X5di0!SO7B4>T-$tm(DC>ap6w~-3ezkbf zUQ!Z(eTpkSt}~wOm`d<(qwmA4ar;^B=O%;u`k2f-R;dI?gN<*tE&|5od?~Uw}#kn@pfbU-d7qQ zTn^!+oLRnc>d!8>#7qmUYFgWrL&H2(c!;^LtHDl+S!*m<1L?UNKJ#jo$@D`6nyy-N zF9r`7^^xE22d%kByVb=9LLI1)pF5?b@~~^nlQxbG;-kv#Rw`1eOBZKIq#Ca5DuP&fw`Pu*vp?-B&g`~3^yb0^jHYKgKwh`ke-n4)0? zPN^EBm-kG%m6496oh47WsJl3$x+fo2ll1e@=DDZ!qqk&ri<~<-OHn|xfj4!0tQC0n zvP}mFGNlLotRsb0yayfjD@SCp&=C6$kHm<55my*OXLInbk%lNmm$m`sFVN3A#~M!{ zh3xWtyN+ns3ll~_E?V>Gw!17{J-r60$Tw82!;Zf#0JUA_mZ-vsS&HF%|ef$Q~%3z}~8r+-;XLmSF8lT(aM zFjvnsqbp1Zjv7}Vh#AUQhH#f2T}<*AWD7=&YNbhyzcCYuKIWL2-7e2x;j45vgnzNx z%AA%2YZBMMgAbddHs_Fr@x9!4$KCf-MIJ;}UC&=6l1E&RJltfe{)HJH0W$|{TDTy; zi4yV_-LldV)Id1s0B@E1>8^uAQSYh>Lg9JXeRm5qgBu&Iv)A{h$ouoh$b%YkArt&Y^1olhH!8SzC9laP5qj7@(9@C7qYE3km$6Y) zH^O3N5XkUWJ`0cUvYfFi4)cHG;0e zi7&+;$fNCT$sZg+giK@QSao<4ZNN!gjeuN4fIh}@aQZp|XCZ}KeB4SGg$f1D}WL%HQv+=h|y#bs+=={8x!QQgGl;%^zw;T~heuK zpZKQ&d$<{}_iN1{9m`5tK?^?vZah;JEwjo98HIE7TcFB|SVR)GPVor(`stP0a1V)$K8GiGvFz|#9Bx$9ZU#^l`1vOJ`ND!F$GcJ!@tG`Z9{J0&Y}C{% zsmtmNVqjR zH&S;;q4!SRH>IZvT|e`X8-_1lmpOG31qbYpk7~6Xh;C3`8C(}If0b_|8?<#LUo}f; zK9Fo2keW^bS^1Lj5ZjF1Qr3z|EfW=bj(;;5d#^E{A0Q;dHQqq!O_+G?>dK6Dn4$wU(xI#ON9HJcGE6 z1xU#a<-`h^rG0F`?u+y-f|ocfB@FckDF&N1OJeyOruhB84uc*g43DQS?CCmmENlvIZptqIV=T|7DKl2Bt$vn|(!tL(gT{Ag*dI?8; z0+M_&zfH8K+9u;5s7+XlHGXd zw^tcZ?*|QtMa`C4NE+S|UhZ-5fR#%&2`vZLj^xVBYIW<<#4bmvE^VWXM#U;Dj?ckt z$?9old9d-fOvq^FMX^_|R#(tO(M!khr|ym%wTMVuhNDm2@sa*_wb1swC=tgtTGNeC zs(>#%{qCAi@W^MlANGi}kiZ|c?y^2*cIZ{`tgeiD1KE%M)Ekre;Sp1GbUyF-K>d5X zB@=~THJw0$)Jlmujho*AP$yyqIq9vRlEFId!UwF6HKBR65~JAds^(Usi~(pLUB9Z8 zoloYl1sjALn;|LruXLQ}mLrbR=XTmB+B^w-o;z+1)USF;R624!I$rjT==YmA>)Q)> z@7Qg37j&SOGzsM^T(tcKXmXcA_pTTzkUo4t!!R ztT8OnwV!fXCMu^inh~w+eOr)smlcYfxK~=02)UWqV}PAW-TYe3KQ;_KzP6(ky2Fy# zCKH|D&jcMp^todX^a#W3vvY4Oq)~vD+oRjJ^Cv zI~N)HsEsQH@fg(0f-3GY+(E=HeZt;)HsP*(MJ^0Ig?Qc@B;Hbt#%ou9?i*XPmn zMfzo()6>wQ{b>0d&-%Q26=XYb ze;2d;XBn#Bl(i$N_3JDI@z`_fJj@~JhzuJLUMeZm?zUH8oZ|iFp$xz$SC3Ky&tyvU z?8ec2VLQPmkSEnh1*4MV7DdDh0CepCr|eEu(PJ% zFLuHWXTNv~fm@c?Y^>Y(_^p0=<;mjL5SO@`&lyeCh0b4df}-{GHe7b}zUDB+!j*na zNFiS?M!E9xh9Q3#FGl4MKj8OkKkvmG!;+Qnv1@7e0FENB51y<7MGwA(<~B4YC;XTR zK72p#U`(-Uh%^z~7_?T-ZUux*if-rRs%gDg;#gHwp8Lke8o{H$$$aBTZulG>de9bP zg##v6Ylp_0Wst?@GUYFoR#wy1=5-As89KLZ0&m<(q3sAs##*cs{)o0y#p8ySDn+3q zZSJT;&SM?wxg&2%F54(){K3{@4#vn3tn~oH;H76~N&N4~t(*EGLq?%VrQ`D|xq%6$ zFHL&MG*Zousy^jx&UPYW($q(3TzGnwZhnslVW>z<&~Dq0Zt<^`^}*y0)ce?1Hh^<6 zNSW6Yk{Egl2+^p458loIq)0F8S`FXazIf^|dE|oeZ2HFH1Jvu>??_{r^x!;pWJ_lt zFi>k@ZqK0ZPMooQ^kLjjD=CD;+~Iu4Y?kf4b7*e%H=)5Dk%c0%ZG@EUR_EgKaG+BNRy^U|9Mhr5D`t&5koZ$eW${Xd(``TS~$>O{67 zyC_cZlL2TEfM-c0Y#vW-#+rYmGbcDgpD%3{h^1_J`{;O7=xphgvZob`u?@lz$67ny z)-7<~#ro>udh3`%rU*Wn2>M?;KhfNJJ|HBv_d91t==Q`%;Ekf^*9p(jF;}3dysoq0 z?piFKInTP2{MR_zRh!0qb(x~c^MKLBVcZy3c1HR8RLgW{JW?fW$ELL??$yD%9=&#> z3BKgw64wAOmKW!qGi4J*gsNn&DTVP2VU`17K5V$uopN#353DnL%d97S^r#CTdMr9r zBni`3I(+AfP0K92hbg>fGxDh(+aEq3caOe%-l9O-E|C1r%ZqH^XFK;w=7atc(B!W3 z!#v?=0D~R_VSWJ?3x<76)wa&|R7}qDZa`ciRG)#TFFl*N-_bjUUmn&x%WK`^E3KtAbkd)hert(7-+LXy22jj}JbEJtCmH3kkj}k4 zRQdh!W!VOc-4#^owuf87XY)Zz{DJ@8;(M$gExMD@XSA<-1AQg$eu_z59;a=C)%S0c zz_-gyX&g>)Jb-**Q**B?CjSKP1N_Oie(lZ;wd?w0hRdHIlmYhql~~2Y`~{A9BRxjm z;y=?v2+SR^^WPMd@f5HX^U(SM5@)K(*V@(!&0zsl67bqCzsJ8r5R(ZhQg=h44PIC0 z4RdA&t9axW16^tB$UZ2_ie6~weqY*#<$`*aEbqmS;O@hVf`m@L9T9Y_HwOG3?p@s8 zYuKtJee~{r-uLegR!@e?FQ3SdKZ)qO)m%N9ejrFA+U>`#mZ3`VCVFD|Uf0~)X6sl? zCP(?V%3G2ki5+1t!pPs>sDOa729#GEe;$j*V4ML)NAm66Y^`IThTw$HnE#(nRkl`-Enk83<~^=;J9bq}mr z8VwIiWFxLmPlsfsl&hI8+VG*#c8Z|3bNHgI%9c7bdGUXHioCXQR|IQFJ)4a&qb{0c#B+%~UEVPX_`Vm9PYQSL4 zYv{UEsE@BOw%x`8Zmcdezzkq$wER(b&Ix*=W#B`eNi|cSnN{7wOn;pxL)yzjk5$~` z97Wo|9ZkKQ)juSlfd`WW49oeThxE`t3Afm*n&@$v?fFQs8kh7`mQKf$>J2NGMqm#1ciKD$lR{75Q=yNPV^&@&Rq9@AO(CnYMjX;7nI zkDMLa0PMN=Ok4o5(mf$m#s)ZcvwwX*oFt(+m%SQY z4n)@-0&)@ZC3#0O3a3L!{gKsl*_2DCZt?fuw#V-%o}7!cec%J|O?_@mVx`wWmNaX? zRgLwRS9zO+v}U%$?3*^(<$)Eg*{}3b~q2xOH5|<>i$x>FI-v>lq@?%Z#|0 z?oWQa3Mla0*V3g-%4vdFu|VW!RNms1n6t$nWG-9vRHydww)t_kjrqtDPdBH^Q%66! zELxvRBuR^YX)^{4K;~Yz;QNZIc4Q(xw!`NUfwZk#KSOc=VTxEih!qdyPx!mJY=Ol? z`&O)Gh`cR2Pnb3r`me&-fDgAJ8{%r1qo0^8gUolRso_swXqe48D zJIhILV;YE^LcJjdU60{hDE}8Gb)w>@Ba4iIyS9uJJW5y!^Uwm{p4mt4DRxS_VV z9xv-7m>(JG>EF(Y5%mn~E4U;YpM5VO{MRgRg! z=vTV@JO)XCe6j_62#<8ByBt?s7D(w`_=j@kVdl~wO4i>a`YP5fYm7XeSz-YcaaO&= zib}|StrGUTOhd(bgl=5A`>HZ$+b&eAlhoCD`wZ|bM%Wb^e9pqslbhn|o!LN$}WfkL`A1^lCZjcI&`eVtu6_A(s5EcDl zQD-1|tB|~2UmmLhIw7EikrAZJgm0VOAB0*v()Y)e0Mtx8b9+8#0s&sjpt`N8o6Gm_ zYx%Q@^3#_$w0y3IjgzL{h#FzApUj&7l_9HTdC$usgrI_xQ~Qu34j z5PXfN^7QmT$M;v;6FoFq)FSwnBZU!yx^)m-}}&w`lX89vR}$LL%;j*1Tww_1@hzQo75*}f642-TZ(gJ zERtsc*(F*R5V>)n%UdyFyyPHY>=e15x`s1?o!bs}7yIRn)kKX)Q9Nf&;~uPP#7CU zXSC1~kaK|wkUr&=rQ8Cg>kxU*`DyPdT7-q*DD*b$6+JIZR|x#xuuh&B78Xuy#Yl-u z^qjak{<-G~pNuYMn6gHYiV7OhLM&?;DJH)iYq^Cv039gJ-o}8Zu2PJ~uZ1P2i}noK zjj=XF;31E>3ptpyc!T=g3Xm2vJ583x&@(1Ya5matZh9=j=F>d<4Izvz%oa#&IicQ5 z-^Y{;g`wrU9MbmU5&xQspzr{bMB|Y^O(Nm2=K5sw0>EA|x|~joF7%0jq>l-V@4iLw zp)wB>^2WA52Cq1?o)x2d zuB5BS2_vusg*``?2Pgw#d%K-#fcn(5-Di=yw>zu;=Rz-5#fOLRi}juwYIX-)Jp|L; z^o;i9#x=$nIPCrulRTN-sx^c9M*!>w9#?I?zI-6f|P{fjG8}dtB$}`(;;?JUu*990$2YdaF{*6Ib zi$@RDYMa`;*F1Vc7Ee!U@>ayVTLI|rm7+mp~r1%W>1=~eI@Fwf(G+)Z;D#jCA57^`gh7M2iH^cdGrR=54!`>nEkPA4AV?9_EFt zZDiPW2l@YGMq49vTew+UaREz(?yhzqhwPds_7C(^G^uZmz8~+u?q-})Pk^_29b5}) zI+LY1y|-IwJw9tXFJV~eL^5^?3wv$6y*>|R{j|I1c5_!Jk-%Neott^9Y5)`R+Lj2U ztDTx(v_DJZm@qbyG|J&{hJ?{(Dw8;JDz!)nmJP(4U7dV<^59_M_IJk0-PZ@c63u}p zE-nscU8noy{f0?DB*@;hE^>`?nu&Zq>oFXcm0`vYv^_U|DJ--?CDwX5SHwx2kw?>JtS>^>*Z$#@{yC2vEih?r7dn17JxUWurkA$x{OTEs5Ft0sv31-L}sd>E<_$* z84$6#XxCy6_BVn6v>KUT0QR51{#Eu$b=daT6)iBj(N>X~<`qW%$FKy5G%qHP7bO9a zPn;O5h!C#<@3}_YWEe1?7)VJ>NF+A=nUmtik(97kF!sU`Kx#PfQFgB^0#}tYhW={aZA1j#GXxHs}v>oO2v9P=ANmxIsWPwk_l^6AvhiM z)mbMaK4ol3UtTjaj@cDYo{7OL#~c{+ZfM`gPe%SxniUsKY$Q!2h_Y<&I##)+y$my6 zp}l_&DJuAM?$_*6nu~5r$>cSZR>X7>n+6wunZWoh4i(dVO{qI0i!{l(%R*@I*5X2c zN=i8yjs)akiJfitom<}J%;lEu0brLgfo3`I{`A6?N4WLmy6e94{_=e6TJbC~kxu0N zXb^ETEIo|TB9A}xnD*k7Q7JT^)5)pG$;UNVE&Zna1SkMxQvGq=WhHeM9vIRX<1ioV z>#}%#iwr&gJzgroR}Ns4FXWmV=wo<8_+`}mTC;Q8khbeq1>mIC<%R5&y3I9P;Pan^ z`sN(|>{UDPhuUoyNgYjcBNpmw9rk*D$|-f_#FqlKhHUlU=IAo7}-ioUh(11QMT=2fWh#&6#;nYwI&z--0pKv=>z2y4QEK4a&vt#dxCS6cA?%?{` z%4@{s{{?|Se!u8l-}mI5k1D8v?VQ~9z`=We;i=Eux^3^;`ub#V?1vuy@TcGPAwXXCZam|wT8M%lB#2Zbixqfh^#UA11DifJ%(bCDD92#l;yp5Cyut*8S<_H;N zLTIHpn2IsBD#NxhKh41sd7G_>X&?$t#zQuM;-o<>#k9u((1O330A1zwiOrelb;)KH zaw4*^F#y!fTkWogo9Ss-Uk*#x(){Ik_O*EJbZUkeh*Ah^t9I>7xbOzgU4Xh309s$- z^(CI4wb@g+dX*{)MK!O{+!>uct?SE@nED~)AcK%9} zMcY-GJ-2rK!urjn+6)bFRJRLPS8rTcS({%cx3QV3SrH?^Kv-1Wz2YwUQ7%PP6bC6F zLhOS1&=jj%8)fD06$1b;NQ{VWk)SFBXlA*zh!{fUJ8VEBrby8YR0Y7i@vJRv;3OL$ zwc;wn_dYh}mS*cR+`r`*{YO_m-|MR`xgS$8DQcoxVJIpD*lKZ2rl=+6BGBaAkNvS~ zY^7cq<&oP?%>XH|p=h&Vz7#)Wy~#>dt2FV@9Uop`t8&Inu#_|?tJ0&Up#a2 z_twj3JWT*N<}%*B|J_#>&;Id?zusC+(Et07{G+?}K5}N^Wq-*qyQ_?{$#|;V?7k5c zWdQu$Z~xD0d2PJf{0oo$qkH!}bYlMbVj8-t(EQ@n@4WlAM-Ohk-uj+h6MHT#pSyR*y=@*{T0Z-p{ZER?uRrq}>v<({`jLBn^xpmVec|L+5rL8R z&g}Z9U;WMN>x&h|ckjC&08Y)G`P}i(ujY2Z)hF(L`tds-`u3UE-@I`lQUI_~&K+?8 zphy{uNe#UmMNG}m7pJDs524Nyh#C)dD=0!KGM*BnA2~2cTok+%hY;#4qFRNh3L=UK zfpK75M2kQ_1rw3dinItIrS-F?%j^ScX*pJE;?V+=)<}KcRw>X?tFUtkF*_{{Hi$81 zh6bt`42Uc0e6~hRPzn&Ms)2Gyj-m%3uR!$noemHzwOe!z#m#HR3ZPmvBu?+2ssI2W z07*naRNi3Q$VNdZ0z@-Y6`~*_W*L<|6ltz1EM>*-!zAh-<%7_3as{N3>GO^Lz7| zQuf@z1a$di-g7iGBe{I4-MTNfs5h_Xdd)WP#rbP>sbP9oq8_YVZ-)zI)9yhfdgJ`s z^d6g-tS+2f8k_2E+B`N~ZEs#*-@ZSsHKpSSh!}Fkyi$rN5OHD_RoyTytv7irB}OdL zd7uV?i+DXQA<-y{DO8E6E3Z&-Mvzzu?83E~0bu|@(d;xmL{PKbMYl{vW8kj#{NESE zy7noRi}P=o;nt~L0DtErrz7-9aW@enwW^H>0yHH@;;tT5ih;66i@_MV3NaA3Qd)6$ zKY?|uSy6A-Rc5ctg_-fa6N4?UpZn@sJtvw)^Q-6o-RHjk-_5U{_sWK+@BQf``yM*} z_E+Ax^2IEzfZ})kc(zeT2w95}9NK;N()!$~xn~gt5JzS8TW9_(h5nHp4_6$(%qiCu zv%@+A;aC{EZTsOjXJ5Urbh0Te04$7VU%PaC*Ua9OCgVo0nur&d;r^LB`f2c2r@v5S z6p)(I9H0HhrNxT}wjXI^bY=14zD>7Ajz@Rib!F-DrTGiDZaoM<_w2fBZu$C+;as9n zm$DJ#a`YS;n1~E>>z6XW=a$mG^Ai;nRW$@rDVn!W-8M76{nhh-xjek)5R}VHuU)=z z`nDZ+03=mybN%fze-0qD(L!WkYUNK}`WIh)^Y`~|KJwmMKk>mMpXtZ3vECqY3glrj z>xIaK4zt>zN|R?<^}NP}qkMB=?M6zKui0CXVucZxhO>+7Hx6&Tr+^*WdTjRQ zmG!)GZt?7Dy>i#idjxR*=36c;Ub@*V|LI$Q^1I*ut?lER-+$n#Pagfj&Es2U#-;;0 z@!np)baH;UAgW7ET|`CoizohSB<)y?jpQ%B`D|0#_uP6c`T9pS5_z9mAe`vMR;8#q zXgMH7rnZOzC_oW21E&}uKovq$l*!}SFi6aZbt|9_q6Y&QufkNn3WQbQE&!og!0IfB z&}7M;MWVRz6p~3n0N>jKf@wy7r>i=5LZpZmBw}Xgi6D8d4Lq$02IhTHV4@jJk)mgQ z(epT>PI7Y_v43WYWattFQ6-BnAd$Rw8aWuCN+BR~LvM(_ZDJ16{X+M?+C~RYK>!ga z+<4!bdwLycO{!8PyPet$vScqFR8V%B4x%q>yDA!A0uqgK<9G!Hb#kTScaf-=usMUA zdmIt1)l5i(kOBoyKJ+<&VJ$hUWGP4_CatG9v?O9QuP2bA#!N+}k)j5PV!z^B-?g;+ z&h^zbd;Piosc%mVM^=kWZ%rD`-w}ypGB<0>SJK`Cu;W(UwTt&0%j+w)wgdps zAGf`?^X8qjWk1d@mbKifS#FH_a@#$z-{ZOSdFR2nbx+#1z1n$4Z~jtat)byynz!t% zwr%aNUTeoUCk#+kp*I!RZ!|X-YKpXZ??jqRGrK0oW_k#aIy5O}Q3Gg1dm;2=$VD3w zH%9sPXwz8LiyRrpVyK+fQB1_#W!H)zfO?J#kklI~5Itz!1;qeCRkUO+?8Rti=E3Ap z8nyNEaAjtEOQ5Ql^g}Xl@lXk&U&T$GB)K7d7#IPjFlk^n*RBz(^6c7>+%B2}g6NC_%39u=x zd5ROj%xcwM9IVw#Ou)#E6f?N7eiaNh^=EufreKlST@vGI3J_Q8C9j6-g&IG)`XA!=%Lx`jZ(LsFR@)v8!NLRVhf!At4|U zV@w2Ca^tccav;C4sj~nGfmAaQBL_1>zz~wJBFqYa-wnFXErsGEiywn7&@p*3v~w{y zio(!}6ptME>_r${d{Y(C>_%{x$^$bax4ErS->0(;Qch=+MLPwjU}r30`ck zWtbc5+;ip~zK9qx1`4Cxy1egALF=V%RlNO+iCn=$0B5+lVY;X$Vgjp({m5}{`EaDH zX=X=r+r8`ey?=$2=dUCH=uZd{Fk-emJ!4R4bp^PufvA?k1QQb|4F#g{&6d3=Bw#=s zufqPjv0k-vFU#!PGHS|bsQoc+iZEgokt4OO8Nk#`jDfRiJ(T68X5q?c{_2o2P(`^& z)epgu-T;HKm>45rWJ-*QxwC$CEKW$wS)>YRhH5(Ww5Iu|6e6LciPS7G2E@ojh-ykE z^4*mVnwKw$mbR_CC@6qPDJAz}f{jRu(_cV6L-IF;wQjqEu)KbKty!F&*wag6_;**; zA`wDI$MX`Atfek_R9ZqTepH&7*9wBAKxgJoP4%bl-2N^nTLdIxHr1p1zW-BC{SRBG z54{um@o!)esq-oVPL1s{vs`lGDsi=a`f!RpVp7vD9RIDaz507I<6GW)_=f|>$RTjZ zQk?h0fPQ;wBs1Zt8BX-45>}BreRF28J%wszv`9!y7}y&>f$)g{iU!VKkXMXJuUifEU zIPouT4i}I*#HZ(+Tz~NCwxd5FIbVL5YXtBP9{(|BRWuxgsAg-WuC;mn+Vfi{w%xJq z*ko_&)oU*}74h7{>0L9sk8Hn-kzc<0A^?2+*!NFVga6~Ne*HJU`Okm*h2J?ncLo6h zQ7;4lTWg2WNn!?psF!+U)p%q=!pJmM^(XsdYxO!Ksp)##s#>esl(L>PfF*WL^;?usT1M5-PdcxXdRRY3tN=BB6` z4gANaOpf95a0M1Ge)OsyD=MuPKQuipCQB52JVOCYOu~-pOYtodw*3^ttRGf?BV8g7|K#|Q!3MMB-G9ZHLAG;_d? zJY1F5@(NW0(h-h@M2wQ5WEBzw$N+0i27qdernXl*ZXerocyim`vB}Lm(fP;P_#H)!b_LUpe?zT7NP5mFH$;|#@__iAR1g>o)>GDVK`S=67o*1NY!Uz`jY8-cRr^4s;p|GY==%%T|rZa<0jvCk}q_`s%f%dQMeq z&9BY9q$-abcw%mKZn?eLqgch>()#l3>b2E+xT!yVbnl&=DPPUkP7Z+Za(E{G=v`0t zxfh8hQvU---b+L;TzYd`&S-XD2pgv4G@o!$y>dm?%Z^1%kSM zWZ8L5OIuz z#36bcBv8lqWDM-NnRhw@wD>;;=dCdZ4rmBwDk30R+ML@B-&xnmRHWq6x`2*^qQ$41 ztYT`zTpMVLsmp1aFj3;*e_F;)NdtsoZrzT|u3@UylGU(iF)&7SYrX?poe@re0k9WR zaOOO8xZ9z$VjV8Q(+STRDI<_ zy*OLWpBV1CwO3W_Ub+-G5bxMGxOri;KHuJ)8=XCQQ$@$8D>YlVxH4R<=PoR*-K-NM zGezb|BnF7+x#o?kA{9|0K!8#dk@}JQDTtZFj;U)Q1IJH9!Yu01*I-%z!CA{<%7ki~ zV)A=#2+lWC&xA7(5f#mmqi2c#LvA$%Kn1(Ja8gaTP4C%QVTkN@_k-}RF}_~4H|w(`{KXmzGHm>%DL zbN%|)U;a%gb;lKT8z(^Ih}3@b#2-F+-;aOt(f@jG=~5N@+c(|v#`$lqj4m78=ZrNo zdFlM0Y@6Qq$dQlTT)#5A{ALkBR4`jAmaC z`j=1s(OO>bXxV1b>x;nZGW^mTfARzOeEPHR`7h>fUhP+dozr_>zI6Qf>@$IRtsS1d ze&*4`PrQEd)mBe|ZOAH4AA zFP?eju{$67SpOgA(oBzSy|VcBj+t8siIC1;fBx954}awTe=us-zVhmC{nd&8`$PBt z+>br}-(J6YDRA7rY2US_i_f3^)2zjl6)u1{kOL6au#vUXvnTGp<>5h^X2eZX+g9sk z0P}^1f0T>LAf%zRE<7S&B=VS)st!v#7UFkTeZF9X4sm7*E4FrJ>GB=Bjy-?&IRF8K zETzuFx#igd+YUWD`-Yhi;)yFKK6uxMe&*djJ!;w=o3=N(H828r=Sg1AZI5E{0x__C zn|J^62Y+^Pb#Z2Vdi&JYXWo4N?80R;Yf2F@VlsmmSOtoibA80?Cjs3u;5fB@2x3sT z;`y8^gse&kUU%#7^DI(@00D9~KnjROvAu$4KDS)7&6rw9^BFZ6p^*eOGRW=Wz0(JltGopZqqCyzZBzaE{ zuz@j#RvHiSFd+gtl&f1dn*wsApk|)5Eg}TCmYdY&Ky2fjGd~^0)bkH&)|4D6cpZV6 z^+WPD^kR}QAX0|h5eta|viC05B{N~f&`4WRjLdZ@38O&VX;L2hqq^6 zZP+ZV)U%haziCublbPYZv~!NNd(J#q$d<7HI%&cJXu-W<+gyc+n}GtrTLq zy7*S4$rP&VtJh|4o;rQw)f-E*mRi%YL{h!__;y0(Ie>npQ!>*qza zk32H!gOt%cEom&Uu-vnUkR*RP!F zLz?K1-B@0H`N~_@Z=O!JFi4YiGkor?KbXIHsT8<8|FVLtj;<}Ptd93*>bAME@Y?e5 z>X~aVnx=j=o=f}s`LBQH^dGG?%PEA4rk2(h=2qTrq(#E4Wg>2wyLsu_+Ud&+Z)u^a zv6(v8Uw`Z0Ew3#tt}R@;d5Wnj*-qVfQLL1WZ&5`Pi))MLZobKsRQSxoD_OIewkmy^ zT3nx>T|K9!Bgp|NQ2<0jSRSoQt1n-DZmn5rr1+f_3XSUen-`zCIaAl$d(w)&a_vN2$~X?*UO1gg)2h{_+_7!{p`Ew?`d9x?5*+W1-CSS!>WOFn z;_dIWDp}MNBD0A0LIg8HYKr8d0O(8+M06TeAg%&M=0=ne5(l@ayS3CM9R$dtYmFG7 ziBBOLfCD7Y-;}QP$IK67q+H~V9XH0(4WdX)8}Kc!#~>sIQ$IyaikV7jx}JCMb~D95 zL;*Vb3sNT;kePO|9i(o_3(#54x*II1Wn7QkKhUri3O!UmB%-?JGQchYzFOm)Fy1ZwVBwW6Eh|#1`vrUKz~wtGg6INWGZU0k1e%{ zc`PQ5AXOP~2#L}p3}%p`k6suPRg)YhfD$_CxEh3%7!k8-D;6S;O;=-+DfL59Xo{rB z(^G@qM3wrH5|8&QGi0JfqyXM^O^B*6$|_<`U~D`9hSn(&wu!TWp=vrcTt`Iwy<*b zbI<IqzB9Vg|Mw<<)q9b0iY9Pw0i9@8YmYYFH#W~ktW*CVYQ)fr6 zmnLx}ppmu#`JD}!|jmfvC3kt zh#3Zq1enEo)EwKikU}!BqIrYt>$0jDfC72qEO78PLp7*2Y!u#V<$yRQcBeG~BN!sJ zQfie}N4P`t(CT@T-6Pzg%Bi zA|mZh0=Mx%;*dBD+tTOgOaKuOwO)vI)?qFQg8(K^G#5o6RqzfgB3AW&0CZ$&ByNkT zVMOvy#iEcXw4!5y>kMYl3$duxt-4(^XN?RWJo566=`%=-W-7%HR0|=40fwlOnWB^i zLqgvOqE~sMNrtPg{Fo^vD>?xdoCVpGHgNEgF*9g# z!`@8m?q?GnkYmLqnrb? z_fH~{m?jF1ikMa$hoy}i7-*zU-t0iYqNeDGNG>9YVxxr+M zXv*SE?a&sji}piQ140JFMik9OIM+I{AL1x07>afTll59s2xyd51CTm~6L1jIN}c;5 zBGIT(?9BQEhAEI+u0z+94x}oIs<2@|^a?Ie5e*#7plC)Q=e03~EFFRMdyFeEzj9qI z57Ny1%4M&TAfLfI;^24J_+8~z_rHcwJ3Mjum2Y4Cq6>6Ln58f>AP8t8ChUaNRwWQR z-?J;OKz3w37zAeTG7QA(yAxuS?Dl2r0tnrw)5MraMV;&fq)vhXDXs!$DOp;kgk2uM zA41>9#9O&ZRM3he0ro=F zY}v$@7DdbuRLx;-k%Fpv2rz?oz0P1_;x&JxQW6DG7=&0Ck2iB-E^1h;6;(Ad3>?(_ zC5Qu7CMuEvz1z_oWa7M6-+>|WJC(1!*lSB$v>;NE&c><$trSw}2vo$a3Zpk9TR)B= zK-~q@fRV*iibbZ1&_)>(_ApA0l{aGMk&Dfp3rhaqU&rvn*%CtTw}!Dm}Usr ziufVa4{4aoFgIhd5`;)l#9Aq$K!E)iix>lp#~7$w+iZ7?YKZ0D#h6E*eaVqylw9z{dQ9azCR(;zBL zjOra67KjLlNC>@;T?aEdm=;1FN=_IIa1c{g8|6%Zp2~9U4FDmcS4JgfF`MF8=h76d zVj4Du4f`<+bIZt)xkyL1YDE(h8q`Hap#?1pfRF+OCIo7WwjzN781^^}v-mJw9D~^T zMWDwd5hAmxjj|!xhF=BO+8l`MqLJP53>(04E3VFg2LD(97`cWfIfP`Uq6L6N3U;;P zB&Ec_1eQf3qoZ2`v6==i<$|uB*8L^dZ=N6J`uvUK7Z+Y$&rRawgQ5Qn%q%d2A)sY3 zCJMx@mR2PZ8AzY0RzpMQN3~jdoVsROvym0Y`4Qqsa(A0Bt0hO~ih`Q8B6Vqmg$X@Y z4L}Z~%#uS$;I+zxs=kYk$XIfY-IF5*`riuvxR@*vp zRI`B8s1(s+R)y$cCg1FgE6Tpqg0`<-i`pE1+xb2!IHQks8U~4vmO) zDUmoZAYm4*OY3t+B+6m{R&mH`k;pA(tw>@PWoLOsVhoUrGLbqfUY*ebqqbEwH6bQ1 zY7~_a2n>sq7@{GlS)@?srb<;SEp|46uDX;J#PV3|wfIGsSpWba07*naRFbpgNC7|* zpfv&A=ytRJx2bRGwJp2O8s8XWuC@2MRk7=3Cw7bzjGZ(B4WrNnSs;XzMj}AUFQ9>x z7C!cle}xwG=ma8^V9~IQ?JBz*SKY_iYt8YX@y&JYPDxdDs?ND*uQlfw-&5Fm zy4x`5BSWMW*f2Kaqr$|Fnk+(RdUSU`vIac6dAlUbBY~+PkuVtBAF0WwW&NJcK6$GgEE9op*#h;8lv}M&u5*HPuqB$)gp5? zCo{YBgy(Dq-6zXN)n*pkC1zw+!8c)Ora~%U{!*{0IsmPj6^Hpvq*2H$H8tNNp6P~Z zk_gnF>f$^`^IrdkTnK4@$Z7WVg+H6Rn zJ1bA9bINJP^#G;@nAs{wblU{YQcgyRn+_;4C*yqR7;^K-Fty3NnQq3b@J@y5>_n!+ zTnO-^222KQQ_JqTI^@|}3zMZ9e^UoUMX;&8&9hgl zoj!8j3@+xX&p97;xLS5?HfD86Jq^!lZ>10BjC3;D%JJTt35M)bs8SIP4 z!(h?vm9BalcFcM;wq%Bb^clJPfa3LW8snm=nQT#k!=7i{ee7;b$^ekv2KcadXPuGv z&EFnLVb60`i9BW+;Cdy$fU!^9BJ?rq<{r{B`^oh&>&$u@_T?k&pZ?jmE?S?uMq$b~ z<%w#;V5+Dqib9r^%Dd{!P}RTl@gKea^pU*Cwl9sqkACsLe(Uzh2k(93gQwsA>Z`AR z^8PoU_K$w`?kA6Ym~k@8qYmkM#4+ofJ*%^&N@`|ai_-$8RgJ%kVX9Dux$+I%eIxe8 zMMqyXfx-{dfA&(pb)rv3ud0WlvL&`0m$%@yb0{S zt12h%-L{Z|;H=nuC{!UU=CZS;Cu>x|-kMzJ^@}J=wWOOR;erwgY4*hIx`9>AU8*XA z%?4#o)XtsBiYl3zM<%zKSv}07npo&&p4BrMUbl)h6bq0&tDd~S_H?L1)mb52ESm&c z+HH%9tTeL=Qg2lTfMJ-O6`T2O-1;JBO*y+tR&Bo8d1kjlslm%GTik=56~(4u5ppp6 zWEHllJM?WH6je54*TFess7I&6l!^`!Hjo~fEY>|TW;L_rtA)w){NK9inH^O(A7?Vk z%v9A?&#Y{DbH4k_pZ-mDIvXFNhB;%l`7X48bvwJ+KK!K*Z8vL?BkJZumMjfSuvgV= zsh0P*LHWb1et#cKeTS^V!1r&P3ZA3rmfK7f`f7|!iR#TXOO7aoeD9Ob-rs+2Hq4wB zY_sjkYzB061$}khys6uKmlieaY#0@Q{nDeeqSypxmJ!OSl=*}iW&_F%I3a>X04vLN zvXVKT-6d6O1HX1A0Je%1RK1$vsT`r#cNgid=6bh$sjGmPuIpvzG~1N*i6Yf#_EsGpHD%T({1?`BzYWWK-e-Ew9r^fvsA?B+?N!}ycbZh9YQ`B%AS>M5%|z<;+tzW)c`{Qe*Q?jL{e z8~^ZkzWVLI{Lz2Qj_YHC0K=GTq`5FRt(TxGU~)W{W(7T^G<}s!F+ZjO%1fZSvvjs9 zS%#+g&_XJG>_TP~%Wt7-utCM7tOho{xKm}LYIK>I38isOk;Iv4EL8;{*mR4IBVtjJ z+~GtNTE$99l=S8!r5!dqg)!+>NZsneKib}!@&8Ason8rD8WrQR_{lXq z&J}Bq=H;h2GtQie zoLSLHcRLxi=v@6>u-O^archY7?B5HzqPIFt5!K(zcA(gYMV7AypYApojE>$8> zK}$0@06s(%G#If<0ovW7N*88JUFn@`G*KNQtv0%n+m4#5ZtMZeoT{2R6_$CDYEvC8 zt!7#2HjLzr3WM8yY`r89##2QJ>c9B7)u1XZtI5zzx~kh0j-p450%7D*^~njXja|Fo?Wap1C+Urs{HOk?$FfEjGY=hT#+P@n{TtLx>WRCo(gQ{ zEmAEh#UdOL1tDk{wHI!#0LhijY*wFHZuXc(y@Nts{doqF)oQT#{A9%D`U#Y|0PLwN z=_uLUHur9@^*pN~xDTJvkEpx*Im^_dJ4?ESeAv4)8AM``VfL8ym<6b-KF?CoBHu4K zqD^s}d2{cUY;jjpixdu>n`w3_U_bcN-@T58l#AqN3m#LE7>mL_V?h(gsMSK=-FTj# z|LC*7iPA6M{_U^ceERv9Kl}Xk&wlyl(_g;+?3b@U{l%A`{qoIczxeXAU%mPChoAn{ z|NGm&q7Sk}fHql|`O5-DM}4zpq&j275(GcmnOr5=6`YvgRu3%7V zz1W|&oTNUwgZ<>HD&4#Vk|m*}iu%{oEY#Uxom~t9Xn~rM7hF}13Te4w*-*7WJl1jP zN_#66W3SpW81W~AL`F%uu2S||$xIXzh#T{|n$+f%y+i4foEZiXDW!9hPYBz&6yQal zEhbvRy__Gu0yry)^6_qWSa+Y54toiBJu8iYye$lx8mdEw3S9kBlhggCp4l5`O#vV! zNJfmcY&IyC8A@P00loXiJ?vI%hWY}`sH7L_P_s6Hxzc}saP0gNzW<;DARh5X)3eI z)Hb%F^*V)~JE@N_;I3m<9XTC(9~(pnqea%vICLqlDcyZk+-zhqLa}G&laDXY=PJGd zQWeyB%2GORnF;B{Y7wqDm=@Jtu3lZ$g1w;%nL%0e-9}2q z6`bX&P#h7PT8os(8S`oEMH9l-{DH)FH08K(i++#+N%eRBjvcf)2l6F2wG`b<&C^I5wqb;#AI4C!oG z7vEX-T2XVX^oGJzR_;Y}#m{+GrDMqA2(3D#iA_pps%chc6_n{nSPuO;8><1-nisic zPP0MVQ%j3lGia_tE`p!qtJ)|MAyQJh_FL7A$ssIa639S{bg}Q?!r7hn9TBML%-F`Q zI*VK~&8;fj$+A)e?j~7o{y3gDj7P@JcQ#Hbk9S3O?9>a*i@z{_&rD-P~#H%A8;TK~;B}4!O$EBSV*Hq@ftTS4Rtt2$KVdL9jaR?LvHJ`xtcL&MON#w2lom|WIeF-tn6MxF8Ao{-fWZ<`|_CMlG1>D zfD=+PPnH7#NTw%%Nll^Z+*nQRjH#*|gw1T|R#M$o=z)4oElKXw*0}ONwpE#b#wiRT16e8pMvmkXC3^-F*Zp+HOL(RV!L!W(}CB zADPT=ceNwa&6qd7*>DzkH>HbqWL{p^&g#wV?()g~aV7d9I@r)9B4+BkMFq#x=XngX z>~tFxBA&G-J~H05ZpR#Mjn%_;R&wVf_Dw7C%|=1kW5ylP&64l&a(&{YMo#(?R;}La z4W8236)l@C=(W02D!UvC(ML`NI9jeWwk%T}=9@X!DXuJM7479m5U&HEXXMN_=p&LL zBWLDjE~2hroO!j}d@N9Pl}Uj$rV8Cn&#Ww=u%!z3F|PRmB4Fne`#|5F$;#>~*`4*f zjAWK9nc2QV*(b>+}~eHMy(wMZS!(YxAI_ zi1Y?iK$ts5u$k@#Icqp_RnbIvhT8}6QN^nNqHa8lUOQ@^n?#144y^?5Z$(z;w*}wcf zs-lbC66RE!jlTeV$4d-3G8HdWDd2V-i&3e7&h1zK-V#gnP;U4 zwiUvYPqrT!0FH{yyderbGVB_PU3I8cbyV&?xCg5&;{A*;DCzNr4E5-$tdA@&(3Sc2q#*Ip@u2+~RA%TTq5NH;L%JnNts9A}U}sI;wBJk!f~lNuO1O+W~!!pjOym zQ!2k216GKN~pqDI}E0MNMXPz=8k$h(Z) z%;C`q)uH~#XwDGOh8}#dVFX5sO=8k$L^pL4ECpE#F_9zs=HtvLLAJQMGKfWlh3K2w znI$4e*38rr=aRWCI%v1R1o0ObgeL|&X#uH ziR=N|9hvE?z)fhI6Ipgq5sgae-p1afPq45%f*Z>at5)5!g4O9f4UP;~zZ@PQ7$87& z&CZ(*0gi~9Z4}WQH8*vlJhQW}B%AdQ6_1FUZ!B)-{prxND!T8!@z+Al*qZiWs``j% z5wws^i7bOg5?Y`sRowK5tQKF2%9Z$jR#h{%Rl8)J2`yjQZwwhZB9B=LY-&edpe=M} z-v+U9IipOKSOvRNUjPq+!>*XQ=D*j+oZ0uyvl^nC*)fUksEVEu*{ISZ%0NLIX3}~h zQEfl?({HKj$^ta&%JmbenGe4IBq41{A$7)FIwG?o1-E=$%0Ac42Gv|tAI&ajw`+4# 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 UEZV2*>;M1&07*qoM6N<$f-XHwjQ{`u From cbe791945138acffd8a21b40b04f201bef02f8b6 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 30 Apr 2026 01:54:42 -0700 Subject: [PATCH 065/130] docs(README): list available --style components in Output style section The Output style section explained how to combine values into --style without listing what those values are, so the recommended path was either to read the man page or grep the source. Adds two tables: the four pre-defined styles (default, full, auto, plain) and the eight individual components (changes, header, header-filename, header-filesize, grid, rule, numbers, snip), copying the descriptions from `bat --help` verbatim. Also calls out the default-enabled set explicitly so the "by default..." sentence in the existing tip block has a referent. Closes #3228 --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 375ec6d7..5ae91cec 100644 --- a/README.md +++ b/README.md @@ -507,6 +507,30 @@ and line numbers but no grid and no file header. Set the `BAT_STYLE` environment variable to make these changes permanent or use `bat`'s [configuration file](#configuration-file). +By default, `bat` enables `changes`, `grid`, `header-filename`, `numbers`, and `snip`. + +The available pre-defined styles are: + +| Style | Description | +|-------|-------------| +| `default` | Enables the recommended style components listed above. | +| `full` | Enables all available components. | +| `auto` | Same as `default`, unless the output is piped. | +| `plain` | Disables all available components. | + +The available individual components are: + +| Component | Description | +|-----------|-------------| +| `changes` | Show Git modification markers. | +| `header` | Alias for `header-filename`. | +| `header-filename` | Show filenames before the content. | +| `header-filesize` | Show file sizes before the content. | +| `grid` | Vertical/horizontal lines to separate the side bar and header from the content. | +| `rule` | Horizontal lines to delimit files. | +| `numbers` | Show line numbers in the side bar. | +| `snip` | Draw separation lines between distinct line ranges. | + >[!tip] > If you specify a default style in `bat`'s config file, you can change which components > are displayed during a single run of `bat` using the `--style` command-line argument. From 816aea06253659123bacb9f76bca4174cc45f1df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:11:34 +0000 Subject: [PATCH 066/130] build(deps): bump assets/syntaxes/02_Extra/PureScript Bumps [assets/syntaxes/02_Extra/PureScript](https://github.com/tellnobody1/sublime-purescript-syntax) from `5acebc1` to `1773f4f`. - [Release notes](https://github.com/tellnobody1/sublime-purescript-syntax/releases) - [Commits](https://github.com/tellnobody1/sublime-purescript-syntax/compare/5acebc18503697be09df047591964e68e80fcf8e...1773f4fddb08560d6bcb354901088e61e9ea0908) --- updated-dependencies: - dependency-name: assets/syntaxes/02_Extra/PureScript dependency-version: 1773f4fddb08560d6bcb354901088e61e9ea0908 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- assets/syntaxes/02_Extra/PureScript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/syntaxes/02_Extra/PureScript b/assets/syntaxes/02_Extra/PureScript index 5acebc18..1773f4fd 160000 --- a/assets/syntaxes/02_Extra/PureScript +++ b/assets/syntaxes/02_Extra/PureScript @@ -1 +1 @@ -Subproject commit 5acebc18503697be09df047591964e68e80fcf8e +Subproject commit 1773f4fddb08560d6bcb354901088e61e9ea0908 From 6edad56b00cb2137d5c0424c0aaa803b0848b072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:43:12 +0000 Subject: [PATCH 067/130] build(deps): bump assets/syntaxes/02_Extra/typst-syntax-highlight Bumps [assets/syntaxes/02_Extra/typst-syntax-highlight](https://github.com/hyrious/typst-syntax-highlight) from `363f0e7` to `5f71d12`. - [Release notes](https://github.com/hyrious/typst-syntax-highlight/releases) - [Commits](https://github.com/hyrious/typst-syntax-highlight/compare/363f0e767c938c615a14912c302db7936f025fc2...5f71d12fa129165bbe51aa867292555cdff6eb75) --- updated-dependencies: - dependency-name: assets/syntaxes/02_Extra/typst-syntax-highlight dependency-version: 5f71d12fa129165bbe51aa867292555cdff6eb75 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- assets/syntaxes/02_Extra/typst-syntax-highlight | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/syntaxes/02_Extra/typst-syntax-highlight b/assets/syntaxes/02_Extra/typst-syntax-highlight index 363f0e76..5f71d12f 160000 --- a/assets/syntaxes/02_Extra/typst-syntax-highlight +++ b/assets/syntaxes/02_Extra/typst-syntax-highlight @@ -1 +1 @@ -Subproject commit 363f0e767c938c615a14912c302db7936f025fc2 +Subproject commit 5f71d12fa129165bbe51aa867292555cdff6eb75 From 1c6e763d7592b06bf10a93238b210c9919e65060 Mon Sep 17 00:00:00 2001 From: Daeraxa <58074586+Daeraxa@users.noreply.github.com> Date: Tue, 5 May 2026 23:42:30 +0100 Subject: [PATCH 068/130] Update readme to fix Fedora section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ae91cec..6c201ef3 100644 --- a/README.md +++ b/README.md @@ -307,7 +307,7 @@ pacman -S bat ### On Fedora -You can install [the `bat` package](https://koji.fedoraproject.org/koji/packageinfo?packageID=27506) from the official [Fedora Modular](https://docs.fedoraproject.org/en-US/modularity/using-modules/) repository. +You can install [the `bat` package](https://koji.fedoraproject.org/koji/packageinfo?packageID=27506) from the official sources: ```bash dnf install bat From c437ad4d27d3d082397cbfc37db6379f06548e4c Mon Sep 17 00:00:00 2001 From: curious-rabbit Date: Wed, 6 May 2026 00:46:35 +0200 Subject: [PATCH 069/130] fix command injection in LESS template --- CHANGELOG.md | 1 + src/lessopen.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4143d9e5..89789c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit) - Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli) - Fix inverted `$LESSCLOSE` warning so bat warns on nonzero exit, not on success. See #3654 (@cuiweixie) - Sanitize control characters in filenames before displaying them in the file header, error messages, and the terminal title, preventing ANSI escape injection via crafted filenames. Closes #3054, see #3691 (@curious-rabbit) diff --git a/src/lessopen.rs b/src/lessopen.rs index c7593b53..8d74d6f8 100644 --- a/src/lessopen.rs +++ b/src/lessopen.rs @@ -14,6 +14,26 @@ use crate::{ input::{Input, InputKind, InputReader, OpenedInput, OpenedInputKind}, }; +/// Wrap `s` in POSIX single quotes so it cannot break out of a shell argument. +fn shell_quote(s: &str) -> String { + let mut quoted = String::with_capacity(s.len() + 2); + quoted.push('\''); + for c in s.chars() { + if c == '\'' { + quoted.push_str("'\\''"); + } else { + quoted.push(c); + } + } + quoted.push('\''); + quoted +} + +/// Substitute the first `%s` in a $LESSOPEN/$LESSCLOSE template with a quoted value. +fn shell_substitute(template: &str, replacement: &str) -> String { + template.replacen("%s", &shell_quote(replacement), 1) +} + /// Preprocess files and/or stdin using $LESSOPEN and $LESSCLOSE pub(crate) struct LessOpenPreprocessor { lessopen: String, @@ -87,7 +107,7 @@ impl LessOpenPreprocessor { None => return input.open(stdin, stdout_identifier), }; - let mut lessopen_command = shell(self.lessopen.replacen("%s", path_str, 1)); + let mut lessopen_command = shell(shell_substitute(&self.lessopen, path_str)); lessopen_command.stdout(Stdio::piped()); let lessopen_output = match lessopen_command.execute_output() { @@ -122,7 +142,7 @@ impl LessOpenPreprocessor { let mut stdin_buffer = Vec::new(); stdin.read_to_end(&mut stdin_buffer)?; - let mut lessopen_command = shell(self.lessopen.replacen("%s", "-", 1)); + let mut lessopen_command = shell(shell_substitute(&self.lessopen, "-")); lessopen_command.stdout(Stdio::piped()); let lessopen_output = match lessopen_command.execute_input_output(&stdin_buffer) @@ -181,7 +201,7 @@ impl LessOpenPreprocessor { lessclose: self .lessclose .as_ref() - .map(|s| s.replacen("%s", &path_str, 1).replacen("%s", &stdout, 1)), + .map(|s| shell_substitute(&shell_substitute(s, &path_str), &stdout)), } } else { Preprocessed { @@ -189,7 +209,7 @@ impl LessOpenPreprocessor { lessclose: self .lessclose .as_ref() - .map(|s| s.replacen("%s", &path_str, 1).replacen("%s", "-", 1)), + .map(|s| shell_substitute(&shell_substitute(s, &path_str), "-")), } }, ))?, @@ -384,4 +404,43 @@ mod tests { Ok(()) } + + #[test] + fn shell_quote_plain_filename() { + assert_eq!(super::shell_quote("file.txt"), "'file.txt'"); + assert_eq!(super::shell_quote("with space.txt"), "'with space.txt'"); + assert_eq!(super::shell_quote(""), "''"); + assert_eq!(super::shell_quote("-"), "'-'"); + } + + #[test] + fn shell_quote_metacharacters_neutralised() { + assert_eq!(super::shell_quote("; rm -rf ~ ;"), "'; rm -rf ~ ;'"); + assert_eq!(super::shell_quote("$(payload)"), "'$(payload)'"); + assert_eq!(super::shell_quote("`payload`"), "'`payload`'"); + assert_eq!(super::shell_quote("a|b&c;d"), "'a|b&c;d'"); + assert_eq!(super::shell_quote("..>/dev/null"), "'..>/dev/null'"); + assert_eq!(super::shell_quote("\n\t"), "'\n\t'"); + } + + #[test] + fn shell_quote_embedded_single_quote() { + assert_eq!(super::shell_quote("it's"), "'it'\\''s'"); + assert_eq!(super::shell_quote("'"), "''\\'''"); + assert_eq!(super::shell_quote("a'b'c"), "'a'\\''b'\\''c'"); + } + + #[test] + fn shell_substitute_only_replaces_first_percent_s() { + assert_eq!(super::shell_substitute("echo %s", "x"), "echo 'x'"); + assert_eq!(super::shell_substitute("echo %s %s", "x"), "echo 'x' %s"); + } + + #[test] + fn shell_substitute_protects_against_filename_injection() { + let template = "|preproc %s"; + let attacker_filename = "; rm -rf ~ ;"; + let result = super::shell_substitute(template, attacker_filename); + assert_eq!(result, "|preproc '; rm -rf ~ ;'"); + } } From 89d7c86b1960dc37d22f5a6b7dadcfc7ffee98fa Mon Sep 17 00:00:00 2001 From: Daeraxa <58074586+Daeraxa@users.noreply.github.com> Date: Tue, 5 May 2026 23:53:13 +0100 Subject: [PATCH 070/130] update fedora package link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c201ef3..0f89875c 100644 --- a/README.md +++ b/README.md @@ -307,7 +307,7 @@ pacman -S bat ### On Fedora -You can install [the `bat` package](https://koji.fedoraproject.org/koji/packageinfo?packageID=27506) from the official sources: +You can install [the `bat` package](https://packages.fedoraproject.org/pkgs/rust-bat/bat/) from the official sources: ```bash dnf install bat From f776d1ad3f0e49be4fd5a5e870c39fc78d863c74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 02:33:22 +0000 Subject: [PATCH 071/130] build(deps): bump bugreport from 0.5.1 to 0.6.0 Bumps [bugreport](https://github.com/sharkdp/bugreport) from 0.5.1 to 0.6.0. - [Release notes](https://github.com/sharkdp/bugreport/releases) - [Commits](https://github.com/sharkdp/bugreport/compare/v0.5.1...v0.6.0) --- updated-dependencies: - dependency-name: bugreport dependency-version: 0.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 202a5295..0bca4269 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "bugreport" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f280f65ce85b880919349bbfcb204930291251eedcb2e5f84ce2f51df969c162" +checksum = "60c65ffd876f5b1dbfe5dde48856f146da8640989cbc11f157762ebfa3c2309e" dependencies = [ "git-version", "shell-escape", diff --git a/Cargo.toml b/Cargo.toml index b63c4e83..79a47c06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ serde_yaml = "0.9.28" semver = "1.0" path_abs = { version = "0.5", default-features = false } clircle = { version = "0.6.1", default-features = false } -bugreport = { version = "0.5.0", optional = true } +bugreport = { version = "0.6.0", optional = true } etcetera = { version = "0.11.0", optional = true } grep-cli = { version = "0.1.12", optional = true } regex = { version = "1.12.2", optional = true } From 443bcb92879a41a87afc462a6a3d4fc4b7762fae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 02:44:23 +0000 Subject: [PATCH 072/130] build(deps): bump clap from 4.5.60 to 4.6.1 Bumps [clap](https://github.com/clap-rs/clap) from 4.5.60 to 4.6.1. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.60...clap_complete-v4.6.1) --- updated-dependencies: - dependency-name: clap dependency-version: 4.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 51 ++++++++++++++++----------------------------------- Cargo.toml | 4 ++-- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bca4269..68a0d77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -43,15 +43,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -252,18 +252,18 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.60" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -943,12 +943,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1309,19 +1303,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "rustix" -version = "0.38.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.1.4" @@ -1331,7 +1312,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -1610,7 +1591,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -1651,12 +1632,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ - "rustix 0.38.43", - "windows-sys 0.59.0", + "rustix", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 79a47c06..ca5eb396 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ default-features = false features = ["parsing"] [dependencies.clap] -version = "4.5.60" +version = "4.6.1" optional = true features = ["wrap_help", "cargo"] @@ -123,7 +123,7 @@ toml = { version = "1.1.1", features = ["preserve_order"] } walkdir = "2.5" [build-dependencies.clap] -version = "4.5.60" +version = "4.6.1" optional = true features = ["wrap_help", "cargo"] From 3bcbc45ab0e75c6be89472d7253e8bfdf01522b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 03:09:47 +0000 Subject: [PATCH 073/130] build(deps): bump regex from 1.12.2 to 1.12.3 Bumps [regex](https://github.com/rust-lang/regex) from 1.12.2 to 1.12.3. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.12.2...1.12.3) --- updated-dependencies: - dependency-name: regex dependency-version: 1.12.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68a0d77f..b22cd63a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1267,9 +1267,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", diff --git a/Cargo.toml b/Cargo.toml index ca5eb396..ae2d5856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ clircle = { version = "0.6.1", default-features = false } bugreport = { version = "0.6.0", optional = true } etcetera = { version = "0.11.0", optional = true } grep-cli = { version = "0.1.12", optional = true } -regex = { version = "1.12.2", optional = true } +regex = { version = "1.12.3", optional = true } walkdir = { version = "2.5", optional = true } bytesize = { version = "2.3.1" } encoding_rs = "0.8.35" @@ -114,7 +114,7 @@ once_cell = "1.20" prettyplease = "0.2.37" proc-macro2 = "1.0.106" quote = "1.0.45" -regex = "1.12.2" +regex = "1.12.3" serde = "1.0" serde_derive = "1.0" serde_with = { version = "3.17.0", default-features = false, features = ["macros"] } From 787a36acb66c76c336b60efed5c58255b96fe480 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 03:29:10 +0000 Subject: [PATCH 074/130] build(deps): bump flate2 from 1.1.2 to 1.1.9 Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.2 to 1.1.9. - [Release notes](https://github.com/rust-lang/flate2-rs/releases) - [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.2...1.1.9) --- updated-dependencies: - dependency-name: flate2 dependency-version: 1.1.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b22cd63a..63bde8d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -583,9 +583,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -984,6 +984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1500,6 +1501,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "smallvec" version = "1.13.2" From 72fc697372d86594742973c98a9074ee6b7e0d97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 03:45:27 +0000 Subject: [PATCH 075/130] build(deps): bump terminal-colorsaurus from 1.0.1 to 1.0.3 Bumps [terminal-colorsaurus](https://github.com/tautropfli/terminal-colorsaurus) from 1.0.1 to 1.0.3. - [Release notes](https://github.com/tautropfli/terminal-colorsaurus/releases) - [Changelog](https://github.com/tautropfli/terminal-colorsaurus/blob/main/changelog.md) - [Commits](https://github.com/tautropfli/terminal-colorsaurus/compare/1.0.1...1.0.3) --- updated-dependencies: - dependency-name: terminal-colorsaurus dependency-version: 1.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63bde8d7..28e370de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1613,9 +1613,9 @@ dependencies = [ [[package]] name = "terminal-colorsaurus" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8909f33134da34b43f69145e748790de650a6abd84faf1f82e773444dd293ec8" +checksum = "7a46bb5364467da040298c573c8a95dbf9a512efc039630409a03126e3703e90" dependencies = [ "cfg-if", "libc", @@ -1628,9 +1628,9 @@ dependencies = [ [[package]] name = "terminal-trx" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662a3cd5ca570df622e848ef18b50c151e65c9835257465417242243b0bce783" +checksum = "3b3f27d9a8a177e57545481faec87acb45c6e854ed1e5a3658ad186c106f38ed" dependencies = [ "cfg-if", "libc", From 3be4e30e00eaf62073e0d38e43c799bdae79e645 Mon Sep 17 00:00:00 2001 From: curious-rabbit Date: Wed, 6 May 2026 10:58:02 +0200 Subject: [PATCH 076/130] improve patch --- src/lessopen.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/lessopen.rs b/src/lessopen.rs index 8d74d6f8..ad8fe69a 100644 --- a/src/lessopen.rs +++ b/src/lessopen.rs @@ -14,8 +14,16 @@ use crate::{ input::{Input, InputKind, InputReader, OpenedInput, OpenedInputKind}, }; -/// Wrap `s` in POSIX single quotes so it cannot break out of a shell argument. +/// Wrap `s` in POSIX single quotes. An embedded `'` is encoded as `'\''` +/// (close, escaped quote, reopen) since `'...'` has no escape character. fn shell_quote(s: &str) -> String { + if !s.contains('\'') { + let mut quoted = String::with_capacity(s.len() + 2); + quoted.push('\''); + quoted.push_str(s); + quoted.push('\''); + return quoted; + } let mut quoted = String::with_capacity(s.len() + 2); quoted.push('\''); for c in s.chars() { @@ -34,6 +42,25 @@ fn shell_substitute(template: &str, replacement: &str) -> String { template.replacen("%s", &shell_quote(replacement), 1) } +/// Substitute the first two `%s` occurrences positionally in one pass; chaining +/// `shell_substitute` twice would mis-target if `first` itself contains `%s`. +fn shell_substitute_two(template: &str, first: &str, second: &str) -> String { + let mut out = String::with_capacity(template.len() + first.len() + second.len() + 4); + let mut remaining = template; + let mut to_substitute = [Some(first), Some(second)]; + for slot in &mut to_substitute { + if let Some(idx) = remaining.find("%s") { + out.push_str(&remaining[..idx]); + out.push_str(&shell_quote(slot.take().unwrap())); + remaining = &remaining[idx + 2..]; + } else { + break; + } + } + out.push_str(remaining); + out +} + /// Preprocess files and/or stdin using $LESSOPEN and $LESSCLOSE pub(crate) struct LessOpenPreprocessor { lessopen: String, @@ -201,7 +228,7 @@ impl LessOpenPreprocessor { lessclose: self .lessclose .as_ref() - .map(|s| shell_substitute(&shell_substitute(s, &path_str), &stdout)), + .map(|s| shell_substitute_two(s, &path_str, &stdout)), } } else { Preprocessed { @@ -209,7 +236,7 @@ impl LessOpenPreprocessor { lessclose: self .lessclose .as_ref() - .map(|s| shell_substitute(&shell_substitute(s, &path_str), "-")), + .map(|s| shell_substitute_two(s, &path_str, "-")), } }, ))?, @@ -443,4 +470,26 @@ mod tests { let result = super::shell_substitute(template, attacker_filename); assert_eq!(result, "|preproc '; rm -rf ~ ;'"); } + + #[test] + fn shell_substitute_two_replaces_first_two_placeholders() { + assert_eq!( + super::shell_substitute_two("echo %s and %s done", "a", "b"), + "echo 'a' and 'b' done" + ); + // A third %s is left alone. + assert_eq!( + super::shell_substitute_two("echo %s %s %s", "a", "b"), + "echo 'a' 'b' %s" + ); + } + + #[test] + fn shell_substitute_two_handles_percent_s_in_first_argument() { + // The chained-substitute approach would replace the injected `%s` here. + assert_eq!( + super::shell_substitute_two("a %s b %s c", "%s", "second"), + "a '%s' b 'second' c" + ); + } } From a0c95618c4bf6d3de9e932d1a3ec85b261b629fc Mon Sep 17 00:00:00 2001 From: June Kim Date: Sat, 9 May 2026 03:35:54 -0700 Subject: [PATCH 077/130] fix: pass --color=never --decorations=never in zsh completions When BAT_OPTS contains --color=always or --decorations=always, the zsh completion script's calls to --list-languages and --list-themes produce ANSI escape codes that corrupt tab completion results. Pass --color=never --decorations=never explicitly so completion output is always plain text regardless of user config. Fixes #3733 --- assets/completions/bat.zsh.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index f8de56e4..4fddcbb4 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -90,20 +90,20 @@ _{{PROJECT_EXECUTABLE}}_main() { languages) local IFS=$'\n' local -a languages - languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }')"} ) + languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }')"} ) _describe 'language' languages && ret=0 ;; themes) local -a themes expl - themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} ) + themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-themes)"} ) _wanted themes expl 'theme' compadd -a themes && ret=0 ;; theme_preferences) local -a themes expl - themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} ) + themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-themes)"} ) _wanted themes expl 'theme' compadd -a themes && ret=0 ;; From 5f952066fb6196c33862d39e7fdf9f0f5960f5a2 Mon Sep 17 00:00:00 2001 From: truffle Date: Sun, 10 May 2026 09:19:54 +0000 Subject: [PATCH 078/130] fix: only offer language names in zsh tab completion for `-l` The previous awk script in `bat.zsh.in` split each line of `bat --list-languages` on `:` or `,` and emitted every field as a completion candidate, including the second column. That column lists file matchers, which can be plain extensions (`rs`), globs (`*.rs`), absolute paths (`/etc/profile`), or filenames (`Containerfile`). None of those parse as `-l` arguments, so completing them produces `unknown syntax` errors. Switch to splitting on `:` only and emit the language name as the completion value with the file-matcher list as its description, which matches the bash completion's behavior. Closes #3735. --- CHANGELOG.md | 1 + assets/completions/bat.zsh.in | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89789c54..4e8e8531 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ - Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l) - Fixed syntax tests path, see #3610 (@foxfromworld) - Fix zsh tab completion word-splitting language names containing spaces (e.g. `HTML (Jinja2)`, `Apache Conf`), see #3693 (@YoshKoz) +- Fix zsh tab completion offering invalid `-l` arguments (file globs, paths, hidden filenames) sourced from the second column of `--list-languages`. Closes #3735, see #PR_NUMBER (@truffle-dev) ## Other - Use git version of cross. See #3533 (@OctopusET) diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 4fddcbb4..3991b996 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -90,7 +90,12 @@ _{{PROJECT_EXECUTABLE}}_main() { languages) local IFS=$'\n' local -a languages - languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }')"} ) + # Only offer language names as completion values. The second column + # of `--list-languages` mixes plain extensions with globs (`*.rs`), + # absolute paths (`/etc/profile`), and full filenames + # (`Containerfile`); none of those parse as `-l` arguments. See + # https://github.com/sharkdp/bat/issues/3735. + languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages | awk -F: '{ printf("%s:%s\n", $1, $2) }')"} ) _describe 'language' languages && ret=0 ;; From 98df25434e8bf160edfab5bae0c978df9766207d Mon Sep 17 00:00:00 2001 From: truffle Date: Sun, 10 May 2026 09:20:40 +0000 Subject: [PATCH 079/130] fix: reference PR number in changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8e8531..81f36a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ - Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l) - Fixed syntax tests path, see #3610 (@foxfromworld) - Fix zsh tab completion word-splitting language names containing spaces (e.g. `HTML (Jinja2)`, `Apache Conf`), see #3693 (@YoshKoz) -- Fix zsh tab completion offering invalid `-l` arguments (file globs, paths, hidden filenames) sourced from the second column of `--list-languages`. Closes #3735, see #PR_NUMBER (@truffle-dev) +- Fix zsh tab completion offering invalid `-l` arguments (file globs, paths, hidden filenames) sourced from the second column of `--list-languages`. Closes #3735, see #3737 (@truffle-dev) ## Other - Use git version of cross. See #3533 (@OctopusET) From 138d70fd4c93759a444c0c9fcc885a0c2202f9d5 Mon Sep 17 00:00:00 2001 From: truffle Date: Sun, 10 May 2026 14:10:54 +0000 Subject: [PATCH 080/130] fix(zsh): drop redundant awk pipeline in language completion `bat --list-languages` already emits each entry in `name:matchers` form, which is the format `_describe` consumes directly. The previous awk script split each line on `:` and re-emitted `$1:$2`, which is byte-identical to the input. Verified with `diff <(bat --list-languages) <(bat --list-languages | awk -F: '{ printf("%s:%s\\n", $1, $2) }')` against the current syntax set. --- assets/completions/bat.zsh.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 3991b996..efdb185e 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -90,12 +90,12 @@ _{{PROJECT_EXECUTABLE}}_main() { languages) local IFS=$'\n' local -a languages - # Only offer language names as completion values. The second column - # of `--list-languages` mixes plain extensions with globs (`*.rs`), - # absolute paths (`/etc/profile`), and full filenames - # (`Containerfile`); none of those parse as `-l` arguments. See + # `--list-languages` emits one `name:matchers` line per language, + # which `_describe` parses as `value:description`. Only the + # language name is offered as the completion value; the matchers + # show up as the menu description. See # https://github.com/sharkdp/bat/issues/3735. - languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages | awk -F: '{ printf("%s:%s\n", $1, $2) }')"} ) + languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages)"} ) _describe 'language' languages && ret=0 ;; From c9fa10e76bd3d220106f214790fe81e0bb96753b Mon Sep 17 00:00:00 2001 From: Justin Su Date: Mon, 18 May 2026 02:45:50 -0400 Subject: [PATCH 081/130] Include `.ssh/` subdirectories in SSH Config syntax mapping --- src/syntax_mapping/builtins/common/50-ssh.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax_mapping/builtins/common/50-ssh.toml b/src/syntax_mapping/builtins/common/50-ssh.toml index 6ec24050..10bce980 100644 --- a/src/syntax_mapping/builtins/common/50-ssh.toml +++ b/src/syntax_mapping/builtins/common/50-ssh.toml @@ -1,2 +1,2 @@ [mappings] -"SSH Config" = ["**/.ssh/config"] +"SSH Config" = ["**/.ssh/**/config"] From 43c4957e18b5c086981ee9fc5dda80c3e273dd57 Mon Sep 17 00:00:00 2001 From: Justin Su Date: Mon, 18 May 2026 03:08:39 -0400 Subject: [PATCH 082/130] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f36a38..b8d723d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - Map several Google Cloud CLI config files to their appropriate syntax #3635 (@victor-gp) - Map all ignore dotfiles to Git Ignore syntax #3636 (@victor-gp) - Improved Kotlin syntax, see #3699 (@guille) +- Include subdirectories in SSH Config syntax mapping, see #3758 (@injust) ## Themes From de396794cff338a2b07947721d8eeadc0274f193 Mon Sep 17 00:00:00 2001 From: Justin Su Date: Mon, 18 May 2026 03:22:25 -0400 Subject: [PATCH 083/130] Add Ghostty syntax mapping --- src/syntax_mapping/builtins/unix-family/50-ghostty.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/syntax_mapping/builtins/unix-family/50-ghostty.toml diff --git a/src/syntax_mapping/builtins/unix-family/50-ghostty.toml b/src/syntax_mapping/builtins/unix-family/50-ghostty.toml new file mode 100644 index 00000000..71804e4b --- /dev/null +++ b/src/syntax_mapping/builtins/unix-family/50-ghostty.toml @@ -0,0 +1,2 @@ +[mappings] +"INI" = ["**/ghostty/**/*.ghostty", "**/ghostty/themes/*"] From 0ef0342ff2149b6bc11c97f4876658aef730ee61 Mon Sep 17 00:00:00 2001 From: Justin Su Date: Mon, 18 May 2026 03:33:50 -0400 Subject: [PATCH 084/130] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f36a38..358926f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - Map several Google Cloud CLI config files to their appropriate syntax #3635 (@victor-gp) - Map all ignore dotfiles to Git Ignore syntax #3636 (@victor-gp) - Improved Kotlin syntax, see #3699 (@guille) +- Add Ghostty syntax mapping, see #3759 (@injust) ## Themes From 66e336bcfbe820f4155125c6a3f44c16fefbcd23 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Tue, 19 May 2026 04:19:31 -0700 Subject: [PATCH 085/130] fix(completions): force --no-paging on bat invocations in completion scripts (#3760) Every shell completion script (bash, zsh, fish, PowerShell) shells out to bat to enumerate languages/themes for tab completion candidates. If the user has wired bat into LESSOPEN (e.g. LESSOPEN='|-bat -f -pp %s'), bat's normal pager auto-detection can engage when stdout looks like a terminal at completion time and reflect ANSI escape sequences back into the candidate list. The result is the issue's reproducer: tab completion expands 'Per' to '\033[38;2;248;248;242mPerl' instead of 'Perl'. The list-languages/list-themes calls are always meant to be machine- readable, so they should never page. Pass --no-paging explicitly to every bat invocation inside the four completion files. The flag is the public alias for --paging=never (already documented in bat --help) and is the same form completion scripts elsewhere in the codebase use. Touches the four completion files only; no production code changes. --- CHANGELOG.md | 1 + assets/completions/_bat.ps1.in | 4 ++-- assets/completions/bat.bash.in | 6 +++--- assets/completions/bat.fish.in | 10 +++++----- assets/completions/bat.zsh.in | 6 +++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2570c6b9..08c502a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- 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) - Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli) - Fix inverted `$LESSCLOSE` warning so bat warns on nonzero exit, not on success. See #3654 (@cuiweixie) diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index 97f76932..b90e2a33 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -14,12 +14,12 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script $ArrayPrint = @('unicode', 'caret') function Get-MyThemes(){ - $themes = {{PROJECT_EXECUTABLE}} --list-themes | ForEach-Object {$_ -replace "^(.*)$", '''$1'''} | select-object + $themes = {{PROJECT_EXECUTABLE}} --no-paging --list-themes | ForEach-Object {$_ -replace "^(.*)$", '''$1'''} | select-object return $themes } function Get-MyLanguages(){ - $themes = {{PROJECT_EXECUTABLE}} --list-languages | ForEach-Object{[pscustomobject]@{MyParameter=$_.Substring(0,$_.IndexOf(":")).Trim();MyDescription=$_.Substring($_.IndexOf(":")+1)}} | select-object + $themes = {{PROJECT_EXECUTABLE}} --no-paging --list-languages | ForEach-Object{[pscustomobject]@{MyParameter=$_.Substring(0,$_.IndexOf(":")).Trim();MyDescription=$_.Substring($_.IndexOf(":")+1)}} | select-object return $themes } diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 6e45bd19..813c532e 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -80,7 +80,7 @@ _bat() { -l | --language) local IFS=$'\n' COMPREPLY=($(compgen -W "$( - "$1" --list-languages | while IFS=: read -r lang _; do + "$1" --no-paging --list-languages | while IFS=: read -r lang _; do printf "%s\n" "$lang" done )" -- "$cur")) @@ -150,14 +150,14 @@ _bat() { ;; --theme) local IFS=$'\n' - COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur")) + COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --no-paging --list-themes)" -- "$cur")) __bat_escape_completions return 0 ;; --theme-dark | \ --theme-light) local IFS=$'\n' - COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur")) + COMPREPLY=($(compgen -W "$("$1" --no-paging --list-themes)" -- "$cur")) __bat_escape_completions return 0 ;; diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 2100338c..9d993cb4 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -15,11 +15,11 @@ function __bat_complete_files -a token end function __bat_complete_one_language -a comp - command $bat --list-languages | string split -f1 : | string match -e "$comp" + command $bat --no-paging --list-languages | string split -f1 : | string match -e "$comp" end function __bat_complete_list_languages - for spec in (command $bat --list-languages) + for spec in (command $bat --no-paging --list-languages) set -l name (string split -f1 : $spec) for ext in (string split -f2 : $spec | string split ,) test -n "$ext"; or continue @@ -234,11 +234,11 @@ complete -c $bat -l tabs -x -a "$tabs_opts" -d "Set tab width" -n __bat_no_excl_ complete -c $bat -l terminal-width -x -d "Set terminal , +, or -" -n __bat_no_excl_args -complete -c $bat -l theme -x -a "$special_themes(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args +complete -c $bat -l theme -x -a "$special_themes(command $bat --no-paging --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args -complete -c $bat -l theme-dark -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args +complete -c $bat -l theme-dark -x -a "(command $bat --no-paging --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args -complete -c $bat -l theme-light -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for light backgrounds" -n __bat_no_excl_args +complete -c $bat -l theme-light -x -a "(command $bat --no-paging --list-themes | command cat)" -d "Set the syntax highlighting theme for light backgrounds" -n __bat_no_excl_args complete -c $bat -s u -l unbuffered -d "Enable unbuffered input reading for streaming use cases" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index efdb185e..94001875 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -95,20 +95,20 @@ _{{PROJECT_EXECUTABLE}}_main() { # language name is offered as the completion value; the matchers # show up as the menu description. See # https://github.com/sharkdp/bat/issues/3735. - languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-languages)"} ) + languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-languages)"} ) _describe 'language' languages && ret=0 ;; themes) local -a themes expl - themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-themes)"} ) + themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-themes)"} ) _wanted themes expl 'theme' compadd -a themes && ret=0 ;; theme_preferences) local -a themes expl - themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --color=never --decorations=never --list-themes)"} ) + themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-themes)"} ) _wanted themes expl 'theme' compadd -a themes && ret=0 ;; From fd53693e1fa953f953e934fae53eff9128d6ae9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 03:30:20 +0000 Subject: [PATCH 086/130] build(deps): bump plist from 1.7.0 to 1.9.0 Bumps [plist](https://github.com/ebarnard/rust-plist) from 1.7.0 to 1.9.0. - [Release notes](https://github.com/ebarnard/rust-plist/releases) - [Changelog](https://github.com/ebarnard/rust-plist/blob/master/CHANGELOG.md) - [Commits](https://github.com/ebarnard/rust-plist/compare/v1.7.0...v1.9.0) --- updated-dependencies: - dependency-name: plist dependency-version: 1.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28e370de..001ba432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1153,9 +1153,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" dependencies = [ "base64", "indexmap", @@ -1221,9 +1221,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index ae2d5856..99b0447e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ optional = true features = ["wrap_help", "cargo"] [target.'cfg(target_os = "macos")'.dependencies] -plist = "1.7.0" +plist = "1.9.0" [dev-dependencies] assert_cmd = "2.0.16" From 2d29ecb9395323a8f055bc1449bcf99b2ddba2d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 03:59:42 +0000 Subject: [PATCH 087/130] build(deps): bump minus from 5.6.1 to 5.7.1 Bumps [minus](https://github.com/AMythicDev/minus) from 5.6.1 to 5.7.1. - [Changelog](https://github.com/AMythicDev/minus/blob/main/CHANGELOG.md) - [Commits](https://github.com/AMythicDev/minus/compare/v5.6.1...v5.7.1) --- updated-dependencies: - dependency-name: minus dependency-version: 5.7.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 228 +++++++++++++++++++++-------------------------------- Cargo.toml | 2 +- 2 files changed, 93 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 001ba432..39ecd511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,7 +154,7 @@ dependencies = [ "syntect", "tempfile", "terminal-colorsaurus", - "thiserror 2.0.16", + "thiserror", "toml", "unicode-segmentation", "unicode-width", @@ -189,9 +189,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bstr" @@ -316,6 +316,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -367,15 +376,17 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" -version = "0.27.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ "bitflags", "crossterm_winapi", - "libc", - "mio 0.8.11", + "derive_more", + "document-features", + "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -447,6 +458,28 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "difflib" version = "0.4.0" @@ -470,6 +503,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.13.0" @@ -633,7 +675,7 @@ dependencies = [ "cfg-if", "libc", "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -955,6 +997,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.12" @@ -989,40 +1037,28 @@ dependencies = [ [[package]] name = "minus" -version = "5.6.1" +version = "5.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093bd0520d2a37943566a73750e6d44094dac75d66a978d1f0d97ffc78686832" +checksum = "2657ec5f6a6edc55a85c8db0c572436b612eb036af00e7eee47e0a622095ed96" dependencies = [ "crossbeam-channel", "crossterm", - "once_cell", "parking_lot", "regex", "textwrap", - "thiserror 1.0.69", + "thiserror", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "mio" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" -dependencies = [ - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1081,9 +1117,6 @@ name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -dependencies = [ - "parking_lot_core", -] [[package]] name = "onig" @@ -1127,7 +1160,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1304,6 +1337,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.4" @@ -1488,7 +1530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 0.8.11", + "mio", "signal-hook", ] @@ -1570,7 +1612,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror 2.0.16", + "thiserror", "walkdir", "yaml-rust", ] @@ -1613,16 +1655,16 @@ dependencies = [ [[package]] name = "terminal-colorsaurus" -version = "1.0.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a46bb5364467da040298c573c8a95dbf9a512efc039630409a03126e3703e90" +checksum = "3f7226dad4b1817567c1e2f5d453897ef36abe79def7783af3fa241a694e30b3" dependencies = [ "cfg-if", "libc", "memchr", - "mio 1.1.0", + "mio", "terminal-trx", - "windows-sys 0.61.2", + "windows-sys 0.59.0", "xterm-color", ] @@ -1662,33 +1704,13 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.16", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1928,7 +1950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ "windows-core 0.56.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1938,7 +1960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ "windows-core 0.57.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1950,7 +1972,7 @@ dependencies = [ "windows-implement 0.56.0", "windows-interface 0.56.0", "windows-result", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1962,7 +1984,7 @@ dependencies = [ "windows-implement 0.57.0", "windows-interface 0.57.0", "windows-result", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2021,16 +2043,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -2039,7 +2052,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2051,67 +2064,34 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2124,48 +2104,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 99b0447e..b366f738 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ thiserror = "2.0" wild = { version = "2.2", optional = true } content_inspector = "0.2.4" shell-words = { version = "1.1.1", optional = true } -minus = { version = "5.6", optional = true, features = [ +minus = { version = "5.7", optional = true, features = [ "dynamic_output", "search", ] } From 67aa491313d277396324c986ae0f077ffac150e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:28:21 +0000 Subject: [PATCH 088/130] build(deps): bump indexmap from 2.13.0 to 2.14.0 Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.13.0 to 2.14.0. - [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md) - [Commits](https://github.com/indexmap-rs/indexmap/compare/2.13.0...2.14.0) --- updated-dependencies: - dependency-name: indexmap dependency-version: 2.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39ecd511..6d4596a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,9 +752,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "icu_collections" @@ -903,12 +903,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] diff --git a/Cargo.toml b/Cargo.toml index b366f738..a4250458 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ nix = { version = "0.31", default-features = false, features = ["term"] } [build-dependencies] anyhow = "1.0.97" -indexmap = { version = "2.13.0", features = ["serde"] } +indexmap = { version = "2.14.0", features = ["serde"] } itertools = "0.14.0" once_cell = "1.20" prettyplease = "0.2.37" From bcd1d3f45b55513a8d1d184d0997f6c7f4fc2301 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:48:18 +0000 Subject: [PATCH 089/130] build(deps): bump predicates from 3.1.3 to 3.1.4 Bumps [predicates](https://github.com/assert-rs/predicates-rs) from 3.1.3 to 3.1.4. - [Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/assert-rs/predicates-rs/compare/v3.1.3...v3.1.4) --- updated-dependencies: - dependency-name: predicates dependency-version: 3.1.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d4596a2..c35aefd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1205,9 +1205,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "predicates" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" dependencies = [ "anstyle", "difflib", diff --git a/Cargo.toml b/Cargo.toml index a4250458..9538844e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,7 @@ plist = "1.9.0" assert_cmd = "2.0.16" expect-test = "1.5.0" serial_test = { version = "2.0.0", default-features = false } -predicates = "3.1.3" +predicates = "3.1.4" wait-timeout = "0.2.1" tempfile = "3.27.0" serde = { version = "1.0", features = ["derive"] } From 8033b1e7be06f31cafb1e2e400f947aea7956295 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 05:03:33 +0000 Subject: [PATCH 090/130] build(deps): bump nix from 0.31.2 to 0.31.3 Bumps [nix](https://github.com/nix-rust/nix) from 0.31.2 to 0.31.3. - [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md) - [Commits](https://github.com/nix-rust/nix/compare/v0.31.2...v0.31.3) --- updated-dependencies: - dependency-name: nix dependency-version: 0.31.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c35aefd1..bad6940c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,9 +951,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libgit2-sys" @@ -1063,9 +1063,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags", "cfg-if", From e9b552d5d06a754384b7ce5803f513f41d64a1ce Mon Sep 17 00:00:00 2001 From: Matei6942 Date: Fri, 5 Jun 2026 17:40:39 +0300 Subject: [PATCH 091/130] docs: fix SML syntax source link --- doc/assets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/assets.md b/doc/assets.md index f9297d42..e073cff0 100644 --- a/doc/assets.md +++ b/doc/assets.md @@ -97,7 +97,7 @@ The following files have been manually modified after converting from a `.tmLang as it is not kept in a standalone repository. The file is generated from https://github.com/open-policy-agent/opa/blob/master/misc/syntax/textmate/Rego.tmLanguage * `SML.sublime_syntax` has been added manually from - https://github.com/seanjames777/SML-Language-Definitiona as it is not + https://github.com/seanjames777/SML-Language-Definition as it is not kept in a standalone repository. The file generated is from https://github.com/seanjames777/SML-Language-Definition/blob/master/sml.tmLanguage * `Cabal.sublime_syntax` has been added manually from From d28aa4a8b4d9fe4fb5567e708cde82d03953f1ee Mon Sep 17 00:00:00 2001 From: weili <541602953@qq.com> Date: Tue, 16 Jun 2026 06:58:55 +0000 Subject: [PATCH 092/130] Fix capacity-overflow panic in print_snip at --terminal-width 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `InteractivePrinter::print_snip` computes the snip separator width as `term_width - panel_count - snip_left_count - title_count`. At `--terminal-width 1` the panel is disabled and `title_count == 2`, so the subtraction underflows `usize` (release has no overflow-checks, so it wraps to ~usize::MAX); `str::repeat` then aborts with "capacity overflow" whenever a snip separator is emitted (two or more disjoint line ranges, or a diff gap) — which the default style does. Use `saturating_sub` for the repeat counts so they clamp to 0 instead of underflowing. Added an integration test. --- CHANGELOG.md | 1 + src/printer.rs | 18 +++++++++++++++--- tests/integration_tests.rs | 13 +++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c502a5..c57dd731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- 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) - Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit) - Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli) diff --git a/src/printer.rs b/src/printer.rs index 17f7aa30..fc8a70ba 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -603,11 +603,23 @@ impl Printer for InteractivePrinter<'_> { let title = "8<"; let title_count = title.chars().count(); - let snip_left = "─ ".repeat((self.config.term_width - panel_count - (title_count / 2)) / 4); + let snip_left = "─ ".repeat( + self.config + .term_width + .saturating_sub(panel_count) + .saturating_sub(title_count / 2) + / 4, + ); let snip_left_count = snip_left.chars().count(); // Can't use .len() with Unicode. - let snip_right = - " ─".repeat((self.config.term_width - panel_count - snip_left_count - title_count) / 2); + let snip_right = " ─".repeat( + self.config + .term_width + .saturating_sub(panel_count) + .saturating_sub(snip_left_count) + .saturating_sub(title_count) + / 2, + ); writeln!( handle, diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 432faed8..2f2dcce8 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -305,6 +305,19 @@ fn line_range_multiple() { .stdout("line 1\nline 2\nline 4\n"); } +#[test] +fn snip_at_terminal_width_one_does_not_panic() { + bat() + .arg("multiline.txt") + .arg("--style=snip") + .arg("--color=always") + .arg("--terminal-width=1") + .arg("--line-range=1:2") + .arg("--line-range=4:4") + .assert() + .success(); +} + #[test] fn line_range_multiple_with_context() { bat() From 6b8887133d3c3bb3df0b8e0e495702e573eba36a Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Sun, 17 May 2026 21:47:45 -0600 Subject: [PATCH 093/130] feat: add syntax highlighting for `Caddyfile` --- .gitmodules | 3 +++ CHANGELOG.md | 1 + assets/syntaxes/02_Extra/Caddy | 1 + .../highlighted/Caddyfile/Caddyfile | 25 +++++++++++++++++++ tests/syntax-tests/source/Caddyfile/Caddyfile | 25 +++++++++++++++++++ 5 files changed, 55 insertions(+) create mode 160000 assets/syntaxes/02_Extra/Caddy create mode 100644 tests/syntax-tests/highlighted/Caddyfile/Caddyfile create mode 100644 tests/syntax-tests/source/Caddyfile/Caddyfile diff --git a/.gitmodules b/.gitmodules index 20e0bbb4..68c07c91 100644 --- a/.gitmodules +++ b/.gitmodules @@ -281,3 +281,6 @@ [submodule "assets/syntaxes/02_Extra/Kotlin"] path = assets/syntaxes/02_Extra/Kotlin url = https://github.com/guille/sublime-kotlin +[submodule "assets/syntaxes/02_Extra/Caddy"] + path = assets/syntaxes/02_Extra/Caddy + url = https://github.com/caddyserver/sublimetext.git diff --git a/CHANGELOG.md b/CHANGELOG.md index c57dd731..6e98c19a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ - Improved Kotlin syntax, see #3699 (@guille) - Include subdirectories in SSH Config syntax mapping, see #3758 (@injust) - Add Ghostty syntax mapping, see #3759 (@injust) +- Add syntax highlighting for `Caddyfile` #3789 (@CosmicHorrorDev) ## Themes diff --git a/assets/syntaxes/02_Extra/Caddy b/assets/syntaxes/02_Extra/Caddy new file mode 160000 index 00000000..cd7d132d --- /dev/null +++ b/assets/syntaxes/02_Extra/Caddy @@ -0,0 +1 @@ +Subproject commit cd7d132da38291b632995f84ae1e920458943b18 diff --git a/tests/syntax-tests/highlighted/Caddyfile/Caddyfile b/tests/syntax-tests/highlighted/Caddyfile/Caddyfile new file mode 100644 index 00000000..ab2f6a55 --- /dev/null +++ b/tests/syntax-tests/highlighted/Caddyfile/Caddyfile @@ -0,0 +1,25 @@ +(logging) { + log { + output file /var/log/caddy.log + } +} + +# a comment +localhost:8080, example.com { + root * /var/www/site + file_server + reverse_proxy /.well-known/matrix/* localhost:8008 { + header_up Host {upstream_hostport} + } + import logging +} + +www.example.com { + redir https://example.com{uri} permanent + import logging +} + +status.example.com { + reverse_proxy localhost:3002 + import logging +} diff --git a/tests/syntax-tests/source/Caddyfile/Caddyfile b/tests/syntax-tests/source/Caddyfile/Caddyfile new file mode 100644 index 00000000..1fa41b9d --- /dev/null +++ b/tests/syntax-tests/source/Caddyfile/Caddyfile @@ -0,0 +1,25 @@ +(logging) { + log { + output file /var/log/caddy.log + } +} + +# a comment +localhost:8080, example.com { + root * /var/www/site + file_server + reverse_proxy /.well-known/matrix/* localhost:8008 { + header_up Host {upstream_hostport} + } + import logging +} + +www.example.com { + redir https://example.com{uri} permanent + import logging +} + +status.example.com { + reverse_proxy localhost:3002 + import logging +} From e7025d9bdbc332aab7fc5e52950f269ba3a2ffc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:38:53 +0000 Subject: [PATCH 094/130] build(deps): bump assets/syntaxes/02_Extra/vscode-wgsl Bumps [assets/syntaxes/02_Extra/vscode-wgsl](https://github.com/PolyMeilex/vscode-wgsl) from `acf2671` to `a285c38`. - [Release notes](https://github.com/PolyMeilex/vscode-wgsl/releases) - [Commits](https://github.com/PolyMeilex/vscode-wgsl/compare/acf26718d7a327377641e31d8f9a9dab376efa84...a285c38f74eba2eb5c5a06be8d95b9f581338509) --- updated-dependencies: - dependency-name: assets/syntaxes/02_Extra/vscode-wgsl dependency-version: a285c38f74eba2eb5c5a06be8d95b9f581338509 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- assets/syntaxes/02_Extra/vscode-wgsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/syntaxes/02_Extra/vscode-wgsl b/assets/syntaxes/02_Extra/vscode-wgsl index acf26718..a285c38f 160000 --- a/assets/syntaxes/02_Extra/vscode-wgsl +++ b/assets/syntaxes/02_Extra/vscode-wgsl @@ -1 +1 @@ -Subproject commit acf26718d7a327377641e31d8f9a9dab376efa84 +Subproject commit a285c38f74eba2eb5c5a06be8d95b9f581338509 From f4d836ee874a86d1731a3a6f4c8445debe10f0dd Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Tue, 23 Jun 2026 08:01:50 +0400 Subject: [PATCH 095/130] Include `.code-workspace` as a JSON extension --- src/syntax_mapping/builtins/common/50-json.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax_mapping/builtins/common/50-json.toml b/src/syntax_mapping/builtins/common/50-json.toml index 6b3252e8..3555d34b 100644 --- a/src/syntax_mapping/builtins/common/50-json.toml +++ b/src/syntax_mapping/builtins/common/50-json.toml @@ -1,3 +1,3 @@ # JSON Lines is a simple variation of JSON #2535 [mappings] -"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld", "*.geojson", "*.ndjson"] +"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld", "*.geojson", "*.ndjson", "*.code-workspace"] From d117bf23202a91978c7497f3cdc89cf8e57e5924 Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Tue, 23 Jun 2026 08:08:14 +0400 Subject: [PATCH 096/130] Summarise the change in the `CHANGELOG.md` file --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e98c19a..2410b5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ - Include subdirectories in SSH Config syntax mapping, see #3758 (@injust) - Add Ghostty syntax mapping, see #3759 (@injust) - Add syntax highlighting for `Caddyfile` #3789 (@CosmicHorrorDev) +- Include `.code-workspace` as a JSON extension #3809 (@dhruvkb) ## Themes From fa00fb8ce8a9b3d139166cd7f7c225f9ae52f2d6 Mon Sep 17 00:00:00 2001 From: greymoth-jp Date: Fri, 26 Jun 2026 17:24:01 +0900 Subject: [PATCH 097/130] fix(list-languages): clamp desired_width to avoid usize underflow at tiny --terminal-width `get_languages` computed `desired_width = term_width - longest - separator.len()`. When `--terminal-width` is smaller than the longest language name (e.g. 1), this underflows `usize`: in overflow-checked builds it panics ("attempt to subtract with overflow"), and in release it wraps to ~usize::MAX, silently disabling the extension line-wrapping so `--terminal-width` is ignored for `--list-languages`. Use `saturating_sub`, mirroring the fix applied to `print_snip` in #3804 (the snip separator had the same `term_width - ...` underflow pattern). Output at normal widths is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/bin/bat/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 1d89f8ec..c8bbd368 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -152,7 +152,14 @@ pub fn get_languages(config: &Config, cache_dir: &Path) -> Result { let comma_separator = ", "; let separator = " "; // Line-wrapping for the possible file extension overflow. - let desired_width = config.term_width - longest - separator.len(); + // Clamp instead of subtracting: a tiny `--terminal-width` (smaller than the + // longest language name) would otherwise underflow `usize` and wrap to a huge + // value, silently disabling wrapping (and panicking in debug/overflow-checked + // builds). Mirrors the `saturating_sub` fix applied to `print_snip` in #3804. + let desired_width = config + .term_width + .saturating_sub(longest) + .saturating_sub(separator.len()); let style = if config.colored_output { Green.normal() From 8f93137246d7057f0dc08ac0a3a4cda7b752e13e Mon Sep 17 00:00:00 2001 From: greymoth Date: Fri, 26 Jun 2026 22:13:51 +0900 Subject: [PATCH 098/130] doc(changelog): add #3812 bugfix entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e98c19a..54cd8192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ - Fixed syntax tests path, see #3610 (@foxfromworld) - Fix zsh tab completion word-splitting language names containing spaces (e.g. `HTML (Jinja2)`, `Apache Conf`), see #3693 (@YoshKoz) - Fix zsh tab completion offering invalid `-l` arguments (file globs, paths, hidden filenames) sourced from the second column of `--list-languages`. Closes #3735, see #3737 (@truffle-dev) +- Fix `usize` underflow in `--list-languages` when `--terminal-width` is smaller than the longest language name, see #3812 (@greymoth-jp) ## Other - Use git version of cross. See #3533 (@OctopusET) From c5e6f6aae346d67d50c4c24d5b808ed6f879238a Mon Sep 17 00:00:00 2001 From: blinxen Date: Sun, 26 Apr 2026 22:00:50 +0200 Subject: [PATCH 099/130] Replace libgit2 with gitoxide --- CHANGELOG.md | 1 + Cargo.lock | 2080 +++++++++++++++++++++++++++++++++---------- Cargo.toml | 8 +- src/diff.rs | 127 +-- tests/tester/mod.rs | 65 +- 5 files changed, 1706 insertions(+), 575 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e98c19a..67f432c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall) - Builtin syntax mapping: cleanup matcher glob parsing logic #3652 (@cyqsimon) - Statically link the CRT for MSVC builds via Cargo config to avoid runtime DLL dependencies. Closes #3634, see #3692 (@barry3406) +- Replace `libgit2` with a pure Rust implementation of git called `gitoxide`, see PR #XXXX (@blinxen) ## Syntaxes diff --git a/Cargo.lock b/Cargo.lock index bad6940c..255cea92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,25 @@ version = 4 [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "ansi_colours" version = "1.2.3" @@ -58,34 +64,44 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] [[package]] name = "assert_cmd" -version = "2.1.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85" +checksum = "39bae1d3fa576f7c6519514180a72559268dd7d1fe104070956cb687bc6673bd" dependencies = [ "anstyle", "bstr", @@ -98,9 +114,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" @@ -127,7 +143,7 @@ dependencies = [ "execute", "expect-test", "flate2", - "git2", + "gix", "globset", "grep-cli", "indexmap", @@ -154,7 +170,7 @@ dependencies = [ "syntect", "tempfile", "terminal-colorsaurus", - "thiserror", + "thiserror 2.0.18", "toml", "unicode-segmentation", "unicode-width", @@ -194,10 +210,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] -name = "bstr" -version = "1.11.3" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", @@ -217,9 +242,21 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bytesize" @@ -229,20 +266,19 @@ checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ - "jobserver", - "libc", + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -274,9 +310,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clircle" @@ -289,20 +325,28 @@ dependencies = [ ] [[package]] -name = "colorchoice" -version = "1.0.3" +name = "clru" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "197fd99cb113a8d5d9b6376f3aa817f32c1078f2343b714fff7d2ca44fdf67d5" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "console" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width", "windows-sys 0.61.2", ] @@ -332,10 +376,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "crc32fast" -version = "1.4.2" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -402,10 +455,20 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.21.3" +name = "crypto-common" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -413,11 +476,10 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", @@ -427,9 +489,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", @@ -450,10 +512,24 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.5.5" +name = "dashmap" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -487,21 +563,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] -name = "displaydoc" -version = "0.2.5" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "proc-macro2", - "quote", - "syn", + "block-buffer", + "crypto-common", ] [[package]] name = "dissimilar" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +checksum = "aeda16ab4059c5fd2a83f2b9c9e9c981327b18aa8e3b313f7e6563799d4f093e" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "document-features" @@ -514,9 +595,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encode_unicode" @@ -535,18 +616,18 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -567,23 +648,23 @@ checksum = "0be3cc61fe54b4cae4463cdbda0401978ffe19d4dcc7a5201a312cddf64726dd" dependencies = [ "execute-command-macro", "execute-command-tokens", - "generic-array", + "generic-array 1.3.5", ] [[package]] name = "execute-command-macro" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" +checksum = "b3e748391d89b43c52decaed8645b4a83a09d14f5ee868071c6813389e9e7036" dependencies = [ "execute-command-macro-impl", ] [[package]] name = "execute-command-macro-impl" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" +checksum = "57dd896da3fbb77138059b015c013459d96063c66bcdd3b9094ff2e9d3f19a47" dependencies = [ "execute-command-tokens", "quote", @@ -592,9 +673,9 @@ dependencies = [ [[package]] name = "execute-command-tokens" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" +checksum = "729eda2ea2f6c5ef85150c85a9b2ce0a8e01f040e59cdb32521eaa6c840c9d51" [[package]] name = "expect-test" @@ -618,10 +699,37 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "2.3.0" +name = "faster-hex" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" @@ -649,33 +757,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "percent-encoding", + "typenum", + "version_check", ] [[package]] name = "generic-array" -version = "1.1.1" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb8bc4c28d15ade99c7e90b219f30da4be5c88e586277e8cbe886beeb868ab2" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" dependencies = [ + "rustversion", "typenum", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasip2", + "wasip3", ] [[package]] @@ -699,23 +822,879 @@ dependencies = [ ] [[package]] -name = "git2" -version = "0.20.4" +name = "gix" +version = "0.82.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" +checksum = "62786b0500a7b8dfd998b5c9fc343e1133dc3804293db98e787f5442e8003591" +dependencies = [ + "gix-actor", + "gix-archive", + "gix-attributes", + "gix-blame", + "gix-command", + "gix-commitgraph", + "gix-config", + "gix-date", + "gix-diff", + "gix-dir", + "gix-discover", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-merge", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-pathspec", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-shallow", + "gix-status", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "gix-worktree-state", + "gix-worktree-stream", + "nonempty", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-actor" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ddba0ea986ef262ee3296a6464194181f20882902c8a7669515eaf1b0891e" +dependencies = [ + "bstr", + "gix-date", + "gix-error", + "winnow", +] + +[[package]] +name = "gix-archive" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af31eddd8d8842dc05e0ae1f35721f12484d2eaab7d989f183182280f2cc04af" +dependencies = [ + "bstr", + "gix-date", + "gix-error", + "gix-object", + "gix-worktree-stream", +] + +[[package]] +name = "gix-attributes" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac00bd435a36fcc518640dad4eca4045e1a2f0b33f74a2bbff58245f8e3744d" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-blame" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5789a39b8638b73e5c1210375910145bcf688f1786b7f71fbb1ff4cd51dc7d" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-diff", + "gix-error", + "gix-hash", + "gix-object", + "gix-revwalk", + "gix-trace", + "gix-traverse", + "gix-worktree", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-chunk" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-command" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae4bb9fa74c44c93f7238b08255f7f9afc158bafea4b95af665fa535352cd73c" +dependencies = [ + "bstr", + "gix-path", + "gix-quote", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-commitgraph" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9dd13b8254d36049e9d1758657e74918a49de0286ec053d02497448fa215246" +dependencies = [ + "bstr", + "gix-chunk", + "gix-error", + "gix-hash", + "memmap2", + "nonempty", +] + +[[package]] +name = "gix-config" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ecbd673d9223a46e6bb766e0802ad9921de3541d95d121a9d05de5e23c8de" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", + "winnow", +] + +[[package]] +name = "gix-config-value" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378c53ec3db049919edf91ff76f56f28886a8b4b4a5a9dc633108d84afc3675" dependencies = [ "bitflags", + "bstr", + "gix-path", "libc", - "libgit2-sys", - "log", - "url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-date" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc99523b8bf32561b9abf72c878fbff3854d806ed46c1198e57899f9f3c7f05" +dependencies = [ + "bstr", + "gix-error", + "itoa", + "jiff", + "smallvec", +] + +[[package]] +name = "gix-diff" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709e2b8c52e027d553e200d07f39865b8d9799004160ea609459dc73c48f357a" +dependencies = [ + "bstr", + "gix-command", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-dir" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5590f35265d28cc65faa0d36896ae467836e989b0e790dd94d51d2417e768c" +dependencies = [ + "bstr", + "gix-discover", + "gix-fs", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-trace", + "gix-utils", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-discover" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31885140cb036e25742275f9848b9266a54d553103caec9f3546c62126214f45" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-error" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c998bf10447f0797e579567382b5e22a19c22435d2df091e25857728c6d9af8d" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-features" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0f40b8c98b4b592a7e9c83fe79eacbfcdae8485aa6148ff15e52a4b6e9fe30" +dependencies = [ + "bytes", + "crc32fast", + "gix-path", + "gix-trace", + "gix-utils", + "libc", + "once_cell", + "prodash", + "thiserror 2.0.18", + "walkdir", + "zlib-rs", +] + +[[package]] +name = "gix-filter" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb4376a18f26bdc0fe88a719574c749a678074beda924571e24c5a83bb26e65" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-fs" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7464e9f4167e98d202c7cfb0b5ccbc170c6069870afc52bfd533ad265ce19872" +dependencies = [ + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-glob" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76beed0974ea539df93e44d10a6c7df0a9554f6e184dd730c33e65dc0e089614" +dependencies = [ + "bitflags", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3c92d6aa7a81bde221f3448e62b0e8d81a7d8086e85ccc24b3a4d54e315f30" +dependencies = [ + "faster-hex", + "gix-features", + "sha1-checked", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-hashtable" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08fe1e638b9b84b11eadfe60bdf5523bf46c40b3ef297e612a4a365832d3c8d3" +dependencies = [ + "gix-hash", + "hashbrown 0.16.1", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f72fe2033f4cf8f784fb413c15c8d1124e84f1ddf4d292ffc8468b05d329a047" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + +[[package]] +name = "gix-imara-diff" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfef60ebcd45e0fad3007d15b45a305e6a25af9a0fdffbca0b02bf8b0f91f83c" +dependencies = [ + "bstr", + "hashbrown 0.16.1", + "memchr", +] + +[[package]] +name = "gix-index" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806c10a84ef9e0e2955ec678b7ba157b9bec1185e6d5c200e14d3496f44e3874" +dependencies = [ + "bitflags", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "gix-utils", + "gix-validate", + "hashbrown 0.16.1", + "itoa", + "libc", + "memmap2", + "rustix", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-lock" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e30507ef152e30cec1d0f70cd00199fac746e3d448c3b39c9dade8446fa1da" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-merge" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8305849c022c954af46029756ffb3afcabcaf0be13a3cf3e8bfd204f1fc2b48" +dependencies = [ + "bstr", + "gix-command", + "gix-diff", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-index", + "gix-object", + "gix-path", + "gix-quote", + "gix-revision", + "gix-revwalk", + "gix-tempfile", + "gix-trace", + "gix-worktree", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-negotiate" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cfa5d5f56dea56f96ced9ea55976b66ce8580e7fbdd23f25e9c99bd3e90927" +dependencies = [ + "bitflags", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", +] + +[[package]] +name = "gix-object" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710b16fe38cd3654ec218dd89911100648cd3f495e3997a1bbdc2af69ebf525a" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 2.0.18", + "winnow", +] + +[[package]] +name = "gix-odb" +version = "0.79.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7080f6ebcb0597c2c8bf7e3e0fd02c4df80260c4bfc50a8427f659129a9067b5" +dependencies = [ + "arc-swap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "memmap2", + "parking_lot", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pack" +version = "0.69.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3f76daacd0e91a523dc741c4ef7b9355da67fd73167db831db1edd4720768b4" +dependencies = [ + "clru", + "gix-chunk", + "gix-error", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "memmap2", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-packetline" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-path" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fd1fe596dc393b538e1d5492c5585971a9311475b3255f7b889023df208476" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pathspec" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d902b0cafb2b691738c585f0be98b1b73fad63644c6a612b9b2708bb7b1d17d" +dependencies = [ + "bitflags", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-protocol" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565eaa4df8438f2919135284410284c99100a2f9836d89efd918292107a80165" +dependencies = [ + "bstr", + "gix-date", + "gix-features", + "gix-hash", + "gix-ref", + "gix-shallow", + "gix-transport", + "gix-utils", + "maybe-async", + "nonempty", + "thiserror 2.0.18", + "winnow", +] + +[[package]] +name = "gix-quote" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be" +dependencies = [ + "bstr", + "gix-error", + "gix-utils", +] + +[[package]] +name = "gix-ref" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4367b66548864dd16873c6c86220b81960994deb63965803d773e377e3fce4" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror 2.0.18", + "winnow", +] + +[[package]] +name = "gix-refspec" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2b2756c9ea849bf63d01c0407be7006ebff78e6893e1845a56446eaeada359" +dependencies = [ + "bstr", + "gix-error", + "gix-glob", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-revision" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c130f74ac580ef8d37d723576b776674fdeb405f8b6126692c28346fdb85ac" +dependencies = [ + "bitflags", + "bstr", + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-object", + "gix-revwalk", + "gix-trace", + "nonempty", +] + +[[package]] +name = "gix-revwalk" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e8ba5213c036d064034cefa6349cba3628498bccd8eca3e22a121b1a6e726" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-sec" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283f4a746c9bde8550be63e6f961ff4651f412ca12666e8f5615f39464960ab9" +dependencies = [ + "bitflags", + "gix-path", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "gix-shallow" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3290e102e9375812e6baddf3354357f07f04baeaece97e045f9dad2c7ce6a033" +dependencies = [ + "bstr", + "gix-hash", + "gix-lock", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-status" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a444038e1bd06039464990f0653265d0707c5d92dda283cdf7cecd1b1f3182ba" +dependencies = [ + "bstr", + "filetime", + "gix-diff", + "gix-dir", + "gix-features", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-worktree", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-submodule" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5099295499844e2d9d06d3e9be91d3db67fe322b9bdacaa4009769059affec02" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-tempfile" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30bb168ab673020410158264e21b267dae3b89d248e901400b1cba788fcba7a" +dependencies = [ + "dashmap 6.1.0", + "gix-fs", + "libc", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e" + +[[package]] +name = "gix-transport" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f7cc0a5f8ff289c2d18077740efbad216d8c85b949c40827cd5bff00e457df" +dependencies = [ + "bstr", + "gix-command", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-traverse" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef81f53b86d0cb1588768aaea171241fb98818c8893dd43aa3343fff49600e09" +dependencies = [ + "bitflags", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-url" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a61ead12e33fa52ae92b207ee27554f646a8e7a3dad8b78da1582ec91eda0a6" +dependencies = [ + "bstr", + "gix-path", + "percent-encoding", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c" +dependencies = [ + "bstr", + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-worktree" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f16b89b6195beba7e5517fea8ba20ca6fca67143fb1b070560ce0a4a866c87a" +dependencies = [ + "bstr", + "gix-attributes", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", +] + +[[package]] +name = "gix-worktree-state" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acaa094bfbba896270a83b19585a0ee4ee83a0a64ab0ed3027302a3425436b3f" +dependencies = [ + "bstr", + "gix-features", + "gix-filter", + "gix-fs", + "gix-index", + "gix-object", + "gix-path", + "gix-worktree", + "io-close", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-worktree-stream" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e0dc22458124d74729003a95225bd63236652671d660a8134843bedc1d4600" +dependencies = [ + "gix-attributes", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-object", + "gix-path", + "gix-traverse", + "parking_lot", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" @@ -744,6 +1723,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -752,127 +1740,51 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.17.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", + "foldhash 0.1.5", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] -name = "icu_locid_transform" -version = "1.5.0" +name = "hashbrown" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", + "hash32", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", ] [[package]] -name = "icu_provider_macros" -version = "1.5.0" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "ident_case" @@ -880,27 +1792,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -908,16 +1799,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.1", + "hashbrown 0.17.0", "serde", "serde_core", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "io-close" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -930,17 +1831,58 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "jobserver" -version = "0.1.32" +name = "jiff" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" dependencies = [ - "libc", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", ] [[package]] @@ -949,34 +1891,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.186" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] -name = "libgit2-sys" -version = "0.18.3+1.9.2" +name = "libredox" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "cc", + "bitflags", "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libz-sys" -version = "1.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "plain", + "redox_syscall 0.7.4", ] [[package]] @@ -991,39 +1927,46 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.22" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "memchr" -version = "2.7.4" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] [[package]] name = "miniz_oxide" @@ -1057,8 +2000,19 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -1073,6 +2027,12 @@ dependencies = [ "libc", ] +[[package]] +name = "nonempty" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1081,9 +2041,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "ntapi" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -1099,9 +2059,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-traits" @@ -1114,9 +2074,18 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +dependencies = [ + "parking_lot_core", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "onig" @@ -1142,9 +2111,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1152,15 +2121,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", - "windows-targets", + "windows-link", ] [[package]] @@ -1174,21 +2143,27 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plist" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64", "indexmap", @@ -1197,6 +2172,21 @@ dependencies = [ "time", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1219,15 +2209,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" dependencies = [ "predicates-core", "termtree", @@ -1253,10 +2243,19 @@ dependencies = [ ] [[package]] -name = "quick-xml" -version = "0.39.4" +name = "prodash" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] @@ -1271,10 +2270,16 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.10.0" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -1282,9 +2287,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1292,9 +2297,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ "bitflags", ] @@ -1313,9 +2327,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1324,15 +2338,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" dependencies = [ "bytemuck", ] @@ -1360,10 +2374,16 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.18" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -1382,9 +2402,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.25" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1418,14 +2438,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -1439,9 +2460,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.17.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "serde_core", "serde_with_macros", @@ -1449,9 +2470,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.17.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ "darling", "proc-macro2", @@ -1478,7 +2499,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ - "dashmap", + "dashmap 5.5.3", "lazy_static", "parking_lot", "serial_test_derive", @@ -1495,6 +2516,27 @@ dependencies = [ "syn", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1-checked" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" +dependencies = [ + "digest", + "sha1", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -1525,9 +2567,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -1536,10 +2578,11 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -1553,13 +2596,25 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std_prelude" @@ -1575,26 +2630,15 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syntect" version = "5.3.0" @@ -1612,7 +2656,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 2.0.18", "walkdir", "yaml-rust", ] @@ -1662,7 +2706,7 @@ dependencies = [ "cfg-if", "libc", "memchr", - "mio", + "mio 1.2.0", "terminal-trx", "windows-sys 0.59.0", "xterm-color", @@ -1706,18 +2750,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1756,20 +2820,25 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.7.6" +name = "tinyvec" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ - "displaydoc", - "zerovec", + "tinyvec_macros", ] [[package]] -name = "toml" -version = "1.1.1+spec-1.1.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", @@ -1791,9 +2860,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow", ] @@ -1806,15 +2875,30 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] [[package]] name = "unicode-segmentation" @@ -1828,35 +2912,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -1864,10 +2931,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" @@ -1890,17 +2957,60 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", ] [[package]] @@ -1930,11 +3040,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2046,15 +3156,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -2133,33 +3234,103 @@ name = "winnow" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" - -[[package]] -name = "wit-bindgen-rt" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags", + "memchr", ] [[package]] -name = "write16" -version = "1.0.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] [[package]] -name = "writeable" -version = "0.5.5" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "xterm-color" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f" +checksum = "7008a9d8ba97a7e47d9b2df63fcdb8dade303010c5a7cd5bf2469d4da6eba673" [[package]] name = "yaml-rust" @@ -2171,68 +3342,13 @@ dependencies = [ ] [[package]] -name = "yoke" -version = "0.7.5" +name = "zlib-rs" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] -name = "yoke-derive" -version = "0.7.5" +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 9538844e..b6336c49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,7 @@ minimal-application = [ "regex-onig", "wild", ] -git = ["git2"] # Support indicating git modifications -vendored-libgit2 = ["git2/vendored-libgit2"] +git = ["gix"] # Support indicating git modifications paging = [ "shell-words", "grep-cli", "minus"] # Support applying a pager on the output lessopen = ["execute"] # Support $LESSOPEN preprocessor build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"] @@ -76,10 +75,11 @@ terminal-colorsaurus = "1.0" unicode-segmentation = "1.13.2" itertools = "0.14.0" -[dependencies.git2] -version = "0.20" +[dependencies.gix] +version = "0.82" optional = true default-features = false +features = ["sha1", "blob-diff"] [dependencies.syntect] version = "5.3.0" diff --git a/src/diff.rs b/src/diff.rs index 78d20c30..07b79dc4 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -1,11 +1,14 @@ #![cfg(feature = "git")] +use gix::diff::blob::pipeline::{Mode, WorktreeRoots}; +use gix::diff::blob::{Algorithm, HunkIter, ResourceKind}; +use gix::index::hash::Kind; +use gix::object::tree::EntryKind; +use gix::{self, ObjectId}; +use path_abs::PathInfo; use std::collections::HashMap; -use std::fs; use std::path::Path; -use git2::{DiffOptions, IntoCString, Repository}; - #[derive(Copy, Clone, Debug)] pub enum LineChange { Added, @@ -16,68 +19,70 @@ pub enum LineChange { pub type LineChanges = HashMap; -pub fn get_git_diff(filename: &Path) -> Option { - let repo = Repository::discover(filename).ok()?; - - let repo_path_absolute = fs::canonicalize(repo.workdir()?).ok()?; - - let filepath_absolute = fs::canonicalize(filename).ok()?; - let filepath_relative_to_repo = filepath_absolute.strip_prefix(&repo_path_absolute).ok()?; - - let mut diff_options = DiffOptions::new(); - let pathspec = filepath_relative_to_repo.into_c_string().ok()?; - diff_options.pathspec(pathspec); - diff_options.context_lines(0); - - let diff = repo - .diff_index_to_workdir(None, Some(&mut diff_options)) - .ok()?; - - let mut line_changes: LineChanges = HashMap::new(); - - let mark_section = - |line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| { - for line in start..=end as u32 { - line_changes.insert(line, change); +fn collect_changes_from_hunks(hunks: HunkIter) -> Option { + let mut changes: LineChanges = HashMap::new(); + for hunk in hunks { + if hunk.before.is_empty() && !hunk.after.is_empty() { + for line in hunk.after { + changes.insert(line + 1, LineChange::Added); } - }; - - let _ = diff.foreach( - &mut |_, _| true, - None, - Some(&mut |delta, hunk| { - let path = delta.new_file().path().unwrap_or_else(|| Path::new("")); - - if filepath_relative_to_repo != path { - return false; - } - - let old_lines = hunk.old_lines(); - let new_start = hunk.new_start(); - let new_lines = hunk.new_lines(); - let new_end = (new_start + new_lines) as i32 - 1; - - if old_lines == 0 && new_lines > 0 { - mark_section(&mut line_changes, new_start, new_end, LineChange::Added); - } else if new_lines == 0 && old_lines > 0 { - if new_start == 0 { - mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove); - } else { - mark_section( - &mut line_changes, - new_start, - new_start as i32, - LineChange::RemovedBelow, - ); - } + } else if hunk.after.is_empty() && !hunk.before.is_empty() { + if hunk.after.start == 0 { + changes.insert(1, LineChange::RemovedAbove); } else { - mark_section(&mut line_changes, new_start, new_end, LineChange::Modified); + changes.insert(hunk.after.start, LineChange::RemovedBelow); } + } else { + for line in hunk.after { + changes.insert(line + 1, LineChange::Modified); + } + } + } - true - }), - None, + Some(changes) +} + +pub fn get_git_diff(filename: &Path) -> Option { + let filepath_absolute = filename.canonicalize().ok()?; + let repository = gix::discover(filepath_absolute.parent().ok()?).unwrap(); + let repo_path_absolute = repository.workdir()?.canonicalize().ok()?; + let filepath_relative_to_repo = filepath_absolute.strip_prefix(&repo_path_absolute).ok()?; + let mut cache = repository + .diff_resource_cache( + Mode::ToGit, + WorktreeRoots { + old_root: None, + new_root: repository.workdir().map(Path::to_path_buf), + }, + ) + .ok()?; + cache + .set_resource( + repository + .head_tree() + .ok()? + .lookup_entry_by_path(filepath_relative_to_repo.to_str()?) + .ok()?? + .object_id(), + EntryKind::Blob, + filepath_relative_to_repo.to_str()?.into(), + ResourceKind::OldOrSource, + &repository, + ) + .ok()?; + cache + .set_resource( + ObjectId::null(Kind::Sha1), + EntryKind::Blob, + filepath_relative_to_repo.to_str()?.into(), + ResourceKind::NewOrDestination, + &repository, + ) + .ok()?; + let diff = gix::diff::blob::diff_with_slider_heuristics( + Algorithm::Myers, + &cache.prepare_diff().ok()?.interned_input(), ); - Some(line_changes) + collect_changes_from_hunks(diff.hunks()) } diff --git a/tests/tester/mod.rs b/tests/tester/mod.rs index b25a634c..538d77c2 100644 --- a/tests/tester/mod.rs +++ b/tests/tester/mod.rs @@ -1,15 +1,17 @@ +use gix::actor::SignatureRef; +use gix::bstr::BString; +use gix::bstr::ByteSlice; +use gix::date::time::Format; +use gix::date::Time; +use gix::objs::tree; use std::env; use std::fs::{self, File}; use std::io::Read; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; use tempfile::TempDir; -use git2::build::CheckoutBuilder; -use git2::Repository; -use git2::Signature; - pub struct BatTester { /// Temporary working directory temp_dir: TempDir, @@ -59,35 +61,42 @@ impl Default for BatTester { } } -fn create_sample_directory() -> Result { +fn create_sample_directory() -> Result> { // Create temp directory and initialize repository let temp_dir = TempDir::new().expect("Temp directory"); - let repo = Repository::init(&temp_dir)?; + let repo = gix::init(&temp_dir)?; + let mut tree = gix::objs::Tree::empty(); - // Copy over `sample.rs` - let sample_path = temp_dir.path().join("sample.rs"); - println!("{sample_path:?}"); - fs::copy("tests/snapshots/sample.rs", &sample_path).expect("successful copy"); + // Create sample.rs from snapshot file + let blob_id = repo.write_blob_stream(File::open("tests/snapshots/sample.rs")?)?; + let entry = tree::Entry { + mode: tree::EntryMode::from(tree::EntryKind::Blob), + oid: blob_id.object()?.id, + filename: BString::from("sample.rs"), + }; + tree.entries.push(entry); + let tree_id = repo.write_object(tree)?; - // Commit - let mut index = repo.index()?; - index.add_path(Path::new("sample.rs"))?; - - let oid = index.write_tree()?; - let signature = Signature::now("bat test runner", "bat@test.runner")?; - let tree = repo.find_tree(oid)?; - let _ = repo.commit( - Some("HEAD"), // point HEAD to our new commit - &signature, // author - &signature, // committer + let author = SignatureRef { + name: "test".as_bytes().as_bstr(), + email: "test@test.test".as_bytes().as_bstr(), + time: &Time::now_local_or_utc().format_or_unix(Format::Raw), + }; + let commit_id = repo.commit_as( + author, + author, + "HEAD", "initial commit", - &tree, - &[], - ); - let mut opts = CheckoutBuilder::new(); - repo.checkout_head(Some(opts.force()))?; + tree_id, + gix::commit::NO_PARENT_IDS, + )?; + assert_eq!(commit_id, repo.head_id()?); - fs::copy("tests/snapshots/sample.modified.rs", &sample_path).expect("successful copy"); + fs::copy( + "tests/snapshots/sample.modified.rs", + temp_dir.path().join("sample.rs"), + ) + .expect("successful copy"); Ok(temp_dir) } From e2568c52cdb45598cc1129bb2b554f10f368d672 Mon Sep 17 00:00:00 2001 From: blinxen Date: Sun, 26 Apr 2026 22:07:50 +0200 Subject: [PATCH 100/130] Use correct PR number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f432c2..9049dca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ - Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall) - Builtin syntax mapping: cleanup matcher glob parsing logic #3652 (@cyqsimon) - Statically link the CRT for MSVC builds via Cargo config to avoid runtime DLL dependencies. Closes #3634, see #3692 (@barry3406) -- Replace `libgit2` with a pure Rust implementation of git called `gitoxide`, see PR #XXXX (@blinxen) +- Replace `libgit2` with a pure Rust implementation of git called `gitoxide`, see PR #3703 (@blinxen) ## Syntaxes From e60d184f97262a173267f0eff87d40d9b7fb71d0 Mon Sep 17 00:00:00 2001 From: blinxen Date: Sun, 26 Apr 2026 23:50:49 +0200 Subject: [PATCH 101/130] Use histogram instead of myers for diffing --- src/diff.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff.rs b/src/diff.rs index 07b79dc4..f498baf1 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -80,7 +80,7 @@ pub fn get_git_diff(filename: &Path) -> Option { ) .ok()?; let diff = gix::diff::blob::diff_with_slider_heuristics( - Algorithm::Myers, + Algorithm::Histogram, &cache.prepare_diff().ok()?.interned_input(), ); From d695ad8f887ee66bfc483aa691c092bbc203e89a Mon Sep 17 00:00:00 2001 From: blinxen Date: Mon, 27 Apr 2026 22:59:19 +0200 Subject: [PATCH 102/130] Add more tests for git diff --- src/diff.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 15 deletions(-) diff --git a/src/diff.rs b/src/diff.rs index f498baf1..4258e2d9 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -2,14 +2,12 @@ use gix::diff::blob::pipeline::{Mode, WorktreeRoots}; use gix::diff::blob::{Algorithm, HunkIter, ResourceKind}; -use gix::index::hash::Kind; use gix::object::tree::EntryKind; -use gix::{self, ObjectId}; use path_abs::PathInfo; use std::collections::HashMap; use std::path::Path; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LineChange { Added, RemovedAbove, @@ -44,9 +42,13 @@ fn collect_changes_from_hunks(hunks: HunkIter) -> Option { pub fn get_git_diff(filename: &Path) -> Option { let filepath_absolute = filename.canonicalize().ok()?; - let repository = gix::discover(filepath_absolute.parent().ok()?).unwrap(); + let repository = gix::discover(filepath_absolute.parent().ok()?).ok()?; let repo_path_absolute = repository.workdir()?.canonicalize().ok()?; - let filepath_relative_to_repo = filepath_absolute.strip_prefix(&repo_path_absolute).ok()?; + let filepath_relative_to_repo = gix::path::to_unix_separators_on_windows( + gix::path::into_bstr(filepath_absolute.strip_prefix(&repo_path_absolute).ok()?) + ); + let index = repository.index_or_load_from_head_or_empty().ok()?; + let index_entry = index.entry_by_path(filepath_relative_to_repo.as_ref())?; let mut cache = repository .diff_resource_cache( Mode::ToGit, @@ -58,23 +60,18 @@ pub fn get_git_diff(filename: &Path) -> Option { .ok()?; cache .set_resource( - repository - .head_tree() - .ok()? - .lookup_entry_by_path(filepath_relative_to_repo.to_str()?) - .ok()?? - .object_id(), - EntryKind::Blob, - filepath_relative_to_repo.to_str()?.into(), + index_entry.id, + index_entry.mode.to_tree_entry_mode()?.kind(), + filepath_relative_to_repo.as_ref(), ResourceKind::OldOrSource, &repository, ) .ok()?; cache .set_resource( - ObjectId::null(Kind::Sha1), + repository.object_hash().null(), EntryKind::Blob, - filepath_relative_to_repo.to_str()?.into(), + filepath_relative_to_repo.as_ref(), ResourceKind::NewOrDestination, &repository, ) @@ -86,3 +83,99 @@ pub fn get_git_diff(filename: &Path) -> Option { collect_changes_from_hunks(diff.hunks()) } + +#[cfg(test)] +mod tests { + use super::{get_git_diff, LineChange}; + use std::path::{Path, PathBuf}; + use std::process::Command; + + fn git(repo: &Path, args: &[&str]) { + let output = Command::new("git") + .args(args) + .current_dir(repo) + .output() + .expect("git command can run"); + assert!( + output.status.success(), + "git {args:?} failed\nstdout: {}\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + } + + fn setup_repo() -> tempfile::TempDir { + let dir = tempfile::tempdir().expect("can create temporary directory"); + let repo = dir.path(); + git(repo, &["init"]); + git(repo, &["config", "user.email", "test@test.test"]); + git(repo, &["config", "user.name", "Test"]); + dir + } + + fn create_and_track_file(repo: &tempfile::TempDir, filename: &str) -> PathBuf { + let filepath = repo.path().join(filename); + std::fs::write(&filepath, "file\n").expect("can write file"); + git(repo.path(), &["add", &filename]); + git(repo.path(), &["commit", "-m", "initial"]); + + filepath.to_owned() + } + + #[test] + fn not_a_git_repository() { + let dir = tempfile::tempdir().expect("can create temporary directory"); + let file = dir.path().join("file.txt"); + std::fs::write(&file, "line 1\n").expect("can write file"); + + assert_eq!(get_git_diff(&file), None); + } + + #[test] + fn diff_is_calculated_against_index() { + let repo = setup_repo(); + let file = create_and_track_file(&repo, "file.txt"); + + std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file"); + // Add modified file to index -> should find 0 changes but not none + git(repo.path(), &["add", "file.txt"]); + + assert_eq!(get_git_diff(&file).expect("empty map").len(), 0); + } + + #[test] + fn diff_is_calculated_against_index_2() { + let repo = setup_repo(); + let file = create_and_track_file(&repo, "file.txt"); + + std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file"); + git(repo.path(), &["add", "file.txt"]); + // modify the second line again which should show a single change + std::fs::write(&file, "line 1\nline 2 modified again\n").expect("can write file"); + + assert_eq!(get_git_diff(&file).expect("one change").len(), 1); + } + + #[test] + fn diff_is_calculated_correctly() { + let repo = setup_repo(); + create_and_track_file(&repo, "committed.txt"); + let filename = "committed2.txt"; + let file = create_and_track_file(&repo, filename); + + std::fs::write(&file, "file\nline 2 added\n").expect("can write file"); + let mut changes = get_git_diff(&file).expect("multiple changes"); + assert_eq!(changes.get(&2), Some(&LineChange::Added)); + git(repo.path(), &["add", filename]); + + std::fs::write(&file, "file\nline 2 modified\n").expect("can write file"); + changes = get_git_diff(&file).expect("multiple changes"); + assert_eq!(changes.get(&2), Some(&LineChange::Modified)); + git(repo.path(), &["add", filename]); + + std::fs::write(&file, "line 2 modified\nline 3 added").expect("can write file"); + changes = get_git_diff(&file).expect("multiple changes"); + assert_eq!(changes.get(&1), Some(&LineChange::RemovedAbove)); + assert_eq!(changes.get(&2), Some(&LineChange::Added)); + } +} From f9eed8d61921073dd0352e45d4a9fcfcc6580489 Mon Sep 17 00:00:00 2001 From: blinxen Date: Mon, 27 Apr 2026 23:28:08 +0200 Subject: [PATCH 103/130] cargo fmt --- src/diff.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diff.rs b/src/diff.rs index 4258e2d9..afc38ddf 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -44,9 +44,9 @@ pub fn get_git_diff(filename: &Path) -> Option { let filepath_absolute = filename.canonicalize().ok()?; let repository = gix::discover(filepath_absolute.parent().ok()?).ok()?; let repo_path_absolute = repository.workdir()?.canonicalize().ok()?; - let filepath_relative_to_repo = gix::path::to_unix_separators_on_windows( - gix::path::into_bstr(filepath_absolute.strip_prefix(&repo_path_absolute).ok()?) - ); + let filepath_relative_to_repo = gix::path::to_unix_separators_on_windows(gix::path::into_bstr( + filepath_absolute.strip_prefix(&repo_path_absolute).ok()?, + )); let index = repository.index_or_load_from_head_or_empty().ok()?; let index_entry = index.entry_by_path(filepath_relative_to_repo.as_ref())?; let mut cache = repository From df164cc2e3cacb286551a99ff172fae6e135d4ce Mon Sep 17 00:00:00 2001 From: blinxen Date: Mon, 27 Apr 2026 23:34:50 +0200 Subject: [PATCH 104/130] fix immediate dereference of reference --- src/diff.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff.rs b/src/diff.rs index afc38ddf..20dd6918 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -116,7 +116,7 @@ mod tests { fn create_and_track_file(repo: &tempfile::TempDir, filename: &str) -> PathBuf { let filepath = repo.path().join(filename); std::fs::write(&filepath, "file\n").expect("can write file"); - git(repo.path(), &["add", &filename]); + git(repo.path(), &["add", filename]); git(repo.path(), &["commit", "-m", "initial"]); filepath.to_owned() From 6e6137ce102c58dd5cd572bd12f22377ac651221 Mon Sep 17 00:00:00 2001 From: blinxen Date: Tue, 28 Apr 2026 20:00:03 +0200 Subject: [PATCH 105/130] Update to gix 0.83 --- Cargo.lock | 198 +++++++++++++++++++++++++---------------------------- Cargo.toml | 2 +- 2 files changed, 95 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 255cea92..0b510b37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -823,9 +823,9 @@ dependencies = [ [[package]] name = "gix" -version = "0.82.0" +version = "0.83.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62786b0500a7b8dfd998b5c9fc343e1133dc3804293db98e787f5442e8003591" +checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567" dependencies = [ "gix-actor", "gix-archive", @@ -880,21 +880,20 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552ddba0ea986ef262ee3296a6464194181f20882902c8a7669515eaf1b0891e" +checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8" dependencies = [ "bstr", "gix-date", "gix-error", - "winnow", ] [[package]] name = "gix-archive" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af31eddd8d8842dc05e0ae1f35721f12484d2eaab7d989f183182280f2cc04af" +checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342" dependencies = [ "bstr", "gix-date", @@ -905,9 +904,9 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac00bd435a36fcc518640dad4eca4045e1a2f0b33f74a2bbff58245f8e3744d" +checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000" dependencies = [ "bstr", "gix-glob", @@ -931,9 +930,9 @@ dependencies = [ [[package]] name = "gix-blame" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5789a39b8638b73e5c1210375910145bcf688f1786b7f71fbb1ff4cd51dc7d" +checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a" dependencies = [ "gix-commitgraph", "gix-date", @@ -960,9 +959,9 @@ dependencies = [ [[package]] name = "gix-command" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae4bb9fa74c44c93f7238b08255f7f9afc158bafea4b95af665fa535352cd73c" +checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8" dependencies = [ "bstr", "gix-path", @@ -973,9 +972,9 @@ dependencies = [ [[package]] name = "gix-commitgraph" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9dd13b8254d36049e9d1758657e74918a49de0286ec053d02497448fa215246" +checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb" dependencies = [ "bstr", "gix-chunk", @@ -987,9 +986,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.55.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ecbd673d9223a46e6bb766e0802ad9921de3541d95d121a9d05de5e23c8de" +checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410" dependencies = [ "bstr", "gix-config-value", @@ -998,18 +997,16 @@ dependencies = [ "gix-path", "gix-ref", "gix-sec", - "memchr", "smallvec", "thiserror 2.0.18", "unicode-bom", - "winnow", ] [[package]] name = "gix-config-value" -version = "0.17.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4378c53ec3db049919edf91ff76f56f28886a8b4b4a5a9dc633108d84afc3675" +checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2" dependencies = [ "bitflags", "bstr", @@ -1020,9 +1017,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc99523b8bf32561b9abf72c878fbff3854d806ed46c1198e57899f9f3c7f05" +checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543" dependencies = [ "bstr", "gix-error", @@ -1033,9 +1030,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.62.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709e2b8c52e027d553e200d07f39865b8d9799004160ea609459dc73c48f357a" +checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a" dependencies = [ "bstr", "gix-command", @@ -1054,9 +1051,9 @@ dependencies = [ [[package]] name = "gix-dir" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5590f35265d28cc65faa0d36896ae467836e989b0e790dd94d51d2417e768c" +checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c" dependencies = [ "bstr", "gix-discover", @@ -1074,9 +1071,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31885140cb036e25742275f9848b9266a54d553103caec9f3546c62126214f45" +checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34" dependencies = [ "bstr", "dunce", @@ -1089,18 +1086,18 @@ dependencies = [ [[package]] name = "gix-error" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c998bf10447f0797e579567382b5e22a19c22435d2df091e25857728c6d9af8d" +checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613" dependencies = [ "bstr", ] [[package]] name = "gix-features" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0f40b8c98b4b592a7e9c83fe79eacbfcdae8485aa6148ff15e52a4b6e9fe30" +checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f" dependencies = [ "bytes", "crc32fast", @@ -1117,9 +1114,9 @@ dependencies = [ [[package]] name = "gix-filter" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb4376a18f26bdc0fe88a719574c749a678074beda924571e24c5a83bb26e65" +checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08" dependencies = [ "bstr", "encoding_rs", @@ -1138,9 +1135,9 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7464e9f4167e98d202c7cfb0b5ccbc170c6069870afc52bfd533ad265ce19872" +checksum = "4b5d9f7e55a0f9a936a877fa4f9758692a308550a39a45684286941a20a8e5c0" dependencies = [ "bstr", "fastrand", @@ -1152,9 +1149,9 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76beed0974ea539df93e44d10a6c7df0a9554f6e184dd730c33e65dc0e089614" +checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c" dependencies = [ "bitflags", "bstr", @@ -1164,9 +1161,9 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3c92d6aa7a81bde221f3448e62b0e8d81a7d8086e85ccc24b3a4d54e315f30" +checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2" dependencies = [ "faster-hex", "gix-features", @@ -1176,9 +1173,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08fe1e638b9b84b11eadfe60bdf5523bf46c40b3ef297e612a4a365832d3c8d3" +checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b" dependencies = [ "gix-hash", "hashbrown 0.16.1", @@ -1187,9 +1184,9 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72fe2033f4cf8f784fb413c15c8d1124e84f1ddf4d292ffc8468b05d329a047" +checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5" dependencies = [ "bstr", "gix-glob", @@ -1200,20 +1197,19 @@ dependencies = [ [[package]] name = "gix-imara-diff" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfef60ebcd45e0fad3007d15b45a305e6a25af9a0fdffbca0b02bf8b0f91f83c" +checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82" dependencies = [ "bstr", "hashbrown 0.16.1", - "memchr", ] [[package]] name = "gix-index" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806c10a84ef9e0e2955ec678b7ba157b9bec1185e6d5c200e14d3496f44e3874" +checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db" dependencies = [ "bitflags", "bstr", @@ -1239,9 +1235,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "22.0.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e30507ef152e30cec1d0f70cd00199fac746e3d448c3b39c9dade8446fa1da" +checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1" dependencies = [ "gix-tempfile", "gix-utils", @@ -1250,9 +1246,9 @@ dependencies = [ [[package]] name = "gix-merge" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8305849c022c954af46029756ffb3afcabcaf0be13a3cf3e8bfd204f1fc2b48" +checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881" dependencies = [ "bstr", "gix-command", @@ -1276,9 +1272,9 @@ dependencies = [ [[package]] name = "gix-negotiate" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cfa5d5f56dea56f96ced9ea55976b66ce8580e7fbdd23f25e9c99bd3e90927" +checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c" dependencies = [ "bitflags", "gix-commitgraph", @@ -1290,9 +1286,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.59.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710b16fe38cd3654ec218dd89911100648cd3f495e3997a1bbdc2af69ebf525a" +checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7" dependencies = [ "bstr", "gix-actor", @@ -1305,14 +1301,13 @@ dependencies = [ "itoa", "smallvec", "thiserror 2.0.18", - "winnow", ] [[package]] name = "gix-odb" -version = "0.79.0" +version = "0.80.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7080f6ebcb0597c2c8bf7e3e0fd02c4df80260c4bfc50a8427f659129a9067b5" +checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba" dependencies = [ "arc-swap", "gix-features", @@ -1331,9 +1326,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.69.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f76daacd0e91a523dc741c4ef7b9355da67fd73167db831db1edd4720768b4" +checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa" dependencies = [ "clru", "gix-chunk", @@ -1362,9 +1357,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fd1fe596dc393b538e1d5492c5585971a9311475b3255f7b889023df208476" +checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e" dependencies = [ "bstr", "gix-trace", @@ -1374,9 +1369,9 @@ dependencies = [ [[package]] name = "gix-pathspec" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d902b0cafb2b691738c585f0be98b1b73fad63644c6a612b9b2708bb7b1d17d" +checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855" dependencies = [ "bitflags", "bstr", @@ -1389,9 +1384,9 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.60.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565eaa4df8438f2919135284410284c99100a2f9836d89efd918292107a80165" +checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd" dependencies = [ "bstr", "gix-date", @@ -1404,7 +1399,6 @@ dependencies = [ "maybe-async", "nonempty", "thiserror 2.0.18", - "winnow", ] [[package]] @@ -1420,9 +1414,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.62.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a4367b66548864dd16873c6c86220b81960994deb63965803d773e377e3fce4" +checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167" dependencies = [ "gix-actor", "gix-features", @@ -1436,14 +1430,13 @@ dependencies = [ "gix-validate", "memmap2", "thiserror 2.0.18", - "winnow", ] [[package]] name = "gix-refspec" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2b2756c9ea849bf63d01c0407be7006ebff78e6893e1845a56446eaeada359" +checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761" dependencies = [ "bstr", "gix-error", @@ -1457,9 +1450,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c130f74ac580ef8d37d723576b776674fdeb405f8b6126692c28346fdb85ac" +checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8" dependencies = [ "bitflags", "bstr", @@ -1475,9 +1468,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96e8ba5213c036d064034cefa6349cba3628498bccd8eca3e22a121b1a6e726" +checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0" dependencies = [ "gix-commitgraph", "gix-date", @@ -1491,9 +1484,9 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.13.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283f4a746c9bde8550be63e6f961ff4651f412ca12666e8f5615f39464960ab9" +checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6" dependencies = [ "bitflags", "gix-path", @@ -1503,9 +1496,9 @@ dependencies = [ [[package]] name = "gix-shallow" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3290e102e9375812e6baddf3354357f07f04baeaece97e045f9dad2c7ce6a033" +checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27" dependencies = [ "bstr", "gix-hash", @@ -1516,9 +1509,9 @@ dependencies = [ [[package]] name = "gix-status" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a444038e1bd06039464990f0653265d0707c5d92dda283cdf7cecd1b1f3182ba" +checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65" dependencies = [ "bstr", "filetime", @@ -1539,9 +1532,9 @@ dependencies = [ [[package]] name = "gix-submodule" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5099295499844e2d9d06d3e9be91d3db67fe322b9bdacaa4009769059affec02" +checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91" dependencies = [ "bstr", "gix-config", @@ -1554,9 +1547,9 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "22.0.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30bb168ab673020410158264e21b267dae3b89d248e901400b1cba788fcba7a" +checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb" dependencies = [ "dashmap 6.1.0", "gix-fs", @@ -1573,9 +1566,9 @@ checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e" [[package]] name = "gix-transport" -version = "0.56.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f7cc0a5f8ff289c2d18077740efbad216d8c85b949c40827cd5bff00e457df" +checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018" dependencies = [ "bstr", "gix-command", @@ -1589,9 +1582,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.56.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef81f53b86d0cb1588768aaea171241fb98818c8893dd43aa3343fff49600e09" +checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c" dependencies = [ "bitflags", "gix-commitgraph", @@ -1606,9 +1599,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.35.3" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a61ead12e33fa52ae92b207ee27554f646a8e7a3dad8b78da1582ec91eda0a6" +checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece" dependencies = [ "bstr", "gix-path", @@ -1638,9 +1631,9 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.51.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f16b89b6195beba7e5517fea8ba20ca6fca67143fb1b070560ce0a4a866c87a" +checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd" dependencies = [ "bstr", "gix-attributes", @@ -1656,9 +1649,9 @@ dependencies = [ [[package]] name = "gix-worktree-state" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acaa094bfbba896270a83b19585a0ee4ee83a0a64ab0ed3027302a3425436b3f" +checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800" dependencies = [ "bstr", "gix-features", @@ -1674,9 +1667,9 @@ dependencies = [ [[package]] name = "gix-worktree-stream" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e0dc22458124d74729003a95225bd63236652671d660a8134843bedc1d4600" +checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213" dependencies = [ "gix-attributes", "gix-error", @@ -3234,9 +3227,6 @@ name = "winnow" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" -dependencies = [ - "memchr", -] [[package]] name = "wit-bindgen" diff --git a/Cargo.toml b/Cargo.toml index b6336c49..8c08ad89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ unicode-segmentation = "1.13.2" itertools = "0.14.0" [dependencies.gix] -version = "0.82" +version = "0.83" optional = true default-features = false features = ["sha1", "blob-diff"] From 69f40d9d8a0082edfff4560bdfd3f05f68ac07ca Mon Sep 17 00:00:00 2001 From: blinxen Date: Sat, 9 May 2026 14:58:39 +0200 Subject: [PATCH 106/130] Regenerate Cargo.lock after rebase --- Cargo.lock | 118 ++++++++++++++++++++--------------------------------- 1 file changed, 45 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b510b37..f0e110ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,9 +266,9 @@ checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "cc" -version = "1.2.60" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -648,7 +648,7 @@ checksum = "0be3cc61fe54b4cae4463cdbda0401978ffe19d4dcc7a5201a312cddf64726dd" dependencies = [ "execute-command-macro", "execute-command-tokens", - "generic-array 1.3.5", + "generic-array 1.4.1", ] [[package]] @@ -716,13 +716,12 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "2d5b2eef6fafbf69f877e55509ce5b11a760690ac9700a2921be067aa6afaef6" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -780,9 +779,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.3.5" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" dependencies = [ "rustversion", "typenum", @@ -1135,9 +1134,9 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b5d9f7e55a0f9a936a877fa4f9758692a308550a39a45684286941a20a8e5c0" +checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86" dependencies = [ "bstr", "fastrand", @@ -1753,9 +1752,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heapless" @@ -1792,7 +1791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -1830,9 +1829,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -1845,9 +1844,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -1892,21 +1891,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" - -[[package]] -name = "libredox" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" -dependencies = [ - "bitflags", - "libc", - "plain", - "redox_syscall 0.7.4", -] +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linked-hash-map" @@ -2082,9 +2069,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "onig" -version = "6.5.1" +version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" +checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ "bitflags", "libc", @@ -2094,9 +2081,9 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.9.1" +version = "69.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" +checksum = "1e68317604e77e53b85896388e1a803c1d21b74c899ec9e5e1112db90735edd7" dependencies = [ "cc", "pkg-config", @@ -2120,7 +2107,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] @@ -2146,17 +2133,11 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - [[package]] name = "plist" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" dependencies = [ "base64", "indexmap", @@ -2246,9 +2227,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] @@ -2297,15 +2278,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_syscall" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.12.3" @@ -2453,9 +2425,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19" dependencies = [ "serde_core", "serde_with_macros", @@ -2463,9 +2435,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "cf2ebbe86054f9b45bc3881e865683ccfaccce97b9b4cb53f3039d67f355a334" dependencies = [ "darling", "proc-macro2", @@ -2585,12 +2557,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - [[package]] name = "smallvec" version = "1.15.1" @@ -2868,9 +2834,9 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicode-bom" @@ -2956,11 +2922,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -2969,7 +2935,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -3224,9 +3190,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" [[package]] name = "wit-bindgen" @@ -3237,6 +3203,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" From 98f09fd6f3410d073849d6028f49ad1b9cc6949a Mon Sep 17 00:00:00 2001 From: blinxen Date: Sun, 31 May 2026 11:57:55 +0200 Subject: [PATCH 107/130] Add test with fauly git repo version --- src/diff.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/diff.rs b/src/diff.rs index 20dd6918..45678bb9 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -178,4 +178,17 @@ mod tests { assert_eq!(changes.get(&1), Some(&LineChange::RemovedAbove)); assert_eq!(changes.get(&2), Some(&LineChange::Added)); } + + #[test] + fn faulty_git_version_does_not_panic() { + let repo = setup_repo(); + let file = create_and_track_file(&repo, "file.txt"); + std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file"); + // changes are detected + assert_eq!(get_git_diff(&file).expect("one change").len(), 2); + // write invalid repositoryformatversion + git(repo.path(), &["config", "core.repositoryformatversion", "one"]); + // changes are no longer detected + assert_eq!(get_git_diff(&file), None); + } } From 6ba745b74b43c527036928850fcce7670e36fcdb Mon Sep 17 00:00:00 2001 From: blinxen Date: Sun, 31 May 2026 12:10:08 +0200 Subject: [PATCH 108/130] cargo fmt --- src/diff.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/diff.rs b/src/diff.rs index 45678bb9..06d25927 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -187,7 +187,10 @@ mod tests { // changes are detected assert_eq!(get_git_diff(&file).expect("one change").len(), 2); // write invalid repositoryformatversion - git(repo.path(), &["config", "core.repositoryformatversion", "one"]); + git( + repo.path(), + &["config", "core.repositoryformatversion", "one"], + ); // changes are no longer detected assert_eq!(get_git_diff(&file), None); } From e34595b53ec6feaa505cc1059c29157879d75fdb Mon Sep 17 00:00:00 2001 From: blinxen Date: Fri, 26 Jun 2026 20:28:29 +0200 Subject: [PATCH 109/130] Update gix to 0.85 --- Cargo.lock | 576 ++++++++++++++++++++++------------------------------- Cargo.toml | 2 +- 2 files changed, 235 insertions(+), 343 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0e110ba..a8cbedf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,7 +170,7 @@ dependencies = [ "syntect", "tempfile", "terminal-colorsaurus", - "thiserror 2.0.18", + "thiserror", "toml", "unicode-segmentation", "unicode-width", @@ -203,6 +203,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.11.1" @@ -433,7 +439,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags", + "bitflags 2.11.1", "crossterm_winapi", "derive_more", "document-features", @@ -525,6 +531,38 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "deranged" version = "0.5.8" @@ -578,12 +616,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aeda16ab4059c5fd2a83f2b9c9e9c981327b18aa8e3b313f7e6563799d4f093e" -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "document-features" version = "0.2.12" @@ -593,6 +625,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.15.0" @@ -716,9 +754,9 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "filetime" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5b2eef6fafbf69f877e55509ce5b11a760690ac9700a2921be067aa6afaef6" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", @@ -822,20 +860,17 @@ dependencies = [ [[package]] name = "gix" -version = "0.83.0" +version = "0.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567" +checksum = "fa8b2e38ebfc4484dfef8580ddcaf8abb7285e6f3eb6413ff6775d104ae96ca6" dependencies = [ "gix-actor", - "gix-archive", "gix-attributes", - "gix-blame", "gix-command", "gix-commitgraph", "gix-config", "gix-date", "gix-diff", - "gix-dir", "gix-discover", "gix-error", "gix-features", @@ -847,8 +882,6 @@ dependencies = [ "gix-ignore", "gix-index", "gix-lock", - "gix-merge", - "gix-negotiate", "gix-object", "gix-odb", "gix-pack", @@ -861,7 +894,6 @@ dependencies = [ "gix-revwalk", "gix-sec", "gix-shallow", - "gix-status", "gix-submodule", "gix-tempfile", "gix-trace", @@ -870,42 +902,28 @@ dependencies = [ "gix-utils", "gix-validate", "gix-worktree", - "gix-worktree-state", "gix-worktree-stream", "nonempty", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-actor" -version = "0.41.0" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8" +checksum = "8bc998b8f746dda8565450d08a63b792ced9165d8c27a1ed3f02799ec6a7820f" dependencies = [ "bstr", "gix-date", "gix-error", ] -[[package]] -name = "gix-archive" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342" -dependencies = [ - "bstr", - "gix-date", - "gix-error", - "gix-object", - "gix-worktree-stream", -] - [[package]] name = "gix-attributes" -version = "0.33.0" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000" +checksum = "39b40888d0ed415c0744a6cdc61eebf0304c9d26ab726725b718443c322e5ba4" dependencies = [ "bstr", "gix-glob", @@ -914,53 +932,33 @@ dependencies = [ "gix-trace", "kstring", "smallvec", - "thiserror 2.0.18", + "thiserror", "unicode-bom", ] [[package]] name = "gix-bitmap" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894" +checksum = "52ebef0c26ad305747649e727bbcd56a7b7910754eb7cea88f6dff6f93c51283" dependencies = [ "gix-error", ] -[[package]] -name = "gix-blame" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a" -dependencies = [ - "gix-commitgraph", - "gix-date", - "gix-diff", - "gix-error", - "gix-hash", - "gix-object", - "gix-revwalk", - "gix-trace", - "gix-traverse", - "gix-worktree", - "smallvec", - "thiserror 2.0.18", -] - [[package]] name = "gix-chunk" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320" +checksum = "9faee47943b638e58ddd5e275a4906ad3e4b6c8584f1d41bd18ab9032ec52afb" dependencies = [ "gix-error", ] [[package]] name = "gix-command" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8" +checksum = "00706d4fef135ef4b01680d5218c6ee40cda8baf697b864296cbc887d19118f6" dependencies = [ "bstr", "gix-path", @@ -971,9 +969,9 @@ dependencies = [ [[package]] name = "gix-commitgraph" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb" +checksum = "7f675d0df484a7f6a47e64bd6f311af489d947c0323b0564f36d14f3d7762abb" dependencies = [ "bstr", "gix-chunk", @@ -985,9 +983,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.56.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410" +checksum = "a29bf266c4cdaf759e535c24ad4ce655b987aeb6911075643403cc7cc5ade583" dependencies = [ "bstr", "gix-config-value", @@ -997,41 +995,40 @@ dependencies = [ "gix-ref", "gix-sec", "smallvec", - "thiserror 2.0.18", + "thiserror", "unicode-bom", ] [[package]] name = "gix-config-value" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2" +checksum = "ed42168329552f6c2e5df09665c104199d45d84bedb53683738a49b57fe1baab" dependencies = [ - "bitflags", + "bitflags 2.11.1", "bstr", "gix-path", "libc", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-date" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543" +checksum = "3d63f9e28b59ddeb1a1eb9e5cf986a9222b5d484947445edbc20473939cc7fd0" dependencies = [ "bstr", "gix-error", "itoa", "jiff", - "smallvec", ] [[package]] name = "gix-diff" -version = "0.63.0" +version = "0.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a" +checksum = "92c6d56c94edf92d78203a1cd416f770e35e10b6955ede6b9d7d0c22ff88a5f3" dependencies = [ "bstr", "gix-command", @@ -1045,34 +1042,14 @@ dependencies = [ "gix-trace", "gix-traverse", "gix-worktree", - "thiserror 2.0.18", -] - -[[package]] -name = "gix-dir" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c" -dependencies = [ - "bstr", - "gix-discover", - "gix-fs", - "gix-ignore", - "gix-index", - "gix-object", - "gix-path", - "gix-pathspec", - "gix-trace", - "gix-utils", - "gix-worktree", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-discover" -version = "0.51.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34" +checksum = "d624d5b23b10c1d85337645227abe353ac95ab8ff66a7bdd5ce689b2db33a722" dependencies = [ "bstr", "dunce", @@ -1080,23 +1057,23 @@ dependencies = [ "gix-path", "gix-ref", "gix-sec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-error" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613" +checksum = "e57831e199be480af90dcd7e459abed8a174c09ec9a6e2cc8f7ca6c54598b06b" dependencies = [ "bstr", ] [[package]] name = "gix-features" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f" +checksum = "1849ae154d38bc403185be14fa871e38e3c93ee606875d94e207fdb9fba52dbc" dependencies = [ "bytes", "crc32fast", @@ -1106,16 +1083,16 @@ dependencies = [ "libc", "once_cell", "prodash", - "thiserror 2.0.18", + "thiserror", "walkdir", "zlib-rs", ] [[package]] name = "gix-filter" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08" +checksum = "6644fb2ef97928c278675b239f366b457103d7e436f811d27331a8daf212759c" dependencies = [ "bstr", "encoding_rs", @@ -1129,30 +1106,30 @@ dependencies = [ "gix-trace", "gix-utils", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-fs" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86" +checksum = "6cdff46db8798e47e2f727d84b9379aac5add3dd3d9d0b07bb4d7d5d640771fe" dependencies = [ "bstr", "fastrand", "gix-features", "gix-path", "gix-utils", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-glob" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c" +checksum = "d1fcb8ef5b16bcf874abe9b68d8abb3c0493c876d367ab824151f30a0f3f3756" dependencies = [ - "bitflags", + "bitflags 2.11.1", "bstr", "gix-features", "gix-path", @@ -1160,32 +1137,32 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2" +checksum = "cb0926d3819c837750b4e03c7754901e73f68b8c9b690753a6372a1bed4eedce" dependencies = [ "faster-hex", "gix-features", "sha1-checked", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-hashtable" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b" +checksum = "7e261d54091f0d1c729bc83f54548c071bdec60a697de1e58e88bdfd7a99d24e" dependencies = [ "gix-hash", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "parking_lot", ] [[package]] name = "gix-ignore" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5" +checksum = "d491bab9bf2c9f341dc754f425c31d5d3f63aca615312167b82e1deeaca97d8d" dependencies = [ "bstr", "gix-glob", @@ -1196,21 +1173,21 @@ dependencies = [ [[package]] name = "gix-imara-diff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82" +checksum = "b305d85504de270ad3525d726a6b69cc59ee7b2269b014387651107ab9f0755b" dependencies = [ "bstr", - "hashbrown 0.16.1", + "hashbrown 0.17.1", ] [[package]] name = "gix-index" -version = "0.51.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db" +checksum = "36d45f82ec5a4d7542ea595e9ad16e03e26c8cb4f221e5bc9fcdcf469f63a681" dependencies = [ - "bitflags", + "bitflags 2.11.1", "bstr", "filetime", "fnv", @@ -1223,13 +1200,13 @@ dependencies = [ "gix-traverse", "gix-utils", "gix-validate", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "itoa", "libc", "memmap2", "rustix", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] @@ -1240,54 +1217,14 @@ checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1" dependencies = [ "gix-tempfile", "gix-utils", - "thiserror 2.0.18", -] - -[[package]] -name = "gix-merge" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881" -dependencies = [ - "bstr", - "gix-command", - "gix-diff", - "gix-filter", - "gix-fs", - "gix-hash", - "gix-imara-diff", - "gix-index", - "gix-object", - "gix-path", - "gix-quote", - "gix-revision", - "gix-revwalk", - "gix-tempfile", - "gix-trace", - "gix-worktree", - "nonempty", - "thiserror 2.0.18", -] - -[[package]] -name = "gix-negotiate" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c" -dependencies = [ - "bitflags", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-object", - "gix-revwalk", + "thiserror", ] [[package]] name = "gix-object" -version = "0.60.0" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7" +checksum = "019b38afc3eac1e41f9fe09a327664b313ba4a120fa5f40e3678795d0e42783e" dependencies = [ "bstr", "gix-actor", @@ -1299,14 +1236,14 @@ dependencies = [ "gix-validate", "itoa", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-odb" -version = "0.80.0" +version = "0.82.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba" +checksum = "7fadc59f6fa0f9dd445eceee61060a2b59ca557f48da9fc677f567db535b782a" dependencies = [ "arc-swap", "gix-features", @@ -1320,14 +1257,14 @@ dependencies = [ "memmap2", "parking_lot", "tempfile", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-pack" -version = "0.70.0" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa" +checksum = "ca3e7f1726cd2c0cd1cf1fc20be8a8e623f0b163f1f8d6fc836cfb9bc8cd758b" dependencies = [ "clru", "gix-chunk", @@ -1339,53 +1276,53 @@ dependencies = [ "gix-path", "memmap2", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-packetline" -version = "0.21.3" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6" +checksum = "b217dd0ee0c4021ecf169a4a519b1b4f80d15e3f3765f3dc466223dc0ac891d7" dependencies = [ "bstr", "faster-hex", "gix-trace", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-path" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e" +checksum = "afa6ac14cd14939ea94a496ce7460daa6511c09f5b84757e9cfc6f9c8d0f93a6" dependencies = [ "bstr", "gix-trace", "gix-validate", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-pathspec" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855" +checksum = "3050783b41ee11511e1e8fb35623df81806194f4030395f14f48ea37c2798c9f" dependencies = [ - "bitflags", + "bitflags 2.11.1", "bstr", "gix-attributes", "gix-config-value", "gix-glob", "gix-path", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-protocol" -version = "0.61.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd" +checksum = "978468bae4ea2df20c72db3b20d0bdb548a0c1090b85a83643b553e6e0e041f2" dependencies = [ "bstr", "gix-date", @@ -1397,14 +1334,14 @@ dependencies = [ "gix-utils", "maybe-async", "nonempty", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-quote" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be" +checksum = "a6e541fc33cc2b783b7979040d445a0c86a2eca747c8faea4ca84230d06ae6ef" dependencies = [ "bstr", "gix-error", @@ -1413,9 +1350,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.63.0" +version = "0.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167" +checksum = "9bbfbce1dfd7d7f8469ddef6d3518376aff664348f153cbe0fc3e58ef993d24e" dependencies = [ "gix-actor", "gix-features", @@ -1428,14 +1365,14 @@ dependencies = [ "gix-utils", "gix-validate", "memmap2", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-refspec" -version = "0.41.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761" +checksum = "7bc36a4fb1a1540b59cf2da498783080743fa274b02a3f19ca444fc4015a9d4f" dependencies = [ "bstr", "gix-error", @@ -1444,16 +1381,15 @@ dependencies = [ "gix-revision", "gix-validate", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-revision" -version = "0.45.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8" +checksum = "885075c3c21eb9c06e0be3b3728ba5932c04e1c1011dcee7c81801980e3e986f" dependencies = [ - "bitflags", "bstr", "gix-commitgraph", "gix-date", @@ -1461,15 +1397,14 @@ dependencies = [ "gix-hash", "gix-object", "gix-revwalk", - "gix-trace", "nonempty", ] [[package]] name = "gix-revwalk" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0" +checksum = "8f11fe7ca2585193d3d70bbe0be175a2008d883a704cc7a55e454e113e689455" dependencies = [ "gix-commitgraph", "gix-date", @@ -1478,16 +1413,16 @@ dependencies = [ "gix-hashtable", "gix-object", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-sec" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6" +checksum = "ab8519976e4c7e486270740a5400369f37940779b80bd1377d94cfa1125d01b3" dependencies = [ - "bitflags", + "bitflags 2.11.1", "gix-path", "libc", "windows-sys 0.61.2", @@ -1495,45 +1430,22 @@ dependencies = [ [[package]] name = "gix-shallow" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27" +checksum = "a292fc2fe548c5dfa575479d16b445b0ddf1dd2f56f1fec6aed386f82553cd97" dependencies = [ "bstr", "gix-hash", "gix-lock", "nonempty", - "thiserror 2.0.18", -] - -[[package]] -name = "gix-status" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65" -dependencies = [ - "bstr", - "filetime", - "gix-diff", - "gix-dir", - "gix-features", - "gix-filter", - "gix-fs", - "gix-hash", - "gix-index", - "gix-object", - "gix-path", - "gix-pathspec", - "gix-worktree", - "portable-atomic", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-submodule" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91" +checksum = "a7f9f594f7cbda0b38ba6b633b3e9a7b7901acdc5d27bc186a16633800cd1ac8" dependencies = [ "bstr", "gix-config", @@ -1541,7 +1453,7 @@ dependencies = [ "gix-pathspec", "gix-refspec", "gix-url", - "thiserror 2.0.18", + "thiserror", ] [[package]] @@ -1559,15 +1471,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e" +checksum = "44dc45eae785c0eb14173e0f152e6e224dcf4d45b6a6999a3aed22af541ad678" [[package]] name = "gix-transport" -version = "0.57.0" +version = "0.57.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018" +checksum = "186874f7ad1fb2f9a2f2aa9c2dabc7f9dd087bef74c1a0eee2b4a9cf0248fcb3" dependencies = [ "bstr", "gix-command", @@ -1576,16 +1488,16 @@ dependencies = [ "gix-quote", "gix-sec", "gix-url", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-traverse" -version = "0.57.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c" +checksum = "5062cca8f2977565bbaf666ec31dbdb9bc9d9293beb65f9bec52e6c1121b62a1" dependencies = [ - "bitflags", + "bitflags 2.11.1", "gix-commitgraph", "gix-date", "gix-hash", @@ -1593,46 +1505,45 @@ dependencies = [ "gix-object", "gix-revwalk", "smallvec", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-url" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece" +checksum = "65bb01ec69d55e82ccb7a19e264501ead4e6aac38463a8cebfdd81e22bb67ab2" dependencies = [ "bstr", "gix-path", "percent-encoding", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "gix-utils" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c" +checksum = "66c50966184123caf580ffa64e28031a878597f1c7fceb8fe19566c38eb1b771" dependencies = [ - "bstr", "fastrand", "unicode-normalization", ] [[package]] name = "gix-validate" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5" +checksum = "7bc6fc771c4063ba7cd2f47b91fb6076251c6a823b64b7fe7b8874b0fe4afae3" dependencies = [ "bstr", ] [[package]] name = "gix-worktree" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd" +checksum = "92399ed66f259592050c6ed9dc80105e095a2f8e87e6b83d98aa2e21d8e27036" dependencies = [ "bstr", "gix-attributes", @@ -1646,29 +1557,11 @@ dependencies = [ "gix-validate", ] -[[package]] -name = "gix-worktree-state" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800" -dependencies = [ - "bstr", - "gix-features", - "gix-filter", - "gix-fs", - "gix-index", - "gix-object", - "gix-path", - "gix-worktree", - "io-close", - "thiserror 2.0.18", -] - [[package]] name = "gix-worktree-stream" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213" +checksum = "55f3a878c89a05470ad98c644b0015777c530da24854dd29e41fe4f41176840f" dependencies = [ "gix-attributes", "gix-error", @@ -1755,6 +1648,11 @@ name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heapless" @@ -1796,16 +1694,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "io-close" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1829,24 +1717,25 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.24" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" +checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46" dependencies = [ + "defmt", "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-link", ] [[package]] name = "jiff-static" -version = "0.2.24" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" +checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f" dependencies = [ "proc-macro2", "quote", @@ -1907,6 +1796,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -1924,9 +1819,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1972,18 +1867,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.2.0" @@ -1991,6 +1874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -2001,7 +1885,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "libc", @@ -2057,9 +1941,6 @@ name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -dependencies = [ - "parking_lot_core", -] [[package]] name = "once_cell_polyfill" @@ -2073,7 +1954,7 @@ version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags", + "bitflags 2.11.1", "libc", "once_cell", "onig_sys", @@ -2207,6 +2088,28 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -2275,7 +2178,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.11.1", ] [[package]] @@ -2331,7 +2234,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -2615,7 +2518,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror 2.0.18", + "thiserror", "walkdir", "yaml-rust", ] @@ -2665,7 +2568,7 @@ dependencies = [ "cfg-if", "libc", "memchr", - "mio 1.2.0", + "mio", "terminal-trx", "windows-sys 0.59.0", "xterm-color", @@ -2707,33 +2610,13 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -2966,7 +2849,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -3115,6 +2998,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -3258,7 +3150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.11.1", "indexmap", "log", "serde", diff --git a/Cargo.toml b/Cargo.toml index 8c08ad89..b6b1cb1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ unicode-segmentation = "1.13.2" itertools = "0.14.0" [dependencies.gix] -version = "0.83" +version = "0.85" optional = true default-features = false features = ["sha1", "blob-diff"] From 53769d03b0707026e8edbd046a91777215f1c3dd Mon Sep 17 00:00:00 2001 From: injust Date: Sat, 27 Jun 2026 12:39:05 -0400 Subject: [PATCH 110/130] Add syntax mapping for DNF config --- src/syntax_mapping/builtins/linux/50-dnf.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/syntax_mapping/builtins/linux/50-dnf.toml diff --git a/src/syntax_mapping/builtins/linux/50-dnf.toml b/src/syntax_mapping/builtins/linux/50-dnf.toml new file mode 100644 index 00000000..054fc6ce --- /dev/null +++ b/src/syntax_mapping/builtins/linux/50-dnf.toml @@ -0,0 +1,2 @@ +[mappings] +"INI" = ["/etc/dnf/dnf.conf", "/etc/yum.repos.d/*.repo"] From 088f1e4253293495c4d5bf05e24a12f9c8e9ddb2 Mon Sep 17 00:00:00 2001 From: injust Date: Sat, 27 Jun 2026 12:40:41 -0400 Subject: [PATCH 111/130] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e5b161e..e270b9e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ - Include subdirectories in SSH Config syntax mapping, see #3758 (@injust) - Add Ghostty syntax mapping, see #3759 (@injust) - Add syntax highlighting for `Caddyfile` #3789 (@CosmicHorrorDev) +- Add syntax mapping for DNF repo configuration files, see #3814 (@injust) ## Themes From c75cc01eef9b1a61611c9e10e767e7b90e44e29b Mon Sep 17 00:00:00 2001 From: Adrian Rivera Date: Tue, 30 Jun 2026 12:14:33 -0700 Subject: [PATCH 112/130] Fix --ignored-suffix to fall back to first-line detection When an ignored suffix is also a registered extension (e.g. `.txt` maps to "Plain Text"), `get_syntax_for_file_extension` matched the raw extension before attempting the suffix strip. As a result `--ignored-suffix .txt` had no effect on such files and first-line/shebang detection was never reached, so a shell script saved as `*.txt` stayed unhighlighted. Strip ignored suffixes first and detect on the remainder; only fall back to the raw extension when no ignored suffix applies. When a suffix is stripped but the remainder has no syntax, return `None` so the caller can use first-line detection. Default behavior (no `--ignored-suffix`) is unchanged. See #2745. --- CHANGELOG.md | 1 + src/assets.rs | 58 ++++++++++++++++--- .../examples/regression_tests/issue_2745.txt | 5 ++ tests/integration_tests.rs | 34 +++++++++++ 4 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 tests/examples/regression_tests/issue_2745.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index ce8fab72..78fdef3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## 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) - 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) diff --git a/src/assets.rs b/src/assets.rs index 9483f032..8407678b 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -347,15 +347,15 @@ impl HighlightingAssets { file_name: &OsStr, ignored_suffixes: &IgnoredSuffixes, ) -> Result>> { - let mut syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?; - if syntax.is_none() { - syntax = - ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| { - // Note: recursion - self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes) - })?; + let stripped = + ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| { + self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes) + .map(Some) + })?; + match stripped { + Some(syntax) => Ok(syntax), + None => self.find_syntax_by_extension(Path::new(file_name).extension()), } - Ok(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", " 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] fn syntax_detection_is_case_insensitive() { let mut test = SyntaxDetectionTest::new(); diff --git a/tests/examples/regression_tests/issue_2745.txt b/tests/examples/regression_tests/issue_2745.txt new file mode 100644 index 00000000..b548c1a2 --- /dev/null +++ b/tests/examples/regression_tests/issue_2745.txt @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +NAME="${0##*/}" +for i in {1..3}; do + echo "hello $NAME $i" +done diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2f2dcce8..3366bf0e 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4248,3 +4248,37 @@ fn tcl_shebang_detection_expect() { .assert() .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 { + 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); +} From 57d436861a84ce78f1d519cd7fd68be1306c5511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 02:03:33 +0000 Subject: [PATCH 113/130] build(deps): bump assets/syntaxes/02_Extra/LESS Bumps [assets/syntaxes/02_Extra/LESS](https://github.com/danro/LESS-sublime) from `836b47e` to `d076a4d`. - [Release notes](https://github.com/danro/LESS-sublime/releases) - [Commits](https://github.com/danro/LESS-sublime/compare/836b47ec61a9c6a6445b4007e8353337fe63e2c9...d076a4dd416bcdb155bacf90cda72f7bf1c8abbb) --- updated-dependencies: - dependency-name: assets/syntaxes/02_Extra/LESS dependency-version: d076a4dd416bcdb155bacf90cda72f7bf1c8abbb dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- assets/syntaxes/02_Extra/LESS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/syntaxes/02_Extra/LESS b/assets/syntaxes/02_Extra/LESS index 836b47ec..d076a4dd 160000 --- a/assets/syntaxes/02_Extra/LESS +++ b/assets/syntaxes/02_Extra/LESS @@ -1 +1 @@ -Subproject commit 836b47ec61a9c6a6445b4007e8353337fe63e2c9 +Subproject commit d076a4dd416bcdb155bacf90cda72f7bf1c8abbb From d72163160e0333a17ff35a0919945c6fbcac60e1 Mon Sep 17 00:00:00 2001 From: curious-rabbit Date: Wed, 1 Jul 2026 07:09:49 +0200 Subject: [PATCH 114/130] add sanitize option --- CHANGELOG.md | 2 + README.md | 4 + assets/completions/_bat.ps1.in | 6 + assets/completions/bat.bash.in | 5 + assets/completions/bat.fish.in | 1 + assets/completions/bat.zsh.in | 1 + doc/long-help.txt | 8 ++ src/bin/bat/app.rs | 43 +++++-- src/bin/bat/clap_app.rs | 18 +++ src/config.rs | 3 + src/preprocessor.rs | 173 +++++++++++++++++++++++++- src/pretty_printer.rs | 12 ++ src/printer.rs | 23 +++- src/vscreen.rs | 216 +++++++++++++++++++++++++++++++-- tests/integration_tests.rs | 152 +++++++++++++++++++++++ 15 files changed, 639 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78fdef3e..0e2fc216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## Features +- Add a `--sanitize=` flag for safe display of untrusted input. It implies `--strip-ansi` at the same value and additionally substitutes terminal-active control bytes (cursor moves, charset switches, beep, etc.) and Unicode bidi / zero-width formatting characters with the Unicode replacement character (U+FFFD). Mitigates Trojan-Source-style spoofing (CVE-2021-42574). See #3729 (@curious-rabbit) - Map justfile, Justfile, .justfile, and *.justfile to Makefile syntax highlighting, see #3623 (@zachvalenta) - Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn) - Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) @@ -22,6 +23,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- `--strip-ansi`: also strip 8-bit C1 introducers (U+0090, U+0098, U+009B, U+009D, U+009E, U+009F) and DCS/SOS/PM/APC sequence bodies, which previously passed through. See #3729 (@curious-rabbit) - 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) - 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) diff --git a/README.md b/README.md index 0f89875c..9eb4c095 100644 --- a/README.md +++ b/README.md @@ -843,6 +843,10 @@ If your version of `bat` supports the `--strip-ansi=auto` option, it can be used before syntax highlighting. Alternatively, you may disable both syntax highlighting and wrapping by passing the `--color=never --wrap=never` options to `bat`. +For untrusted input, the `--sanitize=auto|always|never` option additionally replaces terminal-active +control bytes and Unicode bidi / zero-width formatting characters with the Unicode replacement +character. It implies `--strip-ansi` at the same value. + > [!NOTE] > The `auto` option of `--strip-ansi` avoids removing escape sequences when the syntax is plain text. diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index b90e2a33..2f01f768 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -100,6 +100,11 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script ForEach-Object {[System.Management.Automation.CompletionResult]::new($_, $_, [CompletionResultType]::ParameterValue, $_)} break } + '*;--sanitize' { + $ArrayWhen | + ForEach-Object {[System.Management.Automation.CompletionResult]::new($_, $_, [CompletionResultType]::ParameterValue, $_)} + break + } '*;--strip-ansi' { $ArrayWhen | ForEach-Object {[System.Management.Automation.CompletionResult]::new($_, $_, [CompletionResultType]::ParameterValue, $_)} @@ -158,6 +163,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script [CompletionResult]::new('--ignored-suffix' , 'ignored-suffix' , [CompletionResultType]::ParameterName, 'Ignore extension. For example: ''bat --ignored-suffix ".dev" my_file.json.dev'' will use JSON syntax, and ignore ''.dev''') [CompletionResult]::new('--squeeze-blank' , 'squeeze-blank' , [CompletionResultType]::ParameterName, 'Squeeze consecutive empty lines into a single empty line.') [CompletionResult]::new('--squeeze-limit' , 'squeeze-limit' , [CompletionResultType]::ParameterName, 'Set the maximum number of consecutive empty lines to be printed.') + [CompletionResult]::new('--sanitize' , 'sanitize' , [CompletionResultType]::ParameterName, 'Specify when to sanitize untrusted input for safe display. Implies --strip-ansi and also replaces terminal-active and bidi / zero-width bytes. (auto, always, *never*).') [CompletionResult]::new('--strip-ansi' , 'strip-ansi' , [CompletionResultType]::ParameterName, 'Specify when to strip ANSI escape sequences from the input. The automatic mode will remove escape sequences unless the syntax highlighting language is plain text. (auto, always, *never*).') # [CompletionResult]::new('-p' , 'p' , [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').') [CompletionResult]::new('--plain' , 'plain' , [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').') diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 813c532e..5c4f646b 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -132,6 +132,10 @@ _bat() { COMPREPLY=($(compgen -W "auto never always" -- "$cur")) return 0 ;; + --sanitize) + COMPREPLY=($(compgen -W "auto never always" -- "$cur")) + return 0 + ;; --completion) COMPREPLY=($(compgen -W "bash fish zsh ps1" -- "$cur")) return 0 @@ -221,6 +225,7 @@ _bat() { --list-themes --squeeze-blank --squeeze-limit + --sanitize --strip-ansi --style --line-range diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 9d993cb4..b2e35664 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -221,6 +221,7 @@ complete -c $bat -s s -l squeeze-blank -d "Squeeze consecutive empty lines into complete -c $bat -l squeeze-limit -x -d "Set the maximum number of consecutive empty lines to be printed" -n __bat_no_excl_args complete -c $bat -l strip-ansi -x -a "auto never always" -d "Specify when to strip ANSI escape sequences from the input" -n __bat_no_excl_args +complete -c $bat -l sanitize -x -a "auto never always" -d "Specify when to sanitize untrusted input for safe display" -n __bat_no_excl_args complete -c $bat -s p -l plain -d "Disable decorations" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 94001875..775ae9df 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -54,6 +54,7 @@ _{{PROJECT_EXECUTABLE}}_main() { --squeeze-blank'[squeeze consecutive empty lines into a single empty line]' --squeeze-limit='[set the maximum number of consecutive empty lines]:limit:' --strip-ansi='[specify when to strip ANSI escape sequences]:when:(auto never always)' + --sanitize='[specify when to sanitize untrusted input for safe display]:when:(auto never always)' --style='[comma-separated list of style elements to display]: : _values "style [default]" default auto full plain changes header header-filename header-filesize grid rule numbers snip' \*{-r+,--line-range=}'[only print the specified line range]:start\:end' diff --git a/doc/long-help.txt b/doc/long-help.txt index 7d3cc0e5..5b549e07 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -164,6 +164,14 @@ Options: escape sequences unless the syntax highlighting language is plain text. Possible values: auto, always, *never*. + --sanitize + Specify when to sanitize input bytes for safe terminal display. Implies --strip-ansi to + the same value, and additionally substitutes terminal-active control bytes (cursor moves, + charset switches, beep, etc.) and Unicode bidi / zero-width formatting characters with the + Unicode replacement character (U+FFFD). Tab, LF, FF, and CRLF pass through. Useful for + displaying untrusted file content (e.g. file-manager preview panes). Possible values: + auto, always, *never*. + --style 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 diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 3124cdcd..cc37075c 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -38,6 +38,15 @@ pub fn env_no_color() -> bool { env::var_os("NO_COLOR").is_some_and(|x| !x.is_empty()) } +fn parse_strip_ansi_value(raw: Option<&str>, flag_name: &str) -> StripAnsiMode { + match raw { + Some("never") | None => StripAnsiMode::Never, + Some("always") => StripAnsiMode::Always, + Some("auto") => StripAnsiMode::Auto, + _ => unreachable!("other values for {flag_name} are not allowed"), + } +} + enum HelpType { Short, Long, @@ -458,16 +467,32 @@ impl App { 4 }, ), - strip_ansi: match self - .matches - .get_one::("strip-ansi") - .map(|s| s.as_str()) - { - Some("never") => StripAnsiMode::Never, - Some("always") => StripAnsiMode::Always, - Some("auto") => StripAnsiMode::Auto, - _ => unreachable!("other values for --strip-ansi are not allowed"), + strip_ansi: { + let sanitize = parse_strip_ansi_value( + self.matches + .get_one::("sanitize") + .map(|s| s.as_str()), + "--sanitize", + ); + let strip_ansi = parse_strip_ansi_value( + self.matches + .get_one::("strip-ansi") + .map(|s| s.as_str()), + "--strip-ansi", + ); + // --sanitize implies --strip-ansi to the same value. + if sanitize != StripAnsiMode::Never { + sanitize + } else { + strip_ansi + } }, + sanitize: parse_strip_ansi_value( + self.matches + .get_one::("sanitize") + .map(|s| s.as_str()), + "--sanitize", + ), quiet_empty: self.matches.get_flag("quiet-empty"), unbuffered: self.matches.get_flag("unbuffered"), theme: theme(self.theme_options()).to_string(), diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 6dfdf829..decd0364 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -480,6 +480,24 @@ pub fn build_app(interactive_output: bool) -> Command { language is plain text. Possible values: auto, always, *never*.") .hide_short_help(true) ) + .arg( + Arg::new("sanitize") + .long("sanitize") + .overrides_with("sanitize") + .value_name("when") + .value_parser(["auto", "always", "never"]) + .default_value("never") + .hide_default_value(true) + .help("Sanitize untrusted input for safe display (auto, always, *never*)") + .long_help("Specify when to sanitize input bytes for safe terminal display. \ + Implies --strip-ansi to the same value, and additionally substitutes \ + terminal-active control bytes (cursor moves, charset switches, beep, etc.) \ + and Unicode bidi / zero-width formatting characters with the Unicode \ + replacement character (U+FFFD). Tab, LF, FF, and CRLF pass through. Useful \ + for displaying untrusted file content (e.g. file-manager preview panes). \ + Possible values: auto, always, *never*.") + .hide_short_help(true) + ) .arg( Arg::new("style") .long("style") diff --git a/src/config.rs b/src/config.rs index 97720fb5..121097d9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -111,6 +111,9 @@ pub struct Config<'a> { // Whether or not to strip ANSI escape codes from the input pub strip_ansi: StripAnsiMode, + // Substitute terminal-active and spoofing-relevant bytes; implies strip_ansi. + pub sanitize: StripAnsiMode, + /// Whether or not to produce no output when input is empty pub quiet_empty: bool, diff --git a/src/preprocessor.rs b/src/preprocessor.rs index a34f9f9e..b8e326c7 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -139,16 +139,106 @@ pub fn replace_nonprintable( /// Strips ANSI escape sequences from the input. pub fn strip_ansi(line: &str) -> String { let mut buffer = String::with_capacity(line.len()); - for seq in EscapeSequenceOffsetsIterator::new(line) { if let EscapeSequenceOffsets::Text { .. } = seq { buffer.push_str(&line[seq.index_of_start()..seq.index_past_end()]); } } - buffer } +/// Strips ANSI escape sequences and substitutes terminal-active control bytes +/// and visual-spoofing Unicode codepoints (bidi, zero-width) with U+FFFD. +pub fn sanitize(line: &str) -> String { + let stripped = strip_ansi(line); + let mut buffer = String::with_capacity(stripped.len()); + let bytes = stripped.as_bytes(); + let mut start = 0; + let mut i = 0; + while i < bytes.len() { + if !is_sanitize_trigger(bytes[i]) { + i += 1; + continue; + } + let len = sanitize_at(bytes, i, &stripped, &mut buffer, &mut start); + i += len; + } + buffer.push_str(&stripped[start..]); + buffer +} + +#[inline] +fn is_sanitize_trigger(b: u8) -> bool { + // C0 controls minus \t \n \f; DEL; UTF-8 leads with dangerous codepoints. + matches!(b, 0x00..=0x08 | 0x0B | 0x0D..=0x1F | 0x7F | 0xC2 | 0xE2 | 0xEF) +} + +/// Substitutes the byte/sequence at `bytes[i]` (or passes it through on +/// false-alarm trigger), flushing the prefix from `start`. Returns bytes consumed. +fn sanitize_at( + bytes: &[u8], + i: usize, + full: &str, + buffer: &mut String, + start: &mut usize, +) -> usize { + buffer.push_str(&full[*start..i]); + let consumed = match bytes[i] { + b'\r' if bytes.get(i + 1) == Some(&b'\n') => { + buffer.push_str("\r\n"); + 2 + } + // 0xC2 leads U+0080..U+00FF; filter the C1 range. + 0xC2 if matches!(bytes.get(i + 1), Some(0x80..=0x9F)) => { + buffer.push('\u{FFFD}'); + 2 + } + 0xE2 if is_dangerous_e2(bytes, i) => { + buffer.push('\u{FFFD}'); + 3 + } + // 0xEF 0xBB 0xBF = U+FEFF (BOM / zero-width no-break space). + 0xEF if bytes.get(i + 1) == Some(&0xBB) && bytes.get(i + 2) == Some(&0xBF) => { + buffer.push('\u{FFFD}'); + 3 + } + // False-alarm trigger: pass the full UTF-8 sequence through. + lead @ (0xC2 | 0xE2 | 0xEF) => { + let n = utf8_len_from_lead(lead); + buffer.push_str(&full[i..i + n]); + n + } + _ => { + buffer.push('\u{FFFD}'); + 1 + } + }; + *start = i + consumed; + consumed +} + +#[inline] +fn is_dangerous_e2(bytes: &[u8], i: usize) -> bool { + // U+200B..D (zero-width), U+202A..E (bidi controls), U+2066..9 (isolates). + matches!( + (bytes.get(i + 1), bytes.get(i + 2)), + (Some(0x80), Some(0x8B..=0x8D | 0xAA..=0xAE)) | (Some(0x81), Some(0xA6..=0xA9)) + ) +} + +#[inline] +fn utf8_len_from_lead(lead: u8) -> usize { + if lead < 0x80 { + 1 + } else if lead < 0xE0 { + 2 + } else if lead < 0xF0 { + 3 + } else { + 4 + } +} + /// Escape C0, DEL, and C1 control characters so a string from an untrusted /// filename or path can be safely written to the terminal. pub fn sanitize_for_terminal(input: &str) -> String { @@ -270,6 +360,85 @@ fn test_strip_ansi() { ); } +#[test] +fn test_strip_ansi_8bit_c1_introducers() { + assert_eq!(strip_ansi("a\u{9B}31mRED\u{9B}0mb"), "aREDb"); + assert_eq!(strip_ansi("a\x1bP1;0|payload\x1b\\b"), "ab"); + assert_eq!(strip_ansi("a\u{90}body\u{9C}b"), "ab"); +} + +#[test] +fn test_strip_ansi_single_char_esc() { + // strip_ansi must consume both bytes of single-byte ESC sequences (RIS, DECSC, keypad, VT52). + assert_eq!(strip_ansi("a\x1bcb"), "ab"); + assert_eq!(strip_ansi("a\x1b7b\x1b8c"), "abc"); + assert_eq!(strip_ansi("a\x1b=b\x1b>c"), "abc"); + assert_eq!(strip_ansi("a\x1bZb"), "ab"); +} + +#[test] +fn test_strip_ansi_preserves_control_bytes() { + // strip_ansi removes only ANSI escape sequences; control bytes pass through. + assert_eq!(strip_ansi("safe\rEVIL"), "safe\rEVIL"); + assert_eq!(strip_ansi("a\x08b\x07c\x0E\x0Fd"), "a\x08b\x07c\x0E\x0Fd"); +} + +#[test] +fn test_sanitize_substitutes_dangerous_bytes() { + let r = '\u{FFFD}'; + assert_eq!(sanitize("safe\rEVIL"), format!("safe{r}EVIL")); + assert_eq!(sanitize("a\x08b"), format!("a{r}b")); + assert_eq!(sanitize("a\x07b"), format!("a{r}b")); + assert_eq!(sanitize("a\x0Bb"), format!("a{r}b")); + assert_eq!(sanitize("a\x0Eb\x0Fc"), format!("a{r}b{r}c")); + assert_eq!(sanitize("a\u{8D}b"), format!("a{r}b")); + assert_eq!(sanitize("a\u{85}b"), format!("a{r}b")); + assert_eq!(sanitize("trailing\r"), format!("trailing{r}")); + assert_eq!(sanitize("a\x7Fb"), format!("a{r}b")); +} + +#[test] +fn test_sanitize_substitutes_bidi_and_zero_width() { + let r = '\u{FFFD}'; + // Trojan-Source bidi formatting: U+202A..U+202E + assert_eq!(sanitize("a\u{202A}b"), format!("a{r}b")); + assert_eq!(sanitize("a\u{202E}b"), format!("a{r}b")); + // Bidi isolates: U+2066..U+2069 + assert_eq!(sanitize("a\u{2066}b"), format!("a{r}b")); + assert_eq!(sanitize("a\u{2069}b"), format!("a{r}b")); + // Zero-width: U+200B..U+200D + assert_eq!(sanitize("a\u{200B}b"), format!("a{r}b")); + assert_eq!(sanitize("a\u{200D}b"), format!("a{r}b")); + // BOM in middle of file: U+FEFF + assert_eq!(sanitize("a\u{FEFF}b"), format!("a{r}b")); +} + +#[test] +fn test_sanitize_preserves_legitimate_bytes() { + assert_eq!(sanitize("crlf\r\nline\r\n"), "crlf\r\nline\r\n"); + assert_eq!(sanitize("a\tb\nc"), "a\tb\nc"); + assert_eq!(sanitize("plain ascii"), "plain ascii"); + assert_eq!(sanitize("üñíçödé"), "üñíçödé"); + // FF (U+000C) passes through; section separator in C source / Emacs Lisp. + assert_eq!(sanitize("section1\x0Csection2"), "section1\x0Csection2"); + // Common Unicode that shares a UTF-8 lead byte with dangerous codepoints + // must pass through unchanged. + assert_eq!(sanitize("snowman ☃ moon ☾"), "snowman ☃ moon ☾"); + assert_eq!(sanitize("emoji 🎉 ☃"), "emoji 🎉 ☃"); + assert_eq!(sanitize("0xC2 lead: ÿ ñ ç"), "0xC2 lead: ÿ ñ ç"); + assert_eq!(sanitize("CJK 漢字 emoji 🦀"), "CJK 漢字 emoji 🦀"); +} + +#[test] +fn test_sanitize_strips_ansi() { + let r = '\u{FFFD}'; + // sanitize is a strict superset of strip_ansi. + assert_eq!(sanitize("a\x1B[31mb\x1B[0mc"), "abc"); + assert_eq!(sanitize("a\u{9B}31mb\u{9B}0mc"), "abc"); + // ANSI then dangerous byte: ANSI gone, byte substituted. + assert_eq!(sanitize("\x1B[31mhello\rEVIL"), format!("hello{r}EVIL")); +} + #[test] fn test_strip_overstrike() { // Bold: X\x08X (same char repeated) diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index 89a9854e..582e972b 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -192,6 +192,18 @@ impl<'a> PrettyPrinter<'a> { self } + /// Whether to sanitize untrusted input for safe display (default: never) + /// + /// Implies [`strip_ansi`](Self::strip_ansi) at the same value, and also + /// replaces terminal-active and bidi / zero-width bytes with U+FFFD. + pub fn sanitize(&mut self, mode: StripAnsiMode) -> &mut Self { + self.config.sanitize = mode; + if mode != StripAnsiMode::Never { + self.config.strip_ansi = mode; + } + self + } + /// Text wrapping mode (default: do not wrap) pub fn wrapping_mode(&mut self, mode: WrappingMode) -> &mut Self { self.config.wrapping_mode = mode; diff --git a/src/printer.rs b/src/printer.rs index fc8a70ba..42405902 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -30,7 +30,8 @@ use crate::input::OpenedInput; use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult}; use crate::output::OutputHandle; use crate::preprocessor::{ - expand_tabs, replace_nonprintable, sanitize_for_terminal, strip_ansi, strip_overstrike, + expand_tabs, replace_nonprintable, sanitize, sanitize_for_terminal, strip_ansi, + strip_overstrike, }; use crate::style::StyleComponent; use crate::terminal::{as_terminal_escaped, to_ansi_color}; @@ -210,6 +211,7 @@ pub(crate) struct InteractivePrinter<'a> { background_color_highlight: Option, consecutive_empty_lines: usize, strip_ansi: bool, + sanitize: bool, strip_overstrike: bool, } @@ -273,7 +275,9 @@ impl<'a> InteractivePrinter<'a> { let needs_to_match_syntax = (!is_printing_binary || matches!(config.binary, BinaryBehavior::AsText)) - && (config.colored_output || config.strip_ansi == StripAnsiMode::Auto); + && (config.colored_output + || config.strip_ansi == StripAnsiMode::Auto + || config.sanitize == StripAnsiMode::Auto); let (is_plain_text, strip_overstrike, highlighter_from_set) = if needs_to_match_syntax { // Determine the type of syntax for highlighting @@ -319,6 +323,14 @@ impl<'a> InteractivePrinter<'a> { _ => false, }; + let sanitize = match config.sanitize { + _ if config.show_nonprintable => false, + StripAnsiMode::Always => true, + StripAnsiMode::Auto if is_plain_text => false, + StripAnsiMode::Auto => true, + _ => false, + }; + Ok(InteractivePrinter { panel_width, colors, @@ -332,6 +344,7 @@ impl<'a> InteractivePrinter<'a> { background_color_highlight, consecutive_empty_lines: 0, strip_ansi, + sanitize, strip_overstrike, }) } @@ -675,8 +688,10 @@ impl Printer for InteractivePrinter<'_> { } } - // If ANSI escape sequences are supposed to be stripped, do it before syntax highlighting. - if self.strip_ansi { + // Sanitize is the strict superset; otherwise strip-ansi alone. + if self.sanitize { + line = sanitize(&line).into() + } else if self.strip_ansi { line = strip_ansi(&line).into() } diff --git a/src/vscreen.rs b/src/vscreen.rs index 3acbb72f..6478f577 100644 --- a/src/vscreen.rs +++ b/src/vscreen.rs @@ -386,12 +386,23 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { } fn next_text(&mut self) -> Option { - self.chars_take_while(|c| c != '\x1B') + self.chars_take_while(|c| !is_sequence_introducer(c)) .map(|(start, end)| EscapeSequenceOffsets::Text { start, end }) } fn next_sequence(&mut self) -> Option { let (start_sequence, c) = self.chars.next().expect("to not be finished"); + + // Handle 8-bit C1 introducers as their 7-bit `ESC ` equivalents. + match c { + '\u{9B}' => return self.next_csi_body(start_sequence), + '\u{9D}' => return self.next_osc_body(start_sequence), + '\u{90}' | '\u{98}' | '\u{9E}' | '\u{9F}' => { + return self.next_string_terminated_body(start_sequence) + } + _ => {} + } + match self.chars.peek() { None => Some(EscapeSequenceOffsets::Unknown { start: start_sequence, @@ -400,12 +411,24 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { Some((_, ']')) => self.next_osc(start_sequence), Some((_, '[')) => self.next_csi(start_sequence), - Some((i, c)) => match c { + // 7-bit DCS/SOS/PM/APC: `ESC P/X/^/_` introduces a string body. + Some((_, 'P' | 'X' | '^' | '_')) => { + self.chars.next(); + self.next_string_terminated_body(start_sequence) + } + Some(&(i, c)) => match c { '\x20'..='\x2F' => self.next_nf(start_sequence), - c => Some(EscapeSequenceOffsets::Unknown { - start: start_sequence, - end: i + c.len_utf8(), - }), + c => { + // Single-byte ESC sequence (RIS, DECSC/DECRC, keypad, VT52 etc.). + let end = match self.chars.next() { + Some((j, fc)) => j + fc.len_utf8(), + None => i + c.len_utf8(), + }; + Some(EscapeSequenceOffsets::Unknown { + start: start_sequence, + end, + }) + } }, } } @@ -413,12 +436,27 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { fn next_osc(&mut self, start_sequence: usize) -> Option { let (osc_open_index, osc_open_char) = self.chars.next().expect("to not be finished"); debug_assert_eq!(osc_open_char, ']'); + let start_command = osc_open_index + osc_open_char.len_utf8(); + Some(self.read_osc_body(start_sequence, start_command)) + } + /// OSC body parser entered after the 8-bit introducer U+009D was consumed. + fn next_osc_body(&mut self, start_sequence: usize) -> Option { + let start_command = start_sequence + '\u{9D}'.len_utf8(); + Some(self.read_osc_body(start_sequence, start_command)) + } + + fn read_osc_body( + &mut self, + start_sequence: usize, + start_command: usize, + ) -> EscapeSequenceOffsets { let mut start_terminator: usize; let mut end_sequence: usize; loop { - match self.chars_take_while(|c| !matches!(c, '\x07' | '\x1B')) { + // ST is BEL, ESC `\\`, or U+009C. + match self.chars_take_while(|c| !matches!(c, '\x07' | '\x1B' | '\u{9C}')) { None => { start_terminator = self.text.len(); end_sequence = start_terminator; @@ -437,6 +475,11 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { break; } + Some((ti, '\u{9C}')) => { + end_sequence = ti + '\u{9C}'.len_utf8(); + break; + } + Some((ti, '\x1B')) => { match self.chars.next() { Some((i, '\\')) => { @@ -466,20 +509,82 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { } } - Some(EscapeSequenceOffsets::OSC { + EscapeSequenceOffsets::OSC { start_sequence, - start_command: osc_open_index + osc_open_char.len_utf8(), + start_command, start_terminator, end: end_sequence, + } + } + + /// DCS/SOS/PM/APC body parser. Emitted as `Unknown` so the body is stripped. + fn next_string_terminated_body( + &mut self, + start_sequence: usize, + ) -> Option { + let mut end_sequence: usize; + + loop { + match self.chars_take_while(|c| !matches!(c, '\x07' | '\x1B' | '\u{9C}')) { + None => { + end_sequence = self.text.len(); + break; + } + Some((_, end)) => { + end_sequence = end; + } + } + + match self.chars.next() { + Some((ti, '\x07')) => { + end_sequence = ti + '\x07'.len_utf8(); + break; + } + Some((ti, '\u{9C}')) => { + end_sequence = ti + '\u{9C}'.len_utf8(); + break; + } + Some((ti, '\x1B')) => match self.chars.next() { + Some((i, '\\')) => { + end_sequence = i + '\\'.len_utf8(); + break; + } + None => { + end_sequence = ti + '\x1B'.len_utf8(); + break; + } + _ => {} + }, + None => break, + Some((_, tc)) => { + panic!("this should not be reached: char {tc:?}") + } + } + } + + Some(EscapeSequenceOffsets::Unknown { + start: start_sequence, + end: end_sequence, }) } fn next_csi(&mut self, start_sequence: usize) -> Option { let (csi_open_index, csi_open_char) = self.chars.next().expect("to not be finished"); debug_assert_eq!(csi_open_char, '['); + Some(self.read_csi_body(start_sequence, csi_open_index + csi_open_char.len_utf8())) + } - let start_parameters: usize = csi_open_index + csi_open_char.len_utf8(); + /// CSI body parser entered after the 8-bit introducer U+009B was consumed. + fn next_csi_body(&mut self, start_sequence: usize) -> Option { + let start_parameters = start_sequence + '\u{9B}'.len_utf8(); + Some(self.read_csi_body(start_sequence, start_parameters)) + } + fn read_csi_body( + &mut self, + start_sequence: usize, + start_parameters: usize, + ) -> EscapeSequenceOffsets { // Keep iterating while within the range of `0x30-0x3F`. let mut start_intermediates: usize = start_parameters; if let Some((_, end)) = self.chars_take_while(|c| matches!(c, '\x30'..='\x3F')) { @@ -498,13 +603,13 @@ impl<'a> EscapeSequenceOffsetsIterator<'a> { Some((i, c)) => i + c.len_utf8(), }; - Some(EscapeSequenceOffsets::CSI { + EscapeSequenceOffsets::CSI { start_sequence, start_parameters, start_intermediates, start_final_byte, end: end_of_sequence, - }) + } } fn next_nf(&mut self, start_sequence: usize) -> Option { @@ -543,13 +648,25 @@ impl Iterator for EscapeSequenceOffsetsIterator<'_> { type Item = EscapeSequenceOffsets; fn next(&mut self) -> Option { match self.chars.peek() { - Some((_, '\x1B')) => self.next_sequence(), + Some((_, c)) if is_sequence_introducer(*c) => self.next_sequence(), Some((_, _)) => self.next_text(), None => None, } } } +/// True for ESC and the 8-bit C1 sequence introducers (DCS/SOS/CSI/OSC/PM/APC). +#[inline] +fn is_sequence_introducer(c: char) -> bool { + if (c as u32) < 0x80 { + return c == '\x1B'; + } + matches!( + c, + '\u{90}' | '\u{98}' | '\u{9B}' | '\u{9D}' | '\u{9E}' | '\u{9F}' + ) +} + /// An iterator over ANSI/VT escape sequences within a string. /// /// ## Example @@ -717,6 +834,79 @@ mod tests { ); } + #[test] + fn test_escape_sequence_offsets_iterator_parses_8bit_csi() { + let mut iter = EscapeSequenceOffsetsIterator::new("\u{9B}31m"); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::CSI { + start_sequence: 0, + start_parameters: 2, + start_intermediates: 4, + start_final_byte: 4, + end: 5, + }) + ); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_escape_sequence_offsets_iterator_parses_8bit_osc_with_8bit_st() { + let mut iter = EscapeSequenceOffsetsIterator::new("\u{9D}0;title\u{9C}"); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::OSC { + start_sequence: 0, + start_command: 2, + start_terminator: 9, + end: 11, + }) + ); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_escape_sequence_offsets_iterator_parses_7bit_dcs_consumes_body() { + let mut iter = EscapeSequenceOffsetsIterator::new("\x1BP1;0;|payload\x1B\\rest"); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::Unknown { start: 0, end: 16 }) + ); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::Text { start: 16, end: 20 }) + ); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_escape_sequence_offsets_iterator_8bit_dcs_consumes_body() { + let mut iter = EscapeSequenceOffsetsIterator::new("\u{90}body\u{9C}rest"); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::Unknown { start: 0, end: 8 }) + ); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::Text { start: 8, end: 12 }) + ); + } + + #[test] + fn test_escape_sequence_offsets_iterator_truncated_dcs_consumes_to_eof() { + // Unterminated DCS must still be consumed, not emitted as `Text`. + let input = "\x1BPno-terminator"; + let mut iter = EscapeSequenceOffsetsIterator::new(input); + assert_eq!( + iter.next(), + Some(EscapeSequenceOffsets::Unknown { + start: 0, + end: input.len(), + }) + ); + assert_eq!(iter.next(), None); + } + #[test] fn test_escape_sequence_offsets_iterator_parses_csi() { let mut iter = EscapeSequenceOffsetsIterator::new("\x1B[m"); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3366bf0e..33de3fbf 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3844,6 +3844,158 @@ fn strip_ansi_auto_does_not_strip_ansi_when_plain_text_by_option() { assert!(output.contains("\x1B[33mYellow")) } +#[test] +fn sanitize_implies_strip_ansi() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .stdout("Yellow"); +} + +#[test] +fn sanitize_strips_osc_clipboard_hijack() { + // OSC 52 sets the system clipboard. A file containing this would silently + // overwrite the user's clipboard if displayed unfiltered. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("safe\x1B]52;c;cm0=\x07payload") + .assert() + .success() + .stdout("safepayload"); +} + +#[test] +fn sanitize_strips_osc_8_hyperlink_spoof() { + // OSC 8 hyperlinks let displayed text point to an arbitrary URL. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("\x1B]8;;https://evil.example\x07click here\x1B]8;;\x07") + .assert() + .success() + .stdout("click here"); +} + +#[test] +fn sanitize_strips_window_title_injection() { + // OSC 0/1/2 set the terminal window title. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("hello\x1B]0;evil-title\x07world") + .assert() + .success() + .stdout("helloworld"); +} + +#[test] +fn sanitize_strips_8bit_csi() { + // 8-bit CSI introducer (U+009B) is the single-codepoint equivalent of ESC [. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("a\u{9B}31mRED\u{9B}0mb") + .assert() + .success() + .stdout("aREDb"); +} + +#[test] +fn sanitize_substitutes_bare_cr() { + // Bare CR (not part of CRLF) is the line-overwrite forgery vector. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("safe\rEVIL") + .assert() + .success() + .stdout("safe\u{FFFD}EVIL"); +} + +#[test] +fn sanitize_preserves_crlf() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("line1\r\nline2\r\n") + .assert() + .success() + .stdout("line1\r\nline2\r\n"); +} + +#[test] +fn sanitize_substitutes_bidi_controls() { + // Trojan-Source attack (CVE-2021-42574): U+202E (RLO) reorders display. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("admin\u{202E}check") + .assert() + .success() + .stdout("admin\u{FFFD}check"); +} + +#[test] +fn sanitize_substitutes_zero_width() { + // Zero-width chars allow invisible content / identifier confusion. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("ad\u{200B}min") + .assert() + .success() + .stdout("ad\u{FFFD}min"); +} + +#[test] +fn sanitize_preserves_form_feed_in_source() { + // FF (U+000C) is used as a section separator in C source and Emacs Lisp. + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("section1\x0Csection2") + .assert() + .success() + .stdout("section1\x0Csection2"); +} + +#[test] +fn sanitize_preserves_unicode_text() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--sanitize=always") + .write_stdin("snowman ☃ CJK 漢字 emoji 🦀") + .assert() + .success() + .stdout("snowman ☃ CJK 漢字 emoji 🦀"); +} + // Tests that style components can be removed with `-component`. #[test] fn style_components_can_be_removed() { From 0a52e4321f808f57a91174a98daeab84e7f7de2a Mon Sep 17 00:00:00 2001 From: curious-rabbit Date: Wed, 1 Jul 2026 07:54:51 +0200 Subject: [PATCH 115/130] improve patch --- src/preprocessor.rs | 8 +++----- src/pretty_printer.rs | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/preprocessor.rs b/src/preprocessor.rs index b8e326c7..971ae3d4 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -155,11 +155,9 @@ pub fn sanitize(line: &str) -> String { let bytes = stripped.as_bytes(); let mut start = 0; let mut i = 0; - while i < bytes.len() { - if !is_sanitize_trigger(bytes[i]) { - i += 1; - continue; - } + // Skip directly to the next trigger byte instead of testing each one. + while let Some(off) = bytes[i..].iter().position(|&b| is_sanitize_trigger(b)) { + i += off; let len = sanitize_at(bytes, i, &stripped, &mut buffer, &mut start); i += len; } diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index 582e972b..1dc510f4 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -194,13 +194,10 @@ impl<'a> PrettyPrinter<'a> { /// Whether to sanitize untrusted input for safe display (default: never) /// - /// Implies [`strip_ansi`](Self::strip_ansi) at the same value, and also - /// replaces terminal-active and bidi / zero-width bytes with U+FFFD. + /// Strips ANSI escape sequences and additionally substitutes terminal-active + /// control bytes and bidi / zero-width codepoints with U+FFFD. pub fn sanitize(&mut self, mode: StripAnsiMode) -> &mut Self { self.config.sanitize = mode; - if mode != StripAnsiMode::Never { - self.config.strip_ansi = mode; - } self } From 218afc30ac960fa33ac6f5e50ced4fe9e0e085e1 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Sat, 4 Jul 2026 06:29:43 +0300 Subject: [PATCH 116/130] Respect paging mode for list languages --- CHANGELOG.md | 1 + src/bin/bat/main.rs | 8 +++----- tests/integration_tests.rs | 16 +++++++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e2fc216..a55e31c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres) ## Bugfixes +- Fix `--list-languages` respecting `--paging=never`, see #3828 (@cyphercodes) - `--strip-ansi`: also strip 8-bit C1 introducers (U+0090, U+0098, U+009B, U+009D, U+009E, U+009F) and DCS/SOS/PM/APC sequence bodies, which previously passed through. See #3729 (@curious-rabbit) - 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) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index c8bbd368..705a28b9 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -427,11 +427,9 @@ fn run() -> Result { if app.matches.get_flag("list-languages") { let languages: String = get_languages(&config, cache_dir)?; let inputs: Vec = vec![Input::from_reader(Box::new(languages.as_bytes()))]; - let plain_config = Config { - style_components: StyleComponents::new(StyleComponent::Plain.components(false)), - paging_mode: PagingMode::QuitIfOneScreen, - ..Default::default() - }; + let mut plain_config = config.clone(); + plain_config.style_components = + StyleComponents::new(StyleComponent::Plain.components(false)); run_controller(inputs, &plain_config, cache_dir) } else if app.matches.get_flag("list-themes") { list_themes(&config, config_dir, cache_dir, app.theme_options())?; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 33de3fbf..124a9e5b 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -626,12 +626,18 @@ fn list_themes_to_piped_output() { } #[test] +#[serial] fn list_languages() { - bat() - .arg("--list-languages") - .assert() - .success() - .stdout(predicate::str::contains("Rust").normalize()); + mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| { + bat() + .env("PAGER", mocked_pagers::from("echo pager-output")) + .arg("--list-languages") + .arg("--paging=never") + .assert() + .success() + .stdout(predicate::str::contains("Rust").normalize()) + .stdout(predicate::str::contains("pager-output").not()); + }); } #[test] From d449ce5b7e77a11e50c9bf3e244bce9b87353396 Mon Sep 17 00:00:00 2001 From: Tyce Herrman Date: Wed, 29 Jul 2026 11:30:15 -0400 Subject: [PATCH 117/130] chore(deps): resolve current cargo audit failures --- CHANGELOG.md | 1 + Cargo.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e2fc216..d96a4adc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Other +- Update Cargo dependencies to resolve current RustSec advisories, see #3861 (@TyceHerrman) - Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536 - Add .NET slnx extension, see #3682 (@ltrzesniewski) diff --git a/Cargo.lock b/Cargo.lock index a8cbedf8..4de6d556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arc-swap" @@ -420,9 +420,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] @@ -1178,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b305d85504de270ad3525d726a6b69cc59ee7b2269b014387651107ab9f0755b" dependencies = [ "bstr", - "hashbrown 0.17.1", + "hashbrown 0.15.5", ] [[package]] @@ -2016,9 +2016,9 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plist" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" dependencies = [ "base64", "indexmap", @@ -2130,9 +2130,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.39.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", ] From be5343c742f36f6a90c5438dadff52c9770d8825 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:21:51 +0000 Subject: [PATCH 118/130] build(deps): bump unicode-segmentation from 1.13.2 to 1.13.3 Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.13.2 to 1.13.3. - [Commits](https://github.com/unicode-rs/unicode-segmentation/commits) --- updated-dependencies: - dependency-name: unicode-segmentation dependency-version: 1.13.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4de6d556..b2f64ed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1178,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b305d85504de270ad3525d726a6b69cc59ee7b2269b014387651107ab9f0755b" dependencies = [ "bstr", - "hashbrown 0.15.5", + "hashbrown 0.17.1", ] [[package]] @@ -2744,9 +2744,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-width" From a9b67b2d999197df4ffa3a02ad1bc209428116e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:54:55 +0000 Subject: [PATCH 119/130] build(deps): bump bytesize from 2.3.1 to 2.4.2 Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 2.3.1 to 2.4.2. - [Release notes](https://github.com/bytesize-rs/bytesize/releases) - [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md) - [Commits](https://github.com/bytesize-rs/bytesize/compare/bytesize-v2.3.1...bytesize-v2.4.2) --- updated-dependencies: - dependency-name: bytesize dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2f64ed2..28e7a86a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,9 +266,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bytesize" -version = "2.3.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" +checksum = "3d7c8918969267b2932ffd5655509bbbea0833823058c378876953217f5fc50e" [[package]] name = "cc" From bfa6e59978f886df8d886fb05527021eb0e339ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:17:50 +0000 Subject: [PATCH 120/130] build(deps): bump quote from 1.0.45 to 1.0.47 Bumps [quote](https://github.com/dtolnay/quote) from 1.0.45 to 1.0.47. - [Release notes](https://github.com/dtolnay/quote/releases) - [Commits](https://github.com/dtolnay/quote/compare/1.0.45...1.0.47) --- updated-dependencies: - dependency-name: quote dependency-version: 1.0.46 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28e7a86a..e97ddf92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2139,9 +2139,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] From 839f2300b7c155edec26dc6456df89ea61e71e17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:46:45 +0000 Subject: [PATCH 121/130] build(deps): bump serde_with from 3.19.0 to 3.21.0 Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.19.0 to 3.21.0. - [Release notes](https://github.com/jonasbb/serde_with/releases) - [Commits](https://github.com/jonasbb/serde_with/compare/v3.19.0...v3.21.0) --- updated-dependencies: - dependency-name: serde_with dependency-version: 3.21.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e97ddf92..2f445955 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2328,9 +2328,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.19.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "serde_core", "serde_with_macros", @@ -2338,9 +2338,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.19.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2ebbe86054f9b45bc3881e865683ccfaccce97b9b4cb53f3039d67f355a334" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling", "proc-macro2", From 060ccb138501e9decceb4d6f798b5ba7e57d401e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:09:58 +0000 Subject: [PATCH 122/130] build(deps): bump regex from 1.12.3 to 1.13.1 Bumps [regex](https://github.com/rust-lang/regex) from 1.12.3 to 1.13.1. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.12.3...1.13.1) --- updated-dependencies: - dependency-name: regex dependency-version: 1.12.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f445955..88a0ee30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2183,9 +2183,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -2195,9 +2195,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -2206,9 +2206,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rgb" From c2cc79daa747bbbfcdec22bf1eea95a1d1ef8aa2 Mon Sep 17 00:00:00 2001 From: lenamonj Date: Wed, 29 Jul 2026 15:30:07 -0400 Subject: [PATCH 123/130] Fix --sanitize passing through the bidi control characters U+200E, U+200F and U+061C --- src/preprocessor.rs | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/preprocessor.rs b/src/preprocessor.rs index 971ae3d4..a9b8e712 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -168,7 +168,7 @@ pub fn sanitize(line: &str) -> String { #[inline] fn is_sanitize_trigger(b: u8) -> bool { // C0 controls minus \t \n \f; DEL; UTF-8 leads with dangerous codepoints. - matches!(b, 0x00..=0x08 | 0x0B | 0x0D..=0x1F | 0x7F | 0xC2 | 0xE2 | 0xEF) + matches!(b, 0x00..=0x08 | 0x0B | 0x0D..=0x1F | 0x7F | 0xC2 | 0xD8 | 0xE2 | 0xEF) } /// Substitutes the byte/sequence at `bytes[i]` (or passes it through on @@ -200,8 +200,14 @@ fn sanitize_at( buffer.push('\u{FFFD}'); 3 } + // 0xD8 0x9C = U+061C (Arabic letter mark, a bidi control). The rest of + // the 0xD8 block is ordinary Arabic text. + 0xD8 if bytes.get(i + 1) == Some(&0x9C) => { + buffer.push('\u{FFFD}'); + 2 + } // False-alarm trigger: pass the full UTF-8 sequence through. - lead @ (0xC2 | 0xE2 | 0xEF) => { + lead @ (0xC2 | 0xD8 | 0xE2 | 0xEF) => { let n = utf8_len_from_lead(lead); buffer.push_str(&full[i..i + n]); n @@ -217,10 +223,11 @@ fn sanitize_at( #[inline] fn is_dangerous_e2(bytes: &[u8], i: usize) -> bool { - // U+200B..D (zero-width), U+202A..E (bidi controls), U+2066..9 (isolates). + // U+200B..D (zero-width), U+200E..F (LRM/RLM), U+202A..E (bidi embedding + // and override), U+2066..9 (bidi isolates). matches!( (bytes.get(i + 1), bytes.get(i + 2)), - (Some(0x80), Some(0x8B..=0x8D | 0xAA..=0xAE)) | (Some(0x81), Some(0xA6..=0xA9)) + (Some(0x80), Some(0x8B..=0x8F | 0xAA..=0xAE)) | (Some(0x81), Some(0xA6..=0xA9)) ) } @@ -411,6 +418,34 @@ fn test_sanitize_substitutes_bidi_and_zero_width() { assert_eq!(sanitize("a\u{FEFF}b"), format!("a{r}b")); } +#[test] +fn test_sanitize_substitutes_every_bidi_control() { + // Unicode Bidi_Control is exactly these 12 codepoints. Covering only some + // of them leaves a reordering attack available through the rest. + let r = '\u{FFFD}'; + for c in [ + '\u{061C}', '\u{200E}', '\u{200F}', '\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', + '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{2069}', + ] { + assert_eq!( + sanitize(&format!("a{c}b")), + format!("a{r}b"), + "U+{:04X} was not substituted", + c as u32 + ); + } +} + +#[test] +fn test_sanitize_preserves_arabic_sharing_the_alm_lead_byte() { + // U+061C is reached via lead byte 0xD8, which also leads ordinary Arabic. + assert_eq!( + sanitize("\u{0600}\u{061F}\u{06FF}"), + "\u{0600}\u{061F}\u{06FF}" + ); + assert_eq!(sanitize("مرحبا بالعالم"), "مرحبا بالعالم"); +} + #[test] fn test_sanitize_preserves_legitimate_bytes() { assert_eq!(sanitize("crlf\r\nline\r\n"), "crlf\r\nline\r\n"); From df382256532b96da7cc8eba8fb5dca2d67c5d8ce Mon Sep 17 00:00:00 2001 From: lenamonj Date: Wed, 29 Jul 2026 15:38:59 -0400 Subject: [PATCH 124/130] Add CHANGELOG entry for #3862 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cf4f8f7..7e0abc18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ## Bugfixes - Fix `--list-languages` respecting `--paging=never`, see #3828 (@cyphercodes) +- Fix `--sanitize` passing through the bidi control characters U+200E, U+200F and U+061C, see #3862 (@lenamonj) - `--strip-ansi`: also strip 8-bit C1 introducers (U+0090, U+0098, U+009B, U+009D, U+009E, U+009F) and DCS/SOS/PM/APC sequence bodies, which previously passed through. See #3729 (@curious-rabbit) - 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) From 969b33dad145249cbb5f1d3e5d60deb157dde8d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:03:54 +0000 Subject: [PATCH 125/130] build(deps): bump proc-macro2 from 1.0.106 to 1.0.107 Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.106 to 1.0.107. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.106...1.0.107) --- updated-dependencies: - dependency-name: proc-macro2 dependency-version: 1.0.107 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88a0ee30..5be0c644 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2112,9 +2112,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] From e6ac6ce08dfe3c5f7f761dda5c7a29d8fdaf2abb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:38:14 +0000 Subject: [PATCH 126/130] build(deps): bump execute from 0.2.15 to 0.3.0 Bumps [execute](https://github.com/magiclen/execute) from 0.2.15 to 0.3.0. - [Commits](https://github.com/magiclen/execute/compare/v0.2.15...v0.3.0) --- updated-dependencies: - dependency-name: execute dependency-version: 0.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 31 ++++++++++--------------------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5be0c644..0467f4a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,7 +221,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -466,7 +466,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.7", + "generic-array", "typenum", ] @@ -680,29 +680,28 @@ dependencies = [ [[package]] name = "execute" -version = "0.2.15" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be3cc61fe54b4cae4463cdbda0401978ffe19d4dcc7a5201a312cddf64726dd" +checksum = "a8bc8fa7331f8c9f97fb52c60c4e1d07fd424db18130cb252b1da7c6bbcfc2c9" dependencies = [ "execute-command-macro", "execute-command-tokens", - "generic-array 1.4.1", ] [[package]] name = "execute-command-macro" -version = "0.1.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e748391d89b43c52decaed8645b4a83a09d14f5ee868071c6813389e9e7036" +checksum = "889365b82d31077b07ed81c1b37f49eed80266f57c99bc3706e8d8d4783388ac" dependencies = [ "execute-command-macro-impl", ] [[package]] name = "execute-command-macro-impl" -version = "0.1.12" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57dd896da3fbb77138059b015c013459d96063c66bcdd3b9094ff2e9d3f19a47" +checksum = "581bbece7790a10aa1c40586be3be5b6f466f0a608386885a81924df372fd530" dependencies = [ "execute-command-tokens", "quote", @@ -711,9 +710,9 @@ dependencies = [ [[package]] name = "execute-command-tokens" -version = "0.1.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729eda2ea2f6c5ef85150c85a9b2ce0a8e01f040e59cdb32521eaa6c840c9d51" +checksum = "9ef469ea74199a1c75f76f5176e18e4a8864caa6b7ca528c97a5f5fe67c94902" [[package]] name = "expect-test" @@ -815,16 +814,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "generic-array" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" -dependencies = [ - "rustversion", - "typenum", -] - [[package]] name = "getrandom" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index b6b1cb1a..3baddf5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ regex = { version = "1.12.3", optional = true } walkdir = { version = "2.5", optional = true } bytesize = { version = "2.3.1" } encoding_rs = "0.8.35" -execute = { version = "0.2.15", optional = true } +execute = { version = "0.3.0", optional = true } terminal-colorsaurus = "1.0" unicode-segmentation = "1.13.2" itertools = "0.14.0" From 6573ca9307291edbc27cf04c9e64dc102fb225a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:09:37 +0000 Subject: [PATCH 127/130] build(deps): bump terminal-colorsaurus from 1.0.0 to 1.0.3 Bumps [terminal-colorsaurus](https://github.com/tautropfli/terminal-colorsaurus) from 1.0.0 to 1.0.3. - [Release notes](https://github.com/tautropfli/terminal-colorsaurus/releases) - [Changelog](https://github.com/tautropfli/terminal-colorsaurus/blob/main/changelog.md) - [Commits](https://github.com/tautropfli/terminal-colorsaurus/compare/1.0.0...1.0.3) --- updated-dependencies: - dependency-name: terminal-colorsaurus dependency-version: 1.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0467f4a7..0e426040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -79,7 +79,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -354,7 +354,7 @@ dependencies = [ "encode_unicode", "libc", "unicode-width", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -665,7 +665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -675,7 +675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" dependencies = [ "cfg-if", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1414,7 +1414,7 @@ dependencies = [ "bitflags 2.11.1", "gix-path", "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1865,7 +1865,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1907,7 +1907,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2227,7 +2227,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2536,7 +2536,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2550,16 +2550,16 @@ dependencies = [ [[package]] name = "terminal-colorsaurus" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7226dad4b1817567c1e2f5d453897ef36abe79def7783af3fa241a694e30b3" +checksum = "7a46bb5364467da040298c573c8a95dbf9a512efc039630409a03126e3703e90" dependencies = [ "cfg-if", "libc", "memchr", "mio", "terminal-trx", - "windows-sys 0.59.0", + "windows-sys", "xterm-color", ] @@ -2571,7 +2571,7 @@ checksum = "3b3f27d9a8a177e57545481faec87acb45c6e854ed1e5a3658ad186c106f38ed" dependencies = [ "cfg-if", "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2581,7 +2581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2875,7 +2875,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2987,15 +2987,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" From 1cf12d49bda67f3f26ee0e26d1ab72ccc2e844a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:33:08 +0000 Subject: [PATCH 128/130] build(deps): bump gix from 0.85.0 to 0.86.0 Bumps [gix](https://github.com/GitoxideLabs/gitoxide) from 0.85.0 to 0.86.0. - [Release notes](https://github.com/GitoxideLabs/gitoxide/releases) - [Changelog](https://github.com/GitoxideLabs/gitoxide/blob/main/CHANGELOG.md) - [Commits](https://github.com/GitoxideLabs/gitoxide/compare/gix-v0.85.0...gix-v0.86.0) --- updated-dependencies: - dependency-name: gix dependency-version: 0.86.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 258 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 2 +- 2 files changed, 133 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e426040..3a46f154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,21 @@ dependencies = [ "serde", ] +[[package]] +name = "bisync" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5020822f6d6f23196ccaf55e228db36f9de1cf788052b37992e17cbc96ec41a7" +dependencies = [ + "bisync_macros", +] + +[[package]] +name = "bisync_macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d21f40d350a700f6aa107e45fb26448cf489d34794b2ba4522181dc9f1173af6" + [[package]] name = "bit-set" version = "0.8.0" @@ -519,9 +534,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -849,9 +864,9 @@ dependencies = [ [[package]] name = "gix" -version = "0.85.0" +version = "0.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8b2e38ebfc4484dfef8580ddcaf8abb7285e6f3eb6413ff6775d104ae96ca6" +checksum = "bb3790fd8981cba7949f1ba924ef865d902df731627bc5998d14164063892fce" dependencies = [ "gix-actor", "gix-attributes", @@ -892,6 +907,7 @@ dependencies = [ "gix-validate", "gix-worktree", "gix-worktree-stream", + "gix-zlib", "nonempty", "smallvec", "thiserror", @@ -899,9 +915,9 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.41.1" +version = "0.41.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bc998b8f746dda8565450d08a63b792ced9165d8c27a1ed3f02799ec6a7820f" +checksum = "33f9308ad6fd35b2a865cbe4117ac61b2be59e4a9ef1621c7a9794f7c8e52c5b" dependencies = [ "bstr", "gix-date", @@ -910,16 +926,16 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.33.2" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b40888d0ed415c0744a6cdc61eebf0304c9d26ab726725b718443c322e5ba4" +checksum = "c31c593692ebdc1e38858d9a2b56f6a594c501e24a38971fe6685571f5a07be0" dependencies = [ "bstr", + "gix-features", "gix-glob", "gix-path", "gix-quote", "gix-trace", - "kstring", "smallvec", "thiserror", "unicode-bom", @@ -927,18 +943,18 @@ dependencies = [ [[package]] name = "gix-bitmap" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ebef0c26ad305747649e727bbcd56a7b7910754eb7cea88f6dff6f93c51283" +checksum = "7cd1d118d0f5d88b96e6f6e13b566475fef4797ead4a02c26fed36c1375066f7" dependencies = [ "gix-error", ] [[package]] name = "gix-chunk" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9faee47943b638e58ddd5e275a4906ad3e4b6c8584f1d41bd18ab9032ec52afb" +checksum = "b2a871e5cab12ba568845714473505deefffb3c04eb47f4708ce344cd459c1cc" dependencies = [ "gix-error", ] @@ -958,9 +974,9 @@ dependencies = [ [[package]] name = "gix-commitgraph" -version = "0.37.1" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f675d0df484a7f6a47e64bd6f311af489d947c0323b0564f36d14f3d7762abb" +checksum = "a2cd7f054ae2727223fe46dd39c012f066b12f532962d336d29ee193261787da" dependencies = [ "bstr", "gix-chunk", @@ -972,9 +988,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a29bf266c4cdaf759e535c24ad4ce655b987aeb6911075643403cc7cc5ade583" +checksum = "103d11bef95c467577ecfa8b7b86a22e65af3507b2c9bfa3809a4afbae7df301" dependencies = [ "bstr", "gix-config-value", @@ -983,6 +999,7 @@ dependencies = [ "gix-path", "gix-ref", "gix-sec", + "gix-utils", "smallvec", "thiserror", "unicode-bom", @@ -990,9 +1007,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed42168329552f6c2e5df09665c104199d45d84bedb53683738a49b57fe1baab" +checksum = "9f813e312a3f7f327187823cd4c754a3e4d948c98907d11e8f37554a2b6b8059" dependencies = [ "bitflags 2.11.1", "bstr", @@ -1003,9 +1020,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d63f9e28b59ddeb1a1eb9e5cf986a9222b5d484947445edbc20473939cc7fd0" +checksum = "7e47b9e8cdc688296609b706428de570f88b1e0eed7156dde7b4a89d26fa4567" dependencies = [ "bstr", "gix-error", @@ -1015,9 +1032,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.65.0" +version = "0.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c6d56c94edf92d78203a1cd416f770e35e10b6955ede6b9d7d0c22ff88a5f3" +checksum = "fee7d89a3c507491cdfc57a1d1e0e300214720b4f7709ebc253e422f99822bfc" dependencies = [ "bstr", "gix-command", @@ -1036,9 +1053,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.53.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d624d5b23b10c1d85337645227abe353ac95ab8ff66a7bdd5ce689b2db33a722" +checksum = "b9f517766fa1101dfe2606c1a19a8ffa699099030995a9194445446dfe261bdf" dependencies = [ "bstr", "dunce", @@ -1051,37 +1068,37 @@ dependencies = [ [[package]] name = "gix-error" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57831e199be480af90dcd7e459abed8a174c09ec9a6e2cc8f7ca6c54598b06b" +checksum = "4a9292309fd944e71b2a3c96d3c03a6feb8852db646febdde7cbb9f79cb5f329" dependencies = [ "bstr", ] [[package]] name = "gix-features" -version = "0.48.1" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1849ae154d38bc403185be14fa871e38e3c93ee606875d94e207fdb9fba52dbc" +checksum = "20aa09e83a48dc02c5f5f08578aa79d3ab1bab4618b8c362f88684645a02bdcc" dependencies = [ "bytes", "crc32fast", + "crossbeam-channel", "gix-path", "gix-trace", "gix-utils", "libc", "once_cell", + "parking_lot", "prodash", - "thiserror", "walkdir", - "zlib-rs", ] [[package]] name = "gix-filter" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6644fb2ef97928c278675b239f366b457103d7e436f811d27331a8daf212759c" +checksum = "5e7b5dbf524d97e839f642930c76d7f011c0791e7d11d8148989ac5af7c76aa8" dependencies = [ "bstr", "encoding_rs", @@ -1100,12 +1117,11 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cdff46db8798e47e2f727d84b9379aac5add3dd3d9d0b07bb4d7d5d640771fe" +checksum = "865cf13fcaf5455220546cb9607c416bd1be9a6caafd143655a362fdeab64e80" dependencies = [ "bstr", - "fastrand", "gix-features", "gix-path", "gix-utils", @@ -1114,9 +1130,9 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.26.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1fcb8ef5b16bcf874abe9b68d8abb3c0493c876d367ab824151f30a0f3f3756" +checksum = "421e92a711554fa5827d1b0599d3389acdd0f6729e97a8c5a57d79af1e50bf36" dependencies = [ "bitflags 2.11.1", "bstr", @@ -1126,9 +1142,9 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0926d3819c837750b4e03c7754901e73f68b8c9b690753a6372a1bed4eedce" +checksum = "13adaa73415fd6c902310923f68d0b98e8cecf14b33ea58c02cc387cee56f54e" dependencies = [ "faster-hex", "gix-features", @@ -1138,9 +1154,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e261d54091f0d1c729bc83f54548c071bdec60a697de1e58e88bdfd7a99d24e" +checksum = "78fccd6fea3bcf0b39c076bae60ae49b08daaf538b950202101a981f9d3c01d3" dependencies = [ "gix-hash", "hashbrown 0.17.1", @@ -1149,9 +1165,9 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d491bab9bf2c9f341dc754f425c31d5d3f63aca615312167b82e1deeaca97d8d" +checksum = "12cff8e8aa125e39377456073e63df3334d9e5741372ddcc226198015076dda2" dependencies = [ "bstr", "gix-glob", @@ -1162,9 +1178,9 @@ dependencies = [ [[package]] name = "gix-imara-diff" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b305d85504de270ad3525d726a6b69cc59ee7b2269b014387651107ab9f0755b" +checksum = "1a791e6620676a875f362f3156ed213e73ca099a09bf992c18812abe65cc37b1" dependencies = [ "bstr", "hashbrown 0.17.1", @@ -1172,9 +1188,9 @@ dependencies = [ [[package]] name = "gix-index" -version = "0.53.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d45f82ec5a4d7542ea595e9ad16e03e26c8cb4f221e5bc9fcdcf469f63a681" +checksum = "5009c4e7e9f9b4cfaaab1153e49133eb04d79c015b5702d6c3d2ab94271a89c6" dependencies = [ "bitflags 2.11.1", "bstr", @@ -1200,9 +1216,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1" +checksum = "d4c69157820343bf1c6e4b88b9808e920900de02e18aaf5862b30ada43814848" dependencies = [ "gix-tempfile", "gix-utils", @@ -1211,9 +1227,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.62.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "019b38afc3eac1e41f9fe09a327664b313ba4a120fa5f40e3678795d0e42783e" +checksum = "0e48c235e7f886eb819fc878af75be889333dd3c38bee02ed7af48ae2cf596c4" dependencies = [ "bstr", "gix-actor", @@ -1230,9 +1246,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.82.0" +version = "0.83.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fadc59f6fa0f9dd445eceee61060a2b59ca557f48da9fc677f567db535b782a" +checksum = "8dd494ffb5037e62b8220109e894d2861ff2150a2cacbfccdba57ae1ebab2b96" dependencies = [ "arc-swap", "gix-features", @@ -1243,6 +1259,7 @@ dependencies = [ "gix-pack", "gix-path", "gix-quote", + "gix-zlib", "memmap2", "parking_lot", "tempfile", @@ -1251,9 +1268,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.72.0" +version = "0.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3e7f1726cd2c0cd1cf1fc20be8a8e623f0b163f1f8d6fc836cfb9bc8cd758b" +checksum = "6d5446127b269706e85998065267ddd2ccc3550179da6780b22fe496175ccb20" dependencies = [ "clru", "gix-chunk", @@ -1263,6 +1280,7 @@ dependencies = [ "gix-hashtable", "gix-object", "gix-path", + "gix-zlib", "memmap2", "smallvec", "thiserror", @@ -1270,9 +1288,9 @@ dependencies = [ [[package]] name = "gix-packetline" -version = "0.21.5" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b217dd0ee0c4021ecf169a4a519b1b4f80d15e3f3765f3dc466223dc0ac891d7" +checksum = "3766025c72319c4accdd854a18e6f0dd176c8eb0f3bc8a60a7765be2b50cabf2" dependencies = [ "bstr", "faster-hex", @@ -1282,9 +1300,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa6ac14cd14939ea94a496ce7460daa6511c09f5b84757e9cfc6f9c8d0f93a6" +checksum = "1ed3e8d7a82e886e17a72e03d4ba0c13db6f2219b6cd4e2900b4cae426ec20c9" dependencies = [ "bstr", "gix-trace", @@ -1294,9 +1312,9 @@ dependencies = [ [[package]] name = "gix-pathspec" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3050783b41ee11511e1e8fb35623df81806194f4030395f14f48ea37c2798c9f" +checksum = "49f6fa5f8007f008187c3f60b4373209ca83d1cc947f35ede03e16cd15a4d137" dependencies = [ "bitflags 2.11.1", "bstr", @@ -1309,10 +1327,11 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.63.0" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978468bae4ea2df20c72db3b20d0bdb548a0c1090b85a83643b553e6e0e041f2" +checksum = "dede40e89c1e90f548415f50636bb051f6d9c60f68b8b710bc07825722d19588" dependencies = [ + "bisync", "bstr", "gix-date", "gix-features", @@ -1321,7 +1340,6 @@ dependencies = [ "gix-shallow", "gix-transport", "gix-utils", - "maybe-async", "nonempty", "thiserror", ] @@ -1339,9 +1357,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.65.0" +version = "0.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bbfbce1dfd7d7f8469ddef6d3518376aff664348f153cbe0fc3e58ef993d24e" +checksum = "eeb0c90a8f6202ceaaa22996cbf837c943ccb2d8af9ff3490f0758305e6b7883" dependencies = [ "gix-actor", "gix-features", @@ -1359,9 +1377,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.43.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc36a4fb1a1540b59cf2da498783080743fa274b02a3f19ca444fc4015a9d4f" +checksum = "7406282cc0259b51f6aee299ca3d31279a020530363152a2e6c96e8a7f7bbc83" dependencies = [ "bstr", "gix-error", @@ -1375,9 +1393,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "885075c3c21eb9c06e0be3b3728ba5932c04e1c1011dcee7c81801980e3e986f" +checksum = "e55e09d4a1ecf2beecc8c09cafcad37979e805b31f588b0e957e191df5783681" dependencies = [ "bstr", "gix-commitgraph", @@ -1391,9 +1409,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f11fe7ca2585193d3d70bbe0be175a2008d883a704cc7a55e454e113e689455" +checksum = "36c113c0a53294dc6280ffc06cbcc4f50f820397e97d6a00b429a44b8db26e29" dependencies = [ "gix-commitgraph", "gix-date", @@ -1407,9 +1425,9 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8519976e4c7e486270740a5400369f37940779b80bd1377d94cfa1125d01b3" +checksum = "af4fe6c152c1d50aea36f299825702cd37e303307832fec1d0fdd5844e47ce2f" dependencies = [ "bitflags 2.11.1", "gix-path", @@ -1419,9 +1437,9 @@ dependencies = [ [[package]] name = "gix-shallow" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a292fc2fe548c5dfa575479d16b445b0ddf1dd2f56f1fec6aed386f82553cd97" +checksum = "1ecc9f4b40537043e4bbd7d3d1760e74fb8e7b07a546166b558acaa73ad97f4a" dependencies = [ "bstr", "gix-hash", @@ -1432,9 +1450,9 @@ dependencies = [ [[package]] name = "gix-submodule" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f9f594f7cbda0b38ba6b633b3e9a7b7901acdc5d27bc186a16633800cd1ac8" +checksum = "5fd98077a56d08886112e6b08dc94076d03539f4bc0b9d7880e4be2b8a640d8c" dependencies = [ "bstr", "gix-config", @@ -1447,11 +1465,11 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb" +checksum = "b675b920bd5a61d17ad542772f03ec34c60feb8ff683e1560c03ae967363731e" dependencies = [ - "dashmap 6.1.0", + "dashmap 6.2.1", "gix-fs", "libc", "parking_lot", @@ -1460,20 +1478,21 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dc45eae785c0eb14173e0f152e6e224dcf4d45b6a6999a3aed22af541ad678" +checksum = "be3eb81d9dc914335923e50d52829c551feefd6a72d176c4130c546b67a60814" [[package]] name = "gix-transport" -version = "0.57.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186874f7ad1fb2f9a2f2aa9c2dabc7f9dd087bef74c1a0eee2b4a9cf0248fcb3" +checksum = "b7c1bcf30081eb8ab04540a5795c67a2fcb22cb2e976e8b1a4ea657b1ed61469" dependencies = [ "bstr", "gix-command", "gix-features", "gix-packetline", + "gix-path", "gix-quote", "gix-sec", "gix-url", @@ -1482,9 +1501,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.59.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5062cca8f2977565bbaf666ec31dbdb9bc9d9293beb65f9bec52e6c1121b62a1" +checksum = "008c5cd879e46e86b5c2469e633611978b18775d53d05668d691bc13088bd409" dependencies = [ "bitflags 2.11.1", "gix-commitgraph", @@ -1499,40 +1518,43 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bb01ec69d55e82ccb7a19e264501ead4e6aac38463a8cebfdd81e22bb67ab2" +checksum = "42d10e53b8eae21ee601687f47bbbd6cb2ed7162cb4c1cafdd422fb7ec64cbee" dependencies = [ "bstr", "gix-path", + "gix-utils", "percent-encoding", "thiserror", ] [[package]] name = "gix-utils" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c50966184123caf580ffa64e28031a878597f1c7fceb8fe19566c38eb1b771" +checksum = "b1795bd2a970ca8b2185318c2abb97d955c71992f1cf28de73ad3b593a9f3ce8" dependencies = [ + "bstr", "fastrand", + "getrandom", "unicode-normalization", ] [[package]] name = "gix-validate" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc6fc771c4063ba7cd2f47b91fb6076251c6a823b64b7fe7b8874b0fe4afae3" +checksum = "9a034e84d1e04e1b1f20f51f12491da230b6ac8b925d0c8e1b89bcd87a7c5ccc" dependencies = [ "bstr", ] [[package]] name = "gix-worktree" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92399ed66f259592050c6ed9dc80105e095a2f8e87e6b83d98aa2e21d8e27036" +checksum = "31eb8e675122e83585e461fe28f68ff8c5ed55b49017b697e7e76423ff973424" dependencies = [ "bstr", "gix-attributes", @@ -1548,9 +1570,9 @@ dependencies = [ [[package]] name = "gix-worktree-stream" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f3a878c89a05470ad98c644b0015777c530da24854dd29e41fe4f41176840f" +checksum = "3b088c8724e7be120c4798dd86925cf05332c9d356a463542578600c50c7a549" dependencies = [ "gix-attributes", "gix-error", @@ -1564,6 +1586,16 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "gix-zlib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8813f5579b3075ff9c90f7c59cd2b62b4ebb639361f0911648b22d7446cc7c" +dependencies = [ + "thiserror", + "zlib-rs", +] + [[package]] name = "glob" version = "0.3.3" @@ -1746,15 +1778,6 @@ dependencies = [ "jiff-tzdb", ] -[[package]] -name = "kstring" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" -dependencies = [ - "static_assertions", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1806,17 +1829,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "maybe-async" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "memchr" version = "2.8.0" @@ -1825,9 +1837,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -2461,12 +2473,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "std_prelude" version = "0.2.12" diff --git a/Cargo.toml b/Cargo.toml index 3baddf5b..8d8a3be1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ unicode-segmentation = "1.13.2" itertools = "0.14.0" [dependencies.gix] -version = "0.85" +version = "0.86" optional = true default-features = false features = ["sha1", "blob-diff"] From d3448444863f7ad635e44e14ca73b45b03dda896 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:50:26 +0000 Subject: [PATCH 129/130] build(deps): bump minus from 5.7.1 to 5.7.2 Bumps [minus](https://github.com/AMythicDev/minus) from 5.7.1 to 5.7.2. - [Changelog](https://github.com/AMythicDev/minus/blob/main/CHANGELOG.md) - [Commits](https://github.com/AMythicDev/minus/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: minus dependency-version: 5.7.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a46f154..f04a37ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1856,9 +1856,9 @@ dependencies = [ [[package]] name = "minus" -version = "5.7.1" +version = "5.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2657ec5f6a6edc55a85c8db0c572436b612eb036af00e7eee47e0a622095ed96" +checksum = "1db1df1b8dd701aa57b41283b50b751b3ebc8fe1406955ec90c53b46b475fa56" dependencies = [ "crossbeam-channel", "crossterm", From a58f23724a84bab02dbacf4e573fe702d7f84fe8 Mon Sep 17 00:00:00 2001 From: latent-9 <296084221+latent-9@users.noreply.github.com> Date: Sun, 9 Aug 2026 18:05:20 +1200 Subject: [PATCH 130/130] Fix doubled word in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9eb4c095..8826d4e0 100644 --- a/README.md +++ b/README.md @@ -902,7 +902,7 @@ cargo install --path . --locked --force ``` If you want to build an application that uses `bat`'s pretty-printing -features as a library, check out the [the API documentation](https://docs.rs/bat/). +features as a library, check out the [API documentation](https://docs.rs/bat/). Note that you have to use either `regex-onig` or `regex-fancy` as a feature when you depend on `bat` as a library.