11part of 'format_comment_rule.dart' ;
22
3- const _commentsOperator = {
4- _CommentType .base : '//' ,
5- _CommentType .documentation: '///' ,
6- };
3+ const _punctuation = ['.' , '!' , '?' , ':' ];
74
5+ final _sentencesRegExp = RegExp (r'(?<=([\.|:](?=\s|\n|$)))' );
86final _regMacrosExp = RegExp ('{@(template|macro) .+}' );
97const _macrosEndExp = '{@endtemplate}' ;
108const _ignoreExp = 'ignore:' ;
119const _ignoreForFileExp = 'ignore_for_file:' ;
1210
1311class _Visitor extends RecursiveAstVisitor <void > {
1412 final Iterable <RegExp > _ignoredPatterns;
13+
1514 final bool _onlyDocComments;
1615
1716 // ignore: avoid_positional_boolean_parameters
@@ -21,15 +20,59 @@ class _Visitor extends RecursiveAstVisitor<void> {
2120
2221 Iterable <_CommentInfo > get comments => _comments;
2322
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+
2542 Token ? token = node.beginToken;
2643 while (token != null ) {
44+ final extractedComments = < Token > [];
45+
2746 Token ? commentToken = token.precedingComments;
2847 while (commentToken != null ) {
29- _commentValidation (commentToken);
48+ if (_isRegularComment (commentToken)) {
49+ extractedComments.add (commentToken);
50+ }
3051 commentToken = commentToken.next;
3152 }
3253
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+
3376 if (token == token.next) {
3477 break ;
3578 }
@@ -38,48 +81,99 @@ class _Visitor extends RecursiveAstVisitor<void> {
3881 }
3982 }
4083
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 ('//' );
5088 }
5189
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 );
5593
56- var text = commentText.trim ();
94+ return sentences.every (_isValidSentence);
95+ }
5796
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 ();
60100
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+ }
62107
63- final isAnIgnoredPattern = _ignoredPatterns.any (
64- (regExp) => regExp.hasMatch (text),
65- );
108+ return _isValidSentence (commentText);
109+ }
66110
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 ();
78113
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;
82131 }
83132 }
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);
84144 }
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 ('///' );
85179}
0 commit comments