Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/symbol_index.rs')
-rw-r--r--crates/ide-db/src/symbol_index.rs208
1 files changed, 119 insertions, 89 deletions
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index c5ea9bcf5f..eb0529d6b5 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -27,7 +27,7 @@ use std::{
ops::ControlFlow,
};
-use base_db::{RootQueryDb, SourceDatabase, SourceRootId};
+use base_db::{LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
use fst::{Automaton, Streamer, raw::IndexedValue};
use hir::{
Crate, Module,
@@ -36,8 +36,7 @@ use hir::{
symbols::{FileSymbol, SymbolCollector},
};
use rayon::prelude::*;
-use rustc_hash::FxHashSet;
-use triomphe::Arc;
+use salsa::Update;
use crate::RootDatabase;
@@ -102,63 +101,10 @@ impl Query {
}
}
-#[query_group::query_group]
-pub trait SymbolsDatabase: HirDatabase + SourceDatabase {
- /// The symbol index for a given module. These modules should only be in source roots that
- /// are inside local_roots.
- // FIXME: Is it worth breaking the encapsulation boundary of `hir`, and make this take a `ModuleId`,
- // in order for it to be a non-interned query?
- #[salsa::invoke_interned(module_symbols)]
- fn module_symbols(&self, module: Module) -> Arc<SymbolIndex>;
-
- /// The symbol index for a given source root within library_roots.
- #[salsa::invoke_interned(library_symbols)]
- fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>;
-
- #[salsa::transparent]
- /// The symbol indices of modules that make up a given crate.
- fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>;
-
- /// The set of "local" (that is, from the current workspace) roots.
- /// Files in local roots are assumed to change frequently.
- #[salsa::input]
- fn local_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
-
- /// The set of roots for crates.io libraries.
- /// Files in libraries are assumed to never change.
- #[salsa::input]
- fn library_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
-}
-
-fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc<SymbolIndex> {
- let _p = tracing::info_span!("library_symbols").entered();
-
- // We call this without attaching because this runs in parallel, so we need to attach here.
- hir::attach_db(db, || {
- let mut symbol_collector = SymbolCollector::new(db);
-
- db.source_root_crates(source_root_id)
- .iter()
- .flat_map(|&krate| Crate::from(krate).modules(db))
- // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing,
- // as the index for a library is not going to really ever change, and we do not want to store each
- // the module or crate indices for those in salsa unless we need to.
- .for_each(|module| symbol_collector.collect(module));
-
- Arc::new(SymbolIndex::new(symbol_collector.finish()))
- })
-}
-
-fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
- let _p = tracing::info_span!("module_symbols").entered();
-
- // We call this without attaching because this runs in parallel, so we need to attach here.
- hir::attach_db(db, || Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module))))
-}
-
-pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
+/// The symbol indices of modules that make up a given crate.
+pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_>]> {
let _p = tracing::info_span!("crate_symbols").entered();
- krate.modules(db).into_iter().map(|module| db.module_symbols(module)).collect()
+ krate.modules(db).into_iter().map(|module| SymbolIndex::module_symbols(db, module)).collect()
}
// Feature: Workspace Symbol
@@ -186,25 +132,29 @@ pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolI
// | Editor | Shortcut |
// |---------|-----------|
// | VS Code | <kbd>Ctrl+T</kbd>
-pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
+pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol<'_>> {
let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
let indices: Vec<_> = if query.libs {
- db.library_roots()
+ LibraryRoots::get(db)
+ .roots(db)
.par_iter()
- .map_with(db.clone(), |snap, &root| snap.library_symbols(root))
+ .for_each_with(db.clone(), |snap, &root| _ = SymbolIndex::library_symbols(snap, root));
+ LibraryRoots::get(db)
+ .roots(db)
+ .iter()
+ .map(|&root| SymbolIndex::library_symbols(db, root))
.collect()
} else {
let mut crates = Vec::new();
- for &root in db.local_roots().iter() {
+ for &root in LocalRoots::get(db).roots(db).iter() {
crates.extend(db.source_root_crates(root).iter().copied())
}
- let indices: Vec<_> = crates
- .into_par_iter()
- .map_with(db.clone(), |snap, krate| snap.crate_symbols(krate.into()))
- .collect();
- indices.iter().flat_map(|indices| indices.iter().cloned()).collect()
+ crates
+ .par_iter()
+ .for_each_with(db.clone(), |snap, &krate| _ = crate_symbols(snap, krate.into()));
+ crates.into_iter().flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))).collect()
};
let mut res = vec![];
@@ -216,34 +166,113 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
}
#[derive(Default)]
-pub struct SymbolIndex {
- symbols: Box<[FileSymbol]>,
+pub struct SymbolIndex<'db> {
+ symbols: Box<[FileSymbol<'db>]>,
map: fst::Map<Vec<u8>>,
}
-impl fmt::Debug for SymbolIndex {
+impl<'db> SymbolIndex<'db> {
+ /// The symbol index for a given source root within library_roots.
+ pub fn library_symbols(
+ db: &'db dyn HirDatabase,
+ source_root_id: SourceRootId,
+ ) -> &'db SymbolIndex<'db> {
+ // FIXME:
+ #[salsa::interned]
+ struct InternedSourceRootId {
+ id: SourceRootId,
+ }
+ #[salsa::tracked(returns(ref))]
+ fn library_symbols<'db>(
+ db: &'db dyn HirDatabase,
+ source_root_id: InternedSourceRootId<'db>,
+ ) -> SymbolIndex<'db> {
+ let _p = tracing::info_span!("library_symbols").entered();
+
+ // We call this without attaching because this runs in parallel, so we need to attach here.
+ hir::attach_db(db, || {
+ let mut symbol_collector = SymbolCollector::new(db, true);
+
+ db.source_root_crates(source_root_id.id(db))
+ .iter()
+ .flat_map(|&krate| Crate::from(krate).modules(db))
+ // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing,
+ // as the index for a library is not going to really ever change, and we do not want to store
+ // the module or crate indices for those in salsa unless we need to.
+ .for_each(|module| symbol_collector.collect(module));
+
+ SymbolIndex::new(symbol_collector.finish())
+ })
+ }
+ library_symbols(db, InternedSourceRootId::new(db, source_root_id))
+ }
+
+ /// The symbol index for a given module. These modules should only be in source roots that
+ /// are inside local_roots.
+ pub fn module_symbols(db: &dyn HirDatabase, module: Module) -> &SymbolIndex<'_> {
+ // FIXME:
+ #[salsa::interned]
+ struct InternedModuleId {
+ id: hir::ModuleId,
+ }
+
+ #[salsa::tracked(returns(ref))]
+ fn module_symbols<'db>(
+ db: &'db dyn HirDatabase,
+ module: InternedModuleId<'db>,
+ ) -> SymbolIndex<'db> {
+ let _p = tracing::info_span!("module_symbols").entered();
+
+ // We call this without attaching because this runs in parallel, so we need to attach here.
+ hir::attach_db(db, || {
+ let module: Module = module.id(db).into();
+ SymbolIndex::new(SymbolCollector::new_module(
+ db,
+ module,
+ !module.krate(db).origin(db).is_local(),
+ ))
+ })
+ }
+
+ module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module)))
+ }
+}
+
+impl fmt::Debug for SymbolIndex<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
}
}
-impl PartialEq for SymbolIndex {
- fn eq(&self, other: &SymbolIndex) -> bool {
+impl PartialEq for SymbolIndex<'_> {
+ fn eq(&self, other: &SymbolIndex<'_>) -> bool {
self.symbols == other.symbols
}
}
-impl Eq for SymbolIndex {}
+impl Eq for SymbolIndex<'_> {}
-impl Hash for SymbolIndex {
+impl Hash for SymbolIndex<'_> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.symbols.hash(hasher)
}
}
-impl SymbolIndex {
- fn new(mut symbols: Box<[FileSymbol]>) -> SymbolIndex {
- fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
+unsafe impl Update for SymbolIndex<'_> {
+ unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
+ let this = unsafe { &mut *old_pointer };
+ if *this == new_value {
+ false
+ } else {
+ *this = new_value;
+ true
+ }
+ }
+}
+
+impl<'db> SymbolIndex<'db> {
+ fn new(mut symbols: Box<[FileSymbol<'db>]>) -> SymbolIndex<'db> {
+ fn cmp(lhs: &FileSymbol<'_>, rhs: &FileSymbol<'_>) -> Ordering {
let lhs_chars = lhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
let rhs_chars = rhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
lhs_chars.cmp(rhs_chars)
@@ -289,7 +318,7 @@ impl SymbolIndex {
}
pub fn memory_size(&self) -> usize {
- self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol>()
+ self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol<'_>>()
}
fn range_to_map_value(start: usize, end: usize) -> u64 {
@@ -307,10 +336,10 @@ impl SymbolIndex {
}
impl Query {
- pub(crate) fn search<'sym, T>(
+ pub(crate) fn search<'db, T>(
self,
- indices: &'sym [Arc<SymbolIndex>],
- cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ indices: &[&'db SymbolIndex<'db>],
+ cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow<T>,
) -> Option<T> {
let _p = tracing::info_span!("symbol_index::Query::search").entered();
let mut op = fst::map::OpBuilder::new();
@@ -342,11 +371,11 @@ impl Query {
}
}
- fn search_maps<'sym, T>(
+ fn search_maps<'db, T>(
&self,
- indices: &'sym [Arc<SymbolIndex>],
+ indices: &[&'db SymbolIndex<'db>],
mut stream: fst::map::Union<'_>,
- mut cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ mut cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow<T>,
) -> Option<T> {
let ignore_underscore_prefixed = !self.query.starts_with("__");
while let Some((_, indexed_values)) = stream.next() {
@@ -397,7 +426,8 @@ impl Query {
mod tests {
use expect_test::expect_file;
- use salsa::Durability;
+ use rustc_hash::FxHashSet;
+ use salsa::Setter;
use test_fixture::{WORKSPACE, WithFixture};
use super::*;
@@ -484,7 +514,7 @@ pub(self) use crate::Trait as IsThisJustATrait;
.modules(&db)
.into_iter()
.map(|module_id| {
- let mut symbols = SymbolCollector::new_module(&db, module_id);
+ let mut symbols = SymbolCollector::new_module(&db, module_id, false);
symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})
@@ -511,7 +541,7 @@ struct Duplicate;
.modules(&db)
.into_iter()
.map(|module_id| {
- let mut symbols = SymbolCollector::new_module(&db, module_id);
+ let mut symbols = SymbolCollector::new_module(&db, module_id, false);
symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})
@@ -535,7 +565,7 @@ pub struct Foo;
let mut local_roots = FxHashSet::default();
local_roots.insert(WORKSPACE);
- db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
+ LocalRoots::get(&db).set_roots(&mut db).to(local_roots);
let mut query = Query::new("Foo".to_owned());
let mut symbols = world_symbols(&db, query.clone());