@@ -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,36 @@ 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 source root doesn't have a parent then its parent is declared as None.
271+ pub fn source_root_parent_map ( & self ) -> FxHashMap < SourceRootId , Option < SourceRootId > > {
272+ let roots = self . fsc . roots ( ) ;
273+ let mut map = FxHashMap :: < SourceRootId , Option < SourceRootId > > :: default ( ) ;
274+
275+ ' outer: for ( idx, ( root, root_id) ) in roots. iter ( ) . enumerate ( ) {
276+ if !self . local_filesets . contains ( root_id) {
277+ continue ;
278+ }
279+
280+ for ( _, ( root2, root2_id) ) in roots. iter ( ) . enumerate ( ) . take ( idx) . rev ( ) {
281+ if root2. iter ( ) . enumerate ( ) . all ( |( i, c) | & root[ i] == c) {
282+ // We are interested in parents if they are also local source roots.
283+ // So instead of a non-local parent we may take a local ancestor as a parent to a node.
284+ if self . local_filesets . contains ( root2_id) {
285+ map. insert (
286+ SourceRootId ( root_id. to_owned ( ) as u32 ) ,
287+ Some ( SourceRootId ( root2_id. to_owned ( ) as u32 ) ) ,
288+ ) ;
289+ continue ' outer;
290+ }
291+ }
292+ }
293+ map. insert ( SourceRootId ( idx as u32 ) , None ) ;
294+ }
295+
296+ map
297+ }
268298}
269299
270300/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
@@ -413,4 +443,203 @@ mod tests {
413443 // RA has quite a few crates, but the exact count doesn't matter
414444 assert ! ( n_crates > 20 ) ;
415445 }
446+
447+ mod source_root_parent {
448+ use ide_db:: base_db:: SourceRootId ;
449+ use vfs:: { file_set:: FileSetConfigBuilder , VfsPath } ;
450+
451+ use crate :: SourceRootConfig ;
452+
453+ macro_rules! virp {
454+ ( $s : literal) => {
455+ VfsPath :: new_virtual_path( format!( $s) )
456+ } ;
457+ }
458+
459+ #[ test]
460+ fn test1 ( ) {
461+ let mut builder = FileSetConfigBuilder :: default ( ) ;
462+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
463+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
464+ builder. add_file_set ( root) ;
465+ builder. add_file_set ( root2) ;
466+ let fsc = builder. build ( ) ;
467+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
468+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
469+
470+ assert_eq ! ( vc, vec![ ( SourceRootId ( 0 ) , None ) , ( SourceRootId ( 1 ) , None ) ] )
471+ }
472+
473+ #[ test]
474+ fn test2 ( ) {
475+ let mut builder = FileSetConfigBuilder :: default ( ) ;
476+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
477+ let root2 = vec ! [ virp!( "/ROOT/def/abc" ) ] ;
478+ builder. add_file_set ( root) ;
479+ builder. add_file_set ( root2) ;
480+ let fsc = builder. build ( ) ;
481+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
482+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
483+
484+ assert_eq ! ( vc, vec![ ( SourceRootId ( 0 ) , None ) , ( SourceRootId ( 1 ) , None ) ] )
485+ }
486+
487+ #[ test]
488+ fn test3 ( ) {
489+ let mut builder = FileSetConfigBuilder :: default ( ) ;
490+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
491+ let root2 = vec ! [ virp!( "/ROOT/abc/def" ) ] ;
492+ builder. add_file_set ( root) ;
493+ builder. add_file_set ( root2) ;
494+ let fsc = builder. build ( ) ;
495+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 ] } ;
496+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
497+
498+ assert_eq ! ( vc, vec![ ( SourceRootId ( 0 ) , None ) , ( SourceRootId ( 1 ) , Some ( SourceRootId ( 0 ) ) ) ] )
499+ }
500+
501+ #[ test]
502+ fn test4 ( ) {
503+ let mut builder = FileSetConfigBuilder :: default ( ) ;
504+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
505+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
506+ let root3 = vec ! [ virp!( "/ROOT/def/abc" ) ] ;
507+ builder. add_file_set ( root) ;
508+ builder. add_file_set ( root2) ;
509+ builder. add_file_set ( root3) ;
510+ let fsc = builder. build ( ) ;
511+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
512+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
513+
514+ assert_eq ! (
515+ vc,
516+ vec![
517+ ( SourceRootId ( 0 ) , None ) ,
518+ ( SourceRootId ( 1 ) , None ) ,
519+ ( SourceRootId ( 2 ) , Some ( SourceRootId ( 1 ) ) )
520+ ]
521+ )
522+ }
523+
524+ #[ test]
525+ fn test5 ( ) {
526+ let mut builder = FileSetConfigBuilder :: default ( ) ;
527+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
528+ let root2 = vec ! [ virp!( "/ROOT/ghi" ) ] ;
529+ let root3 = vec ! [ virp!( "/ROOT/def/abc" ) ] ;
530+ builder. add_file_set ( root) ;
531+ builder. add_file_set ( root2) ;
532+ builder. add_file_set ( root3) ;
533+ let fsc = builder. build ( ) ;
534+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
535+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
536+
537+ assert_eq ! (
538+ vc,
539+ vec![ ( SourceRootId ( 0 ) , None ) , ( SourceRootId ( 1 ) , None ) , ( SourceRootId ( 2 ) , None ) ]
540+ )
541+ }
542+
543+ #[ test]
544+ fn test6 ( ) {
545+ let mut builder = FileSetConfigBuilder :: default ( ) ;
546+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
547+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
548+ let root3 = vec ! [ virp!( "/ROOT/def/ghi/jkl" ) ] ;
549+ builder. add_file_set ( root) ;
550+ builder. add_file_set ( root2) ;
551+ builder. add_file_set ( root3) ;
552+ let fsc = builder. build ( ) ;
553+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 ] } ;
554+ let vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
555+
556+ assert_eq ! (
557+ vc,
558+ vec![
559+ ( SourceRootId ( 0 ) , None ) ,
560+ ( SourceRootId ( 1 ) , None ) ,
561+ ( SourceRootId ( 2 ) , Some ( SourceRootId ( 1 ) ) )
562+ ]
563+ )
564+ }
565+
566+ #[ test]
567+ fn test7 ( ) {
568+ let mut builder = FileSetConfigBuilder :: default ( ) ;
569+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
570+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
571+ let root3 = vec ! [ virp!( "/ROOT/def/ghi/jkl" ) ] ;
572+ let root4 = vec ! [ virp!( "/ROOT/def/ghi/klm" ) ] ;
573+ builder. add_file_set ( root) ;
574+ builder. add_file_set ( root2) ;
575+ builder. add_file_set ( root3) ;
576+ builder. add_file_set ( root4) ;
577+ let fsc = builder. build ( ) ;
578+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 2 , 3 ] } ;
579+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
580+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
581+
582+ assert_eq ! (
583+ vc,
584+ vec![
585+ ( SourceRootId ( 0 ) , None ) ,
586+ ( SourceRootId ( 1 ) , None ) ,
587+ ( SourceRootId ( 2 ) , Some ( SourceRootId ( 1 ) ) ) ,
588+ ( SourceRootId ( 3 ) , Some ( SourceRootId ( 1 ) ) )
589+ ]
590+ )
591+ }
592+
593+ #[ test]
594+ fn test8 ( ) {
595+ let mut builder = FileSetConfigBuilder :: default ( ) ;
596+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
597+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
598+ let root3 = vec ! [ virp!( "/ROOT/def/ghi/jkl" ) ] ;
599+ let root4 = vec ! [ virp!( "/ROOT/def/klm" ) ] ;
600+ builder. add_file_set ( root) ;
601+ builder. add_file_set ( root2) ;
602+ builder. add_file_set ( root3) ;
603+ builder. add_file_set ( root4) ;
604+ let fsc = builder. build ( ) ;
605+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 3 ] } ;
606+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
607+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
608+
609+ assert_eq ! (
610+ vc,
611+ vec![
612+ ( SourceRootId ( 0 ) , None ) ,
613+ ( SourceRootId ( 1 ) , None ) ,
614+ ( SourceRootId ( 3 ) , Some ( SourceRootId ( 1 ) ) ) ,
615+ ]
616+ )
617+ }
618+
619+ #[ test]
620+ fn test9 ( ) {
621+ let mut builder = FileSetConfigBuilder :: default ( ) ;
622+ let root = vec ! [ virp!( "/ROOT/abc" ) ] ;
623+ let root2 = vec ! [ virp!( "/ROOT/def" ) ] ;
624+ let root3 = vec ! [ virp!( "/ROOT/def/klm" ) ] ;
625+ let root4 = vec ! [ virp!( "/ROOT/def/klm/jkl" ) ] ;
626+ builder. add_file_set ( root) ;
627+ builder. add_file_set ( root2) ;
628+ builder. add_file_set ( root3) ;
629+ builder. add_file_set ( root4) ;
630+ let fsc = builder. build ( ) ;
631+ let src = SourceRootConfig { fsc, local_filesets : vec ! [ 0 , 1 , 3 ] } ;
632+ let mut vc = src. source_root_parent_map ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
633+ vc. sort_by ( |x, y| x. 0 . 0 . cmp ( & y. 0 . 0 ) ) ;
634+
635+ assert_eq ! (
636+ vc,
637+ vec![
638+ ( SourceRootId ( 0 ) , None ) ,
639+ ( SourceRootId ( 1 ) , None ) ,
640+ ( SourceRootId ( 3 ) , Some ( SourceRootId ( 1 ) ) ) ,
641+ ]
642+ )
643+ }
644+ }
416645}
0 commit comments