Skip to content

Commit 46d223d

Browse files
committed
Fixes to ensure getDefinitionAndBoundSpan works correctly when using composite projects
Project references need to be detached from the project when closing project In SourceMapDecoder handle when the redirected file to project reference is set as the output of the project Keep configured project alive if project it references has open ref Fixes #26164
1 parent 9df8831 commit 46d223d

File tree

5 files changed

+75
-6
lines changed

5 files changed

+75
-6
lines changed

src/compiler/program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2358,7 +2358,7 @@ namespace ts {
23582358
if (sourceFile === undefined) {
23592359
return undefined;
23602360
}
2361-
2361+
sourceFile.path = toPath(refPath);
23622362
const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
23632363
return { commandLine, sourceFile };
23642364
}

src/compiler/sourcemapDecoder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ namespace ts.sourcemaps {
100100
// Lookup file in program, if provided
101101
const path = toPath(fileName, location, host.getCanonicalFileName);
102102
const file = program && program.getSourceFile(path);
103-
if (!file) {
103+
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
104+
if (!file || file.resolvedPath !== path) {
104105
// Otherwise check the cache (which may hit disk)
105106
return fallbackCache.get(path);
106107
}
@@ -373,4 +374,4 @@ namespace ts.sourcemaps {
373374
encodedText.charCodeAt(pos) === CharacterCodes.comma ||
374375
encodedText.charCodeAt(pos) === CharacterCodes.semicolon);
375376
}
376-
}
377+
}

src/server/editorServices.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,6 +2243,20 @@ namespace ts.server {
22432243
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
22442244
markOriginalProjectsAsUsed(project);
22452245
}
2246+
else {
2247+
// If the configured project for project reference has more than zero references, keep it alive
2248+
const resolvedProjectReferences = project.getResolvedProjectReferences();
2249+
if (resolvedProjectReferences) {
2250+
for (const ref of resolvedProjectReferences) {
2251+
if (ref) {
2252+
const refProject = this.configuredProjects.get(ref.sourceFile.path);
2253+
if (refProject && refProject.hasOpenRef()) {
2254+
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
2255+
}
2256+
}
2257+
}
2258+
}
2259+
}
22462260
});
22472261

22482262
// Remove all the non marked projects

src/server/project.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,14 @@ namespace ts.server {
572572
for (const f of this.program.getSourceFiles()) {
573573
this.detachScriptInfoIfNotRoot(f.fileName);
574574
}
575+
const projectReferences = this.program.getProjectReferences();
576+
if (projectReferences) {
577+
for (const ref of projectReferences) {
578+
if (ref) {
579+
this.detachScriptInfoFromProject(ref.sourceFile.fileName);
580+
}
581+
}
582+
}
575583
}
576584
// Release external files
577585
forEach(this.externalFiles, externalFile => this.detachScriptInfoIfNotRoot(externalFile));
@@ -1367,6 +1375,12 @@ namespace ts.server {
13671375
this.projectReferences = refs;
13681376
}
13691377

1378+
/*@internal*/
1379+
getResolvedProjectReferences() {
1380+
const program = this.getCurrentProgram();
1381+
return program && program.getProjectReferences();
1382+
}
1383+
13701384
enablePlugins() {
13711385
const host = this.projectService.host;
13721386
const options = this.getCompilationSettings();

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9183,8 +9183,21 @@ export function Test2() {
91839183
content: 'import { fnA, instanceA } from "../a/bin/a";\nimport { fnB } from "../b/bin/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }',
91849184
};
91859185

9186-
function makeSampleProjects() {
9187-
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs, dummyFile]);
9186+
const userTsForConfigProject: File = {
9187+
path: "/user/user.ts",
9188+
content: 'import { fnA, instanceA } from "../a/a";\nimport { fnB } from "../b/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }',
9189+
};
9190+
9191+
const userTsconfig: File = {
9192+
path: "/user/tsconfig.json",
9193+
content: JSON.stringify({
9194+
file: ["user.ts"],
9195+
references: [{ path: "../a" }, { path: "../b" }]
9196+
})
9197+
};
9198+
9199+
function makeSampleProjects(addUserTsConfig?: boolean) {
9200+
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, ...(addUserTsConfig ? [userTsForConfigProject, userTsconfig] : [userTs]), dummyFile]);
91889201
const session = createSession(host);
91899202

91909203
checkDeclarationFiles(aTs, session, [aDtsMap, aDts]);
@@ -9195,7 +9208,7 @@ export function Test2() {
91959208

91969209
openFilesForSession([userTs], session);
91979210
const service = session.getProjectService();
9198-
checkNumberOfProjects(service, { inferredProjects: 1 });
9211+
checkNumberOfProjects(service, addUserTsConfig ? { configuredProjects: 1 } : { inferredProjects: 1 });
91999212
return session;
92009213
}
92019214

@@ -9247,6 +9260,10 @@ export function Test2() {
92479260
verifyATsConfigProject(session); // ATsConfig should still be alive
92489261
}
92499262

9263+
function verifyUserTsConfigProject(session: TestSession) {
9264+
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aDts.path, userTsconfig.path]);
9265+
}
9266+
92509267
it("goToDefinition", () => {
92519268
const session = makeSampleProjects();
92529269
const response = executeSessionRequest<protocol.DefinitionRequest, protocol.DefinitionResponse>(session, protocol.CommandTypes.Definition, protocolFileLocationFromSubstring(userTs, "fnA()"));
@@ -9264,6 +9281,29 @@ export function Test2() {
92649281
verifySingleInferredProject(session);
92659282
});
92669283

9284+
it("getDefinitionAndBoundSpan with file navigation", () => {
9285+
const session = makeSampleProjects(/*addUserTsConfig*/ true);
9286+
const response = executeSessionRequest<protocol.DefinitionAndBoundSpanRequest, protocol.DefinitionAndBoundSpanResponse>(session, protocol.CommandTypes.DefinitionAndBoundSpan, protocolFileLocationFromSubstring(userTs, "fnA()"));
9287+
assert.deepEqual(response, {
9288+
textSpan: protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }),
9289+
definitions: [protocolFileSpanFromSubstring(aTs, "fnA")],
9290+
});
9291+
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 }); debugger;
9292+
verifyUserTsConfigProject(session);
9293+
9294+
// Navigate to the definition
9295+
closeFilesForSession([userTs], session);
9296+
openFilesForSession([aTs], session);
9297+
9298+
// UserTs configured project should be alive
9299+
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 2 });
9300+
verifyUserTsConfigProject(session);
9301+
verifyATsConfigProject(session);
9302+
9303+
closeFilesForSession([aTs], session);
9304+
verifyOnlyOrphanInferredProject(session);
9305+
});
9306+
92679307
it("goToType", () => {
92689308
const session = makeSampleProjects();
92699309
const response = executeSessionRequest<protocol.TypeDefinitionRequest, protocol.TypeDefinitionResponse>(session, protocol.CommandTypes.TypeDefinition, protocolFileLocationFromSubstring(userTs, "instanceA"));

0 commit comments

Comments
 (0)