From e7b327beff6fbe39039deefe357c5df1f2078af8 Mon Sep 17 00:00:00 2001 From: Rizky Mirzaviandy Priambodo <142987522+Xavrir@users.noreply.github.com> Date: Sun, 8 Mar 2026 12:15:27 +0700 Subject: [PATCH 1/2] Replace path_abs dependency with std::path::absolute std::path::absolute() has been stable since Rust 1.79 and bat's MSRV is 1.88. This replaces path_abs (and its transitive dep std_prelude) with the standard library function, reducing the dependency count by 2. A lexical normalization step is applied after making the path absolute to match PathAbs::new() behavior of resolving . and .. components. --- CHANGELOG.md | 1 + Cargo.lock | 16 ---------------- Cargo.toml | 1 - src/assets.rs | 27 ++++++++++++++++++++------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 293e55dc19..6cefa4ce88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ ## Other +- Replace `path_abs` dependency with `std::path::absolute` (stable since Rust 1.79), see #3622 (@Xavrir) - Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX) ## Syntaxes diff --git a/Cargo.lock b/Cargo.lock index 2e777a2e6b..b5505fd81f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,6 @@ dependencies = [ "nix", "nu-ansi-term", "once_cell", - "path_abs", "plist", "predicates", "prettyplease", @@ -1135,15 +1134,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "path_abs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ef02f6342ac01d8a93b65f96db53fe68a92a15f41144f97fb00a9e669633c3" -dependencies = [ - "std_prelude", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1531,12 +1521,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "std_prelude" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" - [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 6c78333655..ead07788ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,6 @@ serde = "1.0" serde_derive = "1.0" 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 } etcetera = { version = "0.11.0", optional = true } diff --git a/src/assets.rs b/src/assets.rs index 1315537fc3..07043a8905 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -1,14 +1,12 @@ use std::ffi::OsStr; use std::fs; -use std::path::Path; +use std::path::{self, Component, Path, PathBuf}; use once_cell::unsync::OnceCell; use syntect::highlighting::Theme; use syntect::parsing::{SyntaxReference, SyntaxSet}; -use path_abs::PathAbs; - use crate::error::*; use crate::input::{InputReader, OpenedInput}; use crate::syntax_mapping::ignored_suffixes::IgnoredSuffixes; @@ -29,6 +27,24 @@ mod build_assets; mod lazy_theme_set; mod serialized_syntax_set; +fn absolute_normalized(path: &Path) -> PathBuf { + let absolute = match path::absolute(path) { + Ok(p) => p, + Err(_) => return path.to_owned(), + }; + let mut normalized = PathBuf::new(); + for component in absolute.components() { + match component { + Component::ParentDir => { + normalized.pop(); + } + Component::CurDir => {} + _ => normalized.push(component), + } + } + normalized +} + #[derive(Debug)] pub struct HighlightingAssets { syntax_set_cell: OnceCell, @@ -224,10 +240,7 @@ impl HighlightingAssets { let path = input.path(); let path_syntax = if let Some(path) = path { - self.get_syntax_for_path( - PathAbs::new(path).map_or_else(|_| path.to_owned(), |p| p.as_path().to_path_buf()), - mapping, - ) + self.get_syntax_for_path(absolute_normalized(path), mapping) } else { Err(Error::UndetectedSyntax("[unknown]".into())) }; From 76938ff516bb5670c8a0e192332b8e45459a0983 Mon Sep 17 00:00:00 2001 From: Rizky Mirzaviandy Priambodo <142987522+Xavrir@users.noreply.github.com> Date: Sun, 8 Mar 2026 23:35:19 +0700 Subject: [PATCH 2/2] fix: strip Windows verbatim path prefix for consistent glob matching --- src/assets.rs | 18 +++++++++++++++++- src/syntax_mapping/builtin.rs | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/assets.rs b/src/assets.rs index 07043a8905..74fa768146 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -42,7 +42,23 @@ fn absolute_normalized(path: &Path) -> PathBuf { _ => normalized.push(component), } } - normalized + strip_windows_verbatim_prefix(normalized) +} + +/// On Windows, strip the extended-length `\\?\` prefix from paths so they +/// match glob patterns built from environment variables (which are also +/// stripped in `builtin.rs`). +#[cfg(windows)] +fn strip_windows_verbatim_prefix(path: PathBuf) -> PathBuf { + path.to_str() + .and_then(|s| s.strip_prefix(r"\\?\")) + .map(PathBuf::from) + .unwrap_or(path) +} + +#[cfg(not(windows))] +fn strip_windows_verbatim_prefix(path: PathBuf) -> PathBuf { + path } #[derive(Debug)] diff --git a/src/syntax_mapping/builtin.rs b/src/syntax_mapping/builtin.rs index 1822be5706..e2d6750515 100644 --- a/src/syntax_mapping/builtin.rs +++ b/src/syntax_mapping/builtin.rs @@ -72,7 +72,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option { MatcherSegment::Text(s) => buf.push_str(s), MatcherSegment::Env(var) => { let replaced = env::var(var).ok()?; - buf.push_str(&replaced); + buf.push_str(&strip_verbatim_prefix(&replaced)); } } } @@ -81,6 +81,21 @@ fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option { Some(matcher) } +/// On Windows, environment variables (e.g. `XDG_CONFIG_HOME`, `HOME`) may +/// contain paths with the extended-length `\\?\` prefix if they were set from +/// a canonicalized path. Since `std::path::absolute` does NOT produce this +/// prefix, we strip it from env var values so that glob patterns and file +/// paths use a consistent representation. +#[cfg(windows)] +fn strip_verbatim_prefix(s: &str) -> String { + s.strip_prefix(r"\\?\").unwrap_or(s).to_string() +} + +#[cfg(not(windows))] +fn strip_verbatim_prefix(s: &str) -> String { + s.to_string() +} + /// A segment of a dynamic builtin matcher. /// /// Used internally by `Lazy>`'s lazy evaluation closure.