diff options
Diffstat (limited to 'src/tty/tty.zig')
| -rw-r--r-- | src/tty/tty.zig | 50 |
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), |
