From c4504325deefd4a8fb905db95b4e8b5003fb2a6a Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 13:34:46 -0300 Subject: Lspinfo, Lspwhy and the other +Lsp answers share one +Lsp pane per directory Each query kind found its own output pane by its origin. So Lspinfo and Lspwhy, which both write `/+Lsp`, opened two panes of the same name in one directory, and so could a code action list or a format diff. The language server's prose now shares one +Lsp per directory, the way the file searches share one +Search, and each answer replaces the last. Co-Authored-By: Claude Opus 5.5 --- src/Output.zig | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src/Output.zig') diff --git a/src/Output.zig b/src/Output.zig index 545637b3..3541fa57 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -164,6 +164,23 @@ test "stacked empty previews distinguish separators from trailing newlines" { try std.testing.expect(!locations.sameInput("x.c:1:1 \n", "x.c:1:1\n", &rows, .stacked)); } +test "Lspinfo and Lspwhy fill one +Lsp in a directory" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + _ = try p.setTestFile("x\n"); + try fillResults(p, 0, "/work", .{ .query = .status }, "", try p.gpa.dupe(u8, "servers\n"), null); + try fillResults(p, 0, "/work", .{ .query = .explain }, "", try p.gpa.dupe(u8, "why\n"), null); + var lsp_panes: usize = 0; + for (p.panes) |slot| { + const f = (slot orelse continue).file orelse continue; + if (std.mem.eql(u8, f.path, "/work/+Lsp")) { + lsp_panes += 1; + try std.testing.expectEqualStrings("why\n", f.content); + } + } + try std.testing.expectEqual(@as(usize, 1), lsp_panes); +} + test "plain output does not opt into location decoration" { const report = rowDecoration(.{ .from = .{ .cmd = .LocationsConfig } }, "LocationsConfig context:5 tscontext:on"); try std.testing.expectEqualDeep(RowDecoration{}, report); @@ -439,6 +456,12 @@ fn fileSearch(from: Origin) bool { return from != .search and std.mem.eql(u8, traits(from).name, config.search_buffer); } +/// A language server's prose (Lspinfo, Lspwhy, a code action list, a +/// format diff): one `+Lsp` a directory holds for all of them. +fn lspProse(from: Origin) bool { + return from == .query and std.mem.eql(u8, traits(from).name, config.lsp_buffer); +} + pub fn fillResults(p: *Pardes, id: usize, dir: []const u8, from: Origin, arg: []const u8, content: []u8, initial_anchor: ?usize) !void { var result_content = content; errdefer p.gpa.free(result_content); @@ -483,7 +506,7 @@ pub fn fillResults(p: *Pardes, id: usize, dir: []const u8, from: Origin, arg: [] const rp = slot orelse continue; const rf = if (rp.file) |*f| f else continue; const o = if (rf.output) |*x| x else continue; - const shared = fileSearch(from) and fileSearch(o.from); + const shared = (fileSearch(from) and fileSearch(o.from)) or (lspProse(from) and lspProse(o.from)); if (i == id and !shared) continue; if (!shared and !std.meta.eql(o.from, from)) continue; if (!shared and by_arg and !std.mem.eql(u8, o.arg(), arg)) continue; -- cgit v1.3