diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-02 23:43:08 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-03 09:54:39 -0300 |
| commit | 5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e (patch) | |
| tree | f12f4b700fff4ff6f7e818d2a542672ba0b8d76e /vrsite/highlight_test.go | |
| parent | a9263daee9413c5eff3c1f1bebcd224442fd8685 (diff) | |
| download | notevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.tar.gz notevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.zip | |
rebrand to notevi: one CLI over the jj sidecar
vr and vrsite become a single binary. vrsite/ folds into a web package in
one module (0x4200.cafe/notevi); "notevi web" serves and exports exactly
what vrsite did, and "notevi read/grep/note/query" is unchanged.
The sidecar is renamed with it: notevi_log, notevi-log.jsonl, and the
description "private: notevi log". The pre-rebrand names are still
recognized, so an old repository opens and reads; it is renamed in place
on the first write, or up front with "notevi migrate DIR...".
That rename cannot be a single mv inside jj run. jj only auto-tracks a
*new* file in the run working copy below a size limit it does not take
from the command line, so writing a whole log under a name the change has
never held is silently dropped while jj reports success. ensureLogFile
creates the file empty first and lets every later byte be a modification
of a tracked file, which snapshots at any size; that also fixes the same
latent bug when importing a large legacy vr-log.jsonl.
Adds a bem-te-vi mark (favicon and nav brand) and a README.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to 'vrsite/highlight_test.go')
| -rw-r--r-- | vrsite/highlight_test.go | 184 |
1 files changed, 0 insertions, 184 deletions
diff --git a/vrsite/highlight_test.go b/vrsite/highlight_test.go deleted file mode 100644 index e6897d1..0000000 --- a/vrsite/highlight_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package main - -import ( - "strings" - "sync" - "testing" - - sitter "github.com/tree-sitter/go-tree-sitter" -) - -func testHighlighter(t *testing.T) *highlighter { - t.Helper() - return newHighlighter(loadTheme("", "", "dark"), loadTheme("", "", "light")) -} - -// One snippet per language, mapping a piece of its source to the class the One -// theme implies for it. Everything asserted here is a key both One Dark and -// One Light colour differently from plain foreground. -var samples = []struct { - path string - src string - want map[string]string -}{ - {"a.rs", "// note\nfn main() { let s = \"hi\"; }\n", - map[string]string{"fn": "keyword", `"hi"`: "string", "// note": "comment", "main": "function"}}, - {"a.go", "// note\nfunc main() { s := \"hi\"; _ = s }\n", - map[string]string{"func": "keyword", `"hi"`: "string", "// note": "comment", "main": "function"}}, - {"a.py", "# note\nclass Frame:\n def draw(self):\n widget = Widget\n return len(\"hi\")\n", - map[string]string{"class": "keyword", `"hi"`: "string", "# note": "comment", - "draw": "function", "len": "function", "Frame": "constructor", - "Widget": "constructor", "self": "variable-special", "widget": ""}}, - {"a.js", "// note\nfunction main() { const s = \"hi\"; return s; }\n", - map[string]string{"function": "keyword", `"hi"`: "string", "// note": "comment", "main": "function"}}, - {"a.ts", "// note\nfunction main(n: number): string { return \"hi\"; }\n", - map[string]string{"function": "keyword", `"hi"`: "string", "// note": "comment", - "number": "type", "main": "function"}}, - {"a.tsx", "// note\nconst e = <div id=\"x\" />;\n", - map[string]string{"const": "keyword", `"x"`: "string", "// note": "comment", "id": "attribute"}}, - {"a.c", "// note\n#include <stdio.h>\nint main(void) { const char *s = \"hi\"; return MAX; }\n", - map[string]string{"const": "keyword", `"hi"`: "string", "// note": "comment", - "int": "type", "main": "function", "#include": "preproc", - "<stdio.h>": "string", "MAX": "constant"}}, - {"a.cc", "// note\nnamespace n { class C { const char *s = \"hi\"; }; }\n", - map[string]string{"namespace": "keyword", `"hi"`: "string", "// note": "comment", "char": "type"}}, - // .h goes to the C++ grammar, but c.scm's patterns still have to reach it - {"a.h", "// note\ntemplate <class T> struct Box { T v; };\n", - map[string]string{"template": "keyword", "struct": "keyword", "// note": "comment", "T": "type"}}, - // zig names types by casing rather than by node, so Point and MAX are the - // query's two #match? heuristics rather than anything the grammar reports - {"a.zig", "// note\nconst std = @import(\"std\");\nconst Point = struct { x: u8 };\n" + - "pub fn main() !void {\n const MAX: u8 = 1;\n std.debug.print(\"hi\", .{ .x = MAX });\n}\n", - map[string]string{"const": "keyword", `"hi"`: "string", "// note": "comment", - "main": "function", "@import": "function", "u8": "type", "Point": "type", - "MAX": "constant", "x": "property"}}, - {"a.json", "{\n \"a\": \"hi\",\n \"b\": 12\n}\n", - map[string]string{`"hi"`: "string", `"a"`: "string-special", "12": "number"}}, - {"a.sh", "# note\nif true; then\n ls -l \"$dir\"\nfi\n", - map[string]string{"if": "keyword", "# note": "comment", "ls": "function", - "-l": "constant", "dir": "property"}}, -} - -// classOf reports the single class covering every byte of text's first -// occurrence in src, or "" when the run is unclassified or not uniform. -func classOf(hl *highlighter, path, src, text string) string { - classes := hl.classify(path, []byte(src)) - i := strings.Index(src, text) - if i < 0 { - return "" - } - for j := i + 1; j < i+len(text); j++ { - if classes[j] != classes[i] { - return "" - } - } - return hl.classNames[classes[i]] -} - -func TestClassifyPerLanguage(t *testing.T) { - hl := testHighlighter(t) - for _, s := range samples { - for text, want := range s.want { - if got := classOf(hl, s.path, s.src, text); got != want { - t.Errorf("%s: %q classified %q, want %q", s.path, text, got, want) - } - } - } -} - -func TestClassifyUnknownExtension(t *testing.T) { - hl := testHighlighter(t) - // text that every grammar would happily highlight, under paths none claims - src := []byte("// note\nfn main() { let s = \"hi\"; }\n") - for _, path := range []string{"notes.txt", "README.md", "Makefile", "a/b.unknown", ".gitignore", ""} { - classes := hl.classify(path, src) - if len(classes) != len(src) { - t.Fatalf("%q: got %d classes for %d bytes", path, len(classes), len(src)) - } - for i, c := range classes { - if c != 0 { - t.Fatalf("%q: byte %d classified %q, want plain text", path, i, hl.classNames[c]) - } - } - } -} - -// Every predicate a vendored query uses has to be one go-tree-sitter actually -// evaluates in QueryMatches.Next. Anything else lands in these two buckets, -// where it is parsed, ignored, and silently paints matches it should not. -func TestQueriesUseOnlyEvaluatedPredicates(t *testing.T) { - hl := testHighlighter(t) - for _, g := range grammars { - l := hl.byExt[g.exts[0]] - name := strings.Join(g.scms, "+") - for i := uint(0); i < l.query.PatternCount(); i++ { - for _, p := range l.query.GeneralPredicates(i) { - t.Errorf("%s pattern %d: #%s is not evaluated", name, i, p.Operator) - } - for _, p := range l.query.PropertyPredicates(i) { - t.Errorf("%s pattern %d: #is? %s is not evaluated", name, i, p.Property.Key) - } - } - } -} - -// A query that fails to compile is fatal at startup, so report the real error -// here rather than letting the process exit out from under the test binary. -func TestQueriesCompile(t *testing.T) { - for _, g := range grammars { - var scm strings.Builder - for _, name := range g.scms { - b, err := tfs.ReadFile("assets/" + name + ".scm") - if err != nil { - t.Fatal(err) - } - scm.Write(b) - } - if _, err := sitter.NewQuery(sitter.NewLanguage(g.lang()), scm.String()); err != nil { - t.Errorf("%s: %v", strings.Join(g.scms, "+"), err) - } - } -} - -// Class names are theme syntax keys, so css.go's .s-<key> rules — generated -// from the union of both themes' keys — already cover every new language. -func TestClassNamesHaveTheme(t *testing.T) { - dark, light := loadTheme("", "", "dark"), loadTheme("", "", "light") - hl := newHighlighter(dark, light) - styled := map[string]bool{} - for _, th := range []*Theme{dark, light} { - for k := range th.Syntax { - styled[strings.ReplaceAll(k, ".", "-")] = true - } - } - for _, cls := range hl.classNames[1:] { - if !styled[cls] { - t.Errorf("class %q has no .s-%s rule in either theme", cls, cls) - } - } -} - -func TestClassifyIsRaceFree(t *testing.T) { - hl := testHighlighter(t) - want := make([][]int16, len(samples)) - for i, s := range samples { - want[i] = hl.classify(s.path, []byte(s.src)) - } - var wg sync.WaitGroup - for range 8 { - wg.Add(1) - go func() { - defer wg.Done() - for i, s := range samples { - got := hl.classify(s.path, []byte(s.src)) - for j := range got { - if got[j] != want[i][j] { - t.Errorf("%s: byte %d classified %d, want %d", s.path, j, got[j], want[i][j]) - return - } - } - } - }() - } - wg.Wait() -} |
