aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libeditor/src/lib.rs34
-rw-r--r--libeditor/tests/test.rs22
-rw-r--r--src/ast.rs75
3 files changed, 130 insertions, 1 deletions
diff --git a/libeditor/src/lib.rs b/libeditor/src/lib.rs
index ef31efe35..387859d4a 100644
--- a/libeditor/src/lib.rs
+++ b/libeditor/src/lib.rs
@@ -26,6 +26,18 @@ pub struct Symbol {
26 pub range: TextRange, 26 pub range: TextRange,
27} 27}
28 28
29#[derive(Debug)]
30pub struct Runnable {
31 pub range: TextRange,
32 pub kind: RunnableKind,
33}
34
35#[derive(Debug)]
36pub enum RunnableKind {
37 Test { name: String },
38 Bin,
39}
40
29impl File { 41impl File {
30 pub fn new(text: &str) -> File { 42 pub fn new(text: &str) -> File {
31 File { 43 File {
@@ -78,6 +90,28 @@ impl File {
78 let syntax = self.inner.syntax(); 90 let syntax = self.inner.syntax();
79 extend_selection::extend_selection(syntax.as_ref(), range) 91 extend_selection::extend_selection(syntax.as_ref(), range)
80 } 92 }
93
94 pub fn runnables(&self) -> Vec<Runnable> {
95 self.inner
96 .functions()
97 .filter_map(|f| {
98 let name = f.name()?.text();
99 let kind = if name == "main" {
100 RunnableKind::Bin
101 } else if f.has_atom_attr("test") {
102 RunnableKind::Test {
103 name: name.to_string()
104 }
105 } else {
106 return None;
107 };
108 Some(Runnable {
109 range: f.syntax().range(),
110 kind,
111 })
112 })
113 .collect()
114 }
81} 115}
82 116
83 117
diff --git a/libeditor/tests/test.rs b/libeditor/tests/test.rs
index e1c8aee4b..ab8254d16 100644
--- a/libeditor/tests/test.rs
+++ b/libeditor/tests/test.rs
@@ -37,11 +37,33 @@ fn main() {}
37 ); 37 );
38} 38}
39 39
40#[test]
41fn test_runnables() {
42 let file = file(r#"
43fn main() {}
44
45#[test]
46fn test_foo() {}
47
48#[test]
49#[ignore]
50fn test_foo() {}
51"#);
52 let runnables = file.runnables();
53 dbg_eq(
54 &runnables,
55 r#"[Runnable { range: [1; 13), kind: Bin },
56 Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
57 Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
58 )
59}
60
40fn file(text: &str) -> File { 61fn file(text: &str) -> File {
41 File::new(text) 62 File::new(text)
42} 63}
43 64
44fn dbg_eq(actual: &impl fmt::Debug, expected: &str) { 65fn dbg_eq(actual: &impl fmt::Debug, expected: &str) {
66 let actual = format!("{:?}", actual);
45 let expected = expected.lines().map(|l| l.trim()).join(" "); 67 let expected = expected.lines().map(|l| l.trim()).join(" ");
46 assert_eq!(actual, expected); 68 assert_eq!(actual, expected);
47} 69}
diff --git a/src/ast.rs b/src/ast.rs
index caf5fb7ef..a595b9324 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -1,11 +1,25 @@
1use std::sync::Arc; 1use std::sync::Arc;
2use {SyntaxNode, SyntaxRoot, TreeRoot}; 2use {
3 SyntaxNode, SyntaxRoot, TreeRoot,
4 SyntaxKind::*,
5};
3 6
4#[derive(Debug)] 7#[derive(Debug)]
5pub struct File<R: TreeRoot = Arc<SyntaxRoot>> { 8pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
6 syntax: SyntaxNode<R>, 9 syntax: SyntaxNode<R>,
7} 10}
8 11
12#[derive(Debug)]
13pub struct Function<R: TreeRoot = Arc<SyntaxRoot>> {
14 syntax: SyntaxNode<R>,
15}
16
17#[derive(Debug)]
18pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
19 syntax: SyntaxNode<R>,
20}
21
22
9impl File<Arc<SyntaxRoot>> { 23impl File<Arc<SyntaxRoot>> {
10 pub fn parse(text: &str) -> Self { 24 pub fn parse(text: &str) -> Self {
11 File { 25 File {
@@ -15,6 +29,65 @@ impl File<Arc<SyntaxRoot>> {
15} 29}
16 30
17impl<R: TreeRoot> File<R> { 31impl<R: TreeRoot> File<R> {
32 pub fn functions<'a>(&'a self) -> impl Iterator<Item = Function<R>> + 'a {
33 self.syntax
34 .children()
35 .filter(|node| node.kind() == FN_ITEM)
36 .map(|node| Function { syntax: node })
37 }
38}
39
40impl<R: TreeRoot> Function<R> {
41 pub fn syntax(&self) -> SyntaxNode<R> {
42 self.syntax.clone()
43 }
44
45 pub fn name(&self) -> Option<Name<R>> {
46 self.syntax
47 .children()
48 .filter(|node| node.kind() == NAME)
49 .map(|node| Name { syntax: node })
50 .next()
51 }
52
53 pub fn has_atom_attr(&self, atom: &str) -> bool {
54 self.syntax
55 .children()
56 .filter(|node| node.kind() == ATTR)
57 .any(|attr| {
58 let mut metas = attr.children().filter(|node| node.kind() == META_ITEM);
59 let meta = match metas.next() {
60 None => return false,
61 Some(meta) => {
62 if metas.next().is_some() {
63 return false;
64 }
65 meta
66 }
67 };
68 let mut children = meta.children();
69 match children.next() {
70 None => false,
71 Some(child) => {
72 if children.next().is_some() {
73 return false;
74 }
75 child.kind() == IDENT && child.text() == atom
76 }
77 }
78 })
79 }
80}
81
82impl<R: TreeRoot> Name<R> {
83 pub fn text(&self) -> String {
84 self.syntax.text()
85 }
86}
87
88
89
90impl<R: TreeRoot> File<R> {
18 pub fn syntax(&self) -> SyntaxNode<R> { 91 pub fn syntax(&self) -> SyntaxNode<R> {
19 self.syntax.clone() 92 self.syntax.clone()
20 } 93 }