From 3507bcb97aaaafba10d55c101bd295f3ab4fed4f Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Thu, 11 Apr 2019 08:08:19 +0200 Subject: Adds support for multiple editor workspaces on initialization This is a quick, partial fix for #1104 --- crates/ra_lsp_server/src/main.rs | 13 +++++++++- crates/ra_lsp_server/src/main_loop.rs | 31 +++++++++++++---------- crates/ra_lsp_server/src/server_world.rs | 20 ++++++++------- crates/ra_lsp_server/tests/heavy_tests/support.rs | 2 +- 4 files changed, 41 insertions(+), 25 deletions(-) (limited to 'crates') 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<()> { run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); + let workspace_roots = params + .workspace_folders + .map(|workspaces| { + workspaces + .into_iter() + .filter_map(|it| it.uri.to_file_path().ok()) + .collect::>() + }) + .filter(|workspaces| !workspaces.is_empty()) + .unwrap_or_else(|| vec![root]); + let opts = params .initialization_options .and_then(|v| InitializationOptions::deserialize(v).ok()) .unwrap_or(InitializationOptions::default()); - ra_lsp_server::main_loop(root, opts, r, s) + ra_lsp_server::main_loop(workspace_roots, opts, r, s) })?; log::info!("shutting down IO..."); 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 { const THREADPOOL_SIZE: usize = 8; pub fn main_loop( - ws_root: PathBuf, + ws_roots: Vec, options: InitializationOptions, msg_receiver: &Receiver, msg_sender: &Sender, @@ -59,23 +59,26 @@ pub fn main_loop( // FIXME: support dynamic workspace loading. let workspaces = { let ws_worker = workspace_loader(); - ws_worker.sender().send(ws_root.clone()).unwrap(); - match ws_worker.receiver().recv().unwrap() { - Ok(ws) => vec![ws], - Err(e) => { - log::error!("loading workspace failed: {}", e); - - show_message( - req::MessageType::Error, - format!("rust-analyzer failed to load workspace: {}", e), - msg_sender, - ); - Vec::new() + let mut loaded_workspaces = Vec::new(); + for ws_root in &ws_roots { + ws_worker.sender().send(ws_root.clone()).unwrap(); + match ws_worker.receiver().recv().unwrap() { + Ok(ws) => loaded_workspaces.push(ws), + Err(e) => { + log::error!("loading workspace failed: {}", e); + + show_message( + req::MessageType::Error, + format!("rust-analyzer failed to load workspace: {}", e), + msg_sender, + ); + } } } + loaded_workspaces }; - let mut state = ServerWorldState::new(ws_root.clone(), workspaces); + let mut state = ServerWorldState::new(ws_roots, workspaces); log::info!("server initialized, serving requests"); diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 4b1d592bb..45ad8e24e 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs @@ -24,7 +24,7 @@ use crate::{ #[derive(Debug)] pub struct ServerWorldState { pub roots_to_scan: usize, - pub root: PathBuf, + pub roots: Vec, pub workspaces: Arc>, pub analysis_host: AnalysisHost, pub vfs: Arc>, @@ -37,19 +37,20 @@ pub struct ServerWorld { } impl ServerWorldState { - pub fn new(root: PathBuf, workspaces: Vec) -> ServerWorldState { + pub fn new(folder_roots: Vec, workspaces: Vec) -> ServerWorldState { let mut change = AnalysisChange::new(); let mut roots = Vec::new(); - roots.push(IncludeRustFiles::member(root.clone())); + roots.extend(folder_roots.iter().cloned().map(IncludeRustFiles::member)); for ws in workspaces.iter() { roots.extend(IncludeRustFiles::from_roots(ws.to_roots())); } - let (mut vfs, roots) = Vfs::new(roots); - let roots_to_scan = roots.len(); - for r in roots { - let is_local = vfs.root2path(r).starts_with(&root); + let (mut vfs, vfs_roots) = Vfs::new(roots); + let roots_to_scan = vfs_roots.len(); + for r in vfs_roots { + let vfs_root_path = vfs.root2path(r); + let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); change.add_root(SourceRootId(r.0.into()), is_local); } @@ -68,7 +69,7 @@ impl ServerWorldState { analysis_host.apply_change(change); ServerWorldState { roots_to_scan, - root, + roots: folder_roots, workspaces: Arc::new(workspaces), analysis_host, vfs: Arc::new(RwLock::new(vfs)), @@ -90,7 +91,8 @@ impl ServerWorldState { match c { VfsChange::AddRoot { root, files } => { let root_path = self.vfs.read().root2path(root); - if root_path.starts_with(&self.root) { + let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); + if is_local { self.roots_to_scan -= 1; for (file, path, text) in files { change.add_file( diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index ab9db3dd4..4ea6760a1 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -63,7 +63,7 @@ impl Server { 128, move |mut msg_receiver, mut msg_sender| { main_loop( - path, + vec![path], InitializationOptions::default(), &mut msg_receiver, &mut msg_sender, -- cgit v1.2.3 From 7c7cfc5f04c51ed1e31b6a3091efc3941b3383c2 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Sat, 13 Apr 2019 19:45:21 +0200 Subject: Sends cwd info for runnables and code lenses --- crates/ra_lsp_server/src/main_loop/handlers.rs | 5 +++++ crates/ra_lsp_server/src/req.rs | 1 + crates/ra_lsp_server/src/server_world.rs | 7 ++++++- crates/ra_lsp_server/tests/heavy_tests/main.rs | 6 +++++- crates/ra_lsp_server/tests/heavy_tests/support.rs | 6 +++++- crates/ra_project_model/src/cargo_workspace.rs | 3 ++- crates/ra_project_model/src/lib.rs | 12 ++++++++++++ 7 files changed, 36 insertions(+), 4 deletions(-) (limited to 'crates') 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( let line_index = world.analysis().file_line_index(file_id); let offset = params.position.map(|it| it.conv_with(&line_index)); let mut res = Vec::new(); + let workspace_root = world.workspace_root_for(file_id); for runnable in world.analysis().runnables(file_id)? { if let Some(offset) = offset { if !runnable.range.contains_inclusive(offset) { @@ -287,6 +288,7 @@ pub fn handle_runnables( m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); m }, + cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), }; res.push(r); } @@ -309,6 +311,7 @@ pub fn handle_runnables( bin: "cargo".to_string(), args: check_args, env: FxHashMap::default(), + cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), }); Ok(res) } @@ -627,6 +630,7 @@ pub fn handle_code_lens( let line_index = world.analysis().file_line_index(file_id); let mut lenses: Vec = Default::default(); + let workspace_root = world.workspace_root_for(file_id); // Gather runnables for runnable in world.analysis().runnables(file_id)? { @@ -647,6 +651,7 @@ pub fn handle_code_lens( bin: "cargo".into(), args, env: Default::default(), + cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), }; 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 { pub bin: String, pub args: Vec, pub env: FxHashMap, + pub cwd: Option, } #[derive(Serialize, Debug)] diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 45ad8e24e..b2808b817 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs @@ -1,5 +1,5 @@ use std::{ - path::PathBuf, + path::{Path, PathBuf}, sync::Arc, }; @@ -195,4 +195,9 @@ impl ServerWorld { res.push_str(&self.analysis.status()); res } + + pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> { + let path = self.vfs.read().file2path(VfsFile(file_id.0.into())); + self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) + } } diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 407719080..e9ce002de 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -62,6 +62,7 @@ fn foo() { "args": [ "test", "--", "foo", "--nocapture" ], "bin": "cargo", "env": { "RUST_BACKTRACE": "short" }, + "cwd": null, "label": "test foo", "range": { "end": { "character": 1, "line": 2 }, @@ -75,6 +76,7 @@ fn foo() { ], "bin": "cargo", "env": {}, + "cwd": null, "label": "cargo check --all", "range": { "end": { @@ -123,7 +125,8 @@ fn test_eggs() {} "range": { "end": { "character": 17, "line": 1 }, "start": { "character": 0, "line": 0 } - } + }, + "cwd": server.path() }, { "args": [ @@ -135,6 +138,7 @@ fn test_eggs() {} ], "bin": "cargo", "env": {}, + "cwd": server.path(), "label": "cargo check -p foo", "range": { "end": { diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 4ea6760a1..9e115fb7f 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 @@ use std::{ cell::{Cell, RefCell}, fs, - path::PathBuf, + path::{Path, PathBuf}, sync::Once, time::Duration, }; @@ -177,6 +177,10 @@ impl Server { fn send_notification(&self, not: RawNotification) { self.worker.as_ref().unwrap().sender().send(RawMessage::Notification(not)).unwrap(); } + + pub fn path(&self) -> &Path { + self.dir.path() + } } 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; pub struct CargoWorkspace { packages: Arena, targets: Arena, + pub(crate) workspace_root: PathBuf, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -165,7 +166,7 @@ impl CargoWorkspace { } } - Ok(CargoWorkspace { packages, targets }) + Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) } pub fn packages<'a>(&'a self) -> impl Iterator + '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 { } crate_graph } + + pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { + match self { + ProjectWorkspace::Cargo { cargo, .. } => { + Some(cargo.workspace_root.as_ref()).filter(|root| path.starts_with(root)) + } + ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots + .iter() + .find(|root| path.starts_with(&root.path)) + .map(|root| root.path.as_ref()), + } + } } fn find_rust_project_json(path: &Path) -> Option { -- cgit v1.2.3 From c2dfc8a229c0a18dff08d5ce7e6836c91648eee5 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Sat, 13 Apr 2019 20:33:49 +0200 Subject: Modifies runnables test to use multi-workspace root --- crates/ra_lsp_server/tests/heavy_tests/main.rs | 35 ++++++++----- crates/ra_lsp_server/tests/heavy_tests/support.rs | 62 ++++++++++++++++------- 2 files changed, 68 insertions(+), 29 deletions(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index e9ce002de..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::{ use serde_json::json; use tempfile::TempDir; -use crate::support::{project, project_with_tmpdir}; +use crate::support::{project, Project}; const LOG: &'static str = ""; @@ -95,25 +95,34 @@ fn foo() { #[test] fn test_runnables_project() { - let server = project( - r#" -//- Cargo.toml + let code = r#" +//- foo/Cargo.toml [package] name = "foo" version = "0.0.0" -//- src/lib.rs +//- foo/src/lib.rs pub fn foo() {} -//- tests/spam.rs +//- foo/tests/spam.rs #[test] fn test_eggs() {} -"#, - ); + +//- bar/Cargo.toml +[package] +name = "bar" +version = "0.0.0" + +//- bar/src/main.rs +fn main() {} +"#; + + let server = Project::with_fixture(code).root("foo").root("bar").server(); + server.wait_until_workspace_is_loaded(); server.request::( RunnablesParams { - text_document: server.doc_id("tests/spam.rs"), + text_document: server.doc_id("foo/tests/spam.rs"), position: None, }, json!([ @@ -126,7 +135,7 @@ fn test_eggs() {} "end": { "character": 17, "line": 1 }, "start": { "character": 0, "line": 0 } }, - "cwd": server.path() + "cwd": server.path().join("foo") }, { "args": [ @@ -138,7 +147,7 @@ fn test_eggs() {} ], "bin": "cargo", "env": {}, - "cwd": server.path(), + "cwd": server.path().join("foo"), "label": "cargo check -p foo", "range": { "end": { @@ -287,7 +296,9 @@ fn main() {{}} "#, PROJECT = project.to_string(), ); - let server = project_with_tmpdir(tmp_dir, &code); + + let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server(); + server.wait_until_workspace_is_loaded(); let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; server.request::( diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 9e115fb7f..b07882700 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -26,26 +26,51 @@ use ra_lsp_server::{ InitializationOptions, }; -pub fn project(fixture: &str) -> Server { - let tmp_dir = TempDir::new().unwrap(); - project_with_tmpdir(tmp_dir, fixture) +pub struct Project<'a> { + fixture: &'a str, + tmp_dir: Option, + roots: Vec, } -pub fn project_with_tmpdir(tmp_dir: TempDir, fixture: &str) -> Server { - static INIT: Once = Once::new(); - INIT.call_once(|| { - let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); - }); +impl<'a> Project<'a> { + pub fn with_fixture(fixture: &str) -> Project { + Project { fixture, tmp_dir: None, roots: vec![] } + } - let mut paths = vec![]; + pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> { + self.tmp_dir = Some(tmp_dir); + self + } - for entry in parse_fixture(fixture) { - let path = tmp_dir.path().join(entry.meta); - fs::create_dir_all(path.parent().unwrap()).unwrap(); - fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); - paths.push((path, entry.text)); + pub fn root(mut self, path: &str) -> Project<'a> { + self.roots.push(path.into()); + self + } + + pub fn server(self) -> Server { + let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); + static INIT: Once = Once::new(); + INIT.call_once(|| { + let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); + }); + + let mut paths = vec![]; + + for entry in parse_fixture(self.fixture) { + let path = tmp_dir.path().join(entry.meta); + fs::create_dir_all(path.parent().unwrap()).unwrap(); + fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); + paths.push((path, entry.text)); + } + + let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); + + Server::new(tmp_dir, roots, paths) } - Server::new(tmp_dir, paths) +} + +pub fn project(fixture: &str) -> Server { + Project::with_fixture(fixture).server() } pub struct Server { @@ -56,14 +81,17 @@ pub struct Server { } impl Server { - fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server { + fn new(dir: TempDir, roots: Vec, files: Vec<(PathBuf, String)>) -> Server { let path = dir.path().to_path_buf(); + + let roots = if roots.is_empty() { vec![path] } else { roots }; + let worker = Worker::::spawn( "test server", 128, move |mut msg_receiver, mut msg_sender| { main_loop( - vec![path], + roots, InitializationOptions::default(), &mut msg_receiver, &mut msg_sender, -- cgit v1.2.3