From f2931da8d244e2c63b22933fc58e1a7f3a490ce6 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Tue, 23 Jun 2020 10:14:51 -0700 Subject: [PATCH] refactor(renderer): Simplify templates (#634) This eliminates the `ContextualPipeline`, and simplifies the templates a bit, making them hopefully much easier to read and maintain, and also to build derivatives. The templates no longer require the use of functions, for the most part. In particular the detailed rendering functions for nesting are completely hidden from the templates, instead there is just a generic `.Content` field which represents the rendered content for nested content. While here, I've added `.Roles` support in the renderer for top level block. These won't be used until the parser is updated to provide Role attributes for them though. A lot of the templates use the "sanitized" type to track the fact that they can be trusted for HTML safety. None of the tests were modified, in spite of the size of this change. This is foundation work both for future backends (ePUB, docbook), as well as further improvements to the existing rendering (for example adding support for table styling.) --- pkg/renderer/sgml/callout_list.go | 56 ++- pkg/renderer/sgml/contextual_pipeline.go | 11 - pkg/renderer/sgml/delimited_block.go | 325 +++++++++++------- pkg/renderer/sgml/element_id.go | 10 +- pkg/renderer/sgml/element_role.go | 7 +- pkg/renderer/sgml/elements.go | 22 -- pkg/renderer/sgml/footnote_reference.go | 57 ++- pkg/renderer/sgml/html5/callout_list.go | 18 +- pkg/renderer/sgml/html5/delimited_block.go | 135 ++++---- pkg/renderer/sgml/html5/footnote_reference.go | 13 +- pkg/renderer/sgml/html5/labeled_list.go | 60 ++-- pkg/renderer/sgml/html5/literal_blocks.go | 13 +- pkg/renderer/sgml/html5/ordered_list.go | 20 +- pkg/renderer/sgml/html5/paragraph.go | 81 +++-- pkg/renderer/sgml/html5/section.go | 27 +- pkg/renderer/sgml/html5/table.go | 39 ++- pkg/renderer/sgml/html5/table_of_contents.go | 17 +- pkg/renderer/sgml/html5/templates.go | 132 +++---- pkg/renderer/sgml/html5/unordered_list.go | 17 +- pkg/renderer/sgml/icon.go | 4 +- pkg/renderer/sgml/image.go | 4 +- pkg/renderer/sgml/labeled_list.go | 70 +++- pkg/renderer/sgml/literal_blocks.go | 25 +- pkg/renderer/sgml/ordered_list.go | 71 ++-- pkg/renderer/sgml/paragraph.go | 246 +++++++------ pkg/renderer/sgml/quoted_text.go | 22 +- pkg/renderer/sgml/renderer.go | 18 +- pkg/renderer/sgml/section.go | 81 +++-- pkg/renderer/sgml/sgml_renderer.go | 150 ++++---- pkg/renderer/sgml/table.go | 148 +++++++- pkg/renderer/sgml/table_of_contents.go | 63 +++- pkg/renderer/sgml/templates.go | 132 +++---- pkg/renderer/sgml/unordered_list.go | 53 ++- pkg/renderer/sgml/xhtml5/delimited_block.go | 37 +- .../sgml/xhtml5/footnote_reference.go | 8 +- pkg/renderer/sgml/xhtml5/labeled_list.go | 20 +- pkg/renderer/sgml/xhtml5/paragraph.go | 32 +- pkg/renderer/sgml/xhtml5/table.go | 26 +- pkg/renderer/sgml/xhtml5/templates.go | 2 +- 39 files changed, 1310 insertions(+), 962 deletions(-) delete mode 100644 pkg/renderer/sgml/contextual_pipeline.go diff --git a/pkg/renderer/sgml/callout_list.go b/pkg/renderer/sgml/callout_list.go index 53561e05..61734d2d 100644 --- a/pkg/renderer/sgml/callout_list.go +++ b/pkg/renderer/sgml/callout_list.go @@ -1,6 +1,7 @@ package sgml import ( + "io" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -10,22 +11,53 @@ import ( func (r *sgmlRenderer) renderCalloutList(ctx *renderer.Context, l types.CalloutList) (string, error) { result := &strings.Builder{} - err := r.calloutList.Execute(result, ContextualPipeline{ + content := &strings.Builder{} + + for _, item := range l.Items { + + err := r.renderCalloutListItem(ctx, content, item) + if err != nil { + return "", errors.Wrap(err, "unable to render callout list item") + } + } + err := r.calloutList.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content sanitized + Items []types.CalloutListItem + }{ Context: ctx, - Data: struct { - ID string - Title string - Role string - Items []types.CalloutListItem - }{ - ID: r.renderElementID(l.Attributes), - Title: l.Attributes.GetAsStringWithDefault(types.AttrTitle, ""), - Role: l.Attributes.GetAsStringWithDefault(types.AttrRole, ""), - Items: l.Items, - }, + ID: r.renderElementID(l.Attributes), + Title: r.renderElementTitle(l.Attributes), + Roles: r.renderElementRoles(l.Attributes), + Content: sanitized(content.String()), + Items: l.Items, }) if err != nil { return "", errors.Wrap(err, "unable to render callout list") } return result.String(), nil } + +func (r *sgmlRenderer) renderCalloutListItem(ctx *renderer.Context, w io.Writer, item types.CalloutListItem) error { + + content, err := r.renderListElements(ctx, item.Elements) + if err != nil { + return errors.Wrap(err, "unable to render callout list item content") + } + err = r.calloutListItem.Execute(w, struct { + Context *renderer.Context + Ref int + Content sanitized + }{ + Context: ctx, + Ref: item.Ref, + Content: sanitized(content), + }) + if err != nil { + return errors.Wrap(err, "unable to render callout list") + } + return nil +} diff --git a/pkg/renderer/sgml/contextual_pipeline.go b/pkg/renderer/sgml/contextual_pipeline.go deleted file mode 100644 index 2ad94a2f..00000000 --- a/pkg/renderer/sgml/contextual_pipeline.go +++ /dev/null @@ -1,11 +0,0 @@ -package sgml - -import "github.com/bytesparadise/libasciidoc/pkg/renderer" - -// ContextualPipeline carries the renderer context along with -// the pipeline data to process in a template or in a nested template -type ContextualPipeline struct { - Context *renderer.Context - // The actual pipeline - Data interface{} -} diff --git a/pkg/renderer/sgml/delimited_block.go b/pkg/renderer/sgml/delimited_block.go index 5110a78d..0b999fae 100644 --- a/pkg/renderer/sgml/delimited_block.go +++ b/pkg/renderer/sgml/delimited_block.go @@ -2,7 +2,6 @@ package sgml import ( "bytes" - "strconv" "strings" "github.com/alecthomas/chroma" @@ -51,17 +50,25 @@ func (r *sgmlRenderer) renderFencedBlock(ctx *renderer.Context, b types.Delimite ctx.WithinDelimitedBlock = true ctx.IncludeBlankLine = true result := &strings.Builder{} - err := r.fencedBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Elements: discardTrailingBlankLines(b.Elements), - }, + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElement(ctx, elements) + if err != nil { + return "", errors.Wrap(err, "unable to render fenced block content") + } + err = r.fencedBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Content: sanitized(content), + Elements: elements, }) return result.String(), err } @@ -76,17 +83,26 @@ func (r *sgmlRenderer) renderListingBlock(ctx *renderer.Context, b types.Delimit ctx.WithinDelimitedBlock = true ctx.IncludeBlankLine = true result := &strings.Builder{} - err := r.listingBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Elements: discardTrailingBlankLines(b.Elements), - }, + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElements(ctx, elements) + if err != nil { + return "", errors.Wrap(err, "unable to render listing block content") + } + + err = r.listingBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Content: sanitized(content), + Elements: discardTrailingBlankLines(b.Elements), }) return result.String(), err } @@ -101,24 +117,19 @@ func (r *sgmlRenderer) renderSourceBlock(ctx *renderer.Context, b types.Delimite ctx.WithinDelimitedBlock = true ctx.IncludeBlankLine = true // first, render the content - contentBuf := &strings.Builder{} - err := r.sourceBlockContent.Execute(contentBuf, ContextualPipeline{ - Context: ctx, - Data: struct { - Elements []interface{} - }{ - Elements: discardTrailingBlankLines(b.Elements), - }}) + + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElements(ctx, elements) + if err != nil { return "", err } - content := contentBuf.String() highlighter, _ := ctx.Attributes.GetAsString(types.AttrSyntaxHighlighter) language, found := b.Attributes.GetAsString(types.AttrLanguage) if found && highlighter == "pygments" { // using github.com/alecthomas/chroma to highlight the content - contentBuf = &strings.Builder{} + contentBuf := &strings.Builder{} lexer := lexers.Get(language) lexer = chroma.Coalesce(lexer) style := styles.Fallback @@ -152,8 +163,9 @@ func (r *sgmlRenderer) renderSourceBlock(ctx *renderer.Context, b types.Delimite result := &bytes.Buffer{} err = r.sourceBlock.Execute(result, struct { - ID string + ID sanitized Title string + Roles sanitized Language string SyntaxHighlighter string Content string @@ -161,91 +173,136 @@ func (r *sgmlRenderer) renderSourceBlock(ctx *renderer.Context, b types.Delimite ID: r.renderElementID(b.Attributes), Title: r.renderElementTitle(b.Attributes), SyntaxHighlighter: highlighter, + Roles: r.renderElementRoles(b.Attributes), Language: language, Content: content, }) return result.String(), err } -func (r *sgmlRenderer) renderExampleBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { +func (r *sgmlRenderer) renderAdmonitionBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { + kind, _ := b.Attributes[types.AttrAdmonitionKind].(types.AdmonitionKind) + icon, err := r.renderIcon(ctx, types.Icon{Class: string(kind)}, true) + if err != nil { + return "", err + } result := &strings.Builder{} - if k, ok := b.Attributes[types.AttrAdmonitionKind].(types.AdmonitionKind); ok { - icon, err := r.renderIcon(ctx, types.Icon{Class: string(k)}, true) - if err != nil { - return "", err - } - err = r.admonitionBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Class string - Title string - Icon sanitized - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Class: renderClass(k), - Icon: icon, - Title: r.renderElementTitle(b.Attributes), - Elements: discardTrailingBlankLines(b.Elements), - }, - }) - return result.String(), err + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElements(ctx, elements) + if err != nil { + return "", errors.Wrap(err, "unable to render admonition block content") } + err = r.admonitionBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Kind types.AdmonitionKind + Roles sanitized + Icon sanitized + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Kind: kind, + Roles: r.renderElementRoles(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Icon: icon, + Content: sanitized(content), + Elements: elements, + }) + return result.String(), err +} + +func (r *sgmlRenderer) renderExampleBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { + if b.Attributes.Has(types.AttrAdmonitionKind) { + return r.renderAdmonitionBlock(ctx, b) + } + result := &strings.Builder{} + // default, example block - var title string - if b.Attributes.Has(types.AttrTitle) { - title = "Example " + strconv.Itoa(ctx.GetAndIncrementExampleBlockCounter()) + ". " + r.renderElementTitle(b.Attributes) + number := ctx.GetAndIncrementExampleBlockCounter() + elements := b.Elements + content, err := r.renderElements(ctx, elements) + if err != nil { + return "", errors.Wrap(err, "unable to render example block content") } - err := r.exampleBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: title, - Elements: discardTrailingBlankLines(b.Elements), - }, + err = r.exampleBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + ExampleNumber int + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + ExampleNumber: number, + Content: sanitized(content), + Elements: elements, }) return result.String(), err } func (r *sgmlRenderer) renderQuoteBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { result := &strings.Builder{} - err := r.quoteBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Attribution Attribution - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Attribution: newDelimitedBlockAttribution(b), - Elements: b.Elements, - }, + + content, err := r.renderElements(ctx, b.Elements) + if err != nil { + return "", errors.Wrap(err, "unable to render example block content") + } + + err = r.quoteBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Attribution Attribution + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Attribution: newDelimitedBlockAttribution(b), + Content: sanitized(content), + Elements: b.Elements, }) return result.String(), err } func (r *sgmlRenderer) renderVerseBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { result := &strings.Builder{} - err := r.verseBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Attribution Attribution - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Attribution: newDelimitedBlockAttribution(b), - Elements: discardTrailingBlankLines(b.Elements), - }, + elements := discardTrailingBlankLines(b.Elements) + content := &strings.Builder{} + + for _, item := range elements { + s, err := r.renderVerseBlockElement(ctx, item) + if err != nil { + return "", errors.Wrap(err, "unable to render verse block element") + } + content.WriteString(s) + } + err := r.verseBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Attribution Attribution + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Attribution: newDelimitedBlockAttribution(b), + Content: sanitized(content.String()), + Elements: elements, }) return result.String(), err } @@ -258,7 +315,7 @@ func (r *sgmlRenderer) renderVerseBlockElement(ctx *renderer.Context, element in ctx.IncludeBlankLine = true switch e := element.(type) { case types.Paragraph: - return r.renderVerseBlockParagraph(ctx, e) + return r.renderLines(ctx, e.Lines) case types.BlankLine: return r.renderBlankLine(ctx, e) default: @@ -266,48 +323,52 @@ func (r *sgmlRenderer) renderVerseBlockElement(ctx *renderer.Context, element in } } -func (r *sgmlRenderer) renderVerseBlockParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { - log.Debugf("rendering paragraph with %d line(s) within a delimited block or a list", len(p.Lines)) - result := &strings.Builder{} - err := r.verseBlockParagraph.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - Lines [][]interface{} - }{ - Lines: p.Lines, - }, - }) - return result.String(), err -} - func (r *sgmlRenderer) renderSidebarBlock(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { result := &strings.Builder{} - err := r.sidebarBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Elements: discardTrailingBlankLines(b.Elements), - }, + + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElements(ctx, elements) + if err != nil { + return "", errors.Wrap(err, "unable to render sidebar block content") + } + + err = r.sidebarBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content sanitized + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Content: sanitized(content), + Elements: discardTrailingBlankLines(b.Elements), }) return result.String(), err } func (r *sgmlRenderer) renderPassthrough(ctx *renderer.Context, b types.DelimitedBlock) (string, error) { result := &strings.Builder{} - err := r.passthroughBlock.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Elements []interface{} - }{ - ID: r.renderElementID(b.Attributes), - Elements: discardTrailingBlankLines(b.Elements), - }, + elements := discardTrailingBlankLines(b.Elements) + content, err := r.renderElement(ctx, b.Elements) + if err != nil { + return "", errors.Wrap(err, "unable to render passthrough") + } + err = r.passthroughBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Roles sanitized + Content string + Elements []interface{} + }{ + Context: ctx, + ID: r.renderElementID(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Content: content, + Elements: elements, }) return result.String(), err } diff --git a/pkg/renderer/sgml/element_id.go b/pkg/renderer/sgml/element_id.go index a102098a..7f285557 100644 --- a/pkg/renderer/sgml/element_id.go +++ b/pkg/renderer/sgml/element_id.go @@ -1,10 +1,14 @@ package sgml -import "github.com/bytesparadise/libasciidoc/pkg/types" +import ( + "text/template" -func (r *sgmlRenderer) renderElementID(attrs types.Attributes) string { + "github.com/bytesparadise/libasciidoc/pkg/types" +) + +func (r *sgmlRenderer) renderElementID(attrs types.Attributes) sanitized { if id, ok := attrs[types.AttrID].(string); ok { - return id + return sanitized(template.HTMLEscapeString(id)) } return "" } diff --git a/pkg/renderer/sgml/element_role.go b/pkg/renderer/sgml/element_role.go index 83325954..bfc1caf5 100644 --- a/pkg/renderer/sgml/element_role.go +++ b/pkg/renderer/sgml/element_role.go @@ -1,17 +1,18 @@ package sgml import ( + "html/template" "strings" "github.com/bytesparadise/libasciidoc/pkg/types" ) -func (r *sgmlRenderer) renderElementRoles(attrs types.Attributes) string { +func (r *sgmlRenderer) renderElementRoles(attrs types.Attributes) sanitized { switch r := attrs[types.AttrRole].(type) { case []string: - return strings.Join(r, " ") + return sanitized(template.HTMLEscapeString(strings.Join(r, " "))) case string: - return r + return sanitized(template.HTMLEscapeString(r)) default: return "" } diff --git a/pkg/renderer/sgml/elements.go b/pkg/renderer/sgml/elements.go index 7fc5614c..2edc8fe5 100644 --- a/pkg/renderer/sgml/elements.go +++ b/pkg/renderer/sgml/elements.go @@ -1,7 +1,6 @@ package sgml import ( - "reflect" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -176,24 +175,3 @@ func (r *sgmlRenderer) renderPlainText(ctx *renderer.Context, element interface{ return "", errors.Errorf("unable to render plain string for element of type '%T'", element) } } - -// includeNewline returns an "\n" sequence if the given index is NOT the last entry in the given description lines, empty string otherwise. -// also, it ignores the element if it is a blank line, depending on the context -func (r *sgmlRenderer) includeNewline(ctx renderer.Context, index int, content interface{}) string { - switch reflect.TypeOf(content).Kind() { - case reflect.Slice, reflect.Array: - s := reflect.ValueOf(content) - if _, match := s.Index(index).Interface().(types.BlankLine); match { - if ctx.IncludeBlankLine { - return "\n" // TODO: parameterize this? - } - return "" - } - if index < s.Len()-1 { - return "\n" - } - default: - log.Warnf("content of type '%T' is not an array or a slice", content) - } - return "" -} diff --git a/pkg/renderer/sgml/footnote_reference.go b/pkg/renderer/sgml/footnote_reference.go index ec70c51c..5bae221a 100644 --- a/pkg/renderer/sgml/footnote_reference.go +++ b/pkg/renderer/sgml/footnote_reference.go @@ -2,6 +2,7 @@ package sgml import ( "fmt" + "io" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -9,14 +10,6 @@ import ( "github.com/pkg/errors" ) -func (r *sgmlRenderer) renderFootnote(ctx *renderer.Context, elements []interface{}) (string, error) { - result, err := r.renderInlineElements(ctx, elements) - if err != nil { - return "", errors.Wrapf(err, "unable to render foot note content") - } - return strings.TrimSpace(string(result)), nil -} - func (r *sgmlRenderer) renderFootnoteReference(note types.FootnoteReference) (string, error) { result := &strings.Builder{} if note.ID != types.InvalidFootnoteReference && !note.Duplicate { @@ -83,17 +76,47 @@ func (r *sgmlRenderer) renderFootnotes(ctx *renderer.Context, notes []types.Foot return "", nil } result := &strings.Builder{} - err := r.footnotes.Execute(result, - ContextualPipeline{ - Context: ctx, - Data: struct { - Footnotes []types.Footnote - }{ - Footnotes: notes, - }, - }) + content := &strings.Builder{} + + for _, item := range notes { + if err := r.renderFootnoteItem(ctx, content, item); err != nil { + return "", errors.Wrap(err, "failed to render footnote item") + } + } + + err := r.footnotes.Execute(result, struct { + Context *renderer.Context + Content sanitized + Footnotes []types.Footnote + }{ + Context: ctx, + Content: sanitized(content.String()), + Footnotes: notes, + }) if err != nil { return "", errors.Wrap(err, "failed to render footnotes") } return result.String(), nil } + +func (r *sgmlRenderer) renderFootnoteItem(ctx *renderer.Context, w io.Writer, item types.Footnote) error { + + content, err := r.renderInlineElements(ctx, item.Elements) + if err != nil { + return errors.Wrapf(err, "unable to render foot note content") + } + content = strings.TrimSpace(content) + + err = r.footnoteItem.Execute(w, struct { + Context *renderer.Context + ID int + Ref string + Content sanitized + }{ + Context: ctx, + ID: item.ID, + Ref: item.Ref, + Content: sanitized(content), + }) + return err +} diff --git a/pkg/renderer/sgml/html5/callout_list.go b/pkg/renderer/sgml/html5/callout_list.go index 94cae305..ff0c75e3 100644 --- a/pkg/renderer/sgml/html5/callout_list.go +++ b/pkg/renderer/sgml/html5/callout_list.go @@ -2,12 +2,14 @@ package html5 // initializes the sgml const ( - calloutListTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ $items := .Items }} -{{ if .Title }}
{{ escape .Title }}
-{{ end }}
    -{{ range $itemIndex, $item := $items }}
  1. -{{ renderList $ctx $item.Elements }} -
  2. -{{ end }}
-{{ end }}` + calloutListTmpl = `\n" + + "{{ if .Title }}
{{ escape .Title }}
\n{{ end }}" + + "
    \n" + + "{{ .Content }}" + + "
\n" + + // NB: The items are numbered sequentially. + calloutListItemTmpl = "
  • \n{{ .Content }}\n
  • \n" ) diff --git a/pkg/renderer/sgml/html5/delimited_block.go b/pkg/renderer/sgml/html5/delimited_block.go index 0d628960..6f0afd00 100644 --- a/pkg/renderer/sgml/html5/delimited_block.go +++ b/pkg/renderer/sgml/html5/delimited_block.go @@ -1,79 +1,82 @@ package html5 const ( - fencedBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -
    {{ render $ctx .Elements }}
    -
    -
    {{ end }}` + fencedBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "
    {{ .Content }}
    \n" + + "
    \n" + + "
    " - listingBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -
    {{ renderElements $ctx .Elements }}
    -
    -
    {{ end }}` + listingBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "
    {{ .Content }}
    \n" + + "
    \n" + + "
    " - sourceBlockTmpl = `
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -
    {{ .Content }}
    -
    -
    ` + sourceBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "
    ` +
    +		`` +
    +		"{{ .Content }}
    \n" + + "
    \n" + + "
    " - sourceBlockContentTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ render $ctx .Elements }}{{ end }}` + exampleBlockTmpl = `
    \n" + + "{{ if .Title }}
    Example {{ .ExampleNumber }}. {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "{{ .Content }}\n" + + "
    \n" + + "
    " - exampleBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -{{ renderElements $ctx .Elements }} -
    -
    {{ end }}` + quoteBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "{{ .Content }}\n" + + "
    \n" + + "{{ if .Attribution.First }}
    \n" + + "— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}{{ end }}\n" + + "
    \n{{ end }}" + + "
    " - quoteBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -{{ renderElements $ctx .Elements }} -
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + verseBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    {{ .Content }}
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}{{ end }}\n" + + "
    \n{{ end }}" + + "
    " - verseBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    {{ range $index, $element := .Elements }}{{ renderVerse $ctx $element }}{{ end }}
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + admonitionBlockTmpl = `
    \n" + + "\n" + + "\n" + + "\n" + + "\n\n
    \n{{ .Icon }}\n\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "{{ .Content }}\n" + + "
    \n
    " - verseBlockParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ renderLines $ctx .Lines }}{{ end }}` - - admonitionBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    - - - - - -
    -{{ .Icon }} - -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }}{{ renderElements $ctx .Elements }} -
    -
    {{ end }}` - - sidebarBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    -
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -{{ renderElements $ctx .Elements }} -
    -
    {{ end }}` + sidebarBlockTmpl = "
    \n" + + "
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "{{ .Content }}\n" + + "
    \n" + + "
    " // the name here is weird because "pass" as a prefix triggers a false security warning - pssThroughBlock = `{{ $ctx := .Context }}{{ with .Data }}{{ render $ctx .Elements }}{{ end }}` + pssThroughBlock = `{{ .Content }}` ) diff --git a/pkg/renderer/sgml/html5/footnote_reference.go b/pkg/renderer/sgml/html5/footnote_reference.go index 83f9d38d..126a5c8c 100644 --- a/pkg/renderer/sgml/html5/footnote_reference.go +++ b/pkg/renderer/sgml/html5/footnote_reference.go @@ -5,11 +5,10 @@ const ( footnoteRefTmpl = `[{{ .ID }}]` footnoteRefPlainTmpl = `[{{ .ID }}]` invalidFootnoteTmpl = `[{{ .Ref }}]` - footnotesTmpl = ` -
    -
    {{ $ctx := .Context }}{{ with .Data }}{{ $footnotes := .Footnotes }}{{ range $index, $footnote := $footnotes }} -
    -{{ $footnote.ID }}. {{ renderFootnote $ctx $footnote.Elements }} -
    {{ end }}{{ end }} -
    ` + footnotesTmpl = "\n
    \n
    \n{{ .Content }}
    " + + // arguably this should instead be an ordered list. + footnoteItemTmpl = "
    \n" + + "{{ .ID }}. {{ .Content }}\n" + + "
    \n" ) diff --git a/pkg/renderer/sgml/html5/labeled_list.go b/pkg/renderer/sgml/html5/labeled_list.go index 8a020987..c184ccf4 100644 --- a/pkg/renderer/sgml/html5/labeled_list.go +++ b/pkg/renderer/sgml/html5/labeled_list.go @@ -1,40 +1,32 @@ package html5 const ( - labeledListTmpl = `{{ $ctx := .Context }}{{ with .Data }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }}
    -{{ $items := .Items }}{{ range $itemIndex, $item := $items }}
    {{ renderInline $ctx $item.Term }}
    {{ if $item.Elements }} -
    -{{ renderList $ctx $item.Elements }} -
    {{ end }} -{{ end }}
    -{{ end }}` + labeledListTmpl = `\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n{{ .Content }}
    \n" - labeledListHorizontalTmpl = `{{ $ctx := .Context }}{{ with .Data }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }} - - - - - -{{ end }}{{ else }}
    {{ end }}{{ end }} - -
    {{ $items := .Items }}{{ range $itemIndex, $item := $items }} -{{ renderInline $ctx $item.Term }} -{{ if $item.Elements }} -{{ renderList $ctx $item.Elements }} -{{ if includeNewline $ctx $itemIndex $items }}
    {{ else }}
    -{{ end }}` + labeledListItemTmpl = "
    {{ .Term }}
    \n" + + "{{ if .Content }}
    \n{{ .Content }}\n
    \n{{ end }}" - qAndAListTmpl = `{{ $ctx := .Context }}{{ with .Data }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }}
      -{{ $items := .Items }}{{ range $itemIndex, $item := $items }}
    1. -

      {{ renderInline $ctx $item.Term }}

      -{{ if $item.Elements }}{{ renderList $ctx $item.Elements }}{{ end }} -
    2. -{{ end }}
    -{{ end }}` + labeledListHorizontalTmpl = `\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "\n{{ .Content }}
    \n" + + // Continuation items (multiple terms sharing a single definition) make this a bit more complex. + labeledListHorizontalItemTmpl = "{{ if not .Continuation }}\n" + + "\n{{ else }}
    \n{{ end }}" + + "{{ .Term }}\n" + + "{{ if .Content }}\n\n{{ .Content }}\n\n\n{{ end }}" + + qAndAListTmpl = "\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
      \n{{ .Content }}
    \n" + + qAndAListItemTmpl = "
  • \n

    {{ .Term }}

    \n{{ .Content }}\n
  • \n" ) diff --git a/pkg/renderer/sgml/html5/literal_blocks.go b/pkg/renderer/sgml/html5/literal_blocks.go index 2b69ef18..f2177f70 100644 --- a/pkg/renderer/sgml/html5/literal_blocks.go +++ b/pkg/renderer/sgml/html5/literal_blocks.go @@ -1,10 +1,11 @@ package html5 const ( - literalBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }}
    -
    {{ $lines := .Lines }}{{ range $index, $line := $lines}}{{ $line }}{{ includeNewline $ctx $index $lines }}{{ end }}
    -
    -
    {{ end }}` + literalBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "
    {{ .Content }}
    \n" + + "
    \n" + + "
    " ) diff --git a/pkg/renderer/sgml/html5/ordered_list.go b/pkg/renderer/sgml/html5/ordered_list.go index dbefb3c3..a0a71d39 100644 --- a/pkg/renderer/sgml/html5/ordered_list.go +++ b/pkg/renderer/sgml/html5/ordered_list.go @@ -1,12 +1,16 @@ package html5 const ( - orderedListTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ $items := .Items }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }}
      -{{ range $itemIndex, $item := $items }}
    1. -{{ renderList $ctx $item.Elements }} -
    2. -{{ end }}
    -{{ end }}` + orderedListTmpl = `\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + `\n{{ .Content }}\n" + + orderedListItemTmpl = "
  • \n{{ .Content }}\n
  • \n" ) diff --git a/pkg/renderer/sgml/html5/paragraph.go b/pkg/renderer/sgml/html5/paragraph.go index d153dbd2..35f1292d 100644 --- a/pkg/renderer/sgml/html5/paragraph.go +++ b/pkg/renderer/sgml/html5/paragraph.go @@ -1,52 +1,49 @@ package html5 const ( - paragraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ $renderedLines := renderLines $ctx .Lines .HardBreaks }}
    {{ if ne .Title "" }} -
    {{ escape .Title }}
    {{ end }} -

    {{ $renderedLines }}

    -
    {{ end }}` + paragraphTmpl = "\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "

    {{ .Content }}

    \n" + + "" - admonitionParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ $renderedLines := renderLines $ctx .Lines }}{{ if ne $renderedLines "" }}
    - - - - - -
    -{{ if .Icon }}{{ .Icon }}{{ end }} -{{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -{{ $renderedLines }} -
    -
    {{ end }}{{ end }}` + admonitionParagraphTmpl = `{{ if .Content }}` + + "
    \n" + + "\n\n\n" + + "\n\n
    \n" + + "{{ if .Icon }}{{ .Icon }}{{ end }}\n" + + "\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "{{ .Content }}\n" + + "
    \n
    {{ end }}" - delimitedBlockParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}

    {{ .CheckStyle }}{{ renderLines $ctx .Lines }}

    {{ end }}` + delimitedBlockParagraphTmpl = `

    {{ .CheckStyle }}{{ .Content }}

    ` - sourceParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    -
    -
    {{ if .Language }}{{ else }}{{ end }}{{ renderLines $ctx .Lines }}
    -
    -
    {{ end }}` + sourceParagraphTmpl = "
    \n" + + "
    \n" + + "
    " +
    +		`` +
    +		"{{ .Content }}" +
    +		"
    \n" + + "
    \n" + + "
    " - verseParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    {{ renderLines $ctx .Lines plainText }}
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + verseParagraphTmpl = "
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    {{ .Content }}
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}\n{{ else }}\n{{ end }}" + + "
    \n{{ end }}
    " - quoteParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -{{ renderLines $ctx .Lines }} -
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + quoteParagraphTmpl = "
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "{{ .Content }}\n" + + "
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}\n{{ else }}\n{{ end }}" + + "
    \n{{ end }}
    " - manpageNameParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ $renderedLines := renderLines $ctx .Lines }}

    {{ $renderedLines }}

    {{ end }}` + manpageNameParagraphTmpl = `

    {{ .Content }}

    ` ) diff --git a/pkg/renderer/sgml/html5/section.go b/pkg/renderer/sgml/html5/section.go index 6e2e9989..099f3432 100644 --- a/pkg/renderer/sgml/html5/section.go +++ b/pkg/renderer/sgml/html5/section.go @@ -2,23 +2,16 @@ package html5 // initializes the sgml const ( - preambleTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ if .Wrapper }}
    -
    -{{ end }}{{ renderElements $ctx .Elements }}{{ if .Wrapper }} -
    -
    {{ end }}{{ end }}` + preambleTmpl = "{{ if .Wrapper }}
    \n
    \n{{ end }}" + + `{{ .Content }}` + + "{{ if .Wrapper }}\n
    \n
    {{ end }}" - sectionOneTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    -{{ .SectionTitle }} -
    {{ $elements := renderElements $ctx .Elements }}{{ if $elements }} -{{ $elements }}{{ end }} -
    -
    {{ end }}` + sectionContentTmpl = "
    \n" + + "{{ .Header }}" + + "{{ if eq .Level 1 }}\n
    {{ end }}" + + "{{ if .Content }}\n{{ .Content }}{{ end }}\n" + + "{{ if eq .Level 1 }}
    \n{{ end }}" + + "
    " - sectionContentTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    -{{ .SectionTitle }}{{ $elements := renderElements $ctx .Elements }}{{ if $elements }} -{{ $elements }}{{ end }} -
    {{ end }}` - - sectionHeaderTmpl = `{{ .Content }}` + sectionHeaderTmpl = `{{ .Content }}` ) diff --git a/pkg/renderer/sgml/html5/table.go b/pkg/renderer/sgml/html5/table.go index c254185b..46a310e0 100644 --- a/pkg/renderer/sgml/html5/table.go +++ b/pkg/renderer/sgml/html5/table.go @@ -1,20 +1,27 @@ package html5 const ( - tableTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ if .Lines }} -{{ if .Title }} -{{ end }}-{{ $cellWidths := .CellWidths }}{{ range $index, $width := $cellWidths }}{{ includeNewline $ctx $index $cellWidths }}{{ end }} - -{{ if .Header }}{{ if .Header.Cells }} - -{{ $headerCells := .Header.Cells }}{{ range $index, $cell := $headerCells }}{{ includeNewline $ctx $index $headerCells }}{{ end }} - - -{{ end }}{{ end }} -{{ range $indexLine, $line := .Lines }} -{{ range $indexCells, $cell := $line.Cells }}{{ includeNewline $ctx $indexCells $line.Cells }}{{ end }} - -{{ end }}{{ end }} -
    {{ escape .Title }}
    {{ renderInline $ctx $cell }}

    {{ renderInline $ctx $cell }}

    {{ end }}` + // TODO: These class settings need to be overridable via attributes + tableTmpl = "\n" + + "{{ if .Title }}\n{{ end }}" + + "{{ if .Body }}" + + "\n" + + "{{ range $i, $w := .CellWidths }}\n{{ end}}" + + "\n" + + "{{ .Header }}" + + "{{ .Body }}" + + "{{ end }}" + + "
    Table {{ .TableNumber }}. {{ escape .Title }}
    " + + tableBodyTmpl = "{{ if .Content }}\n{{ .Content }}\n{{ end }}" + + tableHeaderTmpl = "{{ if .Content }}\n\n{{ .Content }}\n\n{{ end }}" + + tableRowTmpl = "\n{{ .Content }}\n" + + // TODO: review these alignment choices ... should they be overrideable? + + tableHeaderCellTmpl = "{{ .Content }}\n" + + tableCellTmpl = "

    {{ .Content }}

    \n" ) diff --git a/pkg/renderer/sgml/html5/table_of_contents.go b/pkg/renderer/sgml/html5/table_of_contents.go index 87ddd288..081e5479 100644 --- a/pkg/renderer/sgml/html5/table_of_contents.go +++ b/pkg/renderer/sgml/html5/table_of_contents.go @@ -1,14 +1,13 @@ package html5 const ( - tocRootTmpl = `
    -
    Table of Contents
    -{{ . }} -
    ` + tocRootTmpl = "
    \n" + + "
    Table of Contents
    \n" + + "{{ . }}\n" + + "
    " - tocSectionTmpl = `{{ $ctx := .Context }}{{ with .Data }}
      -{{ range .Sections }}
    • {{ .Title }}{{ if .Children }} -{{ renderToC $ctx .Children }} -
    • {{else}}{{end}} -{{end}}{{end}}
    ` + tocSectionTmpl = "
      \n{{ .Content }}
    " + + tocEntryTmpl = "
  • {{ .Title }}" + + "{{ if .Content }}\n{{ .Content }}\n{{ end }}
  • \n" ) diff --git a/pkg/renderer/sgml/html5/templates.go b/pkg/renderer/sgml/html5/templates.go index 402b58ec..f074a9e5 100644 --- a/pkg/renderer/sgml/html5/templates.go +++ b/pkg/renderer/sgml/html5/templates.go @@ -9,67 +9,77 @@ import ( ) var templates = sgml.Templates{ - AdmonitionBlock: admonitionBlockTmpl, - AdmonitionParagraph: admonitionParagraphTmpl, - Article: articleTmpl, - ArticleHeader: articleHeaderTmpl, - BlankLine: blankLineTmpl, - BlockImage: blockImageTmpl, - BoldText: boldTextTmpl, - CalloutList: calloutListTmpl, - DelimitedBlockParagraph: delimitedBlockParagraphTmpl, - DocumentDetails: documentDetailsTmpl, - DocumentAuthorDetails: documentAuthorDetailsTmpl, - ExternalCrossReference: externalCrossReferenceTmpl, - ExampleBlock: exampleBlockTmpl, - FencedBlock: fencedBlockTmpl, - Footnote: footnoteTmpl, - FootnoteRef: footnoteRefTmpl, - FootnoteRefPlain: footnoteRefPlainTmpl, - Footnotes: footnotesTmpl, - IconFont: iconFontTmpl, - IconImage: iconImageTmpl, - IconText: iconTextTmpl, - InlineIcon: inlineIconTmpl, - InlineImage: inlineImageTmpl, - InternalCrossReference: internalCrossReferenceTmpl, - InvalidFootnote: invalidFootnoteTmpl, - ItalicText: italicTextTmpl, - LabeledList: labeledListTmpl, - LabeledListHorizontal: labeledListHorizontalTmpl, - LineBreak: lineBreakTmpl, - Link: linkTmpl, - ListingBlock: listingBlockTmpl, - LiteralBlock: literalBlockTmpl, - ManpageHeader: manpageHeaderTmpl, - ManpageNameParagraph: manpageNameParagraphTmpl, - MarkedText: markedTextTmpl, - MonospaceText: monospaceTextTmpl, - OrderedList: orderedListTmpl, - PassthroughBlock: pssThroughBlock, - Paragraph: paragraphTmpl, - Preamble: preambleTmpl, - QAndAList: qAndAListTmpl, - QuoteBlock: quoteBlockTmpl, - QuoteParagraph: quoteParagraphTmpl, - SectionContent: sectionContentTmpl, - SectionHeader: sectionHeaderTmpl, - SectionOne: sectionOneTmpl, - SidebarBlock: sidebarBlockTmpl, - SourceBlock: sourceBlockTmpl, - SourceBlockContent: sourceBlockContentTmpl, - SourceParagraph: sourceParagraphTmpl, - StringElement: stringTmpl, - SubscriptText: subscriptTextTmpl, - SuperscriptText: superscriptTextTmpl, - Table: tableTmpl, - TocRoot: tocRootTmpl, - TocSection: tocSectionTmpl, - UnorderedList: unorderedListTmpl, - VerbatimLine: verbatimLineTmpl, - VerseBlock: verseBlockTmpl, - VerseBlockParagraph: verseBlockParagraphTmpl, - VerseParagraph: verseParagraphTmpl, + AdmonitionBlock: admonitionBlockTmpl, + AdmonitionParagraph: admonitionParagraphTmpl, + Article: articleTmpl, + ArticleHeader: articleHeaderTmpl, + BlankLine: blankLineTmpl, + BlockImage: blockImageTmpl, + BoldText: boldTextTmpl, + CalloutList: calloutListTmpl, + CalloutListItem: calloutListItemTmpl, + DelimitedBlockParagraph: delimitedBlockParagraphTmpl, + DocumentDetails: documentDetailsTmpl, + DocumentAuthorDetails: documentAuthorDetailsTmpl, + ExternalCrossReference: externalCrossReferenceTmpl, + ExampleBlock: exampleBlockTmpl, + FencedBlock: fencedBlockTmpl, + Footnote: footnoteTmpl, + FootnoteItem: footnoteItemTmpl, + FootnoteRef: footnoteRefTmpl, + FootnoteRefPlain: footnoteRefPlainTmpl, + Footnotes: footnotesTmpl, + IconFont: iconFontTmpl, + IconImage: iconImageTmpl, + IconText: iconTextTmpl, + InlineIcon: inlineIconTmpl, + InlineImage: inlineImageTmpl, + InternalCrossReference: internalCrossReferenceTmpl, + InvalidFootnote: invalidFootnoteTmpl, + ItalicText: italicTextTmpl, + LabeledList: labeledListTmpl, + LabeledListItem: labeledListItemTmpl, + LabeledListHorizontal: labeledListHorizontalTmpl, + LabeledListHorizontalItem: labeledListHorizontalItemTmpl, + LineBreak: lineBreakTmpl, + Link: linkTmpl, + ListingBlock: listingBlockTmpl, + LiteralBlock: literalBlockTmpl, + ManpageHeader: manpageHeaderTmpl, + ManpageNameParagraph: manpageNameParagraphTmpl, + MarkedText: markedTextTmpl, + MonospaceText: monospaceTextTmpl, + OrderedList: orderedListTmpl, + OrderedListItem: orderedListItemTmpl, + PassthroughBlock: pssThroughBlock, + Paragraph: paragraphTmpl, + Preamble: preambleTmpl, + QAndAList: qAndAListTmpl, + QAndAListItem: qAndAListItemTmpl, + QuoteBlock: quoteBlockTmpl, + QuoteParagraph: quoteParagraphTmpl, + SectionContent: sectionContentTmpl, + SectionHeader: sectionHeaderTmpl, + SidebarBlock: sidebarBlockTmpl, + SourceBlock: sourceBlockTmpl, + SourceParagraph: sourceParagraphTmpl, + StringElement: stringTmpl, + SubscriptText: subscriptTextTmpl, + SuperscriptText: superscriptTextTmpl, + Table: tableTmpl, + TableBody: tableBodyTmpl, + TableCell: tableCellTmpl, + TableHeader: tableHeaderTmpl, + TableHeaderCell: tableHeaderCellTmpl, + TableRow: tableRowTmpl, + TocRoot: tocRootTmpl, + TocEntry: tocEntryTmpl, + TocSection: tocSectionTmpl, + UnorderedList: unorderedListTmpl, + UnorderedListItem: unorderedListItemTmpl, + VerbatimLine: verbatimLineTmpl, + VerseBlock: verseBlockTmpl, + VerseParagraph: verseParagraphTmpl, } var defaultRenderer sgml.Renderer diff --git a/pkg/renderer/sgml/html5/unordered_list.go b/pkg/renderer/sgml/html5/unordered_list.go index f7f9ea9d..58305705 100644 --- a/pkg/renderer/sgml/html5/unordered_list.go +++ b/pkg/renderer/sgml/html5/unordered_list.go @@ -1,12 +1,13 @@ package html5 const ( - unorderedListTmpl = `{{ $ctx := .Context }}{{ with .Data }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }} -{{ $items := .Items }}{{ range $itemIndex, $item := $items }}
  • -{{ $elements := $item.Elements }}{{ renderList $ctx $elements }} -
  • -{{ end }} -{{ end }}` + unorderedListTmpl = `\n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "\n" + + "{{ .Content }}\n" + + unorderedListItemTmpl = "
  • \n{{ .Content }}\n
  • \n" ) diff --git a/pkg/renderer/sgml/icon.go b/pkg/renderer/sgml/icon.go index 353771a4..3f1e64f7 100644 --- a/pkg/renderer/sgml/icon.go +++ b/pkg/renderer/sgml/icon.go @@ -24,12 +24,12 @@ func (r *sgmlRenderer) renderInlineIcon(ctx *renderer.Context, icon types.Icon) Role string Link string Window string - ID string + ID sanitized Icon sanitized }{ Class: icon.Class, Icon: iconStr, - ID: icon.Attributes.GetAsStringWithDefault(types.AttrID, ""), + ID: r.renderElementID(icon.Attributes), Link: icon.Attributes.GetAsStringWithDefault(types.AttrInlineLink, ""), Window: icon.Attributes.GetAsStringWithDefault(types.AttrImageWindow, ""), Role: icon.Attributes.GetAsStringWithDefault(types.AttrRole, ""), diff --git a/pkg/renderer/sgml/image.go b/pkg/renderer/sgml/image.go index ebf564d0..4a71c96a 100644 --- a/pkg/renderer/sgml/image.go +++ b/pkg/renderer/sgml/image.go @@ -16,7 +16,7 @@ func (r *sgmlRenderer) renderImageBlock(ctx *renderer.Context, img types.ImageBl title = "Figure " + strconv.Itoa(ctx.GetAndIncrementImageCounter()) + ". " + EscapeString(t) } err := r.blockImage.Execute(result, struct { - ID string + ID sanitized Title string Role string Href string @@ -25,7 +25,7 @@ func (r *sgmlRenderer) renderImageBlock(ctx *renderer.Context, img types.ImageBl Height string Path string }{ - ID: img.Attributes.GetAsStringWithDefault(types.AttrID, ""), + ID: r.renderElementID(img.Attributes), Title: title, Role: img.Attributes.GetAsStringWithDefault(types.AttrRole, ""), Href: img.Attributes.GetAsStringWithDefault(types.AttrInlineLink, ""), diff --git a/pkg/renderer/sgml/labeled_list.go b/pkg/renderer/sgml/labeled_list.go index 474824a0..11de83f2 100644 --- a/pkg/renderer/sgml/labeled_list.go +++ b/pkg/renderer/sgml/labeled_list.go @@ -1,6 +1,7 @@ package sgml import ( + "io" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -9,26 +10,35 @@ import ( ) func (r *sgmlRenderer) renderLabeledList(ctx *renderer.Context, l types.LabeledList) (string, error) { - tmpl, err := r.getLabeledListTmpl(l) + tmpl, itemTmpl, err := r.getLabeledListTmpl(l) if err != nil { return "", errors.Wrap(err, "unable to render labeled list") } + content := &strings.Builder{} + cont := false + for _, item := range l.Items { + if cont, err = r.renderLabeledListItem(ctx, itemTmpl, content, cont, item); err != nil { + return "", errors.Wrap(err, "unable to render unordered list") + } + } + result := &strings.Builder{} // here we must preserve the HTML tags - err = tmpl.Execute(result, ContextualPipeline{ + err = tmpl.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content sanitized + Items []types.LabeledListItem + }{ Context: ctx, - Data: struct { - ID string - Title string - Role string - Items []types.LabeledListItem - }{ - ID: r.renderElementID(l.Attributes), - Title: r.renderElementTitle(l.Attributes), - Role: l.Attributes.GetAsStringWithDefault(types.AttrRole, ""), - Items: l.Items, - }, + ID: r.renderElementID(l.Attributes), + Title: r.renderElementTitle(l.Attributes), + Roles: r.renderElementRoles(l.Attributes), + Content: sanitized(content.String()), + Items: l.Items, }) if err != nil { return "", errors.Wrap(err, "unable to render labeled list") @@ -37,17 +47,41 @@ func (r *sgmlRenderer) renderLabeledList(ctx *renderer.Context, l types.LabeledL return result.String(), nil } -func (r *sgmlRenderer) getLabeledListTmpl(l types.LabeledList) (*textTemplate, error) { +func (r *sgmlRenderer) getLabeledListTmpl(l types.LabeledList) (*textTemplate, *textTemplate, error) { if layout, ok := l.Attributes["layout"]; ok { switch layout { case "horizontal": - return r.labeledListHorizontal, nil + return r.labeledListHorizontal, r.labeledListHorizontalItem, nil default: - return nil, errors.Errorf("unsupported labeled list layout: %s", layout) + return nil, nil, errors.Errorf("unsupported labeled list layout: %s", layout) } } if l.Attributes.Has(types.AttrQandA) { - return r.qAndAList, nil + return r.qAndAList, r.qAndAListItem, nil } - return r.labeledList, nil + return r.labeledList, r.labeledListItem, nil +} + +func (r *sgmlRenderer) renderLabeledListItem(ctx *renderer.Context, tmpl *textTemplate, w io.Writer, continuation bool, item types.LabeledListItem) (bool, error) { + + term, err := r.renderInlineElements(ctx, item.Term) + if err != nil { + return false, errors.Wrap(err, "unable to render labeled list term") + } + content, err := r.renderListElements(ctx, item.Elements) + if err != nil { + return false, errors.Wrap(err, "unable to render labeled list content") + } + err = tmpl.Execute(w, struct { + Context *renderer.Context + Term sanitized + Content sanitized + Continuation bool + }{ + Context: ctx, + Term: sanitized(term), + Continuation: continuation, + Content: sanitized(content), + }) + return content == "", err } diff --git a/pkg/renderer/sgml/literal_blocks.go b/pkg/renderer/sgml/literal_blocks.go index cb3efe4f..9c85cb8a 100644 --- a/pkg/renderer/sgml/literal_blocks.go +++ b/pkg/renderer/sgml/literal_blocks.go @@ -40,17 +40,22 @@ func (r *sgmlRenderer) renderLiteralBlock(ctx *renderer.Context, b types.Literal lines = b.Lines } result := &strings.Builder{} - err := r.literalBlock.Execute(result, ContextualPipeline{ + err := r.literalBlock.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Content string + Lines []string + }{ + Context: ctx, - Data: struct { - ID string - Title string - Lines []string - }{ - ID: r.renderElementID(b.Attributes), - Title: r.renderElementTitle(b.Attributes), - Lines: lines, - }}) + ID: r.renderElementID(b.Attributes), + Title: r.renderElementTitle(b.Attributes), + Roles: r.renderElementRoles(b.Attributes), + Lines: lines, + Content: strings.Join(lines, "\n"), + }) if err != nil { return "", errors.Wrap(err, "unable to render delimited block") } diff --git a/pkg/renderer/sgml/ordered_list.go b/pkg/renderer/sgml/ordered_list.go index 3b11d697..fdb4857c 100644 --- a/pkg/renderer/sgml/ordered_list.go +++ b/pkg/renderer/sgml/ordered_list.go @@ -1,6 +1,7 @@ package sgml import ( + "io" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -10,25 +11,33 @@ import ( func (r *sgmlRenderer) renderOrderedList(ctx *renderer.Context, l types.OrderedList) (string, error) { result := &strings.Builder{} - err := r.orderedList.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Role string - NumberingStyle string - ListStyle string - Start string - Items []types.OrderedListItem - }{ - ID: r.renderElementID(l.Attributes), - Title: l.Attributes.GetAsStringWithDefault(types.AttrTitle, ""), - Role: l.Attributes.GetAsStringWithDefault(types.AttrRole, ""), - NumberingStyle: getNumberingStyle(l), - ListStyle: r.numberingType(getNumberingStyle(l)), - Start: l.Attributes.GetAsStringWithDefault(types.AttrStart, ""), - Items: l.Items, - }, + content := &strings.Builder{} + + for _, item := range l.Items { + if err := r.renderOrderedListItem(ctx, content, item); err != nil { + return "", errors.Wrap(err, "unable to render unordered list") + } + } + + err := r.orderedList.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + NumberingStyle string + ListStyle string + Start string + Content sanitized + Items []types.OrderedListItem + }{ + ID: r.renderElementID(l.Attributes), + Title: r.renderElementTitle(l.Attributes), + Roles: r.renderElementRoles(l.Attributes), + NumberingStyle: getNumberingStyle(l), + ListStyle: r.numberingType(getNumberingStyle(l)), + Start: l.Attributes.GetAsStringWithDefault(types.AttrStart, ""), + Content: sanitized(content.String()), + Items: l.Items, }) if err != nil { return "", errors.Wrap(err, "unable to render ordered list") @@ -43,19 +52,33 @@ func getNumberingStyle(l types.OrderedList) string { return string(l.Items[0].NumberingStyle) } -// TODO: Move this to the HTML output perhaps. // this numbering style is only really relevant to HTML func (r *sgmlRenderer) numberingType(style string) string { switch style { case string(types.LowerAlpha): - return ` type="a"` + return `a` case string(types.UpperAlpha): - return ` type="A"` + return `A` case string(types.LowerRoman): - return ` type="i"` + return `i` case string(types.UpperRoman): - return ` type="I"` + return `I` default: return "" } } + +func (r *sgmlRenderer) renderOrderedListItem(ctx *renderer.Context, w io.Writer, item types.OrderedListItem) error { + + content, err := r.renderListElements(ctx, item.Elements) + if err != nil { + return errors.Wrap(err, "unable to render unordered list item content") + } + return r.orderedListItem.Execute(w, struct { + Context *renderer.Context + Content sanitized + }{ + Context: ctx, + Content: sanitized(content), + }) +} diff --git a/pkg/renderer/sgml/paragraph.go b/pkg/renderer/sgml/paragraph.go index e49a62c4..f15343db 100644 --- a/pkg/renderer/sgml/paragraph.go +++ b/pkg/renderer/sgml/paragraph.go @@ -11,8 +11,12 @@ import ( func (r *sgmlRenderer) renderParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { result := &strings.Builder{} - id := r.renderElementID(p.Attributes) - var err error + hardbreaks := p.Attributes.Has(types.AttrHardBreaks) || ctx.Attributes.Has(types.DocumentAttrHardBreaks) + content, err := r.renderLines(ctx, p.Lines, r.withHardBreaks(hardbreaks)) + + if err != nil { + return "", errors.Wrap(err, "unable to render paragraph content") + } if _, ok := p.Attributes[types.AttrAdmonitionKind]; ok { return r.renderAdmonitionParagraph(ctx, p) } else if kind, ok := p.Attributes[types.AttrKind]; ok && kind == types.Source { @@ -27,21 +31,20 @@ func (r *sgmlRenderer) renderParagraph(ctx *renderer.Context, p types.Paragraph) return r.renderDelimitedBlockParagraph(ctx, p) } else { log.Debug("rendering a standalone paragraph") - err = r.paragraph.Execute(result, ContextualPipeline{ + err = r.paragraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Roles sanitized + Title string + Lines [][]interface{} + Content sanitized + }{ Context: ctx, - Data: struct { - ID string - Class string - Title string - Lines [][]interface{} - HardBreaks RenderLinesOption - }{ - ID: id, - Class: getParagraphClass(p), - Title: r.renderElementTitle(p.Attributes), - Lines: p.Lines, - HardBreaks: r.withHardBreaks(p.Attributes.Has(types.AttrHardBreaks) || ctx.Attributes.Has(types.DocumentAttrHardBreaks)), - }, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + Content: sanitized(content), + Roles: r.renderElementRoles(p.Attributes), + Lines: p.Lines, }) } if err != nil { @@ -62,21 +65,28 @@ func (r *sgmlRenderer) renderAdmonitionParagraph(ctx *renderer.Context, p types. if err != nil { return "", err } - err = r.admonitionParagraph.Execute(result, ContextualPipeline{ + content, err := r.renderLines(ctx, p.Lines) + if err != nil { + return "", err + } + err = r.admonitionParagraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Icon sanitized + Kind sanitized + Content sanitized + Lines [][]interface{} + }{ Context: ctx, - Data: struct { - ID string - Title string - Class string - Icon sanitized - Lines [][]interface{} - }{ - ID: r.renderElementID(p.Attributes), - Title: r.renderElementTitle(p.Attributes), - Icon: icon, - Lines: p.Lines, - Class: renderClass(k), - }, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + Kind: sanitized(k), + Roles: r.renderElementRoles(p.Attributes), + Icon: icon, + Content: sanitized(content), + Lines: p.Lines, }) return result.String(), err @@ -85,19 +95,26 @@ func (r *sgmlRenderer) renderAdmonitionParagraph(ctx *renderer.Context, p types. func (r *sgmlRenderer) renderSourceParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { log.Debug("rendering source paragraph...") result := &strings.Builder{} - err := r.sourceParagraph.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Language string - Lines [][]interface{} - }{ - ID: r.renderElementID(p.Attributes), - Title: r.renderElementTitle(p.Attributes), - Language: p.Attributes.GetAsStringWithDefault(types.AttrLanguage, ""), - Lines: p.Lines, - }, + + content, err := r.renderLines(ctx, p.Lines) + if err != nil { + return "", errors.Wrap(err, "unable to render source paragraph lines") + } + err = r.sourceParagraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Language string + Content sanitized + Lines [][]interface{} + }{ + + Context: ctx, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + Language: p.Attributes.GetAsStringWithDefault(types.AttrLanguage, ""), + Content: sanitized(content), + Lines: p.Lines, }) return result.String(), err } @@ -105,53 +122,74 @@ func (r *sgmlRenderer) renderSourceParagraph(ctx *renderer.Context, p types.Para func (r *sgmlRenderer) renderVerseParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { log.Debug("rendering verse paragraph...") result := &strings.Builder{} - err := r.verseParagraph.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Attribution Attribution - Lines [][]interface{} - }{ - ID: r.renderElementID(p.Attributes), - Title: r.renderElementTitle(p.Attributes), - Attribution: newParagraphAttribution(p), - Lines: p.Lines, - }, + + content, err := r.renderLines(ctx, p.Lines, r.withPlainText()) + if err != nil { + return "", errors.Wrap(err, "unable to render verse paragraph lines") + } + err = r.verseParagraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Attribution Attribution + Content sanitized + Lines [][]interface{} + }{ + Context: ctx, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + Attribution: newParagraphAttribution(p), + Content: sanitized(content), + Lines: p.Lines, }) + return result.String(), err } func (r *sgmlRenderer) renderQuoteParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { log.Debug("rendering quote paragraph...") result := &strings.Builder{} - err := r.quoteParagraph.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Attribution Attribution - Lines [][]interface{} - }{ - ID: r.renderElementID(p.Attributes), - Title: r.renderElementTitle(p.Attributes), - Attribution: newParagraphAttribution(p), - Lines: p.Lines, - }, + + content, err := r.renderLines(ctx, p.Lines) + if err != nil { + return "", errors.Wrap(err, "unable to render quote paragraph lines") + } + err = r.quoteParagraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Attribution Attribution + Content sanitized + Lines [][]interface{} + }{ + Context: ctx, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + Attribution: newParagraphAttribution(p), + Content: sanitized(content), + Lines: p.Lines, }) + return result.String(), err } func (r *sgmlRenderer) renderManpageNameParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { log.Debug("rendering name section paragraph in manpage...") result := &strings.Builder{} - err := r.manpageNameParagraph.Execute(result, ContextualPipeline{ + + content, err := r.renderLines(ctx, p.Lines) + if err != nil { + return "", errors.Wrap(err, "unable to render quote paragraph lines") + } + + err = r.manpageNameParagraph.Execute(result, struct { + Context *renderer.Context + Content sanitized + Lines [][]interface{} + }{ Context: ctx, - Data: struct { - Lines [][]interface{} - }{ - Lines: p.Lines, - }, + Content: sanitized(content), + Lines: p.Lines, }) return result.String(), err } @@ -159,19 +197,25 @@ func (r *sgmlRenderer) renderManpageNameParagraph(ctx *renderer.Context, p types func (r *sgmlRenderer) renderDelimitedBlockParagraph(ctx *renderer.Context, p types.Paragraph) (string, error) { log.Debugf("rendering paragraph with %d line(s) within a delimited block or a list", len(p.Lines)) result := &strings.Builder{} - err := r.delimitedBlockParagraph.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - CheckStyle string - Lines [][]interface{} - }{ - ID: r.renderElementID(p.Attributes), - Title: r.renderElementTitle(p.Attributes), - CheckStyle: renderCheckStyle(p.Attributes[types.AttrCheckStyle]), - Lines: p.Lines, - }, + + content, err := r.renderLines(ctx, p.Lines) + if err != nil { + return "", errors.Wrap(err, "unable to render delimited block paragraph content") + } + err = r.delimitedBlockParagraph.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + CheckStyle string + Content sanitized + Lines [][]interface{} + }{ + Context: ctx, + ID: r.renderElementID(p.Attributes), + Title: r.renderElementTitle(p.Attributes), + CheckStyle: renderCheckStyle(p.Attributes[types.AttrCheckStyle]), + Content: sanitized(content), + Lines: p.Lines, }) return result.String(), err } @@ -187,32 +231,6 @@ func renderCheckStyle(style interface{}) string { } } -func renderClass(kind types.AdmonitionKind) string { - switch kind { - case types.Tip: - return "tip" - case types.Note: - return "note" - case types.Important: - return "important" - case types.Warning: - return "warning" - case types.Caution: - return "caution" - default: - log.Errorf("unexpected kind of admonition: %v", kind) - return "" - } -} - -func getParagraphClass(p types.Paragraph) string { - result := "paragraph" - if role, found := p.Attributes.GetAsString(types.AttrRole); found { - result = result + " " + role - } - return result -} - func (r *sgmlRenderer) renderElementTitle(attrs types.Attributes) string { if title, found := attrs.GetAsString(types.AttrTitle); found { return strings.TrimSpace(title) diff --git a/pkg/renderer/sgml/quoted_text.go b/pkg/renderer/sgml/quoted_text.go index 82111f8a..48ca8fed 100644 --- a/pkg/renderer/sgml/quoted_text.go +++ b/pkg/renderer/sgml/quoted_text.go @@ -1,23 +1,14 @@ package sgml import ( - "strings" - "text/template" - "github.com/bytesparadise/libasciidoc/pkg/renderer" "github.com/bytesparadise/libasciidoc/pkg/types" "github.com/pkg/errors" + "strings" ) // TODO: The bold, italic, and monospace items should be refactored to support semantic tags instead. -type quotedText struct { - ID string - Roles string - Attributes types.Attributes - Content sanitized -} - func (r *sgmlRenderer) renderQuotedText(ctx *renderer.Context, t types.QuotedText) (string, error) { elementsBuffer := &strings.Builder{} for _, element := range t.Elements { @@ -49,10 +40,15 @@ func (r *sgmlRenderer) renderQuotedText(ctx *renderer.Context, t types.QuotedTex } result := &strings.Builder{} - err := tmpl.Execute(result, "edText{ + err := tmpl.Execute(result, struct { + ID sanitized + Roles sanitized + Attributes types.Attributes + Content sanitized + }{ Attributes: t.Attributes, - ID: template.HTMLEscapeString(r.renderElementID(t.Attributes)), - Roles: template.HTMLEscapeString(r.renderElementRoles(t.Attributes)), + ID: r.renderElementID(t.Attributes), + Roles: r.renderElementRoles(t.Attributes), Content: sanitized(elementsBuffer.String()), }) //nolint: gosec if err != nil { diff --git a/pkg/renderer/sgml/renderer.go b/pkg/renderer/sgml/renderer.go index 4e02f9b0..aa925537 100644 --- a/pkg/renderer/sgml/renderer.go +++ b/pkg/renderer/sgml/renderer.go @@ -36,20 +36,10 @@ func NewRenderer(t Templates) Renderer { } // Establish some default function handlers. r.functions = funcMap{ - "render": r.renderElements, - "renderElements": r.renderElements, - "renderInline": r.renderInlineElements, - "renderList": r.renderListElements, - "renderLines": r.renderLines, - "escape": EscapeString, - "renderToC": r.renderTableOfContentsSections, - "renderFootnote": r.renderFootnote, - "includeNewline": r.includeNewline, - "renderVerse": r.renderVerseBlockElement, - "plainText": r.withPlainText, - "trimRight": r.trimRight, - "trimLeft": r.trimLeft, - "trim": r.trimBoth, + "escape": EscapeString, + "trimRight": r.trimRight, + "trimLeft": r.trimLeft, + "trim": r.trimBoth, } return r diff --git a/pkg/renderer/sgml/section.go b/pkg/renderer/sgml/section.go index fd21f24a..edeb4d19 100644 --- a/pkg/renderer/sgml/section.go +++ b/pkg/renderer/sgml/section.go @@ -1,7 +1,6 @@ package sgml import ( - "strconv" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -15,15 +14,19 @@ func (r *sgmlRenderer) renderPreamble(ctx *renderer.Context, p types.Preamble) ( result := &strings.Builder{} // the
    wrapper is only necessary // if the document has a section 0 - err := r.preamble.Execute(result, ContextualPipeline{ + + content, err := r.renderElements(ctx, p.Elements) + if err != nil { + return "", errors.Wrap(err, "error rendering preamble elements") + } + err = r.preamble.Execute(result, struct { + Context *renderer.Context + Wrapper bool + Content sanitized + }{ Context: ctx, - Data: struct { - Wrapper bool - Elements []interface{} - }{ - Wrapper: ctx.HasHeader, - Elements: p.Elements, - }, + Wrapper: ctx.HasHeader, + Content: sanitized(content), }) if err != nil { return "", errors.Wrap(err, "error while rendering preamble") @@ -34,29 +37,30 @@ func (r *sgmlRenderer) renderPreamble(ctx *renderer.Context, p types.Preamble) ( func (r *sgmlRenderer) renderSection(ctx *renderer.Context, s types.Section) (string, error) { log.Debugf("rendering section level %d", s.Level) - renderedSectionTitle, err := r.renderSectionTitle(ctx, s) + title, err := r.renderSectionTitle(ctx, s) if err != nil { - return "", errors.Wrap(err, "error while rendering section") + return "", errors.Wrap(err, "error while rendering section title") } - result := &strings.Builder{} - // select the appropriate template for the section - var tmpl *textTemplate - if s.Level == 1 { - tmpl = r.sectionOne - } else { - tmpl = r.sectionContent + + content, err := r.renderElements(ctx, s.Elements) + if err != nil { + return "", errors.Wrap(err, "error while rendering section content") } - err = tmpl.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - Class string - SectionTitle string - Elements []interface{} - }{ - Class: "sect" + strconv.Itoa(s.Level), - SectionTitle: renderedSectionTitle, - Elements: s.Elements, - }}) + + result := &strings.Builder{} + err = r.sectionContent.Execute(result, struct { + Context *renderer.Context + Header sanitized + Content sanitized + Elements []interface{} + Level int + }{ + Context: ctx, + Header: title, + Level: s.Level, + Elements: s.Elements, + Content: sanitized(content), + }) if err != nil { return "", errors.Wrap(err, "error while rendering section") } @@ -64,26 +68,27 @@ func (r *sgmlRenderer) renderSection(ctx *renderer.Context, s types.Section) (st return result.String(), nil } -func (r *sgmlRenderer) renderSectionTitle(ctx *renderer.Context, s types.Section) (string, error) { +func (r *sgmlRenderer) renderSectionTitle(ctx *renderer.Context, s types.Section) (sanitized, error) { result := &strings.Builder{} renderedContent, err := r.renderInlineElements(ctx, s.Title) if err != nil { return "", errors.Wrap(err, "error while rendering sectionTitle content") } renderedContentStr := strings.TrimSpace(renderedContent) - id := r.renderElementID(s.Attributes) err = r.sectionHeader.Execute(result, struct { - Level int - ID string - Content string + Level int + LevelPlusOne int + ID sanitized + Content string }{ - Level: s.Level + 1, - ID: id, - Content: renderedContentStr, + Level: s.Level, + LevelPlusOne: s.Level + 1, // Level 1 is

    . + ID: r.renderElementID(s.Attributes), + Content: renderedContentStr, }) if err != nil { return "", errors.Wrapf(err, "error while rendering sectionTitle") } // log.Debugf("rendered sectionTitle: %s", result.Bytes()) - return result.String(), nil + return sanitized(result.String()), nil } diff --git a/pkg/renderer/sgml/sgml_renderer.go b/pkg/renderer/sgml/sgml_renderer.go index 6b5963f3..33b62df6 100644 --- a/pkg/renderer/sgml/sgml_renderer.go +++ b/pkg/renderer/sgml/sgml_renderer.go @@ -9,67 +9,77 @@ type sgmlRenderer struct { prepareOnce sync.Once // Processed templates - admonitionBlock *textTemplate - admonitionParagraph *textTemplate - article *textTemplate - articleHeader *textTemplate - blankLine *textTemplate - blockImage *textTemplate - boldText *textTemplate - calloutList *textTemplate - delimitedBlockParagraph *textTemplate - documentDetails *textTemplate - documentAuthorDetails *textTemplate - externalCrossReference *textTemplate - exampleBlock *textTemplate - fencedBlock *textTemplate - footnote *textTemplate - footnoteRef *textTemplate - footnoteRefPlain *textTemplate - footnotes *textTemplate - iconFont *textTemplate - iconImage *textTemplate - iconText *textTemplate - inlineIcon *textTemplate - inlineImage *textTemplate - internalCrossReference *textTemplate - invalidFootnote *textTemplate - italicText *textTemplate - labeledList *textTemplate - labeledListHorizontal *textTemplate - lineBreak *textTemplate - link *textTemplate - listingBlock *textTemplate - literalBlock *textTemplate - manpageHeader *textTemplate - manpageNameParagraph *textTemplate - markedText *textTemplate - monospaceText *textTemplate - orderedList *textTemplate - paragraph *textTemplate - passthroughBlock *textTemplate - preamble *textTemplate - qAndAList *textTemplate - quoteBlock *textTemplate - quoteParagraph *textTemplate - sectionContent *textTemplate - sectionHeader *textTemplate - sectionOne *textTemplate - sidebarBlock *textTemplate - sourceBlock *textTemplate - sourceBlockContent *textTemplate - sourceParagraph *textTemplate - stringElement *textTemplate - subscriptText *textTemplate - superscriptText *textTemplate - table *textTemplate - tocRoot *textTemplate - tocSection *textTemplate - unorderedList *textTemplate - verbatimLine *textTemplate - verseBlock *textTemplate - verseBlockParagraph *textTemplate - verseParagraph *textTemplate + admonitionBlock *textTemplate + admonitionParagraph *textTemplate + article *textTemplate + articleHeader *textTemplate + blankLine *textTemplate + blockImage *textTemplate + boldText *textTemplate + calloutList *textTemplate + calloutListItem *textTemplate + delimitedBlockParagraph *textTemplate + documentDetails *textTemplate + documentAuthorDetails *textTemplate + externalCrossReference *textTemplate + exampleBlock *textTemplate + fencedBlock *textTemplate + footnote *textTemplate + footnoteItem *textTemplate + footnoteRef *textTemplate + footnoteRefPlain *textTemplate + footnotes *textTemplate + iconFont *textTemplate + iconImage *textTemplate + iconText *textTemplate + inlineIcon *textTemplate + inlineImage *textTemplate + internalCrossReference *textTemplate + invalidFootnote *textTemplate + italicText *textTemplate + labeledList *textTemplate + labeledListItem *textTemplate + labeledListHorizontal *textTemplate + labeledListHorizontalItem *textTemplate + lineBreak *textTemplate + link *textTemplate + listingBlock *textTemplate + literalBlock *textTemplate + manpageHeader *textTemplate + manpageNameParagraph *textTemplate + markedText *textTemplate + monospaceText *textTemplate + orderedList *textTemplate + orderedListItem *textTemplate + paragraph *textTemplate + passthroughBlock *textTemplate + preamble *textTemplate + qAndAList *textTemplate + qAndAListItem *textTemplate + quoteBlock *textTemplate + quoteParagraph *textTemplate + sectionContent *textTemplate + sectionHeader *textTemplate + sidebarBlock *textTemplate + sourceBlock *textTemplate + sourceParagraph *textTemplate + stringElement *textTemplate + subscriptText *textTemplate + superscriptText *textTemplate + table *textTemplate + tableBody *textTemplate + tableCell *textTemplate + tableHeader *textTemplate + tableHeaderCell *textTemplate + tableRow *textTemplate + tocEntry *textTemplate + tocRoot *textTemplate + tocSection *textTemplate + unorderedList *textTemplate + unorderedListItem *textTemplate + verbatimLine *textTemplate + verseBlock *textTemplate + verseParagraph *textTemplate } func (r *sgmlRenderer) prepareTemplates() error { @@ -84,6 +94,7 @@ func (r *sgmlRenderer) prepareTemplates() error { r.blockImage, err = r.newTemplate("block-image", tmpls.BlockImage, err) r.boldText, err = r.newTemplate("bold-text", tmpls.BoldText, err) r.calloutList, err = r.newTemplate("callout-list", tmpls.CalloutList, err) + r.calloutListItem, err = r.newTemplate("callout-list-item", tmpls.CalloutListItem, err) r.delimitedBlockParagraph, err = r.newTemplate("delimited-block-paragraph", tmpls.DelimitedBlockParagraph, err) r.documentDetails, err = r.newTemplate("document-details", tmpls.DocumentDetails, err) r.documentAuthorDetails, err = r.newTemplate("document-author-details", tmpls.DocumentAuthorDetails, err) @@ -91,6 +102,7 @@ func (r *sgmlRenderer) prepareTemplates() error { r.externalCrossReference, err = r.newTemplate("external-xref", tmpls.ExternalCrossReference, err) r.fencedBlock, err = r.newTemplate("fenced-block", tmpls.FencedBlock, err) r.footnote, err = r.newTemplate("footnote", tmpls.Footnote, err) + r.footnoteItem, err = r.newTemplate("footnote-item", tmpls.FootnoteItem, err) r.footnoteRef, err = r.newTemplate("footnote-ref", tmpls.FootnoteRef, err) r.footnoteRefPlain, err = r.newTemplate("footnote-ref-plain", tmpls.FootnoteRefPlain, err) r.footnotes, err = r.newTemplate("footnotes", tmpls.Footnotes, err) @@ -103,7 +115,9 @@ func (r *sgmlRenderer) prepareTemplates() error { r.invalidFootnote, err = r.newTemplate("invalid-footnote", tmpls.InvalidFootnote, err) r.italicText, err = r.newTemplate("italic-text", tmpls.ItalicText, err) r.labeledList, err = r.newTemplate("labeled-list", tmpls.LabeledList, err) + r.labeledListItem, err = r.newTemplate("labeled-list-item", tmpls.LabeledListItem, err) r.labeledListHorizontal, err = r.newTemplate("labeled-list-horizontal", tmpls.LabeledListHorizontal, err) + r.labeledListHorizontalItem, err = r.newTemplate("labeled-list-horizontal-item", tmpls.LabeledListHorizontalItem, err) r.lineBreak, err = r.newTemplate("line-break", tmpls.LineBreak, err) r.link, err = r.newTemplate("link", tmpls.Link, err) r.listingBlock, err = r.newTemplate("listing", tmpls.ListingBlock, err) @@ -113,29 +127,35 @@ func (r *sgmlRenderer) prepareTemplates() error { r.markedText, err = r.newTemplate("marked-text", tmpls.MarkedText, err) r.monospaceText, err = r.newTemplate("monospace-text", tmpls.MonospaceText, err) r.orderedList, err = r.newTemplate("ordered-list", tmpls.OrderedList, err) + r.orderedListItem, err = r.newTemplate("ordered-list-item", tmpls.OrderedListItem, err) r.paragraph, err = r.newTemplate("paragraph", tmpls.Paragraph, err) r.passthroughBlock, err = r.newTemplate("passthrough", tmpls.PassthroughBlock, err) r.preamble, err = r.newTemplate("preamble", tmpls.Preamble, err) - r.qAndAList, err = r.newTemplate("qanda-block", tmpls.QAndAList, err) + r.qAndAList, err = r.newTemplate("qanda-list", tmpls.QAndAList, err) + r.qAndAListItem, err = r.newTemplate("qanda-list-item", tmpls.QAndAListItem, err) r.quoteBlock, err = r.newTemplate("quote-block", tmpls.QuoteBlock, err) r.quoteParagraph, err = r.newTemplate("quote-paragraph", tmpls.QuoteParagraph, err) r.sectionContent, err = r.newTemplate("section-content", tmpls.SectionContent, err) r.sectionHeader, err = r.newTemplate("section-header", tmpls.SectionHeader, err) - r.sectionOne, err = r.newTemplate("section-one", tmpls.SectionOne, err) r.stringElement, err = r.newTemplate("string-element", tmpls.StringElement, err) r.sidebarBlock, err = r.newTemplate("sidebar-block", tmpls.SidebarBlock, err) r.sourceBlock, err = r.newTemplate("source-block", tmpls.SourceBlock, err) - r.sourceBlockContent, err = r.newTemplate("source-block-content", tmpls.SourceBlockContent, err) r.sourceParagraph, err = r.newTemplate("source-paragraph", tmpls.SourceParagraph, err) r.subscriptText, err = r.newTemplate("subscript", tmpls.SubscriptText, err) r.superscriptText, err = r.newTemplate("superscript", tmpls.SuperscriptText, err) r.table, err = r.newTemplate("table", tmpls.Table, err) + r.tableBody, err = r.newTemplate("table-body", tmpls.TableBody, err) + r.tableCell, err = r.newTemplate("table-cell", tmpls.TableCell, err) + r.tableHeader, err = r.newTemplate("table-header", tmpls.TableHeader, err) + r.tableHeaderCell, err = r.newTemplate("table-header-cell", tmpls.TableHeaderCell, err) + r.tableRow, err = r.newTemplate("table-row", tmpls.TableRow, err) + r.tocEntry, err = r.newTemplate("toc-entry", tmpls.TocEntry, err) r.tocRoot, err = r.newTemplate("toc-root", tmpls.TocRoot, err) r.tocSection, err = r.newTemplate("toc-section", tmpls.TocSection, err) r.unorderedList, err = r.newTemplate("unordered-list", tmpls.UnorderedList, err) + r.unorderedListItem, err = r.newTemplate("unordered-list-item", tmpls.UnorderedListItem, err) r.verbatimLine, err = r.newTemplate("verbatim-line", tmpls.VerbatimLine, err) r.verseBlock, err = r.newTemplate("verse", tmpls.VerseBlock, err) - r.verseBlockParagraph, err = r.newTemplate("verse-block-paragraph", tmpls.VerseBlockParagraph, err) r.verseParagraph, err = r.newTemplate("verse-paragraph", tmpls.VerseParagraph, err) }) return err diff --git a/pkg/renderer/sgml/table.go b/pkg/renderer/sgml/table.go index c119463e..54e87463 100644 --- a/pkg/renderer/sgml/table.go +++ b/pkg/renderer/sgml/table.go @@ -32,23 +32,37 @@ func (r *sgmlRenderer) renderTable(ctx *renderer.Context, t types.Table) (string widths[n-1] = formatColumnWidth(100-total, lastColumn()) // make sure the last width as the upper rounded value log.Debugf("current total width: %v -> %v", total, widths[n-1]) } - var title string - if titleAttr, ok := t.Attributes[types.AttrTitle].(string); ok { - title = fmt.Sprintf("Table %d. %s", ctx.GetAndIncrementTableCounter(), EscapeString(titleAttr)) + number := 0 + if t.Attributes.Has(types.AttrTitle) { + number = ctx.GetAndIncrementTableCounter() } - err := r.table.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - Title string - CellWidths []string - Header types.TableLine - Lines []types.TableLine - }{ - Title: title, - CellWidths: widths, - Header: t.Header, - Lines: t.Lines, - }, + + header, err := r.renderTableHeader(ctx, t.Header) + if err != nil { + return "", errors.Wrap(err, "failed to render table") + } + + body, err := r.renderTableBody(ctx, t) + if err != nil { + return "", errors.Wrap(err, "failed to render table") + } + + err = r.table.Execute(result, struct { + Context *renderer.Context + Title string + CellWidths []string + TableNumber int + Roles sanitized + Header string + Body string + }{ + Context: ctx, + Title: r.renderElementTitle(t.Attributes), + CellWidths: widths, + TableNumber: number, + Roles: r.renderElementRoles(t.Attributes), + Header: header, + Body: body, }) if err != nil { return "", errors.Wrap(err, "failed to render table") @@ -56,6 +70,108 @@ func (r *sgmlRenderer) renderTable(ctx *renderer.Context, t types.Table) (string return result.String(), nil } +func (r *sgmlRenderer) renderTableHeader(ctx *renderer.Context, l types.TableLine) (string, error) { + result := &strings.Builder{} + content := &strings.Builder{} + for _, cell := range l.Cells { + c, err := r.renderTableHeaderCell(ctx, cell) + if err != nil { + return "", errors.Wrap(err, "unable to render header") + } + content.WriteString(c) + } + err := r.tableHeader.Execute(result, struct { + Context *renderer.Context + Content string + Cells [][]interface{} + }{ + Context: ctx, + Content: content.String(), + Cells: l.Cells, + }) + return result.String(), err +} + +func (r *sgmlRenderer) renderTableHeaderCell(ctx *renderer.Context, cell []interface{}) (string, error) { + result := &strings.Builder{} + content, err := r.renderInlineElements(ctx, cell) + if err != nil { + return "", errors.Wrap(err, "unable to render header cell") + } + err = r.tableHeaderCell.Execute(result, struct { + Context *renderer.Context + Content string + Cell []interface{} + }{ + Context: ctx, + Content: content, + Cell: cell, + }) + return result.String(), err +} + +func (r *sgmlRenderer) renderTableBody(ctx *renderer.Context, t types.Table) (string, error) { + result := &strings.Builder{} + content := &strings.Builder{} + for _, row := range t.Lines { + c, err := r.renderTableRow(ctx, row) + if err != nil { + return "", errors.Wrap(err, "unable to render header") + } + content.WriteString(c) + } + err := r.tableBody.Execute(result, struct { + Context *renderer.Context + Content string + Rows []types.TableLine + }{ + Context: ctx, + Content: content.String(), + Rows: t.Lines, + }) + return result.String(), err +} + +func (r *sgmlRenderer) renderTableRow(ctx *renderer.Context, l types.TableLine) (string, error) { + result := &strings.Builder{} + content := &strings.Builder{} + for _, cell := range l.Cells { + c, err := r.renderTableCell(ctx, cell) + if err != nil { + return "", errors.Wrap(err, "unable to render header") + } + content.WriteString(c) + } + err := r.tableRow.Execute(result, struct { + Context *renderer.Context + Content string + Cells [][]interface{} + }{ + Context: ctx, + Content: content.String(), + Cells: l.Cells, + }) + return result.String(), err +} + +func (r *sgmlRenderer) renderTableCell(ctx *renderer.Context, cell []interface{}) (string, error) { + result := &strings.Builder{} + content, err := r.renderInlineElements(ctx, cell) + if err != nil { + return "", errors.Wrap(err, "unable to render header cell") + } + err = r.tableCell.Execute(result, struct { + Context *renderer.Context + Content string + Cell []interface{} + }{ + Context: ctx, + Content: content, + Cell: cell, + }) + return result.String(), err +} + type formatColumnWidthOption func(float64) float64 func lastColumn() formatColumnWidthOption { diff --git a/pkg/renderer/sgml/table_of_contents.go b/pkg/renderer/sgml/table_of_contents.go index 836e322f..e654d4e5 100644 --- a/pkg/renderer/sgml/table_of_contents.go +++ b/pkg/renderer/sgml/table_of_contents.go @@ -29,26 +29,65 @@ func (r *sgmlRenderer) renderTableOfContents(ctx *renderer.Context, toc types.Ta return result.String(), nil } -func (r *sgmlRenderer) renderTableOfContentsSections(ctx *renderer.Context, sections []types.ToCSection) (sanitized, error) { +func (r *sgmlRenderer) renderTableOfContentsSections(ctx *renderer.Context, sections []types.ToCSection) (string, error) { if len(sections) == 0 { return "", nil } resultBuf := &strings.Builder{} - err := r.tocSection.Execute(resultBuf, ContextualPipeline{ - Context: ctx, - Data: struct { - Level int - Sections []types.ToCSection - }{ - Level: sections[0].Level, - Sections: sections, - }, + contents := &strings.Builder{} + for _, entry := range sections { + buf, err := r.renderTableOfContentsEntry(ctx, entry) + if err != nil { + return "", errors.Wrap(err, "unable to render ToC section") + } + contents.WriteString(buf) + } + + err := r.tocSection.Execute(resultBuf, struct { + Context *renderer.Context + Level int + Content string + Sections []types.ToCSection + }{ + Context: ctx, + Level: sections[0].Level, + Content: contents.String(), + Sections: sections, }) if err != nil { return "", errors.Wrap(err, "failed to render document ToC") } log.Debugf("retrieved sections for ToC: %+v", sections) - return sanitized(resultBuf.String()), nil //nolint: gosec + return resultBuf.String(), nil //nolint: gosec +} + +func (r *sgmlRenderer) renderTableOfContentsEntry(ctx *renderer.Context, entry types.ToCSection) (string, error) { + resultBuf := &strings.Builder{} + + content, err := r.renderTableOfContentsSections(ctx, entry.Children) + if err != nil { + return "", errors.Wrap(err, "unable to render ToC entry children") + } + + err = r.tocEntry.Execute(resultBuf, struct { + Context *renderer.Context + Level int + ID sanitized + Title string + Content string + Children []types.ToCSection + }{ + Context: ctx, + Level: entry.Level, + ID: sanitized(entry.ID), + Title: entry.Title, + Content: content, + Children: entry.Children, + }) + if err != nil { + return "", errors.Wrap(err, "failed to render document ToC") + } + return resultBuf.String(), nil //nolint: gosec } // newTableOfContents initializes a TableOfContents from the sections @@ -100,7 +139,7 @@ func (r *sgmlRenderer) visitSection(ctx *renderer.Context, section types.Section { ID: section.Attributes.GetAsStringWithDefault(types.AttrID, ""), Level: section.Level, - Title: string(renderedTitle), + Title: renderedTitle, Children: children, }, }, nil diff --git a/pkg/renderer/sgml/templates.go b/pkg/renderer/sgml/templates.go index ec4821f8..0ab6c638 100644 --- a/pkg/renderer/sgml/templates.go +++ b/pkg/renderer/sgml/templates.go @@ -2,65 +2,75 @@ package sgml // Templates represents all the templates we use. type Templates struct { - AdmonitionBlock string - AdmonitionParagraph string - Article string - ArticleHeader string - BlankLine string - BlockImage string - BoldText string - CalloutList string - DelimitedBlockParagraph string - DocumentDetails string - DocumentAuthorDetails string - ExampleBlock string - ExternalCrossReference string - FencedBlock string - Footnote string - FootnoteRef string - FootnoteRefPlain string - Footnotes string - IconFont string - IconImage string - IconText string - InlineIcon string - InlineImage string - InternalCrossReference string - InvalidFootnote string - ItalicText string - LabeledList string - LabeledListHorizontal string - LineBreak string - Link string - ListingBlock string - LiteralBlock string - ManpageHeader string - ManpageNameParagraph string - MarkedText string - MonospaceText string - OrderedList string - Paragraph string - PassthroughBlock string - Preamble string - QAndAList string - QuoteBlock string - QuoteParagraph string - SectionContent string - SectionHeader string - SectionOne string - SidebarBlock string - SourceBlock string - SourceBlockContent string - SourceParagraph string - StringElement string - SubscriptText string - SuperscriptText string - Table string - TocRoot string - TocSection string - UnorderedList string - VerbatimLine string - VerseBlock string - VerseBlockParagraph string - VerseParagraph string + AdmonitionBlock string + AdmonitionParagraph string + Article string + ArticleHeader string + BlankLine string + BlockImage string + BoldText string + CalloutList string + CalloutListItem string + DelimitedBlockParagraph string + DocumentDetails string + DocumentAuthorDetails string + ExampleBlock string + ExternalCrossReference string + FencedBlock string + Footnote string + FootnoteItem string + FootnoteRef string + FootnoteRefPlain string + Footnotes string + IconFont string + IconImage string + IconText string + InlineIcon string + InlineImage string + InternalCrossReference string + InvalidFootnote string + ItalicText string + LabeledList string + LabeledListItem string + LabeledListHorizontal string + LabeledListHorizontalItem string + LineBreak string + Link string + ListingBlock string + LiteralBlock string + ManpageHeader string + ManpageNameParagraph string + MarkedText string + MonospaceText string + OrderedList string + OrderedListItem string + Paragraph string + PassthroughBlock string + Preamble string + QAndAList string + QAndAListItem string + QuoteBlock string + QuoteParagraph string + SectionContent string + SectionHeader string + SidebarBlock string + SourceBlock string + SourceParagraph string + StringElement string + SubscriptText string + SuperscriptText string + Table string + TableBody string + TableCell string + TableHeader string + TableHeaderCell string + TableRow string + TocEntry string + TocRoot string + TocSection string + UnorderedList string + UnorderedListItem string + VerbatimLine string + VerseBlock string + VerseParagraph string } diff --git a/pkg/renderer/sgml/unordered_list.go b/pkg/renderer/sgml/unordered_list.go index 0369e72d..a5f7260b 100644 --- a/pkg/renderer/sgml/unordered_list.go +++ b/pkg/renderer/sgml/unordered_list.go @@ -1,6 +1,7 @@ package sgml import ( + "io" "strings" "github.com/bytesparadise/libasciidoc/pkg/renderer" @@ -17,25 +18,47 @@ func (r *sgmlRenderer) renderUnorderedList(ctx *renderer.Context, l types.Unorde } } result := &strings.Builder{} + content := &strings.Builder{} + + for _, item := range l.Items { + if err := r.renderUnorderedListItem(ctx, content, item); err != nil { + return "", errors.Wrap(err, "unable to render unordered list") + } + } // here we must preserve the HTML tags - err := r.unorderedList.Execute(result, ContextualPipeline{ - Context: ctx, - Data: struct { - ID string - Title string - Role string - Checklist bool - Items []types.UnorderedListItem - }{ - ID: r.renderElementID(l.Attributes), - Title: r.renderElementTitle(l.Attributes), - Role: l.Attributes.GetAsStringWithDefault(types.AttrRole, ""), - Checklist: checkList, - Items: l.Items, - }, + err := r.unorderedList.Execute(result, struct { + Context *renderer.Context + ID sanitized + Title string + Roles sanitized + Checklist bool + Items []types.UnorderedListItem + Content sanitized + }{ + Context: ctx, + ID: r.renderElementID(l.Attributes), + Title: r.renderElementTitle(l.Attributes), + Checklist: checkList, + Items: l.Items, + Content: sanitized(content.String()), + Roles: r.renderElementRoles(l.Attributes), }) if err != nil { return "", errors.Wrap(err, "unable to render unordered list") } return result.String(), nil } +func (r *sgmlRenderer) renderUnorderedListItem(ctx *renderer.Context, w io.Writer, item types.UnorderedListItem) error { + + content, err := r.renderListElements(ctx, item.Elements) + if err != nil { + return errors.Wrap(err, "unable to render unordered list item content") + } + return r.unorderedListItem.Execute(w, struct { + Context *renderer.Context + Content sanitized + }{ + Context: ctx, + Content: sanitized(content), + }) +} diff --git a/pkg/renderer/sgml/xhtml5/delimited_block.go b/pkg/renderer/sgml/xhtml5/delimited_block.go index c46ed23a..9383c7d1 100644 --- a/pkg/renderer/sgml/xhtml5/delimited_block.go +++ b/pkg/renderer/sgml/xhtml5/delimited_block.go @@ -1,23 +1,24 @@ package xhtml5 const ( - quoteBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -{{ renderElements $ctx .Elements }} -
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + quoteBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "{{ .Content }}\n" + + "
    \n" + + "{{ if .Attribution.First }}
    \n" + + "— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}{{ end }}\n" + + "
    \n{{ end }}" + + "
    " - verseBlockTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    {{ range $index, $element := .Elements }}{{ renderVerse $ctx $element }}{{ end }}
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + verseBlockTmpl = `
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    {{ .Content }}
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}{{ end }}\n" + + "
    \n{{ end }}" + + "
    " ) diff --git a/pkg/renderer/sgml/xhtml5/footnote_reference.go b/pkg/renderer/sgml/xhtml5/footnote_reference.go index d4dcfcda..4caf09ca 100644 --- a/pkg/renderer/sgml/xhtml5/footnote_reference.go +++ b/pkg/renderer/sgml/xhtml5/footnote_reference.go @@ -1,11 +1,5 @@ package xhtml5 const ( - footnotesTmpl = ` -
    -
    {{ $ctx := .Context }}{{ with .Data }}{{ $footnotes := .Footnotes }}{{ range $index, $footnote := $footnotes }} -
    -{{ $footnote.ID }}. {{ renderFootnote $ctx $footnote.Elements }} -
    {{ end }}{{ end }} -
    ` + footnotesTmpl = "\n
    \n
    \n{{ .Content }}
    " ) diff --git a/pkg/renderer/sgml/xhtml5/labeled_list.go b/pkg/renderer/sgml/xhtml5/labeled_list.go index f1f68391..71240e81 100644 --- a/pkg/renderer/sgml/xhtml5/labeled_list.go +++ b/pkg/renderer/sgml/xhtml5/labeled_list.go @@ -1,20 +1,8 @@ package xhtml5 const ( - labeledListHorizontalTmpl = `{{ $ctx := .Context }}{{ with .Data }} -{{ if .Title }}
    {{ escape .Title }}
    -{{ end }} - - - - - -{{ end }}{{ else }}
    {{ end }}{{ end }} - -
    {{ $items := .Items }}{{ range $itemIndex, $item := $items }} -{{ renderInline $ctx $item.Term }} -{{ if $item.Elements }} -{{ renderList $ctx $item.Elements }} -{{ if includeNewline $ctx $itemIndex $items }}
    {{ else }}
    -

    {{ end }}` + labeledListHorizontalItemTmpl = "{{ if not .Continuation }}\n" + + "\n{{ else }}
    \n{{ end }}" + + "{{ .Term }}\n" + + "{{ if .Content }}\n\n{{ .Content }}\n\n\n{{ end }}" ) diff --git a/pkg/renderer/sgml/xhtml5/paragraph.go b/pkg/renderer/sgml/xhtml5/paragraph.go index 484d5c4f..05f40c6a 100644 --- a/pkg/renderer/sgml/xhtml5/paragraph.go +++ b/pkg/renderer/sgml/xhtml5/paragraph.go @@ -1,23 +1,19 @@ package xhtml5 const ( - verseParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    {{ renderLines $ctx .Lines plainText }}
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + verseParagraphTmpl = "
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    {{ .Content }}
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}\n{{ else }}\n{{ end }}" + + "
    \n{{ end }}
    " - quoteParagraphTmpl = `{{ $ctx := .Context }}{{ with .Data }}
    {{ if .Title }} -
    {{ escape .Title }}
    {{ end }} -
    -{{ renderLines $ctx .Lines }} -
    {{ if .Attribution.First }} -
    -— {{ .Attribution.First }}{{ if .Attribution.Second }}
    -{{ .Attribution.Second }}{{ end }} -
    {{ end }} -
    {{ end }}` + quoteParagraphTmpl = "
    \n" + + "{{ if .Title }}
    {{ escape .Title }}
    \n{{ end }}" + + "
    \n" + + "{{ .Content }}\n" + + "
    \n" + + "{{ if .Attribution.First }}
    \n— {{ .Attribution.First }}" + + "{{ if .Attribution.Second }}
    \n{{ .Attribution.Second }}\n{{ else }}\n{{ end }}" + + "
    \n{{ end }}
    " ) diff --git a/pkg/renderer/sgml/xhtml5/table.go b/pkg/renderer/sgml/xhtml5/table.go index 283dd105..d7ab706e 100644 --- a/pkg/renderer/sgml/xhtml5/table.go +++ b/pkg/renderer/sgml/xhtml5/table.go @@ -1,20 +1,14 @@ package xhtml5 const ( - tableTmpl = `{{ $ctx := .Context }}{{ with .Data }}{{ if .Lines }} -{{ if .Title }} -{{ end }}-{{ $cellWidths := .CellWidths }}{{ range $index, $width := $cellWidths }}{{ includeNewline $ctx $index $cellWidths }}{{ end }} - -{{ if .Header }}{{ if .Header.Cells }} - -{{ $headerCells := .Header.Cells }}{{ range $index, $cell := $headerCells }}{{ includeNewline $ctx $index $headerCells }}{{ end }} - - -{{ end }}{{ end }} -{{ range $indexLine, $line := .Lines }} -{{ range $indexCells, $cell := $line.Cells }}{{ includeNewline $ctx $indexCells $line.Cells }}{{ end }} - -{{ end }}{{ end }} -
    {{ escape .Title }}
    {{ renderInline $ctx $cell }}

    {{ renderInline $ctx $cell }}

    {{ end }}` + tableTmpl = "\n" + + "{{ if .Title }}\n{{ end }}" + + "{{ if .Body }}" + + "\n" + + "{{ range $i, $w := .CellWidths }}\n{{ end}}" + + "\n" + + "{{ .Header }}" + + "{{ .Body }}" + + "{{ end }}" + + "
    Table {{ .TableNumber }}. {{ escape .Title }}
    " ) diff --git a/pkg/renderer/sgml/xhtml5/templates.go b/pkg/renderer/sgml/xhtml5/templates.go index cd826737..0236a9b3 100644 --- a/pkg/renderer/sgml/xhtml5/templates.go +++ b/pkg/renderer/sgml/xhtml5/templates.go @@ -26,7 +26,7 @@ func init() { templates.Footnotes = footnotesTmpl templates.IconImage = iconImageTmpl templates.InlineImage = inlineImageTmpl - templates.LabeledListHorizontal = labeledListHorizontalTmpl + templates.LabeledListHorizontalItem = labeledListHorizontalItemTmpl templates.Table = tableTmpl templates.QuoteBlock = quoteBlockTmpl templates.QuoteParagraph = quoteParagraphTmpl