From f43c1e11b44e2464f0bb0b635e0abcaf3c717e23 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 28 Jul 2026 23:30:29 -0300 Subject: LSP seam: async execution model, helix keymap, evaluation harness The base every language backend plugs into. Three parts: ASYNC. The core had no request/response shape - every effect was fire-and-forget or instantaneous. A language query is the first thing that answers later, so: Effect .lsp -> shell worker -> Event .lsp_resp. tty.zig uses io.concurrent + the vaxis queue, gui.zig a detached thread + the mutex queue it already had for ptys; web no-ops it. The worker never touches the core (path/source/arg are snapshotted into an LspJob), one query in flight identified by a monotonic id so a second press makes the first answer stale, and no rows is a legal answer. KEYMAP. Helix's, verified against its default.rs rather than recalled. gd/gD/gy/gi/gr and ]d/[d had no conflicts. The SPC letters did, so pardes's own builtins moved instead of helix's: Kill k->q, Del d->wc (closing a pane is a window op, and c is helix's own close), Dump/Restore s?->f?, Tutor ht->T. A three-exception muscle-memory map is not a map. RESULTS ARE +SEARCH ROWS. path:LINE:COL text, absolute. That is what look.zig resolves and n/N step, so one row from a goto jumps and several open a buffer - helix's multi-result picker needed no picker code. Backends supply exactly one function (lsp.query) plus a supports set and a name; the base has none on purpose. zig build lspbench scores them on the same corpus: feature matrix (trusting results, not the supports flag - a claimed-but-empty kind is reported as a false claim), cold and warm latency, peak RSS. Two snapshot scripts moved. leader.snap encoded the old key paths. chordcut.snap's last two steps clicked column 5, which lands on a FILE pane, so 'key c-b' toggled nothing and the typed text was being read as normal-mode keys - the golden recorded no TTY pane and no cat -v output anywhere. Pointing them at an actual shell makes both steps assert what their comments claim, and the tty paste chord is now covered for the first time. --- build.zig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'build.zig') diff --git a/build.zig b/build.zig index 6073bbcc..5f3ce06f 100644 --- a/build.zig +++ b/build.zig @@ -524,6 +524,27 @@ pub fn build(b: *std.Build) void { } b.step("hxparity", "run the file-vs-pty editing parity suite (-- [--waivers w.jsonl] ...)").dependOn(&run_hxparity.step); + // the language-backend scoreboard. ReleaseFast on purpose: the point + // is to compare backends' real cost, and a Debug build measures the + // safety checks of whichever one allocates most. It links the same + // core module as hxdiff, so `lsp.query` here is the one the editor + // runs. + const lspbench = b.addExecutable(.{ + .name = "pardes-lspbench", + .root_module = b.createModule(.{ + .target = target, + .optimize = .ReleaseFast, + .root_source_file = b.path("test/lspbench.zig"), + .link_libc = true, + }), + }); + lspbench.root_module.addImport("pardes", hx_core_mod); + b.installArtifact(lspbench); + const run_lspbench = b.addRunArtifact(lspbench); + if (b.args) |args| run_lspbench.addArgs(args); + run_lspbench.setCwd(b.path(".")); + b.step("lspbench", "language-backend latency + feature matrix (-- [--json] [repo-root])").dependOn(&run_lspbench.step); + // modal.zig is pure std — its inline unit tests run here const unit = b.addTest(.{ .root_module = b.createModule(.{ .target = target, -- cgit v1.3