diff --git a/src/backend/KQueue.zig b/src/backend/KQueue.zig index b7e38b6..aeee5fd 100644 --- a/src/backend/KQueue.zig +++ b/src/backend/KQueue.zig @@ -189,7 +189,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8) var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return; defer dir.close(); - // Arena for all temporaries - freed in one shot at the end. + // Arena for all temporaries — freed in one shot at the end. var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); const tmp = arena.allocator(); diff --git a/src/backend/KQueueDir.zig b/src/backend/KQueueDir.zig index c1a8139..0cf816f 100644 --- a/src/backend/KQueueDir.zig +++ b/src/backend/KQueueDir.zig @@ -173,7 +173,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8) var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return; defer dir.close(); - // Arena for all temporaries - freed in one shot at the end. + // Arena for all temporaries — freed in one shot at the end. var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); const tmp = arena.allocator(); @@ -242,13 +242,13 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8) var cit = current_files.iterator(); while (cit.next()) |entry| { if (snapshot.getPtr(entry.key_ptr.*)) |stored_mtime| { - // File exists in both - check for modification via mtime change. + // File exists in both — check for modification via mtime change. if (stored_mtime.* != entry.value_ptr.*) { stored_mtime.* = entry.value_ptr.*; try to_modify.append(tmp, entry.key_ptr.*); // from current_files (tmp) } } else { - // New file - add to snapshot and to_create list. + // New file — add to snapshot and to_create list. const owned = allocator.dupe(u8, entry.key_ptr.*) catch |e| { return e; }; diff --git a/src/types.zig b/src/types.zig index 5af8794..b3627ba 100644 --- a/src/types.zig +++ b/src/types.zig @@ -18,25 +18,22 @@ pub const EventType = enum { /// /// Delivery varies by backend: /// - /// - **INotify**: all watches share a single inotify file descriptor, so - /// moves are paired by cookie across all watched roots. Renames between - /// two watched directories - even separate watch roots on the same - /// watcher instance - are delivered as a single atomic `rename` - /// callback. A move out of all watched paths appears as `deleted`; a - /// move in from an unwatched path appears as `created`. - /// - /// - **Windows**: renames within a single watched root are delivered as a - /// single atomic `rename` callback. However, each root uses an - /// independent `ReadDirectoryChangesW` handle with no shared cookie, so - /// a move between two separately watched roots cannot be paired: it - /// appears as `deleted` on the source side and `created` on the - /// destination side. + /// - **INotify**: renames within the watched tree are delivered as a + /// single atomic `rename` callback with both source and destination + /// paths. A move out of the tree appears as `deleted`; a move into + /// the tree appears as `created`. /// /// - **kqueue / kqueuedir**: when a watched *directory* is itself /// renamed, a `renamed` change event is emitted for the old directory /// path (the new path is not known). Renames of *files inside* a /// watched directory are detected indirectly via directory-level - /// `NOTE_WRITE` events and appear as `deleted` + `created`. + /// `NOTE_WRITE` events and appear as a `deleted` event for the old + /// name followed by a `created` event for the new name. + /// + /// - **Windows**: renames within the watched tree are delivered as a + /// single atomic `rename` callback, matching INotify behaviour. A + /// move out of the tree appears as `deleted`; a move into the tree + /// appears as `created`. /// /// - **FSEvents**: each path involved in a rename receives its own /// `renamed` change event; the two sides are not paired. @@ -64,9 +61,9 @@ pub const Error = error{ /// Selects how the watcher delivers events to the caller. /// -/// - `.threaded` - the backend spawns an internal thread that calls the +/// - `.threaded` — the backend spawns an internal thread that calls the /// handler directly. The caller just needs to keep the `Watcher` alive. -/// - `.polling` - no internal thread is created. The caller must poll +/// - `.polling` — no internal thread is created. The caller must poll /// `poll_fd()` for readability and call `handle_read_ready()` whenever /// data is available. Currently only supported on Linux (inotify). pub const InterfaceType = enum { polling, threaded }; diff --git a/test_manual.ps1 b/test_manual.ps1 index 672bc6b..cee98fe 100644 --- a/test_manual.ps1 +++ b/test_manual.ps1 @@ -14,17 +14,15 @@ if (-not (Test-Path $NW)) { exit 1 } -$TESTDIR = Join-Path $env:TEMP "nightwatch_manual_$PID" -$TESTDIR2 = Join-Path $env:TEMP "nightwatch_manual2_$PID" -New-Item -ItemType Directory -Path $TESTDIR | Out-Null -New-Item -ItemType Directory -Path $TESTDIR2 | Out-Null +$TESTDIR = Join-Path $env:TEMP "nightwatch_manual_$PID" +New-Item -ItemType Directory -Path $TESTDIR | Out-Null -Write-Host "--- watching $TESTDIR and $TESTDIR2 ---" +Write-Host "--- watching $TESTDIR ---" Write-Host "--- starting nightwatch (Ctrl-C to stop early) ---" Write-Host "" -# Start nightwatch in background watching both dirs, events go to stdout -$proc = Start-Process -FilePath $NW -ArgumentList $TESTDIR, $TESTDIR2 -NoNewWindow -PassThru +# Start nightwatch in background, events go to stdout +$proc = Start-Process -FilePath $NW -ArgumentList $TESTDIR -NoNewWindow -PassThru Start-Sleep -Milliseconds 500 Write-Host "[op] touch file1.txt" @@ -75,38 +73,8 @@ Write-Host "[op] rmdir dirB (and contents)" Remove-Item -Recurse -Force -Path "$TESTDIR\dirB" Start-Sleep -Milliseconds 500 -Write-Host "" -Write-Host "# cross-root renames (both dirs watched)" -Write-Host "" - -Write-Host "[op] mkdir subA in both roots" -New-Item -ItemType Directory -Path "$TESTDIR\subA" | Out-Null -New-Item -ItemType Directory -Path "$TESTDIR2\subA" | Out-Null -Start-Sleep -Milliseconds 400 - -Write-Host "[op] touch crossfile.txt in dir1" -New-Item -ItemType File -Path "$TESTDIR\crossfile.txt" | Out-Null -Start-Sleep -Milliseconds 400 - -Write-Host "[op] rename crossfile.txt: dir1 -> dir2 (root to root)" -Move-Item -Path "$TESTDIR\crossfile.txt" -Destination "$TESTDIR2\crossfile.txt" -Start-Sleep -Milliseconds 400 - -Write-Host "[op] touch subA\crosssub.txt in dir1" -New-Item -ItemType File -Path "$TESTDIR\subA\crosssub.txt" | Out-Null -Start-Sleep -Milliseconds 400 - -Write-Host "[op] rename subA\crosssub.txt: dir1\subA -> dir2\subA (subdir to subdir)" -Move-Item -Path "$TESTDIR\subA\crosssub.txt" -Destination "$TESTDIR2\subA\crosssub.txt" -Start-Sleep -Milliseconds 400 - -Write-Host "[op] rename subA: dir1 -> dir2 (subdir across roots)" -Move-Item -Path "$TESTDIR\subA" -Destination "$TESTDIR2\subA2" -Start-Sleep -Milliseconds 500 - Write-Host "" Write-Host "--- done, stopping nightwatch ---" Stop-Process -Id $proc.Id -ErrorAction SilentlyContinue $proc.WaitForExit() Remove-Item -Recurse -Force -Path $TESTDIR -Remove-Item -Recurse -Force -Path $TESTDIR2 diff --git a/test_manual.sh b/test_manual.sh index 3680d14..12db2ac 100755 --- a/test_manual.sh +++ b/test_manual.sh @@ -14,13 +14,12 @@ if [ ! -x "$NW" ]; then fi TESTDIR=$(mktemp -d) -TESTDIR2=$(mktemp -d) -echo "--- watching $TESTDIR and $TESTDIR2 ---" +echo "--- watching $TESTDIR ---" echo "--- starting nightwatch (Ctrl-C to stop early) ---" echo "" -# Start nightwatch in background watching both dirs, events go to stdout -"$NW" "$TESTDIR" "$TESTDIR2" & +# Start nightwatch in background, events go to stdout +"$NW" "$TESTDIR" & NW_PID=$! sleep 0.5 @@ -72,37 +71,8 @@ echo "[op] rmdir dirB (and contents)" rm -rf "$TESTDIR/dirB" sleep 0.5 -echo "" -echo "# cross-root renames (both dirs watched)" -echo "" - -echo "[op] mkdir subA in both roots" -mkdir "$TESTDIR/subA" -mkdir "$TESTDIR2/subA" -sleep 0.4 - -echo "[op] touch crossfile.txt in dir1" -touch "$TESTDIR/crossfile.txt" -sleep 0.4 - -echo "[op] rename crossfile.txt: dir1 -> dir2 (root to root)" -mv "$TESTDIR/crossfile.txt" "$TESTDIR2/crossfile.txt" -sleep 0.4 - -echo "[op] touch subA/crosssub.txt in dir1" -touch "$TESTDIR/subA/crosssub.txt" -sleep 0.4 - -echo "[op] rename subA/crosssub.txt: dir1/subA -> dir2/subA (subdir to subdir)" -mv "$TESTDIR/subA/crosssub.txt" "$TESTDIR2/subA/crosssub.txt" -sleep 0.4 - -echo "[op] rename subA: dir1 -> dir2 (subdir across roots)" -mv "$TESTDIR/subA" "$TESTDIR2/subA2" -sleep 0.5 - echo "" echo "--- done, stopping nightwatch ---" kill $NW_PID 2>/dev/null wait $NW_PID 2>/dev/null -rm -rf "$TESTDIR" "$TESTDIR2" +rm -rf "$TESTDIR"