diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 06:01:01 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | 81ec44ca3b103bdc755e9f5d73a270c05c88617b (patch) | |
| tree | 397dd624f7168e5d66c36509fb6f9603c1ce9d5f /src | |
| parent | 6dacd7778b846967fb945297420148e783f48f95 (diff) | |
| download | pardes-81ec44ca3b103bdc755e9f5d73a270c05c88617b.tar.gz pardes-81ec44ca3b103bdc755e9f5d73a270c05c88617b.zip | |
A watched file's deletion wakes the watch at once
The directory mark took CLOSE_WRITE, MOVED_TO and CREATE only, so a file
deleted or moved away was noticed only when something else in the
directory next changed. The mark takes DELETE and MOVED_FROM too, so
changed <serial> deleted comes at once; a test deletes a watched file and
finds the watch readable within a second and the pane told.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/file_watch.zig | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/src/file_watch.zig b/src/file_watch.zig index 71a4680d..d028488e 100644 --- a/src/file_watch.zig +++ b/src/file_watch.zig @@ -225,8 +225,10 @@ fn markDir(fd: c_int, dir_z: [:0]const u8, watches: *const Table) c_int { switch (builtin.os.tag) { .linux => { // CLOSE_WRITE coalesces one writer's writes; MOVED_TO and CREATE - // cover rename-over and delete-then-recreate saves. - const mask = linux.IN.CLOSE_WRITE | linux.IN.MOVED_TO | linux.IN.CREATE | linux.IN.ONLYDIR; + // cover rename-over and delete-then-recreate saves; DELETE and + // MOVED_FROM a file going away, said at once (File.deleted), not + // when something else next changes in the directory. + const mask = linux.IN.CLOSE_WRITE | linux.IN.MOVED_TO | linux.IN.CREATE | linux.IN.DELETE | linux.IN.MOVED_FROM | linux.IN.ONLYDIR; return libc.inotify_add_watch(fd, dir_z, mask); }, .macos => { @@ -838,3 +840,36 @@ test "theme watch reloads valid ZON and keeps the last theme across a bad save" try std.testing.expect(!reloadTheme(core, gpa, &watches, false)); try std.testing.expectEqual(@as(?[3]u8, .{ 9, 8, 7 }), core.theme().bg); } + +test "a watched file deleted wakes the watch at once, and its pane says so" { + if (comptime !supported or !filesystem.platform_has_fs or builtin.os.tag != .linux) return; + const io = std.testing.io; + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(io, .{ .sub_path = "watched", .data = "here\n" }); + var path_buf: [256]u8 = undefined; + const path = try std.fmt.bufPrint(&path_buf, ".zig-cache/tmp/{s}/watched", .{tmp.sub_path}); + const core = try pardes.Pardes.init(gpa, .{ .tty_only = true }); + defer core.deinit(); + while (core.nextEffect()) |_| {} + const id: u8 = @intCast(core.freeSlot().?); + const pane = try pardes.panes.File.open(core, id, path, 0); + const fd = init(true); + if (fd < 0) return error.NoWatcher; + defer _ = libc.close(fd); + var watches: Table = @splat(null); + defer watchPane(fd, &watches, id, null, pane.serial, .{ .text = 0 }); + while (core.nextEffect()) |effect| switch (effect) { + .watch => |watch| if (watch.pane == id and watch.on) { + _ = applyEffect(core, io, fd, &watches, id, watch.on, watch.mode); + }, + else => {}, + }; + _ = drain(fd); + try tmp.dir.deleteFile(io, "watched"); + var pfd = [_]libc.pollfd{.{ .fd = fd, .events = libc.POLL.IN, .revents = 0 }}; + try std.testing.expect(libc.poll(&pfd, 1, 1000) > 0); + _ = reloadChanged(core, io, gpa, &watches); + try std.testing.expect(pane.file.?.disk_gone); +} |
