From eefac04995ffad847a4098f16d2e82ccab16438b Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 31 Jul 2026 04:15:41 -0300 Subject: open files follow the disk, and undo is the merge strategy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An external update is pushed onto the undo stack exactly like an edit the user typed, so unsaved work is one `u` away and pardes never has to merge anything. That is the design, not an implementation detail: the whole feature is pushUndo() then setContent(). One inotify instance in the tty shell, blocking in readVec through std.Io on a concurrent task started beside the pty readers — after loop.start(), so the forkpty ordering is untouched. It watches the containing DIRECTORY, because an editor rewrites by rename-over and a watch on the file would follow the dead inode, and it listens for CLOSE_WRITE rather than MODIFY, which is one event per finished writer and most of the debounce for free. Our own Save does not reach the undo stack: each watch keeps a hash of the bytes last seen on disk and save_file restamps it. A hash rather than mtime and size because the reload has to read the file anyway. The core stays sans-IO — one watch effect out, one file_changed event in, and a shell that cannot watch simply never sends the event, which is what the gui and the web platform do. Linux only; fanotify is what the build system uses and is rejected in a comment: it exists for thousands of directories across mounts, and sixteen panes of inotify is a third of the code with no kernel floor. New golden filewatch: edit without saving, overwrite from a shell in another column, watch it reload, undo, get the unsaved edit back. None moved. --- src/file_pane.zig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/file_pane.zig') diff --git a/src/file_pane.zig b/src/file_pane.zig index 68fea40f..739d7b54 100644 --- a/src/file_pane.zig +++ b/src/file_pane.zig @@ -33,6 +33,9 @@ pub fn open(p: *Pardes, id: usize, path: []const u8, line: usize) !*Pane { pane.file = .{ .path = path_copy, .content = content, .scroll = scroll }; pane.cur_pinned = true; pane.cur_row = @intCast(scroll); + // watches follow pane lifetime: this is the only place a real file is read + // off disk, and deinitPane is the only place one goes away + p.emit(.{ .watch = .{ .pane = @intCast(id), .on = true } }); return pane; } -- cgit v1.3