aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src/ast/traits.rs
diff options
context:
space:
mode:
authorIgor Aleksanov <[email protected]>2020-08-14 05:34:07 +0100
committerIgor Aleksanov <[email protected]>2020-08-14 05:34:07 +0100
commitc26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch)
tree7cff36c38234be0afb65273146d8247083a5cfeb /crates/syntax/src/ast/traits.rs
parent3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff)
parentf1f73649a686dc6e6449afc35e0fa6fed00e225d (diff)
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/syntax/src/ast/traits.rs')
-rw-r--r--crates/syntax/src/ast/traits.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
new file mode 100644
index 000000000..0bdc22d95
--- /dev/null
+++ b/crates/syntax/src/ast/traits.rs
@@ -0,0 +1,141 @@
1//! Various traits that are implemented by ast nodes.
2//!
3//! The implementations are usually trivial, and live in generated.rs
4use itertools::Itertools;
5
6use crate::{
7 ast::{self, support, AstChildren, AstNode, AstToken},
8 syntax_node::SyntaxElementChildren,
9 SyntaxToken, T,
10};
11
12pub trait NameOwner: AstNode {
13 fn name(&self) -> Option<ast::Name> {
14 support::child(self.syntax())
15 }
16}
17
18pub trait VisibilityOwner: AstNode {
19 fn visibility(&self) -> Option<ast::Visibility> {
20 support::child(self.syntax())
21 }
22}
23
24pub trait LoopBodyOwner: AstNode {
25 fn loop_body(&self) -> Option<ast::BlockExpr> {
26 support::child(self.syntax())
27 }
28
29 fn label(&self) -> Option<ast::Label> {
30 support::child(self.syntax())
31 }
32}
33
34pub trait ArgListOwner: AstNode {
35 fn arg_list(&self) -> Option<ast::ArgList> {
36 support::child(self.syntax())
37 }
38}
39
40pub trait ModuleItemOwner: AstNode {
41 fn items(&self) -> AstChildren<ast::Item> {
42 support::children(self.syntax())
43 }
44}
45
46pub trait GenericParamsOwner: AstNode {
47 fn generic_param_list(&self) -> Option<ast::GenericParamList> {
48 support::child(self.syntax())
49 }
50
51 fn where_clause(&self) -> Option<ast::WhereClause> {
52 support::child(self.syntax())
53 }
54}
55
56pub trait TypeBoundsOwner: AstNode {
57 fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
58 support::child(self.syntax())
59 }
60
61 fn colon_token(&self) -> Option<SyntaxToken> {
62 support::token(self.syntax(), T![:])
63 }
64}
65
66pub trait AttrsOwner: AstNode {
67 fn attrs(&self) -> AstChildren<ast::Attr> {
68 support::children(self.syntax())
69 }
70 fn has_atom_attr(&self, atom: &str) -> bool {
71 self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
72 }
73}
74
75pub trait DocCommentsOwner: AstNode {
76 fn doc_comments(&self) -> CommentIter {
77 CommentIter { iter: self.syntax().children_with_tokens() }
78 }
79
80 fn doc_comment_text(&self) -> Option<String> {
81 self.doc_comments().doc_comment_text()
82 }
83}
84
85impl CommentIter {
86 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
87 CommentIter { iter: syntax_node.children_with_tokens() }
88 }
89
90 /// Returns the textual content of a doc comment block as a single string.
91 /// That is, strips leading `///` (+ optional 1 character of whitespace),
92 /// trailing `*/`, trailing whitespace and then joins the lines.
93 pub fn doc_comment_text(self) -> Option<String> {
94 let mut has_comments = false;
95 let docs = self
96 .filter(|comment| comment.kind().doc.is_some())
97 .map(|comment| {
98 has_comments = true;
99 let prefix_len = comment.prefix().len();
100
101 let line: &str = comment.text().as_str();
102
103 // Determine if the prefix or prefix + 1 char is stripped
104 let pos =
105 if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
106 prefix_len + ws.len_utf8()
107 } else {
108 prefix_len
109 };
110
111 let end = if comment.kind().shape.is_block() && line.ends_with("*/") {
112 line.len() - 2
113 } else {
114 line.len()
115 };
116
117 // Note that we do not trim the end of the line here
118 // since whitespace can have special meaning at the end
119 // of a line in markdown.
120 line[pos..end].to_owned()
121 })
122 .join("\n");
123
124 if has_comments {
125 Some(docs)
126 } else {
127 None
128 }
129 }
130}
131
132pub struct CommentIter {
133 iter: SyntaxElementChildren,
134}
135
136impl Iterator for CommentIter {
137 type Item = ast::Comment;
138 fn next(&mut self) -> Option<ast::Comment> {
139 self.iter.by_ref().find_map(|el| el.into_token().and_then(ast::Comment::cast))
140 }
141}