figures.go

  1package parser
  2
  3import (
  4	"bytes"
  5
  6	"github.com/gomarkdown/markdown/ast"
  7)
  8
  9// sFigureLine checks if there's a figure line (e.g., !--- ) at the beginning of data,
 10// and returns the end index if so, or 0 otherwise.
 11func sFigureLine(data []byte, oldmarker string) (end int, marker string) {
 12	i, size := 0, 0
 13
 14	n := len(data)
 15	// skip up to three spaces
 16	for i < n && i < 3 && data[i] == ' ' {
 17		i++
 18	}
 19
 20	// check for the marker characters: !
 21	if i+1 >= n {
 22		return 0, ""
 23	}
 24	if data[i] != '!' || data[i+1] != '-' {
 25		return 0, ""
 26	}
 27	i++
 28
 29	c := data[i] // i.e. the -
 30
 31	// the whole line must be the same char or whitespace
 32	for i < n && data[i] == c {
 33		size++
 34		i++
 35	}
 36
 37	// the marker char must occur at least 3 times
 38	if size < 3 {
 39		return 0, ""
 40	}
 41	marker = string(data[i-size : i])
 42
 43	// if this is the end marker, it must match the beginning marker
 44	if oldmarker != "" && marker != oldmarker {
 45		return 0, ""
 46	}
 47
 48	// there is no syntax modifier although it might be an idea to re-use this space for something?
 49
 50	i = skipChar(data, i, ' ')
 51	if i >= n || data[i] != '\n' {
 52		if i == n {
 53			return i, marker
 54		}
 55		return 0, ""
 56	}
 57	return i + 1, marker // Take newline into account.
 58}
 59
 60// figureBlock returns the end index if data contains a figure block at the beginning,
 61// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
 62// If doRender is true, a final newline is mandatory to recognize the figure block.
 63func (p *Parser) figureBlock(data []byte, doRender bool) int {
 64	beg, marker := sFigureLine(data, "")
 65	if beg == 0 || beg >= len(data) {
 66		return 0
 67	}
 68
 69	var raw bytes.Buffer
 70
 71	for {
 72		// safe to assume beg < len(data)
 73
 74		// check for the end of the code block
 75		figEnd, _ := sFigureLine(data[beg:], marker)
 76		if figEnd != 0 {
 77			beg += figEnd
 78			break
 79		}
 80
 81		// copy the current line
 82		end := skipUntilChar(data, beg, '\n') + 1
 83
 84		// did we reach the end of the buffer without a closing marker?
 85		if end >= len(data) {
 86			return 0
 87		}
 88
 89		// verbatim copy to the working buffer
 90		if doRender {
 91			raw.Write(data[beg:end])
 92		}
 93		beg = end
 94	}
 95
 96	if !doRender {
 97		return beg
 98	}
 99
100	figure := &ast.CaptionFigure{}
101	p.addBlock(figure)
102	p.block(raw.Bytes())
103
104	defer p.finalize(figure)
105
106	if captionContent, id, consumed := p.caption(data[beg:], []byte("Figure: ")); consumed > 0 {
107		caption := &ast.Caption{}
108		p.Inline(caption, captionContent)
109
110		figure.HeadingID = id
111
112		p.addChild(caption)
113
114		beg += consumed
115	}
116
117	p.finalize(figure)
118	return beg
119}