diff options
Diffstat (limited to 'src/File.zig')
| -rw-r--r-- | src/File.zig | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/src/File.zig b/src/File.zig index 6a1ac319..5f6d7097 100644 --- a/src/File.zig +++ b/src/File.zig @@ -58,6 +58,15 @@ pub const State = struct { /// as an undo to the save point is in acme. saved_hash: ?u64 = null, watch_after_save: bool = false, + /// The file changed on disk under unsaved edits, which were kept (see + /// `changed`): its hash, so the same change is said once, until a Save + /// or get settles it. A Save warns once before overwriting it, as acme's + /// Put does (exec.c:577, "modified since last read"). + disk_newer: ?u64 = null, + disk_newer_warned: bool = false, + /// Its file was deleted on disk: said once (`deleted`) until it is + /// written or found again. + disk_gone: bool = false, /// Seconds of the last content change, for the filesystem's stat; zero until edited. mtime: u32 = 0, /// Non-null for a generated output buffer rather than an on-disk file. @@ -809,6 +818,10 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane { .location_rows = location_rows, .scroll = src.scroll, .revision = @intFromBool(saved.dirty), + // Restored with unsaved text, it has text to lose as an edited pane + // has: a hash its text does not match, so Exit, Restore, Del and + // get ask first (builtins.warnModifiedIn reads only edited panes). + .saved_hash = if (saved.dirty) std.hash.Wyhash.hash(0, content) +% 1 else null, .history = history, }; pane.file.?.tree_context = saved.tree_context and supportsContext(pane); @@ -960,12 +973,48 @@ pub fn redo(p: *Pardes, pane: *Pane) void { restoreSnap(pane, f, next); } -/// Commit an externally rewritten file onto the same undo history as typed -/// edits. Unsaved work remains one `u` away; there is no third merge state. +/// A pane's file deleted on disk: the text stays, and it says so once, +/// `<file> deleted on disk (Save writes it again)`, logged `changed +/// <serial> deleted`. +pub fn deleted(p: *Pardes, id: u8) void { + const pane = p.panes[id] orelse return; + const f = if (pane.file) |*file| file else return; + if (f.disk_gone) return; + f.disk_gone = true; + var said: [limits.host_path_cap + 64]u8 = undefined; + p.setMessage(id, std.fmt.bufPrint(&said, "{s} deleted on disk (Save writes it again)", .{f.path}) catch "deleted on disk (Save writes it again)"); + var rec: [32]u8 = undefined; + pardes.ctlfs.events.notePath(p, "changed", std.fmt.bufPrint(&rec, "{d} deleted", .{pane.serial}) catch ""); +} + +/// A file rewritten on disk. A clean buffer takes the new text, committed +/// onto the same undo history as typed edits. A buffer with unsaved edits +/// is never reloaded under them: it keeps its text and stays dirty, and +/// says so once (`<file> changed on disk (get reloads it, Save overwrites +/// it)`, logged `changed <serial>`); get then asks before throwing +/// the edits away and Save before overwriting the newer file. acme reloads +/// nothing by itself: its get asks (winclean) and its Put refuses a file +/// modified since it was read (exec.c:577). pub fn changed(p: *Pardes, id: u8, bytes: []const u8) void { const pane = p.panes[id] orelse return; const f = if (pane.file) |*file| file else return; + f.disk_gone = false; if (std.mem.eql(u8, f.content, bytes)) return; + if (f.saved_hash) |saved| if (f.revision != f.saved_revision and std.hash.Wyhash.hash(0, f.content) != saved) { + const disk = std.hash.Wyhash.hash(0, bytes); + if (f.disk_newer == disk) return; + f.disk_newer = disk; + f.disk_newer_warned = false; + var said: [limits.host_path_cap + 64]u8 = undefined; + p.setMessage(id, std.fmt.bufPrint(&said, "{s} changed on disk (get reloads it, Save overwrites it)", .{f.path}) catch "changed on disk (get reloads it, Save overwrites it)"); + var rec: [16]u8 = undefined; + pardes.ctlfs.events.notePath(p, "changed", std.fmt.bufPrint(&rec, "{d}", .{pane.serial}) catch ""); + return; + }; + f.disk_newer = null; + // Logged as the dirty case is, with what became of it. + var rec: [32]u8 = undefined; + pardes.ctlfs.events.notePath(p, "changed", std.fmt.bufPrint(&rec, "{d} reloaded", .{pane.serial}) catch ""); const new = p.gpa.dupe(u8, bytes) catch return; pushUndo(p, pane); setContent(p, f, new); |
