@@ -21,6 +21,14 @@ fn is_visible(entry: &FileEntry) -> bool {
2121 !entry. name . starts_with ( '.' )
2222}
2323
24+ fn visible_entries < ' a > ( entries : & ' a [ FileEntry ] , include_hidden : bool ) -> Box < dyn Iterator < Item = & ' a FileEntry > + ' a > {
25+ if include_hidden {
26+ Box :: new ( entries. iter ( ) )
27+ } else {
28+ Box :: new ( entries. iter ( ) . filter ( |e| is_visible ( e) ) )
29+ }
30+ }
31+
2432// ============================================================================
2533// Listing lifecycle
2634// ============================================================================
@@ -82,12 +90,7 @@ pub fn list_directory_start_with_volume(
8290 // Generate listing ID
8391 let listing_id = Uuid :: new_v4 ( ) . to_string ( ) ;
8492
85- // Count visible entries based on include_hidden setting
86- let total_count = if include_hidden {
87- all_entries. len ( )
88- } else {
89- all_entries. iter ( ) . filter ( |e| is_visible ( e) ) . count ( )
90- } ;
93+ let total_count = visible_entries ( & all_entries, include_hidden) . count ( ) ;
9194
9295 // Enrich directory entries with index data (recursive_size etc.) before sorting,
9396 // so that sort-by-size works correctly for directories.
@@ -165,19 +168,11 @@ pub fn get_file_range(
165168 . get ( listing_id)
166169 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
167170
168- // Filter entries if not including hidden
169- // Cache entries are already enriched by the path that stored them (streaming,
170- // watcher update, re-sort). Index freshness is handled separately by
171- // `index-dir-updated` → `refreshIndexSizes` → `getDirStatsBatch`.
172- let entries: Vec < FileEntry > = if include_hidden {
173- let end = ( start + count) . min ( listing. entries . len ( ) ) ;
174- listing. entries [ start..end] . to_vec ( )
175- } else {
176- // Need to filter and then slice
177- let visible: Vec < & FileEntry > = listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( ) ;
178- let end = ( start + count) . min ( visible. len ( ) ) ;
179- visible[ start..end] . iter ( ) . cloned ( ) . cloned ( ) . collect ( )
180- } ;
171+ let entries: Vec < FileEntry > = visible_entries ( & listing. entries , include_hidden)
172+ . skip ( start)
173+ . take ( count)
174+ . cloned ( )
175+ . collect ( ) ;
181176
182177 Ok ( entries)
183178}
@@ -190,11 +185,7 @@ pub fn get_total_count(listing_id: &str, include_hidden: bool) -> Result<usize,
190185 . get ( listing_id)
191186 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
192187
193- if include_hidden {
194- Ok ( listing. entries . len ( ) )
195- } else {
196- Ok ( listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . count ( ) )
197- }
188+ Ok ( visible_entries ( & listing. entries , include_hidden) . count ( ) )
198189}
199190
200191/// Gets the maximum filename width for a cached listing.
@@ -210,18 +201,10 @@ pub fn get_max_filename_width(listing_id: &str, include_hidden: bool) -> Result<
210201
211202 let font_id = "system-400-12" ; // Default font (must match list_directory_start_with_volume)
212203
213- let max_width = if include_hidden {
214- let filenames: Vec < & str > = listing. entries . iter ( ) . map ( |e| e. name . as_str ( ) ) . collect ( ) ;
215- crate :: font_metrics:: calculate_max_width ( & filenames, font_id)
216- } else {
217- let filenames: Vec < & str > = listing
218- . entries
219- . iter ( )
220- . filter ( |e| is_visible ( e) )
221- . map ( |e| e. name . as_str ( ) )
222- . collect ( ) ;
223- crate :: font_metrics:: calculate_max_width ( & filenames, font_id)
224- } ;
204+ let filenames: Vec < & str > = visible_entries ( & listing. entries , include_hidden)
205+ . map ( |e| e. name . as_str ( ) )
206+ . collect ( ) ;
207+ let max_width = crate :: font_metrics:: calculate_max_width ( & filenames, font_id) ;
225208
226209 Ok ( max_width)
227210}
@@ -234,13 +217,7 @@ pub fn find_file_index(listing_id: &str, name: &str, include_hidden: bool) -> Re
234217 . get ( listing_id)
235218 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
236219
237- if include_hidden {
238- Ok ( listing. entries . iter ( ) . position ( |e| e. name == name) )
239- } else {
240- // Find index in filtered list
241- let visible: Vec < & FileEntry > = listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( ) ;
242- Ok ( visible. iter ( ) . position ( |e| e. name == name) )
243- }
220+ Ok ( visible_entries ( & listing. entries , include_hidden) . position ( |e| e. name == name) )
244221}
245222
246223/// Finds the indices of multiple files by name in a cached listing (batch version of `find_file_index`).
@@ -260,13 +237,7 @@ pub fn find_file_indices(
260237 let lookup: std:: collections:: HashSet < & str > = names. iter ( ) . map ( |n| n. as_str ( ) ) . collect ( ) ;
261238 let mut result = HashMap :: with_capacity ( names. len ( ) ) ;
262239
263- let entries: Box < dyn Iterator < Item = & FileEntry > > = if include_hidden {
264- Box :: new ( listing. entries . iter ( ) )
265- } else {
266- Box :: new ( listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) )
267- } ;
268-
269- for ( idx, entry) in entries. enumerate ( ) {
240+ for ( idx, entry) in visible_entries ( & listing. entries , include_hidden) . enumerate ( ) {
270241 if lookup. contains ( entry. name . as_str ( ) ) {
271242 result. insert ( entry. name . clone ( ) , idx) ;
272243 }
@@ -283,28 +254,16 @@ pub fn get_file_at(listing_id: &str, index: usize, include_hidden: bool) -> Resu
283254 . get ( listing_id)
284255 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
285256
286- if include_hidden {
287- let result = listing. entries . get ( index) . cloned ( ) ;
288- if result. is_none ( ) {
289- log:: error!(
290- "get_file_at: index {} out of bounds (listing has {} entries) - frontend/backend index mismatch!" ,
291- index,
292- listing. entries. len( )
293- ) ;
294- }
295- Ok ( result)
296- } else {
297- let visible: Vec < & FileEntry > = listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( ) ;
298- let result = visible. get ( index) . cloned ( ) . cloned ( ) ;
299- if result. is_none ( ) {
300- log:: error!(
301- "get_file_at: index {} out of bounds (listing has {} visible entries) - frontend/backend index mismatch!" ,
302- index,
303- visible. len( )
304- ) ;
305- }
306- Ok ( result)
257+ let result = visible_entries ( & listing. entries , include_hidden) . nth ( index) . cloned ( ) ;
258+ if result. is_none ( ) {
259+ let total = visible_entries ( & listing. entries , include_hidden) . count ( ) ;
260+ log:: error!(
261+ "get_file_at: index {} out of bounds (listing has {} entries) - frontend/backend index mismatch!" ,
262+ index,
263+ total
264+ ) ;
307265 }
266+ Ok ( result)
308267}
309268
310269/// Gets file paths at specific indices from a cached listing.
@@ -322,12 +281,7 @@ pub fn get_paths_at_indices(
322281 . get ( listing_id)
323282 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
324283
325- // Build visible entries view (with or without hidden files)
326- let visible: Vec < & FileEntry > = if include_hidden {
327- listing. entries . iter ( ) . collect ( )
328- } else {
329- listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( )
330- } ;
284+ let visible: Vec < & FileEntry > = visible_entries ( & listing. entries , include_hidden) . collect ( ) ;
331285
332286 let mut paths = Vec :: with_capacity ( selected_indices. len ( ) ) ;
333287 for & frontend_idx in selected_indices {
@@ -392,11 +346,7 @@ pub fn resort_listing(
392346 None
393347 } else {
394348 selected_indices. map ( |indices| {
395- let entries_for_index = if include_hidden {
396- listing. entries . iter ( ) . collect :: < Vec < _ > > ( )
397- } else {
398- listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( )
399- } ;
349+ let entries_for_index: Vec < _ > = visible_entries ( & listing. entries , include_hidden) . collect ( ) ;
400350 indices
401351 . iter ( )
402352 . filter_map ( |& idx| entries_for_index. get ( idx) . map ( |e| e. name . clone ( ) ) )
@@ -414,34 +364,16 @@ pub fn resort_listing(
414364 listing. sort_order = sort_order;
415365
416366 // Find the new cursor position
417- let new_cursor_index = cursor_filename. and_then ( |name| {
418- if include_hidden {
419- listing. entries . iter ( ) . position ( |e| e. name == name)
420- } else {
421- listing
422- . entries
423- . iter ( )
424- . filter ( |e| is_visible ( e) )
425- . position ( |e| e. name == name)
426- }
427- } ) ;
367+ let new_cursor_index =
368+ cursor_filename. and_then ( |name| visible_entries ( & listing. entries , include_hidden) . position ( |e| e. name == name) ) ;
428369
429370 // Find new indices of selected files
430371 let new_selected_indices = if all_selected {
431- // All files are still selected after re-sort
432- let count = if include_hidden {
433- listing. entries . len ( )
434- } else {
435- listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . count ( )
436- } ;
372+ let count = visible_entries ( & listing. entries , include_hidden) . count ( ) ;
437373 Some ( ( 0 ..count) . collect ( ) )
438374 } else {
439375 selected_filenames. map ( |filenames| {
440- let entries_for_lookup: Vec < _ > = if include_hidden {
441- listing. entries . iter ( ) . collect ( )
442- } else {
443- listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( )
444- } ;
376+ let entries_for_lookup: Vec < _ > = visible_entries ( & listing. entries , include_hidden) . collect ( ) ;
445377 filenames
446378 . iter ( )
447379 . filter_map ( |name| entries_for_lookup. iter ( ) . position ( |e| e. name == * name) )
@@ -551,20 +483,15 @@ pub fn get_listing_stats(
551483 . get ( listing_id)
552484 . ok_or_else ( || format ! ( "Listing not found: {}" , listing_id) ) ?;
553485
554- // Get visible entries based on include_hidden setting
555- let visible_entries: Vec < & FileEntry > = if include_hidden {
556- listing. entries . iter ( ) . collect ( )
557- } else {
558- listing. entries . iter ( ) . filter ( |e| is_visible ( e) ) . collect ( )
559- } ;
486+ let visible: Vec < & FileEntry > = visible_entries ( & listing. entries , include_hidden) . collect ( ) ;
560487
561488 // Calculate totals
562489 let mut total_files: usize = 0 ;
563490 let mut total_dirs: usize = 0 ;
564491 let mut total_size: u64 = 0 ;
565492 let mut total_physical_size: u64 = 0 ;
566493
567- for entry in & visible_entries {
494+ for entry in & visible {
568495 if entry. is_directory {
569496 total_dirs += 1 ;
570497 if let Some ( size) = entry. recursive_size {
@@ -593,7 +520,7 @@ pub fn get_listing_stats(
593520 let mut sel_physical_size: u64 = 0 ;
594521
595522 for & idx in indices {
596- if let Some ( entry) = visible_entries . get ( idx) {
523+ if let Some ( entry) = visible . get ( idx) {
597524 if entry. is_directory {
598525 sel_dirs += 1 ;
599526 if let Some ( size) = entry. recursive_size {
0 commit comments