diff options
Diffstat (limited to 'src/builtins.zig')
| -rw-r--r-- | src/builtins.zig | 64 |
1 files changed, 63 insertions, 1 deletions
diff --git a/src/builtins.zig b/src/builtins.zig index 2c52b5f9..0e92f5ce 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -280,6 +280,58 @@ pub const Restore = struct { } }; +/// `Attach [name]` — hand this frontend's screen to a detached core, the one +/// `pardes --detach [name]` left running. Bare, it means "the session that is +/// there", which is the case worth typing: one detached session, and one word +/// to walk back into it. +/// +/// Nothing is torn down HERE, and that is the feature rather than an omission. +/// The effect only ASKS; the shell connects first and swaps second, so an +/// Attach that reaches nothing leaves this instance with every pane and every +/// undo exactly where they were and a line on the message row. Absent where +/// there is no unix socket to attach to — the browser and the board — and also +/// absent where the frontend would never NOTICE the request: see +/// `pardes.can_attach`, which is narrower than `hosted` because macOS never +/// polls `takeAttach`, so the word would have queued an effect and then done +/// nothing at all. +pub const Attach = struct { + pub const takes_arg = true; + pub const enabled = pardes.can_attach; + pub fn run(c: Ctx) void { + if (comptime enabled) ask(c) else unreachable; + } + /// A name too long for `Effect.attach` is too long for `sun_path` several + /// times over, so it can never name a session: reporting it here is the + /// same answer a failed connect gets, one round trip earlier. + fn ask(c: Ctx) void { + const name = c.arg orelse ""; + if (name.len > pardes.attach_name_max) + return c.p.reportError(c.id, comptime word(@This()), error.NameTooLong); + c.p.emit(.{ .attach = .{ .pane = @intCast(c.id), .name = .from(name) } }); + } +}; + +/// `Detach` — leave the session and let it carry on without you, which is +/// tmux's detach-client. Executed inside an ATTACHED frontend, where it +/// travels to the daemon as an ordinary command line, is run by the core that +/// owns the panes, and comes back as the effect that dismisses the screen +/// which asked for it. Hence no argument: the daemon knows who typed. +/// +/// It is NOT `Attach` backwards, and no word here is. Making a live local +/// session outlive its terminal means setsid and a fork; a word that pretended +/// to would hand you a session that dies with the window it was typed in. Run +/// locally this therefore REPORTS rather than acts — see the `.detach` arm of +/// Pardes.perform, which finds no host method to call. +pub const Detach = struct { + pub const enabled = pardes.can_attach; + pub fn run(c: Ctx) void { + if (comptime enabled) + c.p.emit(.{ .detach = .{ .pane = @intCast(c.id) } }) + else + unreachable; + } +}; + // ---- the message row ---- /// TEXT onto this pane's transient message row — the row a failed save, a @@ -814,7 +866,17 @@ pub const Last = struct { while (i > 0) { i -= 1; const j = c.p.jumps[i]; - if (j.pane != c.id) return c.p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }); + // `.keep`: Esc is a RETURN, and the pane still holds the view it + // was left with. Recentring it moved the whole screen to show a line + // that was, nearly always, already on it. + // + // Not `line = 0`, which focusPaneLine already understands as "focus + // and touch nothing": a background pane's view CAN move while you + // are away — the wheel scrolls the pane under the pointer, not the + // active one, and a resize recomputes geometry without revealing any + // cursor — so `.keep` restores the recorded cursor and lets + // ensureCursorVisible pull it back on screen by the least it can. + if (j.pane != c.id) return c.p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }, .keep); } } }; |
