Renderer is now an interface

pull/5/merge
Russ Ross 2011-06-29 11:13:17 -06:00
parent 793fee5451
commit 3c6f18afc7
7 changed files with 203 additions and 352 deletions

View File

@ -43,7 +43,7 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) {
// <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 @@ 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

View File

@ -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)

161
html.go
View File

@ -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("</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 @@ 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} => <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 @@ 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("<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 @@ func htmlTable(out *bytes.Buffer, header []byte, body []byte, columnData []int,
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 @@ func htmlTableRow(out *bytes.Buffer, text []byte, opaque interface{}) {
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 @@ func htmlTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}
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 @@ 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("<li>")
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("</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 @@ func htmlParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) {
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 @@ 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("<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 @@ 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("<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 @@ func htmlLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaq
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 @@ func htmlRawTag(out *bytes.Buffer, text []byte, opaque interface{}) bool {
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 @@ func htmlTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) bool
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 @@ func htmlStrikeThrough(out *bytes.Buffer, text []byte, opaque interface{}) bool
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 @@ func htmlDocumentHeader(out *bytes.Buffer, opaque interface{}) {
}
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 @@ func htmlDocumentHeader(out *bytes.Buffer, opaque interface{}) {
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 htmlDocumentFooter(out *bytes.Buffer, opaque interface{}) {
}
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 @@ func htmlTocHeader(text []byte, level int, opaque interface{}) {
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--

View File

@ -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

View File

@ -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")
}

View File

@ -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")

View File

@ -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:])
}
}