Skip to content

Commit a825d67

Browse files
committed
Load the ancestor projects to find default project for the file
1 parent ab24e12 commit a825d67

File tree

156 files changed

+3860
-2530
lines changed

Some content is hidden

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

156 files changed

+3860
-2530
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: 544 additions & 328 deletions
Large diffs are not rendered by default.

src/server/project.ts

Lines changed: 2 additions & 83 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

@@ -2751,9 +2748,6 @@ export class ConfiguredProject extends Project {
27512748
/** @internal */
27522749
canConfigFileJsonReportNoInputFiles = false;
27532750

2754-
/** Ref count to the project when opened from external project */
2755-
private externalProjectRefCount = 0;
2756-
27572751
private projectReferences: readonly ProjectReference[] | undefined;
27582752

27592753
/**
@@ -2858,8 +2852,7 @@ export class ConfiguredProject extends Project {
28582852
case ProgramUpdateLevel.Full:
28592853
this.openFileWatchTriggered.clear();
28602854
const reason = Debug.checkDefined(this.pendingUpdateReason);
2861-
this.pendingUpdateReason = undefined;
2862-
this.projectService.reloadConfiguredProject(this, reason, isInitialLoad, /*clearSemanticCache*/ false);
2855+
this.projectService.reloadConfiguredProject(this, reason, isInitialLoad);
28632856
result = true;
28642857
break;
28652858
default:
@@ -2970,91 +2963,17 @@ export class ConfiguredProject extends Project {
29702963
super.markAsDirty();
29712964
}
29722965

2973-
/** @internal */
2974-
addExternalProjectReference() {
2975-
this.externalProjectRefCount++;
2976-
}
2977-
2978-
/** @internal */
2979-
deleteExternalProjectReference() {
2980-
this.externalProjectRefCount--;
2981-
}
2982-
29832966
/** @internal */
29842967
isSolution() {
29852968
return this.getRootFilesMap().size === 0 &&
29862969
!this.canConfigFileJsonReportNoInputFiles;
29872970
}
29882971

2989-
/**
2990-
* Find the configured project from the project references in project which contains the info directly
2991-
*
2992-
* @internal
2993-
*/
2994-
getDefaultChildProjectFromProjectWithReferences(info: ScriptInfo) {
2995-
return forEachResolvedProjectReferenceProject(
2996-
this,
2997-
info.path,
2998-
child =>
2999-
projectContainsInfoDirectly(child, info) ?
3000-
child :
3001-
undefined,
3002-
ProjectReferenceProjectLoadKind.Find,
3003-
);
3004-
}
3005-
3006-
/**
3007-
* Returns true if the project is needed by any of the open script info/external project
3008-
*
3009-
* @internal
3010-
*/
3011-
hasOpenRef() {
3012-
if (!!this.externalProjectRefCount) {
3013-
return true;
3014-
}
3015-
3016-
// Closed project doesnt have any reference
3017-
if (this.isClosed()) {
3018-
return false;
3019-
}
3020-
3021-
const configFileExistenceInfo = this.projectService.configFileExistenceInfoCache.get(this.canonicalConfigFilePath)!;
3022-
if (this.deferredClose) return !!configFileExistenceInfo.openFilesImpactedByConfigFile?.size;
3023-
if (this.projectService.hasPendingProjectUpdate(this)) {
3024-
// If there is pending update for this project,
3025-
// we dont know if this project would be needed by any of the open files impacted by this config file
3026-
// In that case keep the project alive if there are open files impacted by this project
3027-
return !!configFileExistenceInfo.openFilesImpactedByConfigFile?.size;
3028-
}
3029-
3030-
// If there is no pending update for this project,
3031-
// We know exact set of open files that get impacted by this configured project as the files in the project
3032-
// The project is referenced only if open files impacted by this project are present in this project
3033-
return !!configFileExistenceInfo.openFilesImpactedByConfigFile && forEachEntry(
3034-
configFileExistenceInfo.openFilesImpactedByConfigFile,
3035-
(_value, infoPath) => {
3036-
const info = this.projectService.getScriptInfoForPath(infoPath)!;
3037-
return this.containsScriptInfo(info) ||
3038-
!!forEachResolvedProjectReferenceProject(
3039-
this,
3040-
info.path,
3041-
child => child.containsScriptInfo(info),
3042-
ProjectReferenceProjectLoadKind.Find,
3043-
);
3044-
},
3045-
) || false;
3046-
}
3047-
30482972
/** @internal */
30492973
override isOrphan(): boolean {
30502974
return !!this.deferredClose;
30512975
}
30522976

