diff options
Diffstat (limited to 'vendor/github.com/gomarkdown/markdown/html/renderer.go')
-rw-r--r-- | vendor/github.com/gomarkdown/markdown/html/renderer.go | 441 |
1 files changed, 276 insertions, 165 deletions
diff --git a/vendor/github.com/gomarkdown/markdown/html/renderer.go b/vendor/github.com/gomarkdown/markdown/html/renderer.go index 01e0deec..6c56ac12 100644 --- a/vendor/github.com/gomarkdown/markdown/html/renderer.go +++ b/vendor/github.com/gomarkdown/markdown/html/renderer.go @@ -3,6 +3,7 @@ package html import ( "bytes" "fmt" + "html" "io" "regexp" "sort" @@ -10,6 +11,7 @@ import ( "strings" "github.com/gomarkdown/markdown/ast" + "github.com/gomarkdown/markdown/parser" ) // Flags control optional behavior of HTML renderer. @@ -125,13 +127,60 @@ type Renderer struct { headingIDs map[string]int lastOutputLen int - disableTags int + + // if > 0, will strip html tags in Out and Outs + DisableTags int sr *SPRenderer documentMatter ast.DocumentMatters // keep track of front/main/back matter. } +// Escaper defines how to escape HTML special characters +var Escaper = [256][]byte{ + '&': []byte("&"), + '<': []byte("<"), + '>': []byte(">"), + '"': []byte("""), +} + +// EscapeHTML writes html-escaped d to w. It escapes &, <, > and " characters. +func EscapeHTML(w io.Writer, d []byte) { + var start, end int + n := len(d) + for end < n { + escSeq := Escaper[d[end]] + if escSeq != nil { + w.Write(d[start:end]) + w.Write(escSeq) + start = end + 1 + } + end++ + } + if start < n && end <= n { + w.Write(d[start:end]) + } +} + +func escLink(w io.Writer, text []byte) { + unesc := html.UnescapeString(string(text)) + EscapeHTML(w, []byte(unesc)) +} + +// Escape writes the text to w, but skips the escape character. +func Escape(w io.Writer, text []byte) { + esc := false + for i := 0; i < len(text); i++ { + if text[i] == '\\' { + esc = !esc + } + if esc && text[i] == '\\' { + continue + } + w.Write([]byte{text[i]}) + } +} + // NewRenderer creates and configures an Renderer object, which // satisfies the Renderer interface. func NewRenderer(opts RendererOptions) *Renderer { @@ -384,25 +433,28 @@ func skipParagraphTags(para *ast.Paragraph) bool { return tightOrTerm } -func (r *Renderer) out(w io.Writer, d []byte) { +// Out is a helper to write data to writer +func (r *Renderer) Out(w io.Writer, d []byte) { r.lastOutputLen = len(d) - if r.disableTags > 0 { + if r.DisableTags > 0 { d = htmlTagRe.ReplaceAll(d, []byte{}) } w.Write(d) } -func (r *Renderer) outs(w io.Writer, s string) { +// Outs is a helper to write data to writer +func (r *Renderer) Outs(w io.Writer, s string) { r.lastOutputLen = len(s) - if r.disableTags > 0 { + if r.DisableTags > 0 { s = htmlTagRe.ReplaceAllString(s, "") } io.WriteString(w, s) } -func (r *Renderer) cr(w io.Writer) { +// CR writes a new line +func (r *Renderer) CR(w io.Writer) { if r.lastOutputLen > 0 { - r.outs(w, "\n") + r.Outs(w, "\n") } } @@ -426,11 +478,12 @@ func headingCloseTagFromLevel(level int) string { } func (r *Renderer) outHRTag(w io.Writer, attrs []string) { - hr := tagWithAttributes("<hr", attrs) - r.outOneOf(w, r.opts.Flags&UseXHTML == 0, hr, "<hr />") + hr := TagWithAttributes("<hr", attrs) + r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, hr, "<hr />") } -func (r *Renderer) text(w io.Writer, text *ast.Text) { +// Text writes ast.Text node +func (r *Renderer) Text(w io.Writer, text *ast.Text) { if r.opts.Flags&Smartypants != 0 { var tmp bytes.Buffer EscapeHTML(&tmp, text.Literal) @@ -445,41 +498,46 @@ func (r *Renderer) text(w io.Writer, text *ast.Text) { } } -func (r *Renderer) hardBreak(w io.Writer, node *ast.Hardbreak) { - r.outOneOf(w, r.opts.Flags&UseXHTML == 0, "<br>", "<br />") - r.cr(w) +// HardBreak writes ast.Hardbreak node +func (r *Renderer) HardBreak(w io.Writer, node *ast.Hardbreak) { + r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, "<br>", "<br />") + r.CR(w) } -func (r *Renderer) nonBlockingSpace(w io.Writer, node *ast.NonBlockingSpace) { - r.outs(w, " ") +// NonBlockingSpace writes ast.NonBlockingSpace node +func (r *Renderer) NonBlockingSpace(w io.Writer, node *ast.NonBlockingSpace) { + r.Outs(w, " ") } -func (r *Renderer) outOneOf(w io.Writer, outFirst bool, first string, second string) { +// OutOneOf writes first or second depending on outFirst +func (r *Renderer) OutOneOf(w io.Writer, outFirst bool, first string, second string) { if outFirst { - r.outs(w, first) + r.Outs(w, first) } else { - r.outs(w, second) + r.Outs(w, second) } } -func (r *Renderer) outOneOfCr(w io.Writer, outFirst bool, first string, second string) { +// OutOneOfCr writes CR + first or second + CR depending on outFirst +func (r *Renderer) OutOneOfCr(w io.Writer, outFirst bool, first string, second string) { if outFirst { - r.cr(w) - r.outs(w, first) + r.CR(w) + r.Outs(w, first) } else { - r.outs(w, second) - r.cr(w) + r.Outs(w, second) + r.CR(w) } } -func (r *Renderer) htmlSpan(w io.Writer, span *ast.HTMLSpan) { +// HTMLSpan writes ast.HTMLSpan node +func (r *Renderer) HTMLSpan(w io.Writer, span *ast.HTMLSpan) { if r.opts.Flags&SkipHTML == 0 { - r.out(w, span.Literal) + r.Out(w, span.Literal) } } func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) { - var attrs []string + attrs := link.AdditionalAttributes dest := link.Destination dest = r.addAbsPrefix(dest) var hrefBuf bytes.Buffer @@ -488,7 +546,7 @@ func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) { hrefBuf.WriteByte('"') attrs = append(attrs, hrefBuf.String()) if link.NoteID != 0 { - r.outs(w, footnoteRef(r.opts.FootnoteAnchorPrefix, link)) + r.Outs(w, footnoteRef(r.opts.FootnoteAnchorPrefix, link)) return } @@ -505,14 +563,15 @@ func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) { func (r *Renderer) linkExit(w io.Writer, link *ast.Link) { if link.NoteID == 0 { - r.outs(w, "</a>") + r.Outs(w, "</a>") } } -func (r *Renderer) link(w io.Writer, link *ast.Link, entering bool) { +// Link writes ast.Link node +func (r *Renderer) Link(w io.Writer, link *ast.Link, entering bool) { // mark it but don't link it if it is not a safe link: no smartypants if needSkipLink(r.opts.Flags, link.Destination) { - r.outOneOf(w, entering, "<tt>", "</tt>") + r.OutOneOf(w, entering, "<tt>", "</tt>") return } @@ -526,26 +585,35 @@ func (r *Renderer) link(w io.Writer, link *ast.Link, entering bool) { func (r *Renderer) imageEnter(w io.Writer, image *ast.Image) { dest := image.Destination dest = r.addAbsPrefix(dest) - if r.disableTags == 0 { + if r.DisableTags == 0 { //if options.safe && potentiallyUnsafe(dest) { //out(w, `<img src="" alt="`) //} else { - r.outs(w, `<img src="`) + r.Outs(w, `<img src="`) escLink(w, dest) - r.outs(w, `" alt="`) + r.Outs(w, `" alt="`) //} } - r.disableTags++ + r.DisableTags++ } func (r *Renderer) imageExit(w io.Writer, image *ast.Image) { - r.disableTags-- - if r.disableTags == 0 { + r.DisableTags-- + if r.DisableTags == 0 { if image.Title != nil { - r.outs(w, `" title="`) + r.Outs(w, `" title="`) EscapeHTML(w, image.Title) } - r.outs(w, `" />`) + r.Outs(w, `" />`) + } +} + +// Image writes ast.Image node +func (r *Renderer) Image(w io.Writer, node *ast.Image, entering bool) { + if entering { + r.imageEnter(w, node) + } else { + r.imageExit(w, node) } } @@ -556,33 +624,34 @@ func (r *Renderer) paragraphEnter(w io.Writer, para *ast.Paragraph) { if prev != nil { switch prev.(type) { case *ast.HTMLBlock, *ast.List, *ast.Paragraph, *ast.Heading, *ast.CaptionFigure, *ast.CodeBlock, *ast.BlockQuote, *ast.Aside, *ast.HorizontalRule: - r.cr(w) + r.CR(w) } } if prev == nil { _, isParentBlockQuote := para.Parent.(*ast.BlockQuote) if isParentBlockQuote { - r.cr(w) + r.CR(w) } _, isParentAside := para.Parent.(*ast.Aside) if isParentAside { - r.cr(w) + r.CR(w) } } - tag := tagWithAttributes("<p", BlockAttrs(para)) - r.outs(w, tag) + tag := TagWithAttributes("<p", BlockAttrs(para)) + r.Outs(w, tag) } func (r *Renderer) paragraphExit(w io.Writer, para *ast.Paragraph) { - r.outs(w, "</p>") + r.Outs(w, "</p>") if !(isListItem(para.Parent) && ast.GetNextNode(para) == nil) { - r.cr(w) + r.CR(w) } } -func (r *Renderer) paragraph(w io.Writer, para *ast.Paragraph, entering bool) { +// Paragraph writes ast.Paragraph node +func (r *Renderer) Paragraph(w io.Writer, para *ast.Paragraph, entering bool) { if skipParagraphTags(para) { return } @@ -592,27 +661,22 @@ func (r *Renderer) paragraph(w io.Writer, para *ast.Paragraph, entering bool) { r.paragraphExit(w, para) } } -func (r *Renderer) image(w io.Writer, node *ast.Image, entering bool) { - if entering { - r.imageEnter(w, node) - } else { - r.imageExit(w, node) - } -} -func (r *Renderer) code(w io.Writer, node *ast.Code) { - r.outs(w, "<code>") +// Code writes ast.Code node +func (r *Renderer) Code(w io.Writer, node *ast.Code) { + r.Outs(w, "<code>") EscapeHTML(w, node.Literal) - r.outs(w, "</code>") + r.Outs(w, "</code>") } -func (r *Renderer) htmlBlock(w io.Writer, node *ast.HTMLBlock) { +// HTMLBlock write ast.HTMLBlock node +func (r *Renderer) HTMLBlock(w io.Writer, node *ast.HTMLBlock) { if r.opts.Flags&SkipHTML != 0 { return } - r.cr(w) - r.out(w, node.Literal) - r.cr(w) + r.CR(w) + r.Out(w, node.Literal) + r.CR(w) } func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) { @@ -644,18 +708,19 @@ func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) { attrs = append(attrs, attrID) } attrs = append(attrs, BlockAttrs(nodeData)...) - r.cr(w) + r.CR(w) r.outTag(w, headingOpenTagFromLevel(nodeData.Level), attrs) } func (r *Renderer) headingExit(w io.Writer, heading *ast.Heading) { - r.outs(w, headingCloseTagFromLevel(heading.Level)) + r.Outs(w, headingCloseTagFromLevel(heading.Level)) if !(isListItem(heading.Parent) && ast.GetNextNode(heading) == nil) { - r.cr(w) + r.CR(w) } } -func (r *Renderer) heading(w io.Writer, node *ast.Heading, entering bool) { +// Heading writes ast.Heading node +func (r *Renderer) Heading(w io.Writer, node *ast.Heading, entering bool) { if entering { r.headingEnter(w, node) } else { @@ -663,10 +728,11 @@ func (r *Renderer) heading(w io.Writer, node *ast.Heading, entering bool) { } } -func (r *Renderer) horizontalRule(w io.Writer, node *ast.HorizontalRule) { - r.cr(w) +// HorizontalRule writes ast.HorizontalRule node +func (r *Renderer) HorizontalRule(w io.Writer, node *ast.HorizontalRule) { + r.CR(w) r.outHRTag(w, BlockAttrs(node)) - r.cr(w) + r.CR(w) } func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) { @@ -674,17 +740,17 @@ func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) { var attrs []string if nodeData.IsFootnotesList { - r.outs(w, "\n<div class=\"footnotes\">\n\n") + r.Outs(w, "\n<div class=\"footnotes\">\n\n") if r.opts.Flags&FootnoteNoHRTag == 0 { r.outHRTag(w, nil) - r.cr(w) + r.CR(w) } } - r.cr(w) + r.CR(w) if isListItem(nodeData.Parent) { grand := nodeData.Parent.GetParent() if isListTight(grand) { - r.cr(w) + r.CR(w) } } @@ -700,7 +766,7 @@ func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) { } attrs = append(attrs, BlockAttrs(nodeData)...) r.outTag(w, openTag, attrs) - r.cr(w) + r.CR(w) } func (r *Renderer) listExit(w io.Writer, list *ast.List) { @@ -711,7 +777,7 @@ func (r *Renderer) listExit(w io.Writer, list *ast.List) { if list.ListFlags&ast.ListTypeDefinition != 0 { closeTag = "</dl>" } - r.outs(w, closeTag) + r.Outs(w, closeTag) //cr(w) //if node.parent.Type != Item { @@ -721,18 +787,19 @@ func (r *Renderer) listExit(w io.Writer, list *ast.List) { switch parent.(type) { case *ast.ListItem: if ast.GetNextNode(list) != nil { - r.cr(w) + r.CR(w) } case *ast.Document, *ast.BlockQuote, *ast.Aside: - r.cr(w) + r.CR(w) } if list.IsFootnotesList { - r.outs(w, "\n</div>\n") + r.Outs(w, "\n</div>\n") } } -func (r *Renderer) list(w io.Writer, list *ast.List, entering bool) { +// List writes ast.List node +func (r *Renderer) List(w io.Writer, list *ast.List, entering bool) { if entering { r.listEnter(w, list) } else { @@ -742,11 +809,11 @@ func (r *Renderer) list(w io.Writer, list *ast.List, entering bool) { func (r *Renderer) listItemEnter(w io.Writer, listItem *ast.ListItem) { if listItemOpenCR(listItem) { - r.cr(w) + r.CR(w) } if listItem.RefLink != nil { slug := slugify(listItem.RefLink) - r.outs(w, footnoteItem(r.opts.FootnoteAnchorPrefix, slug)) + r.Outs(w, footnoteItem(r.opts.FootnoteAnchorPrefix, slug)) return } @@ -757,7 +824,7 @@ func (r *Renderer) listItemEnter(w io.Writer, listItem *ast.ListItem) { if listItem.ListFlags&ast.ListTypeTerm != 0 { openTag = "<dt>" } - r.outs(w, openTag) + r.Outs(w, openTag) } func (r *Renderer) listItemExit(w io.Writer, listItem *ast.ListItem) { @@ -766,7 +833,7 @@ func (r *Renderer) listItemExit(w io.Writer, listItem *ast.ListItem) { prefix := r.opts.FootnoteAnchorPrefix link := r.opts.FootnoteReturnLinkContents s := footnoteReturnLink(prefix, link, slug) - r.outs(w, s) + r.Outs(w, s) } closeTag := "</li>" @@ -776,11 +843,12 @@ func (r *Renderer) listItemExit(w io.Writer, listItem *ast.ListItem) { if listItem.ListFlags&ast.ListTypeTerm != 0 { closeTag = "</dt>" } - r.outs(w, closeTag) - r.cr(w) + r.Outs(w, closeTag) + r.CR(w) } -func (r *Renderer) listItem(w io.Writer, listItem *ast.ListItem, entering bool) { +// ListItem writes ast.ListItem node +func (r *Renderer) ListItem(w io.Writer, listItem *ast.ListItem, entering bool) { if entering { r.listItemEnter(w, listItem) } else { @@ -788,38 +856,74 @@ func (r *Renderer) listItem(w io.Writer, listItem *ast.ListItem, entering bool) } } -func (r *Renderer) codeBlock(w io.Writer, codeBlock *ast.CodeBlock) { +// EscapeHTMLCallouts writes html-escaped d to w. It escapes &, <, > and " characters, *but* +// expands callouts <<N>> with the callout HTML, i.e. by calling r.callout() with a newly created +// ast.Callout node. +func (r *Renderer) EscapeHTMLCallouts(w io.Writer, d []byte) { + ld := len(d) +Parse: + for i := 0; i < ld; i++ { + for _, comment := range r.opts.Comments { + if !bytes.HasPrefix(d[i:], comment) { + break + } + + lc := len(comment) + if i+lc < ld { + if id, consumed := parser.IsCallout(d[i+lc:]); consumed > 0 { + // We have seen a callout + callout := &ast.Callout{ID: id} + r.Callout(w, callout) + i += consumed + lc - 1 + continue Parse + } + } + } + + escSeq := Escaper[d[i]] + if escSeq != nil { + w.Write(escSeq) + } else { + w.Write([]byte{d[i]}) + } + } +} + +// CodeBlock writes ast.CodeBlock node +func (r *Renderer) CodeBlock(w io.Writer, codeBlock *ast.CodeBlock) { var attrs []string // TODO(miek): this can add multiple class= attribute, they should be coalesced into one. // This is probably true for some other elements as well attrs = appendLanguageAttr(attrs, codeBlock.Info) attrs = append(attrs, BlockAttrs(codeBlock)...) - r.cr(w) + r.CR(w) - r.outs(w, "<pre>") - code := tagWithAttributes("<code", attrs) - r.outs(w, code) + r.Outs(w, "<pre>") + code := TagWithAttributes("<code", attrs) + r.Outs(w, code) if r.opts.Comments != nil { r.EscapeHTMLCallouts(w, codeBlock.Literal) } else { EscapeHTML(w, codeBlock.Literal) } - r.outs(w, "</code>") - r.outs(w, "</pre>") + r.Outs(w, "</code>") + r.Outs(w, "</pre>") if !isListItem(codeBlock.Parent) { - r.cr(w) + r.CR(w) } } -func (r *Renderer) caption(w io.Writer, caption *ast.Caption, entering bool) { +// Caption writes ast.Caption node +func (r *Renderer) Caption(w io.Writer, caption *ast.Caption, entering bool) { if entering { - r.outs(w, "<figcaption>") + r.Outs(w, "<figcaption>") return } - r.outs(w, "</figcaption>") + r.Outs(w, "</figcaption>") } -func (r *Renderer) captionFigure(w io.Writer, figure *ast.CaptionFigure, entering bool) { +// CaptionFigure writes ast.CaptionFigure node +func (r *Renderer) CaptionFigure(w io.Writer, figure *ast.CaptionFigure, entering bool) { // TODO(miek): copy more generic ways of mmark over to here. fig := "<figure" if figure.HeadingID != "" { @@ -827,13 +931,14 @@ func (r *Renderer) captionFigure(w io.Writer, figure *ast.CaptionFigure, enterin } else { fig += ">" } - r.outOneOf(w, entering, fig, "\n</figure>\n") + r.OutOneOf(w, entering, fig, "\n</figure>\n") } -func (r *Renderer) tableCell(w io.Writer, tableCell *ast.TableCell, entering bool) { +// TableCell writes ast.TableCell node +func (r *Renderer) TableCell(w io.Writer, tableCell *ast.TableCell, entering bool) { if !entering { - r.outOneOf(w, tableCell.IsHeader, "</th>", "</td>") - r.cr(w) + r.OutOneOf(w, tableCell.IsHeader, "</th>", "</td>") + r.CR(w) return } @@ -848,44 +953,47 @@ func (r *Renderer) tableCell(w io.Writer, tableCell *ast.TableCell, entering boo attrs = append(attrs, fmt.Sprintf(`align="%s"`, align)) } if ast.GetPrevNode(tableCell) == nil { - r.cr(w) + r.CR(w) } r.outTag(w, openTag, attrs) } -func (r *Renderer) tableBody(w io.Writer, node *ast.TableBody, entering bool) { +// TableBody writes ast.TableBody node +func (r *Renderer) TableBody(w io.Writer, node *ast.TableBody, entering bool) { if entering { - r.cr(w) - r.outs(w, "<tbody>") + r.CR(w) + r.Outs(w, "<tbody>") // XXX: this is to adhere to a rather silly test. Should fix test. if ast.GetFirstChild(node) == nil { - r.cr(w) + r.CR(w) } } else { - r.outs(w, "</tbody>") - r.cr(w) + r.Outs(w, "</tbody>") + r.CR(w) } } -func (r *Renderer) matter(w io.Writer, node *ast.DocumentMatter, entering bool) { +// DocumentMatter writes ast.DocumentMatter +func (r *Renderer) DocumentMatter(w io.Writer, node *ast.DocumentMatter, entering bool) { if !entering { return } if r.documentMatter != ast.DocumentMatterNone { - r.outs(w, "</section>\n") + r.Outs(w, "</section>\n") } switch node.Matter { case ast.DocumentMatterFront: - r.outs(w, `<section data-matter="front">`) + r.Outs(w, `<section data-matter="front">`) case ast.DocumentMatterMain: - r.outs(w, `<section data-matter="main">`) + r.Outs(w, `<section data-matter="main">`) case ast.DocumentMatterBack: - r.outs(w, `<section data-matter="back">`) + r.Outs(w, `<section data-matter="back">`) } r.documentMatter = node.Matter } -func (r *Renderer) citation(w io.Writer, node *ast.Citation) { +// Citation writes ast.Citation node +func (r *Renderer) Citation(w io.Writer, node *ast.Citation) { for i, c := range node.Destination { attr := []string{`class="none"`} switch node.Type[i] { @@ -897,23 +1005,25 @@ func (r *Renderer) citation(w io.Writer, node *ast.Citation) { attr[0] = `class="suppressed"` } r.outTag(w, "<cite", attr) - r.outs(w, fmt.Sprintf(`<a href="#%s">`+r.opts.CitationFormatString+`</a>`, c, c)) - r.outs(w, "</cite>") + r.Outs(w, fmt.Sprintf(`<a href="#%s">`+r.opts.CitationFormatString+`</a>`, c, c)) + r.Outs(w, "</cite>") } } -func (r *Renderer) callout(w io.Writer, node *ast.Callout) { +// Callout writes ast.Callout node +func (r *Renderer) Callout(w io.Writer, node *ast.Callout) { attr := []string{`class="callout"`} r.outTag(w, "<span", attr) - r.out(w, node.ID) - r.outs(w, "</span>") + r.Out(w, node.ID) + r.Outs(w, "</span>") } -func (r *Renderer) index(w io.Writer, node *ast.Index) { +// Index writes ast.Index node +func (r *Renderer) Index(w io.Writer, node *ast.Index) { // there is no in-text representation. attr := []string{`class="index"`, fmt.Sprintf(`id="%s"`, node.ID)} r.outTag(w, "<span", attr) - r.outs(w, "</span>") + r.Outs(w, "</span>") } // RenderNode renders a markdown node to HTML @@ -926,102 +1036,102 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal } switch node := node.(type) { case *ast.Text: - r.text(w, node) + r.Text(w, node) case *ast.Softbreak: - r.cr(w) + r.CR(w) // TODO: make it configurable via out(renderer.softbreak) case *ast.Hardbreak: - r.hardBreak(w, node) + r.HardBreak(w, node) case *ast.NonBlockingSpace: - r.nonBlockingSpace(w, node) + r.NonBlockingSpace(w, node) case *ast.Emph: - r.outOneOf(w, entering, "<em>", "</em>") + r.OutOneOf(w, entering, "<em>", "</em>") case *ast.Strong: - r.outOneOf(w, entering, "<strong>", "</strong>") + r.OutOneOf(w, entering, "<strong>", "</strong>") case *ast.Del: - r.outOneOf(w, entering, "<del>", "</del>") + r.OutOneOf(w, entering, "<del>", "</del>") case *ast.BlockQuote: - tag := tagWithAttributes("<blockquote", BlockAttrs(node)) - r.outOneOfCr(w, entering, tag, "</blockquote>") + tag := TagWithAttributes("<blockquote", BlockAttrs(node)) + r.OutOneOfCr(w, entering, tag, "</blockquote>") case *ast.Aside: - tag := tagWithAttributes("<aside", BlockAttrs(node)) - r.outOneOfCr(w, entering, tag, "</aside>") + tag := TagWithAttributes("<aside", BlockAttrs(node)) + r.OutOneOfCr(w, entering, tag, "</aside>") case *ast.Link: - r.link(w, node, entering) + r.Link(w, node, entering) case *ast.CrossReference: link := &ast.Link{Destination: append([]byte("#"), node.Destination...)} - r.link(w, link, entering) + r.Link(w, link, entering) case *ast.Citation: - r.citation(w, node) + r.Citation(w, node) case *ast.Image: if r.opts.Flags&SkipImages != 0 { return ast.SkipChildren } - r.image(w, node, entering) + r.Image(w, node, entering) case *ast.Code: - r.code(w, node) + r.Code(w, node) case *ast.CodeBlock: - r.codeBlock(w, node) + r.CodeBlock(w, node) case *ast.Caption: - r.caption(w, node, entering) + r.Caption(w, node, entering) case *ast.CaptionFigure: - r.captionFigure(w, node, entering) + r.CaptionFigure(w, node, entering) case *ast.Document: // do nothing case *ast.Paragraph: - r.paragraph(w, node, entering) + r.Paragraph(w, node, entering) case *ast.HTMLSpan: - r.htmlSpan(w, node) + r.HTMLSpan(w, node) case *ast.HTMLBlock: - r.htmlBlock(w, node) + r.HTMLBlock(w, node) case *ast.Heading: - r.heading(w, node, entering) + r.Heading(w, node, entering) case *ast.HorizontalRule: - r.horizontalRule(w, node) + r.HorizontalRule(w, node) case *ast.List: - r.list(w, node, entering) + r.List(w, node, entering) case *ast.ListItem: - r.listItem(w, node, entering) + r.ListItem(w, node, entering) case *ast.Table: - tag := tagWithAttributes("<table", BlockAttrs(node)) - r.outOneOfCr(w, entering, tag, "</table>") + tag := TagWithAttributes("<table", BlockAttrs(node)) + r.OutOneOfCr(w, entering, tag, "</table>") case *ast.TableCell: - r.tableCell(w, node, entering) + r.TableCell(w, node, entering) case *ast.TableHeader: - r.outOneOfCr(w, entering, "<thead>", "</thead>") + r.OutOneOfCr(w, entering, "<thead>", "</thead>") case *ast.TableBody: - r.tableBody(w, node, entering) + r.TableBody(w, node, entering) case *ast.TableRow: - r.outOneOfCr(w, entering, "<tr>", "</tr>") + r.OutOneOfCr(w, entering, "<tr>", "</tr>") case *ast.TableFooter: - r.outOneOfCr(w, entering, "<tfoot>", "</tfoot>") + r.OutOneOfCr(w, entering, "<tfoot>", "</tfoot>") case *ast.Math: - r.outOneOf(w, true, `<span class="math inline">\(`, `\)</span>`) + r.OutOneOf(w, true, `<span class="math inline">\(`, `\)</span>`) EscapeHTML(w, node.Literal) - r.outOneOf(w, false, `<span class="math inline">\(`, `\)</span>`) + r.OutOneOf(w, false, `<span class="math inline">\(`, `\)</span>`) case *ast.MathBlock: - r.outOneOf(w, entering, `<p><span class="math display">\[`, `\]</span></p>`) + r.OutOneOf(w, entering, `<p><span class="math display">\[`, `\]</span></p>`) if entering { EscapeHTML(w, node.Literal) } case *ast.DocumentMatter: - r.matter(w, node, entering) + r.DocumentMatter(w, node, entering) case *ast.Callout: - r.callout(w, node) + r.Callout(w, node) case *ast.Index: - r.index(w, node) + r.Index(w, node) case *ast.Subscript: - r.outOneOf(w, true, "<sub>", "</sub>") + r.OutOneOf(w, true, "<sub>", "</sub>") if entering { Escape(w, node.Literal) } - r.outOneOf(w, false, "<sub>", "</sub>") + r.OutOneOf(w, false, "<sub>", "</sub>") case *ast.Superscript: - r.outOneOf(w, true, "<sup>", "</sup>") + r.OutOneOf(w, true, "<sup>", "</sup>") if entering { Escape(w, node.Literal) } - r.outOneOf(w, false, "<sup>", "</sup>") + r.OutOneOf(w, false, "<sup>", "</sup>") case *ast.Footnotes: // nothing by default; just output the list. default: @@ -1041,7 +1151,7 @@ func (r *Renderer) RenderHeader(w io.Writer, ast ast.Node) { // RenderFooter writes HTML document footer. func (r *Renderer) RenderFooter(w io.Writer, _ ast.Node) { if r.documentMatter != ast.DocumentMatterNone { - r.outs(w, "</section>\n") + r.Outs(w, "</section>\n") } if r.opts.Flags&CompletePage == 0 { @@ -1315,7 +1425,8 @@ func BlockAttrs(node ast.Node) []string { return s } -func tagWithAttributes(name string, attrs []string) string { +// TagWithAttributes creates a HTML tag with a given name and attributes +func TagWithAttributes(name string, attrs []string) string { s := name if len(attrs) > 0 { s += " " + strings.Join(attrs, " ") |