aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/traits.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/traits.rs')
-rw-r--r--crates/ra_syntax/src/ast/traits.rs154
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
5use itertools::Itertools;
6
7use crate::{
8 syntax_node::{SyntaxNodeChildren, SyntaxElementChildren},
9 ast::{self, child_opt, children, AstNode, AstToken, AstChildren},
10};
11
12pub trait TypeAscriptionOwner: AstNode {
13 fn ascribed_type(&self) -> Option<&ast::TypeRef> {
14 child_opt(self)
15 }
16}
17
18pub trait NameOwner: AstNode {
19 fn name(&self) -> Option<&ast::Name> {
20 child_opt(self)
21 }
22}
23
24pub trait VisibilityOwner: AstNode {
25 fn visibility(&self) -> Option<&ast::Visibility> {
26 child_opt(self)
27 }
28}
29
30pub trait LoopBodyOwner: AstNode {
31 fn loop_body(&self) -> Option<&ast::Block> {
32 child_opt(self)
33 }
34}
35
36pub trait ArgListOwner: AstNode {
37 fn arg_list(&self) -> Option<&ast::ArgList> {
38 child_opt(self)
39 }
40}
41
42pub trait FnDefOwner: AstNode {
43 fn functions(&self) -> AstChildren<ast::FnDef> {
44 children(self)
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum ItemOrMacro<'a> {
50 Item(&'a ast::ModuleItem),
51 Macro(&'a ast::MacroCall),
52}
53
54pub 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)]
64pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>);
65
66impl<'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
81pub 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
91pub trait TypeBoundsOwner: AstNode {
92 fn type_bound_list(&self) -> Option<&ast::TypeBoundList> {
93 child_opt(self)
94 }
95}
96
97pub 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
106pub 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
145pub struct CommentIter<'a> {
146 iter: SyntaxElementChildren<'a>,
147}
148
149impl<'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}