all repos — grayfriday @ bb8ee591d10bb703df8501524ff5b9989364c077

blackfriday fork with a few changes

doc improvements, commenting
Russ Ross russ@russross.com
Thu, 07 Jul 2011 11:56:45 -0600
commit

bb8ee591d10bb703df8501524ff5b9989364c077

parent

256ff9468912f9ee3360107da52e8eae603e63e8

7 files changed, 263 insertions(+), 221 deletions(-)

jump to
M README.mdREADME.md

@@ -164,8 +164,6 @@ Todo

---- * More unit testing -* Code cleanup -* Better code documentation * Markdown pretty-printer output engine * Improve unicode support. It does not understand all unicode rules (about what constitutes a letter, a punctuation symbol,
M block.goblock.go

@@ -20,16 +20,16 @@

// Parse block-level data. // Note: this function and many that it calls assume that // the input buffer ends with a newline. -func (parser *Parser) block(out *bytes.Buffer, data []byte) { +func (p *parser) block(out *bytes.Buffer, data []byte) { if len(data) == 0 || data[len(data)-1] != '\n' { panic("block input is missing terminating newline") } // this is called recursively: enforce a maximum depth - if parser.nesting >= parser.maxNesting { + if p.nesting >= p.maxNesting { return } - parser.nesting++ + p.nesting++ // parse out one block-level construct at a time for len(data) > 0 {

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

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

@@ -50,14 +50,14 @@ // <div>

// ... // </div> if data[0] == '<' { - if i := parser.html(out, data, true); i > 0 { + if i := p.html(out, data, true); i > 0 { data = data[i:] continue } } // blank lines. note: returns the # of bytes to skip - if i := parser.isEmpty(data); i > 0 { + if i := p.isEmpty(data); i > 0 { data = data[i:] continue }

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

// } // return b // } - if parser.codePrefix(data) > 0 { - data = data[parser.code(out, data):] + if p.codePrefix(data) > 0 { + data = data[p.code(out, data):] continue }

@@ -85,8 +85,8 @@ // }

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

@@ -99,8 +99,8 @@ // or

// ****** // or // ______ - if parser.isHRule(data) { - parser.r.HRule(out) + if p.isHRule(data) { + p.r.HRule(out) var i int for i = 0; data[i] != '\n'; i++ { }

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

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

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

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

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

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

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

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

@@ -175,7 +175,7 @@ }

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

@@ -194,15 +194,15 @@ end--

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

@@ -238,24 +238,24 @@

return 0 } -func (parser *Parser) html(out *bytes.Buffer, data []byte, doRender bool) int { +func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int { var i, j int // identify the opening tag if data[0] != '<' { return 0 } - curtag, tagfound := parser.htmlFindTag(data[1:]) + curtag, tagfound := p.htmlFindTag(data[1:]) // handle special cases if !tagfound { // check for an HTML comment - if size := parser.htmlComment(out, data, doRender); size > 0 { + if size := p.htmlComment(out, data, doRender); size > 0 { return size } // check for an <hr> tag - if size := parser.htmlHr(out, data, doRender); size > 0 { + if size := p.htmlHr(out, data, doRender); size > 0 { return size }

@@ -278,14 +278,14 @@ break

} // see if it is the only thing on the line - if skip := parser.isEmpty(data[j:]); skip > 0 { + if skip := p.isEmpty(data[j:]); skip > 0 { // see if it is followed by a blank line/eof j += skip if j >= len(data) { found = true i = j } else { - if skip := parser.isEmpty(data[j:]); skip > 0 { + if skip := p.isEmpty(data[j:]); skip > 0 { j += skip found = true i = j

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

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

@@ -330,14 +330,14 @@ end := i

for end > 0 && data[end-1] == '\n' { end-- } - parser.r.BlockHtml(out, data[:end]) + p.r.BlockHtml(out, data[:end]) } return i } // HTML comment, lax form -func (parser *Parser) htmlComment(out *bytes.Buffer, data []byte, doRender bool) int { +func (p *parser) htmlComment(out *bytes.Buffer, data []byte, doRender bool) int { if data[0] != '<' || data[1] != '!' || data[2] != '-' || data[3] != '-' { return 0 }

@@ -356,7 +356,7 @@ return 0

} // needs to end with a blank line - if j := parser.isEmpty(data[i:]); j > 0 { + if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim trailing newlines

