diff options
-rw-r--r-- | crates/ra_cargo_watch/src/conv.rs | 68 | ||||
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 64 |
3 files changed, 71 insertions, 63 deletions
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs index 3bd4bf7a5..dedb0751e 100644 --- a/crates/ra_cargo_watch/src/conv.rs +++ b/crates/ra_cargo_watch/src/conv.rs | |||
@@ -8,7 +8,11 @@ use lsp_types::{ | |||
8 | Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, | 8 | Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, |
9 | NumberOrString, Position, Range, Url, | 9 | NumberOrString, Position, Range, Url, |
10 | }; | 10 | }; |
11 | use std::{fmt::Write, path::PathBuf}; | 11 | use std::{ |
12 | fmt::Write, | ||
13 | path::{Component, Path, PathBuf, Prefix}, | ||
14 | str::FromStr, | ||
15 | }; | ||
12 | 16 | ||
13 | #[cfg(test)] | 17 | #[cfg(test)] |
14 | mod test; | 18 | mod test; |
@@ -57,7 +61,7 @@ fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Loca | |||
57 | 61 | ||
58 | let mut file_name = workspace_root.clone(); | 62 | let mut file_name = workspace_root.clone(); |
59 | file_name.push(&span.file_name); | 63 | file_name.push(&span.file_name); |
60 | let uri = Url::from_file_path(file_name).unwrap(); | 64 | let uri = url_from_path_with_drive_lowercasing(file_name).unwrap(); |
61 | 65 | ||
62 | let range = Range::new( | 66 | let range = Range::new( |
63 | Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), | 67 | Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), |
@@ -278,3 +282,63 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
278 | 282 | ||
279 | Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes }) | 283 | Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes }) |
280 | } | 284 | } |
285 | |||
286 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. | ||
287 | /// This will only happen when processing windows paths. | ||
288 | /// | ||
289 | /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. | ||
290 | pub fn url_from_path_with_drive_lowercasing( | ||
291 | path: impl AsRef<Path>, | ||
292 | ) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> { | ||
293 | let component_has_windows_drive = path.as_ref().components().any(|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 | |||
303 | // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. | ||
304 | if component_has_windows_drive { | ||
305 | let url_original = Url::from_file_path(&path) | ||
306 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; | ||
307 | |||
308 | let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect(); | ||
309 | |||
310 | // There is a drive partition, but we never found a colon. | ||
311 | // This should not happen, but in this case we just pass it through. | ||
312 | if drive_partition.len() == 1 { | ||
313 | return Ok(url_original); | ||
314 | } | ||
315 | |||
316 | let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; | ||
317 | let url = Url::from_str(&joined).expect("This came from a valid `Url`"); | ||
318 | |||
319 | Ok(url) | ||
320 | } else { | ||
321 | Ok(Url::from_file_path(&path) | ||
322 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) | ||
323 | } | ||
324 | } | ||
325 | |||
326 | // `Url` is not able to parse windows paths on unix machines. | ||
327 | #[cfg(target_os = "windows")] | ||
328 | #[cfg(test)] | ||
329 | mod path_conversion_windows_tests { | ||
330 | use super::url_from_path_with_drive_lowercasing; | ||
331 | #[test] | ||
332 | fn test_lowercase_drive_letter_with_drive() { | ||
333 | let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); | ||
334 | |||
335 | assert_eq!(url.to_string(), "file:///c:/Test"); | ||
336 | } | ||
337 | |||
338 | #[test] | ||
339 | fn test_drive_without_colon_passthrough() { | ||
340 | let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); | ||
341 | |||
342 | assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); | ||
343 | } | ||
344 | } | ||
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index e5c22e599..9bc0fd405 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -21,6 +21,8 @@ mod conv; | |||
21 | 21 | ||
22 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix}; | 22 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix}; |
23 | 23 | ||
24 | pub use crate::conv::url_from_path_with_drive_lowercasing; | ||
25 | |||
24 | #[derive(Clone, Debug)] | 26 | #[derive(Clone, Debug)] |
25 | pub struct CheckOptions { | 27 | pub struct CheckOptions { |
26 | pub enable: bool, | 28 | pub enable: bool, |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 4b3959e38..121ddfd1f 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -12,7 +12,9 @@ use crossbeam_channel::{unbounded, Receiver}; | |||
12 | use lsp_server::ErrorCode; | 12 | use lsp_server::ErrorCode; |
13 | use lsp_types::Url; | 13 | use lsp_types::Url; |
14 | use parking_lot::RwLock; | 14 | use parking_lot::RwLock; |
15 | use ra_cargo_watch::{CheckOptions, CheckWatcher, CheckWatcherSharedState}; | 15 | use ra_cargo_watch::{ |
16 | url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher, CheckWatcherSharedState, | ||
17 | }; | ||
16 | use ra_ide::{ | 18 | use ra_ide::{ |
17 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 19 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
18 | SourceRootId, | 20 | SourceRootId, |
@@ -21,13 +23,11 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; | |||
21 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 23 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
22 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 24 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
23 | use relative_path::RelativePathBuf; | 25 | use relative_path::RelativePathBuf; |
24 | use std::path::{Component, Prefix}; | ||
25 | 26 | ||
26 | use crate::{ | 27 | use crate::{ |
27 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | 28 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, |
28 | LspError, Result, | 29 | LspError, Result, |
29 | }; | 30 | }; |
30 | use std::str::FromStr; | ||
31 | 31 | ||
32 | #[derive(Debug, Clone)] | 32 | #[derive(Debug, Clone)] |
33 | pub struct Options { | 33 | pub struct Options { |
@@ -294,61 +294,3 @@ impl WorldSnapshot { | |||
294 | self.analysis.feature_flags() | 294 | self.analysis.feature_flags() |
295 | } | 295 | } |
296 | } | 296 | } |
297 | |||
298 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. | ||
299 | /// This will only happen when processing windows paths. | ||
300 | /// | ||
301 | /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. | ||
302 | fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> { | ||
303 | let component_has_windows_drive = path.as_ref().components().any(|comp| { | ||
304 | if let Component::Prefix(c) = comp { | ||
305 | match c.kind() { | ||
306 | Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true, | ||
307 | _ => return false, | ||
308 | } | ||
309 | } | ||
310 | false | ||
311 | }); | ||
312 | |||
313 | // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. | ||
314 | if component_has_windows_drive { | ||
315 | let url_original = Url::from_file_path(&path) | ||
316 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; | ||
317 | |||
318 | let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect(); | ||
319 | |||
320 | // There is a drive partition, but we never found a colon. | ||
321 | // This should not happen, but in this case we just pass it through. | ||
322 | if drive_partition.len() == 1 { | ||
323 | return Ok(url_original); | ||
324 | } | ||
325 | |||
326 | let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; | ||
327 | let url = Url::from_str(&joined).expect("This came from a valid `Url`"); | ||
328 | |||
329 | Ok(url) | ||
330 | } else { | ||
331 | Ok(Url::from_file_path(&path) | ||
332 | .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) | ||
333 | } | ||
334 | } | ||
335 | |||
336 | // `Url` is not able to parse windows paths on unix machines. | ||
337 | #[cfg(target_os = "windows")] | ||
338 | #[cfg(test)] | ||
339 | mod path_conversion_windows_tests { | ||
340 | use super::url_from_path_with_drive_lowercasing; | ||
341 | #[test] | ||
342 | fn test_lowercase_drive_letter_with_drive() { | ||
343 | let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); | ||
344 | |||
345 | assert_eq!(url.to_string(), "file:///c:/Test"); | ||
346 | } | ||
347 | |||
348 | #[test] | ||
349 | fn test_drive_without_colon_passthrough() { | ||
350 | let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); | ||
351 | |||
352 | assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); | ||
353 | } | ||
354 | } | ||