From 0b7a480bef38b209741c520e2478d178767a9e51 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 31 Jul 2026 10:37:19 -0300 Subject: the gui shell watches files too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It never did — the effect arm was `.watch => {}` with a comment saying a shell that never delivers the event simply never reloads. That was written as a deliberate scope cut and it is the whole bug: the user runs pardes-gui. The tty watcher was fine, and I proved all four suspicions false against a real binary in a real pty with the write done by a stranger process and NO input delivered afterwards: the loop does wake (postEvent signals an empty queue), zig fmt's rename-over does fire MOVED_TO and is caught, three panes across two directories all reload and closing one leaves its neighbour still following, and the self-write hash guard does not swallow a real change. The live session even had its inotify mark on src with the right mask and re-read config.zig when I touched that directory. Duplicated rather than shared with tty.zig, the way the two shells already each own LspJob, forkShell and their pty readers. The headless PARDES_TEST_GRID path deliberately passes -1: its contract is one frame per scripted input, and a reload on its own clock would put an unasked-for frame in the stream. filewatch.snap proved less than it looked. Its real blind spots were the rename-over shape — the old script only truncated, so it saw CLOSE_WRITE and never MOVED_TO — multiple directories, and the one that mattered: the suite only ever runs the TTY binary, so it structurally cannot see a gui-only regression. It now uses a new `run` directive whose writer is a child of the RUNNER, so nothing it does reaches an app pty. --- test/snapshot.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'test/snapshot.zig') diff --git a/test/snapshot.zig b/test/snapshot.zig index 4344654c..19b65a02 100644 --- a/test/snapshot.zig +++ b/test/snapshot.zig @@ -28,6 +28,7 @@ // file create file in the script's cwd (before start) // lines [tail] create file with n numbered lines, tail on each // dirmk create a subdirectory +// run run a command in the script's cwd (NOT in a pane) // start [arg] fork the app in a pty (optional extra CLI arg) // wait pump until needle appears on the grid (fails hard) // settle pump for a fixed duration @@ -62,6 +63,7 @@ pub const std_options: std.Options = .{ .log_level = .err }; extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; extern "c" fn execvp(file: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; +extern "c" fn system(cmd: [*:0]const u8) c_int; const SNAP_BASE = "/tmp/pardes-snap"; var trace_stable = false; @@ -379,6 +381,14 @@ fn runScript(arena: std.mem.Allocator, exe_z: [:0]const u8, script_path: []const try eh.writeFile(try arena.dupeZ(u8, name), ppm.items); } else if (std.mem.eql(u8, cmd, "dirmk")) { try mkdir(arena, tok.next() orelse return error.BadScript, false); + } else if (std.mem.eql(u8, cmd, "run")) { + // A writer that is NOT a pardes pane. `file` mid-script is already + // one, but it cannot express the rename-over that is how a + // formatter actually replaces a file — and the difference is a + // different inotify event. This runs in the script's cwd, as a + // child of the RUNNER, so nothing it does reaches the app's ptys: + // whatever the app then shows, it woke up for by itself. + if (system(try arena.dupeZ(u8, tok.rest())) != 0) return error.RunFailed; } else if (std.mem.eql(u8, cmd, "start")) { const rows = try std.fmt.parseInt(u16, tok.next() orelse return error.BadScript, 10); const cols = try std.fmt.parseInt(u16, tok.next() orelse return error.BadScript, 10); -- cgit v1.3