aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/runnables.rs
blob: 61ca0930a79b7f12676d2a5294ddd8562546aeef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use ra_syntax::{
    ast::{self, AstNode, NameOwner, ModuleItemOwner},
    SourceFileNode, TextRange, SyntaxNodeRef,
    TextUnit,
};
use crate::{
    Analysis, FileId, FilePosition
};

#[derive(Debug)]
pub struct Runnable {
    pub range: TextRange,
    pub kind: RunnableKind,
}

#[derive(Debug)]
pub enum RunnableKind {
    Test { name: String },
    TestMod { path: String },
    Bin,
}

pub fn runnables(
    analysis: &Analysis,
    file_node: &SourceFileNode,
    file_id: FileId,
) -> Vec<Runnable> {
    file_node
        .syntax()
        .descendants()
        .filter_map(|i| runnable(analysis, i, file_id))
        .collect()
}

fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> {
    if let Some(f) = ast::FnDef::cast(item) {
        let name = f.name()?.text();
        let kind = if name == "main" {
            RunnableKind::Bin
        } else if f.has_atom_attr("test") {
            RunnableKind::Test {
                name: name.to_string(),
            }
        } else {
            return None;
        };
        Some(Runnable {
            range: f.syntax().range(),
            kind,
        })
    } else if let Some(m) = ast::Module::cast(item) {
        if m.item_list()?
            .items()
            .map(ast::ModuleItem::syntax)
            .filter_map(ast::FnDef::cast)
            .any(|f| f.has_atom_attr("test"))
        {
            let postition = FilePosition {
                file_id,
                offset: m.syntax().range().start() + TextUnit::from_usize(1),
            };
            analysis.module_path(postition).ok()?.map(|path| Runnable {
                range: m.syntax().range(),
                kind: RunnableKind::TestMod { path },
            })
        } else {
            None
        }
    } else {
        None
    }
}