@@ -364,7 +364,7 @@ end := size

for end > 0 && data[end-1] == '\n' { end-- } - parser.r.BlockHtml(out, data[:end]) + p.r.BlockHtml(out, data[:end]) } return size }

@@ -373,7 +373,7 @@ return 0

} // HR, which is the only self-closing block tag considered -func (parser *Parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int { +func (p *parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int { if data[0] != '<' || (data[1] != 'h' && data[1] != 'H') || (data[2] != 'r' && data[2] != 'R') { return 0 }

@@ -389,7 +389,7 @@ }

if data[i] == '>' { i++ - if j := parser.isEmpty(data[i:]); j > 0 { + if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim newlines

@@ -397,7 +397,7 @@ end := size

for end > 0 && data[end-1] == '\n' { end-- } - parser.r.BlockHtml(out, data[:end]) + p.r.BlockHtml(out, data[:end]) } return size }

@@ -406,7 +406,7 @@

return 0 } -func (parser *Parser) htmlFindTag(data []byte) (string, bool) { +func (p *parser) htmlFindTag(data []byte) (string, bool) { i := 0 for isalnum(data[i]) { i++

@@ -418,7 +418,7 @@ }

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

@@ -430,7 +430,7 @@ i := len(closetag)

// check that the rest of the line is blank skip := 0 - if skip = parser.isEmpty(data[i:]); skip == 0 { + if skip = p.isEmpty(data[i:]); skip == 0 { return 0 } i += skip

@@ -440,10 +440,10 @@ if i >= len(data) {

return i } - if parser.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { + if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { return i } - if skip = parser.isEmpty(data[i:]); skip == 0 { + if skip = p.isEmpty(data[i:]); skip == 0 { // following line must be blank return 0 }

@@ -451,7 +451,7 @@

return i + skip } -func (parser *Parser) isEmpty(data []byte) int { +func (p *parser) isEmpty(data []byte) int { // it is okay to call isEmpty on an empty buffer if len(data) == 0 { return 0

@@ -466,7 +466,7 @@ }

return i + 1 } -func (parser *Parser) isHRule(data []byte) bool { +func (p *parser) isHRule(data []byte) bool { i := 0 // skip up to three spaces

@@ -495,7 +495,7 @@

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

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

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

@@ -597,7 +597,7 @@ for {

// safe to assume beg < len(data) // check for the end of the code block - fenceEnd, _ := parser.isFencedCode(data[beg:], nil, marker) + fenceEnd, _ := p.isFencedCode(data[beg:], nil, marker) if fenceEnd != 0 { beg += fenceEnd break

@@ -625,14 +625,14 @@ if lang != nil {

syntax = *lang } - parser.r.BlockCode(out, work.Bytes(), syntax) + p.r.BlockCode(out, work.Bytes(), syntax) return beg } -func (parser *Parser) table(out *bytes.Buffer, data []byte) int { +func (p *parser) table(out *bytes.Buffer, data []byte) int { var header bytes.Buffer - i, columns := parser.tableHeader(&header, data) + i, columns := p.tableHeader(&header, data) if i == 0 { return 0 }

@@ -654,15 +654,15 @@ }

// include the newline in data sent to tableRow i++ - parser.tableRow(&body, data[rowStart:i], columns) + p.tableRow(&body, data[rowStart:i], columns) } - parser.r.Table(out, header.Bytes(), body.Bytes(), columns) + p.r.Table(out, header.Bytes(), body.Bytes(), columns) return i } -func (parser *Parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns []int) { +func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns []int) { i := 0 colCount := 1 for i = 0; data[i] != '\n'; i++ {

@@ -757,12 +757,12 @@ if col != colCount {

return } - parser.tableRow(out, header, columns) + p.tableRow(out, header, columns) size = i + 1 return } -func (parser *Parser) tableRow(out *bytes.Buffer, data []byte, columns []int) { +func (p *parser) tableRow(out *bytes.Buffer, data []byte, columns []int) { i, col := 0, 0 var rowWork bytes.Buffer

@@ -789,22 +789,22 @@ cellEnd--

} var cellWork bytes.Buffer - parser.inline(&cellWork, data[cellStart:cellEnd]) - parser.r.TableCell(&rowWork, cellWork.Bytes(), columns[col]) + p.inline(&cellWork, data[cellStart:cellEnd]) + p.r.TableCell(&rowWork, cellWork.Bytes(), columns[col]) } // pad it out with empty columns to get the right number for ; col < len(columns); col++ { - parser.r.TableCell(&rowWork, nil, columns[col]) + p.r.TableCell(&rowWork, nil, columns[col]) } // silently ignore rows with too many cells - parser.r.TableRow(out, rowWork.Bytes()) + p.r.TableRow(out, rowWork.Bytes()) } // returns blockquote prefix length -func (parser *Parser) quotePrefix(data []byte) int { +func (p *parser) quotePrefix(data []byte) int { i := 0 for i < 3 && data[i] == ' ' { i++

@@ -819,7 +819,7 @@ return 0

} // parse a blockquote fragment -func (parser *Parser) quote(out *bytes.Buffer, data []byte) int { +func (p *parser) quote(out *bytes.Buffer, data []byte) int { var raw bytes.Buffer beg, end := 0, 0 for beg < len(data) {

@@ -829,12 +829,12 @@ end++

} end++ - if pre := parser.quotePrefix(data[beg:]); pre > 0 { - // string the prefix + if pre := p.quotePrefix(data[beg:]); pre > 0 { + // skip the prefix beg += pre - } else if parser.isEmpty(data[beg:]) > 0 && + } else if p.isEmpty(data[beg:]) > 0 && (end >= len(data) || - (parser.quotePrefix(data[end:]) == 0 && parser.isEmpty(data[end:]) == 0)) { + (p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0)) { // blockquote ends with at least one blank line // followed by something without a blockquote prefix break

@@ -846,20 +846,20 @@ beg = end

} var cooked bytes.Buffer - parser.block(&cooked, raw.Bytes()) - parser.r.BlockQuote(out, cooked.Bytes()) + p.block(&cooked, raw.Bytes()) + p.r.BlockQuote(out, cooked.Bytes()) return end } // returns prefix length for block code -func (parser *Parser) codePrefix(data []byte) int { +func (p *parser) codePrefix(data []byte) int { if data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { return 4 } return 0 } -func (parser *Parser) code(out *bytes.Buffer, data []byte) int { +func (p *parser) code(out *bytes.Buffer, data []byte) int { var work bytes.Buffer i := 0

@@ -870,8 +870,8 @@ i++

} i++ - blankline := parser.isEmpty(data[beg:i]) > 0 - if pre := parser.codePrefix(data[beg:i]); pre > 0 { + blankline := p.isEmpty(data[beg:i]) > 0 + if pre := p.codePrefix(data[beg:i]); pre > 0 { beg += pre } else if !blankline { // non-empty, non-prefixed line breaks the pre

@@ -899,13 +899,13 @@ }

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

@@ -913,7 +913,7 @@ for i < 3 && data[i] == ' ' {

i++ } - // need a *, +, or - followed by a space/tab + // need a *, +, or - followed by a space if (data[i] != '*' && data[i] != '+' && data[i] != '-') || data[i+1] != ' ' { return 0

@@ -922,7 +922,7 @@ return i + 2

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

@@ -936,7 +936,7 @@ for data[i] >= '0' && data[i] <= '9' {

i++ } - // we need >= 1 digits followed by a dot and a space/tab + // we need >= 1 digits followed by a dot and a space if start == i || data[i] != '.' || data[i+1] != ' ' { return 0 }

@@ -944,12 +944,12 @@ return i + 2

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

@@ -960,22 +960,22 @@ }

return true } - parser.r.List(out, work, flags) + p.r.List(out, work, flags) return i } // Parse a single list item. // Assumes initial prefix is already removed if this is a sublist. -func (parser *Parser) listItem(out *bytes.Buffer, data []byte, flags *int) int { +func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int { // keep track of the indentation of the first line itemIndent := 0 for itemIndent < 3 && data[itemIndent] == ' ' { itemIndent++ } - i := parser.uliPrefix(data) + i := p.uliPrefix(data) if i == 0 { - i = parser.oliPrefix(data) + i = p.oliPrefix(data) } if i == 0 { return 0

