Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,38 @@ This can also be combined with numerical sorting:
]
```

#### Sort order

By default, keep-sorted sorts in ascending order. This can be changed to
descending order using `order=desc`:

<table border="0">
<tr>
<td>

```
# keep-sorted start
a
b
c
# keep-sorted end
```

</td>
<td>

```diff
+# keep-sorted start order=desc
c
b
a
# keep-sorted end
```

</td>
</tr>
</table>

### Post-sorting options

Post-sorting options are additional convenience features that make the resulting
Expand Down
14 changes: 14 additions & 0 deletions goldens/simple.in
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,17 @@ Invalid YAML
1
3
keep-sorted-test end

Ascending order (default):
// keep-sorted-test start
c
b
a
// keep-sorted-test end

Descending order:
// keep-sorted-test start order=desc
a
b
c
// keep-sorted-test end
14 changes: 14 additions & 0 deletions goldens/simple.out
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,17 @@ Invalid YAML
2
3
keep-sorted-test end

Ascending order (default):
// keep-sorted-test start
a
b
c
// keep-sorted-test end

Descending order:
// keep-sorted-test start order=desc
c
b
a
// keep-sorted-test end
9 changes: 7 additions & 2 deletions keepsorted/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,12 @@ func (b block) sorted() (sorted []string, alreadySorted bool) {
groups = deduped
}

if alreadySorted && wasNewlineSeparated && !removedDuplicate && slices.IsSortedFunc(groups, compareLineGroups) {
cmp := compareLineGroups
if b.metadata.opts.Order == OrderDesc {
cmp = cmp.reversed()
}

if alreadySorted && wasNewlineSeparated && !removedDuplicate && slices.IsSortedFunc(groups, cmp) {
log.Printf("It was already sorted!")
if log.Debug().Enabled() {
for _, lg := range groups {
Expand All @@ -278,7 +283,7 @@ func (b block) sorted() (sorted []string, alreadySorted bool) {
return lines, true
}

slices.SortStableFunc(groups, compareLineGroups)
slices.SortStableFunc(groups, cmp)
if log.Debug().Enabled() {
for _, lg := range groups {
log.Print(lg.DebugString())
Expand Down
39 changes: 39 additions & 0 deletions keepsorted/keep_sorted_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,45 @@ func TestLineSorting(t *testing.T) {
},
wantAlreadySorted: true,
},
{
name: "Descending",
opts: blockOptions{
Order: OrderDesc,
},
in: []string{
"Bar",
"Baz",
"Foo",
"Qux",
},

want: []string{
"Qux",
"Foo",
"Baz",
"Bar",
},
},
{
name: "AlreadySorted_Descending",
opts: blockOptions{
Order: OrderDesc,
},
in: []string{
"Qux",
"Foo",
"Baz",
"Bar",
},

want: []string{
"Qux",
"Foo",
"Baz",
"Bar",
},
wantAlreadySorted: true,
},
{
name: "AlreadySorted_ExceptForNewlineSorted",

Expand Down
15 changes: 14 additions & 1 deletion keepsorted/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ type ByRegexOption struct {
Template *string
}

// SortOrder defines whether we sort in ascending or descending order.
type SortOrder string

const (
OrderAsc SortOrder = "asc"
OrderDesc SortOrder = "desc"
)

type BlockOptions struct {
opts blockOptions
}
Expand Down Expand Up @@ -93,6 +101,8 @@ type blockOptions struct {
// Sorting options //
///////////////////////

// Order is whether we sort in ascending or descending order.
Order SortOrder `key:"order"`
// CaseSensitive is whether we're case sensitive while sorting.
CaseSensitive bool `key:"case"`
// Numeric indicates that the contents should be sorted like numbers.
Expand Down Expand Up @@ -127,6 +137,7 @@ var (
Group: true,
StickyComments: true,
StickyPrefixes: nil, // Will be populated with the comment marker of the start directive.
Order: OrderAsc,
CaseSensitive: true,
RemoveDuplicates: true,
}
Expand Down Expand Up @@ -202,6 +213,8 @@ func formatValue(val reflect.Value) (string, error) {
switch val.Type() {
case reflect.TypeFor[bool]():
return boolString[val.Bool()], nil
case reflect.TypeFor[SortOrder]():
return string(val.Interface().(SortOrder)), nil
case reflect.TypeFor[[]string]():
return formatList(val.Interface().([]string))
case reflect.TypeFor[map[string]bool]():
Expand Down Expand Up @@ -359,7 +372,7 @@ func (opts blockOptions) cutFirstPrefix(s string, prefixes iter.Seq[string]) (pr
}
if strings.HasPrefix(t, q) {
after = s
// Remove leading whitepace (t already has its leading whitespace removed).
// Remove leading whitespace (t already has its leading whitespace removed).
after = strings.TrimLeftFunc(after, unicode.IsSpace)
// Remove the prefix.
after = after[len(p):]
Expand Down
16 changes: 16 additions & 0 deletions keepsorted/options_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func (p *parser) popValue(typ reflect.Type) (reflect.Value, error) {
case reflect.TypeFor[int]():
val, err := p.popInt()
return reflect.ValueOf(val), err
case reflect.TypeFor[SortOrder]():
val, err := p.popSortOrder()
return reflect.ValueOf(val), err
case reflect.TypeFor[[]string]():
val, err := p.popList()
return reflect.ValueOf(val), err
Expand Down Expand Up @@ -187,6 +190,19 @@ func (p *parser) popListRegexOption() ([]ByRegexOption, error) {
})
}

func (p *parser) popSortOrder() (SortOrder, error) {
val, rest, _ := strings.Cut(p.line, " ")
p.line = rest
switch val {
case string(OrderAsc):
return OrderAsc, nil
case string(OrderDesc):
return OrderDesc, nil
default:
return OrderAsc, fmt.Errorf("unrecognized order value %q, expected 'asc' or 'desc'", val)
}
}

func tryFindYAMLListAtStart(s string) (list, rest string, err error) {
if s == "" || s[0] != '[' {
return "", "", errNotYAMLList
Expand Down
17 changes: 17 additions & 0 deletions keepsorted/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,23 @@ func TestBlockOptions(t *testing.T) {
},
},
},
{
name: "OrderAsc",
in: "order=asc",
want: blockOptions{Order: OrderAsc},
},
{
name: "OrderDesc",
in: "order=desc",
want: blockOptions{Order: OrderDesc},
},
{
name: "OrderInvalid",
in: "order=foo",
defaultOptions: blockOptions{Order: OrderAsc},
want: blockOptions{Order: OrderAsc},
wantErr: `while parsing option "order": unrecognized order value "foo", expected 'asc' or 'desc'`,
},
} {
t.Run(tc.name, func(t *testing.T) {
initZerolog(t)
Expand Down