@@ -43,26 +43,46 @@ func checkComments(comments []comment, settings Settings) []Issue {
43
43
var issues []Issue
44
44
for _ , c := range comments {
45
45
if settings .Period {
46
- if iss := checkCommentForPeriod (c ); iss != nil {
46
+ if iss := checkPeriod (c ); iss != nil {
47
47
issues = append (issues , * iss )
48
48
}
49
49
}
50
50
if settings .Capital {
51
- if iss := checkCommentForCapital (c ); len (iss ) > 0 {
51
+ if iss := checkCapital (c ); len (iss ) > 0 {
52
52
issues = append (issues , iss ... )
53
53
}
54
54
}
55
55
}
56
56
return issues
57
57
}
58
58
59
- // checkCommentForPeriod checks that the last sentense of the comment ends
59
+ // checkPeriod checks that the last sentense of the comment ends
60
60
// in a period.
61
- func checkCommentForPeriod (c comment ) * Issue {
62
- pos , ok := checkPeriod (c .text )
63
- if ok {
61
+ func checkPeriod (c comment ) * Issue {
62
+ // Check last non-empty line
63
+ var found bool
64
+ var line string
65
+ var pos position
66
+ lines := strings .Split (c .text , "\n " )
67
+ for i := len (lines ) - 1 ; i >= 0 ; i -- {
68
+ line = strings .TrimRightFunc (lines [i ], unicode .IsSpace )
69
+ if line == "" {
70
+ continue
71
+ }
72
+ found = true
73
+ pos .line = i + 1
74
+ break
75
+ }
76
+ // All lines are empty
77
+ if ! found {
64
78
return nil
65
79
}
80
+ // Correct line
81
+ if hasSuffix (line , lastChars ) {
82
+ return nil
83
+ }
84
+
85
+ pos .column = len (line ) + 1
66
86
67
87
// Shift position to its real value. `c.text` doesn't contain comment's
68
88
// special symbols: /* or //, and line indentations inside. It also
@@ -100,108 +120,30 @@ func checkCommentForPeriod(c comment) *Issue {
100
120
return & iss
101
121
}
102
122
103
- // checkCommentForCapital checks that each sentense of the comment starts with
104
- // a capital letter.
105
- // nolint: unparam
106
- func checkCommentForCapital (c comment ) []Issue {
107
- pp := checkCapital (c .text , c .decl )
108
- if len (pp ) == 0 {
109
- return nil
110
- }
111
-
112
- issues := make ([]Issue , len (pp ))
113
- for i , pos := range pp {
114
- // Shift position by the length of comment's special symbols: /* or //
115
- isBlock := strings .HasPrefix (c .lines [0 ], "/*" )
116
- if (isBlock && pos .line == 1 ) || ! isBlock {
117
- pos .column += 2
118
- }
119
-
120
- iss := Issue {
121
- Pos : token.Position {
122
- Filename : c .start .Filename ,
123
- Offset : c .start .Offset ,
124
- Line : pos .line + c .start .Line - 1 ,
125
- Column : pos .column + c .start .Column - 1 ,
126
- },
127
- Message : noCapitalMessage ,
128
- }
129
-
130
- // Make a replacement. Use `pos.original` to get an original original from
131
- // attached lines. Use `iss.Pos.Column` because it's a position in
132
- // the original original.
133
- original := c .lines [pos .line - 1 ]
134
- col := byteToRuneColumn (original , iss .Pos .Column ) - 1
135
- rep := string (unicode .ToTitle ([]rune (original )[col ])) // capital letter
136
- if len (original ) < iss .Pos .Column - 1 + len (rep ) {
137
- // This should never happen. Avoid panics, skip this check.
138
- continue
139
- }
140
- iss .Replacement = original [:iss .Pos .Column - 1 ] + rep +
141
- original [iss .Pos .Column - 1 + len (rep ):]
142
-
143
- // Save replacement to raw lines to be able to combine it with
144
- // further replacements
145
- c .lines [pos .line - 1 ] = iss .Replacement
146
-
147
- issues [i ] = iss
148
- }
149
-
150
- return issues
151
- }
152
-
153
- // checkPeriod checks that the last sentense of the text ends in a period.
154
- // NOTE: Returned position is a position inside given text, not in the
155
- // original file.
156
- func checkPeriod (comment string ) (pos position , ok bool ) {
157
- // Check last non-empty line
158
- var found bool
159
- var line string
160
- lines := strings .Split (comment , "\n " )
161
- for i := len (lines ) - 1 ; i >= 0 ; i -- {
162
- line = strings .TrimRightFunc (lines [i ], unicode .IsSpace )
163
- if line == "" {
164
- continue
165
- }
166
- found = true
167
- pos .line = i + 1
168
- break
169
- }
170
- // All lines are empty
171
- if ! found {
172
- return position {}, true
173
- }
174
- // Correct line
175
- if hasSuffix (line , lastChars ) {
176
- return position {}, true
177
- }
178
-
179
- pos .column = len (line ) + 1
180
- return pos , false
181
- }
182
-
183
- // checkCapital checks that each sentense of the text starts with
123
+ // checkCapital checks that each sentense of the comment starts with
184
124
// a capital letter.
185
- // NOTE: First letter is not checked in declaration comments, because they
186
- // can describe unexported functions, which start with small letter.
187
- func checkCapital (comment string , skipFirst bool ) ( pp [] position ) {
125
+ //
126
+ //nolint:cyclop,funlen
127
+ func checkCapital (c comment ) [] Issue {
188
128
// Remove common abbreviations from the comment
189
129
for _ , abbr := range abbreviations {
190
130
repl := strings .ReplaceAll (abbr , "." , "_" )
191
- comment = strings .ReplaceAll (comment , abbr , repl )
131
+ c . text = strings .ReplaceAll (c . text , abbr , repl )
192
132
}
193
133
194
134
// List of states during the scan: `empty` - nothing special,
195
135
// `endChar` - found one of sentence ending chars (.!?),
196
136
// `endOfSentence` - found `endChar`, and then space or newline.
197
137
const empty , endChar , endOfSentence = 1 , 2 , 3
198
138
139
+ var pp []position
199
140
pos := position {line : 1 }
200
141
state := endOfSentence
201
- if skipFirst {
142
+ if c .decl {
143
+ // Skip first
202
144
state = empty
203
145
}
204
- for _ , r := range comment {
146
+ for _ , r := range c . text {
205
147
s := string (r )
206
148
207
149
pos .column ++
@@ -229,12 +171,54 @@ func checkCapital(comment string, skipFirst bool) (pp []position) {
229
171
if state == endOfSentence && unicode .IsLower (r ) {
230
172
pp = append (pp , position {
231
173
line : pos .line ,
232
- column : runeToByteColumn (comment , pos .column ),
174
+ column : runeToByteColumn (c . text , pos .column ),
233
175
})
234
176
}
235
177
state = empty
236
178
}
237
- return pp
179
+ if len (pp ) == 0 {
180
+ return nil
181
+ }
182
+
183
+ issues := make ([]Issue , len (pp ))
184
+ for i , pos := range pp {
185
+ // Shift position by the length of comment's special symbols: /* or //
186
+ isBlock := strings .HasPrefix (c .lines [0 ], "/*" )
187
+ if (isBlock && pos .line == 1 ) || ! isBlock {
188
+ pos .column += 2
189
+ }
190
+
191
+ iss := Issue {
192
+ Pos : token.Position {
193
+ Filename : c .start .Filename ,
194
+ Offset : c .start .Offset ,
195
+ Line : pos .line + c .start .Line - 1 ,
196
+ Column : pos .column + c .start .Column - 1 ,
197
+ },
198
+ Message : noCapitalMessage ,
199
+ }
200
+
201
+ // Make a replacement. Use `pos.original` to get an original original from
202
+ // attached lines. Use `iss.Pos.Column` because it's a position in
203
+ // the original original.
204
+ original := c .lines [pos .line - 1 ]
205
+ col := byteToRuneColumn (original , iss .Pos .Column ) - 1
206
+ rep := string (unicode .ToTitle ([]rune (original )[col ])) // capital letter
207
+ if len (original ) < iss .Pos .Column - 1 + len (rep ) {
208
+ // This should never happen. Avoid panics, skip this check.
209
+ continue
210
+ }
211
+ iss .Replacement = original [:iss .Pos .Column - 1 ] + rep +
212
+ original [iss .Pos .Column - 1 + len (rep ):]
213
+
214
+ // Save replacement to raw lines to be able to combine it with
215
+ // further replacements
216
+ c .lines [pos .line - 1 ] = iss .Replacement
217
+
218
+ issues [i ] = iss
219
+ }
220
+
221
+ return issues
238
222
}
239
223
240
224
// isSpecialBlock checks that given block of comment lines is special and
0 commit comments