summaryrefslogtreecommitdiff
path: root/src/File.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/File.zig')
-rw-r--r--src/File.zig53
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);