diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 10:18:59 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | d9c6786e19567878e3c89ae212296ce941484096 (patch) | |
| tree | 23b973692fc258bd7910cadf135d06776015da6f /src/ninep | |
| parent | 5b27be228f5961dee8727bec7480f375dfb588ed (diff) | |
| download | pardes-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.zig | 32 |
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"); |
