summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 12:19:39 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit267f48d215366fdf3bb90d655e3af33f723a6f94 (patch)
tree9782e7d288ecd4efa4d38c70d6f3e84f9953ebd4 /src
parent4f57043e39396bc958a103f87466870cf8dafe00 (diff)
downloadpardes-267f48d215366fdf3bb90d655e3af33f723a6f94.tar.gz
pardes-267f48d215366fdf3bb90d655e3af33f723a6f94.zip
Callers and Callees on a .zig file say the language server does not answer them
The in-process ZLS answers no call hierarchy, and no other backend speaks .zig, so the question went nowhere and came back empty, as if nothing called the function. A question no backend answers, or a server that advertises no call or type hierarchy, now answers @none with why, which the core says. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/lsp/lsp.zig24
-rw-r--r--src/lsp/lsp_client.zig5
2 files changed, 26 insertions, 3 deletions
diff --git a/src/lsp/lsp.zig b/src/lsp/lsp.zig
index 9dd1da5a..7e5595fd 100644
--- a/src/lsp/lsp.zig
+++ b/src/lsp/lsp.zig
@@ -61,7 +61,18 @@ pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, req: Req, out: *s
return b.query(gpa, arena, req, out);
}
if (req.kind == .explain and backends.len > 0)
- try backends[0].query(gpa, arena, req, out);
+ return backends[0].query(gpa, arena, req, out);
+ // Nothing asked: said why, where an empty answer would read as nothing
+ // found.
+ inline for (backends) |b| if (b.speaks(req.path))
+ return none(out, "the language server for this file does not answer it", .{});
+ try none(out, "no language server for this file", .{});
+}
+
+/// The one line a backend answers with when it cannot answer at all, and
+/// why: the core says it rather than "nothing found".
+pub fn none(out: *std.Io.Writer, comptime fmt: []const u8, args: anytype) std.Io.Writer.Error!void {
+ try out.print("@none " ++ fmt ++ "\n", args);
}
// Called on server reader threads; the sink must copy text before returning.
@@ -146,3 +157,14 @@ test "LSP encoders report every insufficient output capacity" {
}
}
}
+
+test "a question no backend answers for a file says why rather than find nothing" {
+ if (backends.len == 0) return;
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ var buf: [256]u8 = undefined;
+ var out: std.Io.Writer = .fixed(&buf);
+ // The in-process ZLS answers no call hierarchy for a .zig file.
+ try query(std.testing.allocator, arena.allocator(), .{ .kind = .incoming_calls, .path = "/x/main.zig", .source = "", .offset = 0 }, &out);
+ try std.testing.expectStringStartsWith(out.buffered(), "@none ");
+}
diff --git a/src/lsp/lsp_client.zig b/src/lsp/lsp_client.zig
index c3762f08..55df1f91 100644
--- a/src/lsp/lsp_client.zig
+++ b/src/lsp/lsp_client.zig
@@ -408,6 +408,7 @@ pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, req: lsp.Req, out
tr.note("STOP: no language server spec matches {s}", .{
if (req.path.len == 0) "a pane with no file" else req.path,
});
+ if (req.kind != .explain) return lsp.none(out, "no language server for {s}", .{if (req.path.len == 0) "a pane with no file" else std.fs.path.basename(req.path)});
return traceOut(&tr, req, out, 0, 0);
};
tr.note("file {s} -> {s} (languageId {s})", .{ std.fs.path.basename(req.path), specs[si].name, specs[si].lang });
@@ -807,11 +808,11 @@ fn hierarchy(c: *Conn, si: usize, arena: std.mem.Allocator, cx: *Cx, uri: []cons
const call_side = h == .incoming or h == .outgoing;
if (call_side and !c.caps.call_hier) {
tr.note("STOP: {s} does not advertise callHierarchyProvider", .{specs[si].name});
- return;
+ return lsp.none(cx.out, "{s} does not answer call hierarchy (no callHierarchyProvider)", .{specs[si].name});
}
if (!call_side and !c.caps.type_hier) {
tr.note("STOP: {s} does not advertise typeHierarchyProvider", .{specs[si].name});
- return;
+ return lsp.none(cx.out, "{s} does not answer type hierarchy (no typeHierarchyProvider)", .{specs[si].name});
}
const prepare: []const u8 = if (call_side) "textDocument/prepareCallHierarchy" else "textDocument/prepareTypeHierarchy";
const follow: []const u8 = switch (h) {