From f67fec978a9296c651ec06bd2f43686d34ff86ee Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 14 Aug 2026 13:40:29 -0300 Subject: look: richer path/range parsing, pdf rendering, corner-drag and stepgrain snapshots --- src/tty/tty.zig | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'src/tty/tty.zig') diff --git a/src/tty/tty.zig b/src/tty/tty.zig index eadc3a9d..f6d095e5 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -456,6 +456,11 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void { // per-slot spawn generation: a reused pane id ignores the old shell's // late pty_eof (which would otherwise close the NEW pty on that slot) var gens: [pardes.MAX_PANES]u32 = @splat(0); + // ...and the core's one way to ask about those ptys: is a pane's tty still + // the prompt we forked? Installed here rather than polled per frame (see + // the cwd loop) and re-installed on a replay core, which owns a fresh set + // of panes over this same table. + core.tty_query = .{ .ctx = &ptys, .taken = &ttyTakenAt }; // the single in-flight language query (see the .lsp effect) var lsp_task: ?std.Io.Future(anyerror!void) = null; // Selection filters may overlap: a second submit supersedes the first in @@ -811,6 +816,7 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void { while (image_iterator.next()) |handle| vx.freeImage(tty.writer(), handle.id); kitty_handles.clearRetainingCapacity(); nc.native_images = vx.caps.kitty_graphics; + nc.tty_query = .{ .ctx = &ptys, .taken = &ttyTakenAt }; core.deinit(); core = nc; if (comptime pardes.pdf_enabled) { @@ -822,7 +828,10 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void { } } - // live cwd for tags/look: cheap per-pane lookup, per frame + // live cwd for tags/look: cheap per-pane lookup, per frame. Whether the + // pane's tty still belongs to the prompt we forked is NOT polled here — + // it is a walk through /proc and nothing draws it, so the core pulls it + // through ttyQuery below, at the Exec that cares. for (&ptys, 0..) |*slot, id| if (slot.*) |pt| { var lbuf: [1024]u8 = undefined; if (look.shellCwd(pt.pid, &lbuf)) |cwd| core.setCwd(id, cwd); @@ -989,7 +998,9 @@ fn drainEffects( } const child = forkShell(core.shellBin(), cwd_z, core.screen_h, core.screen_w); ptys[sp.pane] = .{ .file = child.file, .pid = child.pid, .reader = .{ .any_future = null, .result = {} } }; - // report the pane's starting directory back to the core (tags) + // report the pane's starting directory back to the core (tags). The + // slot needs no occupancy reset: nothing is remembered, and the next + // Exec asks about the shell that is there now. var lbuf: [1024]u8 = undefined; if (look.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(sp.pane, wd); if (threads_ok) { @@ -1275,6 +1286,17 @@ fn winchWatch(loop: *Loop, vx: *vaxis.Vaxis, tty: *vaxis.Tty) void { } } +/// The core's `tty_query`: answer for one pane out of this host's pty table. +/// Lazy by construction — it runs only where the core is about to type a +/// command line, so the /proc walk costs nothing on an ordinary frame. A pane +/// with no pty of ours (a document, a slot whose shell already died) is not a +/// terminal a program can be holding. +fn ttyTakenAt(ctx: ?*anyopaque, pane: usize) bool { + const table: *const [pardes.MAX_PANES]?Pty = @ptrCast(@alignCast(ctx orelse return false)); + const pt = table[pane] orelse return false; + return look.ttyTaken(pt.pid, pt.file.handle); +} + fn forkShell(bin: []const u8, cwd: ?[*:0]const u8, rows: u16, cols: u16) struct { file: std.Io.File, pid: posix.pid_t } { var master: c_int = undefined; // resolved BEFORE the fork, into this frame, which the child inherits: -- cgit v1.3