Skip to content

Commit

Permalink
fix(parser): improve support for cross-references
Browse files Browse the repository at this point in the history
refactor the interface and visit/traverse the list elements
and delimited block elements to detect "nested" references

Updates bytesparadise#863

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon committed Nov 22, 2021
1 parent f7e80fd commit fb07aa7
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 12 deletions.
75 changes: 75 additions & 0 deletions pkg/parser/cross_reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,81 @@ some content`
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("to attached element in a list", func() {
source := `a reference to <<table>>
. list element
+
[#table]
.The table
|===
| A | B
|===
`

expected := &types.Document{
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "a reference to ",
},
&types.InternalCrossReference{
ID: "table",
},
},
},
&types.List{
Kind: types.OrderedListKind,
Elements: []types.ListElement{
&types.OrderedListElement{
Style: types.Arabic,
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "list element",
},
},
},
&types.Table{
Attributes: types.Attributes{
types.AttrID: "table",
types.AttrTitle: "The table",
},
Rows: []*types.TableRow{
{
Cells: []*types.TableCell{
{
Elements: []interface{}{
&types.StringElement{
Content: "A ",
},
},
},
{
Elements: []interface{}{
&types.StringElement{
Content: "B",
},
},
},
},
},
},
},
},
},
},
},
},
ElementReferences: types.ElementReferences{
"table": "The table",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
})

Context("external references", func() {
Expand Down
4 changes: 1 addition & 3 deletions pkg/parser/document_processing_aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ func aggregate(ctx *ParseContext, fragmentStream <-chan types.DocumentFragment)
}
// also, check if the element has refs
if e, ok := element.(types.Referencable); ok {
if id, title := e.Ref(); id != "" && title != nil {
refs[id] = title
}
e.Reference(refs)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/parser/q_a_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ What is the answer to the Ultimate Question?:: 42`
},
},
},
ElementReferences: types.ElementReferences{
"quiz": "Q&A",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
3 changes: 3 additions & 0 deletions pkg/parser/unordered_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ var _ = Describe("unordered lists", func() {
},
},
},
ElementReferences: types.ElementReferences{
"listID": "mytitle",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
110 changes: 101 additions & 9 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type BlockWithLocation interface {
}

type Referencable interface {
Ref() (string, interface{})
Reference(refs ElementReferences)
}

// ------------------------------------------
Expand Down Expand Up @@ -717,6 +717,20 @@ func (l *List) AddElement(element interface{}) error {
return errors.Errorf("cannot add element of type '%T' to list of kind '%s'", element, l.Kind)
}

func (l *List) Reference(refs ElementReferences) {
id := l.Attributes.GetAsStringWithDefault(AttrID, "")
title := l.Attributes[AttrTitle]
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range l.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

func (l *List) LastElement() ListElement {
if len(l.Elements) == 0 {
return nil
Expand Down Expand Up @@ -898,6 +912,22 @@ func NewCalloutListElement(ref int, content RawLine) (*CalloutListElement, error
}, nil
}

var _ Referencable = &CalloutListElement{}

func (e *CalloutListElement) Reference(refs ElementReferences) {
id := e.Attributes.GetAsStringWithDefault(AttrID, "")
title := e.Attributes[AttrTitle]
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range e.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

// checks if the given list element matches the level of this element
func (e *CalloutListElement) matchesStyle(other ListElement) bool {
_, ok := other.(*CalloutListElement)
Expand Down Expand Up @@ -991,6 +1021,22 @@ func NewOrderedListElement(prefix OrderedListElementPrefix, content interface{})
}, nil
}

var _ Referencable = &OrderedListElement{}

func (e *OrderedListElement) Reference(refs ElementReferences) {
id := e.Attributes.GetAsStringWithDefault(AttrID, "")
title := e.Attributes[AttrTitle]
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range e.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

// checks if the given list element matches the level of this element
func (e *OrderedListElement) matchesStyle(other ListElement) bool {
if element, ok := other.(*OrderedListElement); ok {
Expand Down Expand Up @@ -1105,6 +1151,22 @@ func NewUnorderedListElement(prefix UnorderedListElementPrefix, checkstyle inter
}, nil
}

var _ Referencable = &UnorderedListElement{}

func (e *UnorderedListElement) Reference(refs ElementReferences) {
id := e.Attributes.GetAsStringWithDefault(AttrID, "")
title := e.Attributes[AttrTitle]
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range e.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

// checks if the given list element matches the level of this element
func (e *UnorderedListElement) matchesStyle(other ListElement) bool {
if other, ok := other.(*UnorderedListElement); ok {
Expand Down Expand Up @@ -1331,6 +1393,22 @@ func NewLabeledListElement(level int, term, description interface{}) (*LabeledLi
}, nil
}

var _ Referencable = &LabeledListElement{}

func (e *LabeledListElement) Reference(refs ElementReferences) {
id := e.Attributes.GetAsStringWithDefault(AttrID, "")
title := e.Attributes[AttrTitle]
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range e.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

// making sure that the `ListElement` interface is implemented by `LabeledListElement`
var _ ListElement = &LabeledListElement{}

Expand Down Expand Up @@ -1509,10 +1587,12 @@ func (p *Paragraph) mapAttributes() {

var _ Referencable = &Paragraph{}

func (p *Paragraph) Ref() (string, interface{}) {
func (p *Paragraph) Reference(refs ElementReferences) {
id := p.Attributes.GetAsStringWithDefault(AttrID, "")
title := p.Attributes[AttrTitle]
return id, title
if id != "" && title != nil {
refs[id] = title
}
}

var _ WithFootnotes = &Paragraph{}
Expand Down Expand Up @@ -1982,10 +2062,18 @@ func (b *DelimitedBlock) mapAttributes() {

var _ Referencable = &DelimitedBlock{}

func (b *DelimitedBlock) Ref() (string, interface{}) {
func (b *DelimitedBlock) Reference(refs ElementReferences) {
id := b.Attributes.GetAsStringWithDefault(AttrID, "")
title := b.Attributes[AttrTitle]
return id, title
if id != "" && title != nil {
refs[id] = title
}
// also, visit elements
for _, e := range b.Elements {
if e, ok := e.(Referencable); ok {
e.Reference(refs)
}
}
}

// TODO: not needed?
Expand Down Expand Up @@ -2098,9 +2186,11 @@ func (s *Section) resolveID(attrs Attributes) (string, error) {

var _ Referencable = &Section{}

func (s *Section) Ref() (string, interface{}) {
func (s *Section) Reference(refs ElementReferences) {
id := s.Attributes.GetAsStringWithDefault(AttrID, "")
return id, s.Title
if id != "" && s.Title != nil {
refs[id] = s.Title
}
}

var _ WithElementAddition = &Section{}
Expand Down Expand Up @@ -3049,10 +3139,12 @@ func (t *Table) SetAttributes(attributes Attributes) {

var _ Referencable = &Table{}

func (t *Table) Ref() (string, interface{}) {
func (t *Table) Reference(refs ElementReferences) {
id := t.Attributes.GetAsStringWithDefault(AttrID, "")
title := t.Attributes[AttrTitle]
return id, title
if id != "" && title != nil {
refs[id] = title
}
}

type HAlign string
Expand Down

0 comments on commit fb07aa7

Please sign in to comment.