aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/runnables.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/runnables.rs')
-rw-r--r--crates/ra_ide/src/runnables.rs111
1 files changed, 87 insertions, 24 deletions
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index b6b0c70f9..863b85591 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::InFile; 3use hir::{InFile, SourceBinder};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_db::SourceDatabase; 5use ra_db::SourceDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
@@ -10,6 +10,7 @@ use ra_syntax::{
10}; 10};
11 11
12use crate::FileId; 12use crate::FileId;
13use std::fmt::Display;
13 14
14#[derive(Debug)] 15#[derive(Debug)]
15pub struct Runnable { 16pub struct Runnable {
@@ -18,38 +19,86 @@ pub struct Runnable {
18} 19}
19 20
20#[derive(Debug)] 21#[derive(Debug)]
22pub enum TestId {
23 Name(String),
24 Path(String),
25}
26
27impl Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
29 match self {
30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path),
32 }
33 }
34}
35
36#[derive(Debug)]
21pub enum RunnableKind { 37pub enum RunnableKind {
22 Test { name: String }, 38 Test { test_id: TestId },
23 TestMod { path: String }, 39 TestMod { path: String },
24 Bench { name: String }, 40 Bench { test_id: TestId },
25 Bin, 41 Bin,
26} 42}
27 43
28pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 44pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
29 let parse = db.parse(file_id); 45 let parse = db.parse(file_id);
30 parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect() 46 let mut sb = SourceBinder::new(db);
47 parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect()
31} 48}
32 49
33fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> { 50fn runnable(
51 db: &RootDatabase,
52 source_binder: &mut SourceBinder<RootDatabase>,
53 file_id: FileId,
54 item: SyntaxNode,
55) -> Option<Runnable> {
34 match_ast! { 56 match_ast! {
35 match item { 57 match item {
36 ast::FnDef(it) => { runnable_fn(it) }, 58 ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) },
37 ast::Module(it) => { runnable_mod(db, file_id, it) }, 59 ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) },
38 _ => { None }, 60 _ => { None },
39 } 61 }
40 } 62 }
41} 63}
42 64
43fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { 65fn runnable_fn(
44 let name = fn_def.name()?.text().clone(); 66 db: &RootDatabase,
45 let kind = if name == "main" { 67 source_binder: &mut SourceBinder<RootDatabase>,
68 file_id: FileId,
69 fn_def: ast::FnDef,
70) -> Option<Runnable> {
71 let name_string = fn_def.name()?.text().to_string();
72
73 let kind = if name_string == "main" {
46 RunnableKind::Bin 74 RunnableKind::Bin
47 } else if has_test_related_attribute(&fn_def) {
48 RunnableKind::Test { name: name.to_string() }
49 } else if fn_def.has_atom_attr("bench") {
50 RunnableKind::Bench { name: name.to_string() }
51 } else { 75 } else {
52 return None; 76 let test_id = if let Some(module) = fn_def
77 .syntax()
78 .ancestors()
79 .find_map(ast::Module::cast)
80 .and_then(|module| source_binder.to_def(InFile::new(file_id.into(), module)))
81 {
82 let path = module
83 .path_to_root(db)
84 .into_iter()
85 .rev()
86 .filter_map(|it| it.name(db))
87 .map(|name| name.to_string())
88 .chain(std::iter::once(name_string))
89 .join("::");
90 TestId::Path(path)
91 } else {
92 TestId::Name(name_string)
93 };
94
95 if has_test_related_attribute(&fn_def) {
96 RunnableKind::Test { test_id }
97 } else if fn_def.has_atom_attr("bench") {
98 RunnableKind::Bench { test_id }
99 } else {
100 return None;
101 }
53 }; 102 };
54 Some(Runnable { range: fn_def.syntax().text_range(), kind }) 103 Some(Runnable { range: fn_def.syntax().text_range(), kind })
55} 104}
@@ -68,7 +117,12 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
68 .any(|attribute_text| attribute_text.contains("test")) 117 .any(|attribute_text| attribute_text.contains("test"))
69} 118}
70 119
71fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { 120fn runnable_mod(
121 db: &RootDatabase,
122 source_binder: &mut SourceBinder<RootDatabase>,
123 file_id: FileId,
124 module: ast::Module,
125) -> Option<Runnable> {
72 let has_test_function = module 126 let has_test_function = module
73 .item_list()? 127 .item_list()?
74 .items() 128 .items()
@@ -76,13 +130,12 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
76 ast::ModuleItem::FnDef(it) => Some(it), 130 ast::ModuleItem::FnDef(it) => Some(it),
77 _ => None, 131 _ => None,
78 }) 132 })
79 .any(|f| f.has_atom_attr("test")); 133 .any(|f| has_test_related_attribute(&f));
80 if !has_test_function { 134 if !has_test_function {
81 return None; 135 return None;
82 } 136 }
83 let range = module.syntax().text_range(); 137 let range = module.syntax().text_range();
84 let mut sb = hir::SourceBinder::new(db); 138 let module = source_binder.to_def(InFile::new(file_id.into(), module))?;
85 let module = sb.to_def(InFile::new(file_id.into(), module))?;
86 139
87 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); 140 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
88 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 141 Some(Runnable { range, kind: RunnableKind::TestMod { path } })
@@ -121,13 +174,17 @@ mod tests {
121 Runnable { 174 Runnable {
122 range: [22; 46), 175 range: [22; 46),
123 kind: Test { 176 kind: Test {
124 name: "test_foo", 177 test_id: Name(
178 "test_foo",
179 ),
125 }, 180 },
126 }, 181 },
127 Runnable { 182 Runnable {
128 range: [47; 81), 183 range: [47; 81),
129 kind: Test { 184 kind: Test {
130 name: "test_foo", 185 test_id: Name(
186 "test_foo",
187 ),
131 }, 188 },
132 }, 189 },
133 ] 190 ]
@@ -160,7 +217,9 @@ mod tests {
160 Runnable { 217 Runnable {
161 range: [28; 57), 218 range: [28; 57),
162 kind: Test { 219 kind: Test {
163 name: "test_foo1", 220 test_id: Path(
221 "test_mod::test_foo1",
222 ),
164 }, 223 },
165 }, 224 },
166 ] 225 ]
@@ -195,7 +254,9 @@ mod tests {
195 Runnable { 254 Runnable {
196 range: [46; 79), 255 range: [46; 79),
197 kind: Test { 256 kind: Test {
198 name: "test_foo1", 257 test_id: Path(
258 "foo::test_mod::test_foo1",
259 ),
199 }, 260 },
200 }, 261 },
201 ] 262 ]
@@ -232,7 +293,9 @@ mod tests {
232 Runnable { 293 Runnable {
233 range: [68; 105), 294 range: [68; 105),
234 kind: Test { 295 kind: Test {
235 name: "test_foo1", 296 test_id: Path(
297 "foo::bar::test_mod::test_foo1",
298 ),
236 }, 299 },
237 }, 300 },
238 ] 301 ]