Renderer is now an interface
@@ -43,7 +43,7 @@ //
// <div> // ... // </div> - if data[0] == '<' && parser.r.BlockHtml != nil { + if data[0] == '<' { if i := parser.blockHtml(out, data, true); i > 0 { data = data[i:] continue@@ -64,9 +64,7 @@ // ******
// or // ______ if parser.isHRule(data) { - if parser.r.HRule != nil { - parser.r.HRule(out, parser.r.Opaque) - } + parser.r.HRule(out) var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { }@@ -189,13 +187,11 @@ for end > 0 && (data[end-1] == ' ' || data[end-1] == '\t') {
end-- } if end > i { - if parser.r.Header != nil { - work := func() bool { - parser.parseInline(out, data[i:end]) - return true - } - parser.r.Header(out, work, level, parser.r.Opaque) + work := func() bool { + parser.parseInline(out, data[i:end]) + return true } + parser.r.Header(out, work, level) } return skip }@@ -261,8 +257,8 @@ }
if j > 0 { size := i + j - if doRender && parser.r.BlockHtml != nil { - parser.r.BlockHtml(out, data[:size], parser.r.Opaque) + if doRender { + parser.r.BlockHtml(out, data[:size]) } return size }@@ -283,8 +279,8 @@ i++
j = parser.isEmpty(data[i:]) if j > 0 { size := i + j - if doRender && parser.r.BlockHtml != nil { - parser.r.BlockHtml(out, data[:size], parser.r.Opaque) + if doRender { + parser.r.BlockHtml(out, data[:size]) } return size }@@ -329,8 +325,8 @@ return 0
} // the end of the block has been found - if doRender && parser.r.BlockHtml != nil { - parser.r.BlockHtml(out, data[:i], parser.r.Opaque) + if doRender { + parser.r.BlockHtml(out, data[:i]) } return i@@ -566,14 +562,12 @@ if work.Len() > 0 && work.Bytes()[work.Len()-1] != '\n' {
work.WriteByte('\n') } - if parser.r.BlockCode != nil { - syntax := "" - if lang != nil { - syntax = *lang - } - - parser.r.BlockCode(out, work.Bytes(), syntax, parser.r.Opaque) + syntax := "" + if lang != nil { + syntax = *lang } + + parser.r.BlockCode(out, work.Bytes(), syntax) return beg }@@ -604,9 +598,7 @@ parser.blockTableRow(&bodyWork, data[rowStart:i], columns, colData)
i++ } - if parser.r.Table != nil { - parser.r.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData, parser.r.Opaque) - } + parser.r.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData) return i }@@ -725,31 +717,25 @@
var cellWork bytes.Buffer parser.parseInline(&cellWork, data[cellStart:cellEnd+1]) - if parser.r.TableCell != nil { - cdata := 0 - if col < len(colData) { - cdata = colData[col] - } - parser.r.TableCell(&rowWork, cellWork.Bytes(), cdata, parser.r.Opaque) + cdata := 0 + if col < len(colData) { + cdata = colData[col] } + parser.r.TableCell(&rowWork, cellWork.Bytes(), cdata) i++ } for ; col < columns; col++ { emptyCell := []byte{} - if parser.r.TableCell != nil { - cdata := 0 - if col < len(colData) { - cdata = colData[col] - } - parser.r.TableCell(&rowWork, emptyCell, cdata, parser.r.Opaque) + cdata := 0 + if col < len(colData) { + cdata = colData[col] } + parser.r.TableCell(&rowWork, emptyCell, cdata) } - if parser.r.TableRow != nil { - parser.r.TableRow(out, rowWork.Bytes(), parser.r.Opaque) - } + parser.r.TableRow(out, rowWork.Bytes()) } // returns blockquote prefix length@@ -794,9 +780,7 @@ beg = end
} parser.parseBlock(&block, work.Bytes()) - if parser.r.BlockQuote != nil { - parser.r.BlockQuote(out, block.Bytes(), parser.r.Opaque) - } + parser.r.BlockQuote(out, block.Bytes()) return end }@@ -851,9 +835,7 @@ }
work.WriteByte('\n') - if parser.r.BlockCode != nil { - parser.r.BlockCode(out, work.Bytes(), "", parser.r.Opaque) - } + parser.r.BlockCode(out, work.Bytes(), "") return beg }@@ -915,9 +897,7 @@ }
return true } - if parser.r.List != nil { - parser.r.List(out, work, flags, parser.r.Opaque) - } + parser.r.List(out, work, flags) return i }@@ -1061,9 +1041,7 @@ }
} // render li itself - if parser.r.ListItem != nil { - parser.r.ListItem(out, inter.Bytes(), *flags, parser.r.Opaque) - } + parser.r.ListItem(out, inter.Bytes(), *flags) return beg }@@ -1081,7 +1059,7 @@ end := len(data)
for end > beg && isspace(data[end-1]) { end-- } - if end == beg || parser.r.Paragraph == nil { + if end == beg { return }@@ -1089,7 +1067,7 @@ work := func() bool {
parser.parseInline(out, data[beg:end]) return true } - parser.r.Paragraph(out, work, parser.r.Opaque) + parser.r.Paragraph(out, work) } func (parser *Parser) blockParagraph(out *bytes.Buffer, data []byte) int {@@ -1112,7 +1090,7 @@ return i + n
} // an underline under some text marks a header, so our paragraph ended on prev line - if i > 0 && parser.r.Header != nil { + if i > 0 { if level := parser.isUnderlinedHeader(current); level > 0 { // render the paragraph parser.renderParagraph(out, data[:prev])@@ -1134,7 +1112,7 @@ p.parseInline(o, d)
return true } }(out, parser, data[prev:eol]) - parser.r.Header(out, work, level, parser.r.Opaque) + parser.r.Header(out, work, level) // find the end of the underline for ; i < len(data) && data[i] != '\n'; i++ {@@ -1145,7 +1123,7 @@ }
// if the next line starts a block of HTML, then the paragraph ends here if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { - if data[i] == '<' && parser.r.BlockHtml != nil && parser.blockHtml(out, current, false) > 0 { + if data[i] == '<' && parser.blockHtml(out, current, false) > 0 { // rewind to before the HTML block parser.renderParagraph(out, data[:i]) return i
@@ -121,7 +121,7 @@ extensions |= blackfriday.EXTENSION_AUTOLINK
extensions |= blackfriday.EXTENSION_STRIKETHROUGH extensions |= blackfriday.EXTENSION_SPACE_HEADERS - var renderer *blackfriday.Renderer + var renderer blackfriday.Renderer if latex { // render the data into LaTeX renderer = blackfriday.LatexRenderer(0)
@@ -37,7 +37,7 @@ HTML_SMARTYPANTS_FRACTIONS
HTML_SMARTYPANTS_LATEX_DASHES ) -type htmlOptions struct { +type Html struct { flags int // HTML_* options closeTag string // how to end singleton tags: either " />\n" or ">\n" title string // document title@@ -52,48 +52,19 @@
smartypants *SmartypantsRenderer } -var xhtmlClose = " />\n" -var htmlClose = ">\n" +const ( + xhtmlClose = " />\n" + htmlClose = ">\n" +) -func HtmlRenderer(flags int, title string, css string) *Renderer { +func HtmlRenderer(flags int, title string, css string) Renderer { // configure the rendering engine - r := new(Renderer) - - r.BlockCode = htmlBlockCode - r.BlockQuote = htmlBlockQuote - r.BlockHtml = htmlBlockHtml - r.Header = htmlHeader - r.HRule = htmlHRule - r.List = htmlList - r.ListItem = htmlListItem - r.Paragraph = htmlParagraph - r.Table = htmlTable - r.TableRow = htmlTableRow - r.TableCell = htmlTableCell - - r.AutoLink = htmlAutoLink - r.CodeSpan = htmlCodeSpan - r.DoubleEmphasis = htmlDoubleEmphasis - r.Emphasis = htmlEmphasis - r.Image = htmlImage - r.LineBreak = htmlLineBreak - r.Link = htmlLink - r.RawHtmlTag = htmlRawTag - r.TripleEmphasis = htmlTripleEmphasis - r.StrikeThrough = htmlStrikeThrough - - r.Entity = htmlEntity - r.NormalText = htmlNormalText - - r.DocumentHeader = htmlDocumentHeader - r.DocumentFooter = htmlDocumentFooter - closeTag := htmlClose if flags&HTML_USE_XHTML != 0 { closeTag = xhtmlClose } - r.Opaque = &htmlOptions{ + return &Html{ flags: flags, closeTag: closeTag, title: title,@@ -105,7 +76,6 @@ toc: new(bytes.Buffer),
smartypants: Smartypants(flags), } - return r } func attrEscape(out *bytes.Buffer, src []byte) {@@ -154,8 +124,7 @@ out.Write(src[org:])
} } -func htmlHeader(out *bytes.Buffer, text func() bool, level int, opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) Header(out *bytes.Buffer, text func() bool, level int) { marker := out.Len() if marker > 0 {@@ -177,14 +146,13 @@ }
// are we building a table of contents? if options.flags&HTML_TOC != 0 { - htmlTocHeader(out.Bytes()[tocMarker:], level, opaque) + options.TocHeader(out.Bytes()[tocMarker:], level) } out.WriteString(fmt.Sprintf("</h%d>\n", level)) } -func htmlBlockHtml(out *bytes.Buffer, text []byte, opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) BlockHtml(out *bytes.Buffer, text []byte) { if options.flags&HTML_SKIP_HTML != 0 { return }@@ -207,9 +175,7 @@ out.Write(text[org:sz])
out.WriteByte('\n') } -func htmlHRule(out *bytes.Buffer, opaque interface{}) { - options := opaque.(*htmlOptions) - +func (options *Html) HRule(out *bytes.Buffer) { if out.Len() > 0 { out.WriteByte('\n') }@@ -217,16 +183,15 @@ out.WriteString("<hr")
out.WriteString(options.closeTag) } -func htmlBlockCode(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) BlockCode(out *bytes.Buffer, text []byte, lang string) { if options.flags&HTML_GITHUB_BLOCKCODE != 0 { - htmlBlockCodeGithub(out, text, lang, opaque) + options.BlockCodeGithub(out, text, lang) } else { - htmlBlockCodeNormal(out, text, lang, opaque) + options.BlockCodeNormal(out, text, lang) } } -func htmlBlockCodeNormal(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { +func (options *Html) BlockCodeNormal(out *bytes.Buffer, text []byte, lang string) { if out.Len() > 0 { out.WriteByte('\n') }@@ -286,7 +251,7 @@ * Note that we only generate HTML for the first specifier.
* E.g. * ~~~~ {.python .numbered} => <pre lang="python"><code> */ -func htmlBlockCodeGithub(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { +func (options *Html) BlockCodeGithub(out *bytes.Buffer, text []byte, lang string) { if out.Len() > 0 { out.WriteByte('\n') }@@ -318,13 +283,13 @@ out.WriteString("</code></pre>\n")
} -func htmlBlockQuote(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Html) BlockQuote(out *bytes.Buffer, text []byte) { out.WriteString("<blockquote>\n") out.Write(text) out.WriteString("</blockquote>") } -func htmlTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) { +func (options *Html) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) { if out.Len() > 0 { out.WriteByte('\n') }@@ -335,7 +300,7 @@ out.Write(body)
out.WriteString("\n</tbody></table>") } -func htmlTableRow(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Html) TableRow(out *bytes.Buffer, text []byte) { if out.Len() > 0 { out.WriteByte('\n') }@@ -344,7 +309,7 @@ out.Write(text)
out.WriteString("\n</tr>") } -func htmlTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) { +func (options *Html) TableCell(out *bytes.Buffer, text []byte, align int) { if out.Len() > 0 { out.WriteByte('\n') }@@ -363,7 +328,7 @@ out.Write(text)
out.WriteString("</td>") } -func htmlList(out *bytes.Buffer, text func() bool, flags int, opaque interface{}) { +func (options *Html) List(out *bytes.Buffer, text func() bool, flags int) { marker := out.Len() if marker > 0 {@@ -385,7 +350,7 @@ out.WriteString("</ul>\n")
} } -func htmlListItem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { +func (options *Html) ListItem(out *bytes.Buffer, text []byte, flags int) { out.WriteString("<li>") size := len(text) for size > 0 && text[size-1] == '\n' {@@ -395,7 +360,7 @@ out.Write(text[:size])
out.WriteString("</li>\n") } -func htmlParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) { +func (options *Html) Paragraph(out *bytes.Buffer, text func() bool) { marker := out.Len() if marker > 0 { out.WriteByte('\n')@@ -409,9 +374,7 @@ }
out.WriteString("</p>\n") } -func htmlAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) bool { - options := opaque.(*htmlOptions) - +func (options *Html) AutoLink(out *bytes.Buffer, link []byte, kind int) bool { if len(link) == 0 { return false }@@ -445,14 +408,14 @@
return true } -func htmlCodeSpan(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) CodeSpan(out *bytes.Buffer, text []byte) bool { out.WriteString("<code>") attrEscape(out, text) out.WriteString("</code>") return true } -func htmlDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) DoubleEmphasis(out *bytes.Buffer, text []byte) bool { if len(text) == 0 { return false }@@ -462,7 +425,7 @@ out.WriteString("</strong>")
return true } -func htmlEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) Emphasis(out *bytes.Buffer, text []byte) bool { if len(text) == 0 { return false }@@ -472,8 +435,7 @@ out.WriteString("</em>")
return true } -func htmlImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) bool { - options := opaque.(*htmlOptions) +func (options *Html) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) bool { if options.flags&HTML_SKIP_IMAGES != 0 { return false }@@ -497,15 +459,13 @@ out.WriteString(options.closeTag)
return true } -func htmlLineBreak(out *bytes.Buffer, opaque interface{}) bool { - options := opaque.(*htmlOptions) +func (options *Html) LineBreak(out *bytes.Buffer) bool { out.WriteString("<br") out.WriteString(options.closeTag) return true } -func htmlLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) bool { - options := opaque.(*htmlOptions) +func (options *Html) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) bool { if options.flags&HTML_SKIP_LINKS != 0 { return false }@@ -526,8 +486,7 @@ out.WriteString("</a>")
return true } -func htmlRawTag(out *bytes.Buffer, text []byte, opaque interface{}) bool { - options := opaque.(*htmlOptions) +func (options *Html) RawHtmlTag(out *bytes.Buffer, text []byte) bool { if options.flags&HTML_SKIP_HTML != 0 { return true }@@ -544,7 +503,7 @@ out.Write(text)
return true } -func htmlTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) TripleEmphasis(out *bytes.Buffer, text []byte) bool { if len(text) == 0 { return false }@@ -554,7 +513,7 @@ out.WriteString("</em></strong>")
return true } -func htmlStrikeThrough(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) StrikeThrough(out *bytes.Buffer, text []byte) bool { if len(text) == 0 { return false }@@ -564,21 +523,48 @@ out.WriteString("</del>")
return true } -func htmlEntity(out *bytes.Buffer, entity []byte, opaque interface{}) { +func (options *Html) Entity(out *bytes.Buffer, entity []byte) { out.Write(entity) } -func htmlNormalText(out *bytes.Buffer, text []byte, opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) NormalText(out *bytes.Buffer, text []byte) { if options.flags&HTML_USE_SMARTYPANTS != 0 { - htmlSmartypants(out, text, opaque) + options.Smartypants(out, text) } else { attrEscape(out, text) } } -func htmlDocumentHeader(out *bytes.Buffer, opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) Smartypants(out *bytes.Buffer, text []byte) { + smrt := smartypantsData{false, false} + + // first do normal entity escaping + var escaped bytes.Buffer + attrEscape(&escaped, text) + text = escaped.Bytes() + + mark := 0 + for i := 0; i < len(text); i++ { + if action := options.smartypants[text[i]]; action != nil { + if i > mark { + out.Write(text[mark:i]) + } + + previousChar := byte(0) + if i > 0 { + previousChar = text[i-1] + } + i += action(out, &smrt, previousChar, text[i:]) + mark = i + 1 + } + } + + if mark < len(text) { + out.Write(text[mark:]) + } +} + +func (options *Html) DocumentHeader(out *bytes.Buffer) { if options.flags&HTML_COMPLETE_PAGE == 0 { return }@@ -596,7 +582,7 @@ out.WriteString("<html>\n")
} out.WriteString("<head>\n") out.WriteString(" <title>") - htmlNormalText(out, []byte(options.title), opaque) + options.NormalText(out, []byte(options.title)) out.WriteString("</title>\n") out.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v") out.WriteString(VERSION)@@ -619,12 +605,10 @@
options.tocMarker = out.Len() } -func htmlDocumentFooter(out *bytes.Buffer, opaque interface{}) { - options := opaque.(*htmlOptions) - +func (options *Html) DocumentFooter(out *bytes.Buffer) { // finalize and insert the table of contents if options.flags&HTML_TOC != 0 { - htmlTocFinalize(opaque) + options.TocFinalize() // now we have to insert the table of contents into the document var temp bytes.Buffer@@ -651,9 +635,7 @@ }
} -func htmlTocHeader(text []byte, level int, opaque interface{}) { - options := opaque.(*htmlOptions) - +func (options *Html) TocHeader(text []byte, level int) { for level > options.currentLevel { switch { case bytes.HasSuffix(options.toc.Bytes(), []byte("</li>\n")):@@ -685,8 +667,7 @@
options.toc.WriteString("</a></li>\n") } -func htmlTocFinalize(opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) TocFinalize() { for options.currentLevel > 1 { options.toc.WriteString("</ul></li>\n") options.currentLevel--
@@ -36,11 +36,7 @@ for end < len(data) && parser.inline[data[end]] == nil {
end++ } - if parser.r.NormalText != nil { - parser.r.NormalText(out, data[i:end], parser.r.Opaque) - } else { - out.Write(data[i:end]) - } + parser.r.NormalText(out, data[i:end]) if end >= len(data) { break@@ -143,10 +139,7 @@ fEnd--
} // render the code span - if parser.r.CodeSpan == nil { - return 0 - } - if !parser.r.CodeSpan(out, data[fBegin:fEnd], parser.r.Opaque) { + if !parser.r.CodeSpan(out, data[fBegin:fEnd]) { end = 0 }@@ -171,10 +164,7 @@ if parser.flags&EXTENSION_HARD_LINE_BREAK == 0 && end-eol < 2 {
return 0 } - if parser.r.LineBreak == nil { - return 0 - } - if parser.r.LineBreak(out, parser.r.Opaque) { + if parser.r.LineBreak(out) { return 1 } else { return 0@@ -197,11 +187,6 @@
i := 1 var title, link []byte textHasNl := false - - // check whether the correct renderer exists - if (isImg && parser.r.Image == nil) || (!isImg && parser.r.Link == nil) { - return 0 - } // look for the matching closing bracket for level := 1; level > 0 && i < len(data); i++ {@@ -439,9 +424,9 @@ if outSize > 0 && outBytes[outSize-1] == '!' {
out.Truncate(outSize - 1) } - ret = parser.r.Image(out, uLink, title, content.Bytes(), parser.r.Opaque) + ret = parser.r.Image(out, uLink, title, content.Bytes()) } else { - ret = parser.r.Link(out, uLink, title, content.Bytes(), parser.r.Opaque) + ret = parser.r.Link(out, uLink, title, content.Bytes()) } if ret {@@ -458,13 +443,12 @@ end := tagLength(data, &altype)
ret := false if end > 2 { - switch { - case parser.r.AutoLink != nil && altype != LINK_TYPE_NOT_AUTOLINK: + if altype != LINK_TYPE_NOT_AUTOLINK { var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) - ret = parser.r.AutoLink(out, uLink.Bytes(), altype, parser.r.Opaque) - case parser.r.RawHtmlTag != nil: - ret = parser.r.RawHtmlTag(out, data[:end], parser.r.Opaque) + ret = parser.r.AutoLink(out, uLink.Bytes(), altype) + } else { + ret = parser.r.RawHtmlTag(out, data[:end]) } }@@ -485,11 +469,7 @@ if bytes.IndexByte(escapeChars, data[1]) < 0 {
return 0 } - if parser.r.NormalText != nil { - parser.r.NormalText(out, data[1:2], parser.r.Opaque) - } else { - out.WriteByte(data[1]) - } + parser.r.NormalText(out, data[1:2]) } return 2@@ -537,11 +517,7 @@ } else {
return 0 // lone '&' } - if parser.r.Entity != nil { - parser.r.Entity(out, data[:end], parser.r.Opaque) - } else { - out.Write(data[:end]) - } + parser.r.Entity(out, data[:end]) return end }@@ -642,12 +618,10 @@ if out.Len() >= rewind {
out.Truncate(len(out.Bytes()) - rewind) } - if parser.r.AutoLink != nil { - var uLink bytes.Buffer - unescapeText(&uLink, data[:linkEnd]) + var uLink bytes.Buffer + unescapeText(&uLink, data[:linkEnd]) - parser.r.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL, parser.r.Opaque) - } + parser.r.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL) return linkEnd - rewind }@@ -863,10 +837,6 @@
func inlineHelperEmph1(out *bytes.Buffer, parser *Parser, data []byte, c byte) int { i := 0 - if parser.r.Emphasis == nil { - return 0 - } - // skip one symbol if coming from emph3 if len(data) > 1 && data[0] == c && data[1] == c { i = 1@@ -897,7 +867,7 @@ }
var work bytes.Buffer parser.parseInline(&work, data[:i]) - if parser.r.Emphasis(out, work.Bytes(), parser.r.Opaque) { + if parser.r.Emphasis(out, work.Bytes()) { return i + 1 } else { return 0@@ -909,15 +879,6 @@ return 0
} func inlineHelperEmph2(out *bytes.Buffer, parser *Parser, data []byte, c byte) int { - renderMethod := parser.r.DoubleEmphasis - if c == '~' { - renderMethod = parser.r.StrikeThrough - } - - if renderMethod == nil { - return 0 - } - i := 0 for i < len(data) {@@ -930,7 +891,15 @@
if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { var work bytes.Buffer parser.parseInline(&work, data[:i]) - if renderMethod(out, work.Bytes(), parser.r.Opaque) { + success := false + + // pick the right renderer + if c == '~' { + success = parser.r.StrikeThrough(out, work.Bytes()) + } else { + success = parser.r.DoubleEmphasis(out, work.Bytes()) + } + if success { return i + 2 } else { return 0@@ -959,12 +928,12 @@ continue
} switch { - case (i+2 < len(data) && data[i+1] == c && data[i+2] == c && parser.r.TripleEmphasis != nil): + case i+2 < len(data) && data[i+1] == c && data[i+2] == c: // triple symbol found var work bytes.Buffer parser.parseInline(&work, data[:i]) - if parser.r.TripleEmphasis(out, work.Bytes(), parser.r.Opaque) { + if parser.r.TripleEmphasis(out, work.Bytes()) { return i + 3 } else { return 0
@@ -19,47 +19,16 @@ import (
"bytes" ) -func LatexRenderer(flags int) *Renderer { - // configure the rendering engine - r := new(Renderer) +type Latex struct { - // block-level rendering - r.BlockCode = latexBlockCode - r.BlockQuote = latexBlockQuote - r.BlockHtml = latexBlockHtml - r.Header = latexHeader - r.HRule = latexHRule - r.List = latexList - r.ListItem = latexListItem - r.Paragraph = latexParagraph - r.Table = latexTable - r.TableRow = latexTableRow - r.TableCell = latexTableCell +} - // inline rendering - r.AutoLink = latexAutoLink - r.CodeSpan = latexCodeSpan - r.DoubleEmphasis = latexDoubleEmphasis - r.Emphasis = latexEmphasis - r.Image = latexImage - r.LineBreak = latexLineBreak - r.Link = latexLink - r.RawHtmlTag = latexRawHtmlTag - r.TripleEmphasis = latexTripleEmphasis - r.StrikeThrough = latexStrikeThrough - - r.Entity = latexEntity - r.NormalText = latexNormalText - - r.DocumentHeader = latexDocumentHeader - r.DocumentFooter = latexDocumentFooter - - r.Opaque = nil - return r +func LatexRenderer(flags int) Renderer { + return &Latex{} } // render code chunks using verbatim, or listings if we have a language -func latexBlockCode(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { +func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, lang string) { if lang == "" { out.WriteString("\n\\begin{verbatim}\n") } else {@@ -75,20 +44,20 @@ out.WriteString("\n\\end{lstlisting}\n")
} } -func latexBlockQuote(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Latex) BlockQuote(out *bytes.Buffer, text []byte) { out.WriteString("\n\\begin{quotation}\n") out.Write(text) out.WriteString("\n\\end{quotation}\n") } -func latexBlockHtml(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Latex) BlockHtml(out *bytes.Buffer, text []byte) { // a pretty lame thing to do... out.WriteString("\n\\begin{verbatim}\n") out.Write(text) out.WriteString("\n\\end{verbatim}\n") } -func latexHeader(out *bytes.Buffer, text func() bool, level int, opaque interface{}) { +func (options *Latex) Header(out *bytes.Buffer, text func() bool, level int) { marker := out.Len() switch level {@@ -112,11 +81,11 @@ }
out.WriteString("}\n") } -func latexHRule(out *bytes.Buffer, opaque interface{}) { +func (options *Latex) HRule(out *bytes.Buffer) { out.WriteString("\n\\HRule\n") } -func latexList(out *bytes.Buffer, text func() bool, flags int, opaque interface{}) { +func (options *Latex) List(out *bytes.Buffer, text func() bool, flags int) { marker := out.Len() if flags&LIST_TYPE_ORDERED != 0 { out.WriteString("\n\\begin{enumerate}\n")@@ -134,12 +103,12 @@ out.WriteString("\n\\end{itemize}\n")
} } -func latexListItem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { +func (options *Latex) ListItem(out *bytes.Buffer, text []byte, flags int) { out.WriteString("\n\\item ") out.Write(text) } -func latexParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) { +func (options *Latex) Paragraph(out *bytes.Buffer, text func() bool) { marker := out.Len() out.WriteString("\n") if !text() {@@ -149,7 +118,7 @@ }
out.WriteString("\n") } -func latexTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) { +func (options *Latex) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) { out.WriteString("\n\\begin{tabular}{") for _, elt := range columnData { switch elt {@@ -168,21 +137,21 @@ out.Write(body)
out.WriteString("\n\\end{tabular}\n") } -func latexTableRow(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Latex) TableRow(out *bytes.Buffer, text []byte) { if out.Len() > 0 { out.WriteString(" \\\\\n") } out.Write(text) } -func latexTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) { +func (options *Latex) TableCell(out *bytes.Buffer, text []byte, align int) { if out.Len() > 0 { out.WriteString(" & ") } out.Write(text) } -func latexAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) bool { +func (options *Latex) AutoLink(out *bytes.Buffer, link []byte, kind int) bool { out.WriteString("\\href{") if kind == LINK_TYPE_EMAIL { out.WriteString("mailto:")@@ -194,28 +163,28 @@ out.WriteString("}")
return true } -func latexCodeSpan(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Latex) CodeSpan(out *bytes.Buffer, text []byte) bool { out.WriteString("\\texttt{") escapeSpecialChars(out, text) out.WriteString("}") return true } -func latexDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Latex) DoubleEmphasis(out *bytes.Buffer, text []byte) bool { out.WriteString("\\textbf{") out.Write(text) out.WriteString("}") return true } -func latexEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Latex) Emphasis(out *bytes.Buffer, text []byte) bool { out.WriteString("\\textit{") out.Write(text) out.WriteString("}") return true } -func latexImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) bool { +func (options *Latex) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) bool { if bytes.HasPrefix(link, []byte("http://")) || bytes.HasPrefix(link, []byte("https://")) { // treat it like a link out.WriteString("\\href{")@@ -231,12 +200,12 @@ }
return true } -func latexLineBreak(out *bytes.Buffer, opaque interface{}) bool { +func (options *Latex) LineBreak(out *bytes.Buffer) bool { out.WriteString(" \\\\\n") return true } -func latexLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) bool { +func (options *Latex) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) bool { out.WriteString("\\href{") out.Write(link) out.WriteString("}{")@@ -245,18 +214,18 @@ out.WriteString("}")
return true } -func latexRawHtmlTag(out *bytes.Buffer, tag []byte, opaque interface{}) bool { +func (options *Latex) RawHtmlTag(out *bytes.Buffer, tag []byte) bool { return true } -func latexTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Latex) TripleEmphasis(out *bytes.Buffer, text []byte) bool { out.WriteString("\\textbf{\\textit{") out.Write(text) out.WriteString("}}") return true } -func latexStrikeThrough(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Latex) StrikeThrough(out *bytes.Buffer, text []byte) bool { out.WriteString("\\sout{") out.Write(text) out.WriteString("}")@@ -293,17 +262,17 @@ out.WriteByte(text[i])
} } -func latexEntity(out *bytes.Buffer, entity []byte, opaque interface{}) { +func (options *Latex) Entity(out *bytes.Buffer, entity []byte) { // TODO: convert this into a unicode character or something out.Write(entity) } -func latexNormalText(out *bytes.Buffer, text []byte, opaque interface{}) { +func (options *Latex) NormalText(out *bytes.Buffer, text []byte) { escapeSpecialChars(out, text) } // header and footer -func latexDocumentHeader(out *bytes.Buffer, opaque interface{}) { +func (options *Latex) DocumentHeader(out *bytes.Buffer) { out.WriteString("\\documentclass{article}\n") out.WriteString("\n") out.WriteString("\\usepackage{graphicx}\n")@@ -332,6 +301,6 @@ out.WriteString("\n")
out.WriteString("\\begin{document}\n") } -func latexDocumentFooter(out *bytes.Buffer, opaque interface{}) { +func (options *Latex) DocumentFooter(out *bytes.Buffer) { out.WriteString("\n\\end{document}\n") }
@@ -105,48 +105,45 @@ // except where noted.
// // This is mostly of interest if you are implementing a new rendering format. // Most users will use the convenience functions to fill in this structure. -type Renderer struct { - // block-level callbacks---nil skips the block - BlockCode func(out *bytes.Buffer, text []byte, lang string, opaque interface{}) - BlockQuote func(out *bytes.Buffer, text []byte, opaque interface{}) - BlockHtml func(out *bytes.Buffer, text []byte, opaque interface{}) - Header func(out *bytes.Buffer, text func() bool, level int, opaque interface{}) - HRule func(out *bytes.Buffer, opaque interface{}) - List func(out *bytes.Buffer, text func() bool, flags int, opaque interface{}) - ListItem func(out *bytes.Buffer, text []byte, flags int, opaque interface{}) - Paragraph func(out *bytes.Buffer, text func() bool, opaque interface{}) - Table func(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) - TableRow func(out *bytes.Buffer, text []byte, opaque interface{}) - TableCell func(out *bytes.Buffer, text []byte, flags int, opaque interface{}) +type Renderer interface { + // block-level callbacks + BlockCode(out *bytes.Buffer, text []byte, lang string) + BlockQuote(out *bytes.Buffer, text []byte) + BlockHtml(out *bytes.Buffer, text []byte) + Header(out *bytes.Buffer, text func() bool, level int) + HRule(out *bytes.Buffer) + List(out *bytes.Buffer, text func() bool, flags int) + ListItem(out *bytes.Buffer, text []byte, flags int) + Paragraph(out *bytes.Buffer, text func() bool) + Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) + TableRow(out *bytes.Buffer, text []byte) + TableCell(out *bytes.Buffer, text []byte, flags int) - // Span-level callbacks---nil or return false prints the span verbatim - AutoLink func(out *bytes.Buffer, link []byte, kind int, opaque interface{}) bool - CodeSpan func(out *bytes.Buffer, text []byte, opaque interface{}) bool - DoubleEmphasis func(out *bytes.Buffer, text []byte, opaque interface{}) bool - Emphasis func(out *bytes.Buffer, text []byte, opaque interface{}) bool - Image func(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) bool - LineBreak func(out *bytes.Buffer, opaque interface{}) bool - Link func(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) bool - RawHtmlTag func(out *bytes.Buffer, tag []byte, opaque interface{}) bool - TripleEmphasis func(out *bytes.Buffer, text []byte, opaque interface{}) bool - StrikeThrough func(out *bytes.Buffer, text []byte, opaque interface{}) bool + // Span-level callbacks---return false prints the span verbatim + AutoLink(out *bytes.Buffer, link []byte, kind int) bool + CodeSpan(out *bytes.Buffer, text []byte) bool + DoubleEmphasis(out *bytes.Buffer, text []byte) bool + Emphasis(out *bytes.Buffer, text []byte) bool + Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) bool + LineBreak(out *bytes.Buffer) bool + Link(out *bytes.Buffer, link []byte, title []byte, content []byte) bool + RawHtmlTag(out *bytes.Buffer, tag []byte) bool + TripleEmphasis(out *bytes.Buffer, text []byte) bool + StrikeThrough(out *bytes.Buffer, text []byte) bool - // Low-level callbacks---nil copies input directly into the output - Entity func(out *bytes.Buffer, entity []byte, opaque interface{}) - NormalText func(out *bytes.Buffer, text []byte, opaque interface{}) + // Low-level callbacks + Entity(out *bytes.Buffer, entity []byte) + NormalText(out *bytes.Buffer, text []byte) // Header and footer - DocumentHeader func(out *bytes.Buffer, opaque interface{}) - DocumentFooter func(out *bytes.Buffer, opaque interface{}) - - // User data---passed back to every callback - Opaque interface{} + DocumentHeader(out *bytes.Buffer) + DocumentFooter(out *bytes.Buffer) } type inlineParser func(out *bytes.Buffer, parser *Parser, data []byte, offset int) int type Parser struct { - r *Renderer + r Renderer refs map[string]*reference inline [256]inlineParser flags int@@ -199,7 +196,7 @@
// Parse and render a block of markdown-encoded text. // The renderer is used to format the output, and extensions dictates which // non-standard extensions are enabled. -func Markdown(input []byte, renderer *Renderer, extensions int) []byte { +func Markdown(input []byte, renderer Renderer, extensions int) []byte { // no point in parsing if we can't render if renderer == nil { return nil@@ -214,22 +211,14 @@ parser.maxNesting = 16
parser.insideLink = false // register inline parsers - if parser.r.Emphasis != nil || parser.r.DoubleEmphasis != nil || parser.r.TripleEmphasis != nil { - parser.inline['*'] = inlineEmphasis - parser.inline['_'] = inlineEmphasis - if extensions&EXTENSION_STRIKETHROUGH != 0 { - parser.inline['~'] = inlineEmphasis - } - } - if parser.r.CodeSpan != nil { - parser.inline['`'] = inlineCodeSpan + parser.inline['*'] = inlineEmphasis + parser.inline['_'] = inlineEmphasis + if extensions&EXTENSION_STRIKETHROUGH != 0 { + parser.inline['~'] = inlineEmphasis } - if parser.r.LineBreak != nil { - parser.inline['\n'] = inlineLineBreak - } - if parser.r.Image != nil || parser.r.Link != nil { - parser.inline['['] = inlineLink - } + parser.inline['`'] = inlineCodeSpan + parser.inline['\n'] = inlineLineBreak + parser.inline['['] = inlineLink parser.inline['<'] = inlineLAngle parser.inline['\\'] = inlineEscape parser.inline['&'] = inlineEntity@@ -291,15 +280,10 @@
// second pass: actual rendering func secondPass(parser *Parser, input []byte) []byte { var output bytes.Buffer - if parser.r.DocumentHeader != nil { - parser.r.DocumentHeader(&output, parser.r.Opaque) - } + parser.r.DocumentHeader(&output) parser.parseBlock(&output, input) - - if parser.r.DocumentFooter != nil { - parser.r.DocumentFooter(&output, parser.r.Opaque) - } + parser.r.DocumentFooter(&output) if parser.nesting != 0 { panic("Nesting level did not end at zero")
@@ -369,33 +369,3 @@ r['<'] = smartLtag
r['`'] = smartBacktick return r } - -func htmlSmartypants(out *bytes.Buffer, text []byte, opaque interface{}) { - options := opaque.(*htmlOptions) - smrt := smartypantsData{false, false} - - // first do normal entity escaping - var escaped bytes.Buffer - attrEscape(&escaped, text) - text = escaped.Bytes() - - mark := 0 - for i := 0; i < len(text); i++ { - if action := options.smartypants[text[i]]; action != nil { - if i > mark { - out.Write(text[mark:i]) - } - - previousChar := byte(0) - if i > 0 { - previousChar = text[i-1] - } - i += action(out, &smrt, previousChar, text[i:]) - mark = i + 1 - } - } - - if mark < len(text) { - out.Write(text[mark:]) - } -}