mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Implement parent-child relation for SourceRoot
s
This commit adds the said relation by keeping a map of type `FxHashMap<SourceRootId,Option<SourceRootId>>` inside the `GlobalState`. Its primary use case is reading the rust-analyzer.toml files that can be placed under every local source root. As a config will be found by traversing this "tree" we need the parent information for every local source root. This commit omits defining this relation for library source roots entirely.
This commit is contained in:
parent
ce15e73a8e
commit
a15cc86c64
4 changed files with 249 additions and 5 deletions
|
@ -10,7 +10,7 @@ use hir_expand::proc_macro::{
|
||||||
ProcMacros,
|
ProcMacros,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{CrateGraph, Env, SourceRoot},
|
base_db::{CrateGraph, Env, SourceRoot, SourceRootId},
|
||||||
prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase,
|
prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -231,7 +231,7 @@ impl ProjectFolders {
|
||||||
res.load.push(entry);
|
res.load.push(entry);
|
||||||
|
|
||||||
if root.is_local {
|
if root.is_local {
|
||||||
local_filesets.push(fsc.len());
|
local_filesets.push(fsc.len() as u64);
|
||||||
}
|
}
|
||||||
fsc.add_file_set(file_set_roots)
|
fsc.add_file_set(file_set_roots)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ impl ProjectFolders {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct SourceRootConfig {
|
pub struct SourceRootConfig {
|
||||||
pub fsc: FileSetConfig,
|
pub fsc: FileSetConfig,
|
||||||
pub local_filesets: Vec<usize>,
|
pub local_filesets: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceRootConfig {
|
impl SourceRootConfig {
|
||||||
|
@ -256,7 +256,7 @@ impl SourceRootConfig {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, file_set)| {
|
.map(|(idx, file_set)| {
|
||||||
let is_local = self.local_filesets.contains(&idx);
|
let is_local = self.local_filesets.contains(&(idx as u64));
|
||||||
if is_local {
|
if is_local {
|
||||||
SourceRoot::new_local(file_set)
|
SourceRoot::new_local(file_set)
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,6 +265,36 @@ impl SourceRootConfig {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps local source roots to their parent source roots by bytewise comparing of root paths .
|
||||||
|
/// If a source root doesn't have a parent then its parent is declared as None.
|
||||||
|
pub fn source_root_parent_map(&self) -> FxHashMap<SourceRootId, Option<SourceRootId>> {
|
||||||
|
let roots = self.fsc.roots();
|
||||||
|
let mut map = FxHashMap::<SourceRootId, Option<SourceRootId>>::default();
|
||||||
|
|
||||||
|
'outer: for (idx, (root, root_id)) in roots.iter().enumerate() {
|
||||||
|
if !self.local_filesets.contains(root_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, (root2, root2_id)) in roots.iter().enumerate().take(idx).rev() {
|
||||||
|
if root2.iter().enumerate().all(|(i, c)| &root[i] == c) {
|
||||||
|
// We are interested in parents if they are also local source roots.
|
||||||
|
// So instead of a non-local parent we may take a local ancestor as a parent to a node.
|
||||||
|
if self.local_filesets.contains(root2_id) {
|
||||||
|
map.insert(
|
||||||
|
SourceRootId(root_id.to_owned() as u32),
|
||||||
|
Some(SourceRootId(root2_id.to_owned() as u32)),
|
||||||
|
);
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.insert(SourceRootId(idx as u32), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
|
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
|
||||||
|
@ -413,4 +443,203 @@ mod tests {
|
||||||
// RA has quite a few crates, but the exact count doesn't matter
|
// RA has quite a few crates, but the exact count doesn't matter
|
||||||
assert!(n_crates > 20);
|
assert!(n_crates > 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod source_root_parent {
|
||||||
|
use ide_db::base_db::SourceRootId;
|
||||||
|
use vfs::{file_set::FileSetConfigBuilder, VfsPath};
|
||||||
|
|
||||||
|
use crate::SourceRootConfig;
|
||||||
|
|
||||||
|
macro_rules! virp {
|
||||||
|
($s : literal) => {
|
||||||
|
VfsPath::new_virtual_path(format!($s))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test1() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), None)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test2() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def/abc")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), None)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test3() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/abc/def")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), Some(SourceRootId(0)))])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test4() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/abc")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![
|
||||||
|
(SourceRootId(0), None),
|
||||||
|
(SourceRootId(1), None),
|
||||||
|
(SourceRootId(2), Some(SourceRootId(1)))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test5() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/ghi")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/abc")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![(SourceRootId(0), None), (SourceRootId(1), None), (SourceRootId(2), None)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test6() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/ghi/jkl")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] };
|
||||||
|
let vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![
|
||||||
|
(SourceRootId(0), None),
|
||||||
|
(SourceRootId(1), None),
|
||||||
|
(SourceRootId(2), Some(SourceRootId(1)))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test7() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/ghi/jkl")];
|
||||||
|
let root4 = vec![virp!("/ROOT/def/ghi/klm")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
builder.add_file_set(root4);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2, 3] };
|
||||||
|
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![
|
||||||
|
(SourceRootId(0), None),
|
||||||
|
(SourceRootId(1), None),
|
||||||
|
(SourceRootId(2), Some(SourceRootId(1))),
|
||||||
|
(SourceRootId(3), Some(SourceRootId(1)))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test8() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/ghi/jkl")];
|
||||||
|
let root4 = vec![virp!("/ROOT/def/klm")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
builder.add_file_set(root4);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] };
|
||||||
|
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![
|
||||||
|
(SourceRootId(0), None),
|
||||||
|
(SourceRootId(1), None),
|
||||||
|
(SourceRootId(3), Some(SourceRootId(1))),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test9() {
|
||||||
|
let mut builder = FileSetConfigBuilder::default();
|
||||||
|
let root = vec![virp!("/ROOT/abc")];
|
||||||
|
let root2 = vec![virp!("/ROOT/def")];
|
||||||
|
let root3 = vec![virp!("/ROOT/def/klm")];
|
||||||
|
let root4 = vec![virp!("/ROOT/def/klm/jkl")];
|
||||||
|
builder.add_file_set(root);
|
||||||
|
builder.add_file_set(root2);
|
||||||
|
builder.add_file_set(root3);
|
||||||
|
builder.add_file_set(root4);
|
||||||
|
let fsc = builder.build();
|
||||||
|
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] };
|
||||||
|
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
|
||||||
|
vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vc,
|
||||||
|
vec![
|
||||||
|
(SourceRootId(0), None),
|
||||||
|
(SourceRootId(1), None),
|
||||||
|
(SourceRootId(3), Some(SourceRootId(1))),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{collections::hash_map::Entry, time::Instant};
|
||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use flycheck::FlycheckHandle;
|
use flycheck::FlycheckHandle;
|
||||||
use hir::ChangeWithProcMacros;
|
use hir::ChangeWithProcMacros;
|
||||||
use ide::{Analysis, AnalysisHost, Cancellable, FileId};
|
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
|
||||||
use ide_db::base_db::{CrateId, ProcMacroPaths};
|
use ide_db::base_db::{CrateId, ProcMacroPaths};
|
||||||
use load_cargo::SourceRootConfig;
|
use load_cargo::SourceRootConfig;
|
||||||
use lsp_types::{SemanticTokens, Url};
|
use lsp_types::{SemanticTokens, Url};
|
||||||
|
@ -66,6 +66,8 @@ pub(crate) struct GlobalState {
|
||||||
pub(crate) diagnostics: DiagnosticCollection,
|
pub(crate) diagnostics: DiagnosticCollection,
|
||||||
pub(crate) mem_docs: MemDocs,
|
pub(crate) mem_docs: MemDocs,
|
||||||
pub(crate) source_root_config: SourceRootConfig,
|
pub(crate) source_root_config: SourceRootConfig,
|
||||||
|
/// A mapping that maps a local source root's `SourceRootId` to it parent's `SourceRootId`, if it has one.
|
||||||
|
pub(crate) local_roots_parent_map: FxHashMap<SourceRootId, Option<SourceRootId>>,
|
||||||
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
|
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
|
||||||
|
|
||||||
// status
|
// status
|
||||||
|
@ -204,6 +206,7 @@ impl GlobalState {
|
||||||
send_hint_refresh_query: false,
|
send_hint_refresh_query: false,
|
||||||
last_reported_status: None,
|
last_reported_status: None,
|
||||||
source_root_config: SourceRootConfig::default(),
|
source_root_config: SourceRootConfig::default(),
|
||||||
|
local_roots_parent_map: FxHashMap::default(),
|
||||||
config_errors: Default::default(),
|
config_errors: Default::default(),
|
||||||
|
|
||||||
proc_macro_clients: Arc::from_iter([]),
|
proc_macro_clients: Arc::from_iter([]),
|
||||||
|
|
|
@ -515,6 +515,7 @@ impl GlobalState {
|
||||||
version: self.vfs_config_version,
|
version: self.vfs_config_version,
|
||||||
});
|
});
|
||||||
self.source_root_config = project_folders.source_root_config;
|
self.source_root_config = project_folders.source_root_config;
|
||||||
|
self.local_roots_parent_map = self.source_root_config.source_root_parent_map();
|
||||||
|
|
||||||
self.recreate_crate_graph(cause);
|
self.recreate_crate_graph(cause);
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,17 @@ impl FileSetConfig {
|
||||||
self.n_file_sets
|
self.n_file_sets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the lexicographically ordered vector of the underlying map.
|
||||||
|
pub fn roots(&self) -> Vec<(Vec<u8>, u64)> {
|
||||||
|
let mut stream = self.map.stream();
|
||||||
|
let mut vc = vec![];
|
||||||
|
while let Some((pth, idx)) = stream.next() {
|
||||||
|
vc.push((pth.to_vec(), idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
vc
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the set index for the given `path`.
|
/// Returns the set index for the given `path`.
|
||||||
///
|
///
|
||||||
/// `scratch_space` is used as a buffer and will be entirely replaced.
|
/// `scratch_space` is used as a buffer and will be entirely replaced.
|
||||||
|
|
Loading…
Reference in a new issue