diff --git a/README.md b/README.md
index 8794385..a8ef68c 100644
--- a/README.md
+++ b/README.md
@@ -396,8 +396,11 @@ treated as sticky. These prefixes cannot contain space characters.
#### Skipping lines
In some cases, it may not be possible to have the start directive on the line
-immediately before the sorted region. In this case, `skip_lines` can be used to
-indicate how many lines are to be skipped before the sorted region.
+immediately before the sorted region or end immediately after. In these cases,
+`skip_lines` can be used to indicate how many lines are to be skipped before
+and/or after the sorted region. The `skip_lines` option interprets positive
+values as lines to skip at the start of the sorted region and negative values
+as lines to skip at the end.
For instance, this can be used with a Markdown table, to prevent the headers
and the dashed line after the headers from being sorted:
@@ -435,6 +438,43 @@ Alpha | Foo
+This can be used to keep the footers of a table from being sorted as well:
+
+
+
+|
+
+```md
+
+Item | Cost
+----- | -----
+Lemon | $1.50
+Pear | $2.10
+Apple | $1.09
+----- | -----
+Total | $4.69
+
+```
+
+ |
+
+
+```diff
++
+ Item | Cost
+ ----- | -----
+ Apple | $1.09
+ Lemon | $1.50
+ Pear | $2.10
+ ----- | -----
+ Total | $4.69
++
+```
+
+ |
+
+
+
### Sorting options
Sorting options tell keep-sorted how the logical lines in your keep-sorted
diff --git a/goldens/simple.err b/goldens/simple.err
index 55f1c03..f9353ff 100644
--- a/goldens/simple.err
+++ b/goldens/simple.err
@@ -1,4 +1,4 @@
-WRN skip_lines has invalid value: -1 line=113
+WRN skip_lines has conflicting values (should one of these be positive, to skip lines at the start of the block instead?): -1,-1 line=113
WRN unrecognized option "foo" line=113
WRN while parsing option "ignore_prefixes": content appears to be an unterminated YAML list: "[abc, foo" line=120
exit status 1
diff --git a/goldens/simple.in b/goldens/simple.in
index 9f5988a..b426939 100644
--- a/goldens/simple.in
+++ b/goldens/simple.in
@@ -110,7 +110,7 @@ B
// keep-sorted-test end
Invalid option
- keep-sorted-test start group=yes skip_lines=-1 foo=bar
+ keep-sorted-test start group=yes skip_lines=-1,-1 foo=bar
2
1
3
diff --git a/goldens/simple.out b/goldens/simple.out
index 7ef981b..5049910 100644
--- a/goldens/simple.out
+++ b/goldens/simple.out
@@ -111,7 +111,7 @@ C
// keep-sorted-test end
Invalid option
- keep-sorted-test start group=yes skip_lines=-1 foo=bar
+ keep-sorted-test start group=yes skip_lines=-1,-1 foo=bar
1
2
3
diff --git a/keepsorted/block.go b/keepsorted/block.go
index 67f5fe3..35a5148 100644
--- a/keepsorted/block.go
+++ b/keepsorted/block.go
@@ -113,7 +113,8 @@ func (f *Fixer) newBlocks(filename string, lines []string, offset int, include f
warnings = append(warnings, finding(filename, start.index+offset, start.index+offset, warn.Error()))
}
- start.index += opts.SkipLines
+ start.index += opts.startOffset()
+ endIndex += opts.endOffset()
if start.index >= endIndex {
continue
}
diff --git a/keepsorted/keep_sorted_test.go b/keepsorted/keep_sorted_test.go
index 0d00180..b90cd0b 100644
--- a/keepsorted/keep_sorted_test.go
+++ b/keepsorted/keep_sorted_test.go
@@ -222,7 +222,7 @@ func TestFindings(t *testing.T) {
want: []*Finding{finding(filename, 3, 5, errorUnordered, automaticReplacement(3, 5, "1\n2\n3\n"))},
},
{
- name: "SkipLines",
+ name: "SkipLinesStart",
in: `
// keep-sorted-test start skip_lines=2
@@ -235,6 +235,48 @@ func TestFindings(t *testing.T) {
want: []*Finding{finding(filename, 5, 7, errorUnordered, automaticReplacement(5, 7, "1\n2\n3\n"))},
},
+ {
+ name: "SkipLinesEnd",
+
+ in: `
+// keep-sorted-test start skip_lines=-2
+5
+4
+3
+2
+1
+// keep-sorted-test end`,
+
+ want: []*Finding{finding(filename, 3, 5, errorUnordered, automaticReplacement(3, 5, "3\n4\n5\n"))},
+ },
+ {
+ name: "SkipLinesStartAndEnd",
+
+ in: `
+// keep-sorted-test start skip_lines=1,-1
+5
+4
+3
+2
+1
+// keep-sorted-test end`,
+
+ want: []*Finding{finding(filename, 4, 6, errorUnordered, automaticReplacement(4, 6, "2\n3\n4\n"))},
+ },
+ {
+ name: "SkipLinesEndAndStart",
+
+ in: `
+// keep-sorted-test start skip_lines=-1,1
+5
+4
+3
+2
+1
+// keep-sorted-test end`,
+
+ want: []*Finding{finding(filename, 4, 6, errorUnordered, automaticReplacement(4, 6, "2\n3\n4\n"))},
+ },
{
name: "MismatchedStart",
diff --git a/keepsorted/options.go b/keepsorted/options.go
index 551f3be..6b5c6fa 100644
--- a/keepsorted/options.go
+++ b/keepsorted/options.go
@@ -75,6 +75,7 @@ func (opts BlockOptions) String() string {
// - []string: key=a,b,c,d
// - map[string]bool: key=a,b,c,d
// - int: key=123
+// - []int: key=1,-1
// - ByRegexOptions key=a,b,c,d, key=[yaml_list]
type blockOptions struct {
// AllowYAMLLists determines whether list.set valued options are allowed to be specified by YAML.
@@ -85,7 +86,7 @@ type blockOptions struct {
///////////////////////////
// SkipLines is the number of lines to ignore before sorting.
- SkipLines int `key:"skip_lines"`
+ SkipLines []int `key:"skip_lines"`
// Group determines whether we group lines together based on increasing indentation.
Group bool
// GroupPrefixes tells us about other types of lines that should be added to a group.
@@ -243,6 +244,8 @@ func formatValue(val reflect.Value) (string, error) {
}
case reflect.TypeFor[int]():
return strconv.Itoa(int(val.Int())), nil
+ case reflect.TypeFor[[]int]():
+ return formatIntList(val.Interface().([]int)), nil
case reflect.TypeFor[[]ByRegexOption]():
opts := val.Interface().([]ByRegexOption)
vals := make([]string, 0, len(opts))
@@ -301,6 +304,14 @@ func formatList(vals []string) (string, error) {
return strings.TrimSpace(string(out)), nil
}
+func formatIntList(intVals []int) string {
+ vals := make([]string, 0, len(intVals))
+ for _, v := range intVals {
+ vals = append(vals, strconv.Itoa(v))
+ }
+ return strings.Join(vals, ",")
+}
+
func guessCommentMarker(startLine string) string {
startLine = strings.TrimSpace(startLine)
for _, marker := range []string{"//", "#", "/*", "--", ";", "