From a15cc86c640a3db70138b8cd067f9a36d6d39d41 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Mon, 26 Feb 2024 14:35:54 +0100 Subject: [PATCH 1/2] Implement parent-child relation for `SourceRoot`s This commit adds the said relation by keeping a map of type `FxHashMap>` 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. --- crates/load-cargo/src/lib.rs | 237 ++++++++++++++++++++++- crates/rust-analyzer/src/global_state.rs | 5 +- crates/rust-analyzer/src/reload.rs | 1 + crates/vfs/src/file_set.rs | 11 ++ 4 files changed, 249 insertions(+), 5 deletions(-) diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 29c1251cce..7b651257cd 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -10,7 +10,7 @@ use hir_expand::proc_macro::{ ProcMacros, }; use ide_db::{ - base_db::{CrateGraph, Env, SourceRoot}, + base_db::{CrateGraph, Env, SourceRoot, SourceRootId}, prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase, }; use itertools::Itertools; @@ -231,7 +231,7 @@ impl ProjectFolders { res.load.push(entry); if root.is_local { - local_filesets.push(fsc.len()); + local_filesets.push(fsc.len() as u64); } fsc.add_file_set(file_set_roots) } @@ -246,7 +246,7 @@ impl ProjectFolders { #[derive(Default, Debug)] pub struct SourceRootConfig { pub fsc: FileSetConfig, - pub local_filesets: Vec, + pub local_filesets: Vec, } impl SourceRootConfig { @@ -256,7 +256,7 @@ impl SourceRootConfig { .into_iter() .enumerate() .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 { SourceRoot::new_local(file_set) } else { @@ -265,6 +265,36 @@ impl SourceRootConfig { }) .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> { + let roots = self.fsc.roots(); + let mut map = FxHashMap::>::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` @@ -413,4 +443,203 @@ mod tests { // RA has quite a few crates, but the exact count doesn't matter 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::>(); + + 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::>(); + + 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::>(); + + 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::>(); + + 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::>(); + + 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::>(); + + 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::>(); + 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::>(); + 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::>(); + 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))), + ] + ) + } + } } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 560410e332..3d14b9198f 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{collections::hash_map::Entry, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use hir::ChangeWithProcMacros; -use ide::{Analysis, AnalysisHost, Cancellable, FileId}; +use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; use ide_db::base_db::{CrateId, ProcMacroPaths}; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; @@ -66,6 +66,8 @@ pub(crate) struct GlobalState { pub(crate) diagnostics: DiagnosticCollection, pub(crate) mem_docs: MemDocs, 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>, pub(crate) semantic_tokens_cache: Arc>>, // status @@ -204,6 +206,7 @@ impl GlobalState { send_hint_refresh_query: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), + local_roots_parent_map: FxHashMap::default(), config_errors: Default::default(), proc_macro_clients: Arc::from_iter([]), diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 95d51baab3..c2725e1fad 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -515,6 +515,7 @@ impl GlobalState { version: self.vfs_config_version, }); 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); diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 0392ef3ceb..794f15577e 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs @@ -123,6 +123,17 @@ impl FileSetConfig { self.n_file_sets } + /// Get the lexicographically ordered vector of the underlying map. + pub fn roots(&self) -> Vec<(Vec, 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`. /// /// `scratch_space` is used as a buffer and will be entirely replaced. From 9c50d129dafe19ecf294fe0862a68e7c39687288 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Mon, 4 Mar 2024 22:58:49 +0100 Subject: [PATCH 2/2] Opt for FxHashMap instead of > and apply requested changes --- crates/load-cargo/src/lib.rs | 329 +++++++++-------------- crates/rust-analyzer/src/global_state.rs | 2 +- crates/vfs/src/file_set.rs | 8 +- 3 files changed, 127 insertions(+), 212 deletions(-) diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 7b651257cd..a1c089520d 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -267,32 +267,27 @@ impl SourceRootConfig { } /// 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> { + /// If a `SourceRoot` doesn't have a parent and is local then it is not contained in this mapping but it can be asserted that it is a root `SourceRoot`. + pub fn source_root_parent_map(&self) -> FxHashMap { let roots = self.fsc.roots(); - let mut map = FxHashMap::>::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; + let mut map = FxHashMap::::default(); + roots + .iter() + .enumerate() + .filter(|(_, (_, id))| self.local_filesets.contains(id)) + .filter_map(|(idx, (root, root_id))| { + // 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. + roots.iter().take(idx).find_map(|(root2, root2_id)| { + if self.local_filesets.contains(root2_id) && root.starts_with(root2) { + return Some((root_id, root2_id)); } - } - } - map.insert(SourceRootId(idx as u32), None); - } - + None + }) + }) + .for_each(|(child, parent)| { + map.insert(SourceRootId(*child as u32), SourceRootId(*parent as u32)); + }); map } } @@ -427,6 +422,11 @@ mod tests { use super::*; + use ide_db::base_db::SourceRootId; + use vfs::{file_set::FileSetConfigBuilder, VfsPath}; + + use crate::SourceRootConfig; + #[test] fn test_loading_rust_analyzer() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); @@ -444,202 +444,123 @@ mod tests { assert!(n_crates > 20); } - mod source_root_parent { - use ide_db::base_db::SourceRootId; - use vfs::{file_set::FileSetConfigBuilder, VfsPath}; + #[test] + fn unrelated_sources() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - use crate::SourceRootConfig; + assert_eq!(vc, vec![]) + } - macro_rules! virp { - ($s : literal) => { - VfsPath::new_virtual_path(format!($s)) - }; - } + #[test] + fn unrelated_source_sharing_dirname() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/abc".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - #[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::>(); + assert_eq!(vc, vec![]) + } - assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), None)]) - } + #[test] + fn basic_child_parent() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc/def".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - #[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::>(); + assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0))]) + } - assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), None)]) - } + #[test] + fn basic_child_parent_with_unrelated_parents_sib() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/abc".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - #[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::>(); + assert_eq!(vc, vec![(SourceRootId(2), SourceRootId(1))]) + } - assert_eq!(vc, vec![(SourceRootId(0), None), (SourceRootId(1), Some(SourceRootId(0)))]) - } + #[test] + fn deep_sources_with_parent_missing() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/ghi".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/abc".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - #[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::>(); + assert_eq!(vc, vec![]) + } - assert_eq!( - vc, - vec![ - (SourceRootId(0), None), - (SourceRootId(1), None), - (SourceRootId(2), Some(SourceRootId(1))) - ] - ) - } + #[test] + fn ancestor_can_be_parent() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/ghi/jkl".to_owned())]); + let fsc = builder.build(); + let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2] }; + let vc = src.source_root_parent_map().into_iter().collect::>(); - #[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::>(); + assert_eq!(vc, vec![(SourceRootId(2), SourceRootId(1))]) + } - assert_eq!( - vc, - vec![(SourceRootId(0), None), (SourceRootId(1), None), (SourceRootId(2), None)] - ) - } + #[test] + fn ancestor_can_be_parent_2() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/ghi/jkl".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/ghi/klm".to_owned())]); + 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::>(); + vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0)); - #[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::>(); + assert_eq!(vc, vec![(SourceRootId(2), SourceRootId(1)), (SourceRootId(3), SourceRootId(1))]) + } - assert_eq!( - vc, - vec![ - (SourceRootId(0), None), - (SourceRootId(1), None), - (SourceRootId(2), Some(SourceRootId(1))) - ] - ) - } + #[test] + fn non_locals_are_skipped() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/ghi/jkl".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/klm".to_owned())]); + 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::>(); + vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0)); - #[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::>(); - vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0)); + assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),]) + } - assert_eq!( - vc, - vec![ - (SourceRootId(0), None), - (SourceRootId(1), None), - (SourceRootId(2), Some(SourceRootId(1))), - (SourceRootId(3), Some(SourceRootId(1))) - ] - ) - } + #[test] + fn child_binds_ancestor_if_parent_nonlocal() { + let mut builder = FileSetConfigBuilder::default(); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/abc".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/klm".to_owned())]); + builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/klm/jkl".to_owned())]); + 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::>(); + vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0)); - #[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::>(); - 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::>(); - 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))), - ] - ) - } + assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),]) } } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 3d14b9198f..0e560e54ed 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -67,7 +67,7 @@ pub(crate) struct GlobalState { pub(crate) mem_docs: MemDocs, 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>, + pub(crate) local_roots_parent_map: FxHashMap, pub(crate) semantic_tokens_cache: Arc>>, // status diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 794f15577e..7eeb10d544 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs @@ -125,13 +125,7 @@ impl FileSetConfig { /// Get the lexicographically ordered vector of the underlying map. pub fn roots(&self) -> Vec<(Vec, 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 + self.map.stream().into_byte_vec() } /// Returns the set index for the given `path`.