Skip to content

Commit de57785

Browse files
committed
xrefstyle should only be present in root
1 parent acd7fbf commit de57785

4 files changed

Lines changed: 215 additions & 120 deletions

File tree

disco/baller.go

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"log/slog"
7+
"strings"
78

89
"github.com/project-chip/alchemy/asciidoc"
910
"github.com/project-chip/alchemy/asciidoc/parse"
@@ -133,30 +134,63 @@ func (b *Baller) disco(cxt context.Context, doc *asciidoc.Document) error {
133134
}
134135

135136
func (b *Baller) discoBallTopLevelSection(dc *discoContext, top *asciidoc.Section, docType matter.DocType) error {
136-
if b.options.AddXrefstyle && dc.doc == dc.library.Root {
137-
var xrefstyleEntry *asciidoc.AttributeEntry
138-
var topIndex int = -1
139-
140-
for i, el := range dc.doc.Elements {
141-
if el == top {
142-
topIndex = i
143-
break
144-
}
145-
if ae, ok := el.(*asciidoc.AttributeEntry); ok && ae.Name == "xrefstyle" {
146-
xrefstyleEntry = ae
137+
if b.options.XrefStyleOnlyInRoot {
138+
// Logic:
139+
// 1. For non-root, no xrefstyle at all anywhere in doc.
140+
// 2. For root, if it is present somewhere in file, it's ok. If not present, add it after Copyright Notice or before first section.
141+
142+
if dc.doc == dc.library.Root {
143+
found := false
144+
for _, el := range dc.doc.Elements {
145+
if ae, ok := el.(*asciidoc.AttributeEntry); ok && ae.Name == "xrefstyle" {
146+
found = true
147+
break
148+
}
147149
}
148-
}
150+
if !found {
151+
ae := asciidoc.NewAttributeEntry("xrefstyle")
152+
ae.Elements = asciidoc.Elements{asciidoc.NewString("basic")}
153+
154+
copyrightIndex := -1
155+
topIndex := -1
156+
for i, el := range dc.doc.Elements {
157+
if s, ok := el.(*asciidoc.Section); ok {
158+
if topIndex == -1 {
159+
topIndex = i
160+
}
161+
name := ""
162+
if len(s.Title) > 0 {
163+
if str, ok := s.Title[0].(*asciidoc.String); ok {
164+
name = str.Value
165+
}
166+
}
167+
if strings.Contains(strings.ToLower(name), "copyright notice") {
168+
copyrightIndex = i
169+
break
170+
}
171+
}
172+
}
149173

150-
if xrefstyleEntry == nil {
151-
ae := asciidoc.NewAttributeEntry("xrefstyle")
152-
ae.Elements = asciidoc.Elements{asciidoc.NewString("basic")}
153-
if topIndex != -1 {
154-
dc.doc.Elements = append(dc.doc.Elements, nil, nil)
155-
copy(dc.doc.Elements[topIndex+2:], dc.doc.Elements[topIndex:])
156-
dc.doc.Elements[topIndex] = ae
157-
dc.doc.Elements[topIndex+1] = &asciidoc.NewLine{}
158-
} else {
159-
dc.doc.Elements = append(asciidoc.Elements{ae, &asciidoc.NewLine{}}, dc.doc.Elements...)
174+
if copyrightIndex != -1 {
175+
dc.doc.Elements = append(dc.doc.Elements[:copyrightIndex+1], append([]asciidoc.Element{&asciidoc.NewLine{}, ae}, dc.doc.Elements[copyrightIndex+1:]...)...)
176+
} else if topIndex != -1 {
177+
dc.doc.Elements = append(dc.doc.Elements[:topIndex], append([]asciidoc.Element{ae, &asciidoc.NewLine{}}, dc.doc.Elements[topIndex:]...)...)
178+
} else {
179+
dc.doc.Elements = append(asciidoc.Elements{ae, &asciidoc.NewLine{}}, dc.doc.Elements...)
180+
}
181+
}
182+
} else {
183+
for i := 0; i < len(dc.doc.Elements); i++ {
184+
el := dc.doc.Elements[i]
185+
if ae, ok := el.(*asciidoc.AttributeEntry); ok && ae.Name == "xrefstyle" {
186+
dc.doc.Elements = append(dc.doc.Elements[:i], dc.doc.Elements[i+1:]...)
187+
i--
188+
if i+1 < len(dc.doc.Elements) {
189+
if _, ok := dc.doc.Elements[i+1].(*asciidoc.NewLine); ok {
190+
dc.doc.Elements = append(dc.doc.Elements[:i+1], dc.doc.Elements[i+2:]...)
191+
}
192+
}
193+
}
160194
}
161195
}
162196
}
@@ -191,3 +225,19 @@ func (b *Baller) discoBallTopLevelSection(dc *discoContext, top *asciidoc.Sectio
191225
b.postCleanUpStrings(dc.doc, top)
192226
return nil
193227
}
228+
229+
func (b *Baller) TestHelperDiscoBall(doc *asciidoc.Document, isRoot bool) error {
230+
lib := &spec.Library{}
231+
if isRoot {
232+
lib.Root = doc
233+
}
234+
dc := &discoContext{
235+
Context: context.Background(),
236+
doc: doc,
237+
library: lib,
238+
potentialDataTypes: make(map[string][]*DataTypeEntry),
239+
}
240+
docType := matter.DocTypeCluster
241+
top := parse.FindFirst[*asciidoc.Section](doc, asciidoc.RawReader, doc)
242+
return b.discoBallTopLevelSection(dc, top, docType)
243+
}

disco/baller_test.go

Lines changed: 0 additions & 96 deletions
This file was deleted.

disco/option.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type DiscoOptions struct {
2222
NormalizeAnchors bool `default:"false" aliases:"normalizeAnchors" help:"rewrite anchors and references without labels" group:"Discoballing:"`
2323
RemoveMandatoryFallbacks bool `default:"true" aliases:"removeMandatoryFallbacks" help:"remove fallback values for mandatory fields" group:"Discoballing:"`
2424
RenameSections bool `default:"false" help:"rename sections to disco-ball standard names" group:"Discoballing:"`
25-
AddXrefstyle bool `default:"true" aliases:"addXrefstyle" help:"add xrefstyle attribute before the first section" group:"Discoballing:"`
25+
XrefStyleOnlyInRoot bool `default:"true" aliases:"xrefStyleOnlyInRoot" help:"enforce xrefstyle: basic only in root" group:"Discoballing:"`
2626
}
2727

2828
var DefaultOptions = DiscoOptions{
@@ -45,5 +45,5 @@ var DefaultOptions = DiscoOptions{
4545
NormalizeAnchors: false,
4646
RemoveMandatoryFallbacks: true,
4747
RenameSections: false,
48-
AddXrefstyle: true,
48+
XrefStyleOnlyInRoot: true,
4949
}

tests/baller_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package tests
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/project-chip/alchemy/asciidoc"
8+
"github.com/project-chip/alchemy/disco"
9+
)
10+
11+
func TestXrefStyleOnlyInRoot(t *testing.T) {
12+
// Create a Baller with the option enabled
13+
b := disco.NewBaller(nil, disco.DiscoOptions{XrefStyleOnlyInRoot: true})
14+
15+
// Test Case 1: Root doc without xrefstyle, with Copyright Notice
16+
{
17+
doc := &asciidoc.Document{
18+
Elements: asciidoc.Elements{
19+
&asciidoc.Section{
20+
Title: asciidoc.Elements{asciidoc.NewString("Copyright Notice")},
21+
Level: 1,
22+
},
23+
&asciidoc.Section{
24+
Title: asciidoc.Elements{asciidoc.NewString("First Section")},
25+
Level: 1,
26+
},
27+
},
28+
}
29+
30+
err := b.TestHelperDiscoBall(doc, true)
31+
if err != nil {
32+
t.Fatalf("unexpected error: %v", err)
33+
}
34+
35+
// Verify added after Copyright Notice
36+
found := false
37+
for i, el := range doc.Elements {
38+
if s, ok := el.(*asciidoc.Section); ok && strings.Contains(s.Title[0].(*asciidoc.String).Value, "Copyright Notice") {
39+
// Check next element
40+
if i+1 < len(doc.Elements) {
41+
if _, ok := doc.Elements[i+1].(*asciidoc.NewLine); ok {
42+
if i+2 < len(doc.Elements) {
43+
if ae, ok := doc.Elements[i+2].(*asciidoc.AttributeEntry); ok && ae.Name == "xrefstyle" {
44+
found = true
45+
break
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
52+
if !found {
53+
t.Error("expected xrefstyle to be added after Copyright Notice section")
54+
}
55+
}
56+
57+
// Test Case 2: Root doc without xrefstyle, without Copyright Notice
58+
{
59+
doc := &asciidoc.Document{
60+
Elements: asciidoc.Elements{
61+
&asciidoc.Section{
62+
Title: asciidoc.Elements{asciidoc.NewString("First Section")},
63+
Level: 1,
64+
},
65+
},
66+
}
67+
68+
err := b.TestHelperDiscoBall(doc, true)
69+
if err != nil {
70+
t.Fatalf("unexpected error: %v", err)
71+
}
72+
73+
// Verify added before first section
74+
if len(doc.Elements) < 3 {
75+
t.Fatalf("expected at least 3 elements, got %d", len(doc.Elements))
76+
}
77+
if ae, ok := doc.Elements[0].(*asciidoc.AttributeEntry); !ok || ae.Name != "xrefstyle" {
78+
t.Error("expected xrefstyle to be added at the beginning")
79+
}
80+
}
81+
82+
// Test Case 3: Root doc with existing xrefstyle somewhere
83+
{
84+
ae := asciidoc.NewAttributeEntry("xrefstyle")
85+
ae.Elements = asciidoc.Elements{asciidoc.NewString("full")}
86+
87+
doc := &asciidoc.Document{
88+
Elements: asciidoc.Elements{
89+
&asciidoc.Section{
90+
Title: asciidoc.Elements{asciidoc.NewString("First Section")},
91+
Level: 1,
92+
},
93+
ae,
94+
},
95+
}
96+
97+
err := b.TestHelperDiscoBall(doc, true)
98+
if err != nil {
99+
t.Fatalf("unexpected error: %v", err)
100+
}
101+
102+
// Verify not changed
103+
if s, ok := ae.Elements[0].(*asciidoc.String); !ok || s.Value != "full" {
104+
t.Errorf("expected xrefstyle to remain full, got %v", ae.Elements[0])
105+
}
106+
}
107+
108+
// Test Case 4: Non-root doc with xrefstyle anywhere (should be removed)
109+
{
110+
ae1 := asciidoc.NewAttributeEntry("xrefstyle")
111+
ae2 := asciidoc.NewAttributeEntry("xrefstyle")
112+
113+
doc := &asciidoc.Document{
114+
Elements: asciidoc.Elements{
115+
ae1,
116+
&asciidoc.Section{
117+
Title: asciidoc.Elements{asciidoc.NewString("First Section")},
118+
Level: 1,
119+
},
120+
ae2,
121+
},
122+
}
123+
124+
err := b.TestHelperDiscoBall(doc, false)
125+
if err != nil {
126+
t.Fatalf("unexpected error: %v", err)
127+
}
128+
129+
// Verify no xrefstyle anywhere
130+
found := false
131+
for _, el := range doc.Elements {
132+
if ae, ok := el.(*asciidoc.AttributeEntry); ok && ae.Name == "xrefstyle" {
133+
found = true
134+
break
135+
}
136+
}
137+
if found {
138+
t.Error("expected all xrefstyle to be removed from non-root document")
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)