Skip to content

Commit

Permalink
fix(parser/renderer): support cross references in elements defined la…
Browse files Browse the repository at this point in the history
…ter (#864)

covers sections, tables, delimited blocks and paragraphs.

Fixes #863

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon authored Nov 21, 2021
1 parent f1156b7 commit f7e80fd
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 8 deletions.
197 changes: 195 additions & 2 deletions pkg/parser/cross_reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var _ = Describe("cross references", func() {

Context("internal references", func() {

It("cross reference with custom id alone", func() {
It("with custom id alone", func() {
source := `[[thetitle]]
== a title
Expand Down Expand Up @@ -57,7 +57,7 @@ with some content linked to <<thetitle>>!`
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("cross reference with custom id and label", func() {
It("with custom id and label", func() {
source := `[[thetitle]]
== a title
Expand Down Expand Up @@ -100,6 +100,199 @@ with some content linked to <<thetitle,a label to the title>>!`
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("to section defined later in the document", func() {
source := `a reference to <<section>>
[#section]
== A section with a link to https://example.com
some content`
title := []interface{}{
&types.StringElement{
Content: "A section with a link to ",
},
&types.InlineLink{
Location: &types.Location{
Scheme: "https://",
Path: []interface{}{
&types.StringElement{
Content: "example.com",
},
},
},
},
}
expected := &types.Document{
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "a reference to ",
},
&types.InternalCrossReference{
ID: "section",
},
},
},
&types.Section{
Attributes: types.Attributes{
types.AttrID: "section",
types.AttrCustomID: true,
},
Level: 1,
Title: title,
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "some content",
},
},
},
},
},
},
ElementReferences: types.ElementReferences{
"section": title,
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("to delimited block defined later in the document", func() {
source := `a reference to <<block>>
[#block]
.The block
----
some content
----`

expected := &types.Document{
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "a reference to ",
},
&types.InternalCrossReference{
ID: "block",
},
},
},
&types.DelimitedBlock{
Kind: types.Listing,
Attributes: types.Attributes{
types.AttrID: "block",
types.AttrTitle: "The block",
},
Elements: []interface{}{
&types.StringElement{
Content: "some content",
},
},
},
},
ElementReferences: types.ElementReferences{
"block": "The block",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("to paragraph defined later in the document", func() {
source := `a reference to <<a-paragraph>>
[#a-paragraph]
.another paragraph
some content`
expected := &types.Document{
Elements: []interface{}{
&types.Paragraph{
Elements: []interface{}{
&types.StringElement{
Content: "a reference to ",
},
&types.InternalCrossReference{
ID: "a-paragraph",
},
},
},
&types.Paragraph{
Attributes: types.Attributes{
types.AttrID: "a-paragraph",
types.AttrTitle: "another paragraph",
},
Elements: []interface{}{
&types.StringElement{
Content: "some content",
},
},
},
},
ElementReferences: types.ElementReferences{
"a-paragraph": "another paragraph",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})

It("to table defined later in the document", func() {
source := `a reference to <<table>>
[#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.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
9 changes: 9 additions & 0 deletions pkg/parser/delimited_block_literal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ a normal paragraph.`
},
},
},
ElementReferences: types.ElementReferences{
"ID": "title",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down Expand Up @@ -166,6 +169,9 @@ a normal paragraph.`
},
},
},
ElementReferences: types.ElementReferences{
"ID": "title",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down Expand Up @@ -234,6 +240,9 @@ a normal paragraph.`
},
},
},
ElementReferences: types.ElementReferences{
"ID": "title",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
3 changes: 3 additions & 0 deletions pkg/parser/delimited_block_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ end`,
},
},
},
ElementReferences: types.ElementReferences{
"id-for-source-block": "app.rb",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
6 changes: 6 additions & 0 deletions pkg/parser/document_processing_aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ func aggregate(ctx *ParseContext, fragmentStream <-chan types.DocumentFragment)
return nil, nil, err
}
}
// 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
}
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions pkg/parser/paragraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,9 @@ NOTE: this is a note.`
},
},
},
ElementReferences: types.ElementReferences{
"cookie": "chocolate",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down Expand Up @@ -1587,6 +1590,9 @@ this is a
},
},
},
ElementReferences: types.ElementReferences{
"cookie": "chocolate",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down Expand Up @@ -1722,6 +1728,9 @@ I am a verse paragraph.`
},
},
},
ElementReferences: types.ElementReferences{
"universal": "universe",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
3 changes: 3 additions & 0 deletions pkg/parser/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ var _ = Describe("tables", func() {
},
},
},
ElementReferences: types.ElementReferences{
"anchor": "table title",
},
}
Expect(ParseDocument(source)).To(MatchDocument(expected))
})
Expand Down
9 changes: 6 additions & 3 deletions pkg/renderer/sgml/cross_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ func (r *sgmlRenderer) renderInternalCrossReference(ctx *renderer.Context, xref
if xrefLabel, ok := xref.Label.(string); ok {
label = xrefLabel
} else if target, found := ctx.ElementReferences[xrefID]; found {
if t, ok := target.([]interface{}); ok {
renderedContent, err := r.renderElement(ctx, t)
switch t := target.(type) {
case string:
label = t
case []interface{}:
renderedContent, err := r.renderPlainText(ctx, t)
if err != nil {
return "", errors.Wrap(err, "error while rendering internal cross reference")
}
label = renderedContent
} else {
default:
return "", errors.Errorf("unable to process internal cross reference to element of type %T", target)
}
} else {
Expand Down
43 changes: 41 additions & 2 deletions pkg/renderer/sgml/html5/cross_reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var _ = Describe("cross references", func() {

Context("internal references", func() {

It("cross reference with custom id", func() {
It("with custom id", func() {

source := `[[thetitle]]
== a title
Expand All @@ -29,7 +29,7 @@ with some content linked to <<thetitle>>!`
Expect(RenderHTML(source)).To(MatchHTML(expected))
})

It("cross reference with custom id and label", func() {
It("custom id and label", func() {
source := `[[thetitle]]
== a title
Expand All @@ -46,6 +46,45 @@ with some content linked to <<thetitle,a label to the title>>!`
Expect(RenderHTML(source)).To(MatchHTML(expected))
})

It("to paragraph defined later in the document", func() {
source := `a reference to <<a-paragraph>>
[#a-paragraph]
.another paragraph
some content`
expected := `<div class="paragraph">
<p>a reference to <a href="#a-paragraph">another paragraph</a></p>
</div>
<div id="a-paragraph" class="paragraph">
<div class="title">another paragraph</div>
<p>some content</p>
</div>
`
Expect(RenderHTML(source)).To(MatchHTML(expected))
})

It("to section defined later in the document", func() {
source := `a reference to <<section>>
[#section]
== A section with a link to https://example.com
some content`
expected := `<div class="paragraph">
<p>a reference to <a href="#section">A section with a link to https://example.com</a></p>
</div>
<div class="sect1">
<h2 id="section">A section with a link to <a href="https://example.com" class="bare">https://example.com</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>some content</p>
</div>
</div>
</div>
`
Expect(RenderHTML(source)).To(MatchHTML(expected))
})

It("invalid section reference", func() {

source := `[[thetitle]]
Expand Down
Loading

0 comments on commit f7e80fd

Please sign in to comment.