all repos — grayfriday @ f0cd9a420e028f3c0d19866fa8c873d6a70e5ae2

blackfriday fork with a few changes

render -> Parser, made parsing functions methods of *Parser
Russ Ross russ@russross.com
Tue, 28 Jun 2011 18:58:53 -0600
commit

f0cd9a420e028f3c0d19866fa8c873d6a70e5ae2

parent

55cde00c8ab66a2037b219eb4ae96d8e30e272fe

3 files changed, 232 insertions(+), 229 deletions(-)

jump to
M block.goblock.go

@@ -18,12 +18,12 @@ "bytes"

) // parse block-level data -func parseBlock(out *bytes.Buffer, rndr *render, data []byte) { +func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) { // this is called recursively: enforce a maximum depth - if rndr.nesting >= rndr.maxNesting { + if parser.nesting >= parser.maxNesting { return } - rndr.nesting++ + parser.nesting++ // parse out one block-level construct at a time for len(data) > 0 {

@@ -33,8 +33,8 @@ // # Header 1

// ## Header 2 // ... // ###### Header 6 - if isPrefixHeader(rndr, data) { - data = data[blockPrefixHeader(out, rndr, data):] + if parser.isPrefixHeader(data) { + data = data[parser.blockPrefixHeader(out, data):] continue }

@@ -43,15 +43,15 @@ //

// <div> // ... // </div> - if data[0] == '<' && rndr.mk.BlockHtml != nil { - if i := blockHtml(out, rndr, data, true); i > 0 { + if data[0] == '<' && parser.r.BlockHtml != nil { + if i := parser.blockHtml(out, data, true); i > 0 { data = data[i:] continue } } // blank lines. note: returns the # of bytes to skip - if i := isEmpty(data); i > 0 { + if i := parser.isEmpty(data); i > 0 { data = data[i:] continue }

@@ -63,9 +63,9 @@ // or

// ****** // or // ______ - if isHRule(data) { - if rndr.mk.HRule != nil { - rndr.mk.HRule(out, rndr.mk.Opaque) + if parser.isHRule(data) { + if parser.r.HRule != nil { + parser.r.HRule(out, parser.r.Opaque) } var i int for i = 0; i < len(data) && data[i] != '\n'; i++ {

@@ -84,8 +84,8 @@ // }

// return n * fact(n-1) // } // ``` - if rndr.flags&EXTENSION_FENCED_CODE != 0 { - if i := blockFencedCode(out, rndr, data); i > 0 { + if parser.flags&EXTENSION_FENCED_CODE != 0 { + if i := parser.blockFencedCode(out, data); i > 0 { data = data[i:] continue }

@@ -97,8 +97,8 @@ // Name | Age | Phone

// ------|-----|--------- // Bob | 31 | 555-1234 // Alice | 27 | 555-4321 - if rndr.flags&EXTENSION_TABLES != 0 { - if i := blockTable(out, rndr, data); i > 0 { + if parser.flags&EXTENSION_TABLES != 0 { + if i := parser.blockTable(out, data); i > 0 { data = data[i:] continue }

@@ -108,8 +108,8 @@ // block quote:

// // > A big quote I found somewhere // > on the web - if blockQuotePrefix(data) > 0 { - data = data[blockQuote(out, rndr, data):] + if parser.blockQuotePrefix(data) > 0 { + data = data[parser.blockQuote(out, data):] continue }

@@ -121,8 +121,8 @@ // return a

// } // return b // } - if blockCodePrefix(data) > 0 { - data = data[blockCode(out, rndr, data):] + if parser.blockCodePrefix(data) > 0 { + data = data[parser.blockCode(out, data):] continue }

@@ -132,8 +132,8 @@ // * Item 1

// * Item 2 // // also works with + or - - if blockUliPrefix(data) > 0 { - data = data[blockList(out, rndr, data, 0):] + if parser.blockUliPrefix(data) > 0 { + data = data[parser.blockList(out, data, 0):] continue }

@@ -141,25 +141,25 @@ // a numbered/ordered list:

// // 1. Item 1 // 2. Item 2 - if blockOliPrefix(data) > 0 { - data = data[blockList(out, rndr, data, LIST_TYPE_ORDERED):] + if parser.blockOliPrefix(data) > 0 { + data = data[parser.blockList(out, data, LIST_TYPE_ORDERED):] continue } // anything else must look like a normal paragraph // note: this finds underlined headers, too - data = data[blockParagraph(out, rndr, data):] + data = data[parser.blockParagraph(out, data):] } - rndr.nesting-- + parser.nesting-- } -func isPrefixHeader(rndr *render, data []byte) bool { +func (parser *Parser) isPrefixHeader(data []byte) bool { if data[0] != '#' { return false } - if rndr.flags&EXTENSION_SPACE_HEADERS != 0 { + if parser.flags&EXTENSION_SPACE_HEADERS != 0 { level := 0 for level < len(data) && level < 6 && data[level] == '#' { level++

@@ -171,7 +171,7 @@ }

