From 9edd81c74032d749d18c51e32f64ad74cc81f9c0 Mon Sep 17 00:00:00 2001
From: Tim <57221600+TiltedToast@users.noreply.github.com>
Date: Mon, 6 May 2024 22:27:26 +0200
Subject: [PATCH] Add Windows specific path parsing (#11119)
Since Windows paths are known to be weird and currently not handled at
all (outside of relative paths that just happen to work), I figured I
would add a windows specific implementation for parsing absolute paths.
It should be functionally the same, of course there's always a chance I
missed an edge case though.
This should fix
- #10849
Note that there are still some cases that will probably break the
current implementation, namely local drives that do not have a drive
letter assigned (not sure how to handle those). There's also UNC paths
but I don't know how important those are at the moment (I'll allow
myself to assume not at all)
Release Notes:
- N/A
---
crates/util/src/paths.rs | 109 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 108 insertions(+), 1 deletion(-)
diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs
index 198b3ebb89..e43c60a5b3 100644
--- a/crates/util/src/paths.rs
+++ b/crates/util/src/paths.rs
@@ -189,7 +189,17 @@ impl
PathLikeWithPosition
{
})
};
- match s.trim().split_once(FILE_ROW_COLUMN_DELIMITER) {
+ let trimmed = s.trim();
+
+ #[cfg(target_os = "windows")]
+ {
+ let is_absolute = trimmed.starts_with(r"\\?\");
+ if is_absolute {
+ return Self::parse_absolute_path(trimmed, parse_path_like_str);
+ }
+ }
+
+ match trimmed.split_once(FILE_ROW_COLUMN_DELIMITER) {
Some((path_like_str, maybe_row_and_col_str)) => {
let path_like_str = path_like_str.trim();
let maybe_row_and_col_str = maybe_row_and_col_str.trim();
@@ -243,6 +253,58 @@ impl
PathLikeWithPosition
{
}
}
+ /// This helper function is used for parsing absolute paths on Windows. It exists because absolute paths on Windows are quite different from other platforms. See [this page](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#dos-device-paths) for more information.
+ #[cfg(target_os = "windows")]
+ fn parse_absolute_path(
+ s: &str,
+ parse_path_like_str: impl Fn(&str) -> Result,
+ ) -> Result {
+ let fallback = |fallback_str| {
+ Ok(Self {
+ path_like: parse_path_like_str(fallback_str)?,
+ row: None,
+ column: None,
+ })
+ };
+
+ let mut iterator = s.split(FILE_ROW_COLUMN_DELIMITER);
+
+ let drive_prefix = iterator.next().unwrap_or_default();
+ let file_path = iterator.next().unwrap_or_default();
+
+ // TODO: How to handle drives without a letter? UNC paths?
+ let complete_path = drive_prefix.replace("\\\\?\\", "") + ":" + &file_path;
+
+ if let Some(row_str) = iterator.next() {
+ if let Some(column_str) = iterator.next() {
+ match row_str.parse::() {
+ Ok(row) => match column_str.parse::() {
+ Ok(col) => {
+ return Ok(Self {
+ path_like: parse_path_like_str(&complete_path)?,
+ row: Some(row),
+ column: Some(col),
+ });
+ }
+
+ Err(_) => {
+ return Ok(Self {
+ path_like: parse_path_like_str(&complete_path)?,
+ row: Some(row),
+ column: None,
+ });
+ }
+ },
+
+ Err(_) => {
+ return fallback(&complete_path);
+ }
+ }
+ }
+ }
+ return fallback(&complete_path);
+ }
+
pub fn map_path_like(
self,
mapping: impl FnOnce(P) -> Result,
@@ -392,6 +454,7 @@ mod tests {
// Trim off trailing `:`s for otherwise valid input.
#[test]
fn path_with_position_parsing_special() {
+ #[cfg(not(target_os = "windows"))]
let input_and_expected = [
(
"test_file.rs:",
@@ -419,6 +482,50 @@ mod tests {
),
];
+ #[cfg(target_os = "windows")]
+ let input_and_expected = [
+ (
+ "test_file.rs:",
+ PathLikeWithPosition {
+ path_like: "test_file.rs".to_string(),
+ row: None,
+ column: None,
+ },
+ ),
+ (
+ "test_file.rs:1:",
+ PathLikeWithPosition {
+ path_like: "test_file.rs".to_string(),
+ row: Some(1),
+ column: None,
+ },
+ ),
+ (
+ "\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:",
+ PathLikeWithPosition {
+ path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
+ row: Some(1902),
+ column: Some(13),
+ },
+ ),
+ (
+ "\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:15:",
+ PathLikeWithPosition {
+ path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
+ row: Some(1902),
+ column: Some(13),
+ },
+ ),
+ (
+ "\\\\?\\C:\\Users\\someone\\test_file.rs:1902:::15:",
+ PathLikeWithPosition {
+ path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
+ row: Some(1902),
+ column: None,
+ },
+ ),
+ ];
+
for (input, expected) in input_and_expected {
let actual = parse_str(input);
assert_eq!(