diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs index f1d72da151d..b80ce1ab5c8 100644 --- a/crates/libanalysis/src/imp.rs +++ b/crates/libanalysis/src/imp.rs @@ -151,7 +151,7 @@ pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> { .collect() } - pub fn crate_root(&self, id: FileId) -> Vec { + pub fn crate_for(&self, id: FileId) -> Vec { let module_map = &self.data.module_map; let crate_graph = &self.data.crate_graph; let mut res = Vec::new(); @@ -177,7 +177,9 @@ pub fn crate_root(&self, id: FileId) -> Vec { } res } - + pub fn crate_root(&self, crate_id: CrateId) -> FileId { + self.data.crate_graph.crate_roots[&crate_id] + } pub fn approximately_resolve_symbol( &self, id: FileId, diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 041abbb8958..ba290f1e019 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -182,8 +182,11 @@ pub fn approximately_resolve_symbol(&self, file_id: FileId, offset: TextUnit, to pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> { self.imp.parent_module(file_id) } - pub fn crate_root(&self, file_id: FileId) -> Vec { - self.imp.crate_root(file_id) + pub fn crate_for(&self, file_id: FileId) -> Vec { + self.imp.crate_for(file_id) + } + pub fn crate_root(&self, crate_id: CrateId) -> FileId { + self.imp.crate_root(crate_id) } pub fn runnables(&self, file_id: FileId) -> Vec { let file = self.file_syntax(file_id); diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs index c098c8e8c01..4fae7c31376 100644 --- a/crates/libanalysis/tests/tests.rs +++ b/crates/libanalysis/tests/tests.rs @@ -126,7 +126,7 @@ fn test_resolve_crate_root() { (1, "/lib.rs"), (2, "/foo.rs"), ])); - assert!(snap.crate_root(FileId(2)).is_empty()); + assert!(snap.crate_for(FileId(2)).is_empty()); let crate_graph = CrateGraph { crate_roots: { @@ -142,7 +142,7 @@ fn test_resolve_crate_root() { (2, "/foo.rs"), ])); assert_eq!( - snap.crate_root(FileId(2)), + snap.crate_for(FileId(2)), vec![CrateId(1)], ); } diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index ab8b6f79958..93d8bd9fe89 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs @@ -16,6 +16,7 @@ req::{self, Decoration}, Result, conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location}, server_world::ServerWorld, + project_model::TargetKind, }; pub fn handle_syntax_tree( @@ -233,6 +234,8 @@ pub fn handle_runnables( } } + let args = runnable_args(&world, file_id, &runnable.kind); + let r = req::Runnable { range: runnable.range.conv_with(&line_index), label: match &runnable.kind { @@ -242,17 +245,7 @@ pub fn handle_runnables( "run binary".to_string(), }, bin: "cargo".to_string(), - args: match runnable.kind { - RunnableKind::Test { name } => { - vec![ - "test".to_string(), - "--".to_string(), - name, - "--nocapture".to_string(), - ] - } - RunnableKind::Bin => vec!["run".to_string()] - }, + args, env: { let mut m = HashMap::new(); m.insert( @@ -265,6 +258,67 @@ pub fn handle_runnables( res.push(r); } return Ok(res); + + fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Vec { + let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id).first() { + let file_id = world.analysis().crate_root(crate_id); + let path = world.path_map.get_path(file_id); + world.workspaces.iter() + .filter_map(|ws| { + let tgt = ws.target_by_root(path)?; + Some((tgt.package(ws).name(ws).clone(), tgt.name(ws).clone(), tgt.kind(ws))) + }) + .next() + } else { + None + }; + let mut res = Vec::new(); + match kind { + RunnableKind::Test { name } => { + res.push("test".to_string()); + if let Some((pkg_name, tgt_name, tgt_kind)) = spec { + spec_args(pkg_name, tgt_name, tgt_kind, &mut res); + } + res.push("--".to_string()); + res.push(name.to_string()); + res.push("--nocapture".to_string()); + } + RunnableKind::Bin => { + res.push("run".to_string()); + if let Some((pkg_name, tgt_name, tgt_kind)) = spec { + spec_args(pkg_name, tgt_name, tgt_kind, &mut res); + } + } + } + res + } + + fn spec_args(pkg_name: &str, tgt_name: &str, tgt_kind: TargetKind, buf: &mut Vec) { + buf.push("--package".to_string()); + buf.push(pkg_name.to_string()); + match tgt_kind { + TargetKind::Bin => { + buf.push("--bin".to_string()); + buf.push(tgt_name.to_string()); + } + TargetKind::Test => { + buf.push("--test".to_string()); + buf.push(tgt_name.to_string()); + } + TargetKind::Bench => { + buf.push("--bench".to_string()); + buf.push(tgt_name.to_string()); + } + TargetKind::Example => { + buf.push("--example".to_string()); + buf.push(tgt_name.to_string()); + } + TargetKind::Lib => { + buf.push("--lib".to_string()); + } + TargetKind::Other => (), + } + } } pub fn handle_decorations( diff --git a/crates/server/src/project_model.rs b/crates/server/src/project_model.rs index 1c5954dadb2..12233f2580a 100644 --- a/crates/server/src/project_model.rs +++ b/crates/server/src/project_model.rs @@ -56,7 +56,7 @@ pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + } impl Target { - pub fn pkg(self, ws: &CargoWorkspace) -> Package { + pub fn package(self, ws: &CargoWorkspace) -> Package { ws.tgt(self).pkg } pub fn name(self, ws: &CargoWorkspace) -> &str { @@ -114,7 +114,7 @@ pub fn packages<'a>(&'a self) -> impl Iterator + 'a { pub fn ws_members<'a>(&'a self) -> impl Iterator + 'a { self.ws_members.iter().cloned() } - pub fn target_by_roo(&self, root: &Path) -> Option { + pub fn target_by_root(&self, root: &Path) -> Option { self.packages() .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) .next() diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 4d5c504289b..f78b56cf8dd 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs @@ -6,7 +6,7 @@ }; use languageserver_types::Url; -use libanalysis::{FileId, AnalysisHost, Analysis}; +use libanalysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId}; use { Result, @@ -95,7 +95,22 @@ pub fn remove_mem_file(&mut self, path: &Path) -> Result { Ok(file_id) } pub fn set_workspaces(&mut self, ws: Vec) { + let mut crate_roots = HashMap::new(); + ws.iter() + .flat_map(|ws| { + ws.packages() + .flat_map(move |pkg| pkg.targets(ws)) + .map(move |tgt| tgt.root(ws)) + }) + .for_each(|root| { + if let Some(file_id) = self.path_map.get_id(root) { + let crate_id = CrateId(crate_roots.len() as u32); + crate_roots.insert(crate_id, file_id); + } + }); + let crate_graph = CrateGraph { crate_roots }; self.workspaces = Arc::new(ws); + self.analysis_host.set_crate_graph(crate_graph); } pub fn snapshot(&self) -> ServerWorld { ServerWorld { diff --git a/crates/server/tests/heavy_tests/main.rs b/crates/server/tests/heavy_tests/main.rs index 9c0196f22fd..0dc6074b2bb 100644 --- a/crates/server/tests/heavy_tests/main.rs +++ b/crates/server/tests/heavy_tests/main.rs @@ -14,10 +14,10 @@ use support::project; -const LOG: &'static str = "WARN"; +const LOG: &'static str = ""; #[test] -fn test_runnables() { +fn test_runnables_no_project() { let server = project(r" //- lib.rs #[test] @@ -44,6 +44,42 @@ fn foo() { ); } +#[test] +fn test_runnables_project() { + let server = project(r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +pub fn foo() {} + +//- tests/spam.rs +#[test] +fn test_eggs() {} +"#); + server.wait_for_notification::(); + server.request::( + RunnablesParams { + text_document: server.doc_id("tests/spam.rs"), + position: None, + }, + r#"[ + { + "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--nocapture" ], + "bin": "cargo", + "env": { "RUST_BACKTRACE": "short" }, + "label": "test test_eggs", + "range": { + "end": { "character": 17, "line": 1 }, + "start": { "character": 0, "line": 0 } + } + } + ]"# + ); +} + #[test] fn test_project_model() { let server = project(r#" diff --git a/crates/server/tests/heavy_tests/support.rs b/crates/server/tests/heavy_tests/support.rs index 76cbd56ea3f..38a9e6c7658 100644 --- a/crates/server/tests/heavy_tests/support.rs +++ b/crates/server/tests/heavy_tests/support.rs @@ -134,7 +134,7 @@ pub fn notification( { let expected = expected.replace("$PROJECT_ROOT$", &self.dir.path().display().to_string()); let expected: Value = from_str(&expected).unwrap(); - let actual = self.wait_for_notification(N::METHOD); + let actual = self.wait_for_notification::(); assert_eq!( expected, actual, "Expected:\n{}\n\ @@ -150,6 +150,11 @@ fn send_request(&self, id: u64, params: R::Params) -> Value R::Params: Serialize, { let r = RawRequest::new::(id, ¶ms); + self.send_request_(r) + } + fn send_request_(&self, r: RawRequest) -> Value + { + let id = r.id; self.sender.as_ref() .unwrap() .send(RawMessage::Request(r)); @@ -168,7 +173,10 @@ fn send_request(&self, id: u64, params: R::Params) -> Value } panic!("no response"); } - fn wait_for_notification(&self, method: &str) -> Value { + pub fn wait_for_notification(&self) -> Value { + self.wait_for_notification_(N::METHOD) + } + fn wait_for_notification_(&self, method: &str) -> Value { let f = |msg: &RawMessage| match msg { RawMessage::Notification(n) if n.method == method => { Some(n.params.clone()) @@ -215,7 +223,6 @@ fn drop(&mut self) { drop(msg); } } - eprintln!("joining server"); self.server.take() .unwrap() .join().unwrap().unwrap();