@@ -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 ,
@@ -644,10 +645,37 @@ export interface ProjectServiceOptions {
644
645
*/
645
646
export type ConfigFileName = NormalizedPath | false ;
646
647
648
+ /**
649
+ * Stores cached config file name for info as well as ancestor so is a map
650
+ * Key is false for Open ScriptInfo
651
+ * Key is NormalizedPath for Config file name
652
+ * @internal
653
+ */
654
+ export type ConfigFileMapForOpenFile = Map < ConfigFileName , ConfigFileName > ;
655
+
656
+ /**
657
+ * The cache for open script info will have
658
+ * ConfigFileName or false if ancestors are not looked up
659
+ * Map if ancestors are looked up
660
+ * @internal
661
+ */
662
+ export type ConfigFileForOpenFile = ConfigFileName | ConfigFileMapForOpenFile ;
663
+
647
664
/** Gets cached value of config file name based on open script info or ancestor script info */
648
- function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileName > | undefined ) : ConfigFileName | undefined {
649
- if ( ! cache || isAncestorConfigFileInfo ( info ) ) return undefined ;
650
- return cache . get ( info . path ) ;
665
+ function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileForOpenFile > | undefined ) : ConfigFileName | undefined {
666
+ if ( ! cache ) return undefined ;
667
+ const configFileForOpenFile = cache . get ( info . path ) ;
668
+ if ( configFileForOpenFile === undefined ) return undefined ;
669
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
670
+ return isString ( configFileForOpenFile ) || ! configFileForOpenFile ?
671
+ configFileForOpenFile : // direct result
672
+ configFileForOpenFile . get ( /*key*/ false ) ; // Its a map, use false as the key for the info's config file name
673
+ }
674
+ else {
675
+ return configFileForOpenFile && ! isString ( configFileForOpenFile ) ? // Map with fileName as key
676
+ configFileForOpenFile . get ( info . fileName ) :
677
+ undefined ; // No result for the config file name
678
+ }
651
679
}
652
680
653
681
/** @internal */
@@ -662,6 +690,7 @@ export interface AncestorConfigFileInfo {
662
690
/** path of open file so we can look at correct root */
663
691
path : Path ;
664
692
configFileInfo : true ;
693
+ isForDefaultProject : boolean ;
665
694
}
666
695
/** @internal */
667
696
export type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo ;
@@ -710,6 +739,8 @@ function forEachAncestorProject<T>(
710
739
allowDeferredClosed : boolean | undefined ,
711
740
/** Used with ConfiguredProjectLoadKind.Reload to check if this project was already reloaded */
712
741
reloadedProjects : Set < ConfiguredProject > | undefined ,
742
+ /** true means we are looking for solution, so we can stop if found project is not composite to go into parent solution */
743
+ searchOnlyPotentialSolution : boolean ,
713
744
/** Used with ConfiguredProjectLoadKind.Reload to specify delay reload, and also a set of configured projects already marked for delay load */
714
745
delayReloadedConfiguredProjects ?: Set < ConfiguredProject > ,
715
746
) : T | undefined {
@@ -719,7 +750,10 @@ function forEachAncestorProject<T>(
719
750
if (
720
751
! project . initialLoadPending &&
721
752
(
722
- ! project . getCompilerOptions ( ) . composite ||
753
+ ( searchOnlyPotentialSolution && ! project . getCompilerOptions ( ) . composite ) ||
754
+ // Currently disableSolutionSearching is shared for finding solution/project when
755
+ // - loading solution for find all references
756
+ // - trying to find default project
723
757
project . getCompilerOptions ( ) . disableSolutionSearching
724
758
)
725
759
) return ;
@@ -729,6 +763,7 @@ function forEachAncestorProject<T>(
729
763
fileName : project . getConfigFilePath ( ) ,
730
764
path : info . path ,
731
765
configFileInfo : true ,
766
+ isForDefaultProject : ! searchOnlyPotentialSolution ,
732
767
} , kind === ConfiguredProjectLoadKind . Find ) ;
733
768
if ( ! configFileName ) return ;
734
769
@@ -738,9 +773,9 @@ function forEachAncestorProject<T>(
738
773
kind ,
739
774
reason ,
740
775
allowDeferredClosed ,
741
- /*triggerFile*/ undefined ,
776
+ ! searchOnlyPotentialSolution ? info . fileName : undefined , // Config Diag event for project if its for default project
742
777
reloadedProjects ,
743
- /*delayLoad*/ true ,
778
+ searchOnlyPotentialSolution , // Delay load if we are searching for solution
744
779
delayReloadedConfiguredProjects ,
745
780
) ;
746
781
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
@@ -2268,7 +2303,7 @@ export class ProjectService {
2268
2303
const configFileExistenceInfo = this . configFileExistenceInfoCache . get ( canonicalConfigFilePath ) ;
2269
2304
2270
2305
let openFilesImpactedByConfigFile : Set < Path > | undefined ;
2271
- if ( this . openFiles . has ( info . path ) && ! isAncestorConfigFileInfo ( info ) ) {
2306
+ if ( this . openFiles . has ( info . path ) && ( ! isAncestorConfigFileInfo ( info ) || info . isForDefaultProject ) ) {
2272
2307
// By default the info would get impacted by presence of config file since its in the detection path
2273
2308
// Only adding the info as a root to inferred project will need the existence to be watched by file watcher
2274
2309
if ( configFileExistenceInfo ) ( configFileExistenceInfo . openFilesImpactedByConfigFile ??= new Set ( ) ) . add ( info . path ) ;
@@ -2461,31 +2496,39 @@ export class ProjectService {
2461
2496
2462
2497
// If projectRootPath doesn't contain info.path, then do normal search for config file
2463
2498
const anySearchPathOk = ! projectRootPath || ! isSearchPathInProjectRoot ( ) ;
2464
- // For ancestor of config file always ignore its own directory since its going to result in itself
2465
- let searchInDirectory = ! isAncestorConfigFileInfo ( info ) ;
2499
+
2500
+ let searchTsconfig = true ;
2501
+ let searchJsconfig = true ;
2502
+ if ( isAncestorConfigFileInfo ( info ) ) {
2503
+ // For ancestor of config file always ignore itself
2504
+ if ( endsWith ( info . fileName , "tsconfig.json" ) ) searchTsconfig = false ;
2505
+ else searchTsconfig = searchJsconfig = false ;
2506
+ }
2466
2507
do {
2467
- if ( searchInDirectory ) {
2468
- const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2508
+ const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2509
+ if ( searchTsconfig ) {
2469
2510
const tsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "tsconfig.json" ) ) ;
2470
- let result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2511
+ const result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2471
2512
if ( result ) return tsconfigFileName ;
2513
+ }
2472
2514
2515
+ if ( searchJsconfig ) {
2473
2516
const jsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "jsconfig.json" ) ) ;
2474
- result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2517
+ const result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2475
2518
if ( result ) return jsconfigFileName ;
2519
+ }
2476
2520
2477
- // If we started within node_modules, don't look outside node_modules.
2478
- // Otherwise, we might pick up a very large project and pull in the world,
2479
- // causing an editor delay.
2480
- if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2481
- break ;
2482
- }
2521
+ // If we started within node_modules, don't look outside node_modules.
2522
+ // Otherwise, we might pick up a very large project and pull in the world,
2523
+ // causing an editor delay.
2524
+ if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2525
+ break ;
2483
2526
}
2484
2527
2485
2528
const parentPath = asNormalizedPath ( getDirectoryPath ( searchPath ) ) ;
2486
2529
if ( parentPath === searchPath ) break ;
2487
2530
searchPath = parentPath ;
2488
- searchInDirectory = true ;
2531
+ searchTsconfig = searchJsconfig = true ;
2489
2532
}
2490
2533
while ( anySearchPathOk || isSearchPathInProjectRoot ( ) ) ;
2491
2534
@@ -2520,8 +2563,24 @@ export class ProjectService {
2520
2563
configFileName : NormalizedPath | undefined ,
2521
2564
) {
2522
2565
if ( ! this . openFiles . has ( info . path ) ) return ; // Dont cache for closed script infos
2523
- if ( isAncestorConfigFileInfo ( info ) ) return ; // Dont cache for ancestors
2524
- this . configFileForOpenFiles . set ( info . path , configFileName || false ) ;
2566
+ const config = configFileName || false ;
2567
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
2568
+ // Set value for open script info
2569
+ this . configFileForOpenFiles . set ( info . path , config ) ;
2570
+ }
2571
+ else {
2572
+ // Need to set value for ancestor in ConfigFileMapForOpenFile
2573
+ let configFileForOpenFile = this . configFileForOpenFiles . get ( info . path ) ! ;
2574
+ if ( ! configFileForOpenFile || isString ( configFileForOpenFile ) ) {
2575
+ // We have value for open script info in cache, make a map with that as false key and set new vlaue
2576
+ this . configFileForOpenFiles . set (
2577
+ info . path ,
2578
+ configFileForOpenFile = new Map ( ) . set ( false , configFileForOpenFile ) ,
2579
+ ) ;
2580
+ }
2581
+ // Set value of for ancestor in the map
2582
+ configFileForOpenFile . set ( info . fileName , config ) ;
2583
+ }
2525
2584
}
2526
2585
2527
2586
/**
@@ -4247,7 +4306,8 @@ export class ProjectService {
4247
4306
function tryFindDefaultConfiguredProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
4248
4307
return isDefaultProject ( project ) ?
4249
4308
defaultProject :
4250
- tryFindDefaultConfiguredProjectFromReferences ( project ) ;
4309
+ ( tryFindDefaultConfiguredProjectFromReferences ( project ) ??
4310
+ tryFindDefaultConfiguredProjectFromAncestor ( project ) ) ;
4251
4311
}
4252
4312
4253
4313
function isDefaultProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
@@ -4277,6 +4337,19 @@ export class ProjectService {
4277
4337
reloadedProjects ,
4278
4338
) ;
4279
4339
}
4340
+
4341
+ function tryFindDefaultConfiguredProjectFromAncestor ( project : ConfiguredProject ) {
4342
+ return forEachAncestorProject ( // If not in referenced projects, try ancestors and its references
4343
+ info ,
4344
+ project ,
4345
+ tryFindDefaultConfiguredProject ,
4346
+ kind ,
4347
+ `Creating possible configured project for ${ info . fileName } to open` ,
4348
+ allowDeferredClosed ,
4349
+ reloadedProjects ,
4350
+ /*searchOnlyPotentialSolution*/ false ,
4351
+ ) ;
4352
+ }
4280
4353
}
4281
4354
4282
4355
/**
@@ -4321,6 +4394,7 @@ export class ProjectService {
4321
4394
`Creating project possibly referencing default composite project ${ defaultProject . getProjectName ( ) } of open file ${ info . fileName } ` ,
4322
4395
allowDeferredClosed ,
4323
4396
reloadedProjects ,
4397
+ /*searchOnlyPotentialSolution*/ true ,
4324
4398
delayReloadedConfiguredProjects ,
4325
4399
) ;
4326
4400
}
0 commit comments