diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 07:56:48 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 08:53:11 -0300 |
| commit | 6c82d06cd2a15d724b8e82e1128fa15dc9e35d05 (patch) | |
| tree | 1c13f10465ce797333f8712d95d587af97ca2bc0 /src/File.zig | |
| parent | 989f2bae90fde52b5dd36b57c31f535dd011461e (diff) | |
| download | pardes-6c82d06cd2a15d724b8e82e1128fa15dc9e35d05.tar.gz pardes-6c82d06cd2a15d724b8e82e1128fa15dc9e35d05.zip | |
Restore reads a clean file from disk, and an unsaved one whose file changed since the Dump is said changed, its first Save asking before overwriting it
A Restore put back the dump's copy of every file pane and armed the
watch with the disk as it now was, so a file changed on disk after the
Dump came back as its old text, marked clean, and the next Save wrote
the old text over the new without a word.
A clean file pane's text is now not in the dump: Restore reads its file
(an older dump's copy is read past too, and used only when the file is
gone). An unsaved pane keeps its text once, base64, the plain copy beside
it dropped, with the hash of the file it was read against
(`disk_hash`); a Restore that finds another there says the file changed
on disk, and Save asks once before overwriting it, as for a change seen
while it was open. file_watch.zig's own tests were never collected (no
test root imported it): main.zig's test block does now, and the restored
watch test says what a clean restore now reads.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/File.zig')
| -rw-r--r-- | src/File.zig | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/src/File.zig b/src/File.zig index c738ee58..d41007c3 100644 --- a/src/File.zig +++ b/src/File.zig @@ -105,6 +105,7 @@ pub const State = struct { pub const NodeStep = struct { ranges: []modal.Selection, primary: usize }; pub fn dumpPane( + p: *Pardes, arena: std.mem.Allocator, pane: *const Pane, file: *const State, @@ -114,6 +115,18 @@ pub fn dumpPane( origin: []const u8, origin_arg: []const u8, ) !dump.Pane { + const dirty = file.revision != file.saved_revision; + const on_disk = onDisk(file.path); + // A clean file's text is its file's: read from disk when restored, so + // a change made there since is what comes back. Any other is kept, once. + const from_disk = file.output == null and !dirty and on_disk; + // What an unsaved pane's text was read against: the disk as it is, or, + // when a change there was seen already, something it is not. + const disk_hash: ?u64 = if (file.output != null or !dirty or !on_disk) null else if (file.disk_newer) |newer| newer +% 1 else blk: { + const bytes = filesystem.read(p, file.path) catch break :blk null; + defer p.gpa.free(bytes); + break :blk std.hash.Wyhash.hash(0, bytes); + }; return .{ .kind = .file, .tag = tag, @@ -127,10 +140,10 @@ pub fn dumpPane( .tree_context = file.tree_context, .location_rows = try dumpLocationRows(arena, file.location_rows), .path = file.path, - .content = file.content, - .content_b64 = try dump.encodeBytes(arena, file.content), - .dirty = file.revision != file.saved_revision, - .on_disk = onDisk(file.path), + .content_b64 = if (from_disk) "" else try dump.encodeBytes(arena, file.content), + .dirty = dirty, + .disk_hash = disk_hash, + .on_disk = on_disk, .origin = origin, .origin_arg = origin_arg, .mini_source = if (file.mini) |mini| mini.source else "", @@ -855,7 +868,16 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane { saved.content_b64.len > encoded_limit or saved.mini_colors_b64.len > encoded_limit) return error.InvalidMini; } - const content: []u8 = if (saved.content_b64.len > 0) + // A clean file's text is read from its file: one changed on disk since + // the dump comes back changed, never as the old text marked clean (and + // a dump from before, which kept it, is read so too). Gone from disk, the + // dump's text if it kept one. + const from_disk: ?[]u8 = if (Output.fromWord(saved.origin) == null and !saved.dirty and saved.on_disk and + saved.mini_source.len == 0 and filesystem.localPath(saved.path) != null) + filesystem.read(p, saved.path) catch null + else + null; + const content: []u8 = if (from_disk) |bytes| bytes else if (saved.content_b64.len > 0) try dump.decodeBytes(p.gpa, saved.content_b64) else try p.gpa.dupe(u8, saved.content); @@ -923,6 +945,14 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane { // One never on disk (Config's new file, a name not yet saved) is // restored as it was: clean, and nothing deleted. if (output == null and saved.on_disk and !onDisk(path)) deleted(p, @intCast(id)); + // Unsaved, and its file changed on disk since the text was read: said, + // and its first Save asks before overwriting the newer file. + if (saved.disk_hash) |read_against| if (output == null and onDisk(path)) { + if (filesystem.read(p, path)) |bytes| { + defer p.gpa.free(bytes); + if (std.hash.Wyhash.hash(0, bytes) != read_against) changed(p, @intCast(id), bytes); + } else |_| {} + }; return pane; } |
