Auto merge of #16267 - Veykril:import-map-fix, r=Veykril

internal: Deduplicate some code
This commit is contained in:
bors 2024-01-05 10:43:52 +00:00
commit 11b0126679
3 changed files with 85 additions and 120 deletions

View File

@ -83,29 +83,42 @@ impl ImportMap {
.iter() .iter()
// We've only collected items, whose name cannot be tuple field so unwrapping is fine. // We've only collected items, whose name cannot be tuple field so unwrapping is fine.
.flat_map(|(&item, (info, _))| { .flat_map(|(&item, (info, _))| {
info.iter().enumerate().map(move |(idx, info)| { info.iter()
(item, info.name.as_str().unwrap().to_ascii_lowercase(), idx as u32) .enumerate()
}) .map(move |(idx, info)| (item, info.name.to_smol_str(), idx as u32))
}) })
.collect(); .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(); importables.dedup();
// Build the FST, taking care not to insert duplicate values. // Build the FST, taking care not to insert duplicate values.
let mut builder = fst::MapBuilder::memory(); let mut builder = fst::MapBuilder::memory();
let iter = importables let mut iter = importables
.iter() .iter()
.enumerate() .enumerate()
.dedup_by(|(_, (_, lhs, _)), (_, (_, rhs, _))| lhs == rhs); .dedup_by(|&(_, (_, lhs, _)), &(_, (_, rhs, _))| lhs.eq_ignore_ascii_case(rhs));
for (start_idx, (_, name, _)) in iter {
let _ = builder.insert(name, start_idx as u64); 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 { let importables = importables.into_iter().map(|(item, _, idx)| (item, idx)).collect();
item_to_info_map: map, Arc::new(ImportMap { item_to_info_map: map, fst: builder.into_map(), importables })
fst: builder.into_map(),
importables: importables.into_iter().map(|(item, _, idx)| (item, idx)).collect(),
})
} }
pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { 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. /// A way to match import map contents against the search query.
#[derive(Copy, Clone, Debug)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum SearchMode { pub enum SearchMode {
/// Import map entry should strictly match the query string. /// Import map entry should strictly match the query string.
Exact, Exact,
/// Import map entry should contain all letters from the query string, /// Import map entry should contain all letters from the query string,
@ -277,6 +290,42 @@ enum SearchMode {
Prefix, 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. /// Three possible ways to search for the name in associated and/or other items.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum AssocSearchMode { pub enum AssocSearchMode {
@ -392,67 +441,28 @@ fn search_maps(
query: &Query, query: &Query,
) -> FxHashSet<ItemInNs> { ) -> FxHashSet<ItemInNs> {
let mut res = FxHashSet::default(); 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 { for &IndexedValue { index: import_map_idx, value } in indexed_values {
let import_map = &import_maps[import_map_idx]; let end = (value & 0xFFFF_FFFF) as usize;
let importables = &import_map.importables[value 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 let iter = importables
.iter() .iter()
.copied() .copied()
.map(|(item, info_idx)| { .filter_map(|(item, info_idx)| {
let (import_infos, assoc_mode) = &import_map.item_to_info_map[&item]; let (import_infos, assoc_mode) = &item_to_info_map[&item];
(item, &import_infos[info_idx as usize], *assoc_mode) 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 .filter(|&(_, info)| {
// different name in the importables query.search_mode.check(
// FIXME: Consider putting a range into the value: u64 as (u32, u32)? &query.query,
.take_while(|&(_, info, _)| { query.case_sensitive,
info.name.to_smol_str().as_bytes().eq_ignore_ascii_case(&key) &info.name.to_smol_str(),
}) )
.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,
}
})
}
}
}); });
res.extend(iter.map(TupleExt::head)); res.extend(iter.map(TupleExt::head));
} }

View File

@ -18,11 +18,11 @@ use crate::{Module, ModuleDef, Semantics};
/// possible. /// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol { pub struct FileSymbol {
// even though name can be derived from the def, we store it for efficiency
pub name: SmolStr, pub name: SmolStr,
pub def: ModuleDef, pub def: ModuleDef,
pub loc: DeclarationLocation, pub loc: DeclarationLocation,
pub container_name: Option<SmolStr>, pub container_name: Option<SmolStr>,
/// Whether this symbol is a doc alias for the original symbol.
pub is_alias: bool, pub is_alias: bool,
pub is_assoc: bool, pub is_assoc: bool,
} }
@ -166,8 +166,6 @@ impl<'a> SymbolCollector<'a> {
// FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily
// for now. // for now.
for id in scope.imports() { for id in scope.imports() {
let loc = id.import.lookup(self.db.upcast());
loc.id.item_tree(self.db.upcast());
let source = id.import.child_source(self.db.upcast()); let source = id.import.child_source(self.db.upcast());
let Some(use_tree_src) = source.value.get(id.idx) else { continue }; let Some(use_tree_src) = source.value.get(id.idx) else { continue };
let Some(rename) = use_tree_src.rename() else { continue }; let Some(rename) = use_tree_src.rename() else { continue };

View File

@ -34,7 +34,7 @@ use base_db::{
use fst::{self, raw::IndexedValue, Automaton, Streamer}; use fst::{self, raw::IndexedValue, Automaton, Streamer};
use hir::{ use hir::{
db::HirDatabase, db::HirDatabase,
import_map::AssocSearchMode, import_map::{AssocSearchMode, SearchMode},
symbols::{FileSymbol, SymbolCollector}, symbols::{FileSymbol, SymbolCollector},
Crate, Module, Crate, Module,
}; };
@ -44,22 +44,15 @@ use triomphe::Arc;
use crate::RootDatabase; use crate::RootDatabase;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum SearchMode {
Fuzzy,
Exact,
Prefix,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Query { pub struct Query {
query: String, query: String,
lowercased: String, lowercased: String,
only_types: bool,
libs: bool,
mode: SearchMode, mode: SearchMode,
assoc_mode: AssocSearchMode, assoc_mode: AssocSearchMode,
case_sensitive: bool, case_sensitive: bool,
only_types: bool,
libs: bool,
} }
impl Query { impl Query {
@ -381,43 +374,7 @@ impl Query {
if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) { if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) {
continue; continue;
} }
// FIXME: Deduplicate this from hir-def if self.mode.check(&self.query, self.case_sensitive, &symbol.name) {
let matches = match self.mode {
SearchMode::Exact if self.case_sensitive => symbol.name == self.query,
SearchMode::Exact => symbol.name.eq_ignore_ascii_case(&self.query),
SearchMode::Prefix => {
self.query.len() <= symbol.name.len() && {
let prefix = &symbol.name[..self.query.len() as usize];
if self.case_sensitive {
prefix == self.query
} else {
prefix.eq_ignore_ascii_case(&self.query)
}
}
}
SearchMode::Fuzzy => {
let mut name = &*symbol.name;
self.query.chars().all(|query_char| {
let m = if self.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,
}
})
}
};
if matches {
cb(symbol); cb(symbol);
} }
} }