From 3c6f18afc72d1a27f6eb0673a3ee387f4a0548b5 Mon Sep 17 00:00:00 2001 From: Russ Ross Date: Wed, 29 Jun 2011 11:13:17 -0600 Subject: [PATCH] Renderer is now an interface --- block.go | 94 +++++++++++----------------- example/main.go | 2 +- html.go | 161 +++++++++++++++++++++--------------------------- inline.go | 83 ++++++++----------------- latex.go | 89 +++++++++----------------- markdown.go | 96 ++++++++++++----------------- smartypants.go | 30 --------- 7 files changed, 203 insertions(+), 352 deletions(-) diff --git a/block.go b/block.go index 21cfb97..13f64ab 100644 --- a/block.go +++ b/block.go @@ -43,7 +43,7 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) { //
// ... //
- 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 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) { // 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 @@ func (parser *Parser) blockPrefixHeader(out *bytes.Buffer, data []byte) int { 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 @@ func (parser *Parser) blockHtml(out *bytes.Buffer, data []byte, doRender bool) 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 } @@ -283,8 +279,8 @@ func (parser *Parser) blockHtml(out *bytes.Buffer, data []byte, doRender bool) 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 @@ func (parser *Parser) blockHtml(out *bytes.Buffer, data []byte, doRender bool) i } // 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,15 +562,13 @@ func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int { 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 @@ func (parser *Parser) blockTable(out *bytes.Buffer, data []byte) int { 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 @@ func (parser *Parser) blockTableRow(out *bytes.Buffer, data []byte, columns int, 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 @@ func (parser *Parser) blockQuote(out *bytes.Buffer, data []byte) int { } 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 @@ func (parser *Parser) blockCode(out *bytes.Buffer, data []byte) int { 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 @@ func (parser *Parser) blockList(out *bytes.Buffer, data []byte, flags int) int { 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 @@ func (parser *Parser) blockListItem(out *bytes.Buffer, data []byte, flags *int) } // 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 @@ func (parser *Parser) renderParagraph(out *bytes.Buffer, data []byte) { for end > beg && isspace(data[end-1]) { end-- } - if end == beg || parser.r.Paragraph == nil { + if end == beg { return } @@ -1089,7 +1067,7 @@ func (parser *Parser) renderParagraph(out *bytes.Buffer, data []byte) { 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 @@ func (parser *Parser) blockParagraph(out *bytes.Buffer, data []byte) int { } // 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 @@ func (parser *Parser) blockParagraph(out *bytes.Buffer, data []byte) int { 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 @@ func (parser *Parser) blockParagraph(out *bytes.Buffer, data []byte) int { // 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 diff --git a/example/main.go b/example/main.go index 3466cd6..a6bd1a4 100644 --- a/example/main.go +++ b/example/main.go @@ -121,7 +121,7 @@ func main() { 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) diff --git a/html.go b/html.go index 648a75c..b2d9314 100644 --- a/html.go +++ b/html.go @@ -37,7 +37,7 @@ const ( 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 @@ type htmlOptions struct { 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 @@ func HtmlRenderer(flags int, title string, css string) *Renderer { smartypants: Smartypants(flags), } - return r } func attrEscape(out *bytes.Buffer, src []byte) { @@ -154,8 +124,7 @@ func attrEscape(out *bytes.Buffer, src []byte) { } } -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 @@ func htmlHeader(out *bytes.Buffer, text func() bool, level int, opaque interface // 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("\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 @@ func htmlBlockHtml(out *bytes.Buffer, text []byte, opaque interface{}) { 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 @@ func htmlHRule(out *bytes.Buffer, opaque interface{}) { 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 @@ func htmlBlockCodeNormal(out *bytes.Buffer, text []byte, lang string, opaque int * E.g. * ~~~~ {.python .numbered} =>

  */
-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 @@ func htmlBlockCodeGithub(out *bytes.Buffer, text []byte, lang string, opaque int
 }
 
 
-func htmlBlockQuote(out *bytes.Buffer, text []byte, opaque interface{}) {
+func (options *Html) BlockQuote(out *bytes.Buffer, text []byte) {
 	out.WriteString("
\n") out.Write(text) out.WriteString("
") } -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 @@ func htmlTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, out.WriteString("\n") } -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 @@ func htmlTableRow(out *bytes.Buffer, text []byte, opaque interface{}) { out.WriteString("\n") } -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 @@ func htmlTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{} out.WriteString("") } -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 @@ func htmlList(out *bytes.Buffer, text func() bool, flags int, opaque interface{} } } -func htmlListItem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { +func (options *Html) ListItem(out *bytes.Buffer, text []byte, flags int) { out.WriteString("
  • ") size := len(text) for size > 0 && text[size-1] == '\n' { @@ -395,7 +360,7 @@ func htmlListItem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) out.WriteString("
  • \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 @@ func htmlParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) { out.WriteString("

    \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 @@ func htmlAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) return true } -func htmlCodeSpan(out *bytes.Buffer, text []byte, opaque interface{}) bool { +func (options *Html) CodeSpan(out *bytes.Buffer, text []byte) bool { out.WriteString("") attrEscape(out, text) out.WriteString("") 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 @@ func htmlDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool 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 @@ func htmlEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool { 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 @@ func htmlImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque return true } -func htmlLineBreak(out *bytes.Buffer, opaque interface{}) bool { - options := opaque.(*htmlOptions) +func (options *Html) LineBreak(out *bytes.Buffer) bool { out.WriteString(" 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 @@ func htmlDocumentHeader(out *bytes.Buffer, opaque interface{}) { } out.WriteString("\n") out.WriteString(" ") - htmlNormalText(out, []byte(options.title), opaque) + options.NormalText(out, []byte(options.title)) out.WriteString("\n") out.WriteString(" options.currentLevel { switch { case bytes.HasSuffix(options.toc.Bytes(), []byte("\n")): @@ -685,8 +667,7 @@ func htmlTocHeader(text []byte, level int, opaque interface{}) { options.toc.WriteString("\n") } -func htmlTocFinalize(opaque interface{}) { - options := opaque.(*htmlOptions) +func (options *Html) TocFinalize() { for options.currentLevel > 1 { options.toc.WriteString("\n") options.currentLevel-- diff --git a/inline.go b/inline.go index eca7f43..4ecf862 100644 --- a/inline.go +++ b/inline.go @@ -36,11 +36,7 @@ func (parser *Parser) parseInline(out *bytes.Buffer, data []byte) { 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 @@ func inlineCodeSpan(out *bytes.Buffer, parser *Parser, data []byte, offset int) } // 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 @@ func inlineLineBreak(out *bytes.Buffer, parser *Parser, data []byte, offset int) 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 @@ -198,11 +188,6 @@ func inlineLink(out *bytes.Buffer, parser *Parser, data []byte, offset int) int 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++ { switch { @@ -439,9 +424,9 @@ func inlineLink(out *bytes.Buffer, parser *Parser, data []byte, offset int) int 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 @@ func inlineLAngle(out *bytes.Buffer, parser *Parser, data []byte, offset int) in 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 @@ func inlineEscape(out *bytes.Buffer, parser *Parser, data []byte, offset int) in 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 @@ func inlineEntity(out *bytes.Buffer, parser *Parser, data []byte, offset int) in 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 @@ func inlineAutoLink(out *bytes.Buffer, parser *Parser, data []byte, offset int) 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 inlineHelperFindEmphChar(data []byte, c byte) int { 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 @@ func inlineHelperEmph1(out *bytes.Buffer, parser *Parser, data []byte, c byte) i 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 @@ func inlineHelperEmph1(out *bytes.Buffer, parser *Parser, data []byte, c byte) i } 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 @@ func inlineHelperEmph2(out *bytes.Buffer, parser *Parser, data []byte, c byte) i 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 @@ func inlineHelperEmph3(out *bytes.Buffer, parser *Parser, data []byte, offset in } 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 diff --git a/latex.go b/latex.go index f62b973..7739746 100644 --- a/latex.go +++ b/latex.go @@ -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 @@ func latexBlockCode(out *bytes.Buffer, text []byte, lang string, opaque interfac } } -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 @@ func latexHeader(out *bytes.Buffer, text func() bool, level int, opaque interfac 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 @@ func latexList(out *bytes.Buffer, text func() bool, flags int, opaque interface{ } } -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 @@ func latexParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) { 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 @@ func latexTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, 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 @@ func latexAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) 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 @@ func latexImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque 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 @@ func latexLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opa 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 @@ func escapeSpecialChars(out *bytes.Buffer, text []byte) { } } -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 @@ func latexDocumentHeader(out *bytes.Buffer, opaque interface{}) { 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") } diff --git a/markdown.go b/markdown.go index 5efc9e9..4b18155 100644 --- a/markdown.go +++ b/markdown.go @@ -105,48 +105,45 @@ var blockTags = map[string]bool{ // // 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 @@ func MarkdownCommon(input []byte) []byte { // 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 @@ func Markdown(input []byte, renderer *Renderer, extensions int) []byte { 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 - } - if parser.r.LineBreak != nil { - parser.inline['\n'] = inlineLineBreak - } - if parser.r.Image != nil || parser.r.Link != nil { - parser.inline['['] = inlineLink + parser.inline['*'] = inlineEmphasis + parser.inline['_'] = inlineEmphasis + if extensions&EXTENSION_STRIKETHROUGH != 0 { + parser.inline['~'] = inlineEmphasis } + parser.inline['`'] = inlineCodeSpan + parser.inline['\n'] = inlineLineBreak + parser.inline['['] = inlineLink parser.inline['<'] = inlineLAngle parser.inline['\\'] = inlineEscape parser.inline['&'] = inlineEntity @@ -291,15 +280,10 @@ func firstPass(parser *Parser, input []byte) []byte { // 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") diff --git a/smartypants.go b/smartypants.go index 24e34e5..771af25 100644 --- a/smartypants.go +++ b/smartypants.go @@ -369,33 +369,3 @@ func Smartypants(flags int) *SmartypantsRenderer { 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:]) - } -}