@@ -137,6 +137,8 @@ pub(crate) struct FileSync {
137137 files : StableVec < FileItem > ,
138138 indexable_count : usize ,
139139 base_count : usize ,
140+ /// Number of active present files that exists in the file sytsem
141+ live_count : usize ,
140142 /// Sorted directory table. `StableVec` so post-scan snapshots can keep
141143 /// the allocation alive across a picker drop without copying, and so
142144 /// concurrent readers observe a consistent view via the same shared
@@ -159,6 +161,7 @@ impl FileSync {
159161 files : StableVec :: from_vec_with_reserve ( Vec :: new ( ) , MAX_OVERFLOW_FILES ) ,
160162 indexable_count : 0 ,
161163 base_count : 0 ,
164+ live_count : 0 ,
162165 dirs : StableVec :: from_vec_with_reserve ( Vec :: new ( ) , 0 ) ,
163166 overflow_builder : None ,
164167 git_workdir : None ,
@@ -321,10 +324,11 @@ impl FileSync {
321324 overflow_arena
322325 } ;
323326 if predicate ( file, arena) {
324- file. delete ( ) ;
327+ file. set_deleted ( true ) ;
325328 tombstoned += 1 ;
326329 }
327330 }
331+ self . live_count -= tombstoned;
328332 tombstoned
329333 }
330334}
@@ -566,6 +570,12 @@ impl FilePicker {
566570 self . sync_data . files ( )
567571 }
568572
573+ /// Count of live (non-tombstoned) files. O(1).
574+ #[ inline]
575+ pub fn live_file_count ( & self ) -> usize {
576+ self . sync_data . live_count
577+ }
578+
569579 pub fn get_overflow_files ( & self ) -> & [ FileItem ] {
570580 self . sync_data . overflow_files ( )
571581 }
@@ -871,7 +881,7 @@ impl FilePicker {
871881 "Fuzzy search" ,
872882 ) ;
873883
874- let total_files = files . len ( ) ;
884+ let total_files = self . live_file_count ( ) ;
875885 let location = query. location ;
876886
877887 // Get effective query for max_typos calculation (without location suffix)
@@ -1060,7 +1070,7 @@ impl FilePicker {
10601070 items : vec ! [ ] ,
10611071 scores : vec ! [ ] ,
10621072 total_matched,
1063- total_files : self . sync_data . files ( ) . len ( ) ,
1073+ total_files : self . live_file_count ( ) ,
10641074 total_dirs,
10651075 location,
10661076 } ;
@@ -1074,7 +1084,7 @@ impl FilePicker {
10741084 items,
10751085 scores,
10761086 total_matched,
1077- total_files : self . sync_data . files ( ) . len ( ) ,
1087+ total_files : self . live_file_count ( ) ,
10781088 total_dirs,
10791089 location,
10801090 } ;
@@ -1422,7 +1432,7 @@ impl FilePicker {
14221432 "File market for modification doesn't exists or not accessible"
14231433 )
14241434 } )
1425- . ok ( ) ?; // if we can't read metadata this file either doesn't exists or not accessible
1435+ . ok ( ) ?;
14261436
14271437 let size = metadata. len ( ) ;
14281438 let modified_time = metadata
@@ -1431,7 +1441,8 @@ impl FilePicker {
14311441 . and_then ( |t| t. duration_since ( SystemTime :: UNIX_EPOCH ) . ok ( ) )
14321442 . map ( |d| d. as_secs ( ) ) ;
14331443
1434- if file. is_deleted ( ) {
1444+ let was_deleted = file. is_deleted ( ) ;
1445+ if was_deleted {
14351446 file. set_deleted ( false ) ;
14361447 }
14371448
@@ -1451,7 +1462,13 @@ impl FilePicker {
14511462 }
14521463 }
14531464
1454- Some ( & * self . sync_data . get_file_mut ( pos) ?)
1465+ // Increment after dropping the mutable file reference so the
1466+ // reborrow for the return doesn't conflict.
1467+ if was_deleted {
1468+ self . sync_data . live_count += 1 ;
1469+ }
1470+
1471+ self . sync_data . files ( ) . get ( pos)
14551472 }
14561473
14571474 /// Adds a new file to picker, if the file can not be added returns `None`
@@ -1495,6 +1512,7 @@ impl FilePicker {
14951512 return None ;
14961513 }
14971514
1515+ self . sync_data . live_count += 1 ;
14981516 self . sync_data . files . last ( )
14991517 }
15001518
@@ -1505,19 +1523,12 @@ impl FilePicker {
15051523 Ok ( index) => {
15061524 let file = & mut self . sync_data . files [ index] ;
15071525 file. set_deleted ( true ) ;
1508- // Clear any cached git status — the tombstone no longer
1509- // corresponds to a real worktree file, so any previously
1510- // cached status (e.g. `WT_MODIFIED` from before the delete)
1511- // is actively misleading. All user-facing search
1512- // paths filter `is_deleted()` so this is invisible today,
1513- // but keeping the invariant "tombstone ⇒ git_status=None"
1514- // means a new reader that forgets the filter can't leak
1515- // stale data.
15161526 file. git_status = None ;
15171527 file. invalidate_mmap ( & self . cache_budget ) ;
15181528 if let Some ( ref overlay) = self . sync_data . bigram_overlay {
15191529 overlay. write ( ) . delete_file ( index) ;
15201530 }
1531+ self . sync_data . live_count -= 1 ;
15211532 true
15221533 }
15231534 Err ( _) => {
@@ -1528,6 +1539,7 @@ impl FilePicker {
15281539 file. set_deleted ( true ) ;
15291540 file. git_status = None ;
15301541 file. invalidate_mmap ( & self . cache_budget ) ;
1542+ self . sync_data . live_count -= 1 ;
15311543 true
15321544 } else {
15331545 false
@@ -1979,6 +1991,7 @@ impl FileSync {
19791991 files : StableVec :: from_vec_with_reserve ( files, MAX_OVERFLOW_FILES ) ,
19801992 indexable_count,
19811993 base_count,
1994+ live_count : base_count,
19821995 dirs : StableVec :: from_vec_with_reserve ( dirs, 0 ) ,
19831996 overflow_builder : None ,
19841997 git_workdir,
0 commit comments