emmylua_parser/syntax/node/doc/
mod.rs

1mod description;
2mod tag;
3mod test;
4mod types;
5
6pub use description::*;
7pub use tag::*;
8pub use types::*;
9
10use super::{
11    LuaAst, LuaBinaryOpToken, LuaLiteralToken, LuaNameToken, LuaNumberToken, LuaStringToken,
12};
13use crate::{
14    kind::{LuaSyntaxKind, LuaTokenKind},
15    syntax::traits::LuaAstNode,
16    LuaAstChildren, LuaAstToken, LuaAstTokenChildren, LuaKind, LuaSyntaxNode,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct LuaComment {
21    syntax: LuaSyntaxNode,
22}
23
24impl LuaAstNode for LuaComment {
25    fn syntax(&self) -> &LuaSyntaxNode {
26        &self.syntax
27    }
28
29    fn can_cast(kind: LuaSyntaxKind) -> bool
30    where
31        Self: Sized,
32    {
33        kind == LuaSyntaxKind::Comment
34    }
35
36    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
37    where
38        Self: Sized,
39    {
40        if Self::can_cast(syntax.kind().into()) {
41            Some(Self { syntax })
42        } else {
43            None
44        }
45    }
46}
47
48impl LuaComment {
49    pub fn get_owner(&self) -> Option<LuaAst> {
50        if let Some(inline_node) = find_inline_node(&self.syntax) {
51            LuaAst::cast(inline_node)
52        } else if let Some(attached_node) = find_attached_node(&self.syntax) {
53            LuaAst::cast(attached_node)
54        } else {
55            None
56        }
57    }
58
59    pub fn get_doc_tags(&self) -> LuaAstChildren<LuaDocTag> {
60        self.children()
61    }
62
63    pub fn get_description(&self) -> Option<LuaDocDescription> {
64        for child in self.syntax.children_with_tokens() {
65            match child.kind() {
66                LuaKind::Syntax(LuaSyntaxKind::DocDescription) => {
67                    return LuaDocDescription::cast(child.into_node().unwrap());
68                }
69                LuaKind::Token(LuaTokenKind::TkDocStart) => {
70                    return None;
71                }
72                _ => {}
73            }
74        }
75        None
76    }
77}
78
79fn find_inline_node(comment: &LuaSyntaxNode) -> Option<LuaSyntaxNode> {
80    let mut prev_sibling = comment.prev_sibling_or_token();
81    loop {
82        prev_sibling.as_ref()?;
83
84        if let Some(sibling) = prev_sibling {
85            match sibling.kind() {
86                LuaKind::Token(
87                    LuaTokenKind::TkWhitespace | LuaTokenKind::TkComma | LuaTokenKind::TkSemicolon,
88                ) => {}
89                LuaKind::Token(LuaTokenKind::TkEndOfLine)
90                | LuaKind::Syntax(LuaSyntaxKind::Comment) => {
91                    return None;
92                }
93                LuaKind::Token(k) if k != LuaTokenKind::TkName => {
94                    return comment.parent();
95                }
96                _ => match sibling {
97                    rowan::NodeOrToken::Node(node) => {
98                        return Some(node);
99                    }
100                    rowan::NodeOrToken::Token(token) => {
101                        return token.parent();
102                    }
103                },
104            }
105            prev_sibling = sibling.prev_sibling_or_token();
106        } else {
107            return None;
108        }
109    }
110}
111
112fn find_attached_node(comment: &LuaSyntaxNode) -> Option<LuaSyntaxNode> {
113    let mut meet_end_of_line = false;
114
115    let mut next_sibling = comment.next_sibling_or_token();
116    loop {
117        next_sibling.as_ref()?;
118
119        if let Some(sibling) = next_sibling {
120            match sibling.kind() {
121                LuaKind::Token(LuaTokenKind::TkEndOfLine) => {
122                    if meet_end_of_line {
123                        return None;
124                    }
125
126                    meet_end_of_line = true;
127                }
128                LuaKind::Token(LuaTokenKind::TkWhitespace) => {}
129                LuaKind::Syntax(LuaSyntaxKind::Comment) => {
130                    return None;
131                }
132                LuaKind::Syntax(LuaSyntaxKind::Block) => {
133                    let first_child = comment.first_child()?;
134                    if first_child.kind() == LuaKind::Syntax(LuaSyntaxKind::Comment) {
135                        return None;
136                    }
137                    return Some(first_child);
138                }
139                _ => match sibling {
140                    rowan::NodeOrToken::Node(node) => {
141                        return Some(node);
142                    }
143                    rowan::NodeOrToken::Token(token) => {
144                        return token.parent();
145                    }
146                },
147            }
148            next_sibling = sibling.next_sibling_or_token();
149        }
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub struct LuaDocGenericDeclList {
155    syntax: LuaSyntaxNode,
156}
157
158impl LuaAstNode for LuaDocGenericDeclList {
159    fn syntax(&self) -> &LuaSyntaxNode {
160        &self.syntax
161    }
162
163    fn can_cast(kind: LuaSyntaxKind) -> bool
164    where
165        Self: Sized,
166    {
167        kind == LuaSyntaxKind::DocGenericDeclareList
168    }
169
170    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
171    where
172        Self: Sized,
173    {
174        if Self::can_cast(syntax.kind().into()) {
175            Some(Self { syntax })
176        } else {
177            None
178        }
179    }
180}
181
182impl LuaDocGenericDeclList {
183    pub fn get_generic_decl(&self) -> LuaAstChildren<LuaDocGenericDecl> {
184        self.children()
185    }
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub struct LuaDocGenericDecl {
190    syntax: LuaSyntaxNode,
191}
192
193impl LuaAstNode for LuaDocGenericDecl {
194    fn syntax(&self) -> &LuaSyntaxNode {
195        &self.syntax
196    }
197
198    fn can_cast(kind: LuaSyntaxKind) -> bool
199    where
200        Self: Sized,
201    {
202        kind == LuaSyntaxKind::DocGenericParameter
203    }
204
205    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
206    where
207        Self: Sized,
208    {
209        if Self::can_cast(syntax.kind().into()) {
210            Some(Self { syntax })
211        } else {
212            None
213        }
214    }
215}
216
217impl LuaDocGenericDecl {
218    pub fn get_name_token(&self) -> Option<LuaNameToken> {
219        self.token()
220    }
221
222    pub fn get_type(&self) -> Option<LuaDocType> {
223        self.child()
224    }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct LuaDocTypeList {
229    syntax: LuaSyntaxNode,
230}
231
232impl LuaAstNode for LuaDocTypeList {
233    fn syntax(&self) -> &LuaSyntaxNode {
234        &self.syntax
235    }
236
237    fn can_cast(kind: LuaSyntaxKind) -> bool
238    where
239        Self: Sized,
240    {
241        kind == LuaSyntaxKind::DocTypeList
242    }
243
244    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
245    where
246        Self: Sized,
247    {
248        if Self::can_cast(syntax.kind().into()) {
249            Some(Self { syntax })
250        } else {
251            None
252        }
253    }
254}
255
256impl LuaDocTypeList {
257    pub fn get_types(&self) -> LuaAstChildren<LuaDocType> {
258        self.children()
259    }
260
261    pub fn get_return_type_list(&self) -> LuaAstChildren<LuaDocNamedReturnType> {
262        self.children()
263    }
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
267pub struct LuaDocOpType {
268    syntax: LuaSyntaxNode,
269}
270
271impl LuaAstNode for LuaDocOpType {
272    fn syntax(&self) -> &LuaSyntaxNode {
273        &self.syntax
274    }
275
276    fn can_cast(kind: LuaSyntaxKind) -> bool
277    where
278        Self: Sized,
279    {
280        kind == LuaSyntaxKind::DocOpType
281    }
282
283    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
284    where
285        Self: Sized,
286    {
287        if Self::can_cast(syntax.kind().into()) {
288            Some(Self { syntax })
289        } else {
290            None
291        }
292    }
293}
294
295impl LuaDocOpType {
296    pub fn get_op(&self) -> Option<LuaBinaryOpToken> {
297        self.token()
298    }
299
300    pub fn get_type(&self) -> Option<LuaDocType> {
301        self.child()
302    }
303
304    pub fn is_nullable(&self) -> bool {
305        self.token_by_kind(LuaTokenKind::TkDocQuestion).is_some()
306    }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct LuaDocObjectField {
311    syntax: LuaSyntaxNode,
312}
313
314impl LuaAstNode for LuaDocObjectField {
315    fn syntax(&self) -> &LuaSyntaxNode {
316        &self.syntax
317    }
318
319    fn can_cast(kind: LuaSyntaxKind) -> bool
320    where
321        Self: Sized,
322    {
323        kind == LuaSyntaxKind::DocObjectField
324    }
325
326    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
327    where
328        Self: Sized,
329    {
330        if Self::can_cast(syntax.kind().into()) {
331            Some(Self { syntax })
332        } else {
333            None
334        }
335    }
336}
337
338impl LuaDocObjectField {
339    pub fn get_field_key(&self) -> Option<LuaDocObjectFieldKey> {
340        for child in self.syntax.children_with_tokens() {
341            match child.kind() {
342                LuaKind::Token(LuaTokenKind::TkName) => {
343                    return LuaNameToken::cast(child.into_token().unwrap())
344                        .map(LuaDocObjectFieldKey::Name);
345                }
346                kind if LuaDocType::can_cast(kind.into()) => {
347                    let doc_type = LuaDocType::cast(child.into_node().unwrap())?;
348                    if let LuaDocType::Literal(literal) = &doc_type {
349                        let literal = literal.get_literal()?;
350                        match literal {
351                            LuaLiteralToken::Number(num) => {
352                                return Some(LuaDocObjectFieldKey::Integer(num));
353                            }
354                            LuaLiteralToken::String(str) => {
355                                return Some(LuaDocObjectFieldKey::String(str));
356                            }
357                            _ => {}
358                        }
359                    }
360
361                    return LuaDocObjectFieldKey::Type(doc_type).into();
362                }
363                LuaKind::Token(LuaTokenKind::TkColon) => {
364                    return None;
365                }
366                _ => {}
367            }
368        }
369
370        None
371    }
372
373    pub fn get_type(&self) -> Option<LuaDocType> {
374        self.children().last()
375    }
376
377    pub fn is_nullable(&self) -> bool {
378        self.token_by_kind(LuaTokenKind::TkDocQuestion).is_some()
379    }
380}
381
382#[derive(Debug, Clone, PartialEq, Eq, Hash)]
383pub enum LuaDocObjectFieldKey {
384    Name(LuaNameToken),
385    String(LuaStringToken),
386    Integer(LuaNumberToken),
387    Type(LuaDocType),
388}
389
390#[derive(Debug, Clone, PartialEq, Eq, Hash)]
391pub struct LuaDocAttribute {
392    syntax: LuaSyntaxNode,
393}
394
395impl LuaAstNode for LuaDocAttribute {
396    fn syntax(&self) -> &LuaSyntaxNode {
397        &self.syntax
398    }
399
400    fn can_cast(kind: LuaSyntaxKind) -> bool
401    where
402        Self: Sized,
403    {
404        kind == LuaSyntaxKind::DocAttribute
405    }
406
407    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
408    where
409        Self: Sized,
410    {
411        if Self::can_cast(syntax.kind().into()) {
412            Some(Self { syntax })
413        } else {
414            None
415        }
416    }
417}
418
419impl LuaDocAttribute {
420    pub fn get_attrib_tokens(&self) -> LuaAstTokenChildren<LuaNameToken> {
421        self.tokens()
422    }
423}
424
425#[derive(Debug, Clone, PartialEq, Eq, Hash)]
426pub struct LuaDocNamedReturnType {
427    syntax: LuaSyntaxNode,
428}
429
430impl LuaAstNode for LuaDocNamedReturnType {
431    fn syntax(&self) -> &LuaSyntaxNode {
432        &self.syntax
433    }
434
435    fn can_cast(kind: LuaSyntaxKind) -> bool
436    where
437        Self: Sized,
438    {
439        kind == LuaSyntaxKind::DocNamedReturnType
440    }
441
442    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
443    where
444        Self: Sized,
445    {
446        if Self::can_cast(syntax.kind().into()) {
447            Some(Self { syntax })
448        } else {
449            None
450        }
451    }
452}
453
454impl LuaDocNamedReturnType {
455    pub fn get_name_and_type(&self) -> (Option<LuaNameToken>, Option<LuaDocType>) {
456        let types = self.children().collect::<Vec<LuaDocType>>();
457        if types.len() == 1 {
458            (None, Some(types[0].clone()))
459        } else if types.len() == 2 {
460            if let LuaDocType::Name(name) = &types[0] {
461                (name.get_name_token(), Some(types[1].clone()))
462            } else {
463                (None, None)
464            }
465        } else {
466            (None, None)
467        }
468    }
469}