summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 17:14:40 -0300
committerGabriel Schneider <[email protected]>2026-09-06 17:14:40 -0300
commitfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (patch)
tree75e6c02e75cfde2282c7b4f9d5a467afa71e0a6b /src/pardes.zig
parent1852dd3c1247ee66b4265b0a7ee8b3afd08a8fa5 (diff)
downloadpardes-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.zig18
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);