@@ -10,7 +10,7 @@ use hir_expand::proc_macro::{
10
10
ProcMacros ,
11
11
} ;
12
12
use ide_db:: {
13
- base_db:: { CrateGraph , Env , SourceRoot } ,
13
+ base_db:: { CrateGraph , Env , SourceRoot , SourceRootId } ,
14
14
prime_caches, ChangeWithProcMacros , FxHashMap , RootDatabase ,
15
15
} ;
16
16
use itertools:: Itertools ;
@@ -231,7 +231,7 @@ impl ProjectFolders {
231
231
res. load . push ( entry) ;
232
232
233
233
if root. is_local {
234
- local_filesets. push ( fsc. len ( ) ) ;
234
+ local_filesets. push ( fsc. len ( ) as u64 ) ;
235
235
}
236
236
fsc. add_file_set ( file_set_roots)
237
237
}
@@ -246,7 +246,7 @@ impl ProjectFolders {
246
246
#[ derive( Default , Debug ) ]
247
247
pub struct SourceRootConfig {
248
248
pub fsc : FileSetConfig ,
249
- pub local_filesets : Vec < usize > ,
249
+ pub local_filesets : Vec < u64 > ,
250
250
}
251
251
252
252
impl SourceRootConfig {
@@ -256,7 +256,7 @@ impl SourceRootConfig {
256
256
. into_iter ( )
257
257
. enumerate ( )
258
258
. map ( |( idx, file_set) | {
259
- let is_local = self . local_filesets . contains ( & idx) ;
259
+ let is_local = self . local_filesets . contains ( & ( idx as u64 ) ) ;
260
260
if is_local {
261
261
SourceRoot :: new_local ( file_set)
262
262
} else {
@@ -265,6 +265,31 @@ impl SourceRootConfig {
265
265
} )
266
266
. collect ( )
267
267
}
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
+ }
268
293
}
269
294
270
295
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
@@ -397,6 +422,11 @@ mod tests {
397
422
398
423
use super :: * ;
399
424
425
+ use ide_db:: base_db:: SourceRootId ;
426
+ use vfs:: { file_set:: FileSetConfigBuilder , VfsPath } ;
427
+
428
+ use crate :: SourceRootConfig ;
429
+
400
430
#[ test]
401
431
fn test_loading_rust_analyzer ( ) {
402
432
let path = Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . parent ( ) . unwrap ( ) . parent ( ) . unwrap ( ) ;
@@ -413,4 +443,124 @@ mod tests {
413
443
// RA has quite a few crates, but the exact count doesn't matter
414
444
assert ! ( n_crates > 20 ) ;
415
445
}
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
+ }
416
566
}
0 commit comments