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

🛟 Updated. 28d5985 · on v1.0.0 · k33g · 20h ago
yaml_test.go · 282 lines · 8.8 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
package syntax

import (
	"strings"
	"testing"
)

// yamlClassOf returns the class covering the first occurrence of a piece of
// text in a YAML document.
func yamlClassOf(t *testing.T, src, want string) Class {
	t.Helper()

	index := strings.Index(src, want)
	if index < 0 {
		t.Fatalf("%q does not appear in the source", want)
	}
	line := strings.Count(src[:index], "\n")
	col := index - (strings.LastIndex(src[:index], "\n") + 1)

	class, ok := classAt(Highlight(LanguageYAML, src), line, col)
	if !ok {
		t.Fatalf("no span covers %q at line %d column %d", want, line, col)
	}
	return class
}

func TestYAMLColoursACompseFilesParts(t *testing.T) {
	const src = `# a compose file
services:
  web:
    image: "nginx:1.27"
    replicas: 3
    enabled: true
`

	tests := map[string]Class{
		"# a compose file": ClassComment,
		"services":         ClassIdentifier,
		`"nginx:1.27"`:     ClassString,
		"3":                ClassNumber,
		"true":             ClassConstant,
	}

	for text, want := range tests {
		t.Run(text, func(t *testing.T) {
			if got := yamlClassOf(t, src, text); got != want {
				t.Errorf("%q is %v, want %v", text, got, want)
			}
		})
	}
}

func TestYAMLColoursTheColonAsPunctuation(t *testing.T) {
	spans := Highlight(LanguageYAML, "image: nginx")

	class, ok := classAt(spans, 0, len("image"))
	if !ok || class != ClassPunctuation {
		t.Errorf("the colon after a key is %v (covered: %v), want punctuation", class, ok)
	}
}

func TestYAMLColoursAListMarker(t *testing.T) {
	const src = "ports:\n  - \"8080:80\"\n"

	spans := Highlight(LanguageYAML, src)
	class, ok := classAt(spans, 1, 2) // the "-"
	if !ok || class != ClassPunctuation {
		t.Errorf("the list marker is %v (covered: %v), want punctuation", class, ok)
	}
	if got := yamlClassOf(t, src, `"8080:80"`); got != ClassString {
		t.Errorf("the quoted entry is %v, want string", got)
	}
}

func TestAColonWithNoSpaceAfterItStaysInsideTheValue(t *testing.T) {
	// `image: nginx:1.27` and `url: http://x` are one value each. A colon is a
	// separator only when a space follows it — otherwise every image tag and
	// every URL in every compose file comes out in three colours.
	tests := map[string]string{
		"image: nginx:1.27":         "nginx:1.27",
		"url: http://example.com/x": "http://example.com/x",
	}

	for src, value := range tests {
		t.Run(value, func(t *testing.T) {
			spans := Highlight(LanguageYAML, src)
			start := strings.Index(src, value)

			for col := start; col < start+len(value); col++ {
				class, ok := classAt(spans, 0, col)
				if !ok || class != ClassIdentifier {
					t.Fatalf("column %d of %q is %v (covered: %v); the value was split", col-start, value, class, ok)
				}
			}
		})
	}
}

func TestAColonWithASpaceAfterItIsStillASeparator(t *testing.T) {
	// The other half of the rule, in flow style where it is the only marker.
	spans := Highlight(LanguageYAML, "a: {b: 1}")

	class, ok := classAt(spans, 0, len("a: {b"))
	if !ok || class != ClassPunctuation {
		t.Errorf("the colon in a flow mapping is %v (covered: %v), want punctuation", class, ok)
	}
}

func TestYAMLColoursAnAnchorAndItsAlias(t *testing.T) {
	const src = "defaults: &base\n  a: 1\nuse:\n  <<: *base\n"

	if got := yamlClassOf(t, src, "&base"); got != ClassBuiltin {
		t.Errorf("the anchor is %v, want builtin", got)
	}
	if got := yamlClassOf(t, src, "*base"); got != ClassBuiltin {
		t.Errorf("the alias is %v, want builtin", got)
	}
}

func TestYAMLColoursATag(t *testing.T) {
	if got := yamlClassOf(t, "value: !!str 3\n", "!!str"); got != ClassType {
		t.Errorf("a tag is %v, want type", got)
	}
}

func TestYAMLColoursDocumentMarkers(t *testing.T) {
	for _, marker := range []string{"---", "..."} {
		t.Run(marker, func(t *testing.T) {
			spans := Highlight(LanguageYAML, marker+"\na: 1\n")
			if class, ok := classAt(spans, 0, 0); !ok || class != ClassPunctuation {
				t.Errorf("%q is %v (covered: %v), want punctuation", marker, class, ok)
			}
		})
	}
}

func TestYAMLTreatsTheGenerousBooleanSpellings(t *testing.T) {
	// YAML 1.1 reads all of these as booleans, and most parsers in the wild
	// still do. A reader wants to see that `no` is not the string "no".
	for _, word := range []string{"true", "false", "yes", "no", "on", "off", "null"} {
		t.Run(word, func(t *testing.T) {
			if got := yamlClassOf(t, "key: "+word+"\n", word); got != ClassConstant {
				t.Errorf("%q is %v, want constant", word, got)
			}
		})
	}
}