@@ -1003,7 +1003,7 @@ // process the following lines

containsBlankLine := false sublist := 0 -loop: +gatherlines: for line < len(data) { i++

@@ -1014,7 +1014,7 @@ }

// if it is an empty line, guess that it is part of this item // and move on to the next line - if parser.isEmpty(data[line:i]) > 0 { + if p.isEmpty(data[line:i]) > 0 { containsBlankLine = true line = i continue

@@ -1031,8 +1031,8 @@

// evaluate how this line fits in switch { // is this a nested list item? - case (parser.uliPrefix(chunk) > 0 && !parser.isHRule(chunk)) || - parser.oliPrefix(chunk) > 0: + case (p.uliPrefix(chunk) > 0 && !p.isHRule(chunk)) || + p.oliPrefix(chunk) > 0: if containsBlankLine { *flags |= LIST_ITEM_CONTAINS_BLOCK

@@ -1041,7 +1041,7 @@

// to be a nested list, it must be indented more // if not, it is the next item in the same list if indent <= itemIndent { - break loop + break gatherlines } // is this the first item in the the nested list?

@@ -1050,12 +1050,12 @@ sublist = raw.Len()

} // is this a nested prefix header? - case parser.isPrefixHeader(chunk): + case p.isPrefixHeader(chunk): // if the header is not indented, it is not nested in the list // and thus ends the list if containsBlankLine && indent < 4 { *flags |= LIST_ITEM_END_OF_LIST - break loop + break gatherlines } *flags |= LIST_ITEM_CONTAINS_BLOCK

