| id | 110 |
|---|---|
| title | Ordered list numbering rule |
| status | ✅ |
| summary | New rule MDS046 that pins how ordered lists number their items: literal sequential (`1. 2. 3.`) or all ones (`1. 1. 1.`). Removes the "ordered list which doesn't care how you ordered them" surprise from "Exhibit C" of the bgslabs.org rant. |
| model | sonnet |
Let users pin how ordered list items are numbered in
the source. CommonMark only reads the first item's
number and increments from there in the rendered
output. The remaining numbers are decorative — 1. 1. 1. and 1. 7. 99. both render as 1, 2, 3. This
silent rewrite surprises authors and produces noisy
diffs when an editor renumbers items it did not
touch. The rule pins one of two source styles so
what the author writes matches what the reader sees.
Ordered lists are *ast.List with IsOrdered() == true. The starting number is on the node as Start.
Each *ast.ListItem has a position but goldmark
discards the literal number written by the author —
it only stores the marker character and the start.
The check must read the source line of each list item to recover the literal number written.
sequential— items number1. 2. 3. ...matching their position. Surprises nobody. Painful when inserting an item in the middle of a long list, because every following number shifts.all-ones— every item uses1.(or whichever number the list starts with). Insertion is free. The rendered output still numbers correctly.
The rant's complaint targets the mismatch between source and output, not either choice on its own. A team picks one and stops thinking about it.
MDS016 (list-indent) handles indentation. MDS045 (list-marker-style) handles the unordered marker. MDS046 only handles ordered numbering. The three rules can fire on the same list independently.
rules:
ordered-list-numbering:
style: sequential # sequential | all-ones
start: 1 # required first numberCategory: list. Disabled by default (opt-in).
Plan 112 ships profiles that auto-enable this rule:
profile: portableactivates withstyle: sequentialandstart: 1.profile: githubdoes not activate this rule.profile: plainactivates withstyle: sequentialandstart: 1.
User overrides on top of the profile still win via deep-merge.
Walk *ast.List with IsOrdered(). For each list:
- Read
list.Start. If it differs from thestartsetting, emit one diagnostic for the first item. - For each item, read the source line and parse the leading number.
- Compute the expected number based on
style. Forsequential, expected isstart + i. Forall-ones, expected isstartfor every item. - If the literal number differs from expected, emit one diagnostic at the item's line.
Replace the literal number on each item line with
the expected number. The marker character (. or
)) and trailing space are preserved.
Width changes (e.g. 9. → 10.) shift the item's
content column. The fix re-indents continuation
lines to match the new marker width so list-indent
stays consistent.
ordered list starts at {actual}; configured start is {expected}
ordered list item {position} numbered {actual}; expected {expected}
- Scaffold
internal/rules/orderedlistnumbering/. - Implement
Check()walking ordered*ast.List. - Implement source-line parsing for the literal item number.
- Implement
rule.Configurableforstyleandstart. - Implement
Fix()rewriting numbers and adjusting continuation indentation when marker width changes. - Register as MDS046 in category
list. - Add fixture tests covering each style, the width-change case (single-digit to double-digit), wrong start, nested ordered lists (each nested list is checked independently), and unordered lists (which the rule must not flag).
- Add rule README.
-
1. a\n2. b\n3. cwithstyle: sequentialemits no diagnostic. -
1. a\n1. b\n1. cwithstyle: sequentialemits two diagnostics and fixes to1. 2. 3.. -
1. a\n1. b\n1. cwithstyle: all-onesemits no diagnostic. -
1. a\n3. b\n7. cwithstyle: all-onesemits two diagnostics and fixes to all1.. -
5. a\n6. bwithstart: 1emits one diagnostic naming the wrong start. - A 12-item sequential list fixes the single-to-double-digit boundary without breaking continuation indent.
- Unordered lists emit no diagnostic.
- Rule is disabled by default.
- All tests pass:
go test ./... -
go tool golangci-lint runreports no issues