about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-01-04 22:53:41 +0100
committerLukas Wirth <lukastw97@gmail.com>2024-01-05 11:34:18 +0100
commitd60638e5e6d768e7fb5ccda5d64d713c2253f1b7 (patch)
tree6efc0782f6d2af737899cdcf11d52f1b4c081d86
parent2666349392bf98c836faa1c457e0bf3c5d90417c (diff)
downloadrust-d60638e5e6d768e7fb5ccda5d64d713c2253f1b7.tar.gz
rust-d60638e5e6d768e7fb5ccda5d64d713c2253f1b7.zip
Deduplicate some code
-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 9a40d30637a..989bbc7bfb2 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 c1e171dacd3..841ddfb9c43 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 002b8e7b320..c2e95ca860c 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);
                     }
                 }