diff --git a/CHANGELOG.md b/CHANGELOG.md index 2676d03c..245796e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): ### Fixed +- Normalize `\\?\`-prefixed Verbatim paths when creating relative symlinks on Windows ([#4067]) - Keep package hashes indifferent to line endings when `ya pkg` pulls packages ([#4064]) - Use WebP as `magick` preset preloader cache format to keep image transparency ([#4065]) @@ -1753,3 +1754,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#4040]: https://github.com/sxyazi/yazi/pull/4040 [#4064]: https://github.com/sxyazi/yazi/pull/4064 [#4065]: https://github.com/sxyazi/yazi/pull/4065 +[#4067]: https://github.com/sxyazi/yazi/pull/4067 diff --git a/yazi-fs/src/path/relative.rs b/yazi-fs/src/path/relative.rs index 7b0808e2..f58082e2 100644 --- a/yazi-fs/src/path/relative.rs +++ b/yazi-fs/src/path/relative.rs @@ -1,3 +1,5 @@ +use std::path::PrefixComponent; + use anyhow::{Result, bail}; use yazi_shared::path::{PathBufDyn, PathCow, PathDyn, PathLike}; @@ -28,7 +30,7 @@ fn path_relative_to_impl<'a>(from: PathCow<'_>, to: PathCow<'a>) -> Result {} - (Some(Prefix(a)), Some(Prefix(b))) if a == b => {} + (Some(Prefix(a)), Some(Prefix(b))) if path_prefix_eq(a, b) => {} (Some(Prefix(_) | RootDir), _) | (_, Some(Prefix(_) | RootDir)) => { return Ok(to); } @@ -45,6 +47,20 @@ fn path_relative_to_impl<'a>(from: PathCow<'_>, to: PathCow<'a>) -> Result, b: PrefixComponent<'_>) -> bool { + use std::path::Prefix::*; + + match (a.kind(), b.kind()) { + (Disk(a), VerbatimDisk(b)) | (VerbatimDisk(a), Disk(b)) => a == b, + (UNC(a1, a2), VerbatimUNC(b1, b2)) | (VerbatimUNC(a1, a2), UNC(b1, b2)) => a1 == b1 && a2 == b2, + _ => a == b, + } +} + +#[cfg(not(windows))] +fn path_prefix_eq(a: PrefixComponent<'_>, b: PrefixComponent<'_>) -> bool { a == b } + #[cfg(test)] mod tests { use yazi_shared::path::PathDyn; @@ -80,6 +96,14 @@ mod tests { (r"C:\a\b\d", r"C:\a\b\c", r"..\c"), (r"C:\a\b\c", r"C:\a", r"..\.."), (r"C:\a\b\b", r"C:\a\a\b", r"..\..\a\b"), + // Verbatim (`\\?\`-prefixed) paths + (r"\\?\C:\a\b", r"C:\a\b\c", "c"), + (r"C:\a\b", r"\\?\C:\a\b\c", "c"), + (r"\\?\C:\a\b", r"\\?\C:\a\b\c", "c"), + (r"\\?\C:\a\b\c", r"C:\a\b", r".."), + (r"\\?\C:\a\b\d", r"C:\a\b\c", r"..\c"), + (r"\\?\C:\a\b\c", r"\\?\C:\a", r"..\.."), + (r"\\?\C:\a\b", r"D:\a\b\c", r"D:\a\b\c"), ]; for (from, to, expected) in cases {