@@ -1064,7 +1064,7 @@ // of this item if it is indented 4 spaces

// (regardless of the indentation of the beginning of the item) case containsBlankLine && indent < 4: *flags |= LIST_ITEM_END_OF_LIST - break loop + break gatherlines // a blank line means this should be parsed as a block case containsBlankLine:

@@ -1085,18 +1085,18 @@ var cooked bytes.Buffer

if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 { // intermediate render of block li if sublist > 0 { - parser.block(&cooked, rawBytes[:sublist]) - parser.block(&cooked, rawBytes[sublist:]) + p.block(&cooked, rawBytes[:sublist]) + p.block(&cooked, rawBytes[sublist:]) } else { - parser.block(&cooked, rawBytes) + p.block(&cooked, rawBytes) } } else { // intermediate render of inline li if sublist > 0 { - parser.inline(&cooked, rawBytes[:sublist]) - parser.block(&cooked, rawBytes[sublist:]) + p.inline(&cooked, rawBytes[:sublist]) + p.block(&cooked, rawBytes[sublist:]) } else { - parser.inline(&cooked, rawBytes) + p.inline(&cooked, rawBytes) } }

@@ -1108,13 +1108,13 @@ // strip trailing newlines

for parsedEnd > 0 && cookedBytes[parsedEnd-1] == '\n' { parsedEnd-- } - parser.r.ListItem(out, cookedBytes[:parsedEnd], *flags) + p.r.ListItem(out, cookedBytes[:parsedEnd], *flags) return line } // render a single paragraph that has already been parsed out -func (parser *Parser) renderParagraph(out *bytes.Buffer, data []byte) { +func (p *parser) renderParagraph(out *bytes.Buffer, data []byte) { if len(data) == 0 { return }

@@ -1134,13 +1134,13 @@ end--

} work := func() bool { - parser.inline(out, data[beg:end]) + p.inline(out, data[beg:end]) return true } - parser.r.Paragraph(out, work) + p.r.Paragraph(out, work) } -func (parser *Parser) paragraph(out *bytes.Buffer, data []byte) int { +func (p *parser) paragraph(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

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

line = i // did we find a blank line marking the end of the paragraph? - if n := parser.isEmpty(current); n > 0 { - parser.renderParagraph(out, data[:i]) + if n := p.isEmpty(current); n > 0 { + p.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 { - if level := parser.isUnderlinedHeader(current); level > 0 { + if level := p.isUnderlinedHeader(current); level > 0 { // render the paragraph - parser.renderParagraph(out, data[:prev]) + p.renderParagraph(out, data[:prev]) // ignore leading and trailing whitespace eol := i - 1

@@ -1176,13 +1176,13 @@ }

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

@@ -1193,17 +1193,17 @@ }

} // 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.html(out, current, false) > 0 { + if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { + if data[i] == '<' && p.html(out, current, false) > 0 { // rewind to before the HTML block - parser.renderParagraph(out, data[:i]) + p.renderParagraph(out, data[:i]) return i } } // if there's a prefixed header or a horizontal rule after this, paragraph is over - if parser.isPrefixHeader(current) || parser.isHRule(current) { - parser.renderParagraph(out, data[:i]) + if p.isPrefixHeader(current) || p.isHRule(current) { + p.renderParagraph(out, data[:i]) return i }

