render -> Parser, made parsing functions methods of *Parser

This commit is contained in:
Russ Ross 2011-06-28 18:58:53 -06:00
parent 55cde00c8a
commit f0cd9a420e
3 changed files with 232 additions and 229 deletions

259
block.go
View File

@ -18,12 +18,12 @@ import (
) )
// parse block-level data // 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 // this is called recursively: enforce a maximum depth
if rndr.nesting >= rndr.maxNesting { if parser.nesting >= parser.maxNesting {
return return
} }
rndr.nesting++ parser.nesting++
// parse out one block-level construct at a time // parse out one block-level construct at a time
for len(data) > 0 { for len(data) > 0 {
@ -33,8 +33,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// ## Header 2 // ## Header 2
// ... // ...
// ###### Header 6 // ###### Header 6
if isPrefixHeader(rndr, data) { if parser.isPrefixHeader(data) {
data = data[blockPrefixHeader(out, rndr, data):] data = data[parser.blockPrefixHeader(out, data):]
continue continue
} }
@ -43,15 +43,15 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// <div> // <div>
// ... // ...
// </div> // </div>
if data[0] == '<' && rndr.mk.BlockHtml != nil { if data[0] == '<' && parser.r.BlockHtml != nil {
if i := blockHtml(out, rndr, data, true); i > 0 { if i := parser.blockHtml(out, data, true); i > 0 {
data = data[i:] data = data[i:]
continue continue
} }
} }
// blank lines. note: returns the # of bytes to skip // 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:] data = data[i:]
continue continue
} }
@ -63,9 +63,9 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// ****** // ******
// or // or
// ______ // ______
if isHRule(data) { if parser.isHRule(data) {
if rndr.mk.HRule != nil { if parser.r.HRule != nil {
rndr.mk.HRule(out, rndr.mk.Opaque) parser.r.HRule(out, parser.r.Opaque)
} }
var i int var i int
for i = 0; i < len(data) && data[i] != '\n'; i++ { for i = 0; i < len(data) && data[i] != '\n'; i++ {
@ -84,8 +84,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// return n * fact(n-1) // return n * fact(n-1)
// } // }
// ``` // ```
if rndr.flags&EXTENSION_FENCED_CODE != 0 { if parser.flags&EXTENSION_FENCED_CODE != 0 {
if i := blockFencedCode(out, rndr, data); i > 0 { if i := parser.blockFencedCode(out, data); i > 0 {
data = data[i:] data = data[i:]
continue continue
} }
@ -97,8 +97,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// ------|-----|--------- // ------|-----|---------
// Bob | 31 | 555-1234 // Bob | 31 | 555-1234
// Alice | 27 | 555-4321 // Alice | 27 | 555-4321
if rndr.flags&EXTENSION_TABLES != 0 { if parser.flags&EXTENSION_TABLES != 0 {
if i := blockTable(out, rndr, data); i > 0 { if i := parser.blockTable(out, data); i > 0 {
data = data[i:] data = data[i:]
continue continue
} }
@ -108,8 +108,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// //
// > A big quote I found somewhere // > A big quote I found somewhere
// > on the web // > on the web
if blockQuotePrefix(data) > 0 { if parser.blockQuotePrefix(data) > 0 {
data = data[blockQuote(out, rndr, data):] data = data[parser.blockQuote(out, data):]
continue continue
} }
@ -121,8 +121,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// } // }
// return b // return b
// } // }
if blockCodePrefix(data) > 0 { if parser.blockCodePrefix(data) > 0 {
data = data[blockCode(out, rndr, data):] data = data[parser.blockCode(out, data):]
continue continue
} }
@ -132,8 +132,8 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// * Item 2 // * Item 2
// //
// also works with + or - // also works with + or -
if blockUliPrefix(data) > 0 { if parser.blockUliPrefix(data) > 0 {
data = data[blockList(out, rndr, data, 0):] data = data[parser.blockList(out, data, 0):]
continue continue
} }
@ -141,25 +141,25 @@ func parseBlock(out *bytes.Buffer, rndr *render, data []byte) {
// //
// 1. Item 1 // 1. Item 1
// 2. Item 2 // 2. Item 2
if blockOliPrefix(data) > 0 { if parser.blockOliPrefix(data) > 0 {
data = data[blockList(out, rndr, data, LIST_TYPE_ORDERED):] data = data[parser.blockList(out, data, LIST_TYPE_ORDERED):]
continue continue
} }
// anything else must look like a normal paragraph // anything else must look like a normal paragraph
// note: this finds underlined headers, too // 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] != '#' { if data[0] != '#' {
return false return false
} }
if rndr.flags&EXTENSION_SPACE_HEADERS != 0 { if parser.flags&EXTENSION_SPACE_HEADERS != 0 {
level := 0 level := 0
for level < len(data) && level < 6 && data[level] == '#' { for level < len(data) && level < 6 && data[level] == '#' {
level++ level++
@ -171,7 +171,7 @@ func isPrefixHeader(rndr *render, data []byte) bool {
return true return true
} }
func blockPrefixHeader(out *bytes.Buffer, rndr *render, data []byte) int { func (parser *Parser) blockPrefixHeader(out *bytes.Buffer, data []byte) int {
level := 0 level := 0
for level < len(data) && level < 6 && data[level] == '#' { for level < len(data) && level < 6 && data[level] == '#' {
level++ level++
@ -189,18 +189,18 @@ func blockPrefixHeader(out *bytes.Buffer, rndr *render, data []byte) int {
end-- end--
} }
if end > i { if end > i {
if rndr.mk.Header != nil { if parser.r.Header != nil {
work := func() bool { work := func() bool {
parseInline(out, rndr, data[i:end]) parser.parseInline(out, data[i:end])
return true return true
} }
rndr.mk.Header(out, work, level, rndr.mk.Opaque) parser.r.Header(out, work, level, parser.r.Opaque)
} }
} }
return skip return skip
} }
func isUnderlinedHeader(data []byte) int { func (parser *Parser) isUnderlinedHeader(data []byte) int {
i := 0 i := 0
// test of level 1 header // test of level 1 header
@ -234,14 +234,14 @@ func isUnderlinedHeader(data []byte) int {
return 0 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 var i, j int
// identify the opening tag // identify the opening tag
if len(data) < 2 || data[0] != '<' { if len(data) < 2 || data[0] != '<' {
return 0 return 0
} }
curtag, tagfound := blockHtmlFindTag(data[1:]) curtag, tagfound := parser.blockHtmlFindTag(data[1:])
// handle special cases // handle special cases
if !tagfound { if !tagfound {
@ -256,13 +256,13 @@ func blockHtml(out *bytes.Buffer, rndr *render, data []byte, doRender bool) int
i++ i++
if i < len(data) { if i < len(data) {
j = isEmpty(data[i:]) j = parser.isEmpty(data[i:])
} }
if j > 0 { if j > 0 {
size := i + j size := i + j
if doRender && rndr.mk.BlockHtml != nil { if doRender && parser.r.BlockHtml != nil {
rndr.mk.BlockHtml(out, data[:size], rndr.mk.Opaque) parser.r.BlockHtml(out, data[:size], parser.r.Opaque)
} }
return size return size
} }
@ -280,11 +280,11 @@ func blockHtml(out *bytes.Buffer, rndr *render, data []byte, doRender bool) int
if i+1 < len(data) { if i+1 < len(data) {
i++ i++
j = isEmpty(data[i:]) j = parser.isEmpty(data[i:])
if j > 0 { if j > 0 {
size := i + j size := i + j
if doRender && rndr.mk.BlockHtml != nil { if doRender && parser.r.BlockHtml != nil {
rndr.mk.BlockHtml(out, data[:size], rndr.mk.Opaque) parser.r.BlockHtml(out, data[:size], parser.r.Opaque)
} }
return size return size
} }
@ -314,7 +314,7 @@ func blockHtml(out *bytes.Buffer, rndr *render, data []byte, doRender bool) int
break break
} }
j = blockHtmlFindEnd(curtag, rndr, data[i-1:]) j = parser.blockHtmlFindEnd(curtag, data[i-1:])
if j > 0 { if j > 0 {
i += j - 1 i += j - 1
@ -329,14 +329,14 @@ func blockHtml(out *bytes.Buffer, rndr *render, data []byte, doRender bool) int
} }
// the end of the block has been found // the end of the block has been found
if doRender && rndr.mk.BlockHtml != nil { if doRender && parser.r.BlockHtml != nil {
rndr.mk.BlockHtml(out, data[:i], rndr.mk.Opaque) parser.r.BlockHtml(out, data[:i], parser.r.Opaque)
} }
return i return i
} }
func blockHtmlFindTag(data []byte) (string, bool) { func (parser *Parser) blockHtmlFindTag(data []byte) (string, bool) {
i := 0 i := 0
for i < len(data) && isalnum(data[i]) { for i < len(data) && isalnum(data[i]) {
i++ i++
@ -351,7 +351,7 @@ func blockHtmlFindTag(data []byte) (string, bool) {
return "", false 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 // assume data[0] == '<' && data[1] == '/' already tested
// check if tag is a match // check if tag is a match
@ -364,20 +364,20 @@ func blockHtmlFindEnd(tag string, rndr *render, data []byte) int {
i := len(tag) + 3 i := len(tag) + 3
w := 0 w := 0
if i < len(data) { 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 return 0 // non-blank after tag
} }
} }
i += w i += w
w = 0 w = 0
if rndr.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 {
if i < len(data) { if i < len(data) {
w = isEmpty(data[i:]) w = parser.isEmpty(data[i:])
} }
} else { } else {
if i < len(data) { 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 return 0 // non-blank line after tag line
} }
} }
@ -386,7 +386,7 @@ func blockHtmlFindEnd(tag string, rndr *render, data []byte) int {
return i + w return i + w
} }
func isEmpty(data []byte) int { func (parser *Parser) isEmpty(data []byte) int {
var i int var i int
for i = 0; i < len(data) && data[i] != '\n'; i++ { for i = 0; i < len(data) && data[i] != '\n'; i++ {
if data[i] != ' ' && data[i] != '\t' { if data[i] != ' ' && data[i] != '\t' {
@ -396,7 +396,7 @@ func isEmpty(data []byte) int {
return i + 1 return i + 1
} }
func isHRule(data []byte) bool { func (parser *Parser) isHRule(data []byte) bool {
// skip initial spaces // skip initial spaces
if len(data) < 3 { if len(data) < 3 {
return false return false
@ -429,7 +429,7 @@ func isHRule(data []byte) bool {
return n >= 3 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 i, size := 0, 0
skip = 0 skip = 0
@ -526,9 +526,9 @@ func isFencedCode(data []byte, syntax **string, oldmarker string) (skip int, mar
return return
} }
func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int { func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int {
var lang *string var lang *string
beg, marker := isFencedCode(data, &lang, "") beg, marker := parser.isFencedCode(data, &lang, "")
if beg == 0 { if beg == 0 {
return 0 return 0
} }
@ -536,7 +536,7 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
var work bytes.Buffer var work bytes.Buffer
for beg < len(data) { for beg < len(data) {
fenceEnd, _ := isFencedCode(data[beg:], nil, marker) fenceEnd, _ := parser.isFencedCode(data[beg:], nil, marker)
if fenceEnd != 0 { if fenceEnd != 0 {
beg += fenceEnd beg += fenceEnd
break break
@ -548,7 +548,7 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
if beg < end { if beg < end {
// verbatim copy to the working buffer // verbatim copy to the working buffer
if isEmpty(data[beg:]) > 0 { if parser.isEmpty(data[beg:]) > 0 {
work.WriteByte('\n') work.WriteByte('\n')
} else { } else {
work.Write(data[beg:end]) work.Write(data[beg:end])
@ -566,21 +566,21 @@ func blockFencedCode(out *bytes.Buffer, rndr *render, data []byte) int {
work.WriteByte('\n') work.WriteByte('\n')
} }
if rndr.mk.BlockCode != nil { if parser.r.BlockCode != nil {
syntax := "" syntax := ""
if lang != nil { if lang != nil {
syntax = *lang syntax = *lang
} }
rndr.mk.BlockCode(out, work.Bytes(), syntax, rndr.mk.Opaque) parser.r.BlockCode(out, work.Bytes(), syntax, parser.r.Opaque)
} }
return beg 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 var headerWork bytes.Buffer
i, columns, colData := blockTableHeader(&headerWork, rndr, data) i, columns, colData := parser.blockTableHeader(&headerWork, data)
if i == 0 { if i == 0 {
return 0 return 0
} }
@ -600,18 +600,18 @@ func blockTable(out *bytes.Buffer, rndr *render, data []byte) int {
break break
} }
blockTableRow(&bodyWork, rndr, data[rowStart:i], columns, colData) parser.blockTableRow(&bodyWork, data[rowStart:i], columns, colData)
i++ i++
} }
if rndr.mk.Table != nil { if parser.r.Table != nil {
rndr.mk.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData, rndr.mk.Opaque) parser.r.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData, parser.r.Opaque)
} }
return i 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 i, pipes := 0, 0
columnData = []int{} columnData = []int{}
for i = 0; i < len(data) && data[i] != '\n'; i++ { for i = 0; i < len(data) && data[i] != '\n'; i++ {
@ -692,12 +692,12 @@ func blockTableHeader(out *bytes.Buffer, rndr *render, data []byte) (size int, c
return 0, 0, columnData return 0, 0, columnData
} }
blockTableRow(out, rndr, data[:headerEnd], columns, columnData) parser.blockTableRow(out, data[:headerEnd], columns, columnData)
size = underEnd + 1 size = underEnd + 1
return 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 i, col := 0, 0
var rowWork bytes.Buffer var rowWork bytes.Buffer
@ -723,14 +723,14 @@ func blockTableRow(out *bytes.Buffer, rndr *render, data []byte, columns int, co
} }
var cellWork bytes.Buffer 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 cdata := 0
if col < len(colData) { if col < len(colData) {
cdata = colData[col] cdata = colData[col]
} }
rndr.mk.TableCell(&rowWork, cellWork.Bytes(), cdata, rndr.mk.Opaque) parser.r.TableCell(&rowWork, cellWork.Bytes(), cdata, parser.r.Opaque)
} }
i++ i++
@ -738,22 +738,22 @@ func blockTableRow(out *bytes.Buffer, rndr *render, data []byte, columns int, co
for ; col < columns; col++ { for ; col < columns; col++ {
emptyCell := []byte{} emptyCell := []byte{}
if rndr.mk.TableCell != nil { if parser.r.TableCell != nil {
cdata := 0 cdata := 0
if col < len(colData) { if col < len(colData) {
cdata = colData[col] 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 { if parser.r.TableRow != nil {
rndr.mk.TableRow(out, rowWork.Bytes(), rndr.mk.Opaque) parser.r.TableRow(out, rowWork.Bytes(), parser.r.Opaque)
} }
} }
// returns blockquote prefix length // returns blockquote prefix length
func blockQuotePrefix(data []byte) int { func (parser *Parser) blockQuotePrefix(data []byte) int {
i := 0 i := 0
for i < len(data) && i < 3 && data[i] == ' ' { for i < len(data) && i < 3 && data[i] == ' ' {
i++ i++
@ -768,7 +768,7 @@ func blockQuotePrefix(data []byte) int {
} }
// parse a blockquote fragment // 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 block bytes.Buffer
var work bytes.Buffer var work bytes.Buffer
beg, end := 0, 0 beg, end := 0, 0
@ -776,11 +776,13 @@ func blockQuote(out *bytes.Buffer, rndr *render, data []byte) int {
for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ { 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 beg += pre // skip prefix
} else { } else {
// empty line followed by non-quote line // 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 break
} }
} }
@ -791,15 +793,15 @@ func blockQuote(out *bytes.Buffer, rndr *render, data []byte) int {
beg = end beg = end
} }
parseBlock(&block, rndr, work.Bytes()) parser.parseBlock(&block, work.Bytes())
if rndr.mk.BlockQuote != nil { if parser.r.BlockQuote != nil {
rndr.mk.BlockQuote(out, block.Bytes(), rndr.mk.Opaque) parser.r.BlockQuote(out, block.Bytes(), parser.r.Opaque)
} }
return end return end
} }
// returns prefix length for block code // 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' { if len(data) > 0 && data[0] == '\t' {
return 1 return 1
} }
@ -809,7 +811,7 @@ func blockCodePrefix(data []byte) int {
return 0 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 var work bytes.Buffer
beg, end := 0, 0 beg, end := 0, 0
@ -817,10 +819,10 @@ func blockCode(out *bytes.Buffer, rndr *render, data []byte) int {
for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ { 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 beg += pre
} else { } else {
if isEmpty(data[beg:end]) == 0 { if parser.isEmpty(data[beg:end]) == 0 {
// non-empty non-prefixed line breaks the pre // non-empty non-prefixed line breaks the pre
break break
} }
@ -828,7 +830,7 @@ func blockCode(out *bytes.Buffer, rndr *render, data []byte) int {
if beg < end { if beg < end {
// verbatim copy to the working buffer, escaping entities // verbatim copy to the working buffer, escaping entities
if isEmpty(data[beg:end]) > 0 { if parser.isEmpty(data[beg:end]) > 0 {
work.WriteByte('\n') work.WriteByte('\n')
} else { } else {
work.Write(data[beg:end]) work.Write(data[beg:end])
@ -849,15 +851,15 @@ func blockCode(out *bytes.Buffer, rndr *render, data []byte) int {
work.WriteByte('\n') work.WriteByte('\n')
if rndr.mk.BlockCode != nil { if parser.r.BlockCode != nil {
rndr.mk.BlockCode(out, work.Bytes(), "", rndr.mk.Opaque) parser.r.BlockCode(out, work.Bytes(), "", parser.r.Opaque)
} }
return beg return beg
} }
// returns unordered list item prefix // returns unordered list item prefix
func blockUliPrefix(data []byte) int { func (parser *Parser) blockUliPrefix(data []byte) int {
i := 0 i := 0
// start with up to 3 spaces // start with up to 3 spaces
@ -875,7 +877,7 @@ func blockUliPrefix(data []byte) int {
} }
// returns ordered list item prefix // returns ordered list item prefix
func blockOliPrefix(data []byte) int { func (parser *Parser) blockOliPrefix(data []byte) int {
i := 0 i := 0
// start with up to 3 spaces // start with up to 3 spaces
@ -898,12 +900,12 @@ func blockOliPrefix(data []byte) int {
} }
// parse ordered or unordered list block // 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 i := 0
work := func() bool { work := func() bool {
j := 0 j := 0
for i < len(data) { for i < len(data) {
j = blockListItem(out, rndr, data[i:], &flags) j = parser.blockListItem(out, data[i:], &flags)
i += j i += j
if j == 0 || flags&LIST_ITEM_END_OF_LIST != 0 { if j == 0 || flags&LIST_ITEM_END_OF_LIST != 0 {
@ -913,15 +915,15 @@ func blockList(out *bytes.Buffer, rndr *render, data []byte, flags int) int {
return true return true
} }
if rndr.mk.List != nil { if parser.r.List != nil {
rndr.mk.List(out, work, flags, rndr.mk.Opaque) parser.r.List(out, work, flags, parser.r.Opaque)
} }
return i return i
} }
// parse a single list item // parse a single list item
// assumes initial prefix is already removed // 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 // keep track of the first indentation prefix
beg, end, pre, sublist, orgpre, i := 0, 0, 0, 0, 0, 0 beg, end, pre, sublist, orgpre, i := 0, 0, 0, 0, 0, 0
@ -929,9 +931,9 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
orgpre++ orgpre++
} }
beg = blockUliPrefix(data) beg = parser.blockUliPrefix(data)
if beg == 0 { if beg == 0 {
beg = blockOliPrefix(data) beg = parser.blockOliPrefix(data)
} }
if beg == 0 { if beg == 0 {
return 0 return 0
@ -966,7 +968,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
} }
// process an empty line // process an empty line
if isEmpty(data[beg:end]) > 0 { if parser.isEmpty(data[beg:end]) > 0 {
containsBlankLine = true containsBlankLine = true
beg = end beg = end
continue continue
@ -982,7 +984,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
if data[beg] == '\t' { if data[beg] == '\t' {
i = 1 i = 1
pre = TAB_SIZE_DEFAULT pre = TAB_SIZE_DEFAULT
if rndr.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { if parser.flags&EXTENSION_TAB_SIZE_EIGHT != 0 {
pre = TAB_SIZE_EIGHT pre = TAB_SIZE_EIGHT
} }
} }
@ -990,7 +992,8 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
chunk := data[beg+i : end] chunk := data[beg+i : end]
// check for a nested list item // 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 { if containsBlankLine {
containsBlock = true containsBlock = true
} }
@ -1005,7 +1008,7 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
} }
} else { } else {
// how about a nested prefix header? // how about a nested prefix header?
if isPrefixHeader(rndr, chunk) { if parser.isPrefixHeader(chunk) {
// only nest headers that are indented // only nest headers that are indented
if containsBlankLine && i < 4 && data[beg] != '\t' { if containsBlankLine && i < 4 && data[beg] != '\t' {
*flags |= LIST_ITEM_END_OF_LIST *flags |= LIST_ITEM_END_OF_LIST
@ -1042,31 +1045,31 @@ func blockListItem(out *bytes.Buffer, rndr *render, data []byte, flags *int) int
if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 { if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 {
// intermediate render of block li // intermediate render of block li
if sublist > 0 && sublist < len(workbytes) { if sublist > 0 && sublist < len(workbytes) {
parseBlock(&inter, rndr, workbytes[:sublist]) parser.parseBlock(&inter, workbytes[:sublist])
parseBlock(&inter, rndr, workbytes[sublist:]) parser.parseBlock(&inter, workbytes[sublist:])
} else { } else {
parseBlock(&inter, rndr, workbytes) parser.parseBlock(&inter, workbytes)
} }
} else { } else {
// intermediate render of inline li // intermediate render of inline li
if sublist > 0 && sublist < len(workbytes) { if sublist > 0 && sublist < len(workbytes) {
parseInline(&inter, rndr, workbytes[:sublist]) parser.parseInline(&inter, workbytes[:sublist])
parseBlock(&inter, rndr, workbytes[sublist:]) parser.parseBlock(&inter, workbytes[sublist:])
} else { } else {
parseInline(&inter, rndr, workbytes) parser.parseInline(&inter, workbytes)
} }
} }
// render li itself // render li itself
if rndr.mk.ListItem != nil { if parser.r.ListItem != nil {
rndr.mk.ListItem(out, inter.Bytes(), *flags, rndr.mk.Opaque) parser.r.ListItem(out, inter.Bytes(), *flags, parser.r.Opaque)
} }
return beg return beg
} }
// render a single paragraph that has already been parsed out // 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 // trim leading whitespace
beg := 0 beg := 0
for beg < len(data) && isspace(data[beg]) { for beg < len(data) && isspace(data[beg]) {
@ -1078,18 +1081,18 @@ func renderParagraph(out *bytes.Buffer, rndr *render, data []byte) {
for end > beg && isspace(data[end-1]) { for end > beg && isspace(data[end-1]) {
end-- end--
} }
if end == beg || rndr.mk.Paragraph == nil { if end == beg || parser.r.Paragraph == nil {
return return
} }
work := func() bool { work := func() bool {
parseInline(out, rndr, data[beg:end]) parser.parseInline(out, data[beg:end])
return true 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 // prev: index of 1st char of previous line
// line: index of 1st char of current line // line: index of 1st char of current line
// i: index of cursor/end of current line // i: index of cursor/end of current line
@ -1103,16 +1106,16 @@ func blockParagraph(out *bytes.Buffer, rndr *render, data []byte) int {
line = i line = i
// did we find a blank line marking the end of the paragraph? // did we find a blank line marking the end of the paragraph?
if n := isEmpty(current); n > 0 { if n := parser.isEmpty(current); n > 0 {
renderParagraph(out, rndr, data[:i]) parser.renderParagraph(out, data[:i])
return i + n return i + n
} }
// an underline under some text marks a header, so our paragraph ended on prev line // an underline under some text marks a header, so our paragraph ended on prev line
if i > 0 && rndr.mk.Header != nil { if i > 0 && parser.r.Header != nil {
if level := isUnderlinedHeader(current); level > 0 { if level := parser.isUnderlinedHeader(current); level > 0 {
// render the paragraph // render the paragraph
renderParagraph(out, rndr, data[:prev]) parser.renderParagraph(out, data[:prev])
// ignore leading and trailing whitespace // ignore leading and trailing whitespace
eol := i - 1 eol := i - 1
@ -1125,13 +1128,13 @@ func blockParagraph(out *bytes.Buffer, rndr *render, data []byte) int {
// render the header // render the header
// this ugly double closure avoids forcing variables onto the heap // 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 { return func() bool {
parseInline(o, r, d) p.parseInline(o, d)
return true return true
} }
}(out, rndr, data[prev:eol]) }(out, parser, data[prev:eol])
rndr.mk.Header(out, work, level, rndr.mk.Opaque) parser.r.Header(out, work, level, parser.r.Opaque)
// find the end of the underline // find the end of the underline
for ; i < len(data) && data[i] != '\n'; i++ { for ; i < len(data) && data[i] != '\n'; i++ {
@ -1141,17 +1144,17 @@ func blockParagraph(out *bytes.Buffer, rndr *render, data []byte) int {
} }
// if the next line starts a block of HTML, then the paragraph ends here // if the next line starts a block of HTML, then the paragraph ends here
if rndr.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 {
if data[i] == '<' && rndr.mk.BlockHtml != nil && blockHtml(out, rndr, current, false) > 0 { if data[i] == '<' && parser.r.BlockHtml != nil && parser.blockHtml(out, current, false) > 0 {
// rewind to before the HTML block // rewind to before the HTML block
renderParagraph(out, rndr, data[:i]) parser.renderParagraph(out, data[:i])
return i return i
} }
} }
// if there's a prefixed header or a horizontal rule after this, paragraph is over // if there's a prefixed header or a horizontal rule after this, paragraph is over
if isPrefixHeader(rndr, current) || isHRule(current) { if parser.isPrefixHeader(current) || parser.isHRule(current) {
renderParagraph(out, rndr, data[:i]) parser.renderParagraph(out, data[:i])
return i return i
} }
@ -1162,6 +1165,6 @@ func blockParagraph(out *bytes.Buffer, rndr *render, data []byte) int {
} }
} }
renderParagraph(out, rndr, data[:i]) parser.renderParagraph(out, data[:i])
return i return i
} }

124
inline.go
View File

@ -22,22 +22,22 @@ import (
// data is the complete block being rendered // data is the complete block being rendered
// offset is the number of valid chars before the current cursor // 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 // this is called recursively: enforce a maximum depth
if rndr.nesting >= rndr.maxNesting { if parser.nesting >= parser.maxNesting {
return return
} }
rndr.nesting++ parser.nesting++
i, end := 0, 0 i, end := 0, 0
for i < len(data) { for i < len(data) {
// copy inactive chars into the output // 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++ end++
} }
if rndr.mk.NormalText != nil { if parser.r.NormalText != nil {
rndr.mk.NormalText(out, data[i:end], rndr.mk.Opaque) parser.r.NormalText(out, data[i:end], parser.r.Opaque)
} else { } else {
out.Write(data[i:end]) out.Write(data[i:end])
} }
@ -48,8 +48,8 @@ func parseInline(out *bytes.Buffer, rndr *render, data []byte) {
i = end i = end
// call the trigger // call the trigger
parser := rndr.inline[data[end]] handler := parser.inline[data[end]]
if consumed := parser(out, rndr, data, i); consumed == 0 { if consumed := handler(out, parser, data, i); consumed == 0 {
// no action from the callback; buffer the byte for later // no action from the callback; buffer the byte for later
end = i + 1 end = i + 1
} else { } else {
@ -59,11 +59,11 @@ func parseInline(out *bytes.Buffer, rndr *render, data []byte) {
} }
} }
rndr.nesting-- parser.nesting--
} }
// single and double emphasis parsing // 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:] data = data[offset:]
c := data[0] c := data[0]
ret := 0 ret := 0
@ -74,7 +74,7 @@ func inlineEmphasis(out *bytes.Buffer, rndr *render, data []byte, offset int) in
if c == '~' || isspace(data[1]) { if c == '~' || isspace(data[1]) {
return 0 return 0
} }
if ret = inlineHelperEmph1(out, rndr, data[1:], c); ret == 0 { if ret = inlineHelperEmph1(out, parser, data[1:], c); ret == 0 {
return 0 return 0
} }
@ -85,7 +85,7 @@ func inlineEmphasis(out *bytes.Buffer, rndr *render, data []byte, offset int) in
if isspace(data[2]) { if isspace(data[2]) {
return 0 return 0
} }
if ret = inlineHelperEmph2(out, rndr, data[2:], c); ret == 0 { if ret = inlineHelperEmph2(out, parser, data[2:], c); ret == 0 {
return 0 return 0
} }
@ -96,7 +96,7 @@ func inlineEmphasis(out *bytes.Buffer, rndr *render, data []byte, offset int) in
if c == '~' || isspace(data[3]) { if c == '~' || isspace(data[3]) {
return 0 return 0
} }
if ret = inlineHelperEmph3(out, rndr, data, 3, c); ret == 0 { if ret = inlineHelperEmph3(out, parser, data, 3, c); ret == 0 {
return 0 return 0
} }
@ -106,7 +106,7 @@ func inlineEmphasis(out *bytes.Buffer, rndr *render, data []byte, offset int) in
return 0 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:] data = data[offset:]
nb := 0 nb := 0
@ -143,10 +143,10 @@ func inlineCodeSpan(out *bytes.Buffer, rndr *render, data []byte, offset int) in
} }
// render the code span // render the code span
if rndr.mk.CodeSpan == nil { if parser.r.CodeSpan == nil {
return 0 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 end = 0
} }
@ -156,7 +156,7 @@ func inlineCodeSpan(out *bytes.Buffer, rndr *render, data []byte, offset int) in
// newline preceded by two spaces becomes <br> // newline preceded by two spaces becomes <br>
// newline without two spaces works when EXTENSION_HARD_LINE_BREAK is enabled // 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 // remove trailing spaces from out
outBytes := out.Bytes() outBytes := out.Bytes()
end := len(outBytes) end := len(outBytes)
@ -167,14 +167,14 @@ func inlineLineBreak(out *bytes.Buffer, rndr *render, data []byte, offset int) i
out.Truncate(eol) out.Truncate(eol)
// should there be a hard line break here? // 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 return 0
} }
if rndr.mk.LineBreak == nil { if parser.r.LineBreak == nil {
return 0 return 0
} }
if rndr.mk.LineBreak(out, rndr.mk.Opaque) > 0 { if parser.r.LineBreak(out, parser.r.Opaque) > 0 {
return 1 return 1
} else { } else {
return 0 return 0
@ -184,9 +184,9 @@ func inlineLineBreak(out *bytes.Buffer, rndr *render, data []byte, offset int) i
} }
// '[': parse a link or an image // '[': 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 // no links allowed inside other links
if rndr.insideLink { if parser.insideLink {
return 0 return 0
} }
@ -199,7 +199,7 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
textHasNl := false textHasNl := false
// check whether the correct renderer exists // 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 return 0
} }
@ -362,7 +362,7 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
// find the reference with matching id (ids are case-insensitive) // find the reference with matching id (ids are case-insensitive)
key := string(bytes.ToLower(id)) key := string(bytes.ToLower(id))
lr, ok := rndr.refs[key] lr, ok := parser.refs[key]
if !ok { if !ok {
return 0 return 0
} }
@ -396,7 +396,7 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
// find the reference with matching id // find the reference with matching id
key := string(bytes.ToLower(id)) key := string(bytes.ToLower(id))
lr, ok := rndr.refs[key] lr, ok := parser.refs[key]
if !ok { if !ok {
return 0 return 0
} }
@ -416,10 +416,10 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
content.Write(data[1:txtE]) content.Write(data[1:txtE])
} else { } else {
// links cannot contain other links, so turn off link parsing temporarily // links cannot contain other links, so turn off link parsing temporarily
insideLink := rndr.insideLink insideLink := parser.insideLink
rndr.insideLink = true parser.insideLink = true
parseInline(&content, rndr, data[1:txtE]) parser.parseInline(&content, data[1:txtE])
rndr.insideLink = insideLink parser.insideLink = insideLink
} }
} }
@ -439,9 +439,9 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
out.Truncate(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 { } 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 { if ret > 0 {
@ -451,7 +451,7 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
} }
// '<' when tags or autolinks are allowed // '<' 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:] data = data[offset:]
altype := LINK_TYPE_NOT_AUTOLINK altype := LINK_TYPE_NOT_AUTOLINK
end := tagLength(data, &altype) end := tagLength(data, &altype)
@ -459,12 +459,12 @@ func inlineLAngle(out *bytes.Buffer, rndr *render, data []byte, offset int) int
if end > 2 { if end > 2 {
switch { 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 var uLink bytes.Buffer
unescapeText(&uLink, data[1:end+1-2]) unescapeText(&uLink, data[1:end+1-2])
ret = rndr.mk.AutoLink(out, uLink.Bytes(), altype, rndr.mk.Opaque) ret = parser.r.AutoLink(out, uLink.Bytes(), altype, parser.r.Opaque)
case rndr.mk.RawHtmlTag != nil: case parser.r.RawHtmlTag != nil:
ret = rndr.mk.RawHtmlTag(out, data[:end], rndr.mk.Opaque) ret = parser.r.RawHtmlTag(out, data[:end], parser.r.Opaque)
} }
} }
@ -477,7 +477,7 @@ func inlineLAngle(out *bytes.Buffer, rndr *render, data []byte, offset int) int
// '\\' backslash escape // '\\' backslash escape
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>") 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:] data = data[offset:]
if len(data) > 1 { if len(data) > 1 {
@ -485,8 +485,8 @@ func inlineEscape(out *bytes.Buffer, rndr *render, data []byte, offset int) int
return 0 return 0
} }
if rndr.mk.NormalText != nil { if parser.r.NormalText != nil {
rndr.mk.NormalText(out, data[1:2], rndr.mk.Opaque) parser.r.NormalText(out, data[1:2], parser.r.Opaque)
} else { } else {
out.WriteByte(data[1]) out.WriteByte(data[1])
} }
@ -518,7 +518,7 @@ func unescapeText(ob *bytes.Buffer, src []byte) {
// '&' escaped when it doesn't belong to an entity // '&' escaped when it doesn't belong to an entity
// valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; // 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:] data = data[offset:]
end := 1 end := 1
@ -537,8 +537,8 @@ func inlineEntity(out *bytes.Buffer, rndr *render, data []byte, offset int) int
return 0 // lone '&' return 0 // lone '&'
} }
if rndr.mk.Entity != nil { if parser.r.Entity != nil {
rndr.mk.Entity(out, data[:end], rndr.mk.Opaque) parser.r.Entity(out, data[:end], parser.r.Opaque)
} else { } else {
out.Write(data[:end]) out.Write(data[:end])
} }
@ -546,9 +546,9 @@ func inlineEntity(out *bytes.Buffer, rndr *render, data []byte, offset int) int
return end 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 ':' // 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 return 0
} }
@ -642,11 +642,11 @@ func inlineAutoLink(out *bytes.Buffer, rndr *render, data []byte, offset int) in
out.Truncate(len(out.Bytes()) - rewind) out.Truncate(len(out.Bytes()) - rewind)
} }
if rndr.mk.AutoLink != nil { if parser.r.AutoLink != nil {
var uLink bytes.Buffer var uLink bytes.Buffer
unescapeText(&uLink, data[:linkEnd]) 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 return linkEnd - rewind
@ -860,10 +860,10 @@ func inlineHelperFindEmphChar(data []byte, c byte) int {
return 0 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 i := 0
if rndr.mk.Emphasis == nil { if parser.r.Emphasis == nil {
return 0 return 0
} }
@ -889,15 +889,15 @@ func inlineHelperEmph1(out *bytes.Buffer, rndr *render, data []byte, c byte) int
if data[i] == c && !isspace(data[i-1]) { 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])) { if !(i+1 == len(data) || isspace(data[i+1]) || ispunct(data[i+1])) {
continue continue
} }
} }
var work bytes.Buffer var work bytes.Buffer
parseInline(&work, rndr, data[:i]) parser.parseInline(&work, data[:i])
r := rndr.mk.Emphasis(out, work.Bytes(), rndr.mk.Opaque) r := parser.r.Emphasis(out, work.Bytes(), parser.r.Opaque)
if r > 0 { if r > 0 {
return i + 1 return i + 1
} else { } else {
@ -909,10 +909,10 @@ func inlineHelperEmph1(out *bytes.Buffer, rndr *render, data []byte, c byte) int
return 0 return 0
} }
func inlineHelperEmph2(out *bytes.Buffer, rndr *render, data []byte, c byte) int { func inlineHelperEmph2(out *bytes.Buffer, parser *Parser, data []byte, c byte) int {
renderMethod := rndr.mk.DoubleEmphasis renderMethod := parser.r.DoubleEmphasis
if c == '~' { if c == '~' {
renderMethod = rndr.mk.StrikeThrough renderMethod = parser.r.StrikeThrough
} }
if renderMethod == nil { if renderMethod == nil {
@ -930,8 +930,8 @@ func inlineHelperEmph2(out *bytes.Buffer, rndr *render, data []byte, c byte) int
if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) {
var work bytes.Buffer var work bytes.Buffer
parseInline(&work, rndr, data[:i]) parser.parseInline(&work, data[:i])
r := renderMethod(out, work.Bytes(), rndr.mk.Opaque) r := renderMethod(out, work.Bytes(), parser.r.Opaque)
if r > 0 { if r > 0 {
return i + 2 return i + 2
} else { } else {
@ -943,7 +943,7 @@ func inlineHelperEmph2(out *bytes.Buffer, rndr *render, data []byte, c byte) int
return 0 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 i := 0
origData := data origData := data
data = data[offset:] data = data[offset:]
@ -961,12 +961,12 @@ func inlineHelperEmph3(out *bytes.Buffer, rndr *render, data []byte, offset int,
} }
switch { 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 // triple symbol found
var work bytes.Buffer var work bytes.Buffer
parseInline(&work, rndr, data[:i]) parser.parseInline(&work, data[:i])
r := rndr.mk.TripleEmphasis(out, work.Bytes(), rndr.mk.Opaque) r := parser.r.TripleEmphasis(out, work.Bytes(), parser.r.Opaque)
if r > 0 { if r > 0 {
return i + 3 return i + 3
} else { } else {
@ -974,7 +974,7 @@ func inlineHelperEmph3(out *bytes.Buffer, rndr *render, data []byte, offset int,
} }
case (i+1 < len(data) && data[i+1] == c): case (i+1 < len(data) && data[i+1] == c):
// double symbol found, hand over to emph1 // 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 { if length == 0 {
return 0 return 0
} else { } else {
@ -982,7 +982,7 @@ func inlineHelperEmph3(out *bytes.Buffer, rndr *render, data []byte, offset int,
} }
default: default:
// single symbol found, hand over to emph2 // 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 { if length == 0 {
return 0 return 0
} else { } else {

View File

@ -143,10 +143,10 @@ type Renderer struct {
Opaque interface{} 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 { type Parser struct {
mk *Renderer r *Renderer
refs map[string]*reference refs map[string]*reference
inline [256]inlineParser inline [256]inlineParser
flags int flags int
@ -206,40 +206,40 @@ func Markdown(input []byte, renderer *Renderer, extensions int) []byte {
} }
// fill in the render structure // fill in the render structure
rndr := new(render) parser := new(Parser)
rndr.mk = renderer parser.r = renderer
rndr.flags = extensions parser.flags = extensions
rndr.refs = make(map[string]*reference) parser.refs = make(map[string]*reference)
rndr.maxNesting = 16 parser.maxNesting = 16
rndr.insideLink = false parser.insideLink = false
// register inline parsers // register inline parsers
if rndr.mk.Emphasis != nil || rndr.mk.DoubleEmphasis != nil || rndr.mk.TripleEmphasis != nil { if parser.r.Emphasis != nil || parser.r.DoubleEmphasis != nil || parser.r.TripleEmphasis != nil {
rndr.inline['*'] = inlineEmphasis parser.inline['*'] = inlineEmphasis
rndr.inline['_'] = inlineEmphasis parser.inline['_'] = inlineEmphasis
if extensions&EXTENSION_STRIKETHROUGH != 0 { if extensions&EXTENSION_STRIKETHROUGH != 0 {
rndr.inline['~'] = inlineEmphasis parser.inline['~'] = inlineEmphasis
} }
} }
if rndr.mk.CodeSpan != nil { if parser.r.CodeSpan != nil {
rndr.inline['`'] = inlineCodeSpan parser.inline['`'] = inlineCodeSpan
} }
if rndr.mk.LineBreak != nil { if parser.r.LineBreak != nil {
rndr.inline['\n'] = inlineLineBreak parser.inline['\n'] = inlineLineBreak
} }
if rndr.mk.Image != nil || rndr.mk.Link != nil { if parser.r.Image != nil || parser.r.Link != nil {
rndr.inline['['] = inlineLink parser.inline['['] = inlineLink
} }
rndr.inline['<'] = inlineLAngle parser.inline['<'] = inlineLAngle
rndr.inline['\\'] = inlineEscape parser.inline['\\'] = inlineEscape
rndr.inline['&'] = inlineEntity parser.inline['&'] = inlineEntity
if extensions&EXTENSION_AUTOLINK != 0 { if extensions&EXTENSION_AUTOLINK != 0 {
rndr.inline[':'] = inlineAutoLink parser.inline[':'] = inlineAutoLink
} }
first := firstPass(rndr, input) first := firstPass(parser, input)
second := secondPass(rndr, first) second := secondPass(parser, first)
return second return second
} }
@ -249,15 +249,15 @@ func Markdown(input []byte, renderer *Renderer, extensions int) []byte {
// - expand tabs // - expand tabs
// - normalize newlines // - normalize newlines
// - copy everything else // - copy everything else
func firstPass(rndr *render, input []byte) []byte { func firstPass(parser *Parser, input []byte) []byte {
var out bytes.Buffer var out bytes.Buffer
tabSize := TAB_SIZE_DEFAULT tabSize := TAB_SIZE_DEFAULT
if rndr.flags&EXTENSION_TAB_SIZE_EIGHT != 0 { if parser.flags&EXTENSION_TAB_SIZE_EIGHT != 0 {
tabSize = TAB_SIZE_EIGHT tabSize = TAB_SIZE_EIGHT
} }
beg, end := 0, 0 beg, end := 0, 0
for beg < len(input) { // iterate over lines 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 beg += end
} else { // skip to the next line } else { // skip to the next line
end = beg end = beg
@ -267,7 +267,7 @@ func firstPass(rndr *render, input []byte) []byte {
// add the line body if present // add the line body if present
if end > beg { 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) expandTabs(&out, input[beg:end], tabSize)
} else { } else {
out.Write(input[beg:end]) out.Write(input[beg:end])
@ -289,19 +289,19 @@ func firstPass(rndr *render, input []byte) []byte {
} }
// second pass: actual rendering // second pass: actual rendering
func secondPass(rndr *render, input []byte) []byte { func secondPass(parser *Parser, input []byte) []byte {
var output bytes.Buffer var output bytes.Buffer
if rndr.mk.DocumentHeader != nil { if parser.r.DocumentHeader != nil {
rndr.mk.DocumentHeader(&output, rndr.mk.Opaque) parser.r.DocumentHeader(&output, parser.r.Opaque)
} }
parseBlock(&output, rndr, input) parser.parseBlock(&output, input)
if rndr.mk.DocumentFooter != nil { if parser.r.DocumentFooter != nil {
rndr.mk.DocumentFooter(&output, rndr.mk.Opaque) parser.r.DocumentFooter(&output, parser.r.Opaque)
} }
if rndr.nesting != 0 { if parser.nesting != 0 {
panic("Nesting level did not end at zero") panic("Nesting level did not end at zero")
} }
@ -335,7 +335,7 @@ type reference struct {
// (in the render struct). // (in the render struct).
// Returns the number of bytes to skip to move past it, // Returns the number of bytes to skip to move past it,
// or zero if the first line is not a reference. // 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 // up to 3 optional leading spaces
if len(data) < 4 { if len(data) < 4 {
return 0 return 0
@ -451,13 +451,13 @@ func isReference(rndr *render, data []byte) int {
} }
// a valid ref has been found // a valid ref has been found
if rndr == nil { if parser == nil {
return lineEnd return lineEnd
} }
// id matches are case-insensitive // id matches are case-insensitive
id := string(bytes.ToLower(data[idOffset:idEnd])) id := string(bytes.ToLower(data[idOffset:idEnd]))
rndr.refs[id] = &reference{ parser.refs[id] = &reference{
link: data[linkOffset:linkEnd], link: data[linkOffset:linkEnd],
title: data[titleOffset:titleEnd], title: data[titleOffset:titleEnd],
} }