diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 38637c19c2..fa8a9d92c9 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -9,6 +9,7 @@ use ra_syntax::{ }; use crate::FileId; +use ast::DocCommentsOwner; use std::fmt::Display; #[derive(Debug)] @@ -37,6 +38,7 @@ pub enum RunnableKind { Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, Bench { test_id: TestId }, + DocTest { test_id: TestId }, Bin, } @@ -81,6 +83,8 @@ fn runnable_fn(sema: &Semantics, fn_def: ast::FnDef) -> Option bool { .any(|attribute_text| attribute_text.contains("test")) } +fn has_doc_test(fn_def: &ast::FnDef) -> bool { + fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) +} + fn runnable_mod(sema: &Semantics, module: ast::Module) -> Option { let has_test_function = module .item_list()? @@ -194,6 +202,41 @@ mod tests { ); } + #[test] + fn test_runnables_doc_test() { + let (analysis, pos) = analysis_and_position( + r#" + //- /lib.rs + <|> //empty + fn main() {} + + /// ``` + /// let x = 5; + /// ``` + fn foo() {} + "#, + ); + let runnables = analysis.runnables(pos.file_id).unwrap(); + assert_debug_snapshot!(&runnables, + @r###" + [ + Runnable { + range: 1..21, + kind: Bin, + }, + Runnable { + range: 22..64, + kind: DocTest { + test_id: Path( + "foo", + ), + }, + }, + ] + "### + ); + } + #[test] fn test_runnables_module() { let (analysis, pos) = analysis_and_position( diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 10c25666a0..5e5a17943d 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -27,7 +27,7 @@ impl CargoTargetSpec { RunnableKind::Test { test_id, attr } => { args.push("test".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { @@ -35,13 +35,13 @@ impl CargoTargetSpec { } extra_args.push("--nocapture".to_string()); if attr.ignore { - extra_args.push("--ignored".to_string()) + extra_args.push("--ignored".to_string()); } } RunnableKind::TestMod { path } => { args.push("test".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(path.to_string()); extra_args.push("--nocapture".to_string()); @@ -49,7 +49,7 @@ impl CargoTargetSpec { RunnableKind::Bench { test_id } => { args.push("bench".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { @@ -57,10 +57,19 @@ impl CargoTargetSpec { } extra_args.push("--nocapture".to_string()); } + RunnableKind::DocTest { test_id } => { + args.push("test".to_string()); + args.push("--doc".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut args, kind); + } + extra_args.push(test_id.to_string()); + extra_args.push("--nocapture".to_string()); + } RunnableKind::Bin => { args.push("run".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } } } @@ -91,9 +100,14 @@ impl CargoTargetSpec { Ok(res) } - pub(crate) fn push_to(self, buf: &mut Vec) { + pub(crate) fn push_to(self, buf: &mut Vec, kind: &RunnableKind) { buf.push("--package".to_string()); buf.push(self.package); + + // Can't mix --doc with other target flags + if let RunnableKind::DocTest { .. } = kind { + return; + } match self.target_kind { TargetKind::Bin => { buf.push("--bin".to_string()); diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index c7a96ba932..15e8305f88 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -835,6 +835,7 @@ pub fn handle_code_lens( for runnable in world.analysis().runnables(file_id)? { let title = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", + RunnableKind::DocTest { .. } => "▶️\u{fe0e}Run Doctest", RunnableKind::Bench { .. } => "Run Bench", RunnableKind::Bin => "Run", } @@ -1018,6 +1019,7 @@ fn to_lsp_runnable( RunnableKind::Test { test_id, .. } => format!("test {}", test_id), RunnableKind::TestMod { path } => format!("test-mod {}", path), RunnableKind::Bench { test_id } => format!("bench {}", test_id), + RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), RunnableKind::Bin => "run binary".to_string(), }; Ok(req::Runnable {