From 5a5c9c9698c2621d3509c687880d22e904f25139 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 18:59:01 -0300 Subject: Writes made under mark 0 are one undo step, and no history is lost mark 0 skipped the undo point of each write, and mark 1 then pushed the text as it had become, so the text from before the batch was in no step: with no history before it, no Undo brought it back. Turning mark off now keeps the text as it is, and the writes after it are one step back to it, as acme's nomark joins them. Co-Authored-By: Claude Opus 5.5 --- src/ninep/pane.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 714a2566..a5acccbc 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -516,9 +516,12 @@ fn writeFlag(p: *Pardes, req: Req, pane: *Pane, file: PaneFile) Reply { const f = fileOf(pane) orelse return Reply.fail(req.tag, E.INVAL); f.saved_revision = if (on) f.revision -% 1 else f.revision; }, + // acme's nomark joins the writes after it into one undo step. The + // step starts here: the text as it is now is what Undo brings back, + // and turning mark on again only ends it. .mark => { + if (!on and !pf.nomark) panes.File.pushUndo(p, pane); pf.nomark = !on; - if (on) panes.File.pushUndo(p, pane); }, .scroll => pf.noscroll = !on, else => unreachable, @@ -906,6 +909,22 @@ test "the flag files read and write the state acme kept behind ctl verbs" { try testing.expectEqual(E.INVAL, wr(term, Node.of(serialOf(term), .dirty), "1").errno()); } +test "writes under mark 0 are one undo step, even with no history before them" { + const p = try withFile(testing.allocator, "one\n"); + defer p.deinit(); + const serial = serialOf(p); + const body = Node.of(serial, .body); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .mark), "0").reply.status); + _ = wr(p, body, "two\n"); + _ = wr(p, body, "three\n"); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .mark), "1").reply.status); + try testing.expectEqualStrings("one\ntwo\nthree\n", rd(p, body, 0, 64).bytes); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .ctl), "Undo").reply.status); + try testing.expectEqualStrings("one\n", rd(p, body, 0, 64).bytes); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .ctl), "Redo").reply.status); + try testing.expectEqualStrings("one\ntwo\nthree\n", rd(p, body, 0, 64).bytes); +} + test "truncating the tag clears its editable tail" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); -- cgit v1.3