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