Unnamed repository; edit this file 'description' to name the repository.
Deduplicate some code
Lukas Wirth 2024-01-05
parent 2666349 · commit d60638e
-rw-r--r--crates/hir-def/src/import_map.rs150
-rw-r--r--crates/hir/src/symbols.rs4
-rw-r--r--crates/ide-db/src/symbol_index.rs51
3 files changed, 85 insertions, 120 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));
}
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index c1e171dacd..841ddfb9c4 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -18,11 +18,11 @@ use crate::{Module, ModuleDef, Semantics};
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
- // even though name can be derived from the def, we store it for efficiency
pub name: SmolStr,
pub def: ModuleDef,
pub loc: DeclarationLocation,
pub container_name: Option<SmolStr>,
+ /// Whether this symbol is a doc alias for the original symbol.
pub is_alias: 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
// for now.
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 Some(use_tree_src) = source.value.get(id.idx) else { continue };
let Some(rename) = use_tree_src.rename() else { continue };
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index 002b8e7b32..c2e95ca860 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -34,7 +34,7 @@ use base_db::{
use fst::{self, raw::IndexedValue, Automaton, Streamer};
use hir::{
db::HirDatabase,
- import_map::AssocSearchMode,
+ import_map::{AssocSearchMode, SearchMode},
symbols::{FileSymbol, SymbolCollector},
Crate, Module,
};
@@ -44,22 +44,15 @@ use triomphe::Arc;
use crate::RootDatabase;
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-enum SearchMode {
- Fuzzy,
- Exact,
- Prefix,
-}
-
#[derive(Debug, Clone)]
pub struct Query {
query: String,
lowercased: String,
- only_types: bool,
- libs: bool,
mode: SearchMode,
assoc_mode: AssocSearchMode,
case_sensitive: bool,
+ only_types: bool,
+ libs: bool,
}
impl Query {
@@ -381,43 +374,7 @@ impl Query {
if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) {
continue;
}
- // FIXME: Deduplicate this from hir-def
- 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 {
+ if self.mode.check(&self.query, self.case_sensitive, &symbol.name) {
cb(symbol);
}
}