From f3b0a2de0618f1d528430ebad1e1aa9a7f1ed866 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 27 Jul 2018 12:21:30 -0700 Subject: [PATCH 1/2] Move the original location getter to ProjectService. This is in anticipation of work needed to keep original projects alive --- src/server/editorServices.ts | 67 ++++++++++++------- src/server/project.ts | 2 +- src/server/session.ts | 25 +++---- .../unittests/tsserverProjectSystem.ts | 2 +- 4 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index f0a78e8da4b..00905a28579 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -326,6 +326,13 @@ namespace ts.server { syntaxOnly?: boolean; } + interface OriginalFileInfo { fileName: NormalizedPath; path: Path; } + type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo; + + function isOpenScriptInfo(infoOrFileName: OpenScriptInfoOrClosedFileInfo): infoOrFileName is ScriptInfo { + return !!(infoOrFileName as ScriptInfo).containingProjects; + } + function getDetailWatchInfo(watchType: WatchType, project: Project | undefined) { return `Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`; } @@ -1044,12 +1051,12 @@ namespace ts.server { } } - private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) { + private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: OpenScriptInfoOrClosedFileInfo) { let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo) { // By default the info would get impacted by presence of config file since its in the detection path // Only adding the info as a root to inferred project will need the existence to be watched by file watcher - if (!configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { + if (isOpenScriptInfo(info) && !configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { configFileExistenceInfo.openFilesImpactedByConfigFile.set(info.path, false); this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd); } @@ -1066,9 +1073,11 @@ namespace ts.server { // Or the whole chain of config files for the roots of the inferred projects // Cache the host value of file exists and add the info to map of open files impacted by this config file - const openFilesImpactedByConfigFile = createMap(); - openFilesImpactedByConfigFile.set(info.path, false); const exists = this.host.fileExists(configFileName); + const openFilesImpactedByConfigFile = createMap(); + if (isOpenScriptInfo(info)) { + openFilesImpactedByConfigFile.set(info.path, false); + } configFileExistenceInfo = { exists, openFilesImpactedByConfigFile }; this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo); this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd); @@ -1180,7 +1189,7 @@ namespace ts.server { */ private stopWatchingConfigFilesForClosedScriptInfo(info: ScriptInfo) { Debug.assert(!info.isScriptOpen()); - this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => { + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo) { const infoIsRootOfInferredProject = configFileExistenceInfo.openFilesImpactedByConfigFile.get(info.path); @@ -1214,7 +1223,7 @@ namespace ts.server { /* @internal */ startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) { Debug.assert(info.isScriptOpen()); - this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => { + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (!configFileExistenceInfo) { // Create the cache @@ -1242,7 +1251,7 @@ namespace ts.server { */ /* @internal */ stopWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) { - this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => { + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo && configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { Debug.assert(info.isScriptOpen()); @@ -1265,12 +1274,12 @@ namespace ts.server { * The server must start searching from the directory containing * the newly opened file. */ - private forEachConfigFileLocation(info: ScriptInfo, infoShouldBeOpen: boolean, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) { + private forEachConfigFileLocation(info: OpenScriptInfoOrClosedFileInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) { if (this.syntaxOnly) { return undefined; } - Debug.assert(!infoShouldBeOpen || this.openFiles.has(info.path)); + Debug.assert(!isOpenScriptInfo(info) || this.openFiles.has(info.path)); const projectRootPath = this.openFiles.get(info.path); let searchPath = asNormalizedPath(getDirectoryPath(info.fileName)); @@ -1309,11 +1318,13 @@ namespace ts.server { * current directory (the directory in which tsc was invoked). * The server must start searching from the directory containing * the newly opened file. + * If script info is passed in, it is asserted to be open script info + * otherwise just file name */ - private getConfigFileNameForFile(info: ScriptInfo, infoShouldBeOpen: boolean) { - if (infoShouldBeOpen) Debug.assert(info.isScriptOpen()); + private getConfigFileNameForFile(info: OpenScriptInfoOrClosedFileInfo) { + if (isOpenScriptInfo(info)) Debug.assert(info.isScriptOpen()); this.logger.info(`Search path: ${getDirectoryPath(info.fileName)}`); - const configFileName = this.forEachConfigFileLocation(info, infoShouldBeOpen, (configFileName, canonicalConfigFilePath) => + const configFileName = this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => this.configFileExists(configFileName, canonicalConfigFilePath, info)); if (configFileName) { this.logger.info(`For info: ${info.fileName} :: Config file name: ${configFileName}`); @@ -2005,7 +2016,7 @@ namespace ts.server { // we first detect if there is already a configured project created for it: if so, // we re- read the tsconfig file content and update the project only if we havent already done so // otherwise we create a new one. - const configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true); + const configFileName = this.getConfigFileNameForFile(info); if (configFileName) { const project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { @@ -2093,17 +2104,25 @@ namespace ts.server { return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath ? toNormalizedPath(projectRootPath) : undefined); } - /** @internal */ - getProjectForFileWithoutOpening(fileName: NormalizedPath): { readonly scriptInfo: ScriptInfo, readonly projects: ReadonlyArray } | undefined { - const scriptInfo = this.filenameToScriptInfo.get(fileName) || - this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName, this.currentDirectory, /*fileContent*/ undefined, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined); - if (!scriptInfo) return undefined; - if (scriptInfo.containingProjects.length) { - return { scriptInfo, projects: scriptInfo.containingProjects }; + /*@internal*/ + getOriginalLocationEnsuringConfiguredProject(project: Project, location: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined { + const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location); + if (!originalLocation) return undefined; + + const { fileName } = originalLocation; + const originalScriptInfo = this.getScriptInfo(fileName); + if (originalScriptInfo && originalScriptInfo.containingProjects.length) { + return originalLocation; } - const configFileName = this.getConfigFileNameForFile(scriptInfo, /*infoShouldBeOpen*/ false); - const project = configFileName === undefined ? undefined : this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName); - return project && project.containsScriptInfo(scriptInfo) ? { scriptInfo, projects: [project] } : undefined; + + const info: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) }; + const configFileName = this.getConfigFileNameForFile(info); + if (!configFileName) return undefined; + + const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName); + updateProjectIfDirty(configuredProject); + + return configuredProject.containsFile(info.fileName) ? originalLocation : undefined; } /** @internal */ @@ -2128,7 +2147,7 @@ namespace ts.server { this.openFiles.set(info.path, projectRootPath); let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info); if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization - configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true); + configFileName = this.getConfigFileNameForFile(info); if (configFileName) { project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { diff --git a/src/server/project.ts b/src/server/project.ts index 08b08eae04c..decb31097a9 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -711,7 +711,7 @@ namespace ts.server { } containsFile(filename: NormalizedPath, requireOpen?: boolean): boolean { - const info = this.projectService.getScriptInfoForPath(this.toPath(filename)); + const info = this.projectService.getScriptInfoForNormalizedPath(filename); if (info && (info.isScriptOpen() || !requireOpen)) { return this.containsScriptInfo(info); } diff --git a/src/server/session.ts b/src/server/session.ts index 7dccda3c82c..d2fefb16430 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -427,23 +427,20 @@ namespace ts.server { if (projectAndLocation.project.getCancellationToken().isCancellationRequested()) return undefined; // Skip rest of toDo if cancelled cb(projectAndLocation, (project, location) => { seenProjects.set(projectAndLocation.project.projectName, true); - const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location); + const originalLocation = projectService.getOriginalLocationEnsuringConfiguredProject(project, location); if (!originalLocation) return false; - const originalProjectAndScriptInfo = projectService.getProjectForFileWithoutOpening(toNormalizedPath(originalLocation.fileName)); - if (!originalProjectAndScriptInfo) return false; - if (originalProjectAndScriptInfo) { - toDo = toDo || []; + const originalScriptInfo = projectService.getScriptInfo(originalLocation.fileName)!; + toDo = toDo || []; - for (const project of originalProjectAndScriptInfo.projects) { - addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects); - } - const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalProjectAndScriptInfo.scriptInfo); - if (symlinkedProjectsMap) { - symlinkedProjectsMap.forEach((symlinkedProjects) => { - for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects); - }); - } + for (const project of originalScriptInfo.containingProjects) { + addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects); + } + const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalScriptInfo); + if (symlinkedProjectsMap) { + symlinkedProjectsMap.forEach((symlinkedProjects) => { + for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects); + }); } return true; }); diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index 6d39ecb76d1..d23c2f288f3 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -9458,7 +9458,7 @@ export function Test2() { }); }); - describe("Untitled files", () => { + describe("tsserverProjectSystem Untitled files", () => { it("Can convert positions to locations", () => { const aTs: File = { path: "/proj/a.ts", content: "" }; const tsconfig: File = { path: "/proj/tsconfig.json", content: "{}" }; From d65cb21fcde8f365688db4196c5d89d7b6902c11 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 27 Jul 2018 15:35:13 -0700 Subject: [PATCH 2/2] Keep the original configured project alive created during findAllReferences --- src/server/editorServices.ts | 62 +++++++-- src/server/project.ts | 3 + .../unittests/tsserverProjectSystem.ts | 124 ++++++++++++++---- 3 files changed, 148 insertions(+), 41 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 00905a28579..3ea6b45047c 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -707,7 +707,7 @@ namespace ts.server { /* @internal */ private forEachProject(cb: (project: Project) => void) { - for (const p of this.inferredProjects) cb(p); + this.inferredProjects.forEach(cb); this.configuredProjects.forEach(cb); this.externalProjects.forEach(cb); } @@ -2110,19 +2110,34 @@ namespace ts.server { if (!originalLocation) return undefined; const { fileName } = originalLocation; - const originalScriptInfo = this.getScriptInfo(fileName); - if (originalScriptInfo && originalScriptInfo.containingProjects.length) { - return originalLocation; - } + if (!this.getScriptInfo(fileName) && !this.host.fileExists(fileName)) return undefined; - const info: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) }; - const configFileName = this.getConfigFileNameForFile(info); + const originalFileInfo: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) }; + const configFileName = this.getConfigFileNameForFile(originalFileInfo); if (!configFileName) return undefined; const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName); updateProjectIfDirty(configuredProject); + // Keep this configured project as referenced from project + addOriginalConfiguredProject(configuredProject); - return configuredProject.containsFile(info.fileName) ? originalLocation : undefined; + const originalScriptInfo = this.getScriptInfo(fileName); + if (!originalScriptInfo || !originalScriptInfo.containingProjects.length) return undefined; + + // Add configured projects as referenced + originalScriptInfo.containingProjects.forEach(project => { + if (project.projectKind === ProjectKind.Configured) { + addOriginalConfiguredProject(project as ConfiguredProject); + } + }); + return originalLocation; + + function addOriginalConfiguredProject(originalProject: ConfiguredProject) { + if (!project.originalConfiguredProjects) { + project.originalConfiguredProjects = createMap(); + } + project.originalConfiguredProjects.set(originalProject.canonicalConfigFilePath, true); + } } /** @internal */ @@ -2185,14 +2200,9 @@ namespace ts.server { } Debug.assert(!info.isOrphan()); - // Remove the configured projects that have zero references from open files. // This was postponed from closeOpenFile to after opening next file, // so that we can reuse the project if we need to right away - this.configuredProjects.forEach(project => { - if (!project.hasOpenRef()) { - this.removeProject(project); - } - }); + this.removeOrphanConfiguredProjects(); // Remove orphan inferred projects now that we have reused projects // We need to create a duplicate because we cant guarantee order after removal @@ -2220,6 +2230,30 @@ namespace ts.server { return { configFileName, configFileErrors }; } + private removeOrphanConfiguredProjects() { + const toRemoveConfiguredProjects = cloneMap(this.configuredProjects); + + // Do not remove configured projects that are used as original projects of other + this.inferredProjects.forEach(markOriginalProjectsAsUsed); + this.externalProjects.forEach(markOriginalProjectsAsUsed); + this.configuredProjects.forEach(project => { + // If project has open ref (there are more than zero references from external project/open file), keep it alive as well as any project it references + if (project.hasOpenRef()) { + toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath); + markOriginalProjectsAsUsed(project); + } + }); + + // Remove all the non marked projects + toRemoveConfiguredProjects.forEach(project => this.removeProject(project)); + + function markOriginalProjectsAsUsed(project: Project) { + if (!project.isOrphan() && project.originalConfiguredProjects) { + project.originalConfiguredProjects.forEach((_value, configuredProjectPath) => toRemoveConfiguredProjects.delete(configuredProjectPath)); + } + } + } + private telemetryOnOpenFile(scriptInfo: ScriptInfo): void { if (this.syntaxOnly || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) { return; diff --git a/src/server/project.ts b/src/server/project.ts index decb31097a9..d26ee5d79e5 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -158,6 +158,9 @@ namespace ts.server { /*@internal*/ typingFiles: SortedReadonlyArray = emptyArray; + /*@internal*/ + originalConfiguredProjects: Map | undefined; + private readonly cancellationToken: ThrottledCancellationToken; public isNonTsProject() { diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index d23c2f288f3..1eab4e38aeb 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -9110,7 +9110,7 @@ export function Test2() { }); }); - function makeSampleProjects() { + describe("tsserverProjectSystem project references", () => { const aTs: File = { path: "/a/a.ts", content: "export function fnA() {}\nexport interface IfaceA {}\nexport const instanceA: IfaceA = {};", @@ -9166,62 +9166,121 @@ export function Test2() { content: `export declare function fnB(): void;\n//# source${""}MappingURL=b.d.ts.map`, }; + const dummyFile: File = { + path: "/dummy/dummy.ts", + content: "let a = 10;" + }; + const userTs: File = { path: "/user/user.ts", content: 'import { fnA, instanceA } from "../a/bin/a";\nimport { fnB } from "../b/bin/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }', }; - const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs]); - const session = createSession(host); + function makeSampleProjects() { + const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs, dummyFile]); + const session = createSession(host); - checkDeclarationFiles(aTs, session, [aDtsMap, aDts]); - checkDeclarationFiles(bTs, session, [bDtsMap, bDts]); + checkDeclarationFiles(aTs, session, [aDtsMap, aDts]); + checkDeclarationFiles(bTs, session, [bDtsMap, bDts]); - // Testing what happens if we delete the original sources. - host.removeFile(bTs.path); + // Testing what happens if we delete the original sources. + host.removeFile(bTs.path); - openFilesForSession([userTs], session); + openFilesForSession([userTs], session); + const service = session.getProjectService(); + checkNumberOfProjects(service, { inferredProjects: 1 }); + return session; + } - return { session, aTs, bDts, userTs }; - } + function verifyInferredProjectUnchanged(session: TestSession) { + checkProjectActualFiles(session.getProjectService().inferredProjects[0], [userTs.path, aDts.path, bDts.path]); + } + + function verifyDummyProject(session: TestSession) { + checkProjectActualFiles(session.getProjectService().inferredProjects[0], [dummyFile.path]); + } + + function verifyOnlyOrphanInferredProject(session: TestSession) { + openFilesForSession([dummyFile], session); + checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1 }); + verifyDummyProject(session); + } + + function verifySingleInferredProject(session: TestSession) { + checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1 }); + verifyInferredProjectUnchanged(session); + + // Close user file should close all the projects after opening dummy file + closeFilesForSession([userTs], session); + verifyOnlyOrphanInferredProject(session); + } + + function verifyATsConfigProject(session: TestSession) { + checkProjectActualFiles(session.getProjectService().configuredProjects.get(aTsconfig.path)!, [aTs.path, aTsconfig.path]); + } + + function verifyATsConfigOriginalProject(session: TestSession) { + checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 }); + verifyInferredProjectUnchanged(session); + verifyATsConfigProject(session); + // Close user file should close all the projects + closeFilesForSession([userTs], session); + verifyOnlyOrphanInferredProject(session); + } + + function verifyATsConfigWhenOpened(session: TestSession) { + checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 }); + verifyInferredProjectUnchanged(session); + verifyATsConfigProject(session); + + closeFilesForSession([userTs], session); + openFilesForSession([dummyFile], session); + checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 }); + verifyDummyProject(session); + verifyATsConfigProject(session); // ATsConfig should still be alive + } - describe("tsserverProjectSystem project references", () => { it("goToDefinition", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.Definition, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "fnA")]); + verifySingleInferredProject(session); }); it("getDefinitionAndBoundSpan", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.DefinitionAndBoundSpan, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual(response, { textSpan: protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }), definitions: [protocolFileSpanFromSubstring(aTs, "fnA")], }); + verifySingleInferredProject(session); }); it("goToType", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.TypeDefinition, protocolFileLocationFromSubstring(userTs, "instanceA")); assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "IfaceA")]); + verifySingleInferredProject(session); }); it("goToImplementation", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.Implementation, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "fnA")]); + verifySingleInferredProject(session); }); it("goToDefinition -- target does not exist", () => { - const { session, bDts, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, CommandNames.Definition, protocolFileLocationFromSubstring(userTs, "fnB()")); // bTs does not exist, so stick with bDts assert.deepEqual(response, [protocolFileSpanFromSubstring(bDts, "fnB")]); + verifySingleInferredProject(session); }); it("navigateTo", () => { - const { session, aTs, bDts, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, CommandNames.Navto, { file: userTs.path, searchValue: "fn" }); assert.deepEqual | undefined>(response, [ { @@ -9249,6 +9308,8 @@ export function Test2() { kindModifiers: "export", }, ]); + + verifyATsConfigOriginalProject(session); }); const referenceATs = (aTs: File): protocol.ReferencesResponseItem => makeReferenceItem(aTs, /*isDefinition*/ true, "fnA", "export function fnA() {}"); @@ -9258,7 +9319,7 @@ export function Test2() { ]; it("findAllReferences", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual(response, { @@ -9267,10 +9328,12 @@ export function Test2() { symbolStartOffset: protocolLocationFromSubstring(userTs.content, "fnA()").offset, symbolDisplayString: "(alias) fnA(): void\nimport fnA", }); + + verifyATsConfigOriginalProject(session); }); it("findAllReferences -- starting at definition", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); openFilesForSession([aTs], session); // If it's not opened, the reference isn't found. const response = executeSessionRequest(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(aTs, "fnA")); assert.deepEqual(response, { @@ -9279,10 +9342,11 @@ export function Test2() { symbolStartOffset: protocolLocationFromSubstring(aTs.content, "fnA").offset, symbolDisplayString: "function fnA(): void", }); + verifyATsConfigWhenOpened(session); }); it("findAllReferencesFull", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); interface ReferencesFullRequest extends protocol.FileLocationRequest { command: protocol.CommandTypes.ReferencesFull; } interface ReferencesFullResponse extends protocol.Response { body: ReadonlyArray; } @@ -9339,10 +9403,11 @@ export function Test2() { ], } ]); + verifyATsConfigOriginalProject(session); }); it("findAllReferences -- target does not exist", () => { - const { session, bDts, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(userTs, "fnB()")); assert.deepEqual(response, { @@ -9355,6 +9420,7 @@ export function Test2() { symbolStartOffset: protocolLocationFromSubstring(userTs.content, "fnB()").offset, symbolDisplayString: "(alias) fnB(): void\nimport fnB", }); + verifySingleInferredProject(session); }); const renameATs = (aTs: File): protocol.SpanGroup => ({ @@ -9370,7 +9436,7 @@ export function Test2() { }); it("renameLocations", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual(response, { info: { @@ -9383,10 +9449,11 @@ export function Test2() { }, locs: [renameUserTs(userTs), renameATs(aTs)], }); + verifyATsConfigOriginalProject(session); }); it("renameLocations -- starting at definition", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); openFilesForSession([aTs], session); // If it's not opened, the reference isn't found. const response = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "fnA")); assert.deepEqual(response, { @@ -9400,20 +9467,22 @@ export function Test2() { }, locs: [renameATs(aTs), renameUserTs(userTs)], }); + verifyATsConfigWhenOpened(session); }); it("renameLocationsFull", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.RenameLocationsFull, protocolFileLocationFromSubstring(userTs, "fnA()")); assert.deepEqual>(response, [ documentSpanFromSubstring(userTs, "fnA"), documentSpanFromSubstring(userTs, "fnA", { index: 1 }), documentSpanFromSubstring(aTs, "fnA"), ]); + verifyATsConfigOriginalProject(session); }); it("renameLocations -- target does not exist", () => { - const { session, bDts, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(userTs, "fnB()")); assert.deepEqual(response, { info: { @@ -9438,11 +9507,11 @@ export function Test2() { } ], }); - + verifySingleInferredProject(session); }); it("getEditsForFileRename", () => { - const { session, aTs, userTs } = makeSampleProjects(); + const session = makeSampleProjects(); const response = executeSessionRequest(session, protocol.CommandTypes.GetEditsForFileRename, { oldFilePath: aTs.path, newFilePath: "/a/aNew.ts", @@ -9455,6 +9524,7 @@ export function Test2() { ], }, ]); + verifySingleInferredProject(session); }); });