summaryrefslogtreecommitdiff
path: root/src/builtins.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/builtins.zig')
-rw-r--r--src/builtins.zig64
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);
}
}
};