diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 8d68dce05c7..3821e1d2460 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -862,6 +862,7 @@ mod tests { focus_range: 18..23, name: "tests", kind: Module, + description: "mod tests", }, kind: TestMod { path: "tests", @@ -883,6 +884,7 @@ mod tests { focus_range: 18..23, name: "tests", kind: Module, + description: "mod tests", }, kind: TestMod { path: "tests", diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index cd4c4bd308a..db1596b45a0 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -2508,6 +2508,7 @@ mod tests$0 { focus_range: 4..9, name: "tests", kind: Module, + description: "mod tests", }, kind: TestMod { path: "tests", diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 03faabadca6..934f1adcc12 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -13,12 +13,10 @@ use ide_db::{ }; use itertools::Itertools; use rustc_hash::FxHashSet; +use stdx::always; use syntax::ast::{self, AstNode, AttrsOwner}; -use crate::{ - display::{ToNav, TryToNav}, - references, FileId, NavigationTarget, -}; +use crate::{display::TryToNav, references, FileId, NavigationTarget}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Runnable { @@ -58,6 +56,8 @@ pub struct RunnableAction { } const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true }; +const TEST_MOD: RunnableAction = + RunnableAction { run_title: "▶\u{fe0e} Run Tests", debugee: true }; const DOCTEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false }; const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true }; @@ -79,7 +79,8 @@ impl Runnable { pub fn action(&self) -> &'static RunnableAction { match &self.kind { - RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST, + RunnableKind::TestMod { .. } => &TEST_MOD, + RunnableKind::Test { .. } => &TEST, RunnableKind::DocTest { .. } => &DOCTEST, RunnableKind::Bench { .. } => &BENCH, RunnableKind::Bin => &BIN, @@ -103,6 +104,16 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); let mut res = Vec::new(); + let mut add_opt = |runnable: Option| { + if let Some(runnable) = runnable.filter(|r| { + always!( + r.nav.file_id == file_id, + "tried adding a runnable pointing to a different file" + ) + }) { + res.push(runnable); + } + }; visit_file_defs(&sema, file_id, &mut |def| match def { Either::Left(def) => { let runnable = match def { @@ -110,17 +121,21 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { hir::ModuleDef::Function(it) => runnable_fn(&sema, it), _ => None, }; - res.extend(runnable.or_else(|| module_def_doctest(&sema, def))) + add_opt(runnable.or_else(|| module_def_doctest(&sema, def))); } Either::Right(impl_) => { - res.extend(runnable_impl(&sema, &impl_)); - res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc { - hir::AssocItem::Function(it) => { - runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) - } - hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), - hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), - })) + add_opt(runnable_impl(&sema, &impl_)); + impl_ + .items(db) + .into_iter() + .map(|assoc| match assoc { + hir::AssocItem::Function(it) => { + runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) + } + hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), + hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), + }) + .for_each(|r| add_opt(r)); } }); res @@ -267,7 +282,7 @@ pub(crate) fn runnable_mod(sema: &Semantics, def: hir::Module) -> let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - let nav = def.to_nav(sema.db); + let nav = NavigationTarget::from_module_to_decl(sema.db, def); Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) } @@ -855,7 +870,7 @@ mod test_mod { fn test_foo1() {} } "#, - &[&TEST, &TEST], + &[&TEST_MOD, &TEST], expect![[r#" [ Runnable { @@ -867,6 +882,7 @@ mod test_mod { focus_range: 5..13, name: "test_mod", kind: Module, + description: "mod test_mod", }, kind: TestMod { path: "test_mod", @@ -925,7 +941,7 @@ mod root_tests { mod nested_tests_4 {} } "#, - &[&TEST, &TEST, &TEST, &TEST, &TEST, &TEST], + &[&TEST_MOD, &TEST_MOD, &TEST_MOD, &TEST, &TEST, &TEST], expect![[r#" [ Runnable { @@ -937,6 +953,7 @@ mod root_tests { focus_range: 26..40, name: "nested_tests_0", kind: Module, + description: "mod nested_tests_0", }, kind: TestMod { path: "root_tests::nested_tests_0", @@ -952,6 +969,7 @@ mod root_tests { focus_range: 55..69, name: "nested_tests_1", kind: Module, + description: "mod nested_tests_1", }, kind: TestMod { path: "root_tests::nested_tests_0::nested_tests_1", @@ -967,6 +985,7 @@ mod root_tests { focus_range: 206..220, name: "nested_tests_2", kind: Module, + description: "mod nested_tests_2", }, kind: TestMod { path: "root_tests::nested_tests_0::nested_tests_2", @@ -1210,7 +1229,7 @@ mod tests { gen!(); } "#, - &[&TEST, &TEST], + &[&TEST_MOD, &TEST], expect![[r#" [ Runnable { @@ -1222,6 +1241,7 @@ mod tests { focus_range: 94..99, name: "tests", kind: Module, + description: "mod tests", }, kind: TestMod { path: "tests", @@ -1273,6 +1293,104 @@ mod tests { ); } + #[test] + fn outline_submodule1() { + check( + r#" +//- /lib.rs +$0 +mod m; +//- /m.rs +#[test] +fn t0() {} +#[test] +fn t1() {} +"#, + &[&TEST_MOD], + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..7, + focus_range: 5..6, + name: "m", + kind: Module, + description: "mod m", + }, + kind: TestMod { + path: "m", + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn outline_submodule2() { + check( + r#" +//- /lib.rs +mod m; +//- /m.rs +$0 +#[test] +fn t0() {} +#[test] +fn t1() {} +"#, + &[&TEST, &TEST], + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 1..19, + focus_range: 12..14, + name: "t0", + kind: Function, + }, + kind: Test { + test_id: Path( + "m::t0", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 20..38, + focus_range: 31..33, + name: "t1", + kind: Function, + }, + kind: Test { + test_id: Path( + "m::t1", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + ] + "#]], + ); + } + #[test] fn find_no_tests() { check_tests(