clean up main markdown function: split out first and second passes

This commit is contained in:
Russ Ross 2011-06-26 09:51:36 -06:00
parent f5e3dc8073
commit ea3d80e2d0
2 changed files with 31 additions and 25 deletions

View File

@ -23,10 +23,8 @@ const (
HTML_SKIP_STYLE HTML_SKIP_STYLE
HTML_SKIP_IMAGES HTML_SKIP_IMAGES
HTML_SKIP_LINKS HTML_SKIP_LINKS
HTML_EXPAND_TABS
HTML_SAFELINK HTML_SAFELINK
HTML_TOC HTML_TOC
HTML_HARD_WRAP
HTML_GITHUB_BLOCKCODE HTML_GITHUB_BLOCKCODE
HTML_USE_XHTML HTML_USE_XHTML
HTML_USE_SMARTYPANTS HTML_USE_SMARTYPANTS
@ -36,7 +34,7 @@ const (
type htmlOptions struct { type htmlOptions struct {
flags int flags int
closeTag string // how to end singleton tags: usually " />\n", possibly ">\n" closeTag string // how to end singleton tags: either " />\n" or ">\n"
tocData struct { tocData struct {
headerCount int headerCount int
currentLevel int currentLevel int

View File

@ -194,8 +194,19 @@ func Markdown(input []byte, renderer *Renderer, extensions uint32) []byte {
rndr.inline[':'] = inlineAutoLink rndr.inline[':'] = inlineAutoLink
} }
// first pass: look for references, copy everything else first := FirstPass(rndr, input)
var text bytes.Buffer second := SecondPass(rndr, first)
return second
}
// first pass:
// - extract references
// - expand tabs
// - normalize newlines
// - copy everything else
func FirstPass(rndr *render, input []byte) []byte {
var out bytes.Buffer
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(rndr, input[beg:]); end > 0 {
@ -208,35 +219,32 @@ func Markdown(input []byte, renderer *Renderer, extensions uint32) []byte {
// add the line body if present // add the line body if present
if end > beg { if end > beg {
expandTabs(&text, input[beg:end]) expandTabs(&out, input[beg:end])
} else {
out.WriteByte('\n')
} }
for end < len(input) && (input[end] == '\n' || input[end] == '\r') { if end < len(input) && input[end] == '\r' {
// add one \n per newline end++
if input[end] == '\n' || (end+1 < len(input) && input[end+1] != '\n') { }
text.WriteByte('\n') if end < len(input) && input[end] == '\n' {
}
end++ end++
} }
beg = end beg = end
} }
} }
return out.Bytes()
}
// second pass: actual rendering // second pass: actual rendering
func SecondPass(rndr *render, input []byte) []byte {
var output bytes.Buffer var output bytes.Buffer
if rndr.mk.DocumentHeader != nil { if rndr.mk.DocumentHeader != nil {
rndr.mk.DocumentHeader(&output, rndr.mk.Opaque) rndr.mk.DocumentHeader(&output, rndr.mk.Opaque)
} }
if text.Len() > 0 { parseBlock(&output, rndr, input)
// add a final newline if not already present
finalchar := text.Bytes()[text.Len()-1]
if finalchar != '\n' && finalchar != '\r' {
text.WriteByte('\n')
}
parseBlock(&output, rndr, text.Bytes())
}
if rndr.mk.DocumentFooter != nil { if rndr.mk.DocumentFooter != nil {
rndr.mk.DocumentFooter(&output, rndr.mk.Opaque) rndr.mk.DocumentFooter(&output, rndr.mk.Opaque)
@ -274,8 +282,8 @@ type reference struct {
// Check whether or not data starts with a reference link. // Check whether or not data starts with a reference link.
// If so, it is parsed and stored in the list of references // If so, it is parsed and stored in the list of references
// (in the render struct). // (in the render struct).
// Returns the number of bytes to skip to move past it, or zero // Returns the number of bytes to skip to move past it,
// if there is 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(rndr *render, data []byte) int {
// up to 3 optional leading spaces // up to 3 optional leading spaces
if len(data) < 4 { if len(data) < 4 {
@ -285,9 +293,6 @@ func isReference(rndr *render, data []byte) int {
for i < 3 && data[i] == ' ' { for i < 3 && data[i] == ' ' {
i++ i++
} }
if data[i] == ' ' {
return 0
}
// id part: anything but a newline between brackets // id part: anything but a newline between brackets
if data[i] != '[' { if data[i] != '[' {
@ -440,6 +445,7 @@ func isalnum(c byte) bool {
} }
// Replace tab characters with spaces, aligning to the next TAB_SIZE column. // Replace tab characters with spaces, aligning to the next TAB_SIZE column.
// always ends output with a newline
func expandTabs(out *bytes.Buffer, line []byte) { func expandTabs(out *bytes.Buffer, line []byte) {
// first, check for common cases: no tabs, or only tabs at beginning of line // first, check for common cases: no tabs, or only tabs at beginning of line
i, prefix := 0, 0 i, prefix := 0, 0
@ -461,6 +467,7 @@ func expandTabs(out *bytes.Buffer, line []byte) {
out.WriteByte(' ') out.WriteByte(' ')
} }
out.Write(line[prefix:]) out.Write(line[prefix:])
out.WriteByte('\n')
return return
} }
@ -494,4 +501,5 @@ func expandTabs(out *bytes.Buffer, line []byte) {
i++ i++
} }
out.WriteByte('\n')
} }