summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 17:59:09 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitcbb6f821c29ede01648cdd41d2f1d2259a387645 (patch)
tree0359efa48c1ec626413c0c939e8c718822a20b5a
parentc5fcbc0cb9c1e76bb01283e9ccfa00ed5bc4bef8 (diff)
downloadpardes-cbb6f821c29ede01648cdd41d2f1d2259a387645.tar.gz
pardes-cbb6f821c29ede01648cdd41d2f1d2259a387645.zip
Text brought back to what was saved is clean again
dirty compared revisions only, so undoing to the save point, or typing the text back, left the pane dirty. The saved text's hash is kept from the first edit after a save or load, and an edit that makes the text equal to it marks the pane saved again, as undo to the save point does in acme. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/File.zig9
-rw-r--r--src/ninep/ctl.zig14
-rw-r--r--test/snapshots/chordcut.golden1
-rw-r--r--test/snapshots/lsp-client-edit.golden3
-rw-r--r--test/snapshots/lsp-rename.golden2
-rw-r--r--test/snapshots/pipe.golden1
-rw-r--r--test/snapshots/selregex.golden1
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;