Skip to content

Commit a5b65f2

Browse files
committed
Handle trailing commas with comments
For lines/sequences of blocks which appear to be a comma-delimited list, do not consider comments when performing this check. Also, insert commas before comments to avoid mangling list. I kept the match/replace logic in lineGroup, as hasSuffix/trimSuffix were there. This approach does have a few weaknesses. It doesn't properly handle sorting what appear to be quoted comments, e.g.: - "a, #", - "b, //", - c It also doesn't properly handle commented commas: - a, # some comment, # more comment - b, # another comment, # a comment It is better at handling the desired cases, and the above counterexamples where it fails feel more like less-common edge cases. Lastly, it assumes "#" and "//" are always comments, and doesn't allow for alternatives. Handling the above would need quite a bit more work - likely parsing the lines themselves and adding list_delimiter and/or comment_delimiter options.
1 parent 4c4bb29 commit a5b65f2

4 files changed

Lines changed: 35 additions & 16 deletions

File tree

goldens/trailing_commas.out

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
Trailing comma except for last
22
keep-sorted-test start
33
1,
4-
2,
5-
3
4+
2,
5+
3
66
keep-sorted-test end
77

88

99
Trailing comma and a comment
1010
TODO: https://github.com/google/keep-sorted/issues/33 - Fix this
1111
keep-sorted-test start
1212
1,
13-
2
14-
3, # three
13+
2,
14+
3 # three
1515
keep-sorted-test end
1616

1717
Trailing comma except for last, last has a comment
1818
TODO: https://github.com/google/keep-sorted/issues/33 - Fix this
1919
keep-sorted-test start
2020
1,
21-
2 # two,
22-
3
21+
2, # two
22+
3
2323
keep-sorted-test end
2424

2525
Trailing comma and a comment, last has a comment
2626
TODO: https://github.com/google/keep-sorted/issues/33 - Fix this
2727
keep-sorted-test start
2828
1,
29-
2 # two
30-
3, # three
29+
2, # two
30+
3 # three
3131
keep-sorted-test end

keepsorted/block.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package keepsorted
1616

1717
import (
18+
"regexp"
1819
"slices"
1920
"strings"
2021

@@ -384,13 +385,13 @@ func handleTrailingComma(lgs []*lineGroup) (trimTrailingComma func([]*lineGroup)
384385
}
385386
}
386387

387-
if n := len(dataGroups); n > 1 && allHaveSuffix(dataGroups[0:n-1], ",") && !dataGroups[n-1].hasSuffix(",") {
388-
dataGroups[n-1].append(",")
388+
if n := len(dataGroups); n > 1 && allEndInComma(dataGroups[0:n-1]) && !endInComma(dataGroups[n-1]) {
389+
dataGroups[n-1].replaceSuffix(possibleCommentLineEnd, ", $2")
389390

390391
return func(lgs []*lineGroup) {
391392
for i := len(lgs) - 1; i >= 0; i-- {
392393
if len(lgs[i].lines) > 0 {
393-
lgs[i].trimSuffix(",")
394+
lgs[i].replaceSuffix(commaLineEnd, " $2")
394395
return
395396
}
396397
}
@@ -400,11 +401,20 @@ func handleTrailingComma(lgs []*lineGroup) (trimTrailingComma func([]*lineGroup)
400401
return func([]*lineGroup) {}
401402
}
402403

403-
func allHaveSuffix(lgs []*lineGroup, s string) bool {
404+
func allEndInComma(lgs []*lineGroup) bool {
404405
for _, lg := range lgs {
405-
if !lg.hasSuffix(s) {
406+
if !endInComma(lg) {
406407
return false
407408
}
408409
}
409410
return true
410411
}
412+
413+
var (
414+
commaLineEnd = regexp.MustCompile("(,)(\\s*(#|//).*)?$")
415+
possibleCommentLineEnd = regexp.MustCompile("( +)?((#|//).*)?$")
416+
)
417+
418+
func endInComma(lg *lineGroup) bool {
419+
return lg.matchesSuffix(commaLineEnd)
420+
}

keepsorted/line_group.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type accessRecorder struct {
6161
joinedComment bool
6262
}
6363

64-
// matchesAnyRegex returns true if s matches one of the regexes.
64+
// matchesAnyRegex returns true if s matchesSuffix one of the regexes.
6565
func matchesAnyRegex(s string, regexes []*regexp.Regexp) bool {
6666
for _, regex := range regexes {
6767
if regex.FindStringSubmatch(s) != nil {
@@ -119,7 +119,7 @@ func groupLines(lines []string, metadata blockMetadata) []*lineGroup {
119119
// Returns another boolean indicating whether the group should be ending
120120
// after that line if so.
121121
shouldAddToRegexDelimitedGroup := func(l string) (addToGroup bool, finishGroupAfter bool) {
122-
if metadata.opts.GroupStartRegex != nil {
122+
if metadata.opts.GroupStartRegex != nil {
123123
// For GroupStartRegex, all non-regex-matching lines should be
124124
// part of the group including prior lines.
125125
return !matchesAnyRegex(l, metadata.opts.GroupStartRegex), false
@@ -408,11 +408,20 @@ func (lg *lineGroup) hasSuffix(s string) bool {
408408
return len(lg.lines) > 0 && strings.HasSuffix(lg.lines[len(lg.lines)-1], s)
409409
}
410410

411+
func (lg *lineGroup) matchesSuffix(re *regexp.Regexp) bool {
412+
return len(lg.lines) > 0 && re.MatchString(lg.lines[len(lg.lines)-1])
413+
}
414+
411415
func (lg *lineGroup) trimSuffix(s string) {
412416
lg.access = accessRecorder{}
413417
lg.lines[len(lg.lines)-1] = strings.TrimSuffix(lg.lines[len(lg.lines)-1], s)
414418
}
415419

420+
func (lg *lineGroup) replaceSuffix(re *regexp.Regexp, replacement string) {
421+
lg.access = accessRecorder{}
422+
lg.lines[len(lg.lines)-1] = re.ReplaceAllString(lg.lines[len(lg.lines)-1], replacement)
423+
}
424+
416425
func (lg *lineGroup) commentOnly() bool {
417426
lg.access.commentOnly = true
418427
return len(lg.lines) == 0

keepsorted/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ func (opts blockOptions) hasGroupPrefix(s string) bool {
453453
}
454454

455455
// trimIgnorePrefix removes the first matching IgnorePrefixes from s, if s
456-
// matches one of the IgnorePrefixes.
456+
// matchesSuffix one of the IgnorePrefixes.
457457
func (opts blockOptions) trimIgnorePrefix(s string) string {
458458
_, s, _ = opts.cutFirstPrefix(s, slices.Values(opts.IgnorePrefixes))
459459
return s

0 commit comments

Comments
 (0)