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