3053-
/** @internal */
3054-
hasExternalProjectRef() {
3055-
return !!this.externalProjectRefCount;
3056-
}
3057-
30582977
getEffectiveTypeRoots() {
30592978
return getEffectiveTypeRoots(this.getCompilationSettings(), this) || [];
30602979
}

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
@@ -3190,7 +3190,7 @@ export class Session<TMessage = string> implements EventSender {
31903190
return this.requiredResponse(response);
31913191
},
31923192
[protocol.CommandTypes.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
3193-
this.projectService.openExternalProject(request.arguments, /*print*/ true);
3193+
this.projectService.openExternalProject(request.arguments, /*cleanupAfter*/ true);
31943194
// TODO: GH#20447 report errors
31953195
return this.requiredResponse(/*response*/ true);
31963196
},
@@ -3200,7 +3200,7 @@ export class Session<TMessage = string> implements EventSender {
32003200
return this.requiredResponse(/*response*/ true);
32013201
},
32023202
[protocol.CommandTypes.CloseExternalProject]: (request: protocol.CloseExternalProjectRequest) => {
3203-
this.projectService.closeExternalProject(request.arguments.projectFileName, /*print*/ true);
3203+
this.projectService.closeExternalProject(request.arguments.projectFileName, /*cleanupAfter*/ true);
32043204
// TODO: GH#20447 report errors
32053205
return this.requiredResponse(/*response*/ true);
32063206
},

src/testRunner/unittests/tsserver/projects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ describe("unittests:: tsserver:: projects::", () => {
177177
};
178178
session.host.baselineHost("Before request");
179179
session.logger.info(`request:${ts.server.stringifyIndented(request)}`);
180-
session.getProjectService().openExternalProject(request.arguments, /*print*/ true);
180+
session.getProjectService().openExternalProject(request.arguments, /*cleanupAfter*/ true);
181181
session.host.baselineHost("After request");
182182
baselineTsserverLogs("projects", "external project including config file", session);
183183
});