@@ -1214,6 +1214,6 @@ }

i++ } - parser.renderParagraph(out, data[:i]) + p.renderParagraph(out, data[:i]) return i }
M html.gohtml.go

@@ -38,6 +38,9 @@ HTML_SMARTYPANTS_FRACTIONS

HTML_SMARTYPANTS_LATEX_DASHES ) +// Html is a type that implements the Renderer interface for HTML output. +// +// Do not create this directly, instead use the HtmlRenderer function. type Html struct { flags int // HTML_* options closeTag string // how to end singleton tags: either " />\n" or ">\n"

@@ -50,7 +53,7 @@ headerCount int

currentLevel int toc *bytes.Buffer - smartypants *SmartypantsRenderer + smartypants *smartypantsRenderer } const (

@@ -58,6 +61,13 @@ xhtmlClose = " />\n"

htmlClose = ">\n" ) +// HtmlRenderer creates and configures an Html object, which +// satisfies the Renderer interface. +// +// flags is a set of HTML_* options ORed together. +// title is the title of the document, and css is a URL for the document's +// stylesheet. +// title and css are only used when HTML_COMPLETE_PAGE is selected. func HtmlRenderer(flags int, title string, css string) Renderer { // configure the rendering engine closeTag := htmlClose

@@ -75,7 +85,7 @@ headerCount: 0,

currentLevel: 0, toc: new(bytes.Buffer), - smartypants: Smartypants(flags), + smartypants: smartypants(flags), } }
M inline.goinline.go

@@ -22,21 +22,21 @@ // 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 (parser *Parser) inline(out *bytes.Buffer, data []byte) { +func (p *parser) inline(out *bytes.Buffer, data []byte) { // this is called recursively: enforce a maximum depth - if parser.nesting >= parser.maxNesting { + if p.nesting >= p.maxNesting { return } - parser.nesting++ + p.nesting++ i, end := 0, 0 for i < len(data) { // copy inactive chars into the output - for end < len(data) && parser.inlineCallback[data[end]] == nil { + for end < len(data) && p.inlineCallback[data[end]] == nil { end++ } - parser.r.NormalText(out, data[i:end]) + p.r.NormalText(out, data[i:end]) if end >= len(data) { break

@@ -44,8 +44,8 @@ }

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

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

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

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

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

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

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

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

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

@@ -102,7 +102,7 @@

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

@@ -140,7 +140,7 @@ }

// render the code span if fBegin != fEnd { - parser.r.CodeSpan(out, data[fBegin:fEnd]) + p.r.CodeSpan(out, data[fBegin:fEnd]) } return end

@@ -149,7 +149,7 @@ }

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

@@ -160,18 +160,18 @@ }

out.Truncate(eol) // should there be a hard line break here? - if parser.flags&EXTENSION_HARD_LINE_BREAK == 0 && end-eol < 2 { + if p.flags&EXTENSION_HARD_LINE_BREAK == 0 && end-eol < 2 { return 0 } - parser.r.LineBreak(out) + p.r.LineBreak(out) return 1 } // '[': parse a link or an image -func link(parser *Parser, out *bytes.Buffer, data []byte, offset int) int { +func link(p *parser, out *bytes.Buffer, data []byte, offset int) int { // no links allowed inside other links - if parser.insideLink { + if p.insideLink { return 0 }

@@ -348,7 +348,7 @@ }

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

@@ -382,7 +382,7 @@ }

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

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

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

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

out.Truncate(outSize - 1) } - parser.r.Image(out, uLink, title, content.Bytes()) + p.r.Image(out, uLink, title, content.Bytes()) } else { - parser.r.Link(out, uLink, title, content.Bytes()) + p.r.Link(out, uLink, title, content.Bytes()) } return i } // '<' when tags or autolinks are allowed -func leftAngle(parser *Parser, out *bytes.Buffer, data []byte, offset int) int { +func leftAngle(p *parser, out *bytes.Buffer, data []byte, offset int) int { data = data[offset:] altype := LINK_TYPE_NOT_AUTOLINK end := tagLength(data, &altype)

