220 lines
4.5 KiB
Go
220 lines
4.5 KiB
Go
// Functions to parse quote-like elements.
|
|
|
|
package mmark
|
|
|
|
import "bytes"
|
|
|
|
// returns asidequote prefix length
|
|
func (p *parser) asidePrefix(data []byte) int {
|
|
i := 0
|
|
for i < 3 && data[i] == ' ' {
|
|
i++
|
|
}
|
|
if data[i] == 'A' && data[i+1] == '>' {
|
|
if data[i+2] == ' ' {
|
|
return i + 3
|
|
}
|
|
return i + 2
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// parse an aside fragment
|
|
func (p *parser) aside(out *bytes.Buffer, data []byte) int {
|
|
var raw bytes.Buffer
|
|
beg, end := 0, 0
|
|
for beg < len(data) {
|
|
end = beg
|
|
for data[end] != '\n' {
|
|
end++
|
|
}
|
|
end++
|
|
|
|
if pre := p.asidePrefix(data[beg:]); pre > 0 {
|
|
// skip the prefix
|
|
beg += pre
|
|
} else if p.isEmpty(data[beg:]) > 0 &&
|
|
(end >= len(data) ||
|
|
(p.asidePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0)) {
|
|
break
|
|
}
|
|
raw.Write(data[beg:end])
|
|
beg = end
|
|
}
|
|
|
|
var cooked bytes.Buffer
|
|
p.block(&cooked, raw.Bytes())
|
|
|
|
p.r.SetAttr(p.ial)
|
|
p.ial = nil
|
|
|
|
p.r.Aside(out, cooked.Bytes())
|
|
return end
|
|
}
|
|
|
|
// returns blockquote prefix length
|
|
func (p *parser) quotePrefix(data []byte) int {
|
|
i := 0
|
|
for i < 3 && data[i] == ' ' {
|
|
i++
|
|
}
|
|
if data[i] == '>' {
|
|
if data[i+1] == ' ' {
|
|
return i + 2
|
|
}
|
|
return i + 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// blockquote ends with at least one blank line
|
|
// followed by something without a blockquote prefix
|
|
func (p *parser) terminateBlockquote(data []byte, beg, end int) bool {
|
|
if p.isEmpty(data[beg:]) <= 0 {
|
|
return false
|
|
}
|
|
if end >= len(data) {
|
|
return true
|
|
}
|
|
return p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0
|
|
}
|
|
|
|
// parse a blockquote fragment
|
|
func (p *parser) quote(out *bytes.Buffer, data []byte) int {
|
|
var raw bytes.Buffer
|
|
beg, end := 0, 0
|
|
for beg < len(data) {
|
|
end = beg
|
|
// Step over whole lines, collecting them. While doing that, check for
|
|
// fenced code and if one's found, incorporate it altogether,
|
|
// irregardless of any contents inside it
|
|
for data[end] != '\n' {
|
|
if p.flags&EXTENSION_FENCED_CODE != 0 {
|
|
if i := p.fencedCode(out, data[end:], false); i > 0 {
|
|
// -1 to compensate for the extra end++ after the loop:
|
|
end += i - 1
|
|
break
|
|
}
|
|
}
|
|
end++
|
|
}
|
|
end++
|
|
|
|
if pre := p.quotePrefix(data[beg:]); pre > 0 {
|
|
// skip the prefix
|
|
beg += pre
|
|
} else if bytes.HasPrefix(data[beg:], []byte("Quote: ")) {
|
|
break
|
|
} else if p.terminateBlockquote(data, beg, end) {
|
|
break
|
|
}
|
|
|
|
// this line is part of the blockquote
|
|
raw.Write(data[beg:end])
|
|
beg = end
|
|
}
|
|
var attribution bytes.Buffer
|
|
line := beg
|
|
j := beg
|
|
if bytes.HasPrefix(data[j:], []byte("Quote: ")) {
|
|
for line < len(data) {
|
|
j++
|
|
// find the end of this line
|
|
for data[j-1] != '\n' {
|
|
j++
|
|
}
|
|
if p.isEmpty(data[line:j]) > 0 {
|
|
break
|
|
}
|
|
line = j
|
|
}
|
|
p.inline(&attribution, data[beg+7:j-1]) // +7 for 'Quote: '
|
|
}
|
|
ials := p.ial
|
|
p.ial = nil
|
|
|
|
// This set a new level of attributes, we could possible override what
|
|
// we have gotten above. TODO(miek): this might need to happen in more
|
|
// places.
|
|
var cooked bytes.Buffer
|
|
p.block(&cooked, raw.Bytes())
|
|
|
|
p.r.SetAttr(ials)
|
|
|
|
p.r.BlockQuote(out, cooked.Bytes(), attribution.Bytes())
|
|
return j
|
|
}
|
|
|
|
// returns figurequote prefix length
|
|
func (p *parser) figurePrefix(data []byte) int {
|
|
i := 0
|
|
for i < 3 && data[i] == ' ' {
|
|
i++
|
|
}
|
|
if data[i] == 'F' && data[i+1] == '>' {
|
|
if data[i+2] == ' ' {
|
|
return i + 3
|
|
}
|
|
return i + 2
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// parse a figurequote fragment
|
|
func (p *parser) figure(out *bytes.Buffer, data []byte) int {
|
|
var raw bytes.Buffer
|
|
beg, end := 0, 0
|
|
for beg < len(data) {
|
|
end = beg
|
|
for data[end] != '\n' {
|
|
end++
|
|
}
|
|
end++
|
|
|
|
if pre := p.figurePrefix(data[beg:]); pre > 0 {
|
|
// skip the prefix
|
|
beg += pre
|
|
} else if bytes.HasPrefix(data[beg:], []byte("Figure: ")) {
|
|
break
|
|
} else if p.isEmpty(data[beg:]) > 0 &&
|
|
(end >= len(data) ||
|
|
(p.figurePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0)) {
|
|
// figurequote ends with at least one blank line
|
|
// followed by something without a figurequote prefix
|
|
break
|
|
}
|
|
|
|
// this line is part of the figurequote
|
|
raw.Write(data[beg:end])
|
|
beg = end
|
|
}
|
|
var caption bytes.Buffer
|
|
line := beg
|
|
j := beg
|
|
// this one must start on j
|
|
if bytes.HasPrefix(data[j:], []byte("Figure: ")) {
|
|
for line < len(data) {
|
|
j++
|
|
// find the end of this line
|
|
for data[j-1] != '\n' {
|
|
j++
|
|
}
|
|
if p.isEmpty(data[line:j]) > 0 {
|
|
break
|
|
}
|
|
line = j
|
|
}
|
|
p.inline(&caption, data[beg+8:j-1]) // +8 for 'Figure: '
|
|
}
|
|
|
|
p.insideFigure = true
|
|
var cooked bytes.Buffer
|
|
p.block(&cooked, raw.Bytes())
|
|
p.insideFigure = false
|
|
|
|
p.r.SetAttr(p.ial)
|
|
p.ial = nil
|
|
|
|
p.r.Figure(out, cooked.Bytes(), caption.Bytes())
|
|
return j
|
|
}
|