Skip to content

Commit

Permalink
Refactor check functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Krivak committed Sep 9, 2023
1 parent 9f6abf7 commit d0ebfb3
Show file tree
Hide file tree
Showing 2 changed files with 355 additions and 200 deletions.
174 changes: 79 additions & 95 deletions checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,46 @@ func checkComments(comments []comment, settings Settings) []Issue {
var issues []Issue
for _, c := range comments {
if settings.Period {
if iss := checkCommentForPeriod(c); iss != nil {
if iss := checkPeriod(c); iss != nil {
issues = append(issues, *iss)
}
}
if settings.Capital {
if iss := checkCommentForCapital(c); len(iss) > 0 {
if iss := checkCapital(c); len(iss) > 0 {
issues = append(issues, iss...)
}
}
}
return issues
}

// checkCommentForPeriod checks that the last sentense of the comment ends
// checkPeriod checks that the last sentense of the comment ends
// in a period.
func checkCommentForPeriod(c comment) *Issue {
pos, ok := checkPeriod(c.text)
if ok {
func checkPeriod(c comment) *Issue {
// Check last non-empty line
var found bool
var line string
var pos position
lines := strings.Split(c.text, "\n")
for i := len(lines) - 1; i >= 0; i-- {
line = strings.TrimRightFunc(lines[i], unicode.IsSpace)
if line == "" {
continue
}
found = true
pos.line = i + 1
break
}
// All lines are empty
if !found {
return nil
}
// Correct line
if hasSuffix(line, lastChars) {
return nil
}

pos.column = len(line) + 1

// Shift position to its real value. `c.text` doesn't contain comment's
// special symbols: /* or //, and line indentations inside. It also
Expand Down Expand Up @@ -100,108 +120,30 @@ func checkCommentForPeriod(c comment) *Issue {
return &iss
}

// checkCommentForCapital checks that each sentense of the comment starts with
// a capital letter.
// nolint: unparam
func checkCommentForCapital(c comment) []Issue {
pp := checkCapital(c.text, c.decl)
if len(pp) == 0 {
return nil
}

issues := make([]Issue, len(pp))
for i, pos := range pp {
// Shift position by the length of comment's special symbols: /* or //
isBlock := strings.HasPrefix(c.lines[0], "/*")
if (isBlock && pos.line == 1) || !isBlock {
pos.column += 2
}

iss := Issue{
Pos: token.Position{
Filename: c.start.Filename,
Offset: c.start.Offset,
Line: pos.line + c.start.Line - 1,
Column: pos.column + c.start.Column - 1,
},
Message: noCapitalMessage,
}

// Make a replacement. Use `pos.original` to get an original original from
// attached lines. Use `iss.Pos.Column` because it's a position in
// the original original.
original := c.lines[pos.line-1]
col := byteToRuneColumn(original, iss.Pos.Column) - 1
rep := string(unicode.ToTitle([]rune(original)[col])) // capital letter
if len(original) < iss.Pos.Column-1+len(rep) {
// This should never happen. Avoid panics, skip this check.
continue
}
iss.Replacement = original[:iss.Pos.Column-1] + rep +
original[iss.Pos.Column-1+len(rep):]

// Save replacement to raw lines to be able to combine it with
// further replacements
c.lines[pos.line-1] = iss.Replacement

issues[i] = iss
}

return issues
}

// checkPeriod checks that the last sentense of the text ends in a period.
// NOTE: Returned position is a position inside given text, not in the
// original file.
func checkPeriod(comment string) (pos position, ok bool) {
// Check last non-empty line
var found bool
var line string
lines := strings.Split(comment, "\n")
for i := len(lines) - 1; i >= 0; i-- {
line = strings.TrimRightFunc(lines[i], unicode.IsSpace)
if line == "" {
continue
}
found = true
pos.line = i + 1
break
}
// All lines are empty
if !found {
return position{}, true
}
// Correct line
if hasSuffix(line, lastChars) {
return position{}, true
}

pos.column = len(line) + 1
return pos, false
}

// checkCapital checks that each sentense of the text starts with
// checkCapital checks that each sentense of the comment starts with
// a capital letter.
// NOTE: First letter is not checked in declaration comments, because they
// can describe unexported functions, which start with small letter.
func checkCapital(comment string, skipFirst bool) (pp []position) {
//
//nolint:cyclop,funlen
func checkCapital(c comment) []Issue {
// Remove common abbreviations from the comment
for _, abbr := range abbreviations {
repl := strings.ReplaceAll(abbr, ".", "_")
comment = strings.ReplaceAll(comment, abbr, repl)
c.text = strings.ReplaceAll(c.text, abbr, repl)
}

// List of states during the scan: `empty` - nothing special,
// `endChar` - found one of sentence ending chars (.!?),
// `endOfSentence` - found `endChar`, and then space or newline.
const empty, endChar, endOfSentence = 1, 2, 3

var pp []position
pos := position{line: 1}
state := endOfSentence
if skipFirst {
if c.decl {
// Skip first
state = empty
}
for _, r := range comment {
for _, r := range c.text {
s := string(r)

pos.column++
Expand Down Expand Up @@ -229,12 +171,54 @@ func checkCapital(comment string, skipFirst bool) (pp []position) {
if state == endOfSentence && unicode.IsLower(r) {
pp = append(pp, position{
line: pos.line,
column: runeToByteColumn(comment, pos.column),
column: runeToByteColumn(c.text, pos.column),
})
}
state = empty
}
return pp
if len(pp) == 0 {
return nil
}

issues := make([]Issue, len(pp))
for i, pos := range pp {
// Shift position by the length of comment's special symbols: /* or //
isBlock := strings.HasPrefix(c.lines[0], "/*")
if (isBlock && pos.line == 1) || !isBlock {
pos.column += 2
}

iss := Issue{
Pos: token.Position{
Filename: c.start.Filename,
Offset: c.start.Offset,
Line: pos.line + c.start.Line - 1,
Column: pos.column + c.start.Column - 1,
},
Message: noCapitalMessage,
}

// Make a replacement. Use `pos.original` to get an original original from
// attached lines. Use `iss.Pos.Column` because it's a position in
// the original original.
original := c.lines[pos.line-1]
col := byteToRuneColumn(original, iss.Pos.Column) - 1
rep := string(unicode.ToTitle([]rune(original)[col])) // capital letter
if len(original) < iss.Pos.Column-1+len(rep) {
// This should never happen. Avoid panics, skip this check.
continue
}
iss.Replacement = original[:iss.Pos.Column-1] + rep +
original[iss.Pos.Column-1+len(rep):]

// Save replacement to raw lines to be able to combine it with
// further replacements
c.lines[pos.line-1] = iss.Replacement

issues[i] = iss
}

return issues
}

// isSpecialBlock checks that given block of comment lines is special and
Expand Down

0 comments on commit d0ebfb3

Please sign in to comment.