diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-06 17:14:40 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-06 17:14:40 -0300 |
| commit | fa82cac885cb4738fe36d1e49b4749b5a3e31a4a (patch) | |
| tree | 75e6c02e75cfde2282c7b4f9d5a467afa71e0a6b /src/pardes.zig | |
| parent | 1852dd3c1247ee66b4265b0a7ee8b3afd08a8fa5 (diff) | |
| download | pardes-fa82cac885cb4738fe36d1e49b4749b5a3e31a4a.tar.gz pardes-fa82cac885cb4738fe36d1e49b4749b5a3e31a4a.zip | |
syntax: a results buffer is coloured as the code it quotes
+Grep, +Search and every language answer render rows like
src/look.zig:718:12-16 fn grepText(path: []const u8, text: []const u8...
— a location, a space, and a piece of some file. The location names the file,
the file names the grammar, and the rest of the row is a fragment of that
language, so a grep over Zig reads as Zig and one Markdown row in the same
buffer does not pretend otherwise. The location itself is left uncoloured: it
is not code, and painting it as code is how a path starts looking like a
keyword.
`look.parsePathLine` decides what counts as a location — the same primitive
n/N already walks these buffers with, so the two agree by construction about
which rows are locations. NOT `lookableLineSpan`, which is n/N's whole
heuristic: it calls `resolve`, and a `realpath` per row per scroll is not
something a render path can afford. A bare filename is refused too; only
`path:line` counts, or a prose line whose first word ended in `.md` would
colour the rest of a sentence.
THE BUFFER IS COLOURED WHOLE, ONCE, WHEN IT IS FILLED. An adversarial pass
measured the obvious per-window implementation and it was untenable: the rows
are independent, so a window pass buys no fidelity, only amortisation, and
pays a burst on every scroll that outran the covered range. Grammars compile
their highlights query on first use — zig 26.9ms, cpp 18.9ms, rust 14.3ms —
so a polyglot grep showing six languages stalled a frame by 66ms, moving a
cost the syntax module had deliberately put on "opening a file" onto a scroll.
It also raised tree-sitter's allocation rate 3.5x (8,785 per refresh against
2,454) into a 16 MiB bump arena that only reclaims LIFO, so ~16 scroll
re-highlights exhausted it — and that arena is shared with real file panes, so
a results pane could evict editing. A grep is capped at 512 rows; colouring it
once makes the covered-range check true forever after and scrolling free.
The rest of that pass, in the same spirit: injections off for a single row
(both build a SECOND parser, per fenced block and per inline node, which is
absurd for one truncated row that almost never contains a fence), one query
cursor for the buffer instead of one per row, a one-entry extension memo so
non-matching rows stop paying a 29-spec scan, and NO highlights at all when
nothing painted — an all-zero run is not the same as none, and it defeated
`recolorSyntax`'s fast path, making every +Help and +Config walk its graphemes
every frame to paint nothing.
One correctness bug from the same pass: a failed `setLanguage` has already
nulled the parser's language, so leaving `held` on the previous grammar made
every later row of it skip the call and silently lose colour.
Documents keep `.source`: a New scratch and a real file are output-shaped but
have one language and an edit per keystroke, and `saves` is the line between
the two.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 357a6b13..6edf3600 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4790,11 +4790,23 @@ pub const Pane = struct { /// The one coloring choice keyed on what a pane IS, so the highlight /// producer (refreshHighlights) and the render pass agree on the algorithm. - pub const ColorAlgo = enum { none, tty, source, diff }; + pub const ColorAlgo = enum { none, tty, source, diff, locations }; pub fn colorAlgo(pane: *const Pane) ColorAlgo { if (pane.isTerminal()) return .tty; if (pane.file) |f| { if (std.mem.endsWith(u8, f.path, ".diff") or std.mem.endsWith(u8, f.path, ".patch")) return .diff; + // A RENDERING, not a document: +Grep, +Search, +Lsp and their kin + // have no language of their own, and their rows quote several at + // once. Colour each row by the file its location names instead. + // Buffers with no locations in them (+Help, +Config) match nothing + // and stay plain, so this needs no table of which origins qualify. + // + // `saves` is the line between the two: a New scratch and a real + // file are output-shaped but ARE documents, with one language and + // an edit on every keystroke — they keep `.source`, which is both + // right for them and what keeps a megabyte of scratch off the + // whole-buffer pass below. + if (f.output != null and !output_pane.fileTraits(f.output).saves) return .locations; return .source; } return .none; @@ -16745,7 +16757,9 @@ pub const Pardes = struct { // to stay on the right glyph when the emulator and this surface // disagree about how many columns a cluster is worth. .tty => if (p.settings.colors) term_pane.recolorAnsi(p, pane, r, tx, tw, body_h, body), - .source, .diff => { + // `.locations` joins them because it feeds the same `f.highlights` + // — only the pass that FILLED it differs (refreshHighlights). + .source, .diff, .locations => { const f = &pane.file.?; file_pane.drawGutter(p, pane, r, tx, tw, body_h, active); if (p.settings.colors) file_pane.recolorSyntax(p, pane, f, r, tx, tw, body_h); |
