summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 10:18:59 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commitd9c6786e19567878e3c89ae212296ce941484096 (patch)
tree23b973692fc258bd7910cadf135d06776015da6f /src/ninep
parent5b27be228f5961dee8727bec7480f375dfb588ed (diff)
downloadpardes-d9c6786e19567878e3c89ae212296ce941484096.tar.gz
pardes-d9c6786e19567878e3c89ae212296ce941484096.zip
Truncating data or xdata deletes only the addressed range, not the whole body
A shell's echo NEW > data opens data with OTRUNC, and truncate() spliced the whole body away before the write replaced the addressed range: a five-byte replacement erased a buffer. data and xdata hold what addr names, so truncating one now deletes that range and nothing else; > on data replaces the range and : > data deletes it, and only truncating body clears the buffer. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/pane.zig32
1 files changed, 29 insertions, 3 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 1e549107..822e0704 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -568,16 +568,27 @@ pub fn appendErrors(p: *Pardes, id: usize, text: []const u8) ?usize {
}
/// Truncation empties whatever the file holds: the body, the tag's editable
-/// tail (acme's `cleartag`), a range. Every other setattr just answers fresh
-/// attributes.
+/// tail (acme's `cleartag`), a range. `data` and `xdata` hold what `addr`
+/// addresses, so truncating one deletes that range and no more, and a
+/// shell's `echo new > data` replaces it. Every other setattr just answers
+/// fresh attributes.
pub fn truncate(p: *Pardes, pane: *Pane, file: PaneFile) tree.Status {
const pf = &pane.fs;
switch (file) {
- .body, .data, .xdata => if (fileOf(pane) != null) {
+ .body => if (fileOf(pane) != null) {
_ = spliceBody(p, pane, 0, bodyOf(pane).len, "") orelse return .err;
pf.addr = .{};
setDot(pane, .{});
},
+ .data, .xdata => if (fileOf(pane) != null) {
+ clampAddr(pf, bodyOf(pane).len);
+ const q0 = pf.addr.q0;
+ const q1 = @max(q0, pf.addr.q1);
+ const before = dotOf(pane);
+ _ = spliceBody(p, pane, q0, q1, "") orelse return .err;
+ setDot(pane, .{ .q0 = shiftOne(before.q0, q0, q1 - q0, 0), .q1 = shiftOne(before.q1, q0, q1 - q0, 0) });
+ pf.addr = .{ .q0 = q0, .q1 = q0 };
+ },
.tag => {
const empty = p.gpa.alloc(u8, 0) catch return .err;
// An edit like a keyboard one: `u` in the tag brings it back.
@@ -1036,6 +1047,21 @@ test "errors append to one +Errors buffer per directory" {
try testing.expectEqual(@as(usize, 1), found);
}
+test "truncating data deletes only the addressed range, so > data replaces it" {
+ const p = try withFile(testing.allocator, "hello world\nsecond line\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ for ([_]PaneFile{ .data, .xdata }) |file| {
+ _ = call(p, .{ .tag = 1, .op = .setattr, .node = Node.of(serial, .body), .truncate = true });
+ _ = wr(p, Node.of(serial, .body), "hello world\nsecond line\n");
+ _ = wr(p, Node.of(serial, .addr), "#0,#5");
+ try testing.expectEqual(Status.ok, call(p, .{ .tag = 2, .op = .setattr, .node = Node.of(serial, file), .truncate = true }).reply.status);
+ try testing.expectEqualStrings(" world\nsecond line\n", p.panes[0].?.file.?.content);
+ try testing.expectEqual(Status.ok, wr(p, Node.of(serial, file), "HOWDY").reply.status);
+ try testing.expectEqualStrings("HOWDY world\nsecond line\n", p.panes[0].?.file.?.content);
+ }
+}
+
test "setattr truncation empties the body and answers fresh attributes" {
const gpa = testing.allocator;
const p = try withFile(gpa, "one\ntwo\n");