aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/highlight_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 00:23:51 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 00:23:51 +0200
commit04b4243ad31d144c4caf9be4c5096a0f27a2648e (patch)
tree056515d145ba8e4fba29a2150b2c58199133b0c1 /gui/internal/model/highlight_test.go
parent9db67b201b80e9b7f824989df8517cefc587036d (diff)
downloadkrino-04b4243ad31d144c4caf9be4c5096a0f27a2648e.tar.gz
krino-04b4243ad31d144c4caf9be4c5096a0f27a2648e.zip
gui: syntax colours, a file preview, and a settings window
Diffstat (limited to 'gui/internal/model/highlight_test.go')
-rw-r--r--gui/internal/model/highlight_test.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/gui/internal/model/highlight_test.go b/gui/internal/model/highlight_test.go
new file mode 100644
index 0000000..f5c1e28
--- /dev/null
+++ b/gui/internal/model/highlight_test.go
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package model
+
+import "testing"
+
+// textOf is the stretch a span covers, by character offset.
+func textOf(text string, s Span) string {
+ return string([]rune(text)[s.From:s.To])
+}
+
+// find is the first span of a kind whose text matches, for the assertions
+// below.
+func find(t *testing.T, text string, kind SpanKind, want string) {
+ t.Helper()
+ for _, s := range Spans(text) {
+ if s.Kind == kind && textOf(text, s) == want {
+ return
+ }
+ }
+ t.Errorf("no %v span %q in %q", kind, want, text)
+}
+
+// TestSpansPaintsTheParts: heads, actions, strings and comments each come
+// back as their own kind.
+func TestSpansPaintsTheParts(t *testing.T) {
+ text := "(rule \"invoices\" ; the ones from suppliers\n (when (type pdf))\n (move \"Docs\"))\n"
+ find(t, text, SpanHead, "rule")
+ find(t, text, SpanHead, "when")
+ find(t, text, SpanHead, "type")
+ find(t, text, SpanAction, "move")
+ find(t, text, SpanString, `"invoices"`)
+ find(t, text, SpanString, `"Docs"`)
+ find(t, text, SpanComment, "; the ones from suppliers")
+}
+
+// TestSpansKeepsQuotingStraight: a semicolon inside a string is not a
+// comment, a quote inside a comment does not start a string, and an escaped
+// quote does not end one.
+func TestSpansKeepsQuotingStraight(t *testing.T) {
+ text := `(rule "a;b" (when (name "say \"hi\"")) (move "Out"))` + "\n;; \"not a string\"\n"
+ find(t, text, SpanString, `"a;b"`)
+ find(t, text, SpanString, `"say \"hi\""`)
+ find(t, text, SpanComment, `;; "not a string"`)
+ for _, s := range Spans(text) {
+ if s.Kind == SpanComment && textOf(text, s) == ";b\" (when (name \"say \\\"hi\\\"\")) (move \"Out\"))" {
+ t.Error("a semicolon inside a string started a comment")
+ }
+ }
+}
+
+// TestSpansOnlyHeadsAreHeads: an argument that happens to be a symbol is
+// not painted as a head.
+func TestSpansOnlyHeadsAreHeads(t *testing.T) {
+ text := "(when (type pdf doc))\n"
+ for _, s := range Spans(text) {
+ if s.Kind == SpanHead && (textOf(text, s) == "pdf" || textOf(text, s) == "doc") {
+ t.Errorf("argument %q painted as a head", textOf(text, s))
+ }
+ }
+ find(t, text, SpanHead, "type")
+}
+
+// TestSpansCountsCharactersNotBytes: offsets are what a text buffer counts,
+// so a file with Polish letters in it still colours the right stretch.
+func TestSpansCountsCharactersNotBytes(t *testing.T) {
+ text := "(rule \"spółka\" (move \"Księgowość\"))\n"
+ find(t, text, SpanString, `"spółka"`)
+ find(t, text, SpanString, `"Księgowość"`)
+ find(t, text, SpanAction, "move")
+}
+
+// TestSpansSurvivesUnclosedForms: text being typed is not yet valid, and
+// the colouring must not run off the end of it.
+func TestSpansSurvivesUnclosedForms(t *testing.T) {
+ for _, text := range []string{`(rule "half`, "(when (name ", ";; just a comment", `"`} {
+ for _, s := range Spans(text) {
+ if s.From < 0 || s.To > len([]rune(text)) || s.From > s.To {
+ t.Errorf("%q: span %+v is outside the text", text, s)
+ }
+ }
+ }
+}