func TestYAMLColoursABlockScalarAcrossLines(t *testing.T) {
	// The one construct whose extent is decided by indentation rather than by
	// a delimiter, and the one every CI file uses.
	const src = "run: |\n  set -e\n  echo hello\nnext: 1\n"
	spans := Highlight(LanguageYAML, src)

	for _, line := range []int{1, 2} {
		if class, ok := classAt(spans, line, 2); !ok || class != ClassString {
			t.Errorf("line %d of the block is %v (covered: %v), want string", line, class, ok)
		}
	}
	if class, ok := classAt(spans, 3, 0); !ok || class != ClassIdentifier {
		t.Errorf("the key after the block is %v (covered: %v), want it read as code again", class, ok)
	}
}

func TestABlankLineDoesNotEndABlockScalar(t *testing.T) {
	// A literal scalar keeps its empty lines, so ending the block at the first
	// paragraph break would cut a shell script in a CI file in half.
	const src = "run: |\n  first\n\n  second\nnext: 1\n"
	spans := Highlight(LanguageYAML, src)

	if class, ok := classAt(spans, 3, 2); !ok || class != ClassString {
		t.Errorf("the line after the blank one is %v (covered: %v), want it still in the block", class, ok)
	}
	if class, ok := classAt(spans, 4, 0); !ok || class != ClassIdentifier {
		t.Errorf("the key after the block is %v (covered: %v), want code", class, ok)
	}
}

func TestABlockScalarEndsAtTheFirstLessIndentedLine(t *testing.T) {
	const src = "a:\n  run: |\n    deep\n  other: 1\n"
	spans := Highlight(LanguageYAML, src)

	if class, ok := classAt(spans, 2, 4); !ok || class != ClassString {
		t.Errorf("the block's body is %v (covered: %v), want string", class, ok)
	}
	if class, ok := classAt(spans, 3, 2); !ok || class != ClassIdentifier {
		t.Errorf("the sibling key is %v (covered: %v), want it out of the block", class, ok)
	}
}

func TestTheFoldedBlockScalarWorksTheSameWay(t *testing.T) {
	spans := Highlight(LanguageYAML, "note: >-\n  folded text\nnext: 1\n")

	if class, ok := classAt(spans, 1, 2); !ok || class != ClassString {
		t.Errorf("a folded block's body is %v (covered: %v), want string", class, ok)
	}
}

func TestYAMLColoursAComment(t *testing.T) {
	if got := yamlClassOf(t, "a: 1 # why\n", "# why"); got != ClassComment {
		t.Errorf("a trailing comment is %v, want comment", got)
	}
}

func TestAHashInsideAValueIsNotAComment(t *testing.T) {
	// YAML needs a space before a `#` for it to start a comment, so a colour
	// or a fragment stays part of the scalar.
	spans := Highlight(LanguageYAML, "colour: ff#00aa\n")

	if class, ok := classAt(spans, 0, len("colour: ff#")); ok && class == ClassComment {
		t.Error("a # with no space before it was read as a comment")
	}
}

func TestYAMLColoursAQuotedKey(t *testing.T) {
	if got := yamlClassOf(t, `"a key": 1`, `"a key"`); got != ClassIdentifier {
		t.Errorf("a quoted key is %v, want identifier", got)
	}
}

func TestYAMLColoursFlowCollections(t *testing.T) {
	const src = `test: ["CMD", "curl"]`

	if got := yamlClassOf(t, src, "["); got != ClassPunctuation {
		t.Errorf("a flow sequence bracket is %v, want punctuation", got)
	}
	if got := yamlClassOf(t, src, `"CMD"`); got != ClassString {
		t.Errorf("an entry is %v, want string", got)
	}
}

func TestAComposeFileIsRecognisedByItsExtension(t *testing.T) {
	for _, name := range []string{
		"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml",
		"k8s/deployment.yaml", ".github/workflows/ci.yml",
	} {
		t.Run(name, func(t *testing.T) {
			if got := LanguageOf(name, ""); got != LanguageYAML {
				t.Errorf("LanguageOf(%q) = %q, want yaml", name, got)
			}
		})
	}
}

func TestYAMLReturnsOneEntryPerLine(t *testing.T) {
	tests := map[string]int{
		"":                 1,
		"a: 1":             1,
		"a: 1\n":           2,
		"a: |\n  b\n  c\n": 4,
	}

	for src, want := range tests {
		if got := len(Highlight(LanguageYAML, src)); got != want {
			t.Errorf("Highlight(%q) returned %d lines, want %d", src, got, want)
		}
	}
}

func TestBrokenYAMLIsStillColoured(t *testing.T) {
	for _, src := range []string{"a: \"unterminated", "  - ", "!", "&", "a: |"} {
		t.Run(src, func(t *testing.T) {
			if got := len(Highlight(LanguageYAML, src)); got != 1 {
				t.Errorf("Highlight(%q) returned %d lines, want 1", src, got)
			}
		})
	}
}

func TestYAMLSpansAreOrderedAndDoNotOverlap(t *testing.T) {
	const src = "services:\n  web:\n    image: \"nginx\"  # pinned\n    ports: [80, 443]\n"

	for line, onLine := range Highlight(LanguageYAML, src) {
		previousEnd := 0
		for _, span := range onLine {
			if span.Start < previousEnd {
				t.Errorf("line %d: span %+v starts before the previous one ended at %d", line, span, previousEnd)
			}
			previousEnd = span.End
		}
	}
}