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
This commit is contained in:
Sheetal Nandi 2018-08-02 15:33:22 -07:00
parent 9df88316a2
commit 46d223dc1b
5 changed files with 75 additions and 6 deletions

View File

@ -2358,7 +2358,7 @@ namespace ts {
if (sourceFile === undefined) {
return undefined;
}
sourceFile.path = toPath(refPath);
const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
return { commandLine, sourceFile };
}

View File

@ -100,7 +100,8 @@ namespace ts.sourcemaps {
// Lookup file in program, if provided
const path = toPath(fileName, location, host.getCanonicalFileName);
const file = program && program.getSourceFile(path);
if (!file) {
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
if (!file || file.resolvedPath !== path) {
// Otherwise check the cache (which may hit disk)
return fallbackCache.get(path);
}
@ -373,4 +374,4 @@ namespace ts.sourcemaps {
encodedText.charCodeAt(pos) === CharacterCodes.comma ||
encodedText.charCodeAt(pos) === CharacterCodes.semicolon);
}
}
}

View File

@ -2243,6 +2243,20 @@ namespace ts.server {
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
markOriginalProjectsAsUsed(project);
}
else {
// If the configured project for project reference has more than zero references, keep it alive
const resolvedProjectReferences = project.getResolvedProjectReferences();
if (resolvedProjectReferences) {
for (const ref of resolvedProjectReferences) {
if (ref) {
const refProject = this.configuredProjects.get(ref.sourceFile.path);
if (refProject && refProject.hasOpenRef()) {
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
}
}
}
}
}
});
// Remove all the non marked projects

View File

@ -572,6 +572,14 @@ namespace ts.server {
for (const f of this.program.getSourceFiles()) {
this.detachScriptInfoIfNotRoot(f.fileName);
}
const projectReferences = this.program.getProjectReferences();
if (projectReferences) {
for (const ref of projectReferences) {
if (ref) {
this.detachScriptInfoFromProject(ref.sourceFile.fileName);
}
}
}
}
// Release external files
forEach(this.externalFiles, externalFile => this.detachScriptInfoIfNotRoot(externalFile));
@ -1367,6 +1375,12 @@ namespace ts.server {
this.projectReferences = refs;
}
/*@internal*/
getResolvedProjectReferences() {
const program = this.getCurrentProgram();
return program && program.getProjectReferences();
}
enablePlugins() {
const host = this.projectService.host;
const options = this.getCompilationSettings();

View File

@ -9183,8 +9183,21 @@ export function Test2() {
content: 'import { fnA, instanceA } from "../a/bin/a";\nimport { fnB } from "../b/bin/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }',
};
function makeSampleProjects() {
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs, dummyFile]);
const userTsForConfigProject: File = {
path: "/user/user.ts",
content: 'import { fnA, instanceA } from "../a/a";\nimport { fnB } from "../b/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }',
};
const userTsconfig: File = {
path: "/user/tsconfig.json",
content: JSON.stringify({
file: ["user.ts"],
references: [{ path: "../a" }, { path: "../b" }]
})
};
function makeSampleProjects(addUserTsConfig?: boolean) {
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, ...(addUserTsConfig ? [userTsForConfigProject, userTsconfig] : [userTs]), dummyFile]);
const session = createSession(host);
checkDeclarationFiles(aTs, session, [aDtsMap, aDts]);
@ -9195,7 +9208,7 @@ export function Test2() {
openFilesForSession([userTs], session);
const service = session.getProjectService();
checkNumberOfProjects(service, { inferredProjects: 1 });
checkNumberOfProjects(service, addUserTsConfig ? { configuredProjects: 1 } : { inferredProjects: 1 });
return session;
}
@ -9247,6 +9260,10 @@ export function Test2() {
verifyATsConfigProject(session); // ATsConfig should still be alive
}
function verifyUserTsConfigProject(session: TestSession) {
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aDts.path, userTsconfig.path]);
}
it("goToDefinition", () => {
const session = makeSampleProjects();
const response = executeSessionRequest<protocol.DefinitionRequest, protocol.DefinitionResponse>(session, protocol.CommandTypes.Definition, protocolFileLocationFromSubstring(userTs, "fnA()"));
@ -9264,6 +9281,29 @@ export function Test2() {
verifySingleInferredProject(session);
});
it("getDefinitionAndBoundSpan with file navigation", () => {
const session = makeSampleProjects(/*addUserTsConfig*/ true);
const response = executeSessionRequest<protocol.DefinitionAndBoundSpanRequest, protocol.DefinitionAndBoundSpanResponse>(session, protocol.CommandTypes.DefinitionAndBoundSpan, protocolFileLocationFromSubstring(userTs, "fnA()"));
assert.deepEqual(response, {
textSpan: protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }),
definitions: [protocolFileSpanFromSubstring(aTs, "fnA")],
});
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 }); debugger;
verifyUserTsConfigProject(session);
// Navigate to the definition
closeFilesForSession([userTs], session);
openFilesForSession([aTs], session);
// UserTs configured project should be alive
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 2 });
verifyUserTsConfigProject(session);
verifyATsConfigProject(session);
closeFilesForSession([aTs], session);
verifyOnlyOrphanInferredProject(session);
});
it("goToType", () => {
const session = makeSampleProjects();
const response = executeSessionRequest<protocol.TypeDefinitionRequest, protocol.TypeDefinitionResponse>(session, protocol.CommandTypes.TypeDefinition, protocolFileLocationFromSubstring(userTs, "instanceA"));