| id | 109 |
|---|---|
| title | List marker style rule |
| status | ✅ |
| summary | New rule MDS045 that pins one of `-`, `*`, or `+` as the bullet for unordered lists. Removes the three-way ambiguity called out in "Exhibit C" of the bgslabs.org rant. |
| model | sonnet |
Let users pin a single bullet character for unordered
lists. CommonMark accepts -, *, and +
interchangeably. Mixed markers in one corpus produce
noisy diffs and surprise readers when nested lists
flip styles. This rule pins the marker globally.
Unordered lists are *ast.List with IsOrdered() == false. The marker character is on the node as
Marker (a byte). Each *ast.ListItem carries
the same marker.
MDS016 (list-indent) enforces nesting indentation, not marker choice. The two rules can fire on the same list independently.
Some style guides want nested lists to use a
different marker for visual distinction (e.g. -
at the top level, * at the next). The rule
supports this with nested: as an ordered list of
markers cycled by depth. The default is to use the
same marker at every depth.
rules:
list-marker-style:
style: dash # dash | asterisk | plus
nested: [] # optional [dash, asterisk]
# cycles by depthCategory: list. Disabled by default (opt-in).
Plan 112 ships profiles that auto-enable this rule:
profile: portableactivates withstyle: dashand emptynested.profile: githubactivates with the same defaults.profile: plainactivates with the same defaults.
User overrides on top of the profile still win via deep-merge.
Walk *ast.List with !IsOrdered(). For each list:
- Compute the depth — the count of
*ast.Listancestors. - Determine the expected marker. When
nestedis empty, usestyle. Otherwise usenested[depth % len(nested)]. - If
list.Markerdiffers, emit one diagnostic per list (not per item) at the first item's line.
Replace the marker byte at each item's start. The indent column does not change because all three markers are one byte wide.
unordered list uses {actual}; configured style is {expected}
unordered list at depth {n} uses {actual}; expected {expected}
- Scaffold
internal/rules/listmarkerstyle/. - Implement
Check()walking*ast.Listand computing depth. - Implement
rule.Configurableforstyleandnested. Documentnestedas replace-mode. - Implement
Fix()replacing the marker byte at each item start. - Register as MDS045 in category
list. - Add fixture tests covering each marker choice,
mixed markers in one list, nested lists with and
without
nestedset, and ordered lists (must not flag). - Add rule README.
-
- itemwithstyle: dashemits no diagnostic. -
* itemwithstyle: dashemits one diagnostic and fixes to- item. -
+ itemwithstyle: dashemits one diagnostic and fixes to- item. - A nested list using
*inside a-parent emits no diagnostic whennested: [dash, asterisk]. - A nested list using
-inside a-parent emits one diagnostic whennested: [dash, asterisk]. - Ordered lists (
1. item) emit no diagnostic. - Rule is disabled by default.
- All tests pass:
go test ./... -
go tool golangci-lint runreports no issues