From e27ec50ecad669a21b33c2c2e7c591828ba97e1b Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 12:10:51 -0300 Subject: Lspwhy explains the last language server query asked, its kind, argument and place, not always a definition at the cursor Lspwhy said it explains how the last request was answered, but it always replayed a definition query at the current cursor. So after a failed Hover it narrated a gd somewhere else, which told nothing about the failure. Now the core remembers the last query (kind, argument, pane, offset), and Lspwhy sends it as ` `, at the query's offset when asked from the same pane. The client narrates that kind: lsp.explaining reads it, and the heading names it ("the hover query at byte N"). With nothing asked yet it is a definition, as before. The effect's arg grows to 160 bytes so a 128-byte argument fits with its kind. Co-Authored-By: Claude Opus 5.5 --- src/lsp/lsp_client.zig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/lsp/lsp_client.zig') diff --git a/src/lsp/lsp_client.zig b/src/lsp/lsp_client.zig index 7704d0ad..0c5e3b7e 100644 --- a/src/lsp/lsp_client.zig +++ b/src/lsp/lsp_client.zig @@ -405,8 +405,9 @@ const Trace = struct { // ------------------------------------------------------------------- query -pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer) !void { +pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, asked: lsp.Req, out: *std.Io.Writer) !void { _ = gpa; + const req = lsp.explaining(asked); if (req.kind == .status) return status(req, out); var tr: Trace = .{ .on = req.kind == .explain }; @@ -443,8 +444,8 @@ pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, req: lsp.Req, out fn traceOut(tr: *const Trace, req: lsp.Req, out: *std.Io.Writer, rows: usize, us: u64) std.Io.Writer.Error!void { if (req.kind != .explain) return; - try out.print("lsp explain — the definition query at byte {d} of {s}\n\n", .{ - req.offset, if (req.path.len == 0) "(no file)" else std.fs.path.basename(req.path), + try out.print("lsp explain — the {s} query at byte {d} of {s}\n\n", .{ + @tagName(req.explains), req.offset, if (req.path.len == 0) "(no file)" else std.fs.path.basename(req.path), }); try out.writeAll(tr.buf[0..tr.len]); if (hideTime()) @@ -488,9 +489,10 @@ fn answer(arena: std.mem.Allocator, si: usize, req: lsp.Req, out: *std.Io.Writer } fn run(c: *Conn, si: usize, arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer, tr: *Trace) Err!void { - // `explain` narrates the definition query — dispatch below on the - // effective kind so the trace follows the code `gd` really runs. - const kind: lsp.Kind = if (req.kind == .explain) .definition else req.kind; + // `explain` narrates the last query asked (a definition, `gd`, when + // none was) — dispatch below on the effective kind so the trace + // follows the code that query really runs. + const kind: lsp.Kind = if (req.kind == .explain) req.explains else req.kind; // Both arg-taking kinds are useless without one, and an empty // workspace/symbol query means "every symbol in the project". -- cgit v1.3