Skip to content

Commit b1878e6

Browse files
authored
Merge pull request #26173 from Microsoft/compositeProjectGoToDefinition
Fixes to ensure getDefinitionAndBoundSpan works correctly when using composite projects
2 parents 9df8831 + 46d223d commit b1878e6

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)