Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/import_map.rs')
| -rw-r--r-- | crates/hir-def/src/import_map.rs | 150 |
1 files changed, 80 insertions, 70 deletions
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 9a40d30637..989bbc7bfb 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -83,29 +83,42 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.as_str().unwrap().to_ascii_lowercase(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.to_smol_str(), idx as u32)) }) .collect(); - importables.sort_by(|(_, lhs_name, _), (_, rhs_name, _)| lhs_name.cmp(rhs_name)); + importables.sort_by(|(_, l_info, _), (_, r_info, _)| { + let lhs_chars = l_info.chars().map(|c| c.to_ascii_lowercase()); + let rhs_chars = r_info.chars().map(|c| c.to_ascii_lowercase()); + lhs_chars.cmp(rhs_chars) + }); importables.dedup(); // Build the FST, taking care not to insert duplicate values. let mut builder = fst::MapBuilder::memory(); - let iter = importables + let mut iter = importables .iter() .enumerate() - .dedup_by(|(_, (_, lhs, _)), (_, (_, rhs, _))| lhs == rhs); - for (start_idx, (_, name, _)) in iter { - let _ = builder.insert(name, start_idx as u64); + .dedup_by(|&(_, (_, lhs, _)), &(_, (_, rhs, _))| lhs.eq_ignore_ascii_case(rhs)); + + let mut insert = |name: &str, start, end| { + builder.insert(name.to_ascii_lowercase(), ((start as u64) << 32) | end as u64).unwrap() + }; + + if let Some((mut last, (_, name, _))) = iter.next() { + debug_assert_eq!(last, 0); + let mut last_name = name; + for (next, (_, next_name, _)) in iter { + insert(last_name, last, next); + last = next; + last_name = next_name; + } + insert(last_name, last, importables.len()); } - Arc::new(ImportMap { - item_to_info_map: map, - fst: builder.into_map(), - importables: importables.into_iter().map(|(item, _, idx)| (item, idx)).collect(), - }) + let importables = importables.into_iter().map(|(item, _, idx)| (item, idx)).collect(); + Arc::new(ImportMap { item_to_info_map: map, fst: builder.into_map(), importables }) } pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { @@ -266,8 +279,8 @@ impl fmt::Debug for ImportMap { } /// A way to match import map contents against the search query. -#[derive(Copy, Clone, Debug)] -enum SearchMode { +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SearchMode { /// Import map entry should strictly match the query string. Exact, /// Import map entry should contain all letters from the query string, @@ -277,6 +290,42 @@ enum SearchMode { Prefix, } +impl SearchMode { + pub fn check(self, query: &str, case_sensitive: bool, candidate: &str) -> bool { + match self { + SearchMode::Exact if case_sensitive => candidate == query, + SearchMode::Exact => candidate.eq_ignore_ascii_case(&query), + SearchMode::Prefix => { + query.len() <= candidate.len() && { + let prefix = &candidate[..query.len() as usize]; + if case_sensitive { + prefix == query + } else { + prefix.eq_ignore_ascii_case(&query) + } + } + } + SearchMode::Fuzzy => { + let mut name = candidate; + query.chars().all(|query_char| { + let m = if case_sensitive { + name.match_indices(query_char).next() + } else { + name.match_indices([query_char, query_char.to_ascii_uppercase()]).next() + }; + match m { + Some((index, _)) => { + name = &name[index + 1..]; + true + } + None => false, + } + }) + } + } + } +} + /// Three possible ways to search for the name in associated and/or other items. #[derive(Debug, Clone, Copy)] pub enum AssocSearchMode { @@ -392,67 +441,28 @@ fn search_maps( query: &Query, ) -> FxHashSet<ItemInNs> { let mut res = FxHashSet::default(); - while let Some((key, indexed_values)) = stream.next() { + while let Some((_, indexed_values)) = stream.next() { for &IndexedValue { index: import_map_idx, value } in indexed_values { - let import_map = &import_maps[import_map_idx]; - let importables = &import_map.importables[value as usize..]; + let end = (value & 0xFFFF_FFFF) as usize; + let start = (value >> 32) as usize; + let ImportMap { item_to_info_map, importables, .. } = &*import_maps[import_map_idx]; + let importables = &importables[start as usize..end]; let iter = importables .iter() .copied() - .map(|(item, info_idx)| { - let (import_infos, assoc_mode) = &import_map.item_to_info_map[&item]; - (item, &import_infos[info_idx as usize], *assoc_mode) + .filter_map(|(item, info_idx)| { + let (import_infos, assoc_mode) = &item_to_info_map[&item]; + query + .matches_assoc_mode(*assoc_mode) + .then(|| (item, &import_infos[info_idx as usize])) }) - // we put all entries with the same lowercased name in a row, so stop once we find a - // different name in the importables - // FIXME: Consider putting a range into the value: u64 as (u32, u32)? - .take_while(|&(_, info, _)| { - info.name.to_smol_str().as_bytes().eq_ignore_ascii_case(&key) - }) - .filter(|&(_, info, assoc_mode)| { - if !query.matches_assoc_mode(assoc_mode) { - return false; - } - if !query.case_sensitive { - return true; - } - let name = info.name.to_smol_str(); - // FIXME: Deduplicate this from ide-db - match query.search_mode { - SearchMode::Exact => !query.case_sensitive || name == query.query, - SearchMode::Prefix => { - query.query.len() <= name.len() && { - let prefix = &name[..query.query.len() as usize]; - if query.case_sensitive { - prefix == query.query - } else { - prefix.eq_ignore_ascii_case(&query.query) - } - } - } - SearchMode::Fuzzy => { - let mut name = &*name; - query.query.chars().all(|query_char| { - let m = if query.case_sensitive { - name.match_indices(query_char).next() - } else { - name.match_indices([ - query_char, - query_char.to_ascii_uppercase(), - ]) - .next() - }; - match m { - Some((index, _)) => { - name = &name[index + 1..]; - true - } - None => false, - } - }) - } - } + .filter(|&(_, info)| { + query.search_mode.check( + &query.query, + query.case_sensitive, + &info.name.to_smol_str(), + ) }); res.extend(iter.map(TupleExt::head)); } |