summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gomarkdown/markdown/parser/attribute.go
blob: 5fdb07095a80914e27cc8c0fda9a1ebfe0d65512 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package parser

import (
	"bytes"

	"github.com/gomarkdown/markdown/ast"
)

// attribute parses a (potential) block attribute and adds it to p.
func (p *Parser) attribute(data []byte) []byte {
	if len(data) < 3 {
		return data
	}
	i := 0
	if data[i] != '{' {
		return data
	}
	i++

	// last character must be a } otherwise it's not an attribute
	end := skipUntilChar(data, i, '\n')
	if data[end-1] != '}' {
		return data
	}

	i = skipSpace(data, i)
	b := &ast.Attribute{Attrs: make(map[string][]byte)}

	esc := false
	quote := false
	trail := 0
Loop:
	for ; i < len(data); i++ {
		switch data[i] {
		case ' ', '\t', '\f', '\v':
			if quote {
				continue
			}
			chunk := data[trail+1 : i]
			if len(chunk) == 0 {
				trail = i
				continue
			}
			switch {
			case chunk[0] == '.':
				b.Classes = append(b.Classes, chunk[1:])
			case chunk[0] == '#':
				b.ID = chunk[1:]
			default:
				k, v := keyValue(chunk)
				if k != nil && v != nil {
					b.Attrs[string(k)] = v
				} else {
					// this is illegal in an attribute
					return data
				}
			}
			trail = i
		case '"':
			if esc {
				esc = !esc
				continue
			}
			quote = !quote
		case '\\':
			esc = !esc
		case '}':
			if esc {
				esc = !esc
				continue
			}
			chunk := data[trail+1 : i]
			if len(chunk) == 0 {
				return data
			}
			switch {
			case chunk[0] == '.':
				b.Classes = append(b.Classes, chunk[1:])
			case chunk[0] == '#':
				b.ID = chunk[1:]
			default:
				k, v := keyValue(chunk)
				if k != nil && v != nil {
					b.Attrs[string(k)] = v
				} else {
					return data
				}
			}
			i++
			break Loop
		default:
			esc = false
		}
	}

	p.attr = b
	return data[i:]
}

// key="value" quotes are mandatory.
func keyValue(data []byte) ([]byte, []byte) {
	chunk := bytes.SplitN(data, []byte{'='}, 2)
	if len(chunk) != 2 {
		return nil, nil
	}
	key := chunk[0]
	value := chunk[1]

	if len(value) < 3 || len(key) == 0 {
		return nil, nil
	}
	if value[0] != '"' || value[len(value)-1] != '"' {
		return key, nil
	}
	return key, value[1 : len(value)-1]
}