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 | |
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')
-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 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/main.rs | 37 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/support.rs | 68 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 3 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 12 |
9 files changed, 142 insertions, 55 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 | } |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 407719080..6f37a980d 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -14,7 +14,7 @@ use ra_lsp_server::req::{ | |||
14 | use serde_json::json; | 14 | use serde_json::json; |
15 | use tempfile::TempDir; | 15 | use tempfile::TempDir; |
16 | 16 | ||
17 | use crate::support::{project, project_with_tmpdir}; | 17 | use crate::support::{project, Project}; |
18 | 18 | ||
19 | const LOG: &'static str = ""; | 19 | const LOG: &'static str = ""; |
20 | 20 | ||
@@ -62,6 +62,7 @@ fn foo() { | |||
62 | "args": [ "test", "--", "foo", "--nocapture" ], | 62 | "args": [ "test", "--", "foo", "--nocapture" ], |
63 | "bin": "cargo", | 63 | "bin": "cargo", |
64 | "env": { "RUST_BACKTRACE": "short" }, | 64 | "env": { "RUST_BACKTRACE": "short" }, |
65 | "cwd": null, | ||
65 | "label": "test foo", | 66 | "label": "test foo", |
66 | "range": { | 67 | "range": { |
67 | "end": { "character": 1, "line": 2 }, | 68 | "end": { "character": 1, "line": 2 }, |
@@ -75,6 +76,7 @@ fn foo() { | |||
75 | ], | 76 | ], |
76 | "bin": "cargo", | 77 | "bin": "cargo", |
77 | "env": {}, | 78 | "env": {}, |
79 | "cwd": null, | ||
78 | "label": "cargo check --all", | 80 | "label": "cargo check --all", |
79 | "range": { | 81 | "range": { |
80 | "end": { | 82 | "end": { |
@@ -93,25 +95,34 @@ fn foo() { | |||
93 | 95 | ||
94 | #[test] | 96 | #[test] |
95 | fn test_runnables_project() { | 97 | fn test_runnables_project() { |
96 | let server = project( | 98 | let code = r#" |
97 | r#" | 99 | //- foo/Cargo.toml |
98 | //- Cargo.toml | ||
99 | [package] | 100 | [package] |
100 | name = "foo" | 101 | name = "foo" |
101 | version = "0.0.0" | 102 | version = "0.0.0" |
102 | 103 | ||
103 | //- src/lib.rs | 104 | //- foo/src/lib.rs |
104 | pub fn foo() {} | 105 | pub fn foo() {} |
105 | 106 | ||
106 | //- tests/spam.rs | 107 | //- foo/tests/spam.rs |
107 | #[test] | 108 | #[test] |
108 | fn test_eggs() {} | 109 | fn test_eggs() {} |
109 | "#, | 110 | |
110 | ); | 111 | //- bar/Cargo.toml |
112 | [package] | ||
113 | name = "bar" | ||
114 | version = "0.0.0" | ||
115 | |||
116 | //- bar/src/main.rs | ||
117 | fn main() {} | ||
118 | "#; | ||
119 | |||
120 | let server = Project::with_fixture(code).root("foo").root("bar").server(); | ||
121 | |||
111 | server.wait_until_workspace_is_loaded(); | 122 | server.wait_until_workspace_is_loaded(); |
112 | server.request::<Runnables>( | 123 | server.request::<Runnables>( |
113 | RunnablesParams { | 124 | RunnablesParams { |
114 | text_document: server.doc_id("tests/spam.rs"), | 125 | text_document: server.doc_id("foo/tests/spam.rs"), |
115 | position: None, | 126 | position: None, |
116 | }, | 127 | }, |
117 | json!([ | 128 | json!([ |
@@ -123,7 +134,8 @@ fn test_eggs() {} | |||
123 | "range": { | 134 | "range": { |
124 | "end": { "character": 17, "line": 1 }, | 135 | "end": { "character": 17, "line": 1 }, |
125 | "start": { "character": 0, "line": 0 } | 136 | "start": { "character": 0, "line": 0 } |
126 | } | 137 | }, |
138 | "cwd": server.path().join("foo") | ||
127 | }, | 139 | }, |
128 | { | 140 | { |
129 | "args": [ | 141 | "args": [ |
@@ -135,6 +147,7 @@ fn test_eggs() {} | |||
135 | ], | 147 | ], |
136 | "bin": "cargo", | 148 | "bin": "cargo", |
137 | "env": {}, | 149 | "env": {}, |
150 | "cwd": server.path().join("foo"), | ||
138 | "label": "cargo check -p foo", | 151 | "label": "cargo check -p foo", |
139 | "range": { | 152 | "range": { |
140 | "end": { | 153 | "end": { |
@@ -283,7 +296,9 @@ fn main() {{}} | |||
283 | "#, | 296 | "#, |
284 | PROJECT = project.to_string(), | 297 | PROJECT = project.to_string(), |
285 | ); | 298 | ); |
286 | let server = project_with_tmpdir(tmp_dir, &code); | 299 | |
300 | let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server(); | ||
301 | |||
287 | server.wait_until_workspace_is_loaded(); | 302 | server.wait_until_workspace_is_loaded(); |
288 | let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; | 303 | let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; |
289 | server.request::<CodeActionRequest>( | 304 | server.request::<CodeActionRequest>( |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index ab9db3dd4..b07882700 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | cell::{Cell, RefCell}, | 2 | cell::{Cell, RefCell}, |
3 | fs, | 3 | fs, |
4 | path::PathBuf, | 4 | path::{Path, PathBuf}, |
5 | sync::Once, | 5 | sync::Once, |
6 | time::Duration, | 6 | time::Duration, |
7 | }; | 7 | }; |
@@ -26,26 +26,51 @@ use ra_lsp_server::{ | |||
26 | InitializationOptions, | 26 | InitializationOptions, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | pub fn project(fixture: &str) -> Server { | 29 | pub struct Project<'a> { |
30 | let tmp_dir = TempDir::new().unwrap(); | 30 | fixture: &'a str, |
31 | project_with_tmpdir(tmp_dir, fixture) | 31 | tmp_dir: Option<TempDir>, |
32 | roots: Vec<PathBuf>, | ||
32 | } | 33 | } |
33 | 34 | ||
34 | pub fn project_with_tmpdir(tmp_dir: TempDir, fixture: &str) -> Server { | 35 | impl<'a> Project<'a> { |
35 | static INIT: Once = Once::new(); | 36 | pub fn with_fixture(fixture: &str) -> Project { |
36 | INIT.call_once(|| { | 37 | Project { fixture, tmp_dir: None, roots: vec![] } |
37 | let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); | 38 | } |
38 | }); | ||
39 | 39 | ||
40 | let mut paths = vec![]; | 40 | pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> { |
41 | self.tmp_dir = Some(tmp_dir); | ||
42 | self | ||
43 | } | ||
41 | 44 | ||
42 | for entry in parse_fixture(fixture) { | 45 | pub fn root(mut self, path: &str) -> Project<'a> { |
43 | let path = tmp_dir.path().join(entry.meta); | 46 | self.roots.push(path.into()); |
44 | fs::create_dir_all(path.parent().unwrap()).unwrap(); | 47 | self |
45 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); | ||
46 | paths.push((path, entry.text)); | ||
47 | } | 48 | } |
48 | Server::new(tmp_dir, paths) | 49 | |
50 | pub fn server(self) -> Server { | ||
51 | let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); | ||
52 | static INIT: Once = Once::new(); | ||
53 | INIT.call_once(|| { | ||
54 | let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); | ||
55 | }); | ||
56 | |||
57 | let mut paths = vec![]; | ||
58 | |||
59 | for entry in parse_fixture(self.fixture) { | ||
60 | let path = tmp_dir.path().join(entry.meta); | ||
61 | fs::create_dir_all(path.parent().unwrap()).unwrap(); | ||
62 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); | ||
63 | paths.push((path, entry.text)); | ||
64 | } | ||
65 | |||
66 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); | ||
67 | |||
68 | Server::new(tmp_dir, roots, paths) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | pub fn project(fixture: &str) -> Server { | ||
73 | Project::with_fixture(fixture).server() | ||
49 | } | 74 | } |
50 | 75 | ||
51 | pub struct Server { | 76 | pub struct Server { |
@@ -56,14 +81,17 @@ pub struct Server { | |||
56 | } | 81 | } |
57 | 82 | ||
58 | impl Server { | 83 | impl Server { |
59 | fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server { | 84 | fn new(dir: TempDir, roots: Vec<PathBuf>, files: Vec<(PathBuf, String)>) -> Server { |
60 | let path = dir.path().to_path_buf(); | 85 | let path = dir.path().to_path_buf(); |
86 | |||
87 | let roots = if roots.is_empty() { vec![path] } else { roots }; | ||
88 | |||
61 | let worker = Worker::<RawMessage, RawMessage>::spawn( | 89 | let worker = Worker::<RawMessage, RawMessage>::spawn( |
62 | "test server", | 90 | "test server", |
63 | 128, | 91 | 128, |
64 | move |mut msg_receiver, mut msg_sender| { | 92 | move |mut msg_receiver, mut msg_sender| { |
65 | main_loop( | 93 | main_loop( |
66 | path, | 94 | roots, |
67 | InitializationOptions::default(), | 95 | InitializationOptions::default(), |
68 | &mut msg_receiver, | 96 | &mut msg_receiver, |
69 | &mut msg_sender, | 97 | &mut msg_sender, |
@@ -177,6 +205,10 @@ impl Server { | |||
177 | fn send_notification(&self, not: RawNotification) { | 205 | fn send_notification(&self, not: RawNotification) { |
178 | self.worker.as_ref().unwrap().sender().send(RawMessage::Notification(not)).unwrap(); | 206 | self.worker.as_ref().unwrap().sender().send(RawMessage::Notification(not)).unwrap(); |
179 | } | 207 | } |
208 | |||
209 | pub fn path(&self) -> &Path { | ||
210 | self.dir.path() | ||
211 | } | ||
180 | } | 212 | } |
181 | 213 | ||
182 | impl Drop for Server { | 214 | impl Drop for Server { |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 81cb506b7..71976071f 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -19,6 +19,7 @@ use crate::Result; | |||
19 | pub struct CargoWorkspace { | 19 | pub struct CargoWorkspace { |
20 | packages: Arena<Package, PackageData>, | 20 | packages: Arena<Package, PackageData>, |
21 | targets: Arena<Target, TargetData>, | 21 | targets: Arena<Target, TargetData>, |
22 | pub(crate) workspace_root: PathBuf, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 25 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
@@ -165,7 +166,7 @@ impl CargoWorkspace { | |||
165 | } | 166 | } |
166 | } | 167 | } |
167 | 168 | ||
168 | Ok(CargoWorkspace { packages, targets }) | 169 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) |
169 | } | 170 | } |
170 | 171 | ||
171 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a { | 172 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a { |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 3bad4f8d3..63eb7041e 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -255,6 +255,18 @@ impl ProjectWorkspace { | |||
255 | } | 255 | } |
256 | crate_graph | 256 | crate_graph |
257 | } | 257 | } |
258 | |||
259 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { | ||
260 | match self { | ||
261 | ProjectWorkspace::Cargo { cargo, .. } => { | ||
262 | Some(cargo.workspace_root.as_ref()).filter(|root| path.starts_with(root)) | ||
263 | } | ||
264 | ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots | ||
265 | .iter() | ||
266 | .find(|root| path.starts_with(&root.path)) | ||
267 | .map(|root| root.path.as_ref()), | ||
268 | } | ||
269 | } | ||
258 | } | 270 | } |
259 | 271 | ||
260 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { | 272 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { |