summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 06:18:15 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit399d005f1997f23d50f36829946f2d5d7a98b928 (patch)
tree1200840db782be0e1b85e44734596e3e065ed6fc
parentc78641fdbe931ca2e6e87eb3e001bd021c1eba3f (diff)
downloadpardes-399d005f1997f23d50f36829946f2d5d7a98b928.tar.gz
pardes-399d005f1997f23d50f36829946f2d5d7a98b928.zip
A pane from pane/new is the session's directory's scratch, not the last active pane's
No pane asked for it, so borrowing the directory of whichever pane last had the keyboard named it after an accident; acme's new window has acme's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/fs.md4
-rw-r--r--src/ninep/pane.zig8
-rw-r--r--src/ninep/tree.zig9
-rw-r--r--src/pardes.zig14
4 files changed, 24 insertions, 11 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 9ba9c0e4..0da2ccd0 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -71,7 +71,9 @@ for any process, and `$NINE_MOUNT/pardes/NAME/` a `--detach=NAME` session's.
9ns exports `$NINE_MOUNT` to everything it starts, so a script checks that
variable to know the mount is there, and takes the name from `$PARDES_9P`
(`pardes-9p-<pid or NAME>.sock`). A new pane made through `pane/new` is a scratch named
-`<dir>/+New` until it is given a name. A column may hold no pane, as in acme:
+`<dir>/+New` until it is given a name, where `<dir>` is the session's
+directory (the one pardes started in), whichever pane last had the keyboard:
+no pane asked for it, as acme's new window has acme's directory. A column may hold no pane, as in acme:
`Newcol` makes one empty, and closing a column's last pane leaves it empty
with its tag holding the keyboard (`focus` reads empty) and logs only the
`del`. `pane/new` places its pane as acme's makenewwindow(nil) does: in the
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 899d8528..61642999 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -1143,11 +1143,15 @@ test "relative names use the file directory and can name a new target" {
try testing.expect(dirtyOf(pane));
}
-test "relative names follow inherited scratch and virtual directories" {
+test "relative names follow a pane/new scratch's session directory and virtual directories" {
const p = try withFile(testing.allocator, "source body\n");
defer p.deinit();
const source = p.panes[0].?;
- try testing.expectEqual(Status.ok, wr(p, Node.of(source.serial, .name), "/project/src/source.zig\n").reply.status);
+ try testing.expectEqual(Status.ok, wr(p, Node.of(source.serial, .name), "/elsewhere/source.zig\n").reply.status);
+ // pane/new's scratch is the session's, not the active pane's.
+ const session = "/project/src";
+ @memcpy(p.session_dir[0..session.len], session);
+ p.session_dir_len = session.len;
const serial = try th.newPane(p);
const scratch = p.panes[p.paneBySerial(serial).?].?;
const name = Node.of(serial, .name);
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index af1c589d..69b89f53 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -1326,6 +1326,10 @@ test "opening /pane/new makes a pane and removing one closes it" {
const id = p.paneBySerial(serial).?;
try testing.expectEqualStrings("", p.panes[id].?.file.?.content);
+ // Its directory is the session's, not that of /test.txt, the pane
+ // that happened to have the keyboard.
+ var named: [4096]u8 = undefined;
+ try testing.expectEqualStrings(try std.fmt.bufPrint(&named, "{s}/+New", .{p.sessionDir()}), p.panes[id].?.file.?.path);
_ = wr(p, Node.of(serial, .body), "hi");
// A second open is a second pane, and releasing a clone fid leaves the
@@ -1433,8 +1437,9 @@ test "editor paths resolve to the same nodes the wire serves" {
test "at the pane cap, pane/new, look and New each say so, and look reads back empty" {
const p = try th.withFile(testing.allocator, "x\n");
defer p.deinit();
- // Tall enough that the column holds every pane at its tag and two rows.
- p.update(.{ .resize = .{ .cols = 80, .rows = pardes.MAX_PANES * 4 } });
+ // Tall enough that the column holds every pane at its tag and two rows,
+ // and wide enough that each tag, the session directory's +New, is one.
+ p.update(.{ .resize = .{ .cols = 400, .rows = pardes.MAX_PANES * 4 } });
const new = @intFromEnum(TopFile.new);
while (p.freeSlot() != null) try testing.expectEqual(Status.ok, call(p, .{ .tag = 1, .op = .open, .node = new }).reply.status);
var want: [64]u8 = undefined;
diff --git a/src/pardes.zig b/src/pardes.zig
index 6341de91..8e9d3c7e 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4588,7 +4588,7 @@ pub const Pardes = struct {
// text under the shell. The shell keeps the keyboard. A boot
// layout, like .classic's, not a placement.
if (p.boot == .tty) {
- const np = try p.newScratch(0, 1);
+ const np = try p.newScratch(0, 1, .scratch);
layout.insert(p, 0, 1, 1);
layout.splitBelow(p, 0, np);
}
@@ -4846,15 +4846,17 @@ pub const Pardes = struct {
return pane;
}
- fn newScratch(p: *Pardes, from_id: usize, free: usize) !*Pane {
+ fn newScratch(p: *Pardes, from_id: usize, free: usize, kind: exec.NewPane) !*Pane {
const src = p.panes[from_id] orelse return error.MissingPane;
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)
- // has no directory: the scratch is the session's, <cwd>/+New.
- const dir = paneDir(src);
+ // has no directory: the scratch is the session's, <cwd>/+New. So is
+ // 9P's pane/new: no pane asked for it, whichever last had the
+ // keyboard (acme's new window has acme's directory).
+ const dir = if (kind == .fs) "" else paneDir(src);
const np = try panes.Output.open(p, free, if (dir.len == 0) p.sessionDir() else dir, .{ .cmd = .New }, "", content);
- np.cwd = .{ .inherited = src };
+ if (kind != .fs) np.cwd = .{ .inherited = src };
np.body.cur_pinned = true;
return np;
}
@@ -4868,7 +4870,7 @@ pub const Pardes = struct {
/// A scratch for New (`.scratch`) or 9P's pane/new (`.fs`).
pub fn newScratchFor(p: *Pardes, from_id: usize, kind: exec.NewPane) void {
const free = p.freeSlot() orelse return p.reportError(from_id, "New", error.NoPaneSlots);
- _ = p.newScratch(from_id, free) catch return;
+ _ = p.newScratch(from_id, free, kind) catch return;
exec.placeNew(p, from_id, from_id, free, kind);
}