tests/baselines/reference/api/typescript.d.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,8 +2872,6 @@ declare namespace ts {
28722872
*/
28732873
class ConfiguredProject extends Project {
28742874
readonly canonicalConfigFilePath: NormalizedPath;
2875-
/** Ref count to the project when opened from external project */
2876-
private externalProjectRefCount;
28772875
private projectReferences;
28782876
/**
28792877
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
@@ -3135,6 +3133,7 @@ declare namespace ts {
31353133
private safelist;
31363134
private readonly legacySafelist;
31373135
private pendingProjectUpdates;
3136+
private pendingOpenFileProjectUpdates?;
31383137
readonly currentDirectory: NormalizedPath;
31393138
readonly toCanonicalFileName: (f: string) => string;
31403139
readonly host: ServerHost;
@@ -3185,13 +3184,6 @@ declare namespace ts {
31853184
private delayUpdateSourceInfoProjects;
31863185
private delayUpdateProjectsOfScriptInfoPath;
31873186
private handleDeletedFile;
3188-
/**
3189-
* This function goes through all the openFiles and tries to file the config file for them.
3190-
* If the config file is found and it refers to existing project, it schedules the reload it for reload
3191-
* If there is no existing project it just opens the configured project for the config file
3192-
* shouldReloadProjectFor provides a way to filter out files to reload configured project for
3193-
*/
3194-
private delayReloadConfiguredProjectsForFile;
31953187
private removeProject;
31963188
private assignOrphanScriptInfosToInferredProject;
31973189
/**
@@ -3218,6 +3210,8 @@ declare namespace ts {
32183210
* the newly opened file.
32193211
*/
32203212
private forEachConfigFileLocation;
3213+
private getConfigFileNameForFileFromCache;
3214+
private setConfigFileNameForFileInCache;
32213215
/**
32223216
* This function tries to search for a tsconfig.json for the given file.
32233217
* This is different from the method the compiler uses because
@@ -3269,12 +3263,6 @@ declare namespace ts {
32693263
* This does not reload contents of open files from disk. But we could do that if needed
32703264
*/
32713265
reloadProjects(): void;
3272-
/**
3273-
* This function goes through all the openFiles and tries to file the config file for them.
3274-
* If the config file is found and it refers to existing project, it reloads it either immediately
3275-
* If there is no existing project it just opens the configured project for the config file
3276-
*/
3277-
private reloadConfiguredProjectForFiles;
32783266
/**
32793267
* Remove the root of inferred project if script info is part of another project
32803268
*/
@@ -3295,12 +3283,14 @@ declare namespace ts {
32953283
openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult;
32963284
private findExternalProjectContainingOpenScriptInfo;
32973285
private getOrCreateOpenScriptInfo;
3286+
private tryFindDefaultConfiguredProjectForOpenScriptInfo;
3287+
private tryFindDefaultConfiguredProjectAndLoadAncestorsForOpenScriptInfo;
32983288
private assignProjectToOpenedScriptInfo;
3299-
private createAncestorProjects;
3289+
private forEachAncestorProject;
33003290
private ensureProjectChildren;
3301-
private cleanupAfterOpeningFile;
3291+
private cleanupConfiguredProjects;
3292+
private cleanupProjectsAndScriptInfos;
33023293
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult;
3303-
private removeOrphanConfiguredProjects;
33043294
private removeOrphanScriptInfos;
33053295
private telemetryOnOpenFile;
33063296
/**
@@ -3309,7 +3299,6 @@ declare namespace ts {
33093299
*/
33103300
closeClientFile(uncheckedFileName: string): void;
33113301
private collectChanges;
3312-
private closeConfiguredProjectReferencedFromExternalProject;
33133302
closeExternalProject(uncheckedFileName: string): void;
33143303
openExternalProjects(projects: protocol.ExternalProject[]): void;
33153304
/** Makes a filename safe to insert in a RegExp */

tests/baselines/reference/tsserver/configFileSearch/tsconfig-for-the-file-does-not-exist.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,11 @@ Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*
110110
Info seq [hh:mm:ss:mss] Elapsed:: *ms FileWatcher:: Triggered with /a/b/projects/project/tsconfig.json 0:: WatchInfo: /a/b/projects/project/tsconfig.json 2000 undefined WatchType: Config file for the inferred project root
111111
Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /a/b/projects/project/tsconfig.json 0:: WatchInfo: /a/b/projects/project/tsconfig.json 2000 undefined WatchType: Config file for the inferred project root
112112
Info seq [hh:mm:ss:mss] getConfigFileNameForFile:: File: /a/b/projects/project/src/index.ts ProjectRootPath: /a/b/projects/proj:: Result: /a/b/projects/project/tsconfig.json
113-
Info seq [hh:mm:ss:mss] Scheduled: /a/b/projects/project/tsconfig.json, Cancelled earlier one
114113
Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
115114
Info seq [hh:mm:ss:mss] Elapsed:: *ms FileWatcher:: Triggered with /a/b/projects/project/tsconfig.json 0:: WatchInfo: /a/b/projects/project/tsconfig.json 2000 undefined WatchType: Config file for the inferred project root
116115
Before running Timeout callback:: count: 2
117-
3: /a/b/projects/project/tsconfig.json
118-
4: *ensureProjectForOpenFiles*
116+
1: /a/b/projects/project/tsconfig.json
117+
3: *ensureProjectForOpenFiles*
119118
//// [/a/b/projects/project/tsconfig.json]
120119
{}
121120

@@ -145,8 +144,8 @@ FsWatches::
145144
{}
146145

147146
Timeout callback:: count: 2
148-
3: /a/b/projects/project/tsconfig.json *new*
149-
4: *ensureProjectForOpenFiles* *new*
147+
1: /a/b/projects/project/tsconfig.json *new*
148+
3: *ensureProjectForOpenFiles* *new*
150149

151150
Projects::
152151
/a/b/projects/project/tsconfig.json (Configured) *new*
@@ -364,11 +363,11 @@ Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /a/b/projects/project
364363
Info seq [hh:mm:ss:mss] Project: /a/b/projects/project/tsconfig.json Detected file add/remove of non supported extension: /a/b/projects/project/tsconfig.json
365364
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /a/b/projects/project/tsconfig.json :: WatchInfo: /a/b/projects/project 1 undefined Config: /a/b/projects/project/tsconfig.json WatchType: Wild card directory
366365
Before running Timeout callback:: count: 1
367-
5: *ensureProjectForOpenFiles*
366+
4: *ensureProjectForOpenFiles*
368367
//// [/a/b/projects/project/tsconfig.json] deleted
369368

370369
Timeout callback:: count: 1
371-
5: *ensureProjectForOpenFiles* *new*
370+
4: *ensureProjectForOpenFiles* *new*
372371

373372
Projects::
374373
/a/b/projects/project/tsconfig.json (Configured) *changed*

0 commit comments

Comments
 (0)