Codebase list golang-github-miekg-mmark / upstream/1.3.6+git20180823.4a11391 quote.go
upstream/1.3.6+git20180823.4a11391

Tree @upstream/1.3.6+git20180823.4a11391 (Download .tar.gz)

quote.go @upstream/1.3.6+git20180823.4a11391raw · history · blame

// 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
}