diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/File.zig | 9 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 14 |
2 files changed, 23 insertions, 0 deletions
diff --git a/src/File.zig b/src/File.zig index b8368482..929e33bf 100644 --- a/src/File.zig +++ b/src/File.zig @@ -53,6 +53,10 @@ pub const State = struct { /// Revision last known to match disk, after Save or an external reload. /// Equal means the screen matches disk. saved_revision: u32 = 0, + /// The text at `saved_revision`, hashed as the first edit after it + /// replaces it: an edit that brings the text back to it is clean again, + /// as an undo to the save point is in acme. + saved_hash: ?u64 = null, watch_after_save: bool = false, /// Seconds of the last content change, for the filesystem's stat; zero until edited. mtime: u32 = 0, @@ -859,9 +863,14 @@ pub fn setContent(p: *Pardes, f: *State, new: []u8) void { if (f.mini) |*mini| mini.deinit(p.gpa); f.mini = null; const starts: []usize = if (f.line_starts.len > 0) editedLineStarts(p.gpa, f.line_starts, f.content, new) catch &.{} else &.{}; + // ponytail: a whole-text hash per edit; a rolling hash when files grow large. + if (f.revision == f.saved_revision or f.saved_hash == null) f.saved_hash = std.hash.Wyhash.hash(0, f.content); p.gpa.free(f.content); f.content = new; f.revision +%= 1; + if (f.saved_hash) |h| if (std.hash.Wyhash.hash(0, new) == h) { + f.saved_revision = f.revision; + }; f.mtime = pardes.ctlfs.events.now(); if (f.line_starts.len > 0) p.gpa.free(f.line_starts); f.line_starts = starts; diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index bb562672..16935f41 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -744,6 +744,20 @@ test "a second lock is refused until the holder unlocks or closes, and binds nob for (p.fs.opens) |o| try testing.expect(o.node == 0); } +test "dirty goes back to 0 when the text is the saved text again" { + const p = try withFile(testing.allocator, "one\n"); + defer p.deinit(); + const serial = serialOf(p); + const dirty = Node.of(serial, .dirty); + try testing.expectEqualStrings("0\n", rd(p, dirty, 0, 8).bytes); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .body), "two\n").reply.status); + try testing.expectEqualStrings("1\n", rd(p, dirty, 0, 8).bytes); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .ctl), "Undo").reply.status); + try testing.expectEqualStrings("0\n", rd(p, dirty, 0, 8).bytes); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .ctl), "Redo").reply.status); + try testing.expectEqualStrings("1\n", rd(p, dirty, 0, 8).bytes); +} + test "a message from a root ctl write is the session's in the log" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); |
