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