summaryrefslogtreecommitdiff
path: root/src/tty/tty.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/tty/tty.zig')
-rw-r--r--src/tty/tty.zig50
1 files changed, 44 insertions, 6 deletions
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index c85908d6..f8f7150b 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -64,6 +64,10 @@ pub const Command = struct {
paste_end,
/// a language query finished on a worker; rows are lsp-domain-owned
lsp_done: struct { id: u32, rows: []u8 },
+ /// a language SERVER changed state (spawned, indexing, exited) — the
+ /// client's reader thread narrates and this lands it on the message
+ /// row; text is lsp-domain-owned
+ lsp_status: []u8,
/// a selection-filter worker finished; every stdout is gpa-owned
pipe_done: selection_pipe.Response,
/// something happened in a watched directory (see watchFiles)
@@ -702,7 +706,14 @@ fn localSession(
.inotify_fd = if (builtin.os.tag == .linux) libc.inotify_init1(linux.IN.CLOEXEC) else -1,
.fs = fs,
};
+ // The protocol client's reader threads narrate server state through this
+ // sink from the moment it is set; posting is safe because the loop queue
+ // outlives them all — and it is UNSET first thing in the defer below,
+ // under the sink's own lock, so no reader can be mid-post when the queue
+ // starts draining for teardown.
+ pardes.lsp.setStatusSink(&sh, lspStatusSink);
defer {
+ pardes.lsp.setStatusSink(null, null);
// reap the reader tasks (cancel interrupts a blocked read) before
// closing the masters — the runtime joins those threads on exit and a
// reader stuck in read(2) would hang the process — then drain the
@@ -750,6 +761,7 @@ fn localSession(
.command => |line| gpa.free(line),
.paste => |b| gpa.free(@constCast(b)),
.lsp_done => |d| allocs.lsp.free(d.rows),
+ .lsp_status => |text| allocs.lsp.free(text),
.pipe_done => |response_value| {
var response = response_value;
response.deinit(gpa);
@@ -1185,6 +1197,15 @@ const Shell = struct {
s.lsp_task = null;
}
},
+ // Server state on the transient message row — the same row, the
+ // same `message.stamp` clock, and the same shell-side ownership a
+ // completed save uses. The ACTIVE pane, because the state of a
+ // server is session news, not a fact about the pane that asked.
+ .lsp_status => |text| {
+ var mbuf: [256]u8 = undefined;
+ core.setMessage(core.active, message.stamp(&mbuf, "lsp", text));
+ s.lsp_gpa.free(text);
+ },
.pipe_done => |response_value| {
var response = response_value;
core.update(.{ .pipe_resp = .{
@@ -1620,6 +1641,20 @@ fn lspWorker(allocator: std.mem.Allocator, job: *LspJob, loop: *Loop) anyerror!v
return;
}
+/// The registered `lsp.setStatusSink` target, called from the protocol
+/// client's READER threads. Only thread-safe, NON-BLOCKING things happen
+/// here: a dupe with the concurrent lsp allocator and a TRY-post onto the
+/// loop's queue. Never the blocking post — the sink lock is held around this
+/// call, and a full queue plus a teardown spinning on that lock would be a
+/// deadlock; server state is periodic news, so a dropped line is repriced
+/// by the next one.
+fn lspStatusSink(ctx: ?*anyopaque, text: []const u8) void {
+ const s: *Shell = @ptrCast(@alignCast(ctx orelse return));
+ const copy = s.lsp_gpa.dupe(u8, text) catch return;
+ const posted = s.loop.tryPostEvent(.{ .lsp_status = copy }) catch false;
+ if (!posted) s.lsp_gpa.free(copy);
+}
+
fn pipeWorker(
io: std.Io,
gpa: std.mem.Allocator,
@@ -2110,14 +2145,17 @@ const Attach = struct {
// (server.zig `nap`) and the frames simply arrive. `fs_ready`
// belongs to `--fs`, which lives with the core. `lsp_done` and
// `pipe_done` answer work the core dispatches, and it dispatches it
- // there. `pty_read`, `pty_eof` and `files_changed` are the ones
- // that MOVED: the daemon forks the pane shells and holds the
- // inotify instance now, so the only descriptors this process reads
- // are its terminal and one socket. An in-place switch (`Attach` in
- // a local session) cancels its readers and its watcher and drains
+ // there; `lsp_status` narrates servers whose sink the local loop
+ // UNSET in its teardown before this loop started, and the queue
+ // was drained after that, so none is in flight. `pty_read`,
+ // `pty_eof` and `files_changed` are the ones that MOVED: the
+ // daemon forks the pane shells and holds the inotify instance
+ // now, so the only descriptors this process reads are its
+ // terminal and one socket. An in-place switch (`Attach` in a
+ // local session) cancels its readers and its watcher and drains
// this queue before the attached loop starts, so not even a late
// post from the session it just left arrives here.
- .nop, .tick, .fs_ready, .lsp_done, .pipe_done, .pty_read, .pty_eof, .files_changed => {},
+ .nop, .tick, .fs_ready, .lsp_done, .lsp_status, .pipe_done, .pty_read, .pty_eof, .files_changed => {},
.quit => return .none,
.focus_in => {},
.focus_out => return a.send(.pointer_leave),