diff options
author | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
commit | b21d9337d9200e2cfdc90b386591c72c302dc03e (patch) | |
tree | f81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_lsp_server/src/world.rs | |
parent | 18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff) | |
parent | ce07a2daa9e53aa86a769f8641b14c2878444fbc (diff) |
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_lsp_server/src/world.rs')
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 75 |
1 files changed, 69 insertions, 6 deletions
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 927449b45..79431e7e6 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! The context or environment in which the language server functions. |
2 | //! In our server implementation this is know as the `WorldState`. | ||
3 | //! | ||
4 | //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. | ||
2 | 5 | ||
3 | use std::{ | 6 | use std::{ |
4 | path::{Path, PathBuf}, | 7 | path::{Path, PathBuf}, |
@@ -17,11 +20,13 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; | |||
17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 20 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 21 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
19 | use relative_path::RelativePathBuf; | 22 | use relative_path::RelativePathBuf; |
23 | use std::path::{Component, Prefix}; | ||
20 | 24 | ||
21 | use crate::{ | 25 | use crate::{ |
22 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | 26 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, |
23 | LspError, Result, | 27 | LspError, Result, |
24 | }; | 28 | }; |
29 | use std::str::FromStr; | ||
25 | 30 | ||
26 | #[derive(Debug, Clone)] | 31 | #[derive(Debug, Clone)] |
27 | pub struct Options { | 32 | pub struct Options { |
@@ -140,10 +145,10 @@ impl WorldState { | |||
140 | /// FIXME: better API here | 145 | /// FIXME: better API here |
141 | pub fn process_changes( | 146 | pub fn process_changes( |
142 | &mut self, | 147 | &mut self, |
143 | ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> { | 148 | ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> { |
144 | let changes = self.vfs.write().commit_changes(); | 149 | let changes = self.vfs.write().commit_changes(); |
145 | if changes.is_empty() { | 150 | if changes.is_empty() { |
146 | return Vec::new(); | 151 | return None; |
147 | } | 152 | } |
148 | let mut libs = Vec::new(); | 153 | let mut libs = Vec::new(); |
149 | let mut change = AnalysisChange::new(); | 154 | let mut change = AnalysisChange::new(); |
@@ -177,7 +182,7 @@ impl WorldState { | |||
177 | } | 182 | } |
178 | } | 183 | } |
179 | self.analysis_host.apply_change(change); | 184 | self.analysis_host.apply_change(change); |
180 | libs | 185 | Some(libs) |
181 | } | 186 | } |
182 | 187 | ||
183 | pub fn add_lib(&mut self, data: LibraryData) { | 188 | pub fn add_lib(&mut self, data: LibraryData) { |
@@ -233,8 +238,8 @@ impl WorldSnapshot { | |||
233 | 238 | ||
234 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { | 239 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { |
235 | let path = self.vfs.read().file2path(VfsFile(id.0)); | 240 | let path = self.vfs.read().file2path(VfsFile(id.0)); |
236 | let url = Url::from_file_path(&path) | 241 | let url = url_from_path_with_drive_lowercasing(path)?; |
237 | .map_err(|_| format!("can't convert path to url: {}", path.display()))?; | 242 | |
238 | Ok(url) | 243 | Ok(url) |
239 | } | 244 | } |
240 | 245 | ||
@@ -279,3 +284,61 @@ impl WorldSnapshot { | |||
279 | self.analysis.feature_flags() | 284 | self.analysis.feature_flags() |
280 | } | 285 | } |
281 | } | 286 | } |
287 | |||
288 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. | ||
289 | /// This will only happen when processing windows paths. | ||
290 | /// | ||
291 | /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. | ||
292 | fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> { | ||
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 | } | ||