turbo-editors/turbo-corepublic Fork 0
v0.9.0
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.go · 129 lines · 4.1 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 19h ago1package syntax
2
3import "strings"
4
5// highlightYAML colours a YAML document, one slice of spans per line.
6//
7// A compose file, a Kubernetes manifest and a CI workflow are all just YAML, so
8// there is one scanner rather than one per dialect. Colouring `services:`
9// differently from any other key would mean carrying Docker's schema here and
10// watching it go stale every time a key is added.
11func highlightYAML(src string) [][]Span {
12 return ScanLines(src, scanYAMLLine)
13}
14
15// scanYAMLLine returns the spans of one line, and what it leaves open.
16func scanYAMLLine(line []rune, carry yamlCarry) ([]Span, yamlCarry) {
17 s := &LineScanner{line: line}
18
19 if carry.inBlock {
20 if continues, next := continueBlockScalar(s, carry); continues {
21 return s.spans, next
22 }
23 }
24
25 s.SkipSpaces()
26 if s.AtEnd() {
27 return s.spans, yamlCarry{}
28 }
29 return scanYAMLContent(s)
30}
31
32// scanYAMLContent colours one ordinary line of YAML.
33func scanYAMLContent(s *LineScanner) ([]Span, yamlCarry) {
34 switch {
35 case s.Peek(0) == '#':
36 s.TakeRest(ClassComment)
37 return s.spans, yamlCarry{}
38 case s.HasPrefix(0, "---") || s.HasPrefix(0, "..."):
39 s.TakeRest(ClassPunctuation)
40 return s.spans, yamlCarry{}
41 }
42
43 keyIndent := s.Pos()
44 takeYAMLListMarkers(s)
45 takeYAMLKey(s)
46
47 return scanYAMLValue(s, keyIndent)
48}
49
50// takeYAMLListMarkers colours the "- " that opens a sequence entry, and any
51// that follow it on the same line: `- - nested` is legal YAML.
52func takeYAMLListMarkers(s *LineScanner) {
53 for s.Peek(0) == '-' && (s.Peek(1) == ' ' || s.Peek(1) == 0) {
54 s.Take(1, ClassPunctuation)
55 s.SkipSpaces()
56 }
57}
58
59// scanYAMLValue colours whatever follows a key, and reports a block scalar left
60// open at the end of the line.
61func scanYAMLValue(s *LineScanner, keyIndent int) ([]Span, yamlCarry) {
62 if opensBlockScalar(s) {
63 s.TakeRest(ClassOperator)
64 return s.spans, yamlCarry{inBlock: true, keyIndent: keyIndent}
65 }
66
67 for !s.AtEnd() {
68 stepYAMLValue(s)
69 }
70 return s.spans, yamlCarry{}
71}
72
73// stepYAMLValue colours one thing inside a value.
74func stepYAMLValue(s *LineScanner) {
75 switch r := s.Peek(0); {
76 case r == ' ' || r == '\t':
77 s.SkipSpaces()
78 case r == '#' && startsComment(s):
79 s.TakeRest(ClassComment)
80 case r == '"' || r == '\'':
81 TakeQuoted(s, r, ClassString)
82 case r == '&' || r == '*':
83 // An anchor and the alias that refers to it: &defaults, *defaults.
84 s.Take(1, ClassBuiltin)
85 s.TakeWhile(ClassBuiltin, isYAMLNameRune)
86 case r == '!':
87 s.TakeWhile(ClassType, func(c rune) bool { return c != ' ' })
88 case isYAMLFlowRune(r):
89 s.Take(1, ClassPunctuation)
90 case r == ':' && endsWord(s, 1):
91 // A colon separates a flow mapping's key from its value. One with no
92 // space after it is part of the scalar — `nginx:1.27` and
93 // `http://example.com` are one value each, not a key and a value.
94 s.Take(1, ClassPunctuation)
95 case IsDigit(r) || (r == '-' && IsDigit(s.Peek(1))):
96 s.TakeWhile(ClassNumber, isYAMLNumberRune)
97 case IsLetter(r) || r == '_':
98 takeYAMLWord(s)
99 default:
100 s.Advance(1)
101 }
102}
103
104// startsComment reports whether a `#` begins a comment rather than sitting
105// inside a bare value. YAML needs a space before it, so `a#b` is one scalar.
106func startsComment(s *LineScanner) bool {
107 return s.Pos() == 0 || s.Peek(-1) == ' ' || s.Peek(-1) == '\t'
108}
109
110// isYAMLNameRune reports whether a rune can appear in a bare word or an anchor.
111func isYAMLNameRune(r rune) bool { return IsWordRune(r) || r == '-' || r == '.' }
112
113// yamlFlowRunes are the characters that structure a flow collection —
114// `{a: 1, b: [2]}` — as opposed to appearing inside a scalar.
115const yamlFlowRunes = "{}[],"
116
117// isYAMLFlowRune reports whether a rune is one of them.
118func isYAMLFlowRune(r rune) bool { return strings.ContainsRune(yamlFlowRunes, r) }
119
120// yamlNumberRunes are the characters that may follow the first digit of an
121// unquoted number, date or time. YAML writes all three without quotes, and
122// `2026-09-01` and `11:30:00` are numbers to a reader whatever the spec calls
123// them.
124const yamlNumberRunes = ".-+:eEx"
125
126// isYAMLNumberRune reports whether a rune can appear in one.
127func isYAMLNumberRune(r rune) bool {
128 return IsDigit(r) || strings.ContainsRune(yamlNumberRunes, r)
129}