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    LuaAstChildren, LuaAstToken, LuaAstTokenChildren, LuaKind, LuaLiteralExpr, LuaSyntaxNode,
15    kind::{LuaSyntaxKind, LuaTokenKind},
16    syntax::traits::LuaAstNode,
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
48/// 检查语法节点是否为附加性质的文档标签
49///
50/// 附加性质的标签不会阻止查找 DocDescription
51fn is_additive_doc_tag(kind: LuaSyntaxKind) -> bool {
52    matches!(
53        kind,
54        LuaSyntaxKind::DocTagVisibility
55            | LuaSyntaxKind::DocTagExport
56            | LuaSyntaxKind::DocTagVersion
57            | LuaSyntaxKind::DocTagNodiscard
58    )
59}
60
61impl LuaComment {
62    pub fn get_owner(&self) -> Option<LuaAst> {
63        if let Some(inline_node) = find_inline_node(&self.syntax) {
64            LuaAst::cast(inline_node)
65        } else if let Some(attached_node) = find_attached_node(&self.syntax) {
66            LuaAst::cast(attached_node)
67        } else {
68            None
69        }
70    }
71
72    pub fn get_doc_tags(&self) -> LuaAstChildren<LuaDocTag> {
73        self.children()
74    }
75
76    pub fn get_description(&self) -> Option<LuaDocDescription> {
77        for child in self.syntax.children_with_tokens() {
78            match child.kind() {
79                LuaKind::Syntax(LuaSyntaxKind::DocDescription) => {
80                    return LuaDocDescription::cast(child.into_node().unwrap());
81                }
82                LuaKind::Token(LuaTokenKind::TkDocStart) => {}
83                LuaKind::Syntax(syntax_kind) => {
84                    if !is_additive_doc_tag(syntax_kind) {
85                        return None;
86                    }
87                }
88                _ => {}
89            }
90        }
91        None
92    }
93}
94
95fn find_inline_node(comment: &LuaSyntaxNode) -> Option<LuaSyntaxNode> {
96    let mut prev_sibling = comment.prev_sibling_or_token();
97    loop {
98        prev_sibling.as_ref()?;
99
100        if let Some(sibling) = prev_sibling {
101            match sibling.kind() {
102                LuaKind::Token(
103                    LuaTokenKind::TkWhitespace | LuaTokenKind::TkComma | LuaTokenKind::TkSemicolon,
104                ) => {}
105                LuaKind::Token(LuaTokenKind::TkEndOfLine)
106                | LuaKind::Syntax(LuaSyntaxKind::Comment) => {
107                    return None;
108                }
109                LuaKind::Token(k) if k != LuaTokenKind::TkName => {
110                    return comment.parent();
111                }
112                _ => match sibling {
113                    rowan::NodeOrToken::Node(node) => {
114                        return Some(node);
115                    }
116                    rowan::NodeOrToken::Token(token) => {
117                        return token.parent();
118                    }
119                },
120            }
121            prev_sibling = sibling.prev_sibling_or_token();
122        } else {
123            return None;
124        }
125    }
126}
127
128fn find_attached_node(comment: &LuaSyntaxNode) -> Option<LuaSyntaxNode> {
129    let mut meet_end_of_line = false;
130
131    let mut next_sibling = comment.next_sibling_or_token();
132    loop {
133        next_sibling.as_ref()?;
134
135        if let Some(sibling) = next_sibling {
136            match sibling.kind() {
137                LuaKind::Token(LuaTokenKind::TkEndOfLine) => {
138                    if meet_end_of_line {
139                        return None;
140                    }
141
142                    meet_end_of_line = true;
143                }
144                LuaKind::Token(LuaTokenKind::TkWhitespace) => {}
145                LuaKind::Syntax(LuaSyntaxKind::Comment) => {
146                    return None;
147                }
148                LuaKind::Syntax(LuaSyntaxKind::Block) => {
149                    let first_child = comment.first_child()?;
150                    if first_child.kind() == LuaKind::Syntax(LuaSyntaxKind::Comment) {
151                        return None;
152                    }
153                    return Some(first_child);
154                }
155                _ => match sibling {
156                    rowan::NodeOrToken::Node(node) => {
157                        return Some(node);
158                    }
159                    rowan::NodeOrToken::Token(token) => {
160                        return token.parent();
161                    }
162                },
163            }
164            next_sibling = sibling.next_sibling_or_token();
165        }
166    }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Hash)]
170pub struct LuaDocGenericDeclList {
171    syntax: LuaSyntaxNode,
172}
173
174impl LuaAstNode for LuaDocGenericDeclList {
175    fn syntax(&self) -> &LuaSyntaxNode {
176        &self.syntax
177    }
178
179    fn can_cast(kind: LuaSyntaxKind) -> bool
180    where
181        Self: Sized,
182    {
183        kind == LuaSyntaxKind::DocGenericDeclareList
184    }
185
186    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
187    where
188        Self: Sized,
189    {
190        if Self::can_cast(syntax.kind().into()) {
191            Some(Self { syntax })
192        } else {
193            None
194        }
195    }
196}
197
198impl LuaDocGenericDeclList {
199    pub fn get_generic_decl(&self) -> LuaAstChildren<LuaDocGenericDecl> {
200        self.children()
201    }
202}
203
204#[derive(Debug, Clone, PartialEq, Eq, Hash)]
205pub struct LuaDocGenericDecl {
206    syntax: LuaSyntaxNode,
207}
208
209impl LuaAstNode for LuaDocGenericDecl {
210    fn syntax(&self) -> &LuaSyntaxNode {
211        &self.syntax
212    }
213
214    fn can_cast(kind: LuaSyntaxKind) -> bool
215    where
216        Self: Sized,
217    {
218        kind == LuaSyntaxKind::DocGenericParameter
219    }
220
221    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
222    where
223        Self: Sized,
224    {
225        if Self::can_cast(syntax.kind().into()) {
226            Some(Self { syntax })
227        } else {
228            None
229        }
230    }
231}
232
233impl LuaDocGenericDecl {
234    pub fn get_name_token(&self) -> Option<LuaNameToken> {
235        self.token()
236    }
237
238    pub fn get_type(&self) -> Option<LuaDocType> {
239        self.child()
240    }
241
242    pub fn is_variadic(&self) -> bool {
243        self.token_by_kind(LuaTokenKind::TkDots).is_some()
244    }
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
248pub struct LuaDocTypeList {
249    syntax: LuaSyntaxNode,
250}
251
252impl LuaAstNode for LuaDocTypeList {
253    fn syntax(&self) -> &LuaSyntaxNode {
254        &self.syntax
255    }
256
257    fn can_cast(kind: LuaSyntaxKind) -> bool
258    where
259        Self: Sized,
260    {
261        kind == LuaSyntaxKind::DocTypeList
262    }
263
264    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
265    where
266        Self: Sized,
267    {
268        if Self::can_cast(syntax.kind().into()) {
269            Some(Self { syntax })
270        } else {
271            None
272        }
273    }
274}
275
276impl LuaDocTypeList {
277    pub fn get_types(&self) -> LuaAstChildren<LuaDocType> {
278        self.children()
279    }
280
281    pub fn get_return_type_list(&self) -> LuaAstChildren<LuaDocNamedReturnType> {
282        self.children()
283    }
284}
285
286#[derive(Debug, Clone, PartialEq, Eq, Hash)]
287pub struct LuaDocOpType {
288    syntax: LuaSyntaxNode,
289}
290
291impl LuaAstNode for LuaDocOpType {
292    fn syntax(&self) -> &LuaSyntaxNode {
293        &self.syntax
294    }
295
296    fn can_cast(kind: LuaSyntaxKind) -> bool
297    where
298        Self: Sized,
299    {
300        kind == LuaSyntaxKind::DocOpType
301    }
302
303    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
304    where
305        Self: Sized,
306    {
307        if Self::can_cast(syntax.kind().into()) {
308            Some(Self { syntax })
309        } else {
310            None
311        }
312    }
313}
314
315impl LuaDocOpType {
316    pub fn get_op(&self) -> Option<LuaBinaryOpToken> {
317        self.token()
318    }
319
320    pub fn get_type(&self) -> Option<LuaDocType> {
321        self.child()
322    }
323
324    pub fn is_nullable(&self) -> bool {
325        self.token_by_kind(LuaTokenKind::TkDocQuestion).is_some()
326    }
327}
328
329#[derive(Debug, Clone, PartialEq, Eq, Hash)]
330pub struct LuaDocObjectField {
331    syntax: LuaSyntaxNode,
332}
333
334impl LuaAstNode for LuaDocObjectField {
335    fn syntax(&self) -> &LuaSyntaxNode {
336        &self.syntax
337    }
338
339    fn can_cast(kind: LuaSyntaxKind) -> bool
340    where
341        Self: Sized,
342    {
343        kind == LuaSyntaxKind::DocObjectField
344    }
345
346    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
347    where
348        Self: Sized,
349    {
350        if Self::can_cast(syntax.kind().into()) {
351            Some(Self { syntax })
352        } else {
353            None
354        }
355    }
356}
357
358impl LuaDocObjectField {
359    pub fn get_field_key(&self) -> Option<LuaDocObjectFieldKey> {
360        for child in self.syntax.children_with_tokens() {
361            match child.kind() {
362                LuaKind::Token(LuaTokenKind::TkName) => {
363                    return LuaNameToken::cast(child.into_token().unwrap())
364                        .map(LuaDocObjectFieldKey::Name);
365                }
366                kind if LuaDocType::can_cast(kind.into()) => {
367                    let doc_type = LuaDocType::cast(child.into_node().unwrap())?;
368                    if let LuaDocType::Literal(literal) = &doc_type {
369                        let literal = literal.get_literal()?;
370                        match literal {
371                            LuaLiteralToken::Number(num) => {
372                                return Some(LuaDocObjectFieldKey::Integer(num));
373                            }
374                            LuaLiteralToken::String(str) => {
375                                return Some(LuaDocObjectFieldKey::String(str));
376                            }
377                            _ => {}
378                        }
379                    }
380
381                    return LuaDocObjectFieldKey::Type(doc_type).into();
382                }
383                LuaKind::Token(LuaTokenKind::TkColon) => {
384                    return None;
385                }
386                _ => {}
387            }
388        }
389
390        None
391    }
392
393    pub fn get_type(&self) -> Option<LuaDocType> {
394        self.children().last()
395    }
396
397    pub fn is_nullable(&self) -> bool {
398        self.token_by_kind(LuaTokenKind::TkDocQuestion).is_some()
399    }
400}
401
402#[derive(Debug, Clone, PartialEq, Eq, Hash)]
403pub enum LuaDocObjectFieldKey {
404    Name(LuaNameToken),
405    String(LuaStringToken),
406    Integer(LuaNumberToken),
407    Type(LuaDocType),
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, Hash)]
411pub struct LuaDocTypeFlag {
412    syntax: LuaSyntaxNode,
413}
414
415impl LuaAstNode for LuaDocTypeFlag {
416    fn syntax(&self) -> &LuaSyntaxNode {
417        &self.syntax
418    }
419
420    fn can_cast(kind: LuaSyntaxKind) -> bool
421    where
422        Self: Sized,
423    {
424        kind == LuaSyntaxKind::DocTypeFlag
425    }
426
427    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
428    where
429        Self: Sized,
430    {
431        if Self::can_cast(syntax.kind().into()) {
432            Some(Self { syntax })
433        } else {
434            None
435        }
436    }
437}
438
439impl LuaDocTypeFlag {
440    pub fn get_attrib_tokens(&self) -> LuaAstTokenChildren<LuaNameToken> {
441        self.tokens()
442    }
443}
444
445#[derive(Debug, Clone, PartialEq, Eq, Hash)]
446pub struct LuaDocNamedReturnType {
447    syntax: LuaSyntaxNode,
448}
449
450impl LuaAstNode for LuaDocNamedReturnType {
451    fn syntax(&self) -> &LuaSyntaxNode {
452        &self.syntax
453    }
454
455    fn can_cast(kind: LuaSyntaxKind) -> bool
456    where
457        Self: Sized,
458    {
459        kind == LuaSyntaxKind::DocNamedReturnType
460    }
461
462    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
463    where
464        Self: Sized,
465    {
466        if Self::can_cast(syntax.kind().into()) {
467            Some(Self { syntax })
468        } else {
469            None
470        }
471    }
472}
473
474impl LuaDocNamedReturnType {
475    pub fn get_name_and_type(&self) -> (Option<LuaNameToken>, Option<LuaDocType>) {
476        let types = self.children().collect::<Vec<LuaDocType>>();
477        if types.len() == 1 {
478            (None, Some(types[0].clone()))
479        } else if types.len() == 2 {
480            if let LuaDocType::Name(name) = &types[0] {
481                (name.get_name_token(), Some(types[1].clone()))
482            } else {
483                (None, None)
484            }
485        } else {
486            (None, None)
487        }
488    }
489}
490
491#[derive(Debug, Clone, PartialEq, Eq, Hash)]
492pub struct LuaDocAttributeUse {
493    syntax: LuaSyntaxNode,
494}
495
496impl LuaAstNode for LuaDocAttributeUse {
497    fn syntax(&self) -> &LuaSyntaxNode {
498        &self.syntax
499    }
500
501    fn can_cast(kind: LuaSyntaxKind) -> bool
502    where
503        Self: Sized,
504    {
505        kind == LuaSyntaxKind::DocAttributeUse
506    }
507
508    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
509    where
510        Self: Sized,
511    {
512        if Self::can_cast(syntax.kind().into()) {
513            Some(Self { syntax })
514        } else {
515            None
516        }
517    }
518}
519
520impl LuaDocAttributeUse {
521    pub fn get_type(&self) -> Option<LuaDocNameType> {
522        self.child()
523    }
524
525    pub fn get_arg_list(&self) -> Option<LuaDocAttributeCallArgList> {
526        self.child()
527    }
528}
529
530#[derive(Debug, Clone, PartialEq, Eq, Hash)]
531pub struct LuaDocAttributeCallArgList {
532    syntax: LuaSyntaxNode,
533}
534
535impl LuaAstNode for LuaDocAttributeCallArgList {
536    fn syntax(&self) -> &LuaSyntaxNode {
537        &self.syntax
538    }
539
540    fn can_cast(kind: LuaSyntaxKind) -> bool
541    where
542        Self: Sized,
543    {
544        kind == LuaSyntaxKind::DocAttributeCallArgList
545    }
546
547    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
548    where
549        Self: Sized,
550    {
551        if Self::can_cast(syntax.kind().into()) {
552            Some(Self { syntax })
553        } else {
554            None
555        }
556    }
557}
558
559impl LuaDocAttributeCallArgList {
560    pub fn get_args(&self) -> LuaAstChildren<LuaLiteralExpr> {
561        self.children()
562    }
563}
564
565#[derive(Debug, Clone, PartialEq, Eq, Hash)]
566pub struct LuaDocTagCallGeneric {
567    syntax: LuaSyntaxNode,
568}
569
570impl LuaAstNode for LuaDocTagCallGeneric {
571    fn syntax(&self) -> &LuaSyntaxNode {
572        &self.syntax
573    }
574
575    fn can_cast(kind: LuaSyntaxKind) -> bool
576    where
577        Self: Sized,
578    {
579        kind == LuaSyntaxKind::DocTagCallGeneric
580    }
581
582    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
583    where
584        Self: Sized,
585    {
586        if Self::can_cast(syntax.kind().into()) {
587            Some(Self { syntax })
588        } else {
589            None
590        }
591    }
592}
593
594impl LuaDocTagCallGeneric {
595    pub fn get_type_list(&self) -> Option<LuaDocTypeList> {
596        self.child()
597    }
598}