Skip to content

Commit

Permalink
feat(parser/renderer): support explicit line breaks
Browse files Browse the repository at this point in the history
supported with the ` +` suffix in paragraph lines,
with the `[%hardbreaks] attribute on a paragraph and
with the `:hardbreaks:` attribute at the document level.

also, refactoring in the renderer to remove some
functions that can be replaced with native templates
functions, and simplify others.

fixes #189

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon committed Oct 6, 2018
1 parent e8a26b0 commit b1ccebb
Show file tree
Hide file tree
Showing 20 changed files with 14,096 additions and 13,073 deletions.
27 changes: 17 additions & 10 deletions pkg/parser/asciidoc-grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ TitleElements <- elements:(!NEWLINE WS* !InlineElementID TitleElement WS*)+ { //
return types.NewInlineElements(elements.([]interface{}))
}

TitleElement <- element:(CrossReference / Passthrough / InlineImage / QuotedText / Link / DocumentAttributeSubstitution / InlineFootnote / Word / Parenthesis) {
TitleElement <- element:(CrossReference / Passthrough / InlineImage / QuotedText / Link / DocumentAttributeSubstitution / InlineFootnote / Parenthesis / LineBreak / Word) {
return element, nil
}

Expand Down Expand Up @@ -538,25 +538,32 @@ MasqueradeParagraph <- // admonition paragraph
InlineElements <-
comment:(SingleLineComment) {
return types.NewInlineElements([]interface{}{comment})
} / !EOF !BlockDelimiter elements:(!EOL WS* !InlineElementID InlineElement WS*)+ EOL { // absorbs heading and trailing spaces
return types.NewInlineElements(elements.([]interface{}))
} / !BlankLine !BlockDelimiter elements:(InlineElement)+ linebreak:(LineBreak)? EOL? { // absorbs heading and trailing spaces
return types.NewInlineElements(append(elements.([]interface{}), linebreak))
}

InlineElement <- element:(QuotedText / CrossReference / Passthrough / InlineImage / Link / DocumentAttributeSubstitution / InlineFootnote / Word / Parenthesis) {
InlineElement <- !EOL !LineBreak element:(QuotedText / CrossReference / Passthrough / InlineImage / Link / DocumentAttributeSubstitution / InlineFootnote / InlineElementID / Parenthesis / LineBreak / Word / WS*) {
return element, nil
}

// special case for re-parsing a group of elements after a document substitution:
// we should treat substitution that did not happen (eg: missing attribute) as regulart
// strings
InlineElementsWithoutSubtitution <- !EOF !BlockDelimiter elements:(!EOL WS* !InlineElementID InlineElementWithoutSubtitution WS*)+ EOL { // absorbs heading and trailing spaces
return types.NewInlineElements(elements.([]interface{}))
// we should treat substitution that did not happen (eg: missing attribute) as regular
// strings - (used by the inline element renderer)
InlineElementsWithoutSubtitution <- !BlankLine !BlockDelimiter elements:(InlineElementWithoutSubtitution)+ linebreak:(LineBreak)? EOL? { // absorbs heading and trailing spaces
return types.NewInlineElements(append(elements.([]interface{}), linebreak))
}

InlineElementWithoutSubtitution <- element:(QuotedText / CrossReference / Passthrough / InlineImage / Link / Word / Parenthesis) {
InlineElementWithoutSubtitution <- !EOL !LineBreak element:(QuotedText / CrossReference / Passthrough / InlineImage / Link / InlineElementID/ Parenthesis / Word / WS*) {
return element, nil
}

// ----------------------------------------------------------------------------
// Explicit line breaks
// ----------------------------------------------------------------------------
LineBreak <- WS "+" WS* EOL {
return types.NewLineBreak()
}

// ----------------------------------------------------------------------------
// Quoted Texts (bold, italic and monospace) including substitution prevention
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -1015,7 +1022,7 @@ Alphanum <- [a-zA-Z0-9]

Parenthesis <- "(" / ")" / "[" / "]"

Word <- (!NEWLINE !WS !Parenthesis .)+ {
Word <- (!NEWLINE !WS !Parenthesis .)+ { // word cannot contain parenthesis
return string(c.text), nil
}

Expand Down
26,235 changes: 13,498 additions & 12,737 deletions pkg/parser/asciidoc_parser.go

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion pkg/parser/document_attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ v1.0:`
actualContent := `:toc:
:date: 2017-01-01
:author: Xavier
:hardbreaks:
a paragraph`
expectedResult := types.Document{
Attributes: map[string]interface{}{},
Expand All @@ -537,6 +538,7 @@ a paragraph`
types.DocumentAttributeDeclaration{Name: "toc"},
types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"},
types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"},
types.DocumentAttributeDeclaration{Name: types.DocumentAttrHardBreaks},
types.Paragraph{
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
Expand Down Expand Up @@ -585,6 +587,8 @@ a paragraph`
:author: Xavier
:hardbreaks:
a paragraph`
expectedResult := types.Document{
Attributes: map[string]interface{}{},
Expand All @@ -597,6 +601,8 @@ a paragraph`
types.BlankLine{},
types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"},
types.BlankLine{},
types.DocumentAttributeDeclaration{Name: "hardbreaks"},
types.BlankLine{},
types.Paragraph{
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
Expand Down Expand Up @@ -640,7 +646,7 @@ a paragraph`
})
})

Context("document Attribute Substitutions", func() {
Context("document attribute substitutions", func() {

It("paragraph with attribute substitution", func() {
actualContent := `:author: Xavier
Expand Down
66 changes: 66 additions & 0 deletions pkg/parser/paragraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,72 @@ a paragraph`
}
verify(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})

Context("paragraphs with line break", func() {

It("with explicit line break", func() {

actualContent := `foo +
bar
baz`
expectedResult := types.Paragraph{
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
{
types.StringElement{Content: "foo"},
types.LineBreak{},
},
{
types.StringElement{Content: "bar"},
},
{
types.StringElement{Content: "baz"},
},
},
}
verify(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})

It("with paragraph attribute", func() {

actualContent := `[%hardbreaks]
foo
bar
baz`
expectedResult := types.Paragraph{
Attributes: types.ElementAttributes{
types.AttrHardBreaks: nil,
},
Lines: []types.InlineElements{
{
types.StringElement{Content: "foo"},
},
{
types.StringElement{Content: "bar"},
},
{
types.StringElement{Content: "baz"},
},
},
}
verify(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})
})

// It("paragraph with InlineElementID", func() {
// actualContent := `foo [[id]] bar`
// expectedResult := types.Paragraph{
// Attributes: types.ElementAttributes{},
// Lines: []types.InlineElements{
// {
// types.StringElement{Content: "foo "},
// types.StringElement{Content: " bar"},
// },
// },
// }
// verify(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
// })

})

Context("admonition paragraphs", func() {
Expand Down
Loading

0 comments on commit b1ccebb

Please sign in to comment.