diff options
| -rw-r--r-- | src/File.zig | 9 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 14 | ||||
| -rw-r--r-- | test/snapshots/chordcut.golden | 1 | ||||
| -rw-r--r-- | test/snapshots/lsp-client-edit.golden | 3 | ||||
| -rw-r--r-- | test/snapshots/lsp-rename.golden | 2 | ||||
| -rw-r--r-- | test/snapshots/pipe.golden | 1 | ||||
| -rw-r--r-- | test/snapshots/selregex.golden | 1 |
7 files changed, 30 insertions, 1 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(); diff --git a/test/snapshots/chordcut.golden b/test/snapshots/chordcut.golden index d5266717..120b261f 100644 --- a/test/snapshots/chordcut.golden +++ b/test/snapshots/chordcut.golden @@ -77,7 +77,6 @@ |13: 0-0 d,#608269, 1-1 d,#0d1410, 2-6 #59675c,#0d1410, 7-12 #e5eee5,#0d1410, 13-59 d,#0d1410, 60-60 d,#608269, 61-61 d,#0d1410, 62-119 #e5eee5,#0d1410, |14: 0-0 d,#608269, 1-1 d,#0d1410, 2-6 #616f64,#0d1410,b 7-12 #e5eee5,#0d1410, 13-59 d,#0d1410, 60-60 d,#608269, 61-61 d,#0d1410, 62-119 #e5eee5,#0d1410, == snap ttyprobe grid=120x31 cursor=42,22 -|12: /tmp/pardes-snap/chordcut/cwd/g.txt * Save Tty Collapse D |17: $ /tmp/pardes-snap/chordcut/cwd Tty+bash Save Mode Filter C |18: $ ls |20: $ stty -icanon -echo; printf '\x1b[?2004h\x1b[?1000h\x1b[? diff --git a/test/snapshots/lsp-client-edit.golden b/test/snapshots/lsp-client-edit.golden index c75bae4c..81079615 100644 --- a/test/snapshots/lsp-client-edit.golden +++ b/test/snapshots/lsp-client-edit.golden @@ -12,6 +12,7 @@ | 8 } | 9 == snap unformatted grid=100x25 cursor=7,3 +|2: /tmp/pardes-snap/lsp-client-edit/cwd/main.rs Save Tty Collapse Del TreeContext |3: 1 mod lib; |4: 2 |5: 3 fn main() { @@ -22,10 +23,12 @@ |10: 8 |11: == snap renamed grid=100x25 cursor=12,5 +|2: /tmp/pardes-snap/lsp-client-edit/cwd/main.rs * Save Tty Collapse Del TreeContext |3: 1 mod lib; renamed 2 range(s) |6: 4 let x = sum(1, 2); |7: 5 let y = sum(3, 4); == snap unrenamed grid=100x25 cursor=12,5 +|2: /tmp/pardes-snap/lsp-client-edit/cwd/main.rs Save Tty Collapse Del TreeContext |3: 1 mod lib; |6: 4 let x = add(1, 2); |7: 5 let y = add(3, 4); diff --git a/test/snapshots/lsp-rename.golden b/test/snapshots/lsp-rename.golden index f6dc8358..26571b26 100644 --- a/test/snapshots/lsp-rename.golden +++ b/test/snapshots/lsp-rename.golden @@ -18,9 +18,11 @@ | 14 } | 15 == snap undone grid=110x25 cursor=17,10 +|2: /tmp/pardes-snap/lsp-rename/cwd/rename.zig Save Tty Collapse Del TreeContext |3: 1 const std = @import("std"); |5: 3 fn helper(x: u32) u32 { |10: 8 _ = helper(41); == snap redone grid=110x25 cursor=17,10 +|2: /tmp/pardes-snap/lsp-rename/cwd/rename.zig * Save Tty Collapse Del TreeContext |5: 3 fn renamed_helper(x: u32) u32 { |10: 8 _ = renamed_helper(41); diff --git a/test/snapshots/pipe.golden b/test/snapshots/pipe.golden index 14353ebf..6b7347bf 100644 --- a/test/snapshots/pipe.golden +++ b/test/snapshots/pipe.golden @@ -8,4 +8,5 @@ |2: /tmp/pardes-snap/pipe/cwd/pipe.txt * Save Tty Collapse Del |3: 1 ALPHA BETA == snap undone grid=180x13 cursor=17,3 +|2: /tmp/pardes-snap/pipe/cwd/pipe.txt Save Tty Collapse Del |3: 1 alpha beta diff --git a/test/snapshots/selregex.golden b/test/snapshots/selregex.golden index 8b45ae43..7362a85a 100644 --- a/test/snapshots/selregex.golden +++ b/test/snapshots/selregex.golden @@ -56,6 +56,7 @@ |4: 2 // const two = 2; |5: 3 // const three = 3; == snap uncommented grid=160x25 cursor=11,3 +|2: /tmp/pardes-snap/selregex/cwd/m.zig Save Tty Collapse Del TreeContext |3: 1 const one = 1; |4: 2 const two = 2; |5: 3 const three = 3; |
