diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 04:56:17 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | e2452380ed42d3b418930e375780745612d599c5 (patch) | |
| tree | 61ca969e4ba5df779953e7b9c06b03d09cc1af43 | |
| parent | 253a5c1e036d96eada910c47eb500ed0c8e37b5c (diff) | |
| download | pardes-e2452380ed42d3b418930e375780745612d599c5.tar.gz pardes-e2452380ed42d3b418930e375780745612d599c5.zip | |
An a, c or i text with no . line fails the Edit, as an open { does
sam takes the end of input as a text block's end, so a block cut short (a
write that lost its tail, an open closed early) inserted half of itself.
An Edit whose a, c or i text never meets its . line now fails with "a, c
or i text not ended by a . line" and changes nothing, as an unclosed {
does; at a close, logged as an err.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | .agents/skills/pardes-9p/SKILL.md | 3 | ||||
| -rw-r--r-- | docs/fs.md | 6 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 11 | ||||
| -rw-r--r-- | src/sam_edit.zig | 5 |
4 files changed, 22 insertions, 3 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md index ae42bc86..e363b7cb 100644 --- a/.agents/skills/pardes-9p/SKILL.md +++ b/.agents/skills/pardes-9p/SKILL.md @@ -150,7 +150,8 @@ A read is a stream: once read, a second read on the same fd gives EOF (on an open that wrote, until its next write); open again, or seek to 0, to read it again. Each command line runs once whole, however a mount cuts a big write; a last line with no newline runs when the open closes, and an Edit block still open -then fails there (an `err`, ``unmatched `{'``), changing nothing. A command that fails is reported in +then fails there (an `err`: ``unmatched `{'``, or an a/c/i text with no `.` +line), changing nothing. A command that fails is reported in the editor, not as a write error, so inspect the resulting pane, index, message or screen; only a malformed line fails the write itself. A word no builtin knows (a typo included) is a command line: written at a terminal at its @@ -429,8 +429,10 @@ kernel's, 8 KiB from a client that asks), anywhere, and each piece comes as a write of its own, so the open keeps a last line with no newline yet, or an Edit block whose text has not ended, until its next write, and runs it then; what is left when it closes runs at the close, where an Edit block -whose `{` or text never ended fails as any unclosed block does (``unmatched -`{'``, logged as an `err`; nothing changes). A fragment never runs on its +whose `{` or `a`/`c`/`i` text never ended fails and changes nothing +(``unmatched `{'``, or `a, c or i text not ended by a . line`, logged as an +`err`): sam takes the end of input for a `.`, but a block that reaches Edit +unfinished was cut short. A fragment never runs on its own. A line or Edit block held past 1 MiB is refused (EINVAL). A look takes acme's addresses after a colon (editors/acme/look.c:450): a diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index 398824d5..54d21da6 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -1536,3 +1536,14 @@ test "an Edit block still open when its open closes fails there with an err and try testing.expect(th.logHas(p, "unmatched")); try testing.expectEqualStrings("abc\n", p.panes[0].?.file.?.content); } + +test "an a, c or i text still open when its open closes fails there too and changes nothing" { + const p = try th.withFile(testing.allocator, "abc\n"); + defer p.deinit(); + const ctl_node = Node.of(serialOf(p), .ctl); + const c = call(p, .{ .tag = 1, .op = .open, .node = ctl_node, .omode = 1 }).reply.handle; + _ = call(p, .{ .tag = 2, .op = .write, .node = ctl_node, .handle = c, .data = "Edit $a\nhalf of a block\n" }); + _ = call(p, .{ .tag = 3, .op = .release, .node = ctl_node, .handle = c, .opened = true }); + try testing.expect(th.logHas(p, "not ended by a . line")); + try testing.expectEqualStrings("abc\n", p.panes[0].?.file.?.content); +} diff --git a/src/sam_edit.zig b/src/sam_edit.zig index 604e2b31..cd2998ef 100644 --- a/src/sam_edit.zig +++ b/src/sam_edit.zig @@ -75,6 +75,11 @@ pub fn run(arena: std.mem.Allocator, text: []const u8, dot: Range, name: []const if (last) |l| l.next = c else first = c; last = c; } + // An a, c or i text that never met its `.` line is refused, as an + // unclosed `{` is: sam takes the end of input for one, but a block that + // reaches Edit unfinished was cut short (a write, an open closed), and + // half of it inserted is worse than none. + if (ps.open_text) return fail(why, "a, c or i text not ended by a . line", .{}); const cmds = first orelse return .{ .ops = &.{}, .dot = dot, .printed = "" }; // acme's u runs at once (ecmd.c:532), outside the edit log; here it is // an Edit of its own. |
