func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { level := 0 for level < 6 && data[level] == '#' { level++ } i := skipChar(data, level, ' ') end := skipUntilChar(data, i, '\n') skip := end id := "" if p.flags&EXTENSION_HEADER_IDS != 0 { j, k := 0, 0 // find start/end of header id for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ { } for k = j + 1; k < end && data[k] != '}'; k++ { } // extract header id iff found if j < end && k < end { id = string(data[j+2 : k]) end = j skip = k + 1 for end > 0 && data[end-1] == ' ' { end-- } } } for end > 0 && data[end-1] == '#' { if isBackslashEscaped(data, end-1) { break } end-- } for end > 0 && data[end-1] == ' ' { end-- } if end > i { if id == "" && p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { id = sanitized_anchor_name.Create(string(data[i:end])) } work := func() bool { p.inline(out, data[i:end]) return true } p.r.Header(out, work, level, id) } return skip }
func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { // prev: index of 1st char of previous line // line: index of 1st char of current line // i: index of cursor/end of current line var prev, line, i int // keep going until we find something to mark the end of the paragraph for i < len(data) { // mark the beginning of the current line prev = line current := data[i:] line = i // did we find a blank line marking the end of the paragraph? if n := p.isEmpty(current); n > 0 { // did this blank line followed by a definition list item? if p.flags&EXTENSION_DEFINITION_LISTS != 0 { if i < len(data)-1 && data[i+1] == ':' { return p.list(out, data[prev:], LIST_TYPE_DEFINITION) } } p.renderParagraph(out, data[:i]) return i + n } // an underline under some text marks a header, so our paragraph ended on prev line if i > 0 { if level := p.isUnderlinedHeader(current); level > 0 { // render the paragraph p.renderParagraph(out, data[:prev]) // ignore leading and trailing whitespace eol := i - 1 for prev < eol && data[prev] == ' ' { prev++ } for eol > prev && data[eol-1] == ' ' { eol-- } // render the header // this ugly double closure avoids forcing variables onto the heap work := func(o *bytes.Buffer, pp *parser, d []byte) func() bool { return func() bool { pp.inline(o, d) return true } }(out, p, data[prev:eol]) id := "" if p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { id = sanitized_anchor_name.Create(string(data[prev:eol])) } p.r.Header(out, work, level, id) // find the end of the underline for data[i] != '\n' { i++ } return i } } // if the next line starts a block of HTML, then the paragraph ends here if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { if data[i] == '<' && p.html(out, current, false) > 0 { // rewind to before the HTML block p.renderParagraph(out, data[:i]) return i } } // if there's a prefixed header or a horizontal rule after this, paragraph is over if p.isPrefixHeader(current) || p.isHRule(current) { p.renderParagraph(out, data[:i]) return i } // if there's a definition list item, prev line is a definition term if p.flags&EXTENSION_DEFINITION_LISTS != 0 { if p.dliPrefix(current) != 0 { return p.list(out, data[prev:], LIST_TYPE_DEFINITION) } } // if there's a list after this, paragraph is over if p.flags&EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK != 0 { if p.uliPrefix(current) != 0 || p.oliPrefix(current) != 0 || p.quotePrefix(current) != 0 || p.codePrefix(current) != 0 { p.renderParagraph(out, data[:i]) return i } } // otherwise, scan to the beginning of the next line for data[i] != '\n' { i++ } i++ } p.renderParagraph(out, data[:i]) return i }