@@ -152,7 +152,7 @@ struct Queue {
152
152
153
153
impl Queue {
154
154
fn was_created ( & self ) -> bool {
155
- self . events . front ( ) . map_or ( false , |event| {
155
+ self . events . front ( ) . is_some_and ( |event| {
156
156
matches ! (
157
157
event. kind,
158
158
EventKind :: Create ( _) | EventKind :: Modify ( ModifyKind :: Name ( RenameMode :: To ) )
@@ -161,7 +161,7 @@ impl Queue {
161
161
}
162
162
163
163
fn was_removed ( & self ) -> bool {
164
- self . events . front ( ) . map_or ( false , |event| {
164
+ self . events . front ( ) . is_some_and ( |event| {
165
165
matches ! (
166
166
event. kind,
167
167
EventKind :: Remove ( _) | EventKind :: Modify ( ModifyKind :: Name ( RenameMode :: From ) )
@@ -170,9 +170,48 @@ impl Queue {
170
170
}
171
171
}
172
172
173
+ #[ derive( Debug ) ]
174
+ pub struct BlockEntry {
175
+ pub blocker_path : PathBuf ,
176
+ pub blocker_time : Instant ,
177
+ pub blockee_path : PathBuf ,
178
+ }
179
+
180
+ #[ derive( Debug , Default ) ]
181
+ pub struct BlockManager {
182
+ entries : Vec < BlockEntry > ,
183
+ }
184
+
185
+ impl BlockManager {
186
+ pub fn new ( ) -> BlockManager {
187
+ BlockManager {
188
+ entries : Vec :: new ( ) ,
189
+ }
190
+ }
191
+
192
+ pub fn add_blocker ( & mut self , entry : BlockEntry ) {
193
+ self . entries . push ( entry) ;
194
+ }
195
+
196
+ pub fn remove_blocker ( & mut self , path : & PathBuf , time : Instant ) {
197
+ self . entries
198
+ . retain ( |entry| entry. blocker_path != * path || entry. blocker_time != time) ;
199
+ }
200
+
201
+ pub fn is_blocked_by ( & self , path : & PathBuf ) -> Option < ( & PathBuf , Instant ) > {
202
+ for entry in & self . entries {
203
+ if entry. blockee_path == * path {
204
+ return Some ( ( & entry. blocker_path , entry. blocker_time ) ) ;
205
+ }
206
+ }
207
+ None
208
+ }
209
+ }
210
+
173
211
#[ derive( Debug ) ]
174
212
pub ( crate ) struct DebounceDataInner < T > {
175
213
queues : HashMap < PathBuf , Queue > ,
214
+ blocking : BlockManager ,
176
215
roots : Vec < ( PathBuf , RecursiveMode ) > ,
177
216
cache : T ,
178
217
rename_event : Option < ( DebouncedEvent , Option < FileId > ) > ,
@@ -185,6 +224,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
185
224
pub ( crate ) fn new ( cache : T , timeout : Duration ) -> Self {
186
225
Self {
187
226
queues : HashMap :: new ( ) ,
227
+ blocking : BlockManager :: new ( ) ,
188
228
roots : Vec :: new ( ) ,
189
229
cache,
190
230
rename_event : None ,
@@ -194,11 +234,17 @@ impl<T: FileIdCache> DebounceDataInner<T> {
194
234
}
195
235
}
196
236
237
+ fn contains_event ( & self , path : & PathBuf , time : Instant ) -> bool {
238
+ self . queues
239
+ . get ( path)
240
+ . is_some_and ( |queue| queue. events . iter ( ) . any ( |event| event. time == time) )
241
+ }
242
+
197
243
/// Retrieve a vec of debounced events, removing them if not continuous
198
244
pub fn debounced_events ( & mut self ) -> Vec < DebouncedEvent > {
199
245
let now = Instant :: now ( ) ;
200
- let mut events_expired = Vec :: with_capacity ( self . queues . len ( ) ) ;
201
- let mut queues_remaining = HashMap :: with_capacity ( self . queues . len ( ) ) ;
246
+ let events_count = self . queues . values ( ) . map ( |queue| queue . events . len ( ) ) . sum ( ) ;
247
+ let mut events_expired = Vec :: with_capacity ( events_count ) ;
202
248
203
249
if let Some ( event) = self . rescan_event . take ( ) {
204
250
if now. saturating_duration_since ( event. time ) >= self . timeout {
@@ -209,48 +255,62 @@ impl<T: FileIdCache> DebounceDataInner<T> {
209
255
}
210
256
}
211
257
212
- // TODO: perfect fit for drain_filter https://github.com/rust-lang/rust/issues/59618
213
- for ( path, mut queue) in self . queues . drain ( ) {
214
- let mut kind_index = HashMap :: new ( ) ;
215
-
216
- while let Some ( event) = queue. events . pop_front ( ) {
217
- if now. saturating_duration_since ( event. time ) >= self . timeout {
218
- // remove previous event of the same kind
219
- if let Some ( idx) = kind_index. get ( & event. kind ) . copied ( ) {
220
- events_expired. remove ( idx) ;
221
-
222
- kind_index. values_mut ( ) . for_each ( |i| {
223
- if * i > idx {
224
- * i -= 1
225
- }
226
- } )
227
- }
258
+ let mut kind_index: HashMap < PathBuf , HashMap < EventKind , usize > > = HashMap :: new ( ) ;
228
259
229
- kind_index. insert ( event. kind , events_expired. len ( ) ) ;
260
+ while let Some ( path) = self
261
+ . queues
262
+ // iterate over all queues
263
+ . iter ( )
264
+ // get the first event of every queue
265
+ . filter_map ( |( path, queue) | queue. events . front ( ) . map ( |event| ( path, event. time ) ) )
266
+ // filter out all blocked events
267
+ . filter ( |( path, _) | {
268
+ self . blocking
269
+ . is_blocked_by ( path)
270
+ . map_or ( true , |( path, time) | !self . contains_event ( path, time) )
271
+ } )
272
+ // get the event with the earliest timestamp
273
+ . min_by_key ( |( _, time) | * time)
274
+ // get the path of the event
275
+ . map ( |( path, _) | path. clone ( ) )
276
+ {
277
+ let event = self
278
+ . queues
279
+ . get_mut ( & path)
280
+ . unwrap ( )
281
+ . events
282
+ . pop_front ( )
283
+ . unwrap ( ) ;
230
284
231
- events_expired. push ( event) ;
232
- } else {
233
- queue. events . push_front ( event) ;
234
- break ;
285
+ if now. saturating_duration_since ( event. time ) >= self . timeout {
286
+ // remove previous event of the same kind
287
+ let kind_index = kind_index. entry ( path. clone ( ) ) . or_default ( ) ;
288
+ if let Some ( idx) = kind_index. get ( & event. kind ) . copied ( ) {
289
+ events_expired. remove ( idx) ;
290
+
291
+ kind_index. values_mut ( ) . for_each ( |i| {
292
+ if * i > idx {
293
+ * i -= 1
294
+ }
295
+ } )
235
296
}
236
- }
297
+ kind_index . insert ( event . kind , events_expired . len ( ) ) ;
237
298
238
- if !queue. events . is_empty ( ) {
239
- queues_remaining. insert ( path, queue) ;
299
+ self . blocking . remove_blocker ( & path, event. time ) ;
300
+
301
+ events_expired. push ( event) ;
302
+ } else {
303
+ self . queues . get_mut ( & path) . unwrap ( ) . events . push_front ( event) ;
304
+
305
+ break ;
240
306
}
241
307
}
242
308
243
- self . queues = queues_remaining ;
309
+ self . queues . retain ( |_ , queue| !queue . events . is_empty ( ) ) ;
244
310
245
- // order events for different files chronologically, but keep the order of events for the same file
246
- events_expired. sort_by ( |event_a, event_b| {
247
- // use the last path because rename events are emitted for the target path
248
- if event_a. paths . last ( ) == event_b. paths . last ( ) {
249
- std:: cmp:: Ordering :: Equal
250
- } else {
251
- event_a. time . cmp ( & event_b. time )
252
- }
253
- } ) ;
311
+ if self . queues . is_empty ( ) {
312
+ self . blocking . entries . clear ( ) ;
313
+ }
254
314
255
315
events_expired
256
316
}
@@ -425,18 +485,6 @@ impl<T: FileIdCache> DebounceDataInner<T> {
425
485
source_queue. events . remove ( remove_index) ;
426
486
}
427
487
428
- // split off remove or move out event and add it back to the events map
429
- if source_queue. was_removed ( ) {
430
- let event = source_queue. events . pop_front ( ) . unwrap ( ) ;
431
-
432
- self . queues . insert (
433
- event. paths [ 0 ] . clone ( ) ,
434
- Queue {
435
- events : [ event] . into ( ) ,
436
- } ,
437
- ) ;
438
- }
439
-
440
488
// update paths
441
489
for e in & mut source_queue. events {
442
490
e. paths = vec ! [ event. paths[ 0 ] . clone( ) ] ;
@@ -455,7 +503,12 @@ impl<T: FileIdCache> DebounceDataInner<T> {
455
503
}
456
504
457
505
if let Some ( target_queue) = self . queues . get_mut ( & event. paths [ 0 ] ) {
458
- if !target_queue. was_created ( ) {
506
+ if target_queue. was_removed ( ) {
507
+ let event = target_queue. events . pop_front ( ) . unwrap ( ) ;
508
+ source_queue. events . push_front ( event) ;
509
+ }
510
+
511
+ if !target_queue. was_created ( ) && !source_queue. was_removed ( ) {
459
512
let mut remove_event = DebouncedEvent {
460
513
event : Event {
461
514
kind : EventKind :: Remove ( RemoveKind :: Any ) ,
@@ -473,6 +526,8 @@ impl<T: FileIdCache> DebounceDataInner<T> {
473
526
} else {
474
527
self . queues . insert ( event. paths [ 0 ] . clone ( ) , source_queue) ;
475
528
}
529
+
530
+ self . find_blocked_events ( & event. paths [ 0 ] ) ;
476
531
}
477
532
478
533
fn push_remove_event ( & mut self , event : Event , time : Instant ) {
@@ -518,6 +573,25 @@ impl<T: FileIdCache> DebounceDataInner<T> {
518
573
) ;
519
574
}
520
575
}
576
+
577
+ fn find_blocked_events ( & mut self , path : & Path ) {
578
+ for queue in self . queues . values_mut ( ) {
579
+ for event in & mut queue. events {
580
+ if matches ! (
581
+ event. event. kind,
582
+ EventKind :: Modify ( ModifyKind :: Name ( RenameMode :: Both ) )
583
+ ) && event. event . paths [ 0 ] == path
584
+ {
585
+ self . blocking . add_blocker ( BlockEntry {
586
+ blocker_path : event. event . paths [ 1 ] . clone ( ) ,
587
+ blocker_time : event. time ,
588
+ blockee_path : path. to_path_buf ( ) ,
589
+ } ) ;
590
+ break ;
591
+ }
592
+ }
593
+ }
594
+ }
521
595
}
522
596
523
597
/// Debouncer guard, stops the debouncer on drop.
@@ -755,6 +829,11 @@ mod tests {
755
829
"add_remove_parent_event_after_remove_child_event" ,
756
830
"add_errors" ,
757
831
"emit_continuous_modify_content_events" ,
832
+ "emit_create_event_after_safe_save_and_backup_override" ,
833
+ "emit_create_event_after_safe_save_and_backup_rotation_twice" ,
834
+ "emit_create_event_after_safe_save_and_backup_rotation" ,
835
+ "emit_create_event_after_safe_save_and_double_move" ,
836
+ "emit_create_event_after_safe_save_and_double_move_and_recreate" ,
758
837
"emit_events_in_chronological_order" ,
759
838
"emit_events_with_a_prepended_rename_event" ,
760
839
"emit_close_events_only_once" ,
0 commit comments