1
1
part of 'format_comment_rule.dart' ;
2
2
3
- const _commentsOperator = {
4
- _CommentType .base : '//' ,
5
- _CommentType .documentation: '///' ,
6
- };
3
+ const _punctuation = ['.' , '!' , '?' , ':' ];
7
4
5
+ final _sentencesRegExp = RegExp (r'(?<=([\.|:](?=\s|\n|$)))' );
8
6
final _regMacrosExp = RegExp ('{@(template|macro) .+}' );
9
7
const _macrosEndExp = '{@endtemplate}' ;
10
8
const _ignoreExp = 'ignore:' ;
11
9
const _ignoreForFileExp = 'ignore_for_file:' ;
12
10
13
11
class _Visitor extends RecursiveAstVisitor <void > {
14
12
final Iterable <RegExp > _ignoredPatterns;
13
+
15
14
final bool _onlyDocComments;
16
15
17
16
// ignore: avoid_positional_boolean_parameters
@@ -21,15 +20,59 @@ class _Visitor extends RecursiveAstVisitor<void> {
21
20
22
21
Iterable <_CommentInfo > get comments => _comments;
23
22
24
- void checkComments (AstNode node) {
23
+ @override
24
+ void visitComment (Comment node) {
25
+ super .visitComment (node);
26
+
27
+ if (node.isDocumentation) {
28
+ final isValid = node.tokens.length == 1
29
+ ? _hasValidSingleLine (node.tokens.first, _CommentType .doc)
30
+ : _hasValidMultiline (node.tokens, _CommentType .doc);
31
+ if (! isValid) {
32
+ _comments.add (_DocCommentInfo (node));
33
+ }
34
+ }
35
+ }
36
+
37
+ void checkRegularComments (AstNode node) {
38
+ if (_onlyDocComments) {
39
+ return ;
40
+ }
41
+
25
42
Token ? token = node.beginToken;
26
43
while (token != null ) {
44
+ final extractedComments = < Token > [];
45
+
27
46
Token ? commentToken = token.precedingComments;
28
47
while (commentToken != null ) {
29
- _commentValidation (commentToken);
48
+ if (_isRegularComment (commentToken)) {
49
+ extractedComments.add (commentToken);
50
+ }
30
51
commentToken = commentToken.next;
31
52
}
32
53
54
+ if (extractedComments.isNotEmpty) {
55
+ final isValid = extractedComments.length > 1
56
+ ? _hasValidMultiline (extractedComments, _CommentType .regular)
57
+ : _hasValidSingleLine (
58
+ extractedComments.first,
59
+ _CommentType .regular,
60
+ );
61
+ if (! isValid) {
62
+ final notIgnored = extractedComments.where ((comment) {
63
+ final trimmed = comment
64
+ .toString ()
65
+ .replaceAll (_CommentType .regular.pattern, '' )
66
+ .trim ();
67
+
68
+ return ! _isIgnoreComment (trimmed) && ! _isIgnoredPattern (trimmed);
69
+ }).toList ();
70
+ _comments.add (_RegularCommentInfo (notIgnored));
71
+ }
72
+
73
+ extractedComments.clear ();
74
+ }
75
+
33
76
if (token == token.next) {
34
77
break ;
35
78
}
@@ -38,48 +81,99 @@ class _Visitor extends RecursiveAstVisitor<void> {
38
81
}
39
82
}
40
83
41
- void _commentValidation (Token commentToken) {
42
- if (commentToken.type == TokenType .SINGLE_LINE_COMMENT ) {
43
- final token = commentToken.toString ();
44
- if (token.startsWith ('///' )) {
45
- _checkCommentByType (commentToken, _CommentType .documentation);
46
- } else if (token.startsWith ('//' ) && ! _onlyDocComments) {
47
- _checkCommentByType (commentToken, _CommentType .base );
48
- }
49
- }
84
+ bool _isRegularComment (Token commentToken) {
85
+ final token = commentToken.toString ();
86
+
87
+ return ! token.startsWith ('///' ) && token.startsWith ('//' );
50
88
}
51
89
52
- void _checkCommentByType ( Token commentToken , _CommentType type) {
53
- final commentText =
54
- commentToken. toString (). substring (_commentsOperator[type] ! .length );
90
+ bool _hasValidMultiline ( List < Token > commentTokens , _CommentType type) {
91
+ final text = _extractText (commentTokens, type);
92
+ final sentences = text. split (_sentencesRegExp );
55
93
56
- var text = commentText.trim ();
94
+ return sentences.every (_isValidSentence);
95
+ }
57
96
58
- final isIgnoreComment =
59
- text.startsWith (_ignoreExp) || text.startsWith (_ignoreForFileExp);
97
+ bool _hasValidSingleLine (Token commentToken, _CommentType type) {
98
+ final commentText = commentToken.toString ().substring (type.pattern.length);
99
+ final text = commentText.trim ();
60
100
61
- final isMacros = _regMacrosExp.hasMatch (text) || text == _macrosEndExp;
101
+ if (text.isEmpty ||
102
+ _isIgnoreComment (text) ||
103
+ _isMacros (text) ||
104
+ _isIgnoredPattern (text)) {
105
+ return true ;
106
+ }
62
107
63
- final isAnIgnoredPattern = _ignoredPatterns.any (
64
- (regExp) => regExp.hasMatch (text),
65
- );
108
+ return _isValidSentence (commentText);
109
+ }
66
110
67
- {
68
- if (text.isEmpty || isIgnoreComment || isMacros || isAnIgnoredPattern) {
69
- return ;
70
- } else {
71
- text = text.trim ();
72
- final upperCase = text[0 ] == text[0 ].toUpperCase ();
73
- final lastSymbol = _punctuation.contains (text[text.length - 1 ]);
74
- final hasEmptySpace = commentText[0 ] == ' ' ;
75
- final incorrectFormat = ! upperCase || ! hasEmptySpace || ! lastSymbol;
76
- final single =
77
- commentToken.previous == null && commentToken.next == null ;
111
+ bool _isValidSentence (String sentence) {
112
+ final trimmedSentence = sentence.trim ();
78
113
79
- if (incorrectFormat && single) {
80
- _comments.add (_CommentInfo (type, commentToken));
81
- }
114
+ final upperCase = trimmedSentence[0 ] == trimmedSentence[0 ].toUpperCase ();
115
+ final lastSymbol =
116
+ _punctuation.contains (trimmedSentence[trimmedSentence.length - 1 ]);
117
+ final hasEmptySpace = sentence[0 ] == ' ' ;
118
+
119
+ return upperCase && lastSymbol && hasEmptySpace;
120
+ }
121
+
122
+ String _extractText (List <Token > commentTokens, _CommentType type) {
123
+ var result = '' ;
124
+ var shouldSkipNext = false ;
125
+ for (final token in commentTokens) {
126
+ final commentText = token.toString ().replaceAll (type.pattern, '' );
127
+ if (commentText.contains ('```' )) {
128
+ shouldSkipNext = ! shouldSkipNext;
129
+ } else if (! _shouldSkip (commentText) && ! shouldSkipNext) {
130
+ result += commentText;
82
131
}
83
132
}
133
+
134
+ return result;
135
+ }
136
+
137
+ bool _shouldSkip (String text) {
138
+ final trimmed = text.trim ();
139
+
140
+ return _regMacrosExp.hasMatch (text) ||
141
+ text.contains (_macrosEndExp) ||
142
+ _isIgnoreComment (trimmed) ||
143
+ _isIgnoredPattern (trimmed);
84
144
}
145
+
146
+ bool _isIgnoreComment (String text) =>
147
+ text.startsWith (_ignoreExp) || text.startsWith (_ignoreForFileExp);
148
+
149
+ bool _isMacros (String text) =>
150
+ _regMacrosExp.hasMatch (text) || text == _macrosEndExp;
151
+
152
+ bool _isIgnoredPattern (String text) =>
153
+ _ignoredPatterns.any ((regExp) => regExp.hasMatch (text));
154
+ }
155
+
156
+ abstract class _CommentInfo {
157
+ const _CommentInfo ();
158
+ }
159
+
160
+ class _DocCommentInfo extends _CommentInfo {
161
+ final Comment comment;
162
+
163
+ const _DocCommentInfo (this .comment);
164
+ }
165
+
166
+ class _RegularCommentInfo extends _CommentInfo {
167
+ final List <Token > tokens;
168
+
169
+ const _RegularCommentInfo (this .tokens);
170
+ }
171
+
172
+ class _CommentType {
173
+ final String pattern;
174
+
175
+ const _CommentType (this .pattern);
176
+
177
+ static const regular = _CommentType ('//' );
178
+ static const doc = _CommentType ('///' );
85
179
}
0 commit comments