@@ -83,29 +83,42 @@ impl ImportMap {
83
83
. iter ( )
84
84
// We've only collected items, whose name cannot be tuple field so unwrapping is fine.
85
85
. flat_map ( |( & item, ( info, _) ) | {
86
- info. iter ( ) . enumerate ( ) . map ( move | ( idx , info ) | {
87
- ( item , info . name . as_str ( ) . unwrap ( ) . to_ascii_lowercase ( ) , idx as u32 )
88
- } )
86
+ info. iter ( )
87
+ . enumerate ( )
88
+ . map ( move | ( idx , info ) | ( item , info . name . to_smol_str ( ) , idx as u32 ) )
89
89
} )
90
90
. collect ( ) ;
91
- importables. sort_by ( |( _, lhs_name, _) , ( _, rhs_name, _) | lhs_name. cmp ( rhs_name) ) ;
91
+ importables. sort_by ( |( _, l_info, _) , ( _, r_info, _) | {
92
+ let lhs_chars = l_info. chars ( ) . map ( |c| c. to_ascii_lowercase ( ) ) ;
93
+ let rhs_chars = r_info. chars ( ) . map ( |c| c. to_ascii_lowercase ( ) ) ;
94
+ lhs_chars. cmp ( rhs_chars)
95
+ } ) ;
92
96
importables. dedup ( ) ;
93
97
94
98
// Build the FST, taking care not to insert duplicate values.
95
99
let mut builder = fst:: MapBuilder :: memory ( ) ;
96
- let iter = importables
100
+ let mut iter = importables
97
101
. iter ( )
98
102
. enumerate ( )
99
- . dedup_by ( |( _, ( _, lhs, _) ) , ( _, ( _, rhs, _) ) | lhs == rhs) ;
100
- for ( start_idx, ( _, name, _) ) in iter {
101
- let _ = builder. insert ( name, start_idx as u64 ) ;
103
+ . dedup_by ( |& ( _, ( _, lhs, _) ) , & ( _, ( _, rhs, _) ) | lhs. eq_ignore_ascii_case ( rhs) ) ;
104
+
105
+ let mut insert = |name : & str , start, end| {
106
+ builder. insert ( name. to_ascii_lowercase ( ) , ( ( start as u64 ) << 32 ) | end as u64 ) . unwrap ( )
107
+ } ;
108
+
109
+ if let Some ( ( mut last, ( _, name, _) ) ) = iter. next ( ) {
110
+ debug_assert_eq ! ( last, 0 ) ;
111
+ let mut last_name = name;
112
+ for ( next, ( _, next_name, _) ) in iter {
113
+ insert ( last_name, last, next) ;
114
+ last = next;
115
+ last_name = next_name;
116
+ }
117
+ insert ( last_name, last, importables. len ( ) ) ;
102
118
}
103
119
104
- Arc :: new ( ImportMap {
105
- item_to_info_map : map,
106
- fst : builder. into_map ( ) ,
107
- importables : importables. into_iter ( ) . map ( |( item, _, idx) | ( item, idx) ) . collect ( ) ,
108
- } )
120
+ let importables = importables. into_iter ( ) . map ( |( item, _, idx) | ( item, idx) ) . collect ( ) ;
121
+ Arc :: new ( ImportMap { item_to_info_map : map, fst : builder. into_map ( ) , importables } )
109
122
}
110
123
111
124
pub fn import_info_for ( & self , item : ItemInNs ) -> Option < & [ ImportInfo ] > {
@@ -266,8 +279,8 @@ impl fmt::Debug for ImportMap {
266
279
}
267
280
268
281
/// A way to match import map contents against the search query.
269
- #[ derive( Copy , Clone , Debug ) ]
270
- enum SearchMode {
282
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
283
+ pub enum SearchMode {
271
284
/// Import map entry should strictly match the query string.
272
285
Exact ,
273
286
/// Import map entry should contain all letters from the query string,
@@ -277,6 +290,42 @@ enum SearchMode {
277
290
Prefix ,
278
291
}
279
292
293
+ impl SearchMode {
294
+ pub fn check ( self , query : & str , case_sensitive : bool , candidate : & str ) -> bool {
295
+ match self {
296
+ SearchMode :: Exact if case_sensitive => candidate == query,
297
+ SearchMode :: Exact => candidate. eq_ignore_ascii_case ( & query) ,
298
+ SearchMode :: Prefix => {
299
+ query. len ( ) <= candidate. len ( ) && {
300
+ let prefix = & candidate[ ..query. len ( ) as usize ] ;
301
+ if case_sensitive {
302
+ prefix == query
303
+ } else {
304
+ prefix. eq_ignore_ascii_case ( & query)
305
+ }
306
+ }
307
+ }
308
+ SearchMode :: Fuzzy => {
309
+ let mut name = candidate;
310
+ query. chars ( ) . all ( |query_char| {
311
+ let m = if case_sensitive {
312
+ name. match_indices ( query_char) . next ( )
313
+ } else {
314
+ name. match_indices ( [ query_char, query_char. to_ascii_uppercase ( ) ] ) . next ( )
315
+ } ;
316
+ match m {
317
+ Some ( ( index, _) ) => {
318
+ name = & name[ index + 1 ..] ;
319
+ true
320
+ }
321
+ None => false ,
322
+ }
323
+ } )
324
+ }
325
+ }
326
+ }
327
+ }
328
+
280
329
/// Three possible ways to search for the name in associated and/or other items.
281
330
#[ derive( Debug , Clone , Copy ) ]
282
331
pub enum AssocSearchMode {
@@ -392,67 +441,28 @@ fn search_maps(
392
441
query : & Query ,
393
442
) -> FxHashSet < ItemInNs > {
394
443
let mut res = FxHashSet :: default ( ) ;
395
- while let Some ( ( key , indexed_values) ) = stream. next ( ) {
444
+ while let Some ( ( _ , indexed_values) ) = stream. next ( ) {
396
445
for & IndexedValue { index : import_map_idx, value } in indexed_values {
397
- let import_map = & import_maps[ import_map_idx] ;
398
- let importables = & import_map. importables [ value as usize ..] ;
446
+ let end = ( value & 0xFFFF_FFFF ) as usize ;
447
+ let start = ( value >> 32 ) as usize ;
448
+ let ImportMap { item_to_info_map, importables, .. } = & * import_maps[ import_map_idx] ;
449
+ let importables = & importables[ start as usize ..end] ;
399
450
400
451
let iter = importables
401
452
. iter ( )
402
453
. copied ( )
403
- . map ( |( item, info_idx) | {
404
- let ( import_infos, assoc_mode) = & import_map. item_to_info_map [ & item] ;
405
- ( item, & import_infos[ info_idx as usize ] , * assoc_mode)
454
+ . filter_map ( |( item, info_idx) | {
455
+ let ( import_infos, assoc_mode) = & item_to_info_map[ & item] ;
456
+ query
457
+ . matches_assoc_mode ( * assoc_mode)
458
+ . then ( || ( item, & import_infos[ info_idx as usize ] ) )
406
459
} )
407
- // we put all entries with the same lowercased name in a row, so stop once we find a
408
- // different name in the importables
409
- // FIXME: Consider putting a range into the value: u64 as (u32, u32)?
410
- . take_while ( |& ( _, info, _) | {
411
- info. name . to_smol_str ( ) . as_bytes ( ) . eq_ignore_ascii_case ( & key)
412
- } )
413
- . filter ( |& ( _, info, assoc_mode) | {
414
- if !query. matches_assoc_mode ( assoc_mode) {
415
- return false ;
416
- }
417
- if !query. case_sensitive {
418
- return true ;
419
- }
420
- let name = info. name . to_smol_str ( ) ;
421
- // FIXME: Deduplicate this from ide-db
422
- match query. search_mode {
423
- SearchMode :: Exact => !query. case_sensitive || name == query. query ,
424
- SearchMode :: Prefix => {
425
- query. query . len ( ) <= name. len ( ) && {
426
- let prefix = & name[ ..query. query . len ( ) as usize ] ;
427
- if query. case_sensitive {
428
- prefix == query. query
429
- } else {
430
- prefix. eq_ignore_ascii_case ( & query. query )
431
- }
432
- }
433
- }
434
- SearchMode :: Fuzzy => {
435
- let mut name = & * name;
436
- query. query . chars ( ) . all ( |query_char| {
437
- let m = if query. case_sensitive {
438
- name. match_indices ( query_char) . next ( )
439
- } else {
440
- name. match_indices ( [
441
- query_char,
442
- query_char. to_ascii_uppercase ( ) ,
443
- ] )
444
- . next ( )
445
- } ;
446
- match m {
447
- Some ( ( index, _) ) => {
448
- name = & name[ index + 1 ..] ;
449
- true
450
- }
451
- None => false ,
452
- }
453
- } )
454
- }
455
- }
460
+ . filter ( |& ( _, info) | {
461
+ query. search_mode . check (
462
+ & query. query ,
463
+ query. case_sensitive ,
464
+ & info. name . to_smol_str ( ) ,
465
+ )
456
466
} ) ;
457
467
res. extend ( iter. map ( TupleExt :: head) ) ;
458
468
}
0 commit comments