@@ -10,7 +10,7 @@ use hir_expand::proc_macro::{
1010 ProcMacros ,
1111} ;
1212use ide_db:: {
13- base_db:: { CrateGraph , Env , SourceRoot } ,
13+ base_db:: { CrateGraph , Env , SourceRoot , SourceRootId } ,
1414 prime_caches, ChangeWithProcMacros , FxHashMap , RootDatabase ,
1515} ;
1616use itertools:: Itertools ;
@@ -231,7 +231,7 @@ impl ProjectFolders {
231231 res. load . push ( entry) ;
232232
233233 if root. is_local {
234- local_filesets. push ( fsc. len ( ) ) ;
234+ local_filesets. push ( fsc. len ( ) as u64 ) ;
235235 }
236236 fsc. add_file_set ( file_set_roots)
237237 }
@@ -246,7 +246,7 @@ impl ProjectFolders {
246246#[ derive( Default , Debug ) ]
247247pub struct SourceRootConfig {
248248 pub fsc : FileSetConfig ,
249- pub local_filesets : Vec < usize > ,
249+ pub local_filesets : Vec < u64 > ,
250250}
251251
252252impl SourceRootConfig {
@@ -256,7 +256,7 @@ impl SourceRootConfig {
256256 . into_iter ( )
257257 . enumerate ( )
258258 . map ( |( idx, file_set) | {
259- let is_local = self . local_filesets . contains ( & idx) ;
259+ let is_local = self . local_filesets . contains ( & ( idx as u64 ) ) ;
260260 if is_local {
261261 SourceRoot :: new_local ( file_set)
262262 } else {
@@ -265,6 +265,31 @@ impl SourceRootConfig {
265265 } )
266266 . collect ( )
267267 }
268+
269+ /// Maps local source roots to their parent source roots by bytewise comparing of root paths .
270+ /// 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`.
271+ pub fn source_root_parent_map ( & self ) -> FxHashMap < SourceRootId , SourceRootId > {
272+ let roots = self . fsc . roots ( ) ;
273+ let mut map = FxHashMap :: < SourceRootId , SourceRootId > :: default ( ) ;
274+ roots
275+ . iter ( )
276+ . enumerate ( )
277+ . filter ( |( _, ( _, id) ) | self . local_filesets . contains ( id) )
278+ . filter_map ( |( idx, ( root, root_id) ) | {
279+ // We are interested in parents if they are also local source roots.
280+ // So instead of a non-local parent we may take a local ancestor as a parent to a node.
281+ roots. iter ( ) . take ( idx) . find_map ( |( root2, root2_id) | {
282+ if self . local_filesets . contains ( root2_id) && root. starts_with ( root2) {
283+ return Some ( ( root_id, root2_id) ) ;
284+ }
285+ None
286+ } )
287+ } )
288+ . for_each ( |( child, parent) | {
289+ map. insert ( SourceRootId ( * child as u32 ) , SourceRootId ( * parent as u32 ) ) ;
290+ } ) ;
291+ map
292+ }
268293}
269294
270295/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
@@ -397,6 +422,11 @@ mod tests {
397422
398423 use super :: * ;
399424
425+ use ide_db:: base_db:: SourceRootId ;
426+ use vfs:: { file_set:: FileSetConfigBuilder , VfsPath } ;
427+
428+ use crate :: SourceRootConfig ;
429+
400430 #[ test]
401431 fn test_loading_rust_analyzer ( ) {
402432 let path = Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . parent ( ) . unwrap ( ) . parent ( ) . unwrap ( ) ;
@@ -413,4 +443,124 @@ mod tests {
413443 // RA has quite a few crates, but the exact count doesn't matter
414444 assert ! ( n_crates > 20 ) ;
415445 }
446+
447+ #[ test]
448+ fn unrelated_sources ( ) {
449+ let mut builder = FileSetConfigBuilder :: default ( ) ;
450+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
451+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
452+ let fsc = builder. build ( ) ;
453+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
454+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
455+
456+ assert_eq ! ( vc, vec![ ] )
457+ }
458+
459+ #[ test]
460+ fn unrelated_source_sharing_dirname ( ) {
461+ let mut builder = FileSetConfigBuilder :: default ( ) ;
462+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
463+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/abc" . to_owned( ) ) ] ) ;
464+ let fsc = builder. build ( ) ;
465+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
466+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
467+
468+ assert_eq ! ( vc, vec![ ] )
469+ }
470+
471+ #[ test]
472+ fn basic_child_parent ( ) {
473+ let mut builder = FileSetConfigBuilder :: default ( ) ;
474+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
475+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc/def" . to_owned( ) ) ] ) ;
476+ let fsc = builder. build ( ) ;
477+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
478+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
479+
480+ assert_eq ! ( vc, vec![ ( SourceRootId ( 1 ) , SourceRootId ( 0 ) ) ] )
481+ }
482+
483+ #[ test]
484+ fn basic_child_parent_with_unrelated_parents_sib ( ) {
485+ let mut builder = FileSetConfigBuilder :: default ( ) ;
486+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
487+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
488+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/abc" . to_owned( ) ) ] ) ;
489+ let fsc = builder. build ( ) ;
490+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
491+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
492+
493+ assert_eq ! ( vc, vec![ ( SourceRootId ( 2 ) , SourceRootId ( 1 ) ) ] )
494+ }
495+
496+ #[ test]
497+ fn deep_sources_with_parent_missing ( ) {
498+ let mut builder = FileSetConfigBuilder :: default ( ) ;
499+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
500+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/ghi" . to_owned( ) ) ] ) ;
501+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/abc" . to_owned( ) ) ] ) ;
502+ let fsc = builder. build ( ) ;
503+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
504+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
505+
506+ assert_eq ! ( vc, vec![ ] )
507+ }
508+
509+ #[ test]
510+ fn ancestor_can_be_parent ( ) {
511+ let mut builder = FileSetConfigBuilder :: default ( ) ;
512+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
513+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
514+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/ghi/jkl" . to_owned( ) ) ] ) ;
515+ let fsc = builder. build ( ) ;
516+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
517+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
518+
519+ assert_eq ! ( vc, vec![ ( SourceRootId ( 2 ) , SourceRootId ( 1 ) ) ] )
520+ }
521+
522+ #[ test]
523+ fn ancestor_can_be_parent_2 ( ) {
524+ let mut builder = FileSetConfigBuilder :: default ( ) ;
525+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
526+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
527+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/ghi/jkl" . to_owned( ) ) ] ) ;
528+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/ghi/klm" . to_owned( ) ) ] ) ;
529+ let fsc = builder. build ( ) ;
530+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 , 3 ] } ;
531+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
532+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
533+
534+ assert_eq ! ( vc, vec![ ( SourceRootId ( 2 ) , SourceRootId ( 1 ) ) , ( SourceRootId ( 3 ) , SourceRootId ( 1 ) ) ] )
535+ }
536+
537+ #[ test]
538+ fn non_locals_are_skipped ( ) {
539+ let mut builder = FileSetConfigBuilder :: default ( ) ;
540+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
541+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
542+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/ghi/jkl" . to_owned( ) ) ] ) ;
543+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/klm" . to_owned( ) ) ] ) ;
544+ let fsc = builder. build ( ) ;
545+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 3 ] } ;
546+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
547+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
548+
549+ assert_eq ! ( vc, vec![ ( SourceRootId ( 3 ) , SourceRootId ( 1 ) ) , ] )
550+ }
551+
552+ #[ test]
553+ fn child_binds_ancestor_if_parent_nonlocal ( ) {
554+ let mut builder = FileSetConfigBuilder :: default ( ) ;
555+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/abc" . to_owned( ) ) ] ) ;
556+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def" . to_owned( ) ) ] ) ;
557+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/klm" . to_owned( ) ) ] ) ;
558+ builder. add_file_set ( vec ! [ VfsPath :: new_virtual_path( "/ROOT/def/klm/jkl" . to_owned( ) ) ] ) ;
559+ let fsc = builder. build ( ) ;
560+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 3 ] } ;
561+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
562+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
563+
564+ assert_eq ! ( vc, vec![ ( SourceRootId ( 3 ) , SourceRootId ( 1 ) ) , ] )
565+ }
416566}
0 commit comments