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
|
package syntax
// The inline half of the Markdown scanner: what can appear inside a line of
// prose, once the line's own shape has been decided by markdown.go.
// scanMarkdownInline colours what can appear inside a line of prose.
func scanMarkdownInline(s *LineScanner) {
for !s.AtEnd() {
switch {
case s.Peek(0) == '`':
takeCodeSpan(s)
case s.Peek(0) == '!' && s.Peek(1) == '[':
takeLink(s, 1)
case s.Peek(0) == '[':
takeLink(s, 0)
case isEmphasisMarker(s.Peek(0)):
takeEmphasis(s)
default:
s.pos++
}
}
}
// isEmphasisMarker reports whether a rune can begin bold or italic text.
func isEmphasisMarker(r rune) bool { return r == '*' || r == '_' }
// takeCodeSpan colours `code`, or the lone backtick when nothing closes it.
func takeCodeSpan(s *LineScanner) {
start := s.pos
for at := s.pos + 1; at < len(s.line); at++ {
if s.line[at] == '`' {
s.pos = at + 1
s.Emit(start, s.pos, ClassString)
return
}
}
s.pos++
}
// takeEmphasis colours *italic*, **bold** and their underscore spellings.
//
// The run of markers at the start has to be matched by the same run at the
// end, so that **bold** is one span rather than an empty italic followed by
// stray text.
func takeEmphasis(s *LineScanner) {
marker := s.Peek(0)
run := 0
for s.Peek(run) == marker {
run++
}
if end := findRun(s.line, s.pos+run, marker, run); end > 0 {
start := s.pos
s.pos = end + run
s.Emit(start, s.pos, ClassEmphasis)
return
}
s.pos += run
}
// findRun returns where a run of exactly n markers starts at or after from, or
// -1 when the line holds none.
func findRun(line []rune, from int, marker rune, n int) int {
for at := from; at+n <= len(line); at++ {
if line[at] != marker {
continue
}
count := 0
for at+count < len(line) && line[at+count] == marker {
count++
}
if count == n {
return at
}
at += count - 1
}
return -1
}
// takeLink colours [text](target) and its image form .
//
// Both halves are one span: a link is one thing to the eye, and splitting the
// text from the target would put two colours on something read as a unit.
func takeLink(s *LineScanner, offset int) {
closeBracket := indexFrom(s.line, s.pos+offset+1, ']')
if closeBracket < 0 || closeBracket+1 >= len(s.line) || s.line[closeBracket+1] != '(' {
s.pos += offset + 1
return
}
closeParen := indexFrom(s.line, closeBracket+2, ')')
if closeParen < 0 {
s.pos += offset + 1
return
}
start := s.pos
s.pos = closeParen + 1
s.Emit(start, s.pos, ClassLink)
}
// indexFrom returns where a rune first appears at or after an offset, or -1.
func indexFrom(line []rune, from int, want rune) int {
for at := max(from, 0); at < len(line); at++ {
if line[at] == want {
return at
}
}
return -1
}
|