summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 12:24:12 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit276a060dee5c753b513a75cca0b2c942dfe540fd (patch)
tree16c558a5d4bcaaef587ec9060c1ba212c662678d
parentd191193a1e707a6bfd8fc8bbe1429dbc9f34676f (diff)
downloadpardes-276a060dee5c753b513a75cca0b2c942dfe540fd.tar.gz
pardes-276a060dee5c753b513a75cca0b2c942dfe540fd.zip
A name cut across writes on one open is one name, applied once
Each write to name was taken as a whole name, so a name a client wrote in two pieces renamed the pane twice, the first time to half a name. name's writes are now held per open, as exec's and ctl's lines are, until the newline or the close. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/fs.md5
-rw-r--r--src/ninep/pane.zig17
-rw-r--r--src/ninep/tree.zig4
3 files changed, 23 insertions, 3 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 3acee020..dc15ab91 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -554,8 +554,9 @@ writing it renames the buffer; a relative name resolves against the pane's
directory. A name alone is no edit: the pane's `dirty` stays what its text made it
(a renamed clean file is still 0, and nothing asks about it at Exit,
Restore or Del, which ask only about text edited), and `Save` writes it
-under the new name all the same. A write is one name: one trailing newline
-is its end and nothing else is trimmed. A blank inside a name is taken
+under the new name all the same. An open's writes are one name: held
+until its newline, or its close when it has none, then applied once, however
+the writes cut it; nothing else is trimmed. A blank inside a name is taken
(`two words.zig`); refused, EINVAL, in acme's words (xfid.c:650) and why,
are a blank at either end (not quietly cut off), `bad character in file
name: a blank at its end`, a second line, `...: a newline (a name is one
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index fe3312b4..9cb1dba6 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -1057,6 +1057,23 @@ test "the writes of one open are one undo step, as bash's line-buffered printf m
}
}
+test "a name cut across writes is one name, applied once at its newline or its close" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ const node = Node.of(serial, .name);
+ const h = call(p, .{ .tag = 1, .op = .open, .node = node, .omode = 1 }).reply.handle;
+ try testing.expect(h != 0);
+ _ = call(p, .{ .tag = 2, .op = .write, .node = node, .handle = h, .data = "/tmp/pardes-na" });
+ try testing.expectEqualStrings("/test.txt", nameOf(p.panes[p.paneBySerial(serial).?].?));
+ _ = call(p, .{ .tag = 2, .op = .write, .node = node, .handle = h, .data = "me.txt\n" });
+ try testing.expectEqualStrings("/tmp/pardes-name.txt", nameOf(p.panes[p.paneBySerial(serial).?].?));
+ // No newline: the close applies it.
+ _ = call(p, .{ .tag = 2, .op = .write, .node = node, .handle = h, .data = "/tmp/pardes-closed.txt" });
+ _ = call(p, .{ .tag = 3, .op = .release, .node = node, .handle = h, .opened = true });
+ try testing.expectEqualStrings("/tmp/pardes-closed.txt", nameOf(p.panes[p.paneBySerial(serial).?].?));
+}
+
test "a replace through data, truncate then write, is one undo step" {
const p = try withFile(testing.allocator, "one two\n");
defer p.deinit();
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index a8b86a96..4b52be51 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -998,7 +998,9 @@ fn linesFile(target: Target) bool {
return switch (target) {
.top => |f| f == .look or f == .exec or f == .ctl or f == .tagexec,
.col => |c| c.file == .ctl or c.file == .exec,
- .pane => |t| t.file == .look or t.file == .exec or t.file == .ctl or t.file == .tagexec,
+ // `name` too: one name, however its writes cut it, applied once
+ // its newline or its close comes.
+ .pane => |t| t.file == .look or t.file == .exec or t.file == .ctl or t.file == .tagexec or t.file == .name,
};
}