diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 151 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/traits.rs | 148 |
2 files changed, 155 insertions, 144 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index ffd115cef..3e81fa990 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -1,17 +1,22 @@ | |||
1 | //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s | 1 | //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s |
2 | mod generated; | 2 | mod generated; |
3 | mod traits; | ||
3 | 4 | ||
4 | use std::marker::PhantomData; | 5 | use std::marker::PhantomData; |
5 | 6 | ||
6 | use itertools::Itertools; | 7 | use itertools::Itertools; |
7 | 8 | ||
8 | pub use self::generated::*; | ||
9 | use crate::{ | 9 | use crate::{ |
10 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement, SyntaxElementChildren}, | 10 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement}, |
11 | SmolStr, | 11 | SmolStr, |
12 | SyntaxKind::*, | 12 | SyntaxKind::*, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub use self::{ | ||
16 | generated::*, | ||
17 | traits::*, | ||
18 | }; | ||
19 | |||
15 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The | 20 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The |
16 | /// conversion itself has zero runtime cost: ast and syntax nodes have exactly | 21 | /// conversion itself has zero runtime cost: ast and syntax nodes have exactly |
17 | /// the same representation: a pointer to the tree root and a pointer to the | 22 | /// the same representation: a pointer to the tree root and a pointer to the |
@@ -25,137 +30,6 @@ pub trait AstNode: | |||
25 | fn syntax(&self) -> &SyntaxNode; | 30 | fn syntax(&self) -> &SyntaxNode; |
26 | } | 31 | } |
27 | 32 | ||
28 | pub trait TypeAscriptionOwner: AstNode { | ||
29 | fn ascribed_type(&self) -> Option<&TypeRef> { | ||
30 | child_opt(self) | ||
31 | } | ||
32 | } | ||
33 | |||
34 | pub trait NameOwner: AstNode { | ||
35 | fn name(&self) -> Option<&Name> { | ||
36 | child_opt(self) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub trait VisibilityOwner: AstNode { | ||
41 | fn visibility(&self) -> Option<&Visibility> { | ||
42 | child_opt(self) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub trait LoopBodyOwner: AstNode { | ||
47 | fn loop_body(&self) -> Option<&Block> { | ||
48 | child_opt(self) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | pub trait ArgListOwner: AstNode { | ||
53 | fn arg_list(&self) -> Option<&ArgList> { | ||
54 | child_opt(self) | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub trait FnDefOwner: AstNode { | ||
59 | fn functions(&self) -> AstChildren<FnDef> { | ||
60 | children(self) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
65 | pub enum ItemOrMacro<'a> { | ||
66 | Item(&'a ModuleItem), | ||
67 | Macro(&'a MacroCall), | ||
68 | } | ||
69 | |||
70 | pub trait ModuleItemOwner: AstNode { | ||
71 | fn items(&self) -> AstChildren<ModuleItem> { | ||
72 | children(self) | ||
73 | } | ||
74 | fn items_with_macros(&self) -> ItemOrMacroIter { | ||
75 | ItemOrMacroIter(self.syntax().children()) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | #[derive(Debug)] | ||
80 | pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); | ||
81 | |||
82 | impl<'a> Iterator for ItemOrMacroIter<'a> { | ||
83 | type Item = ItemOrMacro<'a>; | ||
84 | fn next(&mut self) -> Option<ItemOrMacro<'a>> { | ||
85 | loop { | ||
86 | let n = self.0.next()?; | ||
87 | if let Some(item) = ModuleItem::cast(n) { | ||
88 | return Some(ItemOrMacro::Item(item)); | ||
89 | } | ||
90 | if let Some(call) = MacroCall::cast(n) { | ||
91 | return Some(ItemOrMacro::Macro(call)); | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | pub trait TypeParamsOwner: AstNode { | ||
98 | fn type_param_list(&self) -> Option<&TypeParamList> { | ||
99 | child_opt(self) | ||
100 | } | ||
101 | |||
102 | fn where_clause(&self) -> Option<&WhereClause> { | ||
103 | child_opt(self) | ||
104 | } | ||
105 | } | ||
106 | |||
107 | pub trait TypeBoundsOwner: AstNode { | ||
108 | fn type_bound_list(&self) -> Option<&TypeBoundList> { | ||
109 | child_opt(self) | ||
110 | } | ||
111 | } | ||
112 | |||
113 | pub trait AttrsOwner: AstNode { | ||
114 | fn attrs(&self) -> AstChildren<Attr> { | ||
115 | children(self) | ||
116 | } | ||
117 | fn has_atom_attr(&self, atom: &str) -> bool { | ||
118 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | pub trait DocCommentsOwner: AstNode { | ||
123 | fn doc_comments(&self) -> CommentIter { | ||
124 | CommentIter { iter: self.syntax().children_with_tokens() } | ||
125 | } | ||
126 | |||
127 | /// Returns the textual content of a doc comment block as a single string. | ||
128 | /// That is, strips leading `///` (+ optional 1 character of whitespace) | ||
129 | /// and joins lines. | ||
130 | fn doc_comment_text(&self) -> Option<std::string::String> { | ||
131 | let docs = self | ||
132 | .doc_comments() | ||
133 | .filter(|comment| comment.is_doc_comment()) | ||
134 | .map(|comment| { | ||
135 | let prefix_len = comment.prefix().len(); | ||
136 | |||
137 | let line = comment.text().as_str(); | ||
138 | |||
139 | // Determine if the prefix or prefix + 1 char is stripped | ||
140 | let pos = | ||
141 | if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) { | ||
142 | prefix_len + 1 | ||
143 | } else { | ||
144 | prefix_len | ||
145 | }; | ||
146 | |||
147 | line[pos..].to_owned() | ||
148 | }) | ||
149 | .join("\n"); | ||
150 | |||
151 | if docs.is_empty() { | ||
152 | None | ||
153 | } else { | ||
154 | Some(docs) | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl Attr { | 33 | impl Attr { |
160 | pub fn is_inner(&self) -> bool { | 34 | pub fn is_inner(&self) -> bool { |
161 | let tt = match self.value() { | 35 | let tt = match self.value() { |
@@ -245,17 +119,6 @@ impl<'a> Comment<'a> { | |||
245 | } | 119 | } |
246 | } | 120 | } |
247 | 121 | ||
248 | pub struct CommentIter<'a> { | ||
249 | iter: SyntaxElementChildren<'a>, | ||
250 | } | ||
251 | |||
252 | impl<'a> Iterator for CommentIter<'a> { | ||
253 | type Item = Comment<'a>; | ||
254 | fn next(&mut self) -> Option<Comment<'a>> { | ||
255 | self.iter.by_ref().find_map(|el| el.as_token().and_then(Comment::cast)) | ||
256 | } | ||
257 | } | ||
258 | |||
259 | #[derive(Debug, PartialEq, Eq)] | 122 | #[derive(Debug, PartialEq, Eq)] |
260 | pub enum CommentFlavor { | 123 | pub enum CommentFlavor { |
261 | Line, | 124 | Line, |
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs new file mode 100644 index 000000000..85fe6d5e1 --- /dev/null +++ b/crates/ra_syntax/src/ast/traits.rs | |||
@@ -0,0 +1,148 @@ | |||
1 | use itertools::Itertools; | ||
2 | |||
3 | use crate::{ | ||
4 | syntax_node::{SyntaxNodeChildren, SyntaxElementChildren}, | ||
5 | ast::{self, child_opt, children, AstNode, AstChildren}, | ||
6 | }; | ||
7 | |||
8 | pub trait TypeAscriptionOwner: AstNode { | ||
9 | fn ascribed_type(&self) -> Option<&ast::TypeRef> { | ||
10 | child_opt(self) | ||
11 | } | ||
12 | } | ||
13 | |||
14 | pub trait NameOwner: AstNode { | ||
15 | fn name(&self) -> Option<&ast::Name> { | ||
16 | child_opt(self) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | pub trait VisibilityOwner: AstNode { | ||
21 | fn visibility(&self) -> Option<&ast::Visibility> { | ||
22 | child_opt(self) | ||
23 | } | ||
24 | } | ||
25 | |||
26 | pub trait LoopBodyOwner: AstNode { | ||
27 | fn loop_body(&self) -> Option<&ast::Block> { | ||
28 | child_opt(self) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | pub trait ArgListOwner: AstNode { | ||
33 | fn arg_list(&self) -> Option<&ast::ArgList> { | ||
34 | child_opt(self) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | pub trait FnDefOwner: AstNode { | ||
39 | fn functions(&self) -> AstChildren<ast::FnDef> { | ||
40 | children(self) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
45 | pub enum ItemOrMacro<'a> { | ||
46 | Item(&'a ast::ModuleItem), | ||
47 | Macro(&'a ast::MacroCall), | ||
48 | } | ||
49 | |||
50 | pub trait ModuleItemOwner: AstNode { | ||
51 | fn items(&self) -> AstChildren<ast::ModuleItem> { | ||
52 | children(self) | ||
53 | } | ||
54 | fn items_with_macros(&self) -> ItemOrMacroIter { | ||
55 | ItemOrMacroIter(self.syntax().children()) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | #[derive(Debug)] | ||
60 | pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); | ||
61 | |||
62 | impl<'a> Iterator for ItemOrMacroIter<'a> { | ||
63 | type Item = ItemOrMacro<'a>; | ||
64 | fn next(&mut self) -> Option<ItemOrMacro<'a>> { | ||
65 | loop { | ||
66 | let n = self.0.next()?; | ||
67 | if let Some(item) = ast::ModuleItem::cast(n) { | ||
68 | return Some(ItemOrMacro::Item(item)); | ||
69 | } | ||
70 | if let Some(call) = ast::MacroCall::cast(n) { | ||
71 | return Some(ItemOrMacro::Macro(call)); | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pub trait TypeParamsOwner: AstNode { | ||
78 | fn type_param_list(&self) -> Option<&ast::TypeParamList> { | ||
79 | child_opt(self) | ||
80 | } | ||
81 | |||
82 | fn where_clause(&self) -> Option<&ast::WhereClause> { | ||
83 | child_opt(self) | ||
84 | } | ||
85 | } | ||
86 | |||
87 | pub trait TypeBoundsOwner: AstNode { | ||
88 | fn type_bound_list(&self) -> Option<&ast::TypeBoundList> { | ||
89 | child_opt(self) | ||
90 | } | ||
91 | } | ||
92 | |||
93 | pub trait AttrsOwner: AstNode { | ||
94 | fn attrs(&self) -> AstChildren<ast::Attr> { | ||
95 | children(self) | ||
96 | } | ||
97 | fn has_atom_attr(&self, atom: &str) -> bool { | ||
98 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
99 | } | ||
100 | } | ||
101 | |||
102 | pub trait DocCommentsOwner: AstNode { | ||
103 | fn doc_comments(&self) -> CommentIter { | ||
104 | CommentIter { iter: self.syntax().children_with_tokens() } | ||
105 | } | ||
106 | |||
107 | /// Returns the textual content of a doc comment block as a single string. | ||
108 | /// That is, strips leading `///` (+ optional 1 character of whitespace) | ||
109 | /// and joins lines. | ||
110 | fn doc_comment_text(&self) -> Option<std::string::String> { | ||
111 | let docs = self | ||
112 | .doc_comments() | ||
113 | .filter(|comment| comment.is_doc_comment()) | ||
114 | .map(|comment| { | ||
115 | let prefix_len = comment.prefix().len(); | ||
116 | |||
117 | let line = comment.text().as_str(); | ||
118 | |||
119 | // Determine if the prefix or prefix + 1 char is stripped | ||
120 | let pos = | ||
121 | if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) { | ||
122 | prefix_len + 1 | ||
123 | } else { | ||
124 | prefix_len | ||
125 | }; | ||
126 | |||
127 | line[pos..].to_owned() | ||
128 | }) | ||
129 | .join("\n"); | ||
130 | |||
131 | if docs.is_empty() { | ||
132 | None | ||
133 | } else { | ||
134 | Some(docs) | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | pub struct CommentIter<'a> { | ||
140 | iter: SyntaxElementChildren<'a>, | ||
141 | } | ||
142 | |||
143 | impl<'a> Iterator for CommentIter<'a> { | ||
144 | type Item = ast::Comment<'a>; | ||
145 | fn next(&mut self) -> Option<ast::Comment<'a>> { | ||
146 | self.iter.by_ref().find_map(|el| el.as_token().and_then(ast::Comment::cast)) | ||
147 | } | ||
148 | } | ||