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.zig152
1 files changed, 32 insertions, 120 deletions
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index e8a36035..ca263f90 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -86,63 +86,14 @@ pub const Command = struct {
};
const Loop = vaxis.Loop(@TypeOf(Command.value));
-/// One language query, owned by the worker that runs it. Everything the
-/// backend may read is copied in here before the worker starts: the core goes
-/// on editing the moment the effect is drained, so a borrowed slice would be a
-/// use-after-free the length of one keystroke.
-const LspJob = struct {
- id: u32,
- kind: pardes.lsp.Kind,
- offset: u32,
- path: []u8,
- source: [:0]u8,
- arg: []u8,
- root: []u8,
+/// The shared snapshot/worker pair every native shell uses. This file used to
+/// carry its own `LspJob` and gui.zig carried a copy of it; the copies said so.
+const lsp_host = @import("../lsp_host.zig");
- fn free(j: *LspJob, allocator: std.mem.Allocator) void {
- allocator.free(j.path);
- allocator.free(j.source);
- allocator.free(j.arg);
- allocator.free(j.root);
- allocator.destroy(j);
- }
-};
-
-const max_pipe_tasks = 16;
-
-const PipeTask = struct {
- id: u32,
- future: std.Io.Future(anyerror!void),
-};
-
-const PipeTasks = struct {
- items: [max_pipe_tasks]PipeTask = undefined,
- len: usize = 0,
-
- fn full(tasks: *const PipeTasks) bool {
- return tasks.len == tasks.items.len;
- }
-
- fn add(tasks: *PipeTasks, task: PipeTask) void {
- std.debug.assert(!tasks.full());
- tasks.items[tasks.len] = task;
- tasks.len += 1;
- }
-
- fn finish(tasks: *PipeTasks, io: std.Io, id: u32) void {
- for (tasks.items[0..tasks.len], 0..) |*task, i| if (task.id == id) {
- task.future.await(io) catch {};
- tasks.len -= 1;
- std.mem.copyForwards(PipeTask, tasks.items[i..tasks.len], tasks.items[i + 1 .. tasks.len + 1]);
- return;
- };
- }
-
- fn cancelAll(tasks: *PipeTasks, io: std.Io) void {
- for (tasks.items[0..tasks.len]) |*task| task.future.cancel(io) catch {};
- tasks.len = 0;
- }
-};
+/// The pipe in-flight set moved to `selection_pipe.Tasks`, beside the Job it
+/// tracks: gui.zig carried this same table verbatim.
+const PipeTask = selection_pipe.Tasks.Task;
+const PipeTasks = selection_pipe.Tasks;
const Pty = struct {
file: std.Io.File,
@@ -655,13 +606,10 @@ fn localSession(
} else try pardes.Pardes.init(allocs.pardes, options);
defer core.deinit();
- // Private, complete before any fork and retained until the last possible
- // spawn; children borrow only these stable in-struct path buffers.
- var prompt_rcs = shell_bin.PromptRcs.init();
+ // PATH, the bash banner and the prompt rc files, in the one order that
+ // works. Children borrow only these stable in-struct path buffers.
+ var prompt_rcs = shell_bin.prepareForFork();
defer prompt_rcs.deinit();
- // macos: apple's bash 3.2 prints the zsh-deprecation banner into every
- // pane unless this is in the env BEFORE bash starts (the rc is too late)
- if (comptime builtin.os.tag.isDarwin()) _ = setenv("BASH_SILENCE_DEPRECATION_WARNING", "1", 1);
var frame_arena: std.heap.ArenaAllocator = .init(allocs.frame);
defer frame_arena.deinit();
@@ -1527,39 +1475,7 @@ const Shell = struct {
fn lsp(ctx: ?*anyopaque, req: host_api.LspRequest) void {
const s = of(ctx);
if (!s.threads_ok) return; // pre-loop drain: nothing to answer to yet
- const pane = s.core.panes[req.pane] orelse return;
- // a pane with no file still asks `status` (it is about the backend,
- // not the buffer): empty path and source, root from the pane's cwd
- const f = pane.file;
- const a = s.lsp_gpa;
- const job = a.create(LspJob) catch return;
- job.* = .{
- .id = req.id,
- .kind = req.kind,
- .offset = req.offset,
- .path = a.dupe(u8, if (f) |ff| ff.path else "") catch {
- a.destroy(job);
- return;
- },
- .source = a.dupeZ(u8, if (f) |ff| ff.content else "") catch {
- a.free(job.path);
- a.destroy(job);
- return;
- },
- .arg = a.dupe(u8, req.arg) catch {
- a.free(job.path);
- a.free(job.source);
- a.destroy(job);
- return;
- },
- .root = a.dupe(u8, if (f) |ff| (std.fs.path.dirname(ff.path) orelse "/") else pane.cwdSlice()) catch {
- a.free(job.path);
- a.free(job.source);
- a.free(job.arg);
- a.destroy(job);
- return;
- },
- };
+ const job = lsp_host.snapshot(s.lsp_gpa, s.core, req) orelse return;
// ponytail: ONE query in flight, so one future slot. Replacing it
// cancels-then-joins the previous worker, which for a backend that
// ignores cancellation means waiting out a query the user already
@@ -1569,8 +1485,8 @@ const Shell = struct {
old.cancel(s.io) catch {};
s.lsp_task = null;
}
- s.lsp_task = s.io.concurrent(lspWorker, .{ a, job, s.loop }) catch {
- job.free(a);
+ s.lsp_task = s.io.concurrent(lspWorker, .{ s.lsp_gpa, job, s.loop }) catch {
+ job.free(s.lsp_gpa);
return;
};
}
@@ -1588,7 +1504,9 @@ const Shell = struct {
job.deinit(s.gpa);
return;
};
- s.pipe_tasks.add(.{ .id = id, .future = future });
+ // `full()` was checked above, so this cannot fail; assert rather than
+ // discard, because a silently dropped task is a future nobody joins.
+ std.debug.assert(s.pipe_tasks.add(.{ .id = id, .future = future }));
}
};
@@ -1617,30 +1535,24 @@ fn requestClipboard(vx: *vaxis.Vaxis, tty: *vaxis.Tty) void {
vx.requestSystemClipboard(tty.writer()) catch {};
}
-/// Answer a language query off the event loop and post the rows back. This is
-/// the whole async execution model: the same shape as readPty — do the slow
-/// thing on a worker, hand the result to the loop as an event, let the core
-/// stay a state machine that never blocks.
-fn lspWorker(allocator: std.mem.Allocator, job: *LspJob, loop: *Loop) anyerror!void {
- defer job.free(allocator);
- var arena: std.heap.ArenaAllocator = .init(allocator);
- defer arena.deinit();
- // The shell owns the result buffer; the backend only ever writes to it.
- var out: std.Io.Writer.Allocating = .init(allocator);
- defer out.deinit();
- pardes.lsp.query(allocator, arena.allocator(), .{
- .kind = job.kind,
- .path = job.path,
- .source = job.source,
- .offset = job.offset,
- .arg = job.arg,
- .root = job.root,
- }, &out.writer);
- const rows = allocator.dupe(u8, out.written()) catch return;
- loop.postEvent(.{ .lsp_done = .{ .id = job.id, .rows = rows } }) catch allocator.free(rows);
- return;
+/// Answer a language query off the event loop and post the rows back. The
+/// snapshot and the query body are `lsp_host`'s; the only part that is this
+/// shell's is the vaxis event the rows travel home on.
+fn lspWorker(allocator: std.mem.Allocator, job: *lsp_host.Job, loop: *Loop) anyerror!void {
+ var sink: LspRowSink = .{ .allocator = allocator, .loop = loop };
+ lsp_host.work(allocator, job, &sink, LspRowSink.take);
}
+const LspRowSink = struct {
+ allocator: std.mem.Allocator,
+ loop: *Loop,
+
+ fn take(ctx: ?*anyopaque, id: u32, rows: []u8) void {
+ const s: *LspRowSink = @ptrCast(@alignCast(ctx orelse return));
+ s.loop.postEvent(.{ .lsp_done = .{ .id = id, .rows = rows } }) catch s.allocator.free(rows);
+ }
+};
+
/// 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