ra_syntax/ast/
mod.rs

1mod generated;
2
3use std::marker::PhantomData;
4
5use itertools::Itertools;
6
7pub use self::generated::*;
8use crate::{
9    yellow::{RefRoot, SyntaxNodeChildren},
10    SmolStr,
11    SyntaxKind::*,
12    SyntaxNodeRef,
13};
14
15pub trait AstNode<'a>: Clone + Copy + 'a {
16    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self>
17    where
18        Self: Sized;
19    fn syntax(self) -> SyntaxNodeRef<'a>;
20}
21
22pub trait NameOwner<'a>: AstNode<'a> {
23    fn name(self) -> Option<Name<'a>> {
24        child_opt(self)
25    }
26}
27
28pub trait LoopBodyOwner<'a>: AstNode<'a> {
29    fn loop_body(self) -> Option<Block<'a>> {
30        child_opt(self)
31    }
32}
33
34pub trait ArgListOwner<'a>: AstNode<'a> {
35    fn arg_list(self) -> Option<ArgList<'a>> {
36        child_opt(self)
37    }
38}
39
40pub trait FnDefOwner<'a>: AstNode<'a> {
41    fn functions(self) -> AstChildren<'a, FnDef<'a>> {
42        children(self)
43    }
44}
45
46pub trait ModuleItemOwner<'a>: AstNode<'a> {
47    fn items(self) -> AstChildren<'a, ModuleItem<'a>> {
48        children(self)
49    }
50}
51
52pub trait TypeParamsOwner<'a>: AstNode<'a> {
53    fn type_param_list(self) -> Option<TypeParamList<'a>> {
54        child_opt(self)
55    }
56
57    fn where_clause(self) -> Option<WhereClause<'a>> {
58        child_opt(self)
59    }
60}
61
62pub trait AttrsOwner<'a>: AstNode<'a> {
63    fn attrs(self) -> AstChildren<'a, Attr<'a>> {
64        children(self)
65    }
66}
67
68pub trait DocCommentsOwner<'a>: AstNode<'a> {
69    fn doc_comments(self) -> AstChildren<'a, Comment<'a>> {
70        children(self)
71    }
72
73    /// Returns the textual content of a doc comment block as a single string.
74    /// That is, strips leading `///` and joins lines
75    fn doc_comment_text(self) -> String {
76        self.doc_comments()
77            .map(|comment| {
78                let prefix = comment.prefix();
79                let trimmed = comment
80                    .text()
81                    .as_str()
82                    .trim()
83                    .trim_start_matches(prefix)
84                    .trim_start();
85                trimmed.to_owned()
86            })
87            .join("\n")
88    }
89}
90
91impl<'a> FnDef<'a> {
92    pub fn has_atom_attr(&self, atom: &str) -> bool {
93        self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom)
94    }
95}
96
97impl<'a> Attr<'a> {
98    pub fn as_atom(&self) -> Option<SmolStr> {
99        let tt = self.value()?;
100        let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?;
101        if attr.kind() == IDENT {
102            Some(attr.leaf_text().unwrap().clone())
103        } else {
104            None
105        }
106    }
107
108    pub fn as_call(&self) -> Option<(SmolStr, TokenTree<'a>)> {
109        let tt = self.value()?;
110        let (_bra, attr, args, _ket) = tt.syntax().children().collect_tuple()?;
111        let args = TokenTree::cast(args)?;
112        if attr.kind() == IDENT {
113            Some((attr.leaf_text().unwrap().clone(), args))
114        } else {
115            None
116        }
117    }
118}
119
120impl<'a> Lifetime<'a> {
121    pub fn text(&self) -> SmolStr {
122        self.syntax().leaf_text().unwrap().clone()
123    }
124}
125
126impl<'a> Char<'a> {
127    pub fn text(&self) -> &SmolStr {
128        &self.syntax().leaf_text().unwrap()
129    }
130}
131
132impl<'a> Comment<'a> {
133    pub fn text(&self) -> &SmolStr {
134        self.syntax().leaf_text().unwrap()
135    }
136
137    pub fn flavor(&self) -> CommentFlavor {
138        let text = self.text();
139        if text.starts_with("///") {
140            CommentFlavor::Doc
141        } else if text.starts_with("//!") {
142            CommentFlavor::ModuleDoc
143        } else if text.starts_with("//") {
144            CommentFlavor::Line
145        } else {
146            CommentFlavor::Multiline
147        }
148    }
149
150    pub fn prefix(&self) -> &'static str {
151        self.flavor().prefix()
152    }
153
154    pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> {
155        self.text().chars().filter(|&c| c == '\n').map(|_| &())
156    }
157
158    pub fn has_newlines(&self) -> bool {
159        self.count_newlines_lazy().count() > 0
160    }
161}
162
163#[derive(Debug, PartialEq, Eq)]
164pub enum CommentFlavor {
165    Line,
166    Doc,
167    ModuleDoc,
168    Multiline,
169}
170
171impl CommentFlavor {
172    pub fn prefix(&self) -> &'static str {
173        use self::CommentFlavor::*;
174        match *self {
175            Line => "//",
176            Doc => "///",
177            ModuleDoc => "//!",
178            Multiline => "/*",
179        }
180    }
181}
182
183impl<'a> Whitespace<'a> {
184    pub fn text(&self) -> &SmolStr {
185        &self.syntax().leaf_text().unwrap()
186    }
187
188    pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> {
189        self.text().chars().filter(|&c| c == '\n').map(|_| &())
190    }
191
192    pub fn has_newlines(&self) -> bool {
193        self.count_newlines_lazy().count() > 0
194    }
195}
196
197impl<'a> Name<'a> {
198    pub fn text(&self) -> SmolStr {
199        let ident = self.syntax().first_child().unwrap();
200        ident.leaf_text().unwrap().clone()
201    }
202}
203
204impl<'a> NameRef<'a> {
205    pub fn text(&self) -> SmolStr {
206        let ident = self.syntax().first_child().unwrap();
207        ident.leaf_text().unwrap().clone()
208    }
209}
210
211impl<'a> ImplItem<'a> {
212    pub fn target_type(self) -> Option<TypeRef<'a>> {
213        match self.target() {
214            (Some(t), None) | (_, Some(t)) => Some(t),
215            _ => None,
216        }
217    }
218
219    pub fn target_trait(self) -> Option<TypeRef<'a>> {
220        match self.target() {
221            (Some(t), Some(_)) => Some(t),
222            _ => None,
223        }
224    }
225
226    fn target(self) -> (Option<TypeRef<'a>>, Option<TypeRef<'a>>) {
227        let mut types = children(self);
228        let first = types.next();
229        let second = types.next();
230        (first, second)
231    }
232}
233
234impl<'a> Module<'a> {
235    pub fn has_semi(self) -> bool {
236        match self.syntax().last_child() {
237            None => false,
238            Some(node) => node.kind() == SEMI,
239        }
240    }
241}
242
243impl<'a> LetStmt<'a> {
244    pub fn has_semi(self) -> bool {
245        match self.syntax().last_child() {
246            None => false,
247            Some(node) => node.kind() == SEMI,
248        }
249    }
250}
251
252impl<'a> IfExpr<'a> {
253    pub fn then_branch(self) -> Option<Block<'a>> {
254        self.blocks().nth(0)
255    }
256    pub fn else_branch(self) -> Option<Block<'a>> {
257        self.blocks().nth(1)
258    }
259    fn blocks(self) -> AstChildren<'a, Block<'a>> {
260        children(self)
261    }
262}
263
264#[derive(Debug, Clone, Copy)]
265pub enum PathSegmentKind<'a> {
266    Name(NameRef<'a>),
267    SelfKw,
268    SuperKw,
269    CrateKw,
270}
271
272impl<'a> PathSegment<'a> {
273    pub fn parent_path(self) -> Path<'a> {
274        self.syntax()
275            .parent()
276            .and_then(Path::cast)
277            .expect("segments are always nested in paths")
278    }
279
280    pub fn kind(self) -> Option<PathSegmentKind<'a>> {
281        let res = if let Some(name_ref) = self.name_ref() {
282            PathSegmentKind::Name(name_ref)
283        } else {
284            match self.syntax().first_child()?.kind() {
285                SELF_KW => PathSegmentKind::SelfKw,
286                SUPER_KW => PathSegmentKind::SuperKw,
287                CRATE_KW => PathSegmentKind::CrateKw,
288                _ => return None,
289            }
290        };
291        Some(res)
292    }
293}
294
295fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
296    children(parent).next()
297}
298
299fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstChildren<'a, C> {
300    AstChildren::new(parent.syntax())
301}
302
303#[derive(Debug)]
304pub struct AstChildren<'a, N> {
305    inner: SyntaxNodeChildren<RefRoot<'a>>,
306    ph: PhantomData<N>,
307}
308
309impl<'a, N> AstChildren<'a, N> {
310    fn new(parent: SyntaxNodeRef<'a>) -> Self {
311        AstChildren {
312            inner: parent.children(),
313            ph: PhantomData,
314        }
315    }
316}
317
318impl<'a, N: AstNode<'a>> Iterator for AstChildren<'a, N> {
319    type Item = N;
320    fn next(&mut self) -> Option<N> {
321        loop {
322            if let Some(n) = N::cast(self.inner.next()?) {
323                return Some(n);
324            }
325        }
326    }
327}