diff --git a/README.md b/README.md index e2f80be..2306565 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,47 @@ allows for sorting data such as Go structs and JSON objects. > Note: angle brackets (`<` and `>`) are not supported by block mode due to > being used for mathematical expressions in an unbalanced format. +#### Custom grouping + +Another way to group lines together is with the `group_prefixes` argument. This +takes a comma-separated list of prefixes. Any line beginning with one of those +prefixes will be treated as a continuation line. + + + + + + +
+ +``` + +spaghetti +with meatballs +peanut butter +and jelly +hamburger +with lettuce +and tomatoes + +``` + + + +```diff ++// keep-sorted start group_prefixes=and,with + hamburger + with lettuce + and tomatoes + peanut butter + and jelly + spaghetti + with meatballs ++// keep-sorted end +``` + +
+ #### Comments Comments embedded within the sorted block are made to stick with their diff --git a/goldens/skip_lines.in b/goldens/skip_lines.in index 1d1bea8..947f7f5 100644 --- a/goldens/skip_lines.in +++ b/goldens/skip_lines.in @@ -34,3 +34,25 @@ This line should not be sorted. Charlie | Baz + +Skip lines with group_prefixes: +// keep-sorted-test start skip_lines=2 group_prefixes=: +| birds | +| ------------ | +| blue footed | +: booby | +| crested | +: bobwhite | +| anhinga | +// keep-sorted-test end + +Skip lines with group_prefixes indented: +// keep-sorted-test start skip_lines=2 group_prefixes=: + | birds | + | ------------ | + | blue footed | + : booby | + | crested | + : bobwhite | + | anhinga | +// keep-sorted-test end diff --git a/goldens/skip_lines.out b/goldens/skip_lines.out index 0ed51af..1954d72 100644 --- a/goldens/skip_lines.out +++ b/goldens/skip_lines.out @@ -34,3 +34,25 @@ This line should not be sorted. Bravo | Foxtrot + +Skip lines with group_prefixes: +// keep-sorted-test start skip_lines=2 group_prefixes=: +| birds | +| ------------ | +| anhinga | +| blue footed | +: booby | +| crested | +: bobwhite | +// keep-sorted-test end + +Skip lines with group_prefixes indented: +// keep-sorted-test start skip_lines=2 group_prefixes=: + | birds | + | ------------ | + | anhinga | + | blue footed | + : booby | + | crested | + : bobwhite | +// keep-sorted-test end diff --git a/keepsorted/keep_sorted_test.go b/keepsorted/keep_sorted_test.go index b46a27f..2a4aa64 100644 --- a/keepsorted/keep_sorted_test.go +++ b/keepsorted/keep_sorted_test.go @@ -29,6 +29,7 @@ var ( Lint: true, SkipLines: 0, Group: true, + GroupPrefixes: nil, IgnorePrefixes: nil, Numeric: false, StickyComments: true, @@ -1266,6 +1267,33 @@ func TestLineGrouping(t *testing.T) { }}, }, }, + { + name: "Group_Prefixes", + opts: defaultOptionsWith(func(opts *blockOptions) { + opts.GroupPrefixes = map[string]bool{"and": true, "with": true} + opts.Block = false + }), + + want: []lineGroup{ + {nil, []string{ + "peanut butter", + "and jelly", + }}, + {nil, []string{ + "spaghetti", + "with meatballs", + }}, + {nil, []string{ + "hamburger", + " with lettuce", + " and tomatoes", + "and cheese", + }}, + {nil, []string{ + "dogs and cats", + }}, + }, + }, { name: "Group_UnindentedNewlines", opts: defaultOptionsWith(func(opts *blockOptions) { @@ -1567,6 +1595,15 @@ func TestBlockOptions(t *testing.T) { opts.IgnorePrefixes = []string{"a", "b", "c", "d"} }), }, + { + name: "GroupPrefixesRequiresGrouping", + in: "// keep-sorted-test group_prefixes=a,b,c group=no", + + want: defaultOptionsWith(func(opts *blockOptions) { + opts.Group = false + }), + wantErr: "group_prefixes may not be used with group=no", + }, { name: "ignore_prefixes_ChecksLognestPrefixesFirst", in: "// keep-sorted-test ignore_prefixes=DoSomething(,DoSomething({", diff --git a/keepsorted/line_group.go b/keepsorted/line_group.go index 5d56c56..ccf5844 100644 --- a/keepsorted/line_group.go +++ b/keepsorted/line_group.go @@ -82,6 +82,8 @@ func groupLines(lines []string, metadata blockMetadata) []lineGroup { appendLine(i, l) } else if metadata.opts.Group && (!lineRange.empty() && initialIndent != nil && indents[i] > *initialIndent || numUnmatchedStartDirectives > 0) { appendLine(i, l) + } else if metadata.opts.Group && metadata.opts.hasGroupPrefix(l) { + appendLine(i, l) } else if metadata.opts.hasStickyPrefix(l) { if !lineRange.empty() { finishGroup() diff --git a/keepsorted/options.go b/keepsorted/options.go index b5e3c97..b0ac606 100644 --- a/keepsorted/options.go +++ b/keepsorted/options.go @@ -56,6 +56,8 @@ type blockOptions struct { SkipLines int `key:"skip_lines"` // Group determines whether we group lines together based on increasing indentation. Group bool `default:"true"` + // GroupPrefixes tells us about other types of lines that should be added to a group. + GroupPrefixes map[string]bool `key:"group_prefixes"` // Block opts us into a more complicated algorithm to try and understand blocks of code. Block bool `default:"false"` // StickyComments tells us to attach comments to the line immediately below them while sorting. @@ -114,6 +116,11 @@ func (f *Fixer) parseBlockOptions(startLine string) (blockOptions, error) { ret.SkipLines = 0 } + if ret.GroupPrefixes != nil && !ret.Group { + errs = errors.Join(errs, fmt.Errorf("group_prefixes may not be used with group=no")) + ret.GroupPrefixes = nil + } + if cm := f.guessCommentMarker(startLine); cm != "" { ret.commentMarker = cm if ret.StickyComments { @@ -216,10 +223,13 @@ func (f *Fixer) guessCommentMarker(startLine string) string { return "" } -// hasStickyPrefix determines if s has one of the StickyPrefixes. -func (opts blockOptions) hasStickyPrefix(s string) bool { +// hasPrefix determines if s has one of the prefixes. +func hasPrefix(s string, prefixes map[string]bool) bool { + if len(prefixes) == 0 { + return false + } s = strings.TrimLeftFunc(s, unicode.IsSpace) - for p := range opts.StickyPrefixes { + for p := range prefixes { if strings.HasPrefix(s, p) { return true } @@ -227,6 +237,16 @@ func (opts blockOptions) hasStickyPrefix(s string) bool { return false } +// hasStickyPrefix determines if s has one of the StickyPrefixes. +func (opts blockOptions) hasStickyPrefix(s string) bool { + return hasPrefix(s, opts.StickyPrefixes) +} + +// hasGroupPrefix determines if s has one of the GroupPrefixes. +func (opts blockOptions) hasGroupPrefix(s string) bool { + return hasPrefix(s, opts.GroupPrefixes) +} + // removeIgnorePrefix removes the first matching IgnorePrefixes from s, if s // matches one of the IgnorePrefixes. func (opts blockOptions) removeIgnorePrefix(s string) (string, bool) {