forked from notify-rs/notify
-
Notifications
You must be signed in to change notification settings - Fork 2
Update WatchFilter API #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
MaggieShan
merged 1 commit into
warpdotdev/notify-8.0.0
from
maggs/update-watchfilter-api
May 21, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -228,6 +228,21 @@ impl EventLoop { | |
| None => self.paths.get(&event.wd).cloned(), | ||
| }; | ||
|
|
||
| // Look up the filter for the watch descriptor that produced this event. | ||
| // For child events, the descriptor belongs to the containing directory, | ||
| // while `event.name` identifies the child path. | ||
| let watch_filter = self | ||
| .paths | ||
| .get(&event.wd) | ||
| .and_then(|watch_path| self.watches.get(watch_path)) | ||
| .map(|(_, _, _, _, watch_filter)| watch_filter); | ||
| let should_emit_path = match (&path, watch_filter) { | ||
| (Some(path), Some(watch_filter)) => { | ||
| watch_filter.should_emit_event(path) | ||
| } | ||
| _ => true, | ||
| }; | ||
|
|
||
| let mut evs = Vec::new(); | ||
|
|
||
| if event.mask.contains(EventMask::MOVED_FROM) { | ||
|
|
@@ -371,8 +386,12 @@ impl EventLoop { | |
| ); | ||
| } | ||
|
|
||
| for ev in evs { | ||
| self.event_handler.handle_event(Ok(ev)); | ||
| // Apply the emit filter only at dispatch time. The event translation above also | ||
| // updates recursive watch bookkeeping, so it must still run for suppressed paths | ||
| if should_emit_path { | ||
| for ev in evs { | ||
| self.event_handler.handle_event(Ok(ev)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -408,29 +427,43 @@ impl EventLoop { | |
| mut watch_self: bool, | ||
| watch_filter: WatchFilter, | ||
| ) -> Result<()> { | ||
| if !watch_filter.should_watch(&path) { | ||
| return Ok(()); | ||
| } | ||
|
|
||
| // If the watch is not recursive, or if we determine (by stat'ing the path to get its | ||
| // metadata) that the watched path is not a directory, add a single path watch. | ||
| if !is_recursive || !metadata(&path).map_err(Error::io_watch)?.is_dir() { | ||
| return self.add_single_watch(path, false, true, WatchFilter::accept_all()); | ||
| return self.add_single_watch(path, false, watch_self, watch_filter); | ||
| } | ||
|
|
||
| // Recursive directory walk. Short-circuit if the root is pruned. | ||
| if !watch_filter.should_watch_directory(&path) { | ||
| return Ok(()); | ||
| } | ||
|
|
||
| for entry in WalkDir::new(path) | ||
| // We use the raw `WalkDir::IntoIter` so we can call `skip_current_dir()` for pruned entries. | ||
| // Non-directory entries are skipped because watches are only registered on directories | ||
| // (file-level events arrive through their parent's watch via `event.name`). | ||
| let mut iter = WalkDir::new(path) | ||
| .follow_links(self.follow_links) | ||
| .into_iter() | ||
| .filter_map(filter_dir) | ||
| .filter(|e| watch_filter.should_watch(e.path())) | ||
| { | ||
| self.add_single_watch( | ||
| entry.path().to_path_buf(), | ||
| is_recursive, | ||
| watch_self, | ||
| watch_filter.clone(), | ||
| )?; | ||
| watch_self = false; | ||
| .into_iter(); | ||
| while let Some(entry_result) = iter.next() { | ||
| let entry = match entry_result { | ||
| Ok(e) => e, | ||
| Err(_) => continue, | ||
| }; | ||
| if !entry.file_type().is_dir() { | ||
| continue; | ||
| } | ||
|
|
||
| if watch_filter.should_watch_directory(entry.path()) { | ||
| self.add_single_watch( | ||
| entry.path().to_path_buf(), | ||
| true, | ||
| watch_self, | ||
| watch_filter.clone(), | ||
| )?; | ||
| watch_self = false; | ||
| } else { | ||
| iter.skip_current_dir(); | ||
| } | ||
| } | ||
|
|
||
| Ok(()) | ||
|
|
@@ -546,18 +579,6 @@ impl EventLoop { | |
| } | ||
| } | ||
|
|
||
| /// return `DirEntry` when it is a directory | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above but I think we should be able to keep the original logic here |
||
| fn filter_dir(e: walkdir::Result<walkdir::DirEntry>) -> Option<walkdir::DirEntry> { | ||
| if let Ok(e) = e { | ||
| if let Ok(metadata) = e.metadata() { | ||
| if metadata.is_dir() { | ||
| return Some(e); | ||
| } | ||
| } | ||
| } | ||
| None | ||
| } | ||
|
|
||
| impl INotifyWatcher { | ||
| fn from_event_handler( | ||
| event_handler: Box<dyn EventHandler>, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooc why do we need to rewrite the for iterator into while let?
My understanding is we should just change should_watch in the original filter to should_watch_directory right?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iiuc the previous
filter_mapandfilterwould evaluate the entry/directory after it's already been explored, whereasWalkDir::skip_current_dirensures that we're skipping entries before exploring themI believe we can similarly use
WalkDir::filter_entryto be slightly more ergonomic but I don't have a strong opinion