From da4bc267cb26ab22a4f2dcbec2aa09f571cfd6d5 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 3 Jun 2026 10:00:29 +0200 Subject: [PATCH] filter files inside ignored directories in the watcher isIgnored only matched a directory path itself, not files beneath it, so a recursive watch still surfaced changes inside .dart_tool/, build/, etc. Linux usually hid this because inotify drops the nested creates; macOS FSEvents delivers them, so the tree reacted to churn it should ignore. Check each ancestor segment as a directory. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 4 ++++ lib/src/files/watcher.dart | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8661474..eebcb89b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,10 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. ### Fixed +- File watcher no longer emits change events for files inside ignored + directories (`.dart_tool/`, `build/`, etc.): it now checks ancestor dirs, + not just the leaf. Most visible on macOS, where FSEvents delivers the nested + creates that inotify usually drops. - The Claude sidebar's Activity / Team / Config sub-tabs are now keyboard- activatable: they were pointer-only (raw `GestureDetector`), so Tab traversal skipped them and Enter/Space did nothing. They now use `ClideTappable` diff --git a/lib/src/files/watcher.dart b/lib/src/files/watcher.dart index e349fe6c..0d109525 100644 --- a/lib/src/files/watcher.dart +++ b/lib/src/files/watcher.dart @@ -86,7 +86,7 @@ class FileWatcher { final rel = _toRelative(e.path); if (rel == null) return; final isDir = e.isDirectory; - if (ignore.isIgnored(rel, isDirectory: isDir)) return; + if (_ignored(rel, isDir)) return; _controller.add(FileChange( kind: FileChangeKind.fromEvent(e), path: rel, @@ -94,6 +94,19 @@ class FileWatcher { )); } + /// True if `rel` is ignored, or sits inside an ignored directory. + /// A `foo/` rule hides the directory *and everything under it*, but a + /// recursive watch still delivers events for those descendants — notably + /// macOS FSEvents, which reports nested creates that inotify often drops. + /// So check each ancestor segment as a directory, not just the leaf. + bool _ignored(String rel, bool isDir) { + if (ignore.isIgnored(rel, isDirectory: isDir)) return true; + for (var slash = rel.indexOf('/'); slash != -1; slash = rel.indexOf('/', slash + 1)) { + if (ignore.isIgnored(rel.substring(0, slash), isDirectory: true)) return true; + } + return false; + } + String? _toRelative(String abs) { final rootPath = root.absolute.path; if (!abs.startsWith(rootPath)) return null;