Skip to content

Commit fb6019c

Browse files
committed
fix(MDS036): address Copilot review feedback
Return all keys from DefaultSettings so re-applying defaults (as the fixture runner cleanup does) fully clears PerLevel and PerHeading, not just Max. Add a defensive headingLine fallback for headings with no line segments, matching the existing pattern in emptysectionbody.
1 parent 7330125 commit fb6019c

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

internal/rules/sectionsizelimits/rule.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,15 @@ func (r *Rule) ApplySettings(settings map[string]any) error {
136136
return nil
137137
}
138138

139-
// DefaultSettings implements rule.Configurable.
139+
// DefaultSettings implements rule.Configurable. All keys are returned so
140+
// re-applying defaults (e.g., test cleanup) fully clears PerLevel and
141+
// PerHeading, not just Max.
140142
func (r *Rule) DefaultSettings() map[string]any {
141-
return map[string]any{"max": 0}
143+
return map[string]any{
144+
"max": 0,
145+
"per-level": map[string]any{},
146+
"per-heading": []any{},
147+
}
142148
}
143149

144150
func (r *Rule) resolveMax(h heading) int {
@@ -187,7 +193,15 @@ func collectHeadings(f *lint.File) []heading {
187193
}
188194

189195
func headingLine(h *ast.Heading, f *lint.File) int {
190-
return f.LineOfOffset(h.Lines().At(0).Start)
196+
if lines := h.Lines(); lines.Len() > 0 {
197+
return f.LineOfOffset(lines.At(0).Start)
198+
}
199+
for c := h.FirstChild(); c != nil; c = c.NextSibling() {
200+
if t, ok := c.(*ast.Text); ok {
201+
return f.LineOfOffset(t.Segment.Start)
202+
}
203+
}
204+
return 1
191205
}
192206

193207
func parsePerLevel(v any) (map[int]int, error) {

internal/rules/sectionsizelimits/rule_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"testing"
77

88
"github.com/jeduden/mdsmith/internal/lint"
9+
"github.com/yuin/goldmark/ast"
10+
gtext "github.com/yuin/goldmark/text"
911

1012
"github.com/stretchr/testify/assert"
1113
"github.com/stretchr/testify/require"
@@ -171,6 +173,42 @@ func TestDefaultSettings(t *testing.T) {
171173
r := &Rule{}
172174
ds := r.DefaultSettings()
173175
assert.Equal(t, 0, ds["max"])
176+
assert.Equal(t, map[string]any{}, ds["per-level"])
177+
assert.Equal(t, []any{}, ds["per-heading"])
178+
}
179+
180+
func TestHeadingLine_FallbackToTextChild(t *testing.T) {
181+
src := []byte("# Hi\n")
182+
f, err := lint.NewFile("t.md", src)
183+
require.NoError(t, err)
184+
185+
// Build a heading with no Lines() but with a Text child, to exercise
186+
// the defensive fallback.
187+
h := ast.NewHeading(1)
188+
text := ast.NewTextSegment(gtext.NewSegment(2, 4))
189+
h.AppendChild(h, text)
190+
assert.Equal(t, 1, headingLine(h, f))
191+
}
192+
193+
func TestHeadingLine_NoLinesNoChildren(t *testing.T) {
194+
f, err := lint.NewFile("t.md", []byte(""))
195+
require.NoError(t, err)
196+
h := ast.NewHeading(1)
197+
assert.Equal(t, 1, headingLine(h, f))
198+
}
199+
200+
func TestApplyDefaultSettings_ClearsPerLevelAndPerHeading(t *testing.T) {
201+
r := &Rule{
202+
Max: 10,
203+
PerLevel: map[int]int{2: 3},
204+
PerHeading: []HeadingPattern{
205+
{Pattern: "x", Regex: regexp.MustCompile("x"), Max: 1},
206+
},
207+
}
208+
require.NoError(t, r.ApplySettings(r.DefaultSettings()))
209+
assert.Equal(t, 0, r.Max)
210+
assert.Empty(t, r.PerLevel)
211+
assert.Empty(t, r.PerHeading)
174212
}
175213

176214
func TestID(t *testing.T) {

0 commit comments

Comments
 (0)