diff options
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 64 |
1 files changed, 44 insertions, 20 deletions
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}; | |||
17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
19 | use relative_path::RelativePathBuf; | 19 | use relative_path::RelativePathBuf; |
20 | use std::path::{Component, Prefix}; | ||
20 | 21 | ||
21 | use crate::{ | 22 | use crate::{ |
22 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | 23 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, |
23 | LspError, Result, | 24 | LspError, Result, |
24 | }; | 25 | }; |
26 | use std::str::FromStr; | ||
25 | 27 | ||
26 | #[derive(Debug, Clone)] | 28 | #[derive(Debug, Clone)] |
27 | pub struct Options { | 29 | pub struct Options { |
@@ -233,11 +235,8 @@ impl WorldSnapshot { | |||
233 | 235 | ||
234 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { | 236 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { |
235 | let path = self.vfs.read().file2path(VfsFile(id.0)); | 237 | let path = self.vfs.read().file2path(VfsFile(id.0)); |
236 | let url = Url::from_file_path(&path) | 238 | let url = url_from_path_with_drive_lowercasing(path)?; |
237 | .map_err(|_| format!("can't convert path to url: {}", path.display()))?; | ||
238 | 239 | ||
239 | #[cfg(target_os = "windows")] | ||
240 | let url = lowercase_drive_letter(&url); | ||
241 | Ok(url) | 240 | Ok(url) |
242 | } | 241 | } |
243 | 242 | ||
@@ -283,34 +282,59 @@ impl WorldSnapshot { | |||
283 | } | 282 | } |
284 | } | 283 | } |
285 | 284 | ||
286 | #[cfg(target_os = "windows")] | 285 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. |
287 | fn lowercase_drive_letter(url: &Url) -> Url { | 286 | /// This will only happen when processing windows paths. |
288 | use std::str::FromStr; | 287 | /// |
288 | /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. | ||
289 | fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> { | ||
290 | let component_has_windows_drive = path | ||
291 | .as_ref() | ||
292 | .components() | ||
293 | .find(|comp| { | ||
294 | if let Component::Prefix(c) = comp { | ||
295 | match c.kind() { | ||
296 | Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true, | ||
297 | _ => return false, | ||
298 | } | ||
299 | } | ||
300 | false | ||
301 | }) | ||
302 | .is_some(); | ||
303 | |||
304 | // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. | ||
305 | if component_has_windows_drive { | ||
306 | let url_original = Url::from_file_path(&path) | ||
307 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; | ||
308 | |||
309 | let drive_partition: Vec<&str> = | ||
310 | url_original.as_str().rsplitn(2, ':').collect::<Vec<&str>>(); | ||
311 | |||
312 | // There is a drive partition, but we never found a colon. | ||
313 | // This should not happen, but in this case we just pass it through. | ||
314 | if drive_partition.len() == 1 { | ||
315 | return Ok(url_original); | ||
316 | } | ||
289 | 317 | ||
290 | let s = url.to_string(); | 318 | let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; |
291 | let drive_partition: Vec<&str> = s.rsplitn(2, ':').collect::<Vec<&str>>(); | 319 | let url = Url::from_str(&joined).expect("This came from a valid `Url`"); |
292 | 320 | ||
293 | if drive_partition.len() == 1 { | 321 | Ok(url) |
294 | return url.clone(); | 322 | } else { |
323 | Ok(Url::from_file_path(&path) | ||
324 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) | ||
295 | } | 325 | } |
296 | |||
297 | let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; | ||
298 | let url = Url::from_str(&joined).expect("This came from a valid `Url`"); | ||
299 | url | ||
300 | } | 326 | } |
301 | 327 | ||
302 | #[test] | 328 | #[test] |
303 | fn test_lowercase_drive_letter_with_drive() { | 329 | fn test_lowercase_drive_letter_with_drive() { |
304 | let url = Url::from_file_path("C:\\Test").unwrap(); | 330 | let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); |
305 | let url = lowercase_drive_letter(&url); | ||
306 | 331 | ||
307 | assert_eq!(url.to_string(), "file:///c:/Test"); | 332 | assert_eq!(url.to_string(), "file:///c:/Test"); |
308 | } | 333 | } |
309 | 334 | ||
310 | #[test] | 335 | #[test] |
311 | fn test_drive_without_colon_passthrough() { | 336 | fn test_drive_without_colon_passthrough() { |
312 | let url = Url::from_file_path(r#"\\localhost\C$\my_dir"#).expect("Should work"); | 337 | let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); |
313 | let url = lowercase_drive_letter(&url); | ||
314 | 338 | ||
315 | assert_eq!(url.to_string(), "file:///C$/my_dir"); | 339 | assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); |
316 | } | 340 | } |