From 324cbe839f3110bd4d51726d5a7afe29808ade02 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Sun, 15 Dec 2019 16:51:57 +0200 Subject: Lowercase drive letters on windows before sending to extension. --- crates/ra_lsp_server/src/world.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 927449b45..be3a5bfb8 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -22,6 +22,7 @@ use crate::{ main_loop::pending_requests::{CompletedRequest, LatestRequests}, LspError, Result, }; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct Options { @@ -235,6 +236,9 @@ impl WorldSnapshot { let path = self.vfs.read().file2path(VfsFile(id.0)); let url = Url::from_file_path(&path) .map_err(|_| format!("can't convert path to url: {}", path.display()))?; + + #[cfg(target_os = "windows")] + let url = lowercase_drive_letter(&url); Ok(url) } @@ -279,3 +283,33 @@ impl WorldSnapshot { self.analysis.feature_flags() } } + +#[cfg(target_os = "windows")] +fn lowercase_drive_letter(url: &Url) -> Url { + let s = url.to_string(); + let drive_partition: Vec<&str> = s.rsplitn(2, ':').collect::>(); + + if drive_partition.len() == 1 { + return url.clone(); + } + + let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; + let url = Url::from_str(&joined).expect("This came from a valid `Url`"); + url +} + +#[test] +fn test_lowercase_drive_letter_with_drive() { + let url = Url::from_file_path("C:\\Test").unwrap(); + let url = lowercase_drive_letter(&url); + + assert_eq!(url.to_string(), "file:///c:/Test"); +} + +#[test] +fn test_drive_without_colon_passthrough() { + let url = Url::from_file_path(r#"\\localhost\C$\my_dir"#).expect("Should work"); + let url = lowercase_drive_letter(&url); + + assert_eq!(url.to_string(), "file:///C$/my_dir"); +} -- cgit v1.2.3 From ebf302d2610527c35b8fb794a03cc1c280c8a9d3 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Sun, 15 Dec 2019 17:03:39 +0200 Subject: move import inside cfg block --- crates/ra_lsp_server/src/world.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index be3a5bfb8..f139a5728 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -22,7 +22,6 @@ use crate::{ main_loop::pending_requests::{CompletedRequest, LatestRequests}, LspError, Result, }; -use std::str::FromStr; #[derive(Debug, Clone)] pub struct Options { @@ -286,6 +285,8 @@ impl WorldSnapshot { #[cfg(target_os = "windows")] fn lowercase_drive_letter(url: &Url) -> Url { + use std::str::FromStr; + let s = url.to_string(); let drive_partition: Vec<&str> = s.rsplitn(2, ':').collect::>(); -- cgit v1.2.3 From 2e2fae32dff6cab508768953bbd34b88b5bea166 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Sun, 15 Dec 2019 17:46:00 +0200 Subject: improved path checking to consider only paths that may contain a windows drive. --- crates/ra_lsp_server/src/world.rs | 64 +++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 20 deletions(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index f139a5728..63a820012 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -17,11 +17,13 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; use relative_path::RelativePathBuf; +use std::path::{Component, Prefix}; use crate::{ main_loop::pending_requests::{CompletedRequest, LatestRequests}, LspError, Result, }; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct Options { @@ -233,11 +235,8 @@ impl WorldSnapshot { pub fn file_id_to_uri(&self, id: FileId) -> Result { let path = self.vfs.read().file2path(VfsFile(id.0)); - let url = Url::from_file_path(&path) - .map_err(|_| format!("can't convert path to url: {}", path.display()))?; + let url = url_from_path_with_drive_lowercasing(path)?; - #[cfg(target_os = "windows")] - let url = lowercase_drive_letter(&url); Ok(url) } @@ -283,34 +282,59 @@ impl WorldSnapshot { } } -#[cfg(target_os = "windows")] -fn lowercase_drive_letter(url: &Url) -> Url { - use std::str::FromStr; +/// Returns a `Url` object from a given path, will lowercase drive letters if present. +/// This will only happen when processing windows paths. +/// +/// When processing non-windows path, this is essentially the same as `Url::from_file_path`. +fn url_from_path_with_drive_lowercasing(path: impl AsRef) -> Result { + let component_has_windows_drive = path + .as_ref() + .components() + .find(|comp| { + if let Component::Prefix(c) = comp { + match c.kind() { + Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true, + _ => return false, + } + } + false + }) + .is_some(); + + // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. + if component_has_windows_drive { + let url_original = Url::from_file_path(&path) + .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; + + let drive_partition: Vec<&str> = + url_original.as_str().rsplitn(2, ':').collect::>(); + + // There is a drive partition, but we never found a colon. + // This should not happen, but in this case we just pass it through. + if drive_partition.len() == 1 { + return Ok(url_original); + } - let s = url.to_string(); - let drive_partition: Vec<&str> = s.rsplitn(2, ':').collect::>(); + let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; + let url = Url::from_str(&joined).expect("This came from a valid `Url`"); - if drive_partition.len() == 1 { - return url.clone(); + Ok(url) + } else { + Ok(Url::from_file_path(&path) + .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) } - - let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; - let url = Url::from_str(&joined).expect("This came from a valid `Url`"); - url } #[test] fn test_lowercase_drive_letter_with_drive() { - let url = Url::from_file_path("C:\\Test").unwrap(); - let url = lowercase_drive_letter(&url); + let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); assert_eq!(url.to_string(), "file:///c:/Test"); } #[test] fn test_drive_without_colon_passthrough() { - let url = Url::from_file_path(r#"\\localhost\C$\my_dir"#).expect("Should work"); - let url = lowercase_drive_letter(&url); + let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); - assert_eq!(url.to_string(), "file:///C$/my_dir"); + assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); } -- cgit v1.2.3 From 40116af598ef51cab8a1059dceda7508f994387c Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Sun, 15 Dec 2019 17:54:24 +0200 Subject: cfg gated tests that only work on windows. --- crates/ra_lsp_server/src/world.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 63a820012..8b48726ee 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -325,16 +325,22 @@ fn url_from_path_with_drive_lowercasing(path: impl AsRef) -> Result { } } -#[test] -fn test_lowercase_drive_letter_with_drive() { - let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); - - assert_eq!(url.to_string(), "file:///c:/Test"); -} +// `Url` is not able to parse windows paths on unix machines. +#[cfg(target_os = "windows")] +#[cfg(test)] +mod path_conversion_windows_tests { + use super::url_from_path_with_drive_lowercasing; + #[test] + fn test_lowercase_drive_letter_with_drive() { + let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); + + assert_eq!(url.to_string(), "file:///c:/Test"); + } -#[test] -fn test_drive_without_colon_passthrough() { - let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); + #[test] + fn test_drive_without_colon_passthrough() { + let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); - assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); + assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); + } } -- cgit v1.2.3 From 6fba427bf35bebdc7aa08a241ecbe83a2f725127 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Sun, 15 Dec 2019 18:15:24 +0200 Subject: remove unnecessary turbofish. --- crates/ra_lsp_server/src/world.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 8b48726ee..16cc11e8c 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -306,8 +306,7 @@ fn url_from_path_with_drive_lowercasing(path: impl AsRef) -> Result { let url_original = Url::from_file_path(&path) .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; - let drive_partition: Vec<&str> = - url_original.as_str().rsplitn(2, ':').collect::>(); + let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect(); // There is a drive partition, but we never found a colon. // This should not happen, but in this case we just pass it through. -- cgit v1.2.3