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
|
package syntax
// Block scalars: the one construct in YAML whose extent is decided by
// indentation rather than by a delimiter, and the one every CI file uses.
// yamlCarry is what a line leaves open for the next.
//
// Only a block scalar does. It is the one construct in YAML whose extent is
// decided by *indentation* rather than by a delimiter: everything indented
// further than the key that opened it belongs to it, and the first line that is
// not ends it. So the carry holds that key's indentation, and whether one is
// open at all — a depth of zero is a real indentation, not an absence.
type yamlCarry struct {
inBlock bool
// keyIndent is the column the key that opened the block starts at. A line
// indented further is inside the block.
keyIndent int
// seenBody is false until the block's first content line, whose indentation
// is not yet known. YAML lets a block be written at any deeper indent, so
// the first line establishes it rather than a fixed offset doing so.
seenBody bool
}
// continueBlockScalar colours a line belonging to an open block scalar, and
// reports whether the block swallowed it.
//
// A blank line inside a block belongs to the block whatever its indentation:
// YAML keeps empty lines in a literal scalar, and treating one as the end would
// cut a shell script in a CI file in half at its first paragraph break.
func continueBlockScalar(s *LineScanner, carry yamlCarry) (bool, yamlCarry) {
if s.Len() == 0 {
return true, carry
}
indent := leadingSpaces(s.line)
if indent == s.Len() { // a line of nothing but spaces
return true, carry
}
if !carry.seenBody {
// The first content line fixes the block's own indentation. It only
// counts as the body if it is indented past the key.
if indent <= carry.keyIndent {
return false, yamlCarry{}
}
carry.seenBody = true
carry.keyIndent = indent - 1 // anything at or past `indent` is inside
}
if indent <= carry.keyIndent {
return false, yamlCarry{}
}
s.TakeRest(ClassString)
return true, carry
}
// opensBlockScalar reports whether what is left of the line is a `|` or `>`
// header, with its optional chomping and indentation indicators.
func opensBlockScalar(s *LineScanner) bool {
if r := s.Peek(0); r != '|' && r != '>' {
return false
}
for at := 1; s.Pos()+at < s.Len(); at++ {
switch s.Peek(at) {
case '-', '+', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ':
continue
case '#':
return true // a trailing comment still leaves the block open
default:
return false
}
}
return true
}
// leadingSpaces returns how many spaces a line starts with.
func leadingSpaces(line []rune) int {
for at, r := range line {
if r != ' ' {
return at
}
}
return len(line)
}
|