diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-14 18:26:07 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-14 18:26:07 +0100 |
commit | 88be6f32172813f53dae60d73c9f5deb0c3fb29f (patch) | |
tree | 0b5f8f793929f651dfe332e0f5545b938ff5189f /crates/ra_lsp_server/src | |
parent | 5d35f284f5ac70cde5d758e7c63a38eae0fb0b55 (diff) | |
parent | c2dfc8a229c0a18dff08d5ce7e6836c91648eee5 (diff) |
Merge #1137
1137: Adds support for multiple editor workspaces on initialization r=matklad a=jrvidal
OK, so this "simple hack" turned out to be way more contrived than I expected :joy:
### What works
This patch only handles multi-folder editor workspaces _on initialization_.
* I've found that modifying the layout of a workspace in VSCode just reloads the extension, so this hack should be enough for now.
* Not sure about how emacs-lsp behaves, but we fallback gracefully to the mono-folder workspace, so it should be fine.
### What doesn't work
* [x] `cargo watch` can only watch a single root folder with a `Cargo.toml`. I've left this part untouched but we could either warn that it's not supported or launch _multiple_ `cargo-watch` processes.
* [x] The `rust-analyzer/runnables` command is not functional, since we don't send the correct `cwd`.
* [x] Should we add some happy path test to `heavy_tests`?
* [ ] Going from a single `root` to multiple `roots` leaves us with a couple of `n * m` loops that smell a bit. The number of folders in the editor workspace is probably low though.
Co-authored-by: Roberto Vidal <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server/src')
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 13 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 31 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 5 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/server_world.rs | 27 |
5 files changed, 52 insertions, 25 deletions
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index eb4091a3d..82f52a6e8 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -40,12 +40,23 @@ fn main_inner() -> Result<()> { | |||
40 | run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { | 40 | run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { |
41 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); | 41 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); |
42 | 42 | ||
43 | let workspace_roots = params | ||
44 | .workspace_folders | ||
45 | .map(|workspaces| { | ||
46 | workspaces | ||
47 | .into_iter() | ||
48 | .filter_map(|it| it.uri.to_file_path().ok()) | ||
49 | .collect::<Vec<_>>() | ||
50 | }) | ||
51 | .filter(|workspaces| !workspaces.is_empty()) | ||
52 | .unwrap_or_else(|| vec![root]); | ||
53 | |||
43 | let opts = params | 54 | let opts = params |
44 | .initialization_options | 55 | .initialization_options |
45 | .and_then(|v| InitializationOptions::deserialize(v).ok()) | 56 | .and_then(|v| InitializationOptions::deserialize(v).ok()) |
46 | .unwrap_or(InitializationOptions::default()); | 57 | .unwrap_or(InitializationOptions::default()); |
47 | 58 | ||
48 | ra_lsp_server::main_loop(root, opts, r, s) | 59 | ra_lsp_server::main_loop(workspace_roots, opts, r, s) |
49 | })?; | 60 | })?; |
50 | log::info!("shutting down IO..."); | 61 | log::info!("shutting down IO..."); |
51 | threads.join()?; | 62 | threads.join()?; |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 82410bee3..07ac4917a 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -48,7 +48,7 @@ enum Task { | |||
48 | const THREADPOOL_SIZE: usize = 8; | 48 | const THREADPOOL_SIZE: usize = 8; |
49 | 49 | ||
50 | pub fn main_loop( | 50 | pub fn main_loop( |
51 | ws_root: PathBuf, | 51 | ws_roots: Vec<PathBuf>, |
52 | options: InitializationOptions, | 52 | options: InitializationOptions, |
53 | msg_receiver: &Receiver<RawMessage>, | 53 | msg_receiver: &Receiver<RawMessage>, |
54 | msg_sender: &Sender<RawMessage>, | 54 | msg_sender: &Sender<RawMessage>, |
@@ -59,23 +59,26 @@ pub fn main_loop( | |||
59 | // FIXME: support dynamic workspace loading. | 59 | // FIXME: support dynamic workspace loading. |
60 | let workspaces = { | 60 | let workspaces = { |
61 | let ws_worker = workspace_loader(); | 61 | let ws_worker = workspace_loader(); |
62 | ws_worker.sender().send(ws_root.clone()).unwrap(); | 62 | let mut loaded_workspaces = Vec::new(); |
63 | match ws_worker.receiver().recv().unwrap() { | 63 | for ws_root in &ws_roots { |
64 | Ok(ws) => vec![ws], | 64 | ws_worker.sender().send(ws_root.clone()).unwrap(); |
65 | Err(e) => { | 65 | match ws_worker.receiver().recv().unwrap() { |
66 | log::error!("loading workspace failed: {}", e); | 66 | Ok(ws) => loaded_workspaces.push(ws), |
67 | 67 | Err(e) => { | |
68 | show_message( | 68 | log::error!("loading workspace failed: {}", e); |
69 | req::MessageType::Error, | 69 | |
70 | format!("rust-analyzer failed to load workspace: {}", e), | 70 | show_message( |
71 | msg_sender, | 71 | req::MessageType::Error, |
72 | ); | 72 | format!("rust-analyzer failed to load workspace: {}", e), |
73 | Vec::new() | 73 | msg_sender, |
74 | ); | ||
75 | } | ||
74 | } | 76 | } |
75 | } | 77 | } |
78 | loaded_workspaces | ||
76 | }; | 79 | }; |
77 | 80 | ||
78 | let mut state = ServerWorldState::new(ws_root.clone(), workspaces); | 81 | let mut state = ServerWorldState::new(ws_roots, workspaces); |
79 | 82 | ||
80 | log::info!("server initialized, serving requests"); | 83 | log::info!("server initialized, serving requests"); |
81 | 84 | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index b96deb061..41d1f759f 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -263,6 +263,7 @@ pub fn handle_runnables( | |||
263 | let line_index = world.analysis().file_line_index(file_id); | 263 | let line_index = world.analysis().file_line_index(file_id); |
264 | let offset = params.position.map(|it| it.conv_with(&line_index)); | 264 | let offset = params.position.map(|it| it.conv_with(&line_index)); |
265 | let mut res = Vec::new(); | 265 | let mut res = Vec::new(); |
266 | let workspace_root = world.workspace_root_for(file_id); | ||
266 | for runnable in world.analysis().runnables(file_id)? { | 267 | for runnable in world.analysis().runnables(file_id)? { |
267 | if let Some(offset) = offset { | 268 | if let Some(offset) = offset { |
268 | if !runnable.range.contains_inclusive(offset) { | 269 | if !runnable.range.contains_inclusive(offset) { |
@@ -287,6 +288,7 @@ pub fn handle_runnables( | |||
287 | m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); | 288 | m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); |
288 | m | 289 | m |
289 | }, | 290 | }, |
291 | cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), | ||
290 | }; | 292 | }; |
291 | res.push(r); | 293 | res.push(r); |
292 | } | 294 | } |
@@ -309,6 +311,7 @@ pub fn handle_runnables( | |||
309 | bin: "cargo".to_string(), | 311 | bin: "cargo".to_string(), |
310 | args: check_args, | 312 | args: check_args, |
311 | env: FxHashMap::default(), | 313 | env: FxHashMap::default(), |
314 | cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), | ||
312 | }); | 315 | }); |
313 | Ok(res) | 316 | Ok(res) |
314 | } | 317 | } |
@@ -627,6 +630,7 @@ pub fn handle_code_lens( | |||
627 | let line_index = world.analysis().file_line_index(file_id); | 630 | let line_index = world.analysis().file_line_index(file_id); |
628 | 631 | ||
629 | let mut lenses: Vec<CodeLens> = Default::default(); | 632 | let mut lenses: Vec<CodeLens> = Default::default(); |
633 | let workspace_root = world.workspace_root_for(file_id); | ||
630 | 634 | ||
631 | // Gather runnables | 635 | // Gather runnables |
632 | for runnable in world.analysis().runnables(file_id)? { | 636 | for runnable in world.analysis().runnables(file_id)? { |
@@ -647,6 +651,7 @@ pub fn handle_code_lens( | |||
647 | bin: "cargo".into(), | 651 | bin: "cargo".into(), |
648 | args, | 652 | args, |
649 | env: Default::default(), | 653 | env: Default::default(), |
654 | cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), | ||
650 | }; | 655 | }; |
651 | 656 | ||
652 | let lens = CodeLens { | 657 | let lens = CodeLens { |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index e0571fd78..4f35ab9b5 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -163,6 +163,7 @@ pub struct Runnable { | |||
163 | pub bin: String, | 163 | pub bin: String, |
164 | pub args: Vec<String>, | 164 | pub args: Vec<String>, |
165 | pub env: FxHashMap<String, String>, | 165 | pub env: FxHashMap<String, String>, |
166 | pub cwd: Option<String>, | ||
166 | } | 167 | } |
167 | 168 | ||
168 | #[derive(Serialize, Debug)] | 169 | #[derive(Serialize, Debug)] |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 4b1d592bb..b2808b817 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | path::PathBuf, | 2 | path::{Path, PathBuf}, |
3 | sync::Arc, | 3 | sync::Arc, |
4 | }; | 4 | }; |
5 | 5 | ||
@@ -24,7 +24,7 @@ use crate::{ | |||
24 | #[derive(Debug)] | 24 | #[derive(Debug)] |
25 | pub struct ServerWorldState { | 25 | pub struct ServerWorldState { |
26 | pub roots_to_scan: usize, | 26 | pub roots_to_scan: usize, |
27 | pub root: PathBuf, | 27 | pub roots: Vec<PathBuf>, |
28 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 28 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
29 | pub analysis_host: AnalysisHost, | 29 | pub analysis_host: AnalysisHost, |
30 | pub vfs: Arc<RwLock<Vfs>>, | 30 | pub vfs: Arc<RwLock<Vfs>>, |
@@ -37,19 +37,20 @@ pub struct ServerWorld { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | impl ServerWorldState { | 39 | impl ServerWorldState { |
40 | pub fn new(root: PathBuf, workspaces: Vec<ProjectWorkspace>) -> ServerWorldState { | 40 | pub fn new(folder_roots: Vec<PathBuf>, workspaces: Vec<ProjectWorkspace>) -> ServerWorldState { |
41 | let mut change = AnalysisChange::new(); | 41 | let mut change = AnalysisChange::new(); |
42 | 42 | ||
43 | let mut roots = Vec::new(); | 43 | let mut roots = Vec::new(); |
44 | roots.push(IncludeRustFiles::member(root.clone())); | 44 | roots.extend(folder_roots.iter().cloned().map(IncludeRustFiles::member)); |
45 | for ws in workspaces.iter() { | 45 | for ws in workspaces.iter() { |
46 | roots.extend(IncludeRustFiles::from_roots(ws.to_roots())); | 46 | roots.extend(IncludeRustFiles::from_roots(ws.to_roots())); |
47 | } | 47 | } |
48 | 48 | ||
49 | let (mut vfs, roots) = Vfs::new(roots); | 49 | let (mut vfs, vfs_roots) = Vfs::new(roots); |
50 | let roots_to_scan = roots.len(); | 50 | let roots_to_scan = vfs_roots.len(); |
51 | for r in roots { | 51 | for r in vfs_roots { |
52 | let is_local = vfs.root2path(r).starts_with(&root); | 52 | let vfs_root_path = vfs.root2path(r); |
53 | let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); | ||
53 | change.add_root(SourceRootId(r.0.into()), is_local); | 54 | change.add_root(SourceRootId(r.0.into()), is_local); |
54 | } | 55 | } |
55 | 56 | ||
@@ -68,7 +69,7 @@ impl ServerWorldState { | |||
68 | analysis_host.apply_change(change); | 69 | analysis_host.apply_change(change); |
69 | ServerWorldState { | 70 | ServerWorldState { |
70 | roots_to_scan, | 71 | roots_to_scan, |
71 | root, | 72 | roots: folder_roots, |
72 | workspaces: Arc::new(workspaces), | 73 | workspaces: Arc::new(workspaces), |
73 | analysis_host, | 74 | analysis_host, |
74 | vfs: Arc::new(RwLock::new(vfs)), | 75 | vfs: Arc::new(RwLock::new(vfs)), |
@@ -90,7 +91,8 @@ impl ServerWorldState { | |||
90 | match c { | 91 | match c { |
91 | VfsChange::AddRoot { root, files } => { | 92 | VfsChange::AddRoot { root, files } => { |
92 | let root_path = self.vfs.read().root2path(root); | 93 | let root_path = self.vfs.read().root2path(root); |
93 | if root_path.starts_with(&self.root) { | 94 | let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); |
95 | if is_local { | ||
94 | self.roots_to_scan -= 1; | 96 | self.roots_to_scan -= 1; |
95 | for (file, path, text) in files { | 97 | for (file, path, text) in files { |
96 | change.add_file( | 98 | change.add_file( |
@@ -193,4 +195,9 @@ impl ServerWorld { | |||
193 | res.push_str(&self.analysis.status()); | 195 | res.push_str(&self.analysis.status()); |
194 | res | 196 | res |
195 | } | 197 | } |
198 | |||
199 | pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> { | ||
200 | let path = self.vfs.read().file2path(VfsFile(file_id.0.into())); | ||
201 | self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) | ||
202 | } | ||
196 | } | 203 | } |