diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 02:15:43 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 9391d35d357ebf18759c10f6c756855e5a8f7b50 (patch) | |
| tree | 181645499d1728c12806dabb1ee65cb61e9f5a41 | |
| parent | 5492da0ba1d5f1c1cc0a880f82107961627c9ba6 (diff) | |
| download | pardes-9391d35d357ebf18759c10f6c756855e5a8f7b50.tar.gz pardes-9391d35d357ebf18759c10f6c756855e5a8f7b50.zip | |
A limit write that fails keeps the limit it had
A shell's > truncates limit, which lifted it, before the write; a write
that then failed left no limit at all. The truncation now keeps the old
limit until that open's write, and a failed write puts it back; a bare
truncation still lifts it once its open is released.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/fs.md | 6 | ||||
| -rw-r--r-- | src/ninep/pane.zig | 28 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 4 |
3 files changed, 35 insertions, 3 deletions
@@ -470,8 +470,10 @@ address expression (`#0,#5`, `/pattern/`, `2+1`, and pardes's own `12:5`, below); `addr` selects what `data` and `xdata` read or replace, `dot` is the editor's own selection and moving it scrolls the pane into view, and `limit` bounds only the end of a forward -search, as acme's does, and reads empty until it is set. Truncating `dot` empties it, truncating `limit` lifts it, and -truncating `addr` leaves it as it is (below). +search, as acme's does, and reads empty until it is set. Truncating `dot` empties it, truncating `limit` lifts it -- +though a write after the truncation that fails puts the old limit back, so +`echo /bad/ > limit` changes nothing -- and truncating `addr` leaves it as +it is (below). A rename everywhere, or any other sam edit, is one write to the pane's `ctl`: `Edit ,x/foo/c/bar/` runs acme's Edit (docs/tags.md) on the body as diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 902f4f2a..e7b72c88 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -41,6 +41,9 @@ pub const State = struct { /// rather than act at the address before it, which acme would do. addr_failed: bool = false, limit: ?Range = null, + /// The limit a truncation cleared, kept until the write that follows it + /// on that open (a shell's `>`): a write that fails puts it back. + limit_truncated: ?Range = null, /// Opens of `event`, which hold the pane scripted. readers: u16 = 0, /// One of `readers` reads `event`; a second reading open is refused. @@ -523,8 +526,13 @@ fn writeRange(req: Req, pane: *Pane, file: PaneFile) Reply { }; const range = r orelse { if (file == .addr) pf.addr_failed = true; + if (file == .limit) if (pf.limit_truncated) |kept| { + pf.limit = kept; + pf.limit_truncated = null; + }; return tree.failText(req.tag, E.INVAL, a.err); }; + if (file == .limit) pf.limit_truncated = null; switch (file) { .addr => { pf.addr = range; @@ -684,7 +692,10 @@ pub fn truncate(p: *Pardes, pane: *Pane, file: PaneFile) tree.Status { // is evaluated from where the last one left off, as with `>>`. // `0` (or `,`) is how to start over. See State.addr. .addr => {}, - .limit => pf.limit = null, + .limit => { + pf.limit_truncated = pf.limit; + pf.limit = null; + }, .dot => if (fileOf(pane) != null) setDot(pane, .{}), else => {}, } @@ -1236,3 +1247,18 @@ test "addresses are in runes: a combining mark and a lone \\r are places of thei _ = wr(p, addr_node, "#4,#5"); try testing.expectEqualStrings("\r", rd(p, Node.of(serial, .xdata), 0, 100).bytes); } + +test "a limit write that fails keeps the limit a truncation cleared before it" { + const gpa = testing.allocator; + const p = try withFile(gpa, "one two\n"); + defer p.deinit(); + const limit = Node.of(serialOf(p), .limit); + _ = wr(p, limit, "#0,#3"); + _ = call(p, .{ .tag = 1, .op = .setattr, .node = limit, .truncate = true }); + try testing.expectEqual(Status.err, wr(p, limit, "/nomatch/").reply.status); + try testing.expectEqualStrings(" 0 3 ", rd(p, limit, 0, 100).bytes); + // A truncation with no write after it clears it. + _ = call(p, .{ .tag = 2, .op = .setattr, .node = limit, .truncate = true }); + _ = call(p, .{ .tag = 3, .op = .release, .node = limit }); + try testing.expectEqualStrings("", rd(p, limit, 0, 100).bytes); +} diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index da9c37f0..278732b5 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -806,6 +806,10 @@ fn releaseHandle(p: *Pardes, req: Req) void { p.panes[id].?.fs.tag_rewrite = false; p.panes[id].?.fs.tag_held_newline = false; }; + // `: > limit` clears it for good once its open goes. + if (t == .pane and t.pane.file == .limit) if (p.paneBySerial(t.pane.serial)) |id| { + p.panes[id].?.fs.limit_truncated = null; + }; if (t == .col and t.col.file == .tag) cols.released(p, t.col.serial); if (t == .top and t.top == .tag) cols.released(p, null); const o = openOf(p, req) orelse return; |
