@@ -31,6 +31,7 @@ import {
31
31
DocumentRegistry ,
32
32
DocumentRegistryBucketKeyWithMode ,
33
33
emptyOptions ,
34
+ endsWith ,
34
35
ensureTrailingDirectorySeparator ,
35
36
equateStringsCaseInsensitive ,
36
37
equateStringsCaseSensitive ,
@@ -643,10 +644,37 @@ export interface ProjectServiceOptions {
643
644
*/
644
645
export type ConfigFileName = NormalizedPath | false ;
645
646
647
+ /**
648
+ * Stores cached config file name for info as well as ancestor so is a map
649
+ * Key is false for Open ScriptInfo
650
+ * Key is NormalizedPath for Config file name
651
+ * @internal
652
+ */
653
+ export type ConfigFileMapForOpenFile = Map < ConfigFileName , ConfigFileName > ;
654
+
655
+ /**
656
+ * The cache for open script info will have
657
+ * ConfigFileName or false if ancestors are not looked up
658
+ * Map if ancestors are looked up
659
+ * @internal
660
+ */
661
+ export type ConfigFileForOpenFile = ConfigFileName | ConfigFileMapForOpenFile ;
662
+
646
663
/** Gets cached value of config file name based on open script info or ancestor script info */
647
- function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileName > | undefined ) : ConfigFileName | undefined {
648
- if ( ! cache || isAncestorConfigFileInfo ( info ) ) return undefined ;
649
- return cache . get ( info . path ) ;
664
+ function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileForOpenFile > | undefined ) : ConfigFileName | undefined {
665
+ if ( ! cache ) return undefined ;
666
+ const configFileForOpenFile = cache . get ( info . path ) ;
667
+ if ( configFileForOpenFile === undefined ) return undefined ;
668
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
669
+ return isString ( configFileForOpenFile ) || ! configFileForOpenFile ?
670
+ configFileForOpenFile : // direct result
671
+ configFileForOpenFile . get ( /*key*/ false ) ; // Its a map, use false as the key for the info's config file name
672
+ }
673
+ else {
674
+ return configFileForOpenFile && ! isString ( configFileForOpenFile ) ? // Map with fileName as key
675
+ configFileForOpenFile . get ( info . fileName ) :
676
+ undefined ; // No result for the config file name
677
+ }
650
678
}
651
679
652
680
/** @internal */
@@ -661,6 +689,7 @@ export interface AncestorConfigFileInfo {
661
689
/** path of open file so we can look at correct root */
662
690
path : Path ;
663
691
configFileInfo : true ;
692
+ isForDefaultProject : boolean ;
664
693
}
665
694
/** @internal */
666
695
export type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo ;
@@ -709,6 +738,8 @@ function forEachAncestorProject<T>(
709
738
allowDeferredClosed : boolean | undefined ,
710
739
/** Used with ConfiguredProjectLoadKind.Reload to check if this project was already reloaded */
711
740
reloadedProjects : Set < ConfiguredProject > | undefined ,
741
+ /** true means we are looking for solution, so we can stop if found project is not composite to go into parent solution */
742
+ searchOnlyPotentialSolution : boolean ,
712
743
/** Used with ConfiguredProjectLoadKind.Reload to specify delay reload, and also a set of configured projects already marked for delay load */
713
744
delayReloadedConfiguredProjects ?: Set < ConfiguredProject > ,
714
745
) : T | undefined {
@@ -718,7 +749,10 @@ function forEachAncestorProject<T>(
718
749
if (
719
750
! project . initialLoadPending &&
720
751
(
721
- ! project . getCompilerOptions ( ) . composite ||
752
+ ( searchOnlyPotentialSolution && ! project . getCompilerOptions ( ) . composite ) ||
753
+ // Currently disableSolutionSearching is shared for finding solution/project when
754
+ // - loading solution for find all references
755
+ // - trying to find default project
722
756
project . getCompilerOptions ( ) . disableSolutionSearching
723
757
)
724
758
) return ;
@@ -728,6 +762,7 @@ function forEachAncestorProject<T>(
728
762
fileName : project . getConfigFilePath ( ) ,
729
763
path : info . path ,
730
764
configFileInfo : true ,
765
+ isForDefaultProject : ! searchOnlyPotentialSolution ,
731
766
} , kind === ConfiguredProjectLoadKind . Find ) ;
732
767
if ( ! configFileName ) return ;
733
768
@@ -737,9 +772,9 @@ function forEachAncestorProject<T>(
737
772
kind ,
738
773
reason ,
739
774
allowDeferredClosed ,
740
- /*triggerFile*/ undefined ,
775
+ ! searchOnlyPotentialSolution ? info . fileName : undefined , // Config Diag event for project if its for default project
741
776
reloadedProjects ,
742
- /*delayLoad*/ true ,
777
+ searchOnlyPotentialSolution , // Delay load if we are searching for solution
743
778
delayReloadedConfiguredProjects ,
744
779
) ;
745
780
if ( ! ancestor ) return ;
@@ -1219,7 +1254,7 @@ export class ProjectService {
1219
1254
*/
1220
1255
readonly openFiles : Map < Path , NormalizedPath | undefined > = new Map < Path , NormalizedPath | undefined > ( ) ;
1221
1256
/** Config files looked up and cached config files for open script info */
1222
- private readonly configFileForOpenFiles = new Map < Path , ConfigFileName > ( ) ;
1257
+ private readonly configFileForOpenFiles = new Map < Path , ConfigFileForOpenFile > ( ) ;
1223
1258
/** Set of open script infos that are root of inferred project */
1224
1259
private rootOfInferredProjects = new Set < ScriptInfo > ( ) ;
1225
1260
/**
@@ -1258,7 +1293,7 @@ export class ProjectService {
1258
1293
* All the open script info that needs recalculation of the default project,
1259
1294
* this also caches config file info before config file change was detected to use it in case projects are not updated yet
1260
1295
*/
1261
- private pendingOpenFileProjectUpdates ?: Map < Path , ConfigFileName > ;
1296
+ private pendingOpenFileProjectUpdates ?: Map < Path , ConfigFileForOpenFile > ;
1262
1297
/** @internal */
1263
1298
pendingEnsureProjectForOpenFiles = false ;
1264
1299
@@ -2277,7 +2312,7 @@ export class ProjectService {
2277
2312
const configFileExistenceInfo = this . configFileExistenceInfoCache . get ( canonicalConfigFilePath ) ;
2278
2313
2279
2314
let openFilesImpactedByConfigFile : Set < Path > | undefined ;
2280
- if ( this . openFiles . has ( info . path ) && ! isAncestorConfigFileInfo ( info ) ) {
2315
+ if ( this . openFiles . has ( info . path ) && ( ! isAncestorConfigFileInfo ( info ) || info . isForDefaultProject ) ) {
2281
2316
// By default the info would get impacted by presence of config file since its in the detection path
2282
2317
// Only adding the info as a root to inferred project will need the existence to be watched by file watcher
2283
2318
if ( configFileExistenceInfo ) ( configFileExistenceInfo . openFilesImpactedByConfigFile ??= new Set ( ) ) . add ( info . path ) ;
@@ -2470,31 +2505,39 @@ export class ProjectService {
2470
2505
2471
2506
// If projectRootPath doesn't contain info.path, then do normal search for config file
2472
2507
const anySearchPathOk = ! projectRootPath || ! isSearchPathInProjectRoot ( ) ;
2473
- // For ancestor of config file always ignore its own directory since its going to result in itself
2474
- let searchInDirectory = ! isAncestorConfigFileInfo ( info ) ;
2508
+
2509
+ let searchTsconfig = true ;
2510
+ let searchJsconfig = true ;
2511
+ if ( isAncestorConfigFileInfo ( info ) ) {
2512
+ // For ancestor of config file always ignore itself
2513
+ if ( endsWith ( info . fileName , "tsconfig.json" ) ) searchTsconfig = false ;
2514
+ else searchTsconfig = searchJsconfig = false ;
2515
+ }
2475
2516
do {
2476
- if ( searchInDirectory ) {
2477
- const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2517
+ const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2518
+ if ( searchTsconfig ) {
2478
2519
const tsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "tsconfig.json" ) ) ;
2479
- let result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2520
+ const result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2480
2521
if ( result ) return tsconfigFileName ;
2522
+ }
2481
2523
2524
+ if ( searchJsconfig ) {
2482
2525
const jsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "jsconfig.json" ) ) ;
2483
- result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2526
+ const result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2484
2527
if ( result ) return jsconfigFileName ;
2528
+ }
2485
2529
2486
- // If we started within node_modules, don't look outside node_modules.
2487
- // Otherwise, we might pick up a very large project and pull in the world,
2488
- // causing an editor delay.
2489
- if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2490
- break ;
2491
- }
2530
+ // If we started within node_modules, don't look outside node_modules.
2531
+ // Otherwise, we might pick up a very large project and pull in the world,
2532
+ // causing an editor delay.
2533
+ if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2534
+ break ;
2492
2535
}
2493
2536
2494
2537
const parentPath = asNormalizedPath ( getDirectoryPath ( searchPath ) ) ;
2495
2538
if ( parentPath === searchPath ) break ;
2496
2539
searchPath = parentPath ;
2497
- searchInDirectory = true ;
2540
+ searchTsconfig = searchJsconfig = true ;
2498
2541
}
2499
2542
while ( anySearchPathOk || isSearchPathInProjectRoot ( ) ) ;
2500
2543
@@ -2529,8 +2572,24 @@ export class ProjectService {
2529
2572
configFileName : NormalizedPath | undefined ,
2530
2573
) {
2531
2574
if ( ! this . openFiles . has ( info . path ) ) return ; // Dont cache for closed script infos
2532
- if ( isAncestorConfigFileInfo ( info ) ) return ; // Dont cache for ancestors
2533
- this . configFileForOpenFiles . set ( info . path , configFileName || false ) ;
2575
+ const config = configFileName || false ;
2576
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
2577
+ // Set value for open script info
2578
+ this . configFileForOpenFiles . set ( info . path , config ) ;
2579
+ }
2580
+ else {
2581
+ // Need to set value for ancestor in ConfigFileMapForOpenFile
2582
+ let configFileForOpenFile = this . configFileForOpenFiles . get ( info . path ) ! ;
2583
+ if ( ! configFileForOpenFile || isString ( configFileForOpenFile ) ) {
2584
+ // We have value for open script info in cache, make a map with that as false key and set new vlaue
2585
+ this . configFileForOpenFiles . set (
2586
+ info . path ,
2587
+ configFileForOpenFile = new Map ( ) . set ( false , configFileForOpenFile ) ,
2588
+ ) ;
2589
+ }
2590
+ // Set value of for ancestor in the map
2591
+ configFileForOpenFile . set ( info . fileName , config ) ;
2592
+ }
2534
2593
}
2535
2594
2536
2595
/**
@@ -4275,7 +4334,8 @@ export class ProjectService {
4275
4334
function tryFindDefaultConfiguredProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
4276
4335
return isDefaultProject ( project ) ?
4277
4336
defaultProject :
4278
- tryFindDefaultConfiguredProjectFromReferences ( project ) ;
4337
+ ( tryFindDefaultConfiguredProjectFromReferences ( project ) ??
4338
+ tryFindDefaultConfiguredProjectFromAncestor ( project ) ) ;
4279
4339
}
4280
4340
4281
4341
function isDefaultProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
@@ -4305,6 +4365,19 @@ export class ProjectService {
4305
4365
reloadedProjects ,
4306
4366
) ;
4307
4367
}
4368
+
4369
+ function tryFindDefaultConfiguredProjectFromAncestor ( project : ConfiguredProject ) {
4370
+ return forEachAncestorProject ( // If not in referenced projects, try ancestors and its references
4371
+ info ,
4372
+ project ,
4373
+ tryFindDefaultConfiguredProject ,
4374
+ kind ,
4375
+ `Creating possible configured project for ${ info . fileName } to open` ,
4376
+ allowDeferredClosed ,
4377
+ reloadedProjects ,
4378
+ /*searchOnlyPotentialSolution*/ false ,
4379
+ ) ;
4380
+ }
4308
4381
}
4309
4382
4310
4383
/**
@@ -4349,6 +4422,7 @@ export class ProjectService {
4349
4422
`Creating project possibly referencing default composite project ${ defaultProject . getProjectName ( ) } of open file ${ info . fileName } ` ,
4350
4423
allowDeferredClosed ,
4351
4424
reloadedProjects ,
4425
+ /*searchOnlyPotentialSolution*/ true ,
4352
4426
delayReloadedConfiguredProjects ,
4353
4427
) ;
4354
4428
}
0 commit comments