Skip to content

Commit

Permalink
feat(parser): allow id and title on paragraphs (#16)
Browse files Browse the repository at this point in the history
Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon authored Aug 22, 2017
1 parent a897a73 commit c499d94
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 34 deletions.
20 changes: 11 additions & 9 deletions parser/asciidoc-grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,59 @@ Document <- blocks:DocumentBlock* EOF {
return types.NewDocument(blocks.([]interface{}))
}

DocumentBlock <- !EOF content:(Section / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
DocumentBlock <- !EOF content:(Section / StandaloneBlock) {
return content.(types.DocElement), nil
}

StandaloneBlock <- List / BlockImage / DelimitedBlock / Paragraph / MetadataElement / BlankLine

Section <- Section1 / Section2 / Section3 / Section4 / Section5 / Section6

Section1 <- heading:(Heading1) elements:(Section1Block*) {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section1Block <- content:(Section2 / Section3 / Section4 / Section5 / Section6 / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section1Block <- content:(Section2 / Section3 / Section4 / Section5 / Section6 / StandaloneBlock) {
return content.(types.DocElement), nil
}

Section2 <- heading:(Heading2) elements:(Section2Block*) {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section2Block <- !Section1 !Section2 content:(Section3 / Section4 / Section5 / Section6 / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section2Block <- !Section1 !Section2 content:(Section3 / Section4 / Section5 / Section6 / StandaloneBlock) {
return content.(types.DocElement), nil
}

Section3 <- heading:(Heading3) elements:(Section3Block*) &(Section3)* {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section3Block <- !Section1 !Section2 !Section3 content:(Section4 / Section5 / Section6 / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section3Block <- !Section1 !Section2 !Section3 content:(Section4 / Section5 / Section6 / StandaloneBlock) {
return content.(types.DocElement), nil
}

Section4 <- heading:(Heading4) elements:(Section4Block*) {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section4Block <- !Section1 !Section2 !Section3 !Section4 content:(Section5 / Section6 / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section4Block <- !Section1 !Section2 !Section3 !Section4 content:(Section5 / Section6 / StandaloneBlock) {
return content.(types.DocElement), nil
}

Section5 <- heading:(Heading5) elements:(Section5Block*) {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section5Block <- !Section1 !Section2 !Section3 !Section4 !Section5 content:(Section6 / List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section5Block <- !Section1 !Section2 !Section3 !Section4 !Section5 content:(Section6 / StandaloneBlock) {
return content.(types.DocElement), nil
}

Section6 <- heading:(Heading6) elements:(Section6Block*) {
return types.NewSection(heading.(*types.Heading), elements.([]interface{}))
}

Section6Block <- !Section1 !Section2 !Section3 !Section4 !Section5 !Section6 content:(List / BlockImage / DelimitedBlock / MetadataElement / Paragraph / BlankLine) {
Section6Block <- !Section1 !Section2 !Section3 !Section4 !Section5 !Section6 content:(StandaloneBlock) {
return content.(types.DocElement), nil
}

Expand Down Expand Up @@ -117,8 +119,8 @@ ListItemContent <- lines:(!(WS* ('*'+ / '-') WS+) InlineContent)+ {
// Paragraphs
// ------------------------------------------
// a paragraph is a group of line ending with a blank line (or end of file)
Paragraph <- lines:(InlineContent)+ {
return types.NewParagraph(c.text, lines.([]interface{}))
Paragraph <- metadata:(MetadataElement)* lines:(InlineContent)+ {
return types.NewParagraph(c.text, lines.([]interface{}), metadata.([]interface{}))
}

// an inline content element may begin and end with spaces,
Expand Down
1 change: 1 addition & 0 deletions parser/asciidoc_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func verify(t GinkgoTInterface, expectedDocument *types.Document, content string
}
require.Nil(t, err)
actualDocument := result.(*types.Document)
t.Logf("actual document structure: %+v", actualDocument.Elements)
t.Logf("actual document: %s", actualDocument.String(0))
t.Logf("expected document: %s", expectedDocument.String(0))
assert.EqualValues(t, *expectedDocument, *actualDocument)
Expand Down
2 changes: 1 addition & 1 deletion parser/block_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ var _ = Describe("Parsing Block Images", func() {
Height: &height,
},
ID: &types.ElementID{Value: "img-foobar"},
Title: &types.ElementTitle{Content: "A title to foobar"},
Title: &types.ElementTitle{Value: "A title to foobar"},
Link: &types.ElementLink{Path: "http://foo.bar"},
},
},
Expand Down
2 changes: 1 addition & 1 deletion parser/meta_elements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ var _ = Describe("Parsing Meta Elements", func() {
actualContent := ".a title"
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementTitle{Content: "a title"},
&types.ElementTitle{Value: "a title"},
},
}
verify(GinkgoT(), expectedDocument, actualContent)
Expand Down
50 changes: 48 additions & 2 deletions parser/paragraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

var _ = Describe("Parsing Paragraphs", func() {

It("inline 1 word", func() {
It("paragraph with 1 word", func() {
actualContent := "hello"
expectedDocument := &types.Document{
Elements: []types.DocElement{
Expand All @@ -25,7 +25,7 @@ var _ = Describe("Parsing Paragraphs", func() {
verify(GinkgoT(), expectedDocument, actualContent)
})

It("inline simple", func() {
It("paragraph with few words", func() {
actualContent := "a paragraph with some content"
expectedDocument := &types.Document{
Elements: []types.DocElement{
Expand All @@ -42,4 +42,50 @@ var _ = Describe("Parsing Paragraphs", func() {
}
verify(GinkgoT(), expectedDocument, actualContent)
})

It("paragraph with bold content", func() {
actualContent := "a paragraph with *some bold content*"
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.Paragraph{
Lines: []*types.InlineContent{
&types.InlineContent{
Elements: []types.DocElement{
&types.StringElement{Content: "a paragraph with "},
&types.QuotedText{
Kind: types.Bold,
Elements: []types.DocElement{
&types.StringElement{Content: "some bold content"},
},
},
},
},
},
},
},
}
verify(GinkgoT(), expectedDocument, actualContent)
})

It("paragraph with id and title", func() {
actualContent := `[#foo]
.a title
a paragraph`
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.Paragraph{
ID: &types.ElementID{Value: "foo"},
Title: &types.ElementTitle{Value: "a title"},
Lines: []*types.InlineContent{
&types.InlineContent{
Elements: []types.DocElement{
&types.StringElement{Content: "a paragraph"},
},
},
},
},
},
}
verify(GinkgoT(), expectedDocument, actualContent)
})
})
2 changes: 1 addition & 1 deletion renderer/html5/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func init() {
<div class="content">
{{if .Link}}<a class="image" href="{{.Link.Path}}">{{end}}<img src="{{.Macro.Path}}" alt="{{.Macro.Alt}}"{{if .Macro.Width}} width="{{.Macro.Width}}"{{end}}{{if .Macro.Height}} height="{{.Macro.Height}}"{{end}}>{{if .Link}}</a>{{end}}
</div>{{if .Title}}
<div class="title">{{.Title.Content}}</div>
<div class="title">{{.Title.Value}}</div>
{{else}}
{{end}}</div>`)
}
Expand Down
32 changes: 22 additions & 10 deletions renderer/html5/paragraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,37 @@ var paragraphTmpl *template.Template
// initializes the template
func init() {
paragraphTmpl = newTemplate("paragraph",
`<div class="paragraph">
<p>{{.}}</p>
`<div {{ if .ID }}id="{{.ID.Value}}" {{ end }}class="paragraph">{{ if .Title}}
<div class="title">{{.Title.Value}}</div>{{ end }}
<p>{{.Lines}}</p>
</div>`)
}

func renderParagraph(ctx context.Context, paragraph types.Paragraph) ([]byte, error) {
renderedElementsBuff := bytes.NewBuffer(make([]byte, 0))
for _, line := range paragraph.Lines {
renderedElement, err := renderInlineContent(ctx, *line)
renderedLinesBuff := bytes.NewBuffer(make([]byte, 0))
for i, line := range paragraph.Lines {
renderedLine, err := renderInlineContent(ctx, *line)
if err != nil {
return nil, errors.Wrapf(err, "unable to render paragraph element")
return nil, errors.Wrapf(err, "unable to render paragraph line")
}
renderedElementsBuff.Write(renderedElement)
renderedLinesBuff.Write(renderedLine)
if i < len(paragraph.Lines)-1 {
renderedLinesBuff.WriteString("\n")
}

}
result := bytes.NewBuffer(make([]byte, 0))
// here we must preserve the HTML tags
err := paragraphTmpl.Execute(result, template.HTML(renderedElementsBuff.String()))
err := paragraphTmpl.Execute(result, struct {
ID *types.ElementID
Title *types.ElementTitle
Lines template.HTML
}{
ID: paragraph.ID,
Title: paragraph.Title,
Lines: template.HTML(renderedLinesBuff.String()), // here we must preserve the HTML tags
})
if err != nil {
return nil, errors.Wrapf(err, "unable to render inline content")
return nil, errors.Wrapf(err, "unable to render paragraph")
}
// log.Debugf("rendered paragraph: %s", result.Bytes())
return result.Bytes(), nil
Expand Down
18 changes: 15 additions & 3 deletions renderer/html5/paragraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@ package html5_test
import . "github.com/onsi/ginkgo"

var _ = Describe("Rendering Paragraph", func() {
It("some paragraph", func() {

content := "*bold content* \n" +
"with more content afterwards..."
It("a standalone paragraph", func() {
content := `*bold content*
with more content afterwards...`
expected := `<div class="paragraph">
<p><strong>bold content</strong>
with more content afterwards...</p>
</div>`
verify(GinkgoT(), expected, content)
})

It("a standalone paragraph with an ID and a title", func() {
content := `[#foo]
.a title
*bold content* with more content afterwards...`
expected := `<div id="foo" class="paragraph">
<div class="title">a title</div>
<p><strong>bold content</strong> with more content afterwards...</p>
</div>`
verify(GinkgoT(), expected, content)
Expand Down
4 changes: 2 additions & 2 deletions renderer/html5/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func renderElement(ctx context.Context, element types.DocElement) ([]byte, error
return renderInlineContent(ctx, *element.(*types.InlineContent))
case *types.StringElement:
return renderStringElement(ctx, *element.(*types.StringElement))
case *types.BlankLine:
// return []byte("\n"), nil
case *types.BlankLine, *types.ElementID, *types.ElementLink, *types.ElementTitle:
// ignored in the output
return make([]byte, 0), nil
default:
return nil, errors.Errorf("unsupported type of element: %v", reflect.TypeOf(element))
Expand Down
18 changes: 13 additions & 5 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,16 +393,24 @@ func (c *ListItemContent) Accept(v Visitor) error {
// Paragraph the structure for the paragraph
type Paragraph struct {
Lines []*InlineContent
ID *ElementID
Title *ElementTitle
}

//NewParagraph initializes a new `Paragraph`
func NewParagraph(text []byte, lines []interface{}) (*Paragraph, error) {
func NewParagraph(text []byte, lines []interface{}, metadata []interface{}) (*Paragraph, error) {
log.Debugf("Initializing a new Paragraph with %d line(s)", len(lines))
id, title, _ := newMetaElements(metadata)

typedLines := make([]*InlineContent, 0)
for _, line := range lines {
typedLines = append(typedLines, line.(*InlineContent))
}
return &Paragraph{Lines: typedLines}, nil
return &Paragraph{
Lines: typedLines,
ID: id,
Title: title,
}, nil
}

//String implements the DocElement#String() method
Expand Down Expand Up @@ -773,7 +781,7 @@ func (e *ElementID) Accept(v Visitor) error {

// ElementTitle the structure for element IDs
type ElementTitle struct {
Content string
Value string
}

//NewElementTitle initializes a new `ElementTitle` from the given content
Expand All @@ -783,12 +791,12 @@ func NewElementTitle(content []interface{}) (*ElementTitle, error) {
return nil, errors.Wrapf(err, "failed to initialize a new ElementTitle")
}
log.Debugf("Initializing a new ElementTitle with content=%s", c)
return &ElementTitle{Content: *c}, nil
return &ElementTitle{Value: *c}, nil
}

//String implements the DocElement#String() method
func (e *ElementTitle) String(indentLevel int) string {
return fmt.Sprintf("%s<%v> %s", indent(indentLevel), reflect.TypeOf(e), e.Content)
return fmt.Sprintf("%s<%v> %s", indent(indentLevel), reflect.TypeOf(e), e.Value)
}

//Accept implements DocElement#Accept(Visitor)
Expand Down

0 comments on commit c499d94

Please sign in to comment.