aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/runnables.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/runnables.rs')
-rw-r--r--crates/ra_analysis/src/runnables.rs72
1 files changed, 72 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
new file mode 100644
index 000000000..61ca0930a
--- /dev/null
+++ b/crates/ra_analysis/src/runnables.rs
@@ -0,0 +1,72 @@
1use ra_syntax::{
2 ast::{self, AstNode, NameOwner, ModuleItemOwner},
3 SourceFileNode, TextRange, SyntaxNodeRef,
4 TextUnit,
5};
6use crate::{
7 Analysis, FileId, FilePosition
8};
9
10#[derive(Debug)]
11pub struct Runnable {
12 pub range: TextRange,
13 pub kind: RunnableKind,
14}
15
16#[derive(Debug)]
17pub enum RunnableKind {
18 Test { name: String },
19 TestMod { path: String },
20 Bin,
21}
22
23pub fn runnables(
24 analysis: &Analysis,
25 file_node: &SourceFileNode,
26 file_id: FileId,
27) -> Vec<Runnable> {
28 file_node
29 .syntax()
30 .descendants()
31 .filter_map(|i| runnable(analysis, i, file_id))
32 .collect()
33}
34
35fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> {
36 if let Some(f) = ast::FnDef::cast(item) {
37 let name = f.name()?.text();
38 let kind = if name == "main" {
39 RunnableKind::Bin
40 } else if f.has_atom_attr("test") {
41 RunnableKind::Test {
42 name: name.to_string(),
43 }
44 } else {
45 return None;
46 };
47 Some(Runnable {
48 range: f.syntax().range(),
49 kind,
50 })
51 } else if let Some(m) = ast::Module::cast(item) {
52 if m.item_list()?
53 .items()
54 .map(ast::ModuleItem::syntax)
55 .filter_map(ast::FnDef::cast)
56 .any(|f| f.has_atom_attr("test"))
57 {
58 let postition = FilePosition {
59 file_id,
60 offset: m.syntax().range().start() + TextUnit::from_usize(1),
61 };
62 analysis.module_path(postition).ok()?.map(|path| Runnable {
63 range: m.syntax().range(),
64 kind: RunnableKind::TestMod { path },
65 })
66 } else {
67 None
68 }
69 } else {
70 None
71 }
72}