removing more redundant checks, additional cleanup of block parsing

This commit is contained in:
Russ Ross 2011-07-01 14:13:26 -06:00
parent 689f6cb79b
commit bd60e3691b
2 changed files with 143 additions and 135 deletions

272
block.go
View File

@ -62,19 +62,16 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) {
continue continue
} }
// horizontal rule: // indented code block:
// //
// ------ // func max(a, b int) int {
// or // if a > b {
// ****** // return a
// or // }
// ______ // return b
if parser.isHRule(data) { // }
parser.r.HRule(out) if parser.blockCodePrefix(data) > 0 {
var i int data = data[parser.blockCode(out, data):]
for i = 0; data[i] != '\n'; i++ {
}
data = data[i:]
continue continue
} }
@ -95,17 +92,20 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) {
} }
} }
// table: // horizontal rule:
// //
// Name | Age | Phone // ------
// ------|-----|--------- // or
// Bob | 31 | 555-1234 // ******
// Alice | 27 | 555-4321 // or
if parser.flags&EXTENSION_TABLES != 0 { // ______
if i := parser.blockTable(out, data); i > 0 { if parser.isHRule(data) {
data = data[i:] parser.r.HRule(out)
continue var i int
for i = 0; data[i] != '\n'; i++ {
} }
data = data[i:]
continue
} }
// block quote: // block quote:
@ -117,17 +117,17 @@ func (parser *Parser) parseBlock(out *bytes.Buffer, data []byte) {
continue continue
} }
// indented code block: // table:
// //
// func max(a, b int) int { // Name | Age | Phone
// if a > b { // ------|-----|---------
// return a // Bob | 31 | 555-1234
// } // Alice | 27 | 555-4321
// return b if parser.flags&EXTENSION_TABLES != 0 {
// } if i := parser.blockTable(out, data); i > 0 {
if parser.blockCodePrefix(data) > 0 { data = data[i:]
data = data[parser.blockCode(out, data):] continue
continue }
} }
// an itemized/unordered list: // an itemized/unordered list:
@ -573,10 +573,11 @@ func (parser *Parser) isFencedCode(data []byte, syntax **string, oldmarker strin
*syntax = &language *syntax = &language
} }
for ; data[i] != '\n'; i++ { for data[i] == ' ' {
if !isspace(data[i]) { i++
return }
} if data[i] != '\n' {
return
} }
skip = i + 1 skip = i + 1
@ -586,41 +587,37 @@ func (parser *Parser) isFencedCode(data []byte, syntax **string, oldmarker strin
func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int { func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int {
var lang *string var lang *string
beg, marker := parser.isFencedCode(data, &lang, "") beg, marker := parser.isFencedCode(data, &lang, "")
if beg == 0 { if beg == 0 || beg >= len(data) {
return 0 return 0
} }
var work bytes.Buffer var work bytes.Buffer
for beg < len(data) { for {
// safe to assume beg < len(data)
// check for the end of the code block
fenceEnd, _ := parser.isFencedCode(data[beg:], nil, marker) fenceEnd, _ := parser.isFencedCode(data[beg:], nil, marker)
if fenceEnd != 0 { if fenceEnd != 0 {
beg += fenceEnd beg += fenceEnd
break break
} }
var end int // copy the current line
for end = beg + 1; end < len(data) && data[end-1] != '\n'; end++ { end := beg
for data[end] != '\n' {
end++
} }
end++
if beg < end { // did we reach the end of the buffer without a closing marker?
// verbatim copy to the working buffer if end >= len(data) {
if parser.isEmpty(data[beg:]) > 0 {
work.WriteByte('\n')
} else {
work.Write(data[beg:end])
}
}
beg = end
// did we find the end of the buffer without a closing marker?
if beg >= len(data) {
return 0 return 0
} }
}
if work.Len() > 0 && work.Bytes()[work.Len()-1] != '\n' { // verbatim copy to the working buffer
work.WriteByte('\n') work.Write(data[beg:end])
beg = end
} }
syntax := "" syntax := ""
@ -634,168 +631,175 @@ func (parser *Parser) blockFencedCode(out *bytes.Buffer, data []byte) int {
} }
func (parser *Parser) blockTable(out *bytes.Buffer, data []byte) int { func (parser *Parser) blockTable(out *bytes.Buffer, data []byte) int {
var headerWork bytes.Buffer var header bytes.Buffer
i, columns, colData := parser.blockTableHeader(&headerWork, data) i, columns := parser.blockTableHeader(&header, data)
if i == 0 { if i == 0 {
return 0 return 0
} }
var bodyWork bytes.Buffer var body bytes.Buffer
for i < len(data) { for i < len(data) {
pipes, rowStart := 0, i pipes, rowStart := 0, i
for ; i < len(data) && data[i] != '\n'; i++ { for ; data[i] != '\n'; i++ {
if data[i] == '|' { if data[i] == '|' {
pipes++ pipes++
} }
} }
if pipes == 0 || i == len(data) { if pipes == 0 {
i = rowStart i = rowStart
break break
} }
parser.blockTableRow(&bodyWork, data[rowStart:i], columns, colData) // include the newline in data sent to blockTableRow
i++ i++
parser.blockTableRow(&body, data[rowStart:i], columns)
} }
parser.r.Table(out, headerWork.Bytes(), bodyWork.Bytes(), colData) parser.r.Table(out, header.Bytes(), body.Bytes(), columns)
return i return i
} }
func (parser *Parser) blockTableHeader(out *bytes.Buffer, data []byte) (size int, columns int, columnData []int) { func (parser *Parser) blockTableHeader(out *bytes.Buffer, data []byte) (size int, columns []int) {
i, pipes := 0, 0 i := 0
columnData = []int{} colCount := 1
for i = 0; i < len(data) && data[i] != '\n'; i++ { for i = 0; data[i] != '\n'; i++ {
if data[i] == '|' { if data[i] == '|' {
pipes++ colCount++
} }
} }
if i == len(data) || pipes == 0 { // doesn't look like a table header
return 0, 0, columnData if colCount == 1 {
return
} }
headerEnd := i // include the newline in the data sent to blockTableRow
header := data[:i+1]
// column count ignores pipes at beginning or end of line
if data[0] == '|' { if data[0] == '|' {
pipes-- colCount--
} }
if i > 2 && data[i-1] == '|' { if i > 2 && data[i-1] == '|' {
pipes-- colCount--
} }
columns = pipes + 1 columns = make([]int, colCount)
columnData = make([]int, columns)
// parse the header underline // move on to the header underline
i++ i++
if i < len(data) && data[i] == '|' { if i >= len(data) {
return
}
if data[i] == '|' {
i++
}
for data[i] == ' ' {
i++ i++
} }
underEnd := i // each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3
for underEnd < len(data) && data[underEnd] != '\n' { // and trailing | optional on last column
underEnd++
}
col := 0 col := 0
for ; col < columns && i < underEnd; col++ { for data[i] != '\n' {
dashes := 0 dashes := 0
for i < underEnd && data[i] == ' ' {
i++
}
if data[i] == ':' { if data[i] == ':' {
i++ i++
columnData[col] |= TABLE_ALIGNMENT_LEFT columns[col] |= TABLE_ALIGNMENT_LEFT
dashes++ dashes++
} }
for data[i] == '-' {
for i < underEnd && data[i] == '-' {
i++ i++
dashes++ dashes++
} }
if data[i] == ':' {
if i < underEnd && data[i] == ':' {
i++ i++
columnData[col] |= TABLE_ALIGNMENT_RIGHT columns[col] |= TABLE_ALIGNMENT_RIGHT
dashes++ dashes++
} }
for data[i] == ' ' {
for i < underEnd && data[i] == ' ' {
i++ i++
} }
if i < underEnd && data[i] != '|' { // end of column test is messy
break switch {
} case dashes < 3:
// not a valid column
return
if dashes < 3 { case data[i] == '|':
break // marker found, now skip past trailing whitespace
} col++
i++
for data[i] == ' ' {
i++
}
i++ case data[i] != '|' && col+1 < colCount:
// something else found where marker was required
return
case data[i] == '\n':
// marker is optional for the last column
col++
default:
// trailing junk found after last column
return
}
}
if col != colCount {
return
} }
if col < columns { parser.blockTableRow(out, header, columns)
return 0, 0, columnData size = i + 1
}
parser.blockTableRow(out, data[:headerEnd], columns, columnData)
size = underEnd + 1
return return
} }
func (parser *Parser) blockTableRow(out *bytes.Buffer, data []byte, columns int, colData []int) { func (parser *Parser) blockTableRow(out *bytes.Buffer, data []byte, columns []int) {
i, col := 0, 0 i, col := 0, 0
var rowWork bytes.Buffer var rowWork bytes.Buffer
if i < len(data) && data[i] == '|' { if data[i] == '|' {
i++ i++
} }
for col = 0; col < columns && i < len(data); col++ { for col = 0; col < len(columns) && data[i] != '\n'; col++ {
for i < len(data) && isspace(data[i]) { for data[i] == ' ' {
i++ i++
} }
cellStart := i cellStart := i
for i < len(data) && data[i] != '|' { for data[i] != '|' && data[i] != '\n' {
i++ i++
} }
cellEnd := i - 1 cellEnd := i
i++
for cellEnd > cellStart && isspace(data[cellEnd]) { for cellEnd > cellStart && data[cellEnd-1] == ' ' {
cellEnd-- cellEnd--
} }
var cellWork bytes.Buffer var cellWork bytes.Buffer
parser.parseInline(&cellWork, data[cellStart:cellEnd+1]) parser.parseInline(&cellWork, data[cellStart:cellEnd])
parser.r.TableCell(&rowWork, cellWork.Bytes(), columns[col])
cdata := 0
if col < len(colData) {
cdata = colData[col]
}
parser.r.TableCell(&rowWork, cellWork.Bytes(), cdata)
i++
} }
for ; col < columns; col++ { // pad it out with empty columns to get the right number
emptyCell := []byte{} for ; col < len(columns); col++ {
cdata := 0 parser.r.TableCell(&rowWork, nil, columns[col])
if col < len(colData) {
cdata = colData[col]
}
parser.r.TableCell(&rowWork, emptyCell, cdata)
} }
// silently ignore rows with too many cells
parser.r.TableRow(out, rowWork.Bytes()) parser.r.TableRow(out, rowWork.Bytes())
} }
@ -819,8 +823,11 @@ func (parser *Parser) blockQuote(out *bytes.Buffer, data []byte) int {
var raw bytes.Buffer var raw bytes.Buffer
beg, end := 0, 0 beg, end := 0, 0
for beg < len(data) { for beg < len(data) {
for end = beg + 1; data[end-1] != '\n'; end++ { end = beg
for data[end] != '\n' {
end++
} }
end++
if pre := parser.blockQuotePrefix(data[beg:]); pre > 0 { if pre := parser.blockQuotePrefix(data[beg:]); pre > 0 {
// string the prefix // string the prefix
@ -848,12 +855,13 @@ func (parser *Parser) blockQuote(out *bytes.Buffer, data []byte) int {
// returns prefix length for block code // returns prefix length for block code
func (parser *Parser) blockCodePrefix(data []byte) int { func (parser *Parser) blockCodePrefix(data []byte) int {
if len(data) > 3 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { if data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' {
return 4 return 4
} }
return 0 return 0
} }
// TODO: continue redundant end-of-buffer check removal here
func (parser *Parser) blockCode(out *bytes.Buffer, data []byte) int { func (parser *Parser) blockCode(out *bytes.Buffer, data []byte) int {
var work bytes.Buffer var work bytes.Buffer

View File

@ -262,16 +262,16 @@ func (options *Html) Table(out *bytes.Buffer, header []byte, body []byte, column
doubleSpace(out) doubleSpace(out)
out.WriteString("<table>\n<thead>\n") out.WriteString("<table>\n<thead>\n")
out.Write(header) out.Write(header)
out.WriteString("\n</thead>\n<tbody>\n") out.WriteString("</thead>\n\n<tbody>\n")
out.Write(body) out.Write(body)
out.WriteString("\n</tbody>\n</table>\n") out.WriteString("</tbody>\n</table>\n")
} }
func (options *Html) TableRow(out *bytes.Buffer, text []byte) { func (options *Html) TableRow(out *bytes.Buffer, text []byte) {
doubleSpace(out) doubleSpace(out)
out.WriteString("<tr>\n") out.WriteString("<tr>\n")
out.Write(text) out.Write(text)
out.WriteString("\n</tr>") out.WriteString("\n</tr>\n")
} }
func (options *Html) TableCell(out *bytes.Buffer, text []byte, align int) { func (options *Html) TableCell(out *bytes.Buffer, text []byte, align int) {