@@ -31,6 +31,7 @@ import {
31
31
DocumentRegistry ,
32
32
DocumentRegistryBucketKeyWithMode ,
33
33
emptyOptions ,
34
+ endsWith ,
34
35
ensureTrailingDirectorySeparator ,
35
36
ExtendedConfigCacheEntry ,
36
37
FileExtensionInfo ,
@@ -633,10 +634,37 @@ export interface ProjectServiceOptions {
633
634
*/
634
635
export type ConfigFileName = NormalizedPath | false ;
635
636
637
+ /**
638
+ * Stores cached config file name for info as well as ancestor so is a map
639
+ * Key is false for Open ScriptInfo
640
+ * Key is NormalizedPath for Config file name
641
+ * @internal
642
+ */
643
+ export type ConfigFileMapForOpenFile = Map < ConfigFileName , ConfigFileName > ;
644
+
645
+ /**
646
+ * The cache for open script info will have
647
+ * ConfigFileName or false if ancestors are not looked up
648
+ * Map if ancestors are looked up
649
+ * @internal
650
+ */
651
+ export type ConfigFileForOpenFile = ConfigFileName | ConfigFileMapForOpenFile ;
652
+
636
653
/** Gets cached value of config file name based on open script info or ancestor script info */
637
- function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileName > | undefined ) : ConfigFileName | undefined {
638
- if ( ! cache || isAncestorConfigFileInfo ( info ) ) return undefined ;
639
- return cache . get ( info . path ) ;
654
+ function getConfigFileNameFromCache ( info : OpenScriptInfoOrClosedOrConfigFileInfo , cache : Map < Path , ConfigFileForOpenFile > | undefined ) : ConfigFileName | undefined {
655
+ if ( ! cache ) return undefined ;
656
+ const configFileForOpenFile = cache . get ( info . path ) ;
657
+ if ( configFileForOpenFile === undefined ) return undefined ;
658
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
659
+ return isString ( configFileForOpenFile ) || ! configFileForOpenFile ?
660
+ configFileForOpenFile : // direct result
661
+ configFileForOpenFile . get ( /*key*/ false ) ; // Its a map, use false as the key for the info's config file name
662
+ }
663
+ else {
664
+ return configFileForOpenFile && ! isString ( configFileForOpenFile ) ? // Map with fileName as key
665
+ configFileForOpenFile . get ( info . fileName ) :
666
+ undefined ; // No result for the config file name
667
+ }
640
668
}
641
669
642
670
/** @internal */
@@ -651,6 +679,7 @@ export interface AncestorConfigFileInfo {
651
679
/** path of open file so we can look at correct root */
652
680
path : Path ;
653
681
configFileInfo : true ;
682
+ isForDefaultProject : boolean ;
654
683
}
655
684
/** @internal */
656
685
export type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo ;
@@ -699,6 +728,8 @@ function forEachAncestorProject<T>(
699
728
allowDeferredClosed : boolean | undefined ,
700
729
/** Used with ConfiguredProjectLoadKind.Reload to check if this project was already reloaded */
701
730
reloadedProjects : Set < ConfiguredProject > | undefined ,
731
+ /** true means we are looking for solution, so we can stop if found project is not composite to go into parent solution */
732
+ searchOnlyPotentialSolution : boolean ,
702
733
/** Used with ConfiguredProjectLoadKind.Reload to specify delay reload, and also a set of configured projects already marked for delay load */
703
734
delayReloadedConfiguredProjects ?: Set < ConfiguredProject > ,
704
735
) : T | undefined {
@@ -708,7 +739,8 @@ function forEachAncestorProject<T>(
708
739
if (
709
740
! project . isInitialLoadPending ( ) &&
710
741
(
711
- ! project . getCompilerOptions ( ) . composite ||
742
+ ( searchOnlyPotentialSolution && ! project . getCompilerOptions ( ) . composite ) ||
743
+ // TODO: Should this flag be shared between trying to load solution for find all references vs trying to find default project
712
744
project . getCompilerOptions ( ) . disableSolutionSearching
713
745
)
714
746
) return ;
@@ -718,6 +750,7 @@ function forEachAncestorProject<T>(
718
750
fileName : project . getConfigFilePath ( ) ,
719
751
path : info . path ,
720
752
configFileInfo : true ,
753
+ isForDefaultProject : ! searchOnlyPotentialSolution ,
721
754
} , kind === ConfiguredProjectLoadKind . Find ) ;
722
755
if ( ! configFileName ) return ;
723
756
@@ -727,9 +760,9 @@ function forEachAncestorProject<T>(
727
760
kind ,
728
761
reason ,
729
762
allowDeferredClosed ,
730
- /*triggerFile*/ undefined ,
763
+ ! searchOnlyPotentialSolution ? info . fileName : undefined , // Config Diag event for project if its for default project
731
764
reloadedProjects ,
732
- /*delayLoad*/ true ,
765
+ searchOnlyPotentialSolution , // Delay load if we are searching for solution
733
766
delayReloadedConfiguredProjects ,
734
767
) ;
735
768
if ( ! ancestor ) return ;
@@ -1195,7 +1228,7 @@ export class ProjectService {
1195
1228
*/
1196
1229
readonly openFiles : Map < Path , NormalizedPath | undefined > = new Map < Path , NormalizedPath | undefined > ( ) ;
1197
1230
/** Config files looked up and cached config files for open script info */
1198
- private readonly configFileForOpenFiles = new Map < Path , ConfigFileName > ( ) ;
1231
+ private readonly configFileForOpenFiles = new Map < Path , ConfigFileForOpenFile > ( ) ;
1199
1232
/** Set of open script infos that are root of inferred project */
1200
1233
private rootOfInferredProjects = new Set < ScriptInfo > ( ) ;
1201
1234
/**
@@ -1234,7 +1267,7 @@ export class ProjectService {
1234
1267
* All the open script info that needs recalculation of the default project,
1235
1268
* this also caches config file info before config file change was detected to use it in case projects are not updated yet
1236
1269
*/
1237
- private pendingOpenFileProjectUpdates ?: Map < Path , ConfigFileName > ;
1270
+ private pendingOpenFileProjectUpdates ?: Map < Path , ConfigFileForOpenFile > ;
1238
1271
/** @internal */
1239
1272
pendingEnsureProjectForOpenFiles = false ;
1240
1273
@@ -2222,7 +2255,7 @@ export class ProjectService {
2222
2255
const configFileExistenceInfo = this . configFileExistenceInfoCache . get ( canonicalConfigFilePath ) ;
2223
2256
2224
2257
let openFilesImpactedByConfigFile : Set < Path > | undefined ;
2225
- if ( this . openFiles . has ( info . path ) && ! isAncestorConfigFileInfo ( info ) ) {
2258
+ if ( this . openFiles . has ( info . path ) && ( ! isAncestorConfigFileInfo ( info ) || info . isForDefaultProject ) ) {
2226
2259
// By default the info would get impacted by presence of config file since its in the detection path
2227
2260
// Only adding the info as a root to inferred project will need the existence to be watched by file watcher
2228
2261
if ( configFileExistenceInfo ) ( configFileExistenceInfo . openFilesImpactedByConfigFile ??= new Set ( ) ) . add ( info . path ) ;
@@ -2416,31 +2449,39 @@ export class ProjectService {
2416
2449
2417
2450
// If projectRootPath doesn't contain info.path, then do normal search for config file
2418
2451
const anySearchPathOk = ! projectRootPath || ! isSearchPathInProjectRoot ( ) ;
2419
- // For ancestor of config file always ignore its own directory since its going to result in itself
2420
- let searchInDirectory = ! isAncestorConfigFileInfo ( info ) ;
2452
+
2453
+ let searchTsconfig = true ;
2454
+ let searchJsconfig = true ;
2455
+ if ( isAncestorConfigFileInfo ( info ) ) {
2456
+ // For ancestor of config file always ignore itself
2457
+ if ( endsWith ( info . fileName , "tsconfig.json" ) ) searchTsconfig = false ;
2458
+ else searchTsconfig = searchJsconfig = false ;
2459
+ }
2421
2460
do {
2422
- if ( searchInDirectory ) {
2423
- const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2461
+ const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
2462
+ if ( searchTsconfig ) {
2424
2463
const tsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "tsconfig.json" ) ) ;
2425
- let result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2464
+ const result = action ( combinePaths ( canonicalSearchPath , "tsconfig.json" ) as NormalizedPath , tsconfigFileName ) ;
2426
2465
if ( result ) return tsconfigFileName ;
2466
+ }
2427
2467
2468
+ if ( searchJsconfig ) {
2428
2469
const jsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "jsconfig.json" ) ) ;
2429
- result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2470
+ const result = action ( combinePaths ( canonicalSearchPath , "jsconfig.json" ) as NormalizedPath , jsconfigFileName ) ;
2430
2471
if ( result ) return jsconfigFileName ;
2472
+ }
2431
2473
2432
- // If we started within node_modules, don't look outside node_modules.
2433
- // Otherwise, we might pick up a very large project and pull in the world,
2434
- // causing an editor delay.
2435
- if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2436
- break ;
2437
- }
2474
+ // If we started within node_modules, don't look outside node_modules.
2475
+ // Otherwise, we might pick up a very large project and pull in the world,
2476
+ // causing an editor delay.
2477
+ if ( isNodeModulesDirectory ( canonicalSearchPath ) ) {
2478
+ break ;
2438
2479
}
2439
2480
2440
2481
const parentPath = asNormalizedPath ( getDirectoryPath ( searchPath ) ) ;
2441
2482
if ( parentPath === searchPath ) break ;
2442
2483
searchPath = parentPath ;
2443
- searchInDirectory = true ;
2484
+ searchTsconfig = searchJsconfig = true ;
2444
2485
}
2445
2486
while ( anySearchPathOk || isSearchPathInProjectRoot ( ) ) ;
2446
2487
@@ -2475,8 +2516,24 @@ export class ProjectService {
2475
2516
configFileName : NormalizedPath | undefined ,
2476
2517
) {
2477
2518
if ( ! this . openFiles . has ( info . path ) ) return ; // Dont cache for closed script infos
2478
- if ( isAncestorConfigFileInfo ( info ) ) return ; // Dont cache for ancestors
2479
- this . configFileForOpenFiles . set ( info . path , configFileName || false ) ;
2519
+ const config = configFileName || false ;
2520
+ if ( ! isAncestorConfigFileInfo ( info ) ) {
2521
+ // Set value for open script info
2522
+ this . configFileForOpenFiles . set ( info . path , config ) ;
2523
+ }
2524
+ else {
2525
+ // Need to set value for ancestor in ConfigFileMapForOpenFile
2526
+ let configFileForOpenFile = this . configFileForOpenFiles . get ( info . path ) ! ;
2527
+ if ( ! configFileForOpenFile || isString ( configFileForOpenFile ) ) {
2528
+ // We have value for open script info in cache, make a map with that as false key and set new vlaue
2529
+ this . configFileForOpenFiles . set (
2530
+ info . path ,
2531
+ configFileForOpenFile = new Map ( ) . set ( false , configFileForOpenFile ) ,
2532
+ ) ;
2533
+ }
2534
+ // Set value of for ancestor in the map
2535
+ configFileForOpenFile . set ( info . fileName , config ) ;
2536
+ }
2480
2537
}
2481
2538
2482
2539
/**
@@ -4191,7 +4248,8 @@ export class ProjectService {
4191
4248
function tryFindDefaultConfiguredProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
4192
4249
return isDefaultProject ( project ) ?
4193
4250
defaultProject :
4194
- tryFindDefaultConfiguredProjectFromReferences ( project ) ;
4251
+ ( tryFindDefaultConfiguredProjectFromReferences ( project ) ??
4252
+ tryFindDefaultConfiguredProjectFromAncestor ( project ) ) ;
4195
4253
}
4196
4254
4197
4255
function isDefaultProject ( project : ConfiguredProject ) : ConfiguredProject | undefined {
@@ -4221,6 +4279,19 @@ export class ProjectService {
4221
4279
reloadedProjects ,
4222
4280
) ;
4223
4281
}
4282
+
4283
+ function tryFindDefaultConfiguredProjectFromAncestor ( project : ConfiguredProject ) {
4284
+ return forEachAncestorProject ( // If not in referenced projects, try ancestors and its references
4285
+ info ,
4286
+ project ,
4287
+ tryFindDefaultConfiguredProject ,
4288
+ kind ,
4289
+ `Creating possible configured project for ${ info . fileName } to open` ,
4290
+ allowDeferredClosed ,
4291
+ reloadedProjects ,
4292
+ /*searchOnlyPotentialSolution*/ false ,
4293
+ ) ;
4294
+ }
4224
4295
}
4225
4296
4226
4297
/**
@@ -4265,6 +4336,7 @@ export class ProjectService {
4265
4336
`Creating project possibly referencing default composite project ${ defaultProject . getProjectName ( ) } of open file ${ info . fileName } ` ,
4266
4337
allowDeferredClosed ,
4267
4338
reloadedProjects ,
4339
+ /*searchOnlyPotentialSolution*/ true ,
4268
4340
delayReloadedConfiguredProjects ,
4269
4341
) ;
4270
4342
}
0 commit comments