return true } -func blockPrefixHeader(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockPrefixHeader(out *bytes.Buffer, data []byte) int { level := 0 for level < len(data) && level < 6 && data[level] == '#' { level++

@@ -189,18 +189,18 @@ for end > 0 && (data[end-1] == ' ' || data[end-1] == '\t') {

end-- } if end > i { - if rndr.mk.Header != nil { + if parser.r.Header != nil { work := func() bool { - parseInline(out, rndr, data[i:end]) + parser.parseInline(out, data[i:end]) return true } - rndr.mk.Header(out, work, level, rndr.mk.Opaque) + parser.r.Header(out, work, level, parser.r.Opaque) } } return skip } -func isUnderlinedHeader(data []byte) int { +func (parser *Parser) isUnderlinedHeader(data []byte) int { i := 0 // test of level 1 header

@@ -234,14 +234,14 @@

return 0 } -func blockHtml(out *bytes.Buffer, rndr *render, data []byte, doRender bool) int { +func (parser *Parser) blockHtml(out *bytes.Buffer, data []byte, doRender bool) int { var i, j int // identify the opening tag if len(data) < 2 || data[0] != '<' { return 0 } - curtag, tagfound := blockHtmlFindTag(data[1:]) + curtag, tagfound := parser.blockHtmlFindTag(data[1:]) // handle special cases if !tagfound {

@@ -256,13 +256,13 @@ }

i++ if i < len(data) { - j = isEmpty(data[i:]) + j = parser.isEmpty(data[i:]) } if j > 0 { size := i + j - if doRender && rndr.mk.BlockHtml != nil { - rndr.mk.BlockHtml(out, data[:size], rndr.mk.Opaque) + if doRender && parser.r.BlockHtml != nil { + parser.r.BlockHtml(out, data[:size], parser.r.Opaque) } return size }

@@ -280,11 +280,11 @@ }

if i+1 < len(data) { i++ - j = isEmpty(data[i:]) + j = parser.isEmpty(data[i:]) if j > 0 { size := i + j - if doRender && rndr.mk.BlockHtml != nil { - rndr.mk.BlockHtml(out, data[:size], rndr.mk.Opaque) + if doRender && parser.r.BlockHtml != nil { + parser.r.BlockHtml(out, data[:size], parser.r.Opaque) } return size }

@@ -314,7 +314,7 @@ if i+2+len(curtag) >= len(data) {

break } - j = blockHtmlFindEnd(curtag, rndr, data[i-1:]) + j = parser.blockHtmlFindEnd(curtag, data[i-1:]) if j > 0 { i += j - 1

@@ -329,14 +329,14 @@ return 0

} // the end of the block has been found - if doRender && rndr.mk.BlockHtml != nil { - rndr.mk.BlockHtml(out, data[:i], rndr.mk.Opaque) + if doRender && parser.r.BlockHtml != nil { + parser.r.BlockHtml(out, data[:i], parser.r.Opaque) } return i } -func blockHtmlFindTag(data []byte) (string, bool) { +func (parser *Parser) blockHtmlFindTag(data []byte) (string, bool) { i := 0 for i < len(data) && isalnum(data[i]) { i++

@@ -351,7 +351,7 @@ }

return "", false } -func blockHtmlFindEnd(tag string, rndr *render, data []byte) int { +func (parser *Parser) blockHtmlFindEnd(tag string, data []byte) int { // assume data[0] == '<' && data[1] == '/' already tested // check if tag is a match

@@ -364,20 +364,20 @@ // check for blank line/eof after the closing tag

i := len(tag) + 3 w := 0 if i < len(data) { - if w = isEmpty(data[i:]); w == 0 { + if w = parser.isEmpty(data[i:]); w == 0 { return 0 // non-blank after tag } } i += w w = 0 - if rndr.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { + if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { if i < len(data) { - w = isEmpty(data[i:]) + w = parser.isEmpty(data[i:]) } } else { if i < len(data) { - if w = isEmpty(data[i:]); w == 0 { + if w = parser.isEmpty(data[i:]); w == 0 { return 0 // non-blank line after tag line } }

@@ -386,7 +386,7 @@

return i + w } -func isEmpty(data []byte) int { +func (parser *Parser) isEmpty(data []byte) int { var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] != ' ' && data[i] != '\t' {

@@ -396,7 +396,7 @@ }

return i + 1 } -func isHRule(data []byte) bool { +func (parser *Parser) isHRule(data []byte) bool { // skip initial spaces if len(data) < 3 { return false

@@ -429,7 +429,7 @@

return n >= 3 } -func isFencedCode(data []byte, syntax **string, oldmarker string) (skip int, marker string) { +func (parser *Parser) isFencedCode(data []byte, syntax **string, oldmarker string) (skip int, marker string) { i, size := 0, 0 skip = 0

@@ -526,9 +526,9 @@ skip = i + 1

return } -func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int { var lang *string - beg, marker := isFencedCode(data, &lang, "") + beg, marker := parser.isFencedCode(data, &lang, "") if beg == 0 { return 0 }

@@ -536,7 +536,7 @@

var work bytes.Buffer for beg < len(data) { - fenceEnd, _ := isFencedCode(data[beg:], nil, marker) + fenceEnd, _ := parser.isFencedCode(data[beg:], nil, marker) if fenceEnd != 0 { beg += fenceEnd break

@@ -548,7 +548,7 @@ }

if beg < end { // verbatim copy to the working buffer - if isEmpty(data[beg:]) > 0 { + if parser.isEmpty(data[beg:]) > 0 { work.WriteByte('\n') } else { work.Write(data[beg:end])

@@ -566,21 +566,21 @@ if work.Len() > 0 && work.Bytes()[work.Len()-1] != '\n' {

work.WriteByte('\n') } - if rndr.mk.BlockCode != nil { + if parser.r.BlockCode != nil { syntax := "" if lang != nil { syntax = *lang } - rndr.mk.BlockCode(out, work.Bytes(), syntax, rndr.mk.Opaque) + parser.r.BlockCode(out, work.Bytes(), syntax, parser.r.Opaque) } return beg } -func blockTable(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockTable(out *bytes.Buffer, data []byte) int { var headerWork bytes.Buffer - i, columns, colData := blockTableHeader(&headerWork, rndr, data) + i, columns, colData := parser.blockTableHeader(&headerWork, data) if i == 0 { return 0 }

@@ -600,18 +600,18 @@ i = rowStart

break } - blockTableRow(&bodyWork, rndr, data[rowStart:i], columns, colData) + parser.blockTableRow(&bodyWork, data[rowStart:i], columns, colData) i++ } - if rndr.mk.Table != nil { - rndr.mk.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData, rndr.mk.Opaque) + if parser.r.Table != nil { + parser.r.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData, parser.r.Opaque) } return i } -func blockTableHeader(out *bytes.Buffer, rndr *render, data []byte) (size int, columns int, columnData []int) { +func (parser *Parser) blockTableHeader(out *bytes.Buffer, data []byte) (size int, columns int, columnData []int) { i, pipes := 0, 0 columnData = []int{} for i = 0; i < len(data) && data[i] != '\n'; i++ {

@@ -692,12 +692,12 @@ if col < columns {

return 0, 0, columnData } - blockTableRow(out, rndr, data[:headerEnd], columns, columnData) + parser.blockTableRow(out, data[:headerEnd], columns, columnData) size = underEnd + 1 return } -func blockTableRow(out *bytes.Buffer, rndr *render, data []byte, columns int, colData []int) { +func (parser *Parser) blockTableRow(out *bytes.Buffer, data []byte, columns int, colData []int) { i, col := 0, 0 var rowWork bytes.Buffer

@@ -723,14 +723,14 @@ cellEnd--

} var cellWork bytes.Buffer - parseInline(&cellWork, rndr, data[cellStart:cellEnd+1]) + parser.parseInline(&cellWork, data[cellStart:cellEnd+1]) - if rndr.mk.TableCell != nil { + if parser.r.TableCell != nil { cdata := 0 if col < len(colData) { cdata = colData[col] } - rndr.mk.TableCell(&rowWork, cellWork.Bytes(), cdata, rndr.mk.Opaque) + parser.r.TableCell(&rowWork, cellWork.Bytes(), cdata, parser.r.Opaque) } i++

@@ -738,22 +738,22 @@ }

for ; col < columns; col++ { emptyCell := []byte{} - if rndr.mk.TableCell != nil { + if parser.r.TableCell != nil { cdata := 0 if col < len(colData) { cdata = colData[col] } - rndr.mk.TableCell(&rowWork, emptyCell, cdata, rndr.mk.Opaque) + parser.r.TableCell(&rowWork, emptyCell, cdata, parser.r.Opaque) } } - if rndr.mk.TableRow != nil { - rndr.mk.TableRow(out, rowWork.Bytes(), rndr.mk.Opaque) + if parser.r.TableRow != nil { + parser.r.TableRow(out, rowWork.Bytes(), parser.r.Opaque) } } // returns blockquote prefix length -func blockQuotePrefix(data []byte) int { +func (parser *Parser) blockQuotePrefix(data []byte) int { i := 0 for i < len(data) && i < 3 && data[i] == ' ' { i++

@@ -768,7 +768,7 @@ return 0

} // parse a blockquote fragment -func blockQuote(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockQuote(out *bytes.Buffer, data []byte) int { var block bytes.Buffer var work bytes.Buffer beg, end := 0, 0

@@ -776,11 +776,13 @@ for beg < len(data) {

for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ { } - if pre := blockQuotePrefix(data[beg:]); pre > 0 { + if pre := parser.blockQuotePrefix(data[beg:]); pre > 0 { beg += pre // skip prefix } else { // empty line followed by non-quote line - if isEmpty(data[beg:]) > 0 && (end >= len(data) || (blockQuotePrefix(data[end:]) == 0 && isEmpty(data[end:]) == 0)) { + if parser.isEmpty(data[beg:]) > 0 && + (end >= len(data) || + (parser.blockQuotePrefix(data[end:]) == 0 && parser.isEmpty(data[end:]) == 0)) { break } }

@@ -791,15 +793,15 @@ }

beg = end } - parseBlock(&block, rndr, work.Bytes()) - if rndr.mk.BlockQuote != nil { - rndr.mk.BlockQuote(out, block.Bytes(), rndr.mk.Opaque) + parser.parseBlock(&block, work.Bytes()) + if parser.r.BlockQuote != nil { + parser.r.BlockQuote(out, block.Bytes(), parser.r.Opaque) } return end } // returns prefix length for block code -func blockCodePrefix(data []byte) int { +func (parser *Parser) blockCodePrefix(data []byte) int { if len(data) > 0 && data[0] == '\t' { return 1 }

@@ -809,7 +811,7 @@ }

return 0 } -func blockCode(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockCode(out *bytes.Buffer, data []byte) int { var work bytes.Buffer beg, end := 0, 0

@@ -817,10 +819,10 @@ for beg < len(data) {

for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ { } - if pre := blockCodePrefix(data[beg:end]); pre > 0 { + if pre := parser.blockCodePrefix(data[beg:end]); pre > 0 { beg += pre } else { - if isEmpty(data[beg:end]) == 0 { + if parser.isEmpty(data[beg:end]) == 0 { // non-empty non-prefixed line breaks the pre break }

@@ -828,7 +830,7 @@ }

if beg < end { // verbatim copy to the working buffer, escaping entities - if isEmpty(data[beg:end]) > 0 { + if parser.isEmpty(data[beg:end]) > 0 { work.WriteByte('\n') } else { work.Write(data[beg:end])

@@ -849,15 +851,15 @@ }

work.WriteByte('\n') - if rndr.mk.BlockCode != nil { - rndr.mk.BlockCode(out, work.Bytes(), "", rndr.mk.Opaque) + if parser.r.BlockCode != nil { + parser.r.BlockCode(out, work.Bytes(), "", parser.r.Opaque) } return beg } // returns unordered list item prefix -func blockUliPrefix(data []byte) int { +func (parser *Parser) blockUliPrefix(data []byte) int { i := 0 // start with up to 3 spaces

@@ -875,7 +877,7 @@ return i + 2

} // returns ordered list item prefix -func blockOliPrefix(data []byte) int { +func (parser *Parser) blockOliPrefix(data []byte) int { i := 0 // start with up to 3 spaces

@@ -898,12 +900,12 @@ return i + 2

} // parse ordered or unordered list block -func blockList(out *bytes.Buffer, rndr *render, data []byte, flags int) int { +func (parser *Parser) blockList(out *bytes.Buffer, data []byte, flags int) int { i := 0 work := func() bool { j := 0 for i < len(data) { - j = blockListItem(out, rndr, data[i:], &flags) + j = parser.blockListItem(out, data[i:], &flags) i += j if j == 0 || flags&LIST_ITEM_END_OF_LIST != 0 {

@@ -913,15 +915,15 @@ }

return true } - if rndr.mk.List != nil { - rndr.mk.List(out, work, flags, rndr.mk.Opaque) + if parser.r.List != nil { + parser.r.List(out, work, flags, parser.r.Opaque) } return i } // parse a single list item // assumes initial prefix is already removed -func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int { +func (parser *Parser) blockListItem(out *bytes.Buffer, data []byte, flags *int) int { // keep track of the first indentation prefix beg, end, pre, sublist, orgpre, i := 0, 0, 0, 0, 0, 0

@@ -929,9 +931,9 @@ for orgpre < 3 && orgpre < len(data) && data[orgpre] == ' ' {

orgpre++ } - beg = blockUliPrefix(data) + beg = parser.blockUliPrefix(data) if beg == 0 { - beg = blockOliPrefix(data) + beg = parser.blockOliPrefix(data) } if beg == 0 { return 0

@@ -966,7 +968,7 @@ end++

} // process an empty line - if isEmpty(data[beg:end]) > 0 { + if parser.isEmpty(data[beg:end]) > 0 { containsBlankLine = true beg = end continue

@@ -982,7 +984,7 @@ pre = i

if data[beg] == '\t' { i = 1 pre = TAB_SIZE_DEFAULT - if rndr.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { + if parser.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { pre = TAB_SIZE_EIGHT } }

@@ -990,7 +992,8 @@

chunk := data[beg+i : end] // check for a nested list item - if (blockUliPrefix(chunk) > 0 && !isHRule(chunk)) || blockOliPrefix(chunk) > 0 { + if (parser.blockUliPrefix(chunk) > 0 && !parser.isHRule(chunk)) || + parser.blockOliPrefix(chunk) > 0 { if containsBlankLine { containsBlock = true }

@@ -1005,7 +1008,7 @@ sublist = work.Len()

} } else { // how about a nested prefix header? - if isPrefixHeader(rndr, chunk) { + if parser.isPrefixHeader(chunk) { // only nest headers that are indented if containsBlankLine && i < 4 && data[beg] != '\t' { *flags |= LIST_ITEM_END_OF_LIST

@@ -1042,31 +1045,31 @@ workbytes := work.Bytes()

if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 { // intermediate render of block li if sublist > 0 && sublist < len(workbytes) { - parseBlock(&inter, rndr, workbytes[:sublist]) - parseBlock(&inter, rndr, workbytes[sublist:]) + parser.parseBlock(&inter, workbytes[:sublist]) + parser.parseBlock(&inter, workbytes[sublist:]) } else { - parseBlock(&inter, rndr, workbytes) + parser.parseBlock(&inter, workbytes) } } else { // intermediate render of inline li if sublist > 0 && sublist < len(workbytes) { - parseInline(&inter, rndr, workbytes[:sublist]) - parseBlock(&inter, rndr, workbytes[sublist:]) + parser.parseInline(&inter, workbytes[:sublist]) + parser.parseBlock(&inter, workbytes[sublist:]) } else { - parseInline(&inter, rndr, workbytes) + parser.parseInline(&inter, workbytes) } } // render li itself - if rndr.mk.ListItem != nil { - rndr.mk.ListItem(out, inter.Bytes(), *flags, rndr.mk.Opaque) + if parser.r.ListItem != nil { + parser.r.ListItem(out, inter.Bytes(), *flags, parser.r.Opaque) } return beg } // render a single paragraph that has already been parsed out -func renderParagraph(out *bytes.Buffer, rndr *render, data []byte) { +func (parser *Parser) renderParagraph(out *bytes.Buffer, data []byte) { // trim leading whitespace beg := 0 for beg < len(data) && isspace(data[beg]) {

@@ -1078,18 +1081,18 @@ end := len(data)

for end > beg && isspace(data[end-1]) { end-- } - if end == beg || rndr.mk.Paragraph == nil { + if end == beg || parser.r.Paragraph == nil { return } work := func() bool { - parseInline(out, rndr, data[beg:end]) + parser.parseInline(out, data[beg:end]) return true } - rndr.mk.Paragraph(out, work, rndr.mk.Opaque) + parser.r.Paragraph(out, work, parser.r.Opaque) } -func blockParagraph(out *bytes.Buffer, rndr *render, data []byte) int { +func (parser *Parser) blockParagraph(out *bytes.Buffer, data []byte) int { // prev: index of 1st char of previous line // line: index of 1st char of current line // i: index of cursor/end of current line

@@ -1103,16 +1106,16 @@ current := data[i:]

line = i // did we find a blank line marking the end of the paragraph? - if n := isEmpty(current); n > 0 { - renderParagraph(out, rndr, data[:i]) + if n := parser.isEmpty(current); n > 0 { + parser.renderParagraph(out, data[:i]) return i + n } // an underline under some text marks a header, so our paragraph ended on prev line - if i > 0 && rndr.mk.Header != nil { - if level := isUnderlinedHeader(current); level > 0 { + if i > 0 && parser.r.Header != nil { + if level := parser.isUnderlinedHeader(current); level > 0 { // render the paragraph - renderParagraph(out, rndr, data[:prev]) + parser.renderParagraph(out, data[:prev]) // ignore leading and trailing whitespace eol := i - 1

@@ -1125,13 +1128,13 @@ }

// render the header // this ugly double closure avoids forcing variables onto the heap - work := func(o *bytes.Buffer, r *render, d []byte) func() bool { + work := func(o *bytes.Buffer, p *Parser, d []byte) func() bool { return func() bool { - parseInline(o, r, d) + p.parseInline(o, d) return true } - }(out, rndr, data[prev:eol]) - rndr.mk.Header(out, work, level, rndr.mk.Opaque) + }(out, parser, data[prev:eol]) + parser.r.Header(out, work, level, parser.r.Opaque) // find the end of the underline for ; i < len(data) && data[i] != '\n'; i++ {

@@ -1141,17 +1144,17 @@ }

} // if the next line starts a block of HTML, then the paragraph ends here - if rndr.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { - if data[i] == '<' && rndr.mk.BlockHtml != nil && blockHtml(out, rndr, current, false) > 0 { + if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { + if data[i] == '<' && parser.r.BlockHtml != nil && parser.blockHtml(out, current, false) > 0 { // rewind to before the HTML block - renderParagraph(out, rndr, data[:i]) + parser.renderParagraph(out, data[:i]) return i } } // if there's a prefixed header or a horizontal rule after this, paragraph is over - if isPrefixHeader(rndr, current) || isHRule(current) { - renderParagraph(out, rndr, data[:i]) + if parser.isPrefixHeader(current) || parser.isHRule(current) { + parser.renderParagraph(out, data[:i]) return i }

@@ -1162,6 +1165,6 @@ i++

} } - renderParagraph(out, rndr, data[:i]) + parser.renderParagraph(out, data[:i]) return i }
M inline.goinline.go

@@ -22,22 +22,22 @@ // Each function returns the number of chars taken care of

// data is the complete block being rendered // offset is the number of valid chars before the current cursor -func parseInline(out *bytes.Buffer, rndr *render, data []byte) { +func (parser *Parser) parseInline(out *bytes.Buffer, data []byte) { // this is called recursively: enforce a maximum depth - if rndr.nesting >= rndr.maxNesting { + if parser.nesting >= parser.maxNesting { return } - rndr.nesting++ + parser.nesting++ i, end := 0, 0 for i < len(data) { // copy inactive chars into the output - for end < len(data) && rndr.inline[data[end]] == nil { + for end < len(data) && parser.inline[data[end]] == nil { end++ } - if rndr.mk.NormalText != nil { - rndr.mk.NormalText(out, data[i:end], rndr.mk.Opaque) + if parser.r.NormalText != nil { + parser.r.NormalText(out, data[i:end], parser.r.Opaque) } else { out.Write(data[i:end]) }

@@ -48,8 +48,8 @@ }

i = end // call the trigger - parser := rndr.inline[data[end]] - if consumed := parser(out, rndr, data, i); consumed == 0 { + handler := parser.inline[data[end]] + if consumed := handler(out, parser, data, i); consumed == 0 { // no action from the callback; buffer the byte for later end = i + 1 } else {

@@ -59,11 +59,11 @@ end = i

} } - rndr.nesting-- + parser.nesting-- } // single and double emphasis parsing -func inlineEmphasis(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineEmphasis(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { data = data[offset:] c := data[0] ret := 0

@@ -74,7 +74,7 @@ // strikethrough only takes two characters '~~'

if c == '~' || isspace(data[1]) { return 0 } - if ret = inlineHelperEmph1(out, rndr, data[1:], c); ret == 0 { + if ret = inlineHelperEmph1(out, parser, data[1:], c); ret == 0 { return 0 }

@@ -85,7 +85,7 @@ if len(data) > 3 && data[1] == c && data[2] != c {

if isspace(data[2]) { return 0 } - if ret = inlineHelperEmph2(out, rndr, data[2:], c); ret == 0 { + if ret = inlineHelperEmph2(out, parser, data[2:], c); ret == 0 { return 0 }

@@ -96,7 +96,7 @@ if len(data) > 4 && data[1] == c && data[2] == c && data[3] != c {

if c == '~' || isspace(data[3]) { return 0 } - if ret = inlineHelperEmph3(out, rndr, data, 3, c); ret == 0 { + if ret = inlineHelperEmph3(out, parser, data, 3, c); ret == 0 { return 0 }

@@ -106,7 +106,7 @@

return 0 } -func inlineCodeSpan(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineCodeSpan(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { data = data[offset:] nb := 0

@@ -143,10 +143,10 @@ fEnd--

} // render the code span - if rndr.mk.CodeSpan == nil { + if parser.r.CodeSpan == nil { return 0 } - if rndr.mk.CodeSpan(out, data[fBegin:fEnd], rndr.mk.Opaque) == 0 { + if parser.r.CodeSpan(out, data[fBegin:fEnd], parser.r.Opaque) == 0 { end = 0 }

@@ -156,7 +156,7 @@ }

// newline preceded by two spaces becomes <br> // newline without two spaces works when EXTENSION_HARD_LINE_BREAK is enabled -func inlineLineBreak(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineLineBreak(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { // remove trailing spaces from out outBytes := out.Bytes() end := len(outBytes)

@@ -167,14 +167,14 @@ }

out.Truncate(eol) // should there be a hard line break here? - if rndr.flags&EXTENSION_HARD_LINE_BREAK == 0 && end-eol < 2 { + if parser.flags&EXTENSION_HARD_LINE_BREAK == 0 && end-eol < 2 { return 0 } - if rndr.mk.LineBreak == nil { + if parser.r.LineBreak == nil { return 0 } - if rndr.mk.LineBreak(out, rndr.mk.Opaque) > 0 { + if parser.r.LineBreak(out, parser.r.Opaque) > 0 { return 1 } else { return 0

@@ -184,9 +184,9 @@ return 0

} // '[': parse a link or an image -func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineLink(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { // no links allowed inside other links - if rndr.insideLink { + if parser.insideLink { return 0 }

@@ -199,7 +199,7 @@ var title, link []byte

textHasNl := false // check whether the correct renderer exists - if (isImg && rndr.mk.Image == nil) || (!isImg && rndr.mk.Link == nil) { + if (isImg && parser.r.Image == nil) || (!isImg && parser.r.Link == nil) { return 0 }

@@ -362,7 +362,7 @@ }

// find the reference with matching id (ids are case-insensitive) key := string(bytes.ToLower(id)) - lr, ok := rndr.refs[key] + lr, ok := parser.refs[key] if !ok { return 0 }

@@ -396,7 +396,7 @@ }

// find the reference with matching id key := string(bytes.ToLower(id)) - lr, ok := rndr.refs[key] + lr, ok := parser.refs[key] if !ok { return 0 }

@@ -416,10 +416,10 @@ if isImg {

content.Write(data[1:txtE]) } else { // links cannot contain other links, so turn off link parsing temporarily - insideLink := rndr.insideLink - rndr.insideLink = true - parseInline(&content, rndr, data[1:txtE]) - rndr.insideLink = insideLink + insideLink := parser.insideLink + parser.insideLink = true + parser.parseInline(&content, data[1:txtE]) + parser.insideLink = insideLink } }

@@ -439,9 +439,9 @@ if outSize > 0 && outBytes[outSize-1] == '!' {

out.Truncate(outSize - 1) } - ret = rndr.mk.Image(out, uLink, title, content.Bytes(), rndr.mk.Opaque) + ret = parser.r.Image(out, uLink, title, content.Bytes(), parser.r.Opaque) } else { - ret = rndr.mk.Link(out, uLink, title, content.Bytes(), rndr.mk.Opaque) + ret = parser.r.Link(out, uLink, title, content.Bytes(), parser.r.Opaque) } if ret > 0 {

@@ -451,7 +451,7 @@ return 0

} // '<' when tags or autolinks are allowed -func inlineLAngle(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineLAngle(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { data = data[offset:] altype := LINK_TYPE_NOT_AUTOLINK end := tagLength(data, &altype)

@@ -459,12 +459,12 @@ ret := 0

if end > 2 { switch { - case rndr.mk.AutoLink != nil && altype != LINK_TYPE_NOT_AUTOLINK: + case parser.r.AutoLink != nil && altype != LINK_TYPE_NOT_AUTOLINK: var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) - ret = rndr.mk.AutoLink(out, uLink.Bytes(), altype, rndr.mk.Opaque) - case rndr.mk.RawHtmlTag != nil: - ret = rndr.mk.RawHtmlTag(out, data[:end], rndr.mk.Opaque) + 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) } }

@@ -477,7 +477,7 @@

// '\\' backslash escape var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>") -func inlineEscape(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineEscape(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { data = data[offset:] if len(data) > 1 {

@@ -485,8 +485,8 @@ if bytes.IndexByte(escapeChars, data[1]) < 0 {

return 0 } - if rndr.mk.NormalText != nil { - rndr.mk.NormalText(out, data[1:2], rndr.mk.Opaque) + if parser.r.NormalText != nil { + parser.r.NormalText(out, data[1:2], parser.r.Opaque) } else { out.WriteByte(data[1]) }

@@ -518,7 +518,7 @@ }

// '&' escaped when it doesn't belong to an entity // valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; -func inlineEntity(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineEntity(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { data = data[offset:] end := 1

@@ -537,8 +537,8 @@ } else {

return 0 // lone '&' } - if rndr.mk.Entity != nil { - rndr.mk.Entity(out, data[:end], rndr.mk.Opaque) + if parser.r.Entity != nil { + parser.r.Entity(out, data[:end], parser.r.Opaque) } else { out.Write(data[:end]) }

@@ -546,9 +546,9 @@

return end } -func inlineAutoLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int { +func inlineAutoLink(out *bytes.Buffer, parser *Parser, data []byte, offset int) int { // quick check to rule out most false hits on ':' - if rndr.insideLink || len(data) < offset+3 || data[offset+1] != '/' || data[offset+2] != '/' { + if parser.insideLink || len(data) < offset+3 || data[offset+1] != '/' || data[offset+2] != '/' { return 0 }

@@ -642,11 +642,11 @@ if out.Len() >= rewind {

out.Truncate(len(out.Bytes()) - rewind) } - if rndr.mk.AutoLink != nil { + if parser.r.AutoLink != nil { var uLink bytes.Buffer unescapeText(&uLink, data[:linkEnd]) - rndr.mk.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL, rndr.mk.Opaque) + parser.r.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL, parser.r.Opaque) } return linkEnd - rewind

@@ -860,10 +860,10 @@ }

return 0 } -func inlineHelperEmph1(out *bytes.Buffer, rndr *render, data []byte, c byte) int { +func inlineHelperEmph1(out *bytes.Buffer, parser *Parser, data []byte, c byte) int { i := 0 - if rndr.mk.Emphasis == nil { + if parser.r.Emphasis == nil { return 0 }

@@ -889,15 +889,15 @@ }

if data[i] == c && !isspace(data[i-1]) { - if rndr.flags&EXTENSION_NO_INTRA_EMPHASIS != 0 { + if parser.flags&EXTENSION_NO_INTRA_EMPHASIS != 0 { if !(i+1 == len(data) || isspace(data[i+1]) || ispunct(data[i+1])) { continue } } var work bytes.Buffer - parseInline(&work, rndr, data[:i]) - r := rndr.mk.Emphasis(out, work.Bytes(), rndr.mk.Opaque) + parser.parseInline(&work, data[:i]) + r := parser.r.Emphasis(out, work.Bytes(), parser.r.Opaque) if r > 0 { return i + 1 } else {

@@ -909,10 +909,10 @@

return 0 } -func inlineHelperEmph2(out *bytes.Buffer, rndr *render, data []byte, c byte) int { - renderMethod := rndr.mk.DoubleEmphasis +func inlineHelperEmph2(out *bytes.Buffer, parser *Parser, data []byte, c byte) int { + renderMethod := parser.r.DoubleEmphasis if c == '~' { - renderMethod = rndr.mk.StrikeThrough + renderMethod = parser.r.StrikeThrough } if renderMethod == nil {

@@ -930,8 +930,8 @@ i += length

if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { var work bytes.Buffer - parseInline(&work, rndr, data[:i]) - r := renderMethod(out, work.Bytes(), rndr.mk.Opaque) + parser.parseInline(&work, data[:i]) + r := renderMethod(out, work.Bytes(), parser.r.Opaque) if r > 0 { return i + 2 } else {

@@ -943,7 +943,7 @@ }

return 0 } -func inlineHelperEmph3(out *bytes.Buffer, rndr *render, data []byte, offset int, c byte) int { +func inlineHelperEmph3(out *bytes.Buffer, parser *Parser, data []byte, offset int, c byte) int { i := 0 origData := data data = data[offset:]

@@ -961,12 +961,12 @@ continue

} switch { - case (i+2 < len(data) && data[i+1] == c && data[i+2] == c && rndr.mk.TripleEmphasis != nil): + case (i+2 < len(data) && data[i+1] == c && data[i+2] == c && parser.r.TripleEmphasis != nil): // triple symbol found var work bytes.Buffer - parseInline(&work, rndr, data[:i]) - r := rndr.mk.TripleEmphasis(out, work.Bytes(), rndr.mk.Opaque) + parser.parseInline(&work, data[:i]) + r := parser.r.TripleEmphasis(out, work.Bytes(), parser.r.Opaque) if r > 0 { return i + 3 } else {

@@ -974,7 +974,7 @@ return 0

} case (i+1 < len(data) && data[i+1] == c): // double symbol found, hand over to emph1 - length = inlineHelperEmph1(out, rndr, origData[offset-2:], c) + length = inlineHelperEmph1(out, parser, origData[offset-2:], c) if length == 0 { return 0 } else {

@@ -982,7 +982,7 @@ return length - 2

} default: // single symbol found, hand over to emph2 - length = inlineHelperEmph2(out, rndr, origData[offset-1:], c) + length = inlineHelperEmph2(out, parser, origData[offset-1:], c) if length == 0 { return 0 } else {
M markdown.gomarkdown.go

@@ -143,10 +143,10 @@ // User data---passed back to every callback

Opaque interface{} } -type inlineParser func(out *bytes.Buffer, rndr *render, data []byte, offset int) int +type inlineParser func(out *bytes.Buffer, parser *Parser, data []byte, offset int) int -type render struct { - mk *Renderer +type Parser struct { + r *Renderer refs map[string]*reference inline [256]inlineParser flags int

@@ -206,40 +206,40 @@ return nil

} // fill in the render structure - rndr := new(render) - rndr.mk = renderer - rndr.flags = extensions - rndr.refs = make(map[string]*reference) - rndr.maxNesting = 16 - rndr.insideLink = false + parser := new(Parser) + parser.r = renderer + parser.flags = extensions + parser.refs = make(map[string]*reference) + parser.maxNesting = 16 + parser.insideLink = false // register inline parsers - if rndr.mk.Emphasis != nil || rndr.mk.DoubleEmphasis != nil || rndr.mk.TripleEmphasis != nil { - rndr.inline['*'] = inlineEmphasis - rndr.inline['_'] = inlineEmphasis + if parser.r.Emphasis != nil || parser.r.DoubleEmphasis != nil || parser.r.TripleEmphasis != nil { + parser.inline['*'] = inlineEmphasis + parser.inline['_'] = inlineEmphasis if extensions&EXTENSION_STRIKETHROUGH != 0 { - rndr.inline['~'] = inlineEmphasis + parser.inline['~'] = inlineEmphasis } } - if rndr.mk.CodeSpan != nil { - rndr.inline['`'] = inlineCodeSpan + if parser.r.CodeSpan != nil { + parser.inline['`'] = inlineCodeSpan } - if rndr.mk.LineBreak != nil { - rndr.inline['\n'] = inlineLineBreak + if parser.r.LineBreak != nil { + parser.inline['\n'] = inlineLineBreak } - if rndr.mk.Image != nil || rndr.mk.Link != nil { - rndr.inline['['] = inlineLink + if parser.r.Image != nil || parser.r.Link != nil { + parser.inline['['] = inlineLink } - rndr.inline['<'] = inlineLAngle - rndr.inline['\\'] = inlineEscape - rndr.inline['&'] = inlineEntity + parser.inline['<'] = inlineLAngle + parser.inline['\\'] = inlineEscape + parser.inline['&'] = inlineEntity if extensions&EXTENSION_AUTOLINK != 0 { - rndr.inline[':'] = inlineAutoLink + parser.inline[':'] = inlineAutoLink } - first := firstPass(rndr, input) - second := secondPass(rndr, first) + first := firstPass(parser, input) + second := secondPass(parser, first) return second }

@@ -249,15 +249,15 @@ // - extract references

// - expand tabs // - normalize newlines // - copy everything else -func firstPass(rndr *render, input []byte) []byte { +func firstPass(parser *Parser, input []byte) []byte { var out bytes.Buffer tabSize := TAB_SIZE_DEFAULT - if rndr.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { + if parser.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { tabSize = TAB_SIZE_EIGHT } beg, end := 0, 0 for beg < len(input) { // iterate over lines - if end = isReference(rndr, input[beg:]); end > 0 { + if end = isReference(parser, input[beg:]); end > 0 { beg += end } else { // skip to the next line end = beg

@@ -267,7 +267,7 @@ }

// add the line body if present if end > beg { - if rndr.flags&EXTENSION_NO_EXPAND_TABS == 0 { + if parser.flags&EXTENSION_NO_EXPAND_TABS == 0 { expandTabs(&out, input[beg:end], tabSize) } else { out.Write(input[beg:end])

@@ -289,19 +289,19 @@ return out.Bytes()

} // second pass: actual rendering -func secondPass(rndr *render, input []byte) []byte { +func secondPass(parser *Parser, input []byte) []byte { var output bytes.Buffer - if rndr.mk.DocumentHeader != nil { - rndr.mk.DocumentHeader(&output, rndr.mk.Opaque) + if parser.r.DocumentHeader != nil { + parser.r.DocumentHeader(&output, parser.r.Opaque) } - parseBlock(&output, rndr, input) + parser.parseBlock(&output, input) - if rndr.mk.DocumentFooter != nil { - rndr.mk.DocumentFooter(&output, rndr.mk.Opaque) + if parser.r.DocumentFooter != nil { + parser.r.DocumentFooter(&output, parser.r.Opaque) } - if rndr.nesting != 0 { + if parser.nesting != 0 { panic("Nesting level did not end at zero") }

@@ -335,7 +335,7 @@ // If so, it is parsed and stored in the list of references

// (in the render struct). // Returns the number of bytes to skip to move past it, // or zero if the first line is not a reference. -func isReference(rndr *render, data []byte) int { +func isReference(parser *Parser, data []byte) int { // up to 3 optional leading spaces if len(data) < 4 { return 0

@@ -451,13 +451,13 @@ return 0

} // a valid ref has been found - if rndr == nil { + if parser == nil { return lineEnd } // id matches are case-insensitive id := string(bytes.ToLower(data[idOffset:idEnd])) - rndr.refs[id] = &reference{ + parser.refs[id] = &reference{ link: data[linkOffset:linkEnd], title: data[titleOffset:titleEnd], }