summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 14:20:12 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit25c847e28ae77f5c648d423d64011736f55eda4a (patch)
tree38a2e3694a1c4db8ee0f6acebb420109e00d020a /src/pardes.zig
parentb7d3c0c1d9b2ba4e5181e56f15015dd9c996c5a8 (diff)
downloadpardes-25c847e28ae77f5c648d423d64011736f55eda4a.tar.gz
pardes-25c847e28ae77f5c648d423d64011736f55eda4a.zip
Delcol on the last column leaves the window, empty but for its workspace tag, as acme's does
The last column's Delcol ended the session. The window now stays, with only the workspace tag; Newcol, and New (which makes the column it goes in), start it again. A word run with no pane at all runs with a stand-in that is no pane, the root ctl takes session words, and pane/new makes its scratch in the session's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig56
1 files changed, 50 insertions, 6 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index ec406094..4bc5829b 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4555,6 +4555,10 @@ pub const Pardes = struct {
/// where the shell wrote the last dump (shell reports back after
/// write_dump); shown in the topbar as `Restore <path>`
last_dump: ?[]const u8 = null,
+ /// What a session word (Newcol, New, Exit) runs with when no pane is
+ /// there to run it: the window Delcol emptied. Never installed, made the
+ /// first time it is wanted.
+ stand_in: ?*Pane = null,
/// a Restore builtin wants this dump loaded into the live session; the
/// shell consumes it via takeRestore each frame (restore contents stay host-fed)
restore_req: ?[]const u8 = null,
@@ -4780,6 +4784,7 @@ pub const Pardes = struct {
for (p.panes) |slot| if (slot) |pane| p.noteClosed(pane);
p.saveRecent();
p.recent.deinit(p.recentGpa());
+ if (p.stand_in) |pane| p.teardownPane(pane);
look.cancelLookHover(p);
if (p.repl_ask) |ask| p.gpa.free(ask.text);
for (0..MAX_PANES) |id| p.dropPendingWrite(id);
@@ -5024,7 +5029,8 @@ pub const Pardes = struct {
}
fn newScratch(p: *Pardes, from_id: usize, free: usize, kind: exec.NewPane) !*Pane {
- const src = p.panes[from_id] orelse return error.MissingPane;
+ // None asking (an empty window): the session's, as pane/new's is.
+ const src_or = p.panes[from_id];
const content = try p.gpa.dupe(u8, "");
errdefer p.gpa.free(content);
// A terminal that has not said where it is yet (the boot layout's)
@@ -5032,10 +5038,10 @@ pub const Pardes = struct {
// 9P's pane/new, and a New from a column's or the workspace's tag:
// no pane asked for it, whichever last had the keyboard (acme's new
// window has acme's directory).
- const own = kind != .fs and !p.exec_header;
- const dir = if (own) paneDir(src) else "";
+ const src = if (kind != .fs and !p.exec_header) src_or else null;
+ const dir = if (src) |s| paneDir(s) else "";
const np = try panes.Output.open(p, free, if (dir.len == 0) p.sessionDir() else dir, .{ .cmd = .New }, "", content);
- if (own) np.cwd = .{ .inherited = src };
+ if (src) |s| np.cwd = .{ .inherited = s };
np.body.cur_pinned = true;
return np;
}
@@ -5056,6 +5062,8 @@ pub const Pardes = struct {
/// Newcol: an empty column right of the calling pane's, the keyboard
/// on its tag, for the next pane made to fill.
pub fn newColumn(p: *Pardes, from_id: usize) void {
+ // An empty window (Delcol took the last column): its first column.
+ if (layout.firstColumn(p)) |c| return tagline.enterHeader(p, c);
const col = p.keyboardColumn(from_id) orelse return;
// Said, not silently nothing: "no space" is 9ns's word for ENOSPC.
const c = layout.insertColumn(p, col, false) orelse {
@@ -5331,6 +5339,12 @@ pub const Pardes = struct {
}
}
+ /// The pane a session word runs with in an empty window.
+ pub fn standIn(p: *Pardes) ?*Pane {
+ if (p.stand_in == null) p.stand_in = panes.Terminal.createDoc(p.gpa, p.screen_w, p.screen_h) catch null;
+ return p.stand_in;
+ }
+
/// Drop a pane's parked bytes: its pty is gone, and the slot it occupied
/// may be handed to a different pane next frame.
fn dropPendingWrite(p: *Pardes, id: usize) void {
@@ -6756,7 +6770,9 @@ pub const Pardes = struct {
p.panes[closed] = null;
}
layout.dropColumn(p, col);
- if (p.panes[p.active] == null) p.active = beside orelse p.anyPane() orelse return p.quitEmpty();
+ // The last column gone, the window stays, empty but for the
+ // workspace tag, as acme's does; Newcol starts again from there.
+ if (p.panes[p.active] == null) p.active = beside orelse p.anyPane() orelse return tagline.enterHeader(p, null);
}
/// No pane is left: the session is over.
@@ -6798,7 +6814,7 @@ pub const Pardes = struct {
}
/// Any live pane: the last resort when nothing took the closed one's place.
- fn anyPane(p: *const Pardes) ?usize {
+ pub fn anyPane(p: *const Pardes) ?usize {
for (p.panes, 0..) |slot, k| if (slot != null) return k;
return null;
}
@@ -8517,3 +8533,31 @@ test "a Save the host could not do logs no save record" {
p.perform(.{ .save_file = .{ .pane = 0 } });
try std.testing.expect(th.logHas(p, "\nsave "));
}
+
+test "Delcol on the last column leaves the window, drawn and typed into, and Newcol starts it again" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ while (p.ncol > 0) {
+ p.exec_column = 0;
+ _ = p.executeBuiltinLine(p.active, "Delcol");
+ p.exec_column = null;
+ p.sync();
+ }
+ try std.testing.expect(!p.quit);
+ try std.testing.expect(p.anyPane() == null);
+ // Drawn, and keys go to the workspace tag.
+ _ = try p.render(p.frame_arena.allocator());
+ p.update(.{ .key = .{ .cp = 'x', .text = "x" } });
+ p.update(.{ .key = .{ .cp = Key.enter } });
+ p.sync();
+ _ = try p.render(p.frame_arena.allocator());
+ try std.testing.expect(!p.quit);
+ _ = p.executeBuiltinLine(p.active, "Newcol");
+ p.sync();
+ try std.testing.expectEqual(@as(usize, 1), p.ncol);
+ _ = p.executeBuiltinLine(p.active, "New");
+ p.sync();
+ try std.testing.expect(p.anyPane() != null);
+ _ = try p.render(p.frame_arena.allocator());
+}