Skip to content

Commit fadc83b

Browse files
authored
Handle config file change and default project management (#58486)
1 parent 017c1e0 commit fadc83b

File tree

76 files changed

+18382
-1245
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+18382
-1245
lines changed

src/harness/projectServiceStateLogger.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ export function patchServiceForStateBaseline(service: ProjectService) {
104104
function baselineProjects(currentMappers: Set<DocumentPositionMapper>) {
105105
const autoImportProviderProjects = [] as AutoImportProviderProject[];
106106
const auxiliaryProjects = [] as AuxiliaryProject[];
107-
const orphanConfiguredProjects = service.getOrphanConfiguredProjects(/*toRetainConfiguredProjects*/ undefined);
107+
const orphanConfiguredProjects = service.getOrphanConfiguredProjects(
108+
/*toRetainConfiguredProjects*/ undefined,
109+
/*openFilesWithRetainedConfiguredProject*/ undefined,
110+
/*externalProjectsRetainingConfiguredProjects*/ undefined,
111+
);
108112
const noOpenRef = (project: Project) => isConfiguredProject(project) && (project.isClosed() || orphanConfiguredProjects.has(project));
109113
return baselineState(
110114
[service.externalProjects, service.configuredProjects, service.inferredProjects, autoImportProviderProjects, auxiliaryProjects],

src/server/editorServices.ts

Lines changed: 744 additions & 482 deletions
Large diffs are not rendered by default.

src/server/project.ts

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,12 @@ import {
141141
emptyArray,
142142
Errors,
143143
FileStats,
144-
forEachResolvedProjectReferenceProject,
145144
LogLevel,
146145
ModuleImportResult,
147146
Msg,
148147
NormalizedPath,
149148
PackageJsonWatcher,
150-
projectContainsInfoDirectly,
151149
ProjectOptions,
152-
ProjectReferenceProjectLoadKind,
153150
ProjectService,
154151
ScriptInfo,
155152
ServerHost,
@@ -2211,7 +2208,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
22112208
private isDefaultProjectForOpenFiles(): boolean {
22122209
return !!forEachEntry(
22132210
this.projectService.openFiles,
2214-
(_, fileName) => this.projectService.tryGetDefaultProjectForFile(toNormalizedPath(fileName)) === this,
2211+
(_projectRootPath, path) => this.projectService.tryGetDefaultProjectForFile(this.projectService.getScriptInfoForPath(path)!) === this,
22152212
);
22162213
}
22172214

@@ -2388,7 +2385,7 @@ export class InferredProject extends Project {
23882385
}
23892386

23902387
override removeRoot(info: ScriptInfo) {
2391-
this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info);
2388+
this.projectService.stopWatchingConfigFilesForScriptInfo(info);
23922389
super.removeRoot(info);
23932390
// Delay toggling to isJsInferredProject = false till we actually need it again
23942391
if (!this.isOrphan() && this._isJsInferredProject && info.isJavaScript()) {
@@ -2412,7 +2409,7 @@ export class InferredProject extends Project {
24122409
}
24132410

24142411
override close() {
2415-
forEach(this.getRootScriptInfos(), info => this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info));
2412+
forEach(this.getRootScriptInfos(), info => this.projectService.stopWatchingConfigFilesForScriptInfo(info));
24162413
super.close();
24172414
}
24182415

@@ -2763,9 +2760,6 @@ export class ConfiguredProject extends Project {
27632760
/** @internal */
27642761
canConfigFileJsonReportNoInputFiles = false;
27652762

2766-
/** Ref count to the project when opened from external project */
2767-
private externalProjectRefCount = 0;
2768-
27692763
private projectReferences: readonly ProjectReference[] | undefined;
27702764

27712765
/**
@@ -2873,8 +2867,7 @@ export class ConfiguredProject extends Project {
28732867
case ProgramUpdateLevel.Full:
28742868
this.openFileWatchTriggered.clear();
28752869
const reason = Debug.checkDefined(this.pendingUpdateReason);
2876-
this.pendingUpdateReason = undefined;
2877-
this.projectService.reloadConfiguredProject(this, reason, /*clearSemanticCache*/ false);
2870+
this.projectService.reloadConfiguredProject(this, reason);
28782871
result = true;
28792872
break;
28802873
default:
@@ -2998,91 +2991,17 @@ export class ConfiguredProject extends Project {
29982991
super.markAsDirty();
29992992
}
30002993

3001-
/** @internal */
3002-
addExternalProjectReference() {
3003-
this.externalProjectRefCount++;
3004-
}
3005-
3006-
/** @internal */
3007-
deleteExternalProjectReference() {
3008-
this.externalProjectRefCount--;
3009-
}
3010-
30112994
/** @internal */
30122995
isSolution() {
30132996
return this.getRootFilesMap().size === 0 &&
30142997
!this.canConfigFileJsonReportNoInputFiles;
30152998
}
30162999

3017-
/**
3018-
* Find the configured project from the project references in project which contains the info directly
3019-
*
3020-
* @internal
3021-
*/
3022-
getDefaultChildProjectFromProjectWithReferences(info: ScriptInfo) {
3023-
return forEachResolvedProjectReferenceProject(
3024-
this,
3025-
info.path,
3026-
child =>
3027-
projectContainsInfoDirectly(child, info) ?
3028-
child :
3029-
undefined,
3030-
ProjectReferenceProjectLoadKind.Find,
3031-
);
3032-
}
3033-
3034-
/**
3035-
* Returns true if the project is needed by any of the open script info/external project
3036-
*
3037-
* @internal
3038-
*/
3039-
hasOpenRef() {
3040-
if (!!this.externalProjectRefCount) {
3041-
return true;
3042-
}
3043-
3044-
// Closed project doesnt have any reference
3045-
if (this.isClosed()) {
3046-
return false;
3047-
}
3048-
3049-
const configFileExistenceInfo = this.projectService.configFileExistenceInfoCache.get(this.canonicalConfigFilePath)!;
3050-
if (this.deferredClose) return !!configFileExistenceInfo.openFilesImpactedByConfigFile?.size;
3051-
if (this.projectService.hasPendingProjectUpdate(this)) {
3052-
// If there is pending update for this project,
3053-
// we dont know if this project would be needed by any of the open files impacted by this config file
3054-
// In that case keep the project alive if there are open files impacted by this project
3055-
return !!configFileExistenceInfo.openFilesImpactedByConfigFile?.size;
3056-
}
3057-
3058-
// If there is no pending update for this project,
3059-
// We know exact set of open files that get impacted by this configured project as the files in the project
3060-
// The project is referenced only if open files impacted by this project are present in this project
3061-
return !!configFileExistenceInfo.openFilesImpactedByConfigFile && forEachEntry(
3062-
configFileExistenceInfo.openFilesImpactedByConfigFile,
3063-
(_value, infoPath) => {
3064-
const info = this.projectService.getScriptInfoForPath(infoPath)!;
3065-
return this.containsScriptInfo(info) ||
3066-
!!forEachResolvedProjectReferenceProject(
3067-
this,
3068-
info.path,
3069-
child => child.containsScriptInfo(info),
3070-
ProjectReferenceProjectLoadKind.Find,
3071-
);
3072-
},
3073-
) || false;
3074-
}
3075-
30763000
/** @internal */
30773001
override isOrphan(): boolean {
30783002
return !!this.deferredClose;
30793003
}
30803004

3081-
/** @internal */
3082-
hasExternalProjectRef() {
3083-
return !!this.externalProjectRefCount;
3084-
}
3085-
30863005
getEffectiveTypeRoots() {
30873006
return getEffectiveTypeRoots(this.getCompilationSettings(), this) || [];
30883007
}

src/server/scriptInfo.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import {
3939
AbsolutePositionAndLineText,
4040
ConfiguredProject,
4141
Errors,
42-
ExternalProject,
4342
InferredProject,
4443
isBackgroundProject,
4544
isConfiguredProject,
@@ -580,18 +579,16 @@ export class ScriptInfo {
580579
case 0:
581580
return Errors.ThrowNoProject();
582581
case 1:
583-
return ensurePrimaryProjectKind(
584-
!isProjectDeferredClose(this.containingProjects[0]) ?
585-
this.containingProjects[0] : undefined,
586-
);
582+
return isProjectDeferredClose(this.containingProjects[0]) || isBackgroundProject(this.containingProjects[0]) ?
583+
Errors.ThrowNoProject() :
584+
this.containingProjects[0];
587585
default:
588586
// If this file belongs to multiple projects, below is the order in which default project is used
587+
// - first external project
589588
// - for open script info, its default configured project during opening is default if info is part of it
590589
// - first configured project of which script info is not a source of project reference redirect
591590
// - first configured project
592-
// - first external project
593591
// - first inferred project
594-
let firstExternalProject: ExternalProject | undefined;
595592
let firstConfiguredProject: ConfiguredProject | undefined;
596593
let firstInferredProject: InferredProject | undefined;
597594
let firstNonSourceOfProjectReferenceRedirect: ConfiguredProject | undefined;
@@ -614,20 +611,17 @@ export class ScriptInfo {
614611
}
615612
if (!firstConfiguredProject) firstConfiguredProject = project;
616613
}
617-
else if (!firstExternalProject && isExternalProject(project)) {
618-
firstExternalProject = project;
614+
else if (isExternalProject(project)) {
615+
return project;
619616
}
620617
else if (!firstInferredProject && isInferredProject(project)) {
621618
firstInferredProject = project;
622619
}
623620
}
624-
return ensurePrimaryProjectKind(
625-
defaultConfiguredProject ||
626-
firstNonSourceOfProjectReferenceRedirect ||
627-
firstConfiguredProject ||
628-
firstExternalProject ||
629-
firstInferredProject,
630-
);
621+
return (defaultConfiguredProject ||
622+
firstNonSourceOfProjectReferenceRedirect ||
623+
firstConfiguredProject ||
624+
firstInferredProject) ?? Errors.ThrowNoProject();
631625
}
632626
}
633627

@@ -742,18 +736,6 @@ export class ScriptInfo {
742736
}
743737
}
744738

745-
/**
746-
* Throws an error if `project` is an AutoImportProvider or AuxiliaryProject,
747-
* which are used in the background by other Projects and should never be
748-
* reported as the default project for a ScriptInfo.
749-
*/
750-
function ensurePrimaryProjectKind(project: Project | undefined) {
751-
if (!project || isBackgroundProject(project)) {
752-
return Errors.ThrowNoProject();
753-
}
754-
return project;
755-
}
756-
757739
function failIfInvalidPosition(position: number) {
758740
Debug.assert(typeof position === "number", `Expected position ${position} to be a number.`);
759741
Debug.assert(position >= 0, `Expected position to be non-negative.`);

src/server/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,7 +3214,7 @@ export class Session<TMessage = string> implements EventSender {
32143214
return this.requiredResponse(response);
32153215
},
32163216
[protocol.CommandTypes.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
3217-
this.projectService.openExternalProject(request.arguments, /*print*/ true);
3217+
this.projectService.openExternalProject(request.arguments, /*cleanupAfter*/ true);
32183218
// TODO: GH#20447 report errors
32193219
return this.requiredResponse(/*response*/ true);
32203220
},
@@ -3224,7 +3224,7 @@ export class Session<TMessage = string> implements EventSender {
32243224
return this.requiredResponse(/*response*/ true);
32253225
},
32263226
[protocol.CommandTypes.CloseExternalProject]: (request: protocol.CloseExternalProjectRequest) => {
3227-
this.projectService.closeExternalProject(request.arguments.projectFileName, /*print*/ true);
3227+
this.projectService.closeExternalProject(request.arguments.projectFileName, /*cleanupAfter*/ true);
32283228
// TODO: GH#20447 report errors
32293229
return this.requiredResponse(/*response*/ true);
32303230
},

0 commit comments

Comments
 (0)