summaryrefslogtreecommitdiff
path: root/src/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lsp')
-rw-r--r--src/lsp/lsp_client.zig16
-rw-r--r--src/lsp/lsp_zls.zig26
2 files changed, 38 insertions, 4 deletions
diff --git a/src/lsp/lsp_client.zig b/src/lsp/lsp_client.zig
index 55df1f91..7c1980f6 100644
--- a/src/lsp/lsp_client.zig
+++ b/src/lsp/lsp_client.zig
@@ -429,6 +429,8 @@ pub fn query(gpa: std.mem.Allocator, arena: std.mem.Allocator, req: lsp.Req, out
record(si, req, us, rows, err_name);
if (req.kind == .explain) return traceOut(&tr, req, out, rows, us);
+ // No server to start for the language: said, not "did not answer".
+ if (failure) |err| if (err == error.NoServer) return lsp.none(out, "language server for {s} not found", .{std.fs.path.extension(req.path)});
if (failure) |err| return err;
try out.writeAll(scratch.buffered());
}
@@ -1148,6 +1150,7 @@ fn ensure(c: *Conn, si: usize, arena: std.mem.Allocator, req: lsp.Req, tr: *Trac
// signal-safe calls, no allocation, no locks. (Same rule as tty.zig's
// forkShell.)
_ = setsid(); // Ctrl-C in pardes's terminal is not the server's business
+ @import("../host_io.zig").resetChildSignals();
_ = libc.dup2(sv[1], 0);
_ = libc.dup2(sv[1], 1);
const devnull = libc.open("/dev/null", .{ .ACCMODE = .WRONLY });
@@ -2230,3 +2233,16 @@ test "LSP source snippets preserve indentation with UTF16 byte ranges and prose
}
}
}
+
+test "a language with no server to start says so, not that it did not answer" {
+ const si = specFor("/x/a.py") orelse return;
+ const was = conns[si].state;
+ conns[si].state = .disabled;
+ defer conns[si].state = was;
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ var buf: [256]u8 = undefined;
+ var out: std.Io.Writer = .fixed(&buf);
+ try query(std.testing.allocator, arena.allocator(), .{ .kind = .hover, .path = "/x/a.py", .source = "", .offset = 0 }, &out);
+ try std.testing.expectEqualStrings("@none language server for .py not found\n", out.buffered());
+}
diff --git a/src/lsp/lsp_zls.zig b/src/lsp/lsp_zls.zig
index 09493c26..2311ec47 100644
--- a/src/lsp/lsp_zls.zig
+++ b/src/lsp/lsp_zls.zig
@@ -354,10 +354,14 @@ fn status(arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer) !void {
// The dependency module map, which is the answer to "why does gd work on
// std and not on @import(\"vaxis\")" — if a name is not on this list, the
// build did not import it and nothing can resolve it.
- try out.print("\ndependency imports gd can follow ({d}):\n", .{cfg.module_names.len});
- for (cfg.module_names, cfg.module_roots) |n, r| {
- try out.print(" {s:<14} {s}\n", .{ n, r });
- }
+ // pardes's own build's imports: listed for a file in its tree, none for
+ // any other project's file, which does not have them.
+ if (inProject(req.path)) {
+ try out.print("\ndependency imports gd can follow ({d}):\n", .{cfg.module_names.len});
+ for (cfg.module_names, cfg.module_roots) |n, r| {
+ try out.print(" {s:<14} {s}\n", .{ n, r });
+ }
+ } else try out.print("\ndependency imports gd can follow: none (this file is not in the tree pardes was built from)\n", .{});
try out.print("asked from: {s}\n", .{if (req.path.len == 0) "a pane with no file" else req.path});
try out.print("\nanswers:", .{});
@@ -426,6 +430,20 @@ fn status(arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer) !void {
/// running ITS build.zig.
/// 3. A module whose root source is a GENERATED file is absent from the
/// table: it has no path until make() runs.
+/// Whether `path` is in the tree pardes was built from, whose build's
+/// imports `cfg.module_names` are.
+fn inProject(path: []const u8) bool {
+ const project = std.mem.trimEnd(u8, cfg.project_root, "/");
+ return project.len > 0 and path.len > project.len and std.mem.startsWith(u8, path, project) and path[project.len] == '/';
+}
+
+test "only a file in pardes's own tree is told pardes's dependency imports" {
+ var buf: [4096]u8 = undefined;
+ const inside = try std.fmt.bufPrint(&buf, "{s}/src/main.zig", .{std.mem.trimEnd(u8, cfg.project_root, "/")});
+ try std.testing.expect(inProject(inside));
+ try std.testing.expect(!inProject("/tmp/elsewhere/main.zig"));
+}
+
fn moduleRoot(name: []const u8) ?[]const u8 {
comptime std.debug.assert(cfg.module_names.len == cfg.module_roots.len);
for (cfg.module_names, cfg.module_roots) |n, r| {