turbo-editors/turbo-corepublic Fork 0
v1.0.1
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.

🛟 Updated. 28d5985 · on v1.0.1 · k33g · 19h ago
yaml.go · 129 lines · 4.1 KBGo Blame HistoryRaw
  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
117
118
119
120
121
122
123
124
125
126
127
128
129
package syntax

import "strings"

// highlightYAML colours a YAML document, one slice of spans per line.
//
// A compose file, a Kubernetes manifest and a CI workflow are all just YAML, so
// there is one scanner rather than one per dialect. Colouring `services:`
// differently from any other key would mean carrying Docker's schema here and
// watching it go stale every time a key is added.
func highlightYAML(src string) [][]Span {
	return ScanLines(src, scanYAMLLine)
}

// scanYAMLLine returns the spans of one line, and what it leaves open.
func scanYAMLLine(line []rune, carry yamlCarry) ([]Span, yamlCarry) {
	s := &LineScanner{line: line}

	if carry.inBlock {
		if continues, next := continueBlockScalar(s, carry); continues {
			return s.spans, next
		}
	}

	s.SkipSpaces()
	if s.AtEnd() {
		return s.spans, yamlCarry{}
	}
	return scanYAMLContent(s)
}

// scanYAMLContent colours one ordinary line of YAML.
func scanYAMLContent(s *LineScanner) ([]Span, yamlCarry) {
	switch {
	case s.Peek(0) == '#':
		s.TakeRest(ClassComment)
		return s.spans, yamlCarry{}
	case s.HasPrefix(0, "---") || s.HasPrefix(0, "..."):
		s.TakeRest(ClassPunctuation)
		return s.spans, yamlCarry{}
	}

	keyIndent := s.Pos()
	takeYAMLListMarkers(s)
	takeYAMLKey(s)

	return scanYAMLValue(s, keyIndent)
}

// takeYAMLListMarkers colours the "- " that opens a sequence entry, and any
// that follow it on the same line: `- - nested` is legal YAML.
func takeYAMLListMarkers(s *LineScanner) {
	for s.Peek(0) == '-' && (s.Peek(1) == ' ' || s.Peek(1) == 0) {
		s.Take(1, ClassPunctuation)
		s.SkipSpaces()
	}
}

// scanYAMLValue colours whatever follows a key, and reports a block scalar left
// open at the end of the line.
func scanYAMLValue(s *LineScanner, keyIndent int) ([]Span, yamlCarry) {
	if opensBlockScalar(s) {
		s.TakeRest(ClassOperator)
		return s.spans, yamlCarry{inBlock: true, keyIndent: keyIndent}
	}

	for !s.AtEnd() {
		stepYAMLValue(s)
	}
	return s.spans, yamlCarry{}
}

// stepYAMLValue colours one thing inside a value.
func stepYAMLValue(s *LineScanner) {
	switch r := s.Peek(0); {
	case r == ' ' || r == '\t':
		s.SkipSpaces()
	case r == '#' && startsComment(s):
		s.TakeRest(ClassComment)
	case r == '"' || r == '\'':
		TakeQuoted(s, r, ClassString)
	case r == '&' || r == '*':
		// An anchor and the alias that refers to it: &defaults, *defaults.
		s.Take(1, ClassBuiltin)
		s.TakeWhile(ClassBuiltin, isYAMLNameRune)
	case r == '!':
		s.TakeWhile(ClassType, func(c rune) bool { return c != ' ' })
	case isYAMLFlowRune(r):
		s.Take(1, ClassPunctuation)
	case r == ':' && endsWord(s, 1):
		// A colon separates a flow mapping's key from its value. One with no
		// space after it is part of the scalar — `nginx:1.27` and
		// `http://example.com` are one value each, not a key and a value.
		s.Take(1, ClassPunctuation)
	case IsDigit(r) || (r == '-' && IsDigit(s.Peek(1))):
		s.TakeWhile(ClassNumber, isYAMLNumberRune)
	case IsLetter(r) || r == '_':
		takeYAMLWord(s)
	default:
		s.Advance(1)
	}
}

// startsComment reports whether a `#` begins a comment rather than sitting
// inside a bare value. YAML needs a space before it, so `a#b` is one scalar.
func startsComment(s *LineScanner) bool {
	return s.Pos() == 0 || s.Peek(-1) == ' ' || s.Peek(-1) == '\t'
}

// isYAMLNameRune reports whether a rune can appear in a bare word or an anchor.
func isYAMLNameRune(r rune) bool { return IsWordRune(r) || r == '-' || r == '.' }

// yamlFlowRunes are the characters that structure a flow collection —
// `{a: 1, b: [2]}` — as opposed to appearing inside a scalar.
const yamlFlowRunes = "{}[],"

// isYAMLFlowRune reports whether a rune is one of them.
func isYAMLFlowRune(r rune) bool { return strings.ContainsRune(yamlFlowRunes, r) }

// yamlNumberRunes are the characters that may follow the first digit of an
// unquoted number, date or time. YAML writes all three without quotes, and
// `2026-09-01` and `11:30:00` are numbers to a reader whatever the spec calls
// them.
const yamlNumberRunes = ".-+:eEx"

// isYAMLNumberRune reports whether a rune can appear in one.
func isYAMLNumberRune(r rune) bool {
	return IsDigit(r) || strings.ContainsRune(yamlNumberRunes, r)
}