refactor HTMLRenderer.RenderNode; change Walk to be more like in go/ast package
This commit is contained in:
parent
80758f072a
commit
41105f379c
811
html_renderer.go
811
html_renderer.go
|
@ -307,29 +307,29 @@ func appendLanguageAttr(attrs []string, info []byte) []string {
|
||||||
return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang]))
|
return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) {
|
func (r *HTMLRenderer) tag(w io.Writer, name string, attrs []string) {
|
||||||
w.Write(name)
|
io.WriteString(w, name)
|
||||||
if len(attrs) > 0 {
|
if len(attrs) > 0 {
|
||||||
w.Write(spaceBytes)
|
w.Write(spaceBytes)
|
||||||
w.Write([]byte(strings.Join(attrs, " ")))
|
io.WriteString(w, strings.Join(attrs, " "))
|
||||||
}
|
}
|
||||||
w.Write(gtBytes)
|
w.Write(gtBytes)
|
||||||
r.lastOutputLen = 1
|
r.lastOutputLen = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func footnoteRef(prefix string, node *LinkData) []byte {
|
func footnoteRef(prefix string, node *LinkData) string {
|
||||||
urlFrag := prefix + string(slugify(node.Destination))
|
urlFrag := prefix + string(slugify(node.Destination))
|
||||||
anchor := fmt.Sprintf(`<a rel="footnote" href="#fn:%s">%d</a>`, urlFrag, node.NoteID)
|
anchor := fmt.Sprintf(`<a rel="footnote" href="#fn:%s">%d</a>`, urlFrag, node.NoteID)
|
||||||
return []byte(fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, urlFrag, anchor))
|
return fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, urlFrag, anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func footnoteItem(prefix string, slug []byte) []byte {
|
func footnoteItem(prefix string, slug []byte) string {
|
||||||
return []byte(fmt.Sprintf(`<li id="fn:%s%s">`, prefix, slug))
|
return fmt.Sprintf(`<li id="fn:%s%s">`, prefix, slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte {
|
func footnoteReturnLink(prefix, returnLink string, slug []byte) string {
|
||||||
const format = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
|
const format = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
|
||||||
return []byte(fmt.Sprintf(format, prefix, slug, returnLink))
|
return fmt.Sprintf(format, prefix, slug, returnLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemOpenCR(node *Node) bool {
|
func itemOpenCR(node *Node) bool {
|
||||||
|
@ -365,13 +365,20 @@ func cellAlignment(align CellAlignFlags) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HTMLRenderer) out(w io.Writer, text []byte) {
|
func (r *HTMLRenderer) out(w io.Writer, d []byte) {
|
||||||
|
r.lastOutputLen = len(d)
|
||||||
if r.disableTags > 0 {
|
if r.disableTags > 0 {
|
||||||
w.Write(htmlTagRe.ReplaceAll(text, []byte{}))
|
d = htmlTagRe.ReplaceAll(d, []byte{})
|
||||||
} else {
|
|
||||||
w.Write(text)
|
|
||||||
}
|
}
|
||||||
r.lastOutputLen = len(text)
|
w.Write(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) outs(w io.Writer, s string) {
|
||||||
|
r.lastOutputLen = len(s)
|
||||||
|
if r.disableTags > 0 {
|
||||||
|
s = htmlTagRe.ReplaceAllString(s, "")
|
||||||
|
}
|
||||||
|
io.WriteString(w, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HTMLRenderer) cr(w io.Writer) {
|
func (r *HTMLRenderer) cr(w io.Writer) {
|
||||||
|
@ -386,92 +393,405 @@ var (
|
||||||
spaceBytes = []byte{' '}
|
spaceBytes = []byte{' '}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func headingOpenTagFromLevel(level int) string {
|
||||||
brTag = []byte("<br>")
|
|
||||||
brXHTMLTag = []byte("<br />")
|
|
||||||
emTag = []byte("<em>")
|
|
||||||
emCloseTag = []byte("</em>")
|
|
||||||
strongTag = []byte("<strong>")
|
|
||||||
strongCloseTag = []byte("</strong>")
|
|
||||||
delTag = []byte("<del>")
|
|
||||||
delCloseTag = []byte("</del>")
|
|
||||||
ttTag = []byte("<tt>")
|
|
||||||
ttCloseTag = []byte("</tt>")
|
|
||||||
aTag = []byte("<a")
|
|
||||||
aCloseTag = []byte("</a>")
|
|
||||||
preTag = []byte("<pre>")
|
|
||||||
preCloseTag = []byte("</pre>")
|
|
||||||
codeTag = []byte("<code>")
|
|
||||||
codeCloseTag = []byte("</code>")
|
|
||||||
pTag = []byte("<p>")
|
|
||||||
pCloseTag = []byte("</p>")
|
|
||||||
blockquoteTag = []byte("<blockquote>")
|
|
||||||
blockquoteCloseTag = []byte("</blockquote>")
|
|
||||||
hrTag = []byte("<hr>")
|
|
||||||
hrXHTMLTag = []byte("<hr />")
|
|
||||||
ulTag = []byte("<ul>")
|
|
||||||
ulCloseTag = []byte("</ul>")
|
|
||||||
olTag = []byte("<ol>")
|
|
||||||
olCloseTag = []byte("</ol>")
|
|
||||||
dlTag = []byte("<dl>")
|
|
||||||
dlCloseTag = []byte("</dl>")
|
|
||||||
liTag = []byte("<li>")
|
|
||||||
liCloseTag = []byte("</li>")
|
|
||||||
ddTag = []byte("<dd>")
|
|
||||||
ddCloseTag = []byte("</dd>")
|
|
||||||
dtTag = []byte("<dt>")
|
|
||||||
dtCloseTag = []byte("</dt>")
|
|
||||||
tableTag = []byte("<table>")
|
|
||||||
tableCloseTag = []byte("</table>")
|
|
||||||
tdTag = []byte("<td")
|
|
||||||
tdCloseTag = []byte("</td>")
|
|
||||||
thTag = []byte("<th")
|
|
||||||
thCloseTag = []byte("</th>")
|
|
||||||
theadTag = []byte("<thead>")
|
|
||||||
theadCloseTag = []byte("</thead>")
|
|
||||||
tbodyTag = []byte("<tbody>")
|
|
||||||
tbodyCloseTag = []byte("</tbody>")
|
|
||||||
trTag = []byte("<tr>")
|
|
||||||
trCloseTag = []byte("</tr>")
|
|
||||||
h1Tag = []byte("<h1")
|
|
||||||
h1CloseTag = []byte("</h1>")
|
|
||||||
h2Tag = []byte("<h2")
|
|
||||||
h2CloseTag = []byte("</h2>")
|
|
||||||
h3Tag = []byte("<h3")
|
|
||||||
h3CloseTag = []byte("</h3>")
|
|
||||||
h4Tag = []byte("<h4")
|
|
||||||
h4CloseTag = []byte("</h4>")
|
|
||||||
h5Tag = []byte("<h5")
|
|
||||||
h5CloseTag = []byte("</h5>")
|
|
||||||
h6Tag = []byte("<h6")
|
|
||||||
h6CloseTag = []byte("</h6>")
|
|
||||||
|
|
||||||
footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")
|
|
||||||
footnotesCloseDivBytes = []byte("\n</div>\n")
|
|
||||||
)
|
|
||||||
|
|
||||||
func headingTagsFromLevel(level int) ([]byte, []byte) {
|
|
||||||
switch level {
|
switch level {
|
||||||
case 1:
|
case 1:
|
||||||
return h1Tag, h1CloseTag
|
return "<h1"
|
||||||
case 2:
|
case 2:
|
||||||
return h2Tag, h2CloseTag
|
return "<h2"
|
||||||
case 3:
|
case 3:
|
||||||
return h3Tag, h3CloseTag
|
return "<h3"
|
||||||
case 4:
|
case 4:
|
||||||
return h4Tag, h4CloseTag
|
return "<h4"
|
||||||
case 5:
|
case 5:
|
||||||
return h5Tag, h5CloseTag
|
return "<h5"
|
||||||
default:
|
default:
|
||||||
return h6Tag, h6CloseTag
|
return "<h6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func headingCloseTagFromLevel(level int) string {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return "</h1>"
|
||||||
|
case 2:
|
||||||
|
return "</h2>"
|
||||||
|
case 3:
|
||||||
|
return "</h3>"
|
||||||
|
case 4:
|
||||||
|
return "</h4>"
|
||||||
|
case 5:
|
||||||
|
return "</h5>"
|
||||||
|
default:
|
||||||
|
return "</h6>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
||||||
if r.params.Flags&UseXHTML == 0 {
|
if r.params.Flags&UseXHTML == 0 {
|
||||||
r.out(w, hrTag)
|
r.out(w, []byte("<hr>"))
|
||||||
} else {
|
} else {
|
||||||
r.out(w, hrXHTMLTag)
|
r.out(w, []byte("<hr />"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) text(w io.Writer, node *Node, nodeData *TextData) {
|
||||||
|
if r.params.Flags&Smartypants != 0 {
|
||||||
|
var tmp bytes.Buffer
|
||||||
|
escapeHTML(&tmp, node.Literal)
|
||||||
|
r.sr.Process(w, tmp.Bytes())
|
||||||
|
} else {
|
||||||
|
if isLinkData(node.Parent.Data) {
|
||||||
|
escLink(w, node.Literal)
|
||||||
|
} else {
|
||||||
|
escapeHTML(w, node.Literal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) hardBreak(w io.Writer, node *Node, nodeData *HardbreakData) {
|
||||||
|
s := "<br>"
|
||||||
|
if r.params.Flags&UseXHTML != 0 {
|
||||||
|
s = "<br />"
|
||||||
|
}
|
||||||
|
r.outs(w, s)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) openOrCloseTag(w io.Writer, isOpen bool, openTag string, closeTag string) {
|
||||||
|
if isOpen {
|
||||||
|
r.outs(w, openTag)
|
||||||
|
} else {
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) crOpenOrCloseTag(w io.Writer, isOpen bool, openTag string, closeTag string) {
|
||||||
|
if isOpen {
|
||||||
|
r.cr(w)
|
||||||
|
r.outs(w, openTag)
|
||||||
|
} else {
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) span(w io.Writer, node *Node, nodeData *HTMLSpanData) {
|
||||||
|
if r.params.Flags&SkipHTML != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.out(w, node.Literal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) link(w io.Writer, node *Node, nodeData *LinkData, entering bool) {
|
||||||
|
var attrs []string
|
||||||
|
// mark it but don't link it if it is not a safe link: no smartypants
|
||||||
|
dest := nodeData.Destination
|
||||||
|
if needSkipLink(r.params.Flags, dest) {
|
||||||
|
r.openOrCloseTag(w, entering, "<tt>", "</tt>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entering {
|
||||||
|
if nodeData.NoteID == 0 {
|
||||||
|
r.out(w, []byte("</a>"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// entering
|
||||||
|
dest = r.addAbsPrefix(dest)
|
||||||
|
var hrefBuf bytes.Buffer
|
||||||
|
hrefBuf.WriteString("href=\"")
|
||||||
|
escLink(&hrefBuf, dest)
|
||||||
|
hrefBuf.WriteByte('"')
|
||||||
|
attrs = append(attrs, hrefBuf.String())
|
||||||
|
if nodeData.NoteID != 0 {
|
||||||
|
r.outs(w, footnoteRef(r.params.FootnoteAnchorPrefix, nodeData))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs = appendLinkAttrs(attrs, r.params.Flags, dest)
|
||||||
|
if len(nodeData.Title) > 0 {
|
||||||
|
var titleBuff bytes.Buffer
|
||||||
|
titleBuff.WriteString("title=\"")
|
||||||
|
escapeHTML(&titleBuff, nodeData.Title)
|
||||||
|
titleBuff.WriteByte('"')
|
||||||
|
attrs = append(attrs, titleBuff.String())
|
||||||
|
}
|
||||||
|
r.tag(w, "<a", attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) imageEnter(w io.Writer, node *Node, nodeData *ImageData) {
|
||||||
|
dest := nodeData.Destination
|
||||||
|
dest = r.addAbsPrefix(dest)
|
||||||
|
if r.disableTags == 0 {
|
||||||
|
//if options.safe && potentiallyUnsafe(dest) {
|
||||||
|
//out(w, `<img src="" alt="`)
|
||||||
|
//} else {
|
||||||
|
r.out(w, []byte(`<img src="`))
|
||||||
|
escLink(w, dest)
|
||||||
|
r.out(w, []byte(`" alt="`))
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
r.disableTags++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) imageExit(w io.Writer, node *Node, nodeData *ImageData) {
|
||||||
|
r.disableTags--
|
||||||
|
if r.disableTags == 0 {
|
||||||
|
if nodeData.Title != nil {
|
||||||
|
r.out(w, []byte(`" title="`))
|
||||||
|
escapeHTML(w, nodeData.Title)
|
||||||
|
}
|
||||||
|
r.out(w, []byte(`" />`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) paragraphEnter(w io.Writer, node *Node, nodeData *ParagraphData) {
|
||||||
|
// TODO: untangle this clusterfuck about when the newlines need
|
||||||
|
// to be added and when not.
|
||||||
|
if node.Prev != nil {
|
||||||
|
switch node.Prev.Data.(type) {
|
||||||
|
case *HTMLBlockData, *ListData, *ParagraphData, *HeadingData, *CodeBlockData, *BlockQuoteData, *HorizontalRuleData:
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isBlockQuoteData(node.Parent.Data) && node.Prev == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.out(w, []byte("<p>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) paragraphExit(w io.Writer, node *Node, nodeData *ParagraphData) {
|
||||||
|
r.out(w, []byte("</p>"))
|
||||||
|
if !(isItemData(node.Parent.Data) && node.Next == nil) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) paragraph(w io.Writer, node *Node, nodeData *ParagraphData, entering bool) {
|
||||||
|
if skipParagraphTags(node) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
r.paragraphEnter(w, node, nodeData)
|
||||||
|
} else {
|
||||||
|
r.paragraphExit(w, node, nodeData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (r *HTMLRenderer) image(w io.Writer, node *Node, nodeData *ImageData, entering bool) {
|
||||||
|
if entering {
|
||||||
|
r.imageEnter(w, node, nodeData)
|
||||||
|
} else {
|
||||||
|
r.imageExit(w, node, nodeData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) code(w io.Writer, node *Node, nodeData *CodeData) {
|
||||||
|
r.outs(w, "<code>")
|
||||||
|
escapeHTML(w, node.Literal)
|
||||||
|
r.outs(w, "</code>")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) htmlBlock(w io.Writer, node *Node, nodeData *HTMLBlockData) {
|
||||||
|
if r.params.Flags&SkipHTML != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, node.Literal)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) heading(w io.Writer, node *Node, nodeData *HeadingData, entering bool) {
|
||||||
|
if !entering {
|
||||||
|
closeTag := headingCloseTagFromLevel(nodeData.Level)
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
if !(isItemData(node.Parent.Data) && node.Next == nil) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// entering
|
||||||
|
var attrs []string
|
||||||
|
if nodeData.IsTitleblock {
|
||||||
|
attrs = append(attrs, `class="title"`)
|
||||||
|
}
|
||||||
|
if nodeData.HeadingID != "" {
|
||||||
|
id := r.ensureUniqueHeadingID(nodeData.HeadingID)
|
||||||
|
if r.params.HeadingIDPrefix != "" {
|
||||||
|
id = r.params.HeadingIDPrefix + id
|
||||||
|
}
|
||||||
|
if r.params.HeadingIDSuffix != "" {
|
||||||
|
id = id + r.params.HeadingIDSuffix
|
||||||
|
}
|
||||||
|
attrID := `id="` + id + `"`
|
||||||
|
attrs = append(attrs, attrID)
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
openTag := headingOpenTagFromLevel(nodeData.Level)
|
||||||
|
r.tag(w, openTag, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) horizontalRule(w io.Writer) {
|
||||||
|
r.cr(w)
|
||||||
|
r.outHRTag(w)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) listEnter(w io.Writer, node *Node, nodeData *ListData) {
|
||||||
|
// TODO: attrs don't seem to be set
|
||||||
|
var attrs []string
|
||||||
|
|
||||||
|
openTag := "<ul"
|
||||||
|
if nodeData.ListFlags&ListTypeOrdered != 0 {
|
||||||
|
openTag = "<ol"
|
||||||
|
}
|
||||||
|
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
openTag = "<dl"
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodeData.IsFootnotesList {
|
||||||
|
r.outs(w, "\n<div class=\"footnotes\">\n\n")
|
||||||
|
r.outHRTag(w)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
if isItemData(node.Parent.Data) {
|
||||||
|
grand := node.Parent.Parent
|
||||||
|
if isListTight(grand.Data) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.tag(w, openTag, attrs)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) listExit(w io.Writer, node *Node, nodeData *ListData) {
|
||||||
|
closeTag := "</ul>"
|
||||||
|
if nodeData.ListFlags&ListTypeOrdered != 0 {
|
||||||
|
closeTag = "</ol>"
|
||||||
|
}
|
||||||
|
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
closeTag = "</dl>"
|
||||||
|
}
|
||||||
|
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
//cr(w)
|
||||||
|
//if node.parent.Type != Item {
|
||||||
|
// cr(w)
|
||||||
|
//}
|
||||||
|
if isItemData(node.Parent.Data) && node.Next != nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if isDocumentData(node.Parent.Data) || isBlockQuoteData(node.Parent.Data) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if nodeData.IsFootnotesList {
|
||||||
|
r.outs(w, "\n</div>\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) list(w io.Writer, node *Node, nodeData *ListData, entering bool) {
|
||||||
|
if entering {
|
||||||
|
r.listEnter(w, node, nodeData)
|
||||||
|
} else {
|
||||||
|
r.listExit(w, node, nodeData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) item(w io.Writer, node *Node, nodeData *ItemData, entering bool) {
|
||||||
|
if entering {
|
||||||
|
openTag := "<li>"
|
||||||
|
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
openTag = "<dd>"
|
||||||
|
}
|
||||||
|
if nodeData.ListFlags&ListTypeTerm != 0 {
|
||||||
|
openTag = "<dt>"
|
||||||
|
}
|
||||||
|
if itemOpenCR(node) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if nodeData.RefLink != nil {
|
||||||
|
slug := slugify(nodeData.RefLink)
|
||||||
|
r.outs(w, footnoteItem(r.params.FootnoteAnchorPrefix, slug))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.outs(w, openTag)
|
||||||
|
} else {
|
||||||
|
closeTag := "</li>"
|
||||||
|
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
closeTag = "</dd>"
|
||||||
|
}
|
||||||
|
if nodeData.ListFlags&ListTypeTerm != 0 {
|
||||||
|
closeTag = "</dt>"
|
||||||
|
}
|
||||||
|
if nodeData.RefLink != nil {
|
||||||
|
slug := slugify(nodeData.RefLink)
|
||||||
|
if r.params.Flags&FootnoteReturnLinks != 0 {
|
||||||
|
r.outs(w, footnoteReturnLink(r.params.FootnoteAnchorPrefix, r.params.FootnoteReturnLinkContents, slug))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) codeBlock(w io.Writer, node *Node, nodeData *CodeBlockData) {
|
||||||
|
var attrs []string
|
||||||
|
attrs = appendLanguageAttr(attrs, nodeData.Info)
|
||||||
|
r.cr(w)
|
||||||
|
r.outs(w, "<pre>")
|
||||||
|
r.tag(w, "<code", attrs)
|
||||||
|
escapeHTML(w, node.Literal)
|
||||||
|
r.outs(w, "</code>")
|
||||||
|
r.outs(w, "</pre>")
|
||||||
|
if !isItemData(node.Parent.Data) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) tableCell(w io.Writer, node *Node, nodeData *TableCellData, entering bool) {
|
||||||
|
if !entering {
|
||||||
|
closeTag := "</td>"
|
||||||
|
if nodeData.IsHeader {
|
||||||
|
closeTag = "</th>"
|
||||||
|
}
|
||||||
|
|
||||||
|
r.outs(w, closeTag)
|
||||||
|
r.cr(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// entering
|
||||||
|
var attrs []string
|
||||||
|
openTag := "<td"
|
||||||
|
if nodeData.IsHeader {
|
||||||
|
openTag = "<th"
|
||||||
|
}
|
||||||
|
align := cellAlignment(nodeData.Align)
|
||||||
|
if align != "" {
|
||||||
|
attrs = append(attrs, fmt.Sprintf(`align="%s"`, align))
|
||||||
|
}
|
||||||
|
if node.Prev == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.tag(w, openTag, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) tableBody(w io.Writer, node *Node, nodeData *TableBodyData, entering bool) {
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.outs(w, "<tbody>")
|
||||||
|
// XXX: this is to adhere to a rather silly test. Should fix test.
|
||||||
|
if node.FirstChild == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.outs(w, "</tbody>")
|
||||||
|
r.cr(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,328 +806,59 @@ func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
||||||
// The typical behavior is to return GoToNext, which asks for the usual
|
// The typical behavior is to return GoToNext, which asks for the usual
|
||||||
// traversal to the next node.
|
// traversal to the next node.
|
||||||
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
||||||
attrs := []string{}
|
|
||||||
switch nodeData := node.Data.(type) {
|
switch nodeData := node.Data.(type) {
|
||||||
case *TextData:
|
case *TextData:
|
||||||
if r.params.Flags&Smartypants != 0 {
|
r.text(w, node, nodeData)
|
||||||
var tmp bytes.Buffer
|
|
||||||
escapeHTML(&tmp, node.Literal)
|
|
||||||
r.sr.Process(w, tmp.Bytes())
|
|
||||||
} else {
|
|
||||||
if isLinkData(node.Parent.Data) {
|
|
||||||
escLink(w, node.Literal)
|
|
||||||
} else {
|
|
||||||
escapeHTML(w, node.Literal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *SoftbreakData:
|
case *SoftbreakData:
|
||||||
r.cr(w)
|
r.cr(w)
|
||||||
// TODO: make it configurable via out(renderer.softbreak)
|
// TODO: make it configurable via out(renderer.softbreak)
|
||||||
case *HardbreakData:
|
case *HardbreakData:
|
||||||
if r.params.Flags&UseXHTML == 0 {
|
r.hardBreak(w, node, nodeData)
|
||||||
r.out(w, brTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, brXHTMLTag)
|
|
||||||
}
|
|
||||||
r.cr(w)
|
|
||||||
case *EmphData:
|
case *EmphData:
|
||||||
if entering {
|
r.openOrCloseTag(w, entering, "<em>", "</em>")
|
||||||
r.out(w, emTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, emCloseTag)
|
|
||||||
}
|
|
||||||
case *StrongData:
|
case *StrongData:
|
||||||
if entering {
|
r.openOrCloseTag(w, entering, "<strong>", "</strong>")
|
||||||
r.out(w, strongTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, strongCloseTag)
|
|
||||||
}
|
|
||||||
case *DelData:
|
case *DelData:
|
||||||
if entering {
|
r.openOrCloseTag(w, entering, "<del>", "</del>")
|
||||||
r.out(w, delTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, delCloseTag)
|
|
||||||
}
|
|
||||||
case *HTMLSpanData:
|
case *HTMLSpanData:
|
||||||
if r.params.Flags&SkipHTML != 0 {
|
r.span(w, node, nodeData)
|
||||||
break
|
|
||||||
}
|
|
||||||
r.out(w, node.Literal)
|
|
||||||
case *LinkData:
|
case *LinkData:
|
||||||
// mark it but don't link it if it is not a safe link: no smartypants
|
r.link(w, node, nodeData, entering)
|
||||||
dest := nodeData.Destination
|
|
||||||
if needSkipLink(r.params.Flags, dest) {
|
|
||||||
if entering {
|
|
||||||
r.out(w, ttTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, ttCloseTag)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if entering {
|
|
||||||
dest = r.addAbsPrefix(dest)
|
|
||||||
var hrefBuf bytes.Buffer
|
|
||||||
hrefBuf.WriteString("href=\"")
|
|
||||||
escLink(&hrefBuf, dest)
|
|
||||||
hrefBuf.WriteByte('"')
|
|
||||||
attrs = append(attrs, hrefBuf.String())
|
|
||||||
if nodeData.NoteID != 0 {
|
|
||||||
r.out(w, footnoteRef(r.params.FootnoteAnchorPrefix, nodeData))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
attrs = appendLinkAttrs(attrs, r.params.Flags, dest)
|
|
||||||
if len(nodeData.Title) > 0 {
|
|
||||||
var titleBuff bytes.Buffer
|
|
||||||
titleBuff.WriteString("title=\"")
|
|
||||||
escapeHTML(&titleBuff, nodeData.Title)
|
|
||||||
titleBuff.WriteByte('"')
|
|
||||||
attrs = append(attrs, titleBuff.String())
|
|
||||||
}
|
|
||||||
r.tag(w, aTag, attrs)
|
|
||||||
} else {
|
|
||||||
if nodeData.NoteID != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.out(w, aCloseTag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ImageData:
|
case *ImageData:
|
||||||
if r.params.Flags&SkipImages != 0 {
|
if r.params.Flags&SkipImages != 0 {
|
||||||
return SkipChildren
|
return SkipChildren
|
||||||
}
|
}
|
||||||
if entering {
|
r.image(w, node, nodeData, entering)
|
||||||
dest := nodeData.Destination
|
|
||||||
dest = r.addAbsPrefix(dest)
|
|
||||||
if r.disableTags == 0 {
|
|
||||||
//if options.safe && potentiallyUnsafe(dest) {
|
|
||||||
//out(w, `<img src="" alt="`)
|
|
||||||
//} else {
|
|
||||||
r.out(w, []byte(`<img src="`))
|
|
||||||
escLink(w, dest)
|
|
||||||
r.out(w, []byte(`" alt="`))
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
r.disableTags++
|
|
||||||
} else {
|
|
||||||
r.disableTags--
|
|
||||||
if r.disableTags == 0 {
|
|
||||||
if nodeData.Title != nil {
|
|
||||||
r.out(w, []byte(`" title="`))
|
|
||||||
escapeHTML(w, nodeData.Title)
|
|
||||||
}
|
|
||||||
r.out(w, []byte(`" />`))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *CodeData:
|
case *CodeData:
|
||||||
r.out(w, codeTag)
|
r.code(w, node, nodeData)
|
||||||
escapeHTML(w, node.Literal)
|
|
||||||
r.out(w, codeCloseTag)
|
|
||||||
case *DocumentData:
|
case *DocumentData:
|
||||||
|
// do nothing
|
||||||
case *ParagraphData:
|
case *ParagraphData:
|
||||||
if skipParagraphTags(node) {
|
r.paragraph(w, node, nodeData, entering)
|
||||||
break
|
|
||||||
}
|
|
||||||
if entering {
|
|
||||||
// TODO: untangle this clusterfuck about when the newlines need
|
|
||||||
// to be added and when not.
|
|
||||||
if node.Prev != nil {
|
|
||||||
switch node.Prev.Data.(type) {
|
|
||||||
case *HTMLBlockData, *ListData, *ParagraphData, *HeadingData, *CodeBlockData, *BlockQuoteData, *HorizontalRuleData:
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBlockQuoteData(node.Parent.Data) && node.Prev == nil {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
r.out(w, pTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, pCloseTag)
|
|
||||||
if !(isItemData(node.Parent.Data) && node.Next == nil) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *BlockQuoteData:
|
case *BlockQuoteData:
|
||||||
if entering {
|
r.crOpenOrCloseTag(w, entering, "<blockquote>", "</blockquote>")
|
||||||
r.cr(w)
|
|
||||||
r.out(w, blockquoteTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, blockquoteCloseTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *HTMLBlockData:
|
case *HTMLBlockData:
|
||||||
if r.params.Flags&SkipHTML != 0 {
|
r.htmlBlock(w, node, nodeData)
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cr(w)
|
|
||||||
r.out(w, node.Literal)
|
|
||||||
r.cr(w)
|
|
||||||
case *HeadingData:
|
case *HeadingData:
|
||||||
openTag, closeTag := headingTagsFromLevel(nodeData.Level)
|
r.heading(w, node, nodeData, entering)
|
||||||
if entering {
|
|
||||||
if nodeData.IsTitleblock {
|
|
||||||
attrs = append(attrs, `class="title"`)
|
|
||||||
}
|
|
||||||
if nodeData.HeadingID != "" {
|
|
||||||
id := r.ensureUniqueHeadingID(nodeData.HeadingID)
|
|
||||||
if r.params.HeadingIDPrefix != "" {
|
|
||||||
id = r.params.HeadingIDPrefix + id
|
|
||||||
}
|
|
||||||
if r.params.HeadingIDSuffix != "" {
|
|
||||||
id = id + r.params.HeadingIDSuffix
|
|
||||||
}
|
|
||||||
attrs = append(attrs, fmt.Sprintf(`id="%s"`, id))
|
|
||||||
}
|
|
||||||
r.cr(w)
|
|
||||||
r.tag(w, openTag, attrs)
|
|
||||||
} else {
|
|
||||||
r.out(w, closeTag)
|
|
||||||
if !(isItemData(node.Parent.Data) && node.Next == nil) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *HorizontalRuleData:
|
case *HorizontalRuleData:
|
||||||
r.cr(w)
|
r.horizontalRule(w)
|
||||||
r.outHRTag(w)
|
|
||||||
r.cr(w)
|
|
||||||
case *ListData:
|
case *ListData:
|
||||||
openTag := ulTag
|
r.list(w, node, nodeData, entering)
|
||||||
closeTag := ulCloseTag
|
|
||||||
if nodeData.ListFlags&ListTypeOrdered != 0 {
|
|
||||||
openTag = olTag
|
|
||||||
closeTag = olCloseTag
|
|
||||||
}
|
|
||||||
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
|
||||||
openTag = dlTag
|
|
||||||
closeTag = dlCloseTag
|
|
||||||
}
|
|
||||||
if entering {
|
|
||||||
if nodeData.IsFootnotesList {
|
|
||||||
r.out(w, footnotesDivBytes)
|
|
||||||
r.outHRTag(w)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
r.cr(w)
|
|
||||||
if isItemData(node.Parent.Data) {
|
|
||||||
grand := node.Parent.Parent
|
|
||||||
if isListTight(grand.Data) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.tag(w, openTag[:len(openTag)-1], attrs)
|
|
||||||
r.cr(w)
|
|
||||||
} else {
|
|
||||||
r.out(w, closeTag)
|
|
||||||
//cr(w)
|
|
||||||
//if node.parent.Type != Item {
|
|
||||||
// cr(w)
|
|
||||||
//}
|
|
||||||
if isItemData(node.Parent.Data) && node.Next != nil {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
if isDocumentData(node.Parent.Data) || isBlockQuoteData(node.Parent.Data) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
if nodeData.IsFootnotesList {
|
|
||||||
r.out(w, footnotesCloseDivBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ItemData:
|
case *ItemData:
|
||||||
openTag := liTag
|
r.item(w, node, nodeData, entering)
|
||||||
closeTag := liCloseTag
|
|
||||||
if nodeData.ListFlags&ListTypeDefinition != 0 {
|
|
||||||
openTag = ddTag
|
|
||||||
closeTag = ddCloseTag
|
|
||||||
}
|
|
||||||
if nodeData.ListFlags&ListTypeTerm != 0 {
|
|
||||||
openTag = dtTag
|
|
||||||
closeTag = dtCloseTag
|
|
||||||
}
|
|
||||||
if entering {
|
|
||||||
if itemOpenCR(node) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
if nodeData.RefLink != nil {
|
|
||||||
slug := slugify(nodeData.RefLink)
|
|
||||||
r.out(w, footnoteItem(r.params.FootnoteAnchorPrefix, slug))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.out(w, openTag)
|
|
||||||
} else {
|
|
||||||
if nodeData.RefLink != nil {
|
|
||||||
slug := slugify(nodeData.RefLink)
|
|
||||||
if r.params.Flags&FootnoteReturnLinks != 0 {
|
|
||||||
r.out(w, footnoteReturnLink(r.params.FootnoteAnchorPrefix, r.params.FootnoteReturnLinkContents, slug))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.out(w, closeTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *CodeBlockData:
|
case *CodeBlockData:
|
||||||
attrs = appendLanguageAttr(attrs, nodeData.Info)
|
r.codeBlock(w, node, nodeData)
|
||||||
r.cr(w)
|
|
||||||
r.out(w, preTag)
|
|
||||||
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
|
||||||
escapeHTML(w, node.Literal)
|
|
||||||
r.out(w, codeCloseTag)
|
|
||||||
r.out(w, preCloseTag)
|
|
||||||
if !isItemData(node.Parent.Data) {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *TableData:
|
case *TableData:
|
||||||
if entering {
|
r.crOpenOrCloseTag(w, entering, "<table>", "</table>")
|
||||||
r.cr(w)
|
|
||||||
r.out(w, tableTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, tableCloseTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *TableCellData:
|
case *TableCellData:
|
||||||
openTag := tdTag
|
r.tableCell(w, node, nodeData, entering)
|
||||||
closeTag := tdCloseTag
|
|
||||||
if nodeData.IsHeader {
|
|
||||||
openTag = thTag
|
|
||||||
closeTag = thCloseTag
|
|
||||||
}
|
|
||||||
if entering {
|
|
||||||
align := cellAlignment(nodeData.Align)
|
|
||||||
if align != "" {
|
|
||||||
attrs = append(attrs, fmt.Sprintf(`align="%s"`, align))
|
|
||||||
}
|
|
||||||
if node.Prev == nil {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
r.tag(w, openTag, attrs)
|
|
||||||
} else {
|
|
||||||
r.out(w, closeTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *TableHeadData:
|
case *TableHeadData:
|
||||||
if entering {
|
r.crOpenOrCloseTag(w, entering, "<thead>", "</thead>")
|
||||||
r.cr(w)
|
|
||||||
r.out(w, theadTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, theadCloseTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *TableBodyData:
|
case *TableBodyData:
|
||||||
if entering {
|
r.tableBody(w, node, nodeData, entering)
|
||||||
r.cr(w)
|
|
||||||
r.out(w, tbodyTag)
|
|
||||||
// XXX: this is to adhere to a rather silly test. Should fix test.
|
|
||||||
if node.FirstChild == nil {
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r.out(w, tbodyCloseTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
case *TableRowData:
|
case *TableRowData:
|
||||||
if entering {
|
r.crOpenOrCloseTag(w, entering, "<tr>", "</tr>")
|
||||||
r.cr(w)
|
|
||||||
r.out(w, trTag)
|
|
||||||
} else {
|
|
||||||
r.out(w, trCloseTag)
|
|
||||||
r.cr(w)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
//panic("Unknown node type " + node.Type.String())
|
//panic("Unknown node type " + node.Type.String())
|
||||||
panic(fmt.Sprintf("Unknown node type %T", node.Data))
|
panic(fmt.Sprintf("Unknown node type %T", node.Data))
|
||||||
|
@ -886,7 +937,7 @@ func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) {
|
||||||
tocLevel := 0
|
tocLevel := 0
|
||||||
headingCount := 0
|
headingCount := 0
|
||||||
|
|
||||||
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
ast.WalkFunc(func(node *Node, entering bool) WalkStatus {
|
||||||
if nodeData, ok := node.Data.(*HeadingData); ok && !nodeData.IsTitleblock {
|
if nodeData, ok := node.Data.(*HeadingData); ok && !nodeData.IsTitleblock {
|
||||||
inHeading = entering
|
inHeading = entering
|
||||||
if entering {
|
if entering {
|
||||||
|
|
20
node.go
20
node.go
|
@ -300,14 +300,24 @@ const (
|
||||||
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||||||
// Called twice for every node: once with entering=true when the branch is
|
// Called twice for every node: once with entering=true when the branch is
|
||||||
// first visited, then with entering=false after all the children are done.
|
// first visited, then with entering=false after all the children are done.
|
||||||
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
type NodeVisitor interface {
|
||||||
|
Visit(node *Node, entering bool) WalkStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeVisitorFunc casts a function to match NodeVisitor interface
|
||||||
|
type NodeVisitorFunc func(node *Node, entering bool) WalkStatus
|
||||||
|
|
||||||
|
// Visit calls visitor function
|
||||||
|
func (f NodeVisitorFunc) Visit(node *Node, entering bool) WalkStatus {
|
||||||
|
return f(node, entering)
|
||||||
|
}
|
||||||
|
|
||||||
// Walk is a convenience method that instantiates a walker and starts a
|
// Walk is a convenience method that instantiates a walker and starts a
|
||||||
// traversal of subtree rooted at n.
|
// traversal of subtree rooted at n.
|
||||||
func (n *Node) Walk(visitor NodeVisitor) {
|
func (n *Node) Walk(visitor NodeVisitor) {
|
||||||
w := newNodeWalker(n)
|
w := newNodeWalker(n)
|
||||||
for w.current != nil {
|
for w.current != nil {
|
||||||
status := visitor(w.current, w.entering)
|
status := visitor.Visit(w.current, w.entering)
|
||||||
switch status {
|
switch status {
|
||||||
case GoToNext:
|
case GoToNext:
|
||||||
w.next()
|
w.next()
|
||||||
|
@ -320,6 +330,12 @@ func (n *Node) Walk(visitor NodeVisitor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WalkFunc is like Walk but accepts just a callback function
|
||||||
|
func (n *Node) WalkFunc(f NodeVisitorFunc) {
|
||||||
|
visitor := NodeVisitorFunc(f)
|
||||||
|
n.Walk(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
type nodeWalker struct {
|
type nodeWalker struct {
|
||||||
current *Node
|
current *Node
|
||||||
root *Node
|
root *Node
|
||||||
|
|
|
@ -238,7 +238,7 @@ func (p *Parser) Render(renderer Renderer) []byte {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
ast := p.Doc
|
ast := p.Doc
|
||||||
renderer.RenderHeader(&buf, ast)
|
renderer.RenderHeader(&buf, ast)
|
||||||
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
ast.WalkFunc(func(node *Node, entering bool) WalkStatus {
|
||||||
return renderer.RenderNode(&buf, node, entering)
|
return renderer.RenderNode(&buf, node, entering)
|
||||||
})
|
})
|
||||||
renderer.RenderFooter(&buf, ast)
|
renderer.RenderFooter(&buf, ast)
|
||||||
|
@ -257,7 +257,7 @@ func (p *Parser) Parse(input []byte) *Node {
|
||||||
p.finalize(p.tip)
|
p.finalize(p.tip)
|
||||||
}
|
}
|
||||||
// Walk the tree again and process inline markdown in each block
|
// Walk the tree again and process inline markdown in each block
|
||||||
p.Doc.Walk(func(node *Node, entering bool) WalkStatus {
|
p.Doc.WalkFunc(func(node *Node, entering bool) WalkStatus {
|
||||||
switch node.Data.(type) {
|
switch node.Data.(type) {
|
||||||
case *ParagraphData, *HeadingData, *TableCellData:
|
case *ParagraphData, *HeadingData, *TableCellData:
|
||||||
p.inline(node, node.content)
|
p.inline(node, node.content)
|
||||||
|
@ -302,7 +302,7 @@ func (p *Parser) parseRefsToAST() {
|
||||||
above := block.Parent
|
above := block.Parent
|
||||||
finalizeList(block, d)
|
finalizeList(block, d)
|
||||||
p.tip = above
|
p.tip = above
|
||||||
block.Walk(func(node *Node, entering bool) WalkStatus {
|
block.WalkFunc(func(node *Node, entering bool) WalkStatus {
|
||||||
switch node.Data.(type) {
|
switch node.Data.(type) {
|
||||||
case *ParagraphData, *HeadingData:
|
case *ParagraphData, *HeadingData:
|
||||||
p.inline(node, node.content)
|
p.inline(node, node.content)
|
||||||
|
|
2
todo.md
2
todo.md
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
[x] simplify oliPrefix()
|
[x] simplify oliPrefix()
|
||||||
|
|
||||||
|
[ ] rename item => listItem
|
||||||
|
|
||||||
[ ] speed: Node is probably too fat
|
[ ] speed: Node is probably too fat
|
||||||
|
|
||||||
[ ] speed: Node could be allocated from a slice and pointers could be replaces with int32 integers. Node would have to keep track of the allocator or we can change the api to pass both Parser (which is allocator of nodes) and Node (which would be a typedef for int)
|
[ ] speed: Node could be allocated from a slice and pointers could be replaces with int32 integers. Node would have to keep track of the allocator or we can change the api to pass both Parser (which is allocator of nodes) and Node (which would be a typedef for int)
|
||||||
|
|
Loading…
Reference in New Issue