summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 03:45:25 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit3eaa4af06c5f997f5d054fde149f7bcc26f1fea3 (patch)
treee7d9d3a2f44af3c8b69740f8ac7acb63a99e6cfd
parent69f8e4703f6c1204f9f0958f264d2a4c4600026f (diff)
downloadpardes-3eaa4af06c5f997f5d054fde149f7bcc26f1fea3.tar.gz
pardes-3eaa4af06c5f997f5d054fde149f7bcc26f1fea3.zip
A command run from a column's tag gets its pane in that column
A command from a column's tag ran in a command pane at the foot of the last column, however far from the tag, and could reuse a finished one anywhere. It now goes to the tag's column, under both placements, and reuses only a finished command pane in that column. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/tags.md4
-rw-r--r--src/exec.zig13
-rw-r--r--src/ninep/cols.zig19
3 files changed, 31 insertions, 5 deletions
diff --git a/docs/tags.md b/docs/tags.md
index 2cd3f16b..3db7f465 100644
--- a/docs/tags.md
+++ b/docs/tags.md
@@ -273,7 +273,9 @@ never into a new column:
- `New` goes into its own column, the bottom half of its last pane
(look.c:921-923);
- a command pane or a `+Errors` pane goes to the last column, the bottom
- half of its last pane (util.c:94-98).
+ half of its last pane (util.c:94-98); a command run from a column's tag
+ goes to that column instead, and reuses a finished command pane only in
+ that column.
`Placement pardes` is what pardes did before: an empty column whose tag asked,
or has the keyboard, is filled; a scratch goes right under the pane that asked;
diff --git a/src/exec.zig b/src/exec.zig
index ddb89d33..6e6f528b 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -585,8 +585,11 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize {
const dir = p.execDir(from);
// Not one whose pty a job it left behind still prints to: reusing it
// would hang that job up.
- const reuse: ?usize = if (src.command != null and src.command_done and !src.command_pty) from else for (p.panes, 0..) |slot, i| {
+ // From a column's tag, only a finished pane in that column is reused,
+ // as the new one goes in that column (placeNew).
+ const reuse: ?usize = if (p.exec_column == null and src.command != null and src.command_done and !src.command_pty) from else for (p.panes, 0..) |slot, i| {
const other = slot orelse continue;
+ if (p.exec_column) |c| if (if (layout.findPane(p, i)) |f| f.col != c else true) continue;
if (other.command != null and other.command_done and !other.command_pty and std.mem.eql(u8, other.cwdSlice(), dir)) break i;
} else null;
if (reuse) |id| {
@@ -838,8 +841,10 @@ pub fn placeNew(p: *Pardes, from: usize, origin: ?usize, id: usize, kind: NewPan
// New: the bottom half of the last pane in its own column
// (look.c:921-923, et->col).
.scratch => layout.acmePlace(p, p.exec_column orelse columnOf(p, from), null, id),
- // +Errors, a command's output: the last column (util.c:94-98).
- .command, .errors => layout.acmePlace(p, p.ncol - 1, null, id),
+ // +Errors, a command's output: the last column (util.c:94-98);
+ // a command run from a column's tag, that column.
+ .command => layout.acmePlace(p, p.exec_column orelse p.ncol - 1, null, id),
+ .errors => layout.acmePlace(p, p.ncol - 1, null, id),
// makenewwindow(nil) for 9P's new (acme.c:877), else (t).
.fs => layout.acmePlace(p, acmeColumn(p, from), null, id),
.shell, .doc => layout.acmePlace(p, acmeColumn(p, from), asked, id),
@@ -928,7 +933,7 @@ fn pardesPlace(p: *Pardes, from: usize, id: usize, kind: NewPane) void {
.shell => belowParent(p, from, id),
.doc, .errors => pardesDoc(p, from, id),
.command => {
- const rc = p.ncol - 1;
+ const rc = p.exec_column orelse p.ncol - 1;
if (p.col_n[rc] > 0) belowParent(p, p.col_panes[rc][p.col_n[rc] - 1], id) else layout.insert(p, rc, 0, id);
},
}
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig
index c55c1e73..ef2e6cbd 100644
--- a/src/ninep/cols.zig
+++ b/src/ninep/cols.zig
@@ -337,3 +337,22 @@ test "a word run from the workspace's or a column's tag runs in the session's di
const made = p.paneBySerial(p.fs.results[0]).?;
try testing.expectEqualStrings(pardes.Pardes.paneDir(p.panes[0].?), pardes.Pardes.paneDir(p.panes[made].?));
}
+
+test "a command run from a column's tag gets its pane in that column, not the last" {
+ const p = try th.withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ p.newColumn(p.active);
+ p.newScratchFor(p.active, .fs); // fills the new, right column
+ p.sync();
+ const left = layout.columnSerial(p, 0);
+ try testing.expectEqual(tree.Status.ok, th.wr(p, Node.ofCol(left, .exec), "true\n").reply.status);
+ const made = p.paneBySerial(p.fs.results[0]).?;
+ try testing.expectEqual(@as(usize, 0), layout.findPane(p, made).?.col);
+ // Finished, it is reused only by its own column's commands.
+ p.panes[made].?.command_done = true;
+ p.panes[made].?.command_pty = false;
+ try testing.expectEqual(tree.Status.ok, th.wr(p, Node.ofCol(layout.columnSerial(p, 1), .exec), "true\n").reply.status);
+ const other = p.paneBySerial(p.fs.results[0]).?;
+ try testing.expect(other != made);
+ try testing.expectEqual(@as(usize, 1), layout.findPane(p, other).?.col);
+}