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!(