mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Auto merge of #16267 - Veykril:import-map-fix, r=Veykril
internal: Deduplicate some code
This commit is contained in:
commit
11b0126679
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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 };
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user