@@ -448,10 +448,10 @@ if altype != LINK_TYPE_NOT_AUTOLINK {

var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) if uLink.Len() > 0 { - parser.r.AutoLink(out, uLink.Bytes(), altype) + p.r.AutoLink(out, uLink.Bytes(), altype) } } else { - parser.r.RawHtmlTag(out, data[:end]) + p.r.RawHtmlTag(out, data[:end]) } }

@@ -461,7 +461,7 @@

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

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

return 0 } - parser.r.NormalText(out, data[1:2]) + p.r.NormalText(out, data[1:2]) } return 2

@@ -498,7 +498,7 @@ }

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

@@ -517,14 +517,14 @@ } else {

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

@@ -622,7 +622,7 @@ var uLink bytes.Buffer

unescapeText(&uLink, data[:linkEnd]) if uLink.Len() > 0 { - parser.r.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL) + p.r.AutoLink(out, uLink.Bytes(), LINK_TYPE_NORMAL) } return linkEnd - rewind

@@ -832,7 +832,7 @@ }

return 0 } -func helperEmphasis(parser *Parser, out *bytes.Buffer, data []byte, c byte) int { +func helperEmphasis(p *parser, out *bytes.Buffer, data []byte, c byte) int { i := 0 // skip one symbol if coming from emph3

@@ -857,15 +857,15 @@ }

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

@@ -873,7 +873,7 @@

return 0 } -func helperDoubleEmphasis(parser *Parser, out *bytes.Buffer, data []byte, c byte) int { +func helperDoubleEmphasis(p *parser, out *bytes.Buffer, data []byte, c byte) int { i := 0 for i < len(data) {

@@ -885,14 +885,14 @@ i += length

if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { var work bytes.Buffer - parser.inline(&work, data[:i]) + p.inline(&work, data[:i]) if work.Len() > 0 { // pick the right renderer if c == '~' { - parser.r.StrikeThrough(out, work.Bytes()) + p.r.StrikeThrough(out, work.Bytes()) } else { - parser.r.DoubleEmphasis(out, work.Bytes()) + p.r.DoubleEmphasis(out, work.Bytes()) } } return i + 2

@@ -902,7 +902,7 @@ }

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

@@ -924,14 +924,14 @@ case i+2 < len(data) && data[i+1] == c && data[i+2] == c:

// triple symbol found var work bytes.Buffer - parser.inline(&work, data[:i]) + p.inline(&work, data[:i]) if work.Len() > 0 { - parser.r.TripleEmphasis(out, work.Bytes()) + p.r.TripleEmphasis(out, work.Bytes()) } return i + 3 case (i+1 < len(data) && data[i+1] == c): // double symbol found, hand over to emph1 - length = helperEmphasis(parser, out, origData[offset-2:], c) + length = helperEmphasis(p, out, origData[offset-2:], c) if length == 0 { return 0 } else {

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

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

@@ -19,10 +19,18 @@ import (

"bytes" ) +// Latex is a type that implements the Renderer interface for LaTeX output. +// +// Do not create this directly, instead use the LatexRenderer function. type Latex struct { } +// LatexRenderer creates and configures a Latex object, which +// satisfies the Renderer interface. +// +// flags is a set of LATEX_* options ORed together (currently no such options +// are defined). func LatexRenderer(flags int) Renderer { return &Latex{} }
M markdown.gomarkdown.go

@@ -45,7 +45,7 @@ LINK_TYPE_NORMAL

LINK_TYPE_EMAIL ) -// These are the possible flag values for the listitem renderer. +// These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const (

@@ -97,8 +97,17 @@ "noscript": true,

"blockquote": true, } -// This interface defines the rendering interface. +// Renderer is the rendering interface. // This is mostly of interest if you are implementing a new rendering format. +// +// When a byte slice is provided, it contains the (rendered) contents of the +// element. +// +// When a callback is provided instead, it will write the contents of the +// respective element directly to the output buffer and return true on success. +// If the callback returns false, the rendering function should reset the +// output buffer as though it had never been called. +// // Currently Html and Latex implementations are provided type Renderer interface { // block-level callbacks

@@ -137,12 +146,11 @@ }

// Callback functions for inline parsing. One such function is defined // for each character that triggers a response when parsing inline data. -type inlineParser func(parser *Parser, out *bytes.Buffer, data []byte, offset int) int +type inlineParser func(p *parser, out *bytes.Buffer, data []byte, offset int) int -// The main parser object. -// This is constructed by the Markdown function and -// contains state used during the parsing process. -type Parser struct { +// Parser holds runtime state used by the parser. +// This is constructed by the Markdown function. +type parser struct { r Renderer refs map[string]*reference inlineCallback [256]inlineParser

@@ -159,7 +167,8 @@ // Public interface

// // -// Call Markdown with no extensions +// MarkdownBasic is a convenience function for simple rendering. +// It processes markdown input with no extensions enabled. func MarkdownBasic(input []byte) []byte { // set up the HTML renderer htmlFlags := HTML_USE_XHTML

@@ -172,6 +181,22 @@ return Markdown(input, renderer, extensions)

} // Call Markdown with most useful extensions enabled +// MarkdownCommon is a convenience function for simple rendering. +// It processes markdown input with common extensions enabled, including: +// +// * Smartypants processing with smart fractions and LaTeX dashes +// +// * Intra-word emphasis supression +// +// * Tables +// +// * Fenced code blocks +// +// * Autolinking +// +// * Strikethrough support +// +// * Strict header parsing func MarkdownCommon(input []byte) []byte { // set up the HTML renderer htmlFlags := 0

@@ -193,9 +218,13 @@

return Markdown(input, renderer, extensions) } -// 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. +// Markdown is the main rendering function. +// It parses and renders a block of markdown-encoded text. +// The supplied Renderer is used to format the output, and extensions dictates +// which non-standard extensions are enabled. +// +// To use the supplied Html or LaTeX renderers, see HtmlRenderer and +// LatexRenderer, respectively. func Markdown(input []byte, renderer Renderer, extensions int) []byte { // no point in parsing if we can't render if renderer == nil {

@@ -203,32 +232,32 @@ return nil

} // fill in the render structure - parser := new(Parser) - parser.r = renderer - parser.flags = extensions - parser.refs = make(map[string]*reference) - parser.maxNesting = 16 - parser.insideLink = false + p := new(parser) + p.r = renderer + p.flags = extensions + p.refs = make(map[string]*reference) + p.maxNesting = 16 + p.insideLink = false // register inline parsers - parser.inlineCallback['*'] = emphasis - parser.inlineCallback['_'] = emphasis + p.inlineCallback['*'] = emphasis + p.inlineCallback['_'] = emphasis if extensions&EXTENSION_STRIKETHROUGH != 0 { - parser.inlineCallback['~'] = emphasis + p.inlineCallback['~'] = emphasis } - parser.inlineCallback['`'] = codeSpan - parser.inlineCallback['\n'] = lineBreak - parser.inlineCallback['['] = link - parser.inlineCallback['<'] = leftAngle - parser.inlineCallback['\\'] = escape - parser.inlineCallback['&'] = entity + p.inlineCallback['`'] = codeSpan + p.inlineCallback['\n'] = lineBreak + p.inlineCallback['['] = link + p.inlineCallback['<'] = leftAngle + p.inlineCallback['\\'] = escape + p.inlineCallback['&'] = entity if extensions&EXTENSION_AUTOLINK != 0 { - parser.inlineCallback[':'] = autoLink + p.inlineCallback[':'] = autoLink } - first := firstPass(parser, input) - second := secondPass(parser, first) + first := firstPass(p, input) + second := secondPass(p, first) return second }

@@ -238,15 +267,15 @@ // - extract references

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

@@ -280,14 +309,14 @@ return out.Bytes()

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

@@ -321,7 +350,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(parser *Parser, data []byte) int { +func isReference(p *parser, data []byte) int { // up to 3 optional leading spaces if len(data) < 4 { return 0

@@ -437,13 +466,10 @@ return 0

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

@@ -343,10 +343,10 @@ }

type smartCallback func(out *bytes.Buffer, smrt *smartypantsData, previousChar byte, text []byte) int -type SmartypantsRenderer [256]smartCallback +type smartypantsRenderer [256]smartCallback -func Smartypants(flags int) *SmartypantsRenderer { - r := new(SmartypantsRenderer) +func smartypants(flags int) *smartypantsRenderer { + r := new(smartypantsRenderer) r['"'] = smartDoubleQuote r['&'] = smartAmp r['\''] = smartSingleQuote