mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
removing more redundant checks, additional cleanup of block parsing
This commit is contained in:
parent
689f6cb79b
commit
bd60e3691b
272
block.go
272
block.go
|
@ -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
|
||||||
|
|
||||||
|
|
6
html.go
6
html.go
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user