turbo-editors/turbo-corepublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

yaml_key.go · 109 lines · 3.1 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 20h ago1package syntax
2
3import "strings"
4
5// Keys and bare words: telling `image: nginx` from a scalar that merely
6// contains a colon, which is most of what makes YAML awkward to scan.
7
8// takeYAMLKey colours a mapping key and its colon, when the line has one.
9//
10// The key is recognised only when a colon follows it before any space, which is
11// what tells `image: nginx` from a bare scalar such as `nginx:latest` in a list
12// entry — the second has no space after its colon and is a value, not a key.
13func takeYAMLKey(s *LineScanner) {
14 end, ok := yamlKeyEnd(s)
15 if !ok {
16 return
17 }
18
19 s.Emit(s.Pos(), end, ClassIdentifier)
20 s.Advance(end - s.Pos())
21 s.Take(1, ClassPunctuation) // the colon
22 s.SkipSpaces()
23}
24
25// yamlKeyEnd returns where a key ends, and whether the line starts with one.
26func yamlKeyEnd(s *LineScanner) (int, bool) {
27 if quote := s.Peek(0); quote == '"' || quote == '\'' {
28 return quotedYAMLKeyEnd(s, quote)
29 }
30
31 for at := s.Pos(); at < s.Len(); at++ {
32 switch s.line[at] {
33 case ':':
34 // A colon ends a key only when a space or the end of the line
35 // follows it. `http://x` is a value, not a key called `http`.
36 if at+1 >= s.Len() || s.line[at+1] == ' ' {
37 return at, at > s.Pos()
38 }
39 case '#':
40 return 0, false
41 }
42 }
43 return 0, false
44}
45
46// quotedYAMLKeyEnd returns where a quoted key ends, and whether it is one.
47func quotedYAMLKeyEnd(s *LineScanner, quote rune) (int, bool) {
48 for at := s.Pos() + 1; at < s.Len(); at++ {
49 if s.line[at] != quote {
50 continue
51 }
52 if at+1 < s.Len() && s.line[at+1] == ':' {
53 return at + 1, true
54 }
55 return 0, false
56 }
57 return 0, false
58}
59
60// takeYAMLWord colours a bare word, which is a constant when YAML names it and
61// plain text otherwise.
62//
63// A colon inside the word is taken with it unless a space follows, which is
64// what keeps `nginx:1.27` and `http://example.com/x` one span each rather than
65// three.
66func takeYAMLWord(s *LineScanner) {
67 start := s.Pos()
68 for !s.AtEnd() {
69 switch {
70 case isYAMLNameRune(s.Peek(0)):
71 s.Advance(1)
72 case s.Peek(0) == ':' && !endsWord(s, 1):
73 s.Advance(1)
74 case s.Peek(0) == '/' && !endsWord(s, 1):
75 s.Advance(1)
76 default:
77 s.Emit(start, s.Pos(), yamlWordClass(s, start))
78 return
79 }
80 }
81
82 s.Emit(start, s.Pos(), yamlWordClass(s, start))
83}
84
85// yamlWordClass says whether the word running from start to the scanner's
86// position is one the language names.
87func yamlWordClass(s *LineScanner, start int) Class {
88 if yamlConstants[strings.ToLower(string(s.line[start:s.Pos()]))] {
89 return ClassConstant
90 }
91 return ClassIdentifier
92}
93
94// endsWord reports whether the rune at an offset is a space or the end of the
95// line — which is what tells a separator from a character inside a scalar.
96func endsWord(s *LineScanner, offset int) bool {
97 r := s.Peek(offset)
98 return r == 0 || r == ' ' || r == '\t'
99}
100
101// yamlConstants are the words YAML 1.1 reads as booleans and nulls. The
102// spelling is generous on purpose: `yes` and `on` are still true to most YAML
103// parsers in the wild, and a reader wants to see that they are not plain text.
104var yamlConstants = map[string]bool{
105 "true": true, "false": true,
106 "yes": true, "no": true,
107 "on": true, "off": true,
108 "null": true, "~": true,
109}