123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- package parser
- import (
- "bytes"
- "path"
- "path/filepath"
- )
- // isInclude parses {{...}}[...], that contains a path between the {{, the [...] syntax contains
- // an address to select which lines to include. It is treated as an opaque string and just given
- // to readInclude.
- func (p *Parser) isInclude(data []byte) (filename string, address []byte, consumed int) {
- i := skipCharN(data, 0, ' ', 3) // start with up to 3 spaces
- if len(data[i:]) < 3 {
- return "", nil, 0
- }
- if data[i] != '{' || data[i+1] != '{' {
- return "", nil, 0
- }
- start := i + 2
- // find the end delimiter
- i = skipUntilChar(data, i, '}')
- if i+1 >= len(data) {
- return "", nil, 0
- }
- end := i
- i++
- if data[i] != '}' {
- return "", nil, 0
- }
- filename = string(data[start:end])
- if i+1 < len(data) && data[i+1] == '[' { // potential address specification
- start := i + 2
- end = skipUntilChar(data, start, ']')
- if end >= len(data) {
- return "", nil, 0
- }
- address = data[start:end]
- return filename, address, end + 1
- }
- return filename, address, i + 1
- }
- func (p *Parser) readInclude(from, file string, address []byte) []byte {
- if p.Opts.ReadIncludeFn != nil {
- return p.Opts.ReadIncludeFn(from, file, address)
- }
- return nil
- }
- // isCodeInclude parses <{{...}} which is similar to isInclude the returned bytes are, however wrapped in a code block.
- func (p *Parser) isCodeInclude(data []byte) (filename string, address []byte, consumed int) {
- i := skipCharN(data, 0, ' ', 3) // start with up to 3 spaces
- if len(data[i:]) < 3 {
- return "", nil, 0
- }
- if data[i] != '<' {
- return "", nil, 0
- }
- start := i
- filename, address, consumed = p.isInclude(data[i+1:])
- if consumed == 0 {
- return "", nil, 0
- }
- return filename, address, start + consumed + 1
- }
- // readCodeInclude acts like include except the returned bytes are wrapped in a fenced code block.
- func (p *Parser) readCodeInclude(from, file string, address []byte) []byte {
- data := p.readInclude(from, file, address)
- if data == nil {
- return nil
- }
- ext := path.Ext(file)
- buf := &bytes.Buffer{}
- buf.Write([]byte("```"))
- if ext != "" { // starts with a dot
- buf.WriteString(" " + ext[1:] + "\n")
- } else {
- buf.WriteByte('\n')
- }
- buf.Write(data)
- buf.WriteString("```\n")
- return buf.Bytes()
- }
- // incStack hold the current stack of chained includes. Each value is the containing
- // path of the file being parsed.
- type incStack struct {
- stack []string
- }
- func newIncStack() *incStack {
- return &incStack{stack: []string{}}
- }
- // Push updates i with new.
- func (i *incStack) Push(new string) {
- if path.IsAbs(new) {
- i.stack = append(i.stack, path.Dir(new))
- return
- }
- last := ""
- if len(i.stack) > 0 {
- last = i.stack[len(i.stack)-1]
- }
- i.stack = append(i.stack, path.Dir(filepath.Join(last, new)))
- }
- // Pop pops the last value.
- func (i *incStack) Pop() {
- if len(i.stack) == 0 {
- return
- }
- i.stack = i.stack[:len(i.stack)-1]
- }
- func (i *incStack) Last() string {
- if len(i.stack) == 0 {
- return ""
- }
- return i.stack[len(i.stack)-1]
- }
|