diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 943fae87af..f0de849163 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -355,6 +355,10 @@ namespace ts.projectSystem { return countWhere(recursiveWatchedDirs, dir => file.length > dir.length && startsWith(file, dir) && file[dir.length] === directorySeparator); } + function checkOpenFiles(projectService: server.ProjectService, expectedFiles: FileOrFolder[]) { + checkFileNames("Open files", projectService.openFiles.map(info => info.fileName), expectedFiles.map(file => file.path)); + } + /** * Test server cancellation token used to mock host token cancellation requests. * The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls @@ -1060,16 +1064,19 @@ namespace ts.projectSystem { projectService.openClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects.get(configFile.path); + assert.isTrue(project.hasOpenRef()); // file1 projectService.closeClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); - assert.equal(project.openRefCount, 0); + assert.isFalse(project.hasOpenRef()); // No open files + assert.isFalse(project.isClosed()); projectService.openClientFile(file2.path); checkNumberOfConfiguredProjects(projectService, 1); assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); - assert.equal(project.openRefCount, 1); + assert.isTrue(project.hasOpenRef()); // file2 + assert.isFalse(project.isClosed()); }); it("should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project", () => { @@ -1091,14 +1098,18 @@ namespace ts.projectSystem { projectService.openClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects.get(configFile.path); + assert.isTrue(project.hasOpenRef()); // file1 projectService.closeClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); - assert.equal(project.openRefCount, 0); + assert.isFalse(project.hasOpenRef()); // No files + assert.isFalse(project.isClosed()); projectService.openClientFile(libFile.path); checkNumberOfConfiguredProjects(projectService, 0); + assert.isFalse(project.hasOpenRef()); // No files + project closed + assert.isTrue(project.isClosed()); }); it("should not close external project with no open files", () => { @@ -2085,39 +2096,224 @@ namespace ts.projectSystem { projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); const project1 = projectService.configuredProjects.get(tsconfig1.path); - assert.equal(project1.openRefCount, 1, "Open ref count in project1 - 1"); + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 1"); // file2 assert.equal(project1.getScriptInfo(file2.path).containingProjects.length, 1, "containing projects count"); + assert.isFalse(project1.isClosed()); projectService.openClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert.equal(project1.openRefCount, 2, "Open ref count in project1 - 2"); + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 2"); // file2 assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); + assert.isFalse(project1.isClosed()); const project2 = projectService.configuredProjects.get(tsconfig2.path); - assert.equal(project2.openRefCount, 1, "Open ref count in project2 - 2"); + assert.isTrue(project2.hasOpenRef(), "Has open ref count in project2 - 2"); // file1 + assert.isFalse(project2.isClosed()); assert.equal(project1.getScriptInfo(file1.path).containingProjects.length, 2, `${file1.path} containing projects count`); assert.equal(project1.getScriptInfo(file2.path).containingProjects.length, 1, `${file2.path} containing projects count`); projectService.closeClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert.equal(project1.openRefCount, 1, "Open ref count in project1 - 3"); - assert.equal(project2.openRefCount, 1, "Open ref count in project2 - 3"); + assert.isFalse(project1.hasOpenRef(), "Has open ref count in project1 - 3"); // No files + assert.isTrue(project2.hasOpenRef(), "Has open ref count in project2 - 3"); // file1 assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); assert.strictEqual(projectService.configuredProjects.get(tsconfig2.path), project2); + assert.isFalse(project1.isClosed()); + assert.isFalse(project2.isClosed()); projectService.closeClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert.equal(project1.openRefCount, 0, "Open ref count in project1 - 4"); - assert.equal(project2.openRefCount, 0, "Open ref count in project2 - 4"); + assert.isFalse(project1.hasOpenRef(), "Has open ref count in project1 - 4"); // No files + assert.isFalse(project2.hasOpenRef(), "Has open ref count in project2 - 4"); // No files assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); assert.strictEqual(projectService.configuredProjects.get(tsconfig2.path), project2); + assert.isFalse(project1.isClosed()); + assert.isFalse(project2.isClosed()); projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); assert.isUndefined(projectService.configuredProjects.get(tsconfig2.path)); - assert.equal(project1.openRefCount, 1, "Open ref count in project1 - 5"); + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 5"); // file2 + assert.isFalse(project1.isClosed()); + assert.isTrue(project2.isClosed()); + }); + + it("Open ref of configured project when open file gets added to the project as part of configured file update", () => { + const file1: FileOrFolder = { + path: "/a/b/src/file1.ts", + content: "let x = 1;" + }; + const file2: FileOrFolder = { + path: "/a/b/src/file2.ts", + content: "let y = 1;" + }; + const file3: FileOrFolder = { + path: "/a/b/file3.ts", + content: "let z = 1;" + }; + const file4: FileOrFolder = { + path: "/a/file4.ts", + content: "let z = 1;" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ files: ["src/file1.ts", "file3.ts"] }) + }; + + const files = [file1, file2, file3, file4]; + const host = createServerHost(files.concat(configFile)); + const projectService = createProjectService(host); + + projectService.openClientFile(file1.path); + projectService.openClientFile(file2.path); + projectService.openClientFile(file3.path); + projectService.openClientFile(file4.path); + + const infos = files.map(file => projectService.getScriptInfoForPath(file.path as Path)); + checkOpenFiles(projectService, files); + checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); + const configProject1 = projectService.configuredProjects.get(configFile.path); + assert.isTrue(configProject1.hasOpenRef()); // file1 and file3 + checkProjectActualFiles(configProject1, [file1.path, file3.path, configFile.path]); + const inferredProject1 = projectService.inferredProjects[0]; + checkProjectActualFiles(inferredProject1, [file2.path]); + const inferredProject2 = projectService.inferredProjects[1]; + checkProjectActualFiles(inferredProject2, [file4.path]); + + configFile.content = "{}"; + host.reloadFS(files.concat(configFile)); + host.runQueuedTimeoutCallbacks(); + + verifyScriptInfos(); + checkOpenFiles(projectService, files); + verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file1, file2, file3 + checkNumberOfInferredProjects(projectService, 1); + const inferredProject3 = projectService.inferredProjects[0]; + checkProjectActualFiles(inferredProject3, [file4.path]); + assert.strictEqual(inferredProject3, inferredProject2); + + projectService.closeClientFile(file1.path); + projectService.closeClientFile(file2.path); + projectService.closeClientFile(file4.path); + + verifyScriptInfos(); + checkOpenFiles(projectService, [file3]); + verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file3 + checkNumberOfInferredProjects(projectService, 0); + + projectService.openClientFile(file4.path); + verifyScriptInfos(); + checkOpenFiles(projectService, [file3, file4]); + verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file3 + checkNumberOfInferredProjects(projectService, 1); + const inferredProject4 = projectService.inferredProjects[0]; + checkProjectActualFiles(inferredProject4, [file4.path]); + + projectService.closeClientFile(file3.path); + verifyScriptInfos(); + checkOpenFiles(projectService, [file4]); + verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ false); // No open files + checkNumberOfInferredProjects(projectService, 1); + const inferredProject5 = projectService.inferredProjects[0]; + checkProjectActualFiles(inferredProject4, [file4.path]); + assert.strictEqual(inferredProject5, inferredProject4); + + const file5: FileOrFolder = { + path: "/file5.ts", + content: "let zz = 1;" + }; + host.reloadFS(files.concat(configFile, file5)); + projectService.openClientFile(file5.path); + verifyScriptInfosAreUndefined([file1, file2, file3]); + assert.strictEqual(projectService.getScriptInfoForPath(file4.path as Path), find(infos, info => info.path === file4.path)); + assert.isDefined(projectService.getScriptInfoForPath(file5.path as Path)); + checkOpenFiles(projectService, [file4, file5]); + checkNumberOfConfiguredProjects(projectService, 0); + + function verifyScriptInfos() { + infos.forEach(info => assert.strictEqual(projectService.getScriptInfoForPath(info.path), info)); + } + + function verifyScriptInfosAreUndefined(files: FileOrFolder[]) { + for (const file of files) { + assert.isUndefined(projectService.getScriptInfoForPath(file.path as Path)); + } + } + + function verifyConfiguredProjectStateAfterUpdate(hasOpenRef: boolean) { + checkNumberOfConfiguredProjects(projectService, 1); + const configProject2 = projectService.configuredProjects.get(configFile.path); + assert.strictEqual(configProject2, configProject1); + checkProjectActualFiles(configProject2, [file1.path, file2.path, file3.path, configFile.path]); + assert.equal(configProject2.hasOpenRef(), hasOpenRef); + } + }); + + it("Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", () => { + const file1: FileOrFolder = { + path: "/a/b/src/file1.ts", + content: "let x = 1;" + }; + const file2: FileOrFolder = { + path: "/a/b/src/file2.ts", + content: "let y = 1;" + }; + const file3: FileOrFolder = { + path: "/a/b/file3.ts", + content: "let z = 1;" + }; + const file4: FileOrFolder = { + path: "/a/file4.ts", + content: "let z = 1;" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ files: ["src/file1.ts", "file3.ts"] }) + }; + + const files = [file1, file2, file3]; + const hostFiles = files.concat(file4, configFile); + const host = createServerHost(hostFiles); + const projectService = createProjectService(host); + + projectService.openClientFile(file1.path); + projectService.openClientFile(file2.path); + projectService.openClientFile(file3.path); + + checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 }); + const configuredProject = projectService.configuredProjects.get(configFile.path); + assert.isTrue(configuredProject.hasOpenRef()); // file1 and file3 + checkProjectActualFiles(configuredProject, [file1.path, file3.path, configFile.path]); + const inferredProject1 = projectService.inferredProjects[0]; + checkProjectActualFiles(inferredProject1, [file2.path]); + + projectService.closeClientFile(file1.path); + projectService.closeClientFile(file3.path); + assert.isFalse(configuredProject.hasOpenRef()); // No files + + configFile.content = "{}"; + host.reloadFS(files.concat(configFile)); + // Time out is not yet run so there is project update pending + assert.isTrue(configuredProject.hasOpenRef()); // Pending update and file2 might get into the project + + projectService.openClientFile(file4.path); + + checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject); + assert.isTrue(configuredProject.hasOpenRef()); // Pending update and F2 might get into the project + assert.strictEqual(projectService.inferredProjects[0], inferredProject1); + const inferredProject2 = projectService.inferredProjects[1]; + checkProjectActualFiles(inferredProject2, [file4.path]); + + host.runQueuedTimeoutCallbacks(); + checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 }); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject); + assert.isTrue(configuredProject.hasOpenRef()); // file2 + checkProjectActualFiles(configuredProject, [file1.path, file2.path, file3.path, configFile.path]); + assert.strictEqual(projectService.inferredProjects[0], inferredProject2); + checkProjectActualFiles(inferredProject2, [file4.path]); }); it("language service disabled state is updated in external projects", () => { @@ -2188,18 +2384,36 @@ namespace ts.projectSystem { projectService.openClientFile(f1.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); const project = projectService.configuredProjects.get(config.path); + assert.isTrue(project.hasOpenRef()); // f1 + assert.isFalse(project.isClosed()); projectService.closeClientFile(f1.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); assert.strictEqual(projectService.configuredProjects.get(config.path), project); - assert.equal(project.openRefCount, 0); + assert.isFalse(project.hasOpenRef()); // No files + assert.isFalse(project.isClosed()); for (const f of [f1, f2, f3]) { - // There shouldnt be any script info as we closed the file that resulted in creation of it + // All the script infos should be present and contain the project since it is still alive. const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path)); assert.equal(scriptInfo.containingProjects.length, 1, `expect 1 containing projects for '${f.path}'`); assert.equal(scriptInfo.containingProjects[0], project, `expect configured project to be the only containing project for '${f.path}'`); } + + const f4 = { + path: "/aa.js", + content: "var x = 1" + }; + host.reloadFS([f1, f2, f3, config, f4]); + projectService.openClientFile(f4.path); + projectService.checkNumberOfProjects({ inferredProjects: 1 }); + assert.isFalse(project.hasOpenRef()); // No files + assert.isTrue(project.isClosed()); + + for (const f of [f1, f2, f3]) { + // All the script infos should not be present since the project is closed and orphan script infos are collected + assert.isUndefined(projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path))); + } }); it("language service disabled events are triggered", () => { @@ -2836,17 +3050,19 @@ namespace ts.projectSystem { projectService.openClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); const project = projectService.configuredProjects.get(config.path); - assert.equal(project.openRefCount, 1); + assert.isTrue(project.hasOpenRef()); // f projectService.closeClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); assert.strictEqual(projectService.configuredProjects.get(config.path), project); - assert.equal(project.openRefCount, 0); + assert.isFalse(project.hasOpenRef()); // No files + assert.isFalse(project.isClosed()); projectService.openClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); assert.strictEqual(projectService.configuredProjects.get(config.path), project); - assert.equal(project.openRefCount, 1); + assert.isTrue(project.hasOpenRef()); // f + assert.isFalse(project.isClosed()); }); }); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ad08cd63da..47c555cdad 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -569,6 +569,11 @@ namespace ts.server { }); } + /*@internal*/ + hasPendingProjectUpdate(project: Project) { + return this.pendingProjectUpdates.has(project.getProjectName()); + } + private sendProjectsUpdatedInBackgroundEvent() { if (!this.eventHandler) { return; @@ -810,8 +815,14 @@ namespace ts.server { ); } + /** Gets the config file existence info for the configured project */ + /*@internal*/ + getConfigFileExistenceInfo(project: ConfiguredProject) { + return this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); + } + private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) { - const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); + const configFileExistenceInfo = this.getConfigFileExistenceInfo(project); if (eventKind === FileWatcherEventKind.Deleted) { // Update the cached status // We arent updating or removing the cached config file presence info as that will be taken care of by @@ -913,18 +924,6 @@ namespace ts.server { return project; } - private addToListOfOpenFiles(info: ScriptInfo) { - Debug.assert(!info.isOrphan()); - for (const p of info.containingProjects) { - // file is the part of configured project, addref the project - if (p.projectKind === ProjectKind.Configured) { - ((p)).addOpenRef(); - } - } - - this.openFiles.push(info); - } - /** * Remove this file from the set of open, non-configured files. * @param info The file that has been closed or newly configured @@ -947,10 +946,8 @@ namespace ts.server { if (info.hasMixedContent) { info.registerFileUpdate(); } - // Delete the reference to the open configured projects but - // do not remove the project so that we can reuse this project + // Do not remove the project so that we can reuse this project // if it would need to be re-created with next file open - (p).deleteOpenRef(); } else if (p.projectKind === ProjectKind.Inferred && p.isRoot(info)) { // If this was the open root file of inferred project @@ -1040,7 +1037,7 @@ namespace ts.server { } private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) { - const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); + const configFileExistenceInfo = this.getConfigFileExistenceInfo(project); if (configFileExistenceInfo) { Debug.assert(configFileExistenceInfo.exists); // close existing watcher @@ -1069,7 +1066,7 @@ namespace ts.server { } private setConfigFileExistenceInfoByClosedConfiguredProject(closedProject: ConfiguredProject) { - const configFileExistenceInfo = this.configFileExistenceInfoCache.get(closedProject.canonicalConfigFilePath); + const configFileExistenceInfo = this.getConfigFileExistenceInfo(closedProject); Debug.assert(!!configFileExistenceInfo); if (configFileExistenceInfo.openFilesImpactedByConfigFile.size) { const configFileName = closedProject.getConfigFilePath(); @@ -1958,7 +1955,8 @@ namespace ts.server { this.assignOrphanScriptInfoToInferredProject(info, projectRootPath); } - this.addToListOfOpenFiles(info); + Debug.assert(!info.isOrphan()); + this.openFiles.push(info); if (sendConfigFileDiagEvent) { configFileErrors = project.getAllProjectErrors(); @@ -2058,11 +2056,14 @@ namespace ts.server { } } - private closeConfiguredProject(configFile: NormalizedPath): boolean { + private closeConfiguredProjectReferencedFromExternalProject(configFile: NormalizedPath): boolean { const configuredProject = this.findConfiguredProjectByProjectName(configFile); - if (configuredProject && configuredProject.deleteOpenRef() === 0) { - this.removeProject(configuredProject); - return true; + if (configuredProject) { + configuredProject.deleteExternalProjectReference(); + if (!configuredProject.hasOpenRef()) { + this.removeProject(configuredProject); + return true; + } } return false; } @@ -2073,7 +2074,7 @@ namespace ts.server { if (configFiles) { let shouldRefreshInferredProjects = false; for (const configFile of configFiles) { - if (this.closeConfiguredProject(configFile)) { + if (this.closeConfiguredProjectReferencedFromExternalProject(configFile)) { shouldRefreshInferredProjects = true; } } @@ -2268,7 +2269,7 @@ namespace ts.server { const newConfig = tsConfigFiles[iNew]; const oldConfig = oldConfigFiles[iOld]; if (oldConfig < newConfig) { - this.closeConfiguredProject(oldConfig); + this.closeConfiguredProjectReferencedFromExternalProject(oldConfig); iOld++; } else if (oldConfig > newConfig) { @@ -2283,7 +2284,7 @@ namespace ts.server { } for (let i = iOld; i < oldConfigFiles.length; i++) { // projects for all remaining old config files should be closed - this.closeConfiguredProject(oldConfigFiles[i]); + this.closeConfiguredProjectReferencedFromExternalProject(oldConfigFiles[i]); } } } @@ -2298,7 +2299,7 @@ namespace ts.server { } if (project && !contains(exisingConfigFiles, tsconfigFile)) { // keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project - project.addOpenRef(); + project.addExternalProjectReference(); } } } diff --git a/src/server/project.ts b/src/server/project.ts index 725ce725c3..c6347dd74c 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -908,9 +908,7 @@ namespace ts.server { } getScriptInfoForNormalizedPath(fileName: NormalizedPath) { - const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath( - fileName, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.directoryStructureHost - ); + const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(fileName); if (scriptInfo && !scriptInfo.isAttached(this)) { return Errors.ThrowProjectDoesNotContainDocument(fileName, this); } @@ -918,7 +916,7 @@ namespace ts.server { } getScriptInfo(uncheckedFileName: string) { - return this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName)); + return this.projectService.getScriptInfo(uncheckedFileName); } filesToString(writeProjectFileNames: boolean) { @@ -1146,8 +1144,8 @@ namespace ts.server { private plugins: PluginModule[] = []; - /** Used for configured projects which may have multiple open roots */ - openRefCount = 0; + /** Ref count to the project when opened from external project */ + private externalProjectRefCount = 0; private projectErrors: Diagnostic[]; @@ -1358,17 +1356,43 @@ namespace ts.server { super.close(); } - addOpenRef() { - this.openRefCount++; + /* @internal */ + addExternalProjectReference() { + this.externalProjectRefCount++; } - deleteOpenRef() { - this.openRefCount--; - return this.openRefCount; + /* @internal */ + deleteExternalProjectReference() { + this.externalProjectRefCount--; } + /** Returns true if the project is needed by any of the open script info/external project */ + /* @internal */ hasOpenRef() { - return !!this.openRefCount; + if (!!this.externalProjectRefCount) { + return true; + } + + // Closed project doesnt have any reference + if (this.isClosed()) { + return false; + } + + const configFileExistenceInfo = this.projectService.getConfigFileExistenceInfo(this); + if (this.projectService.hasPendingProjectUpdate(this)) { + // If there is pending update for this project, + // we dont know if this project would be needed by any of the open files impacted by this config file + // In that case keep the project alive if there are open files impacted by this project + return !!configFileExistenceInfo.openFilesImpactedByConfigFile.size; + } + + // If there is no pending update for this project, + // We know exact set of open files that get impacted by this configured project as the files in the project + // The project is referenced only if open files impacted by this project are present in this project + return forEachEntry( + configFileExistenceInfo.openFilesImpactedByConfigFile, + (_value, infoPath) => this.containsScriptInfo(this.projectService.getScriptInfoForPath(infoPath as Path)) + ) || false; } getEffectiveTypeRoots() { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 52132ae832..ab39d0a6d6 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7280,8 +7280,8 @@ declare namespace ts.server { private directoriesWatchedForWildcards; readonly canonicalConfigFilePath: NormalizedPath; private plugins; - /** Used for configured projects which may have multiple open roots */ - openRefCount: number; + /** Ref count to the project when opened from external project */ + private externalProjectRefCount; private projectErrors; /** * If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph @@ -7305,9 +7305,6 @@ declare namespace ts.server { getTypeAcquisition(): TypeAcquisition; getExternalFiles(): SortedReadonlyArray; close(): void; - addOpenRef(): void; - deleteOpenRef(): number; - hasOpenRef(): boolean; getEffectiveTypeRoots(): string[]; } /** @@ -7537,7 +7534,6 @@ declare namespace ts.server { */ private onConfigFileChangeForOpenScriptInfo(configFileName, eventKind); private removeProject(project); - private addToListOfOpenFiles(info); /** * Remove this file from the set of open, non-configured files. * @param info The file that has been closed or newly configured @@ -7645,7 +7641,7 @@ declare namespace ts.server { */ closeClientFile(uncheckedFileName: string): void; private collectChanges(lastKnownProjectVersions, currentProjects, result); - private closeConfiguredProject(configFile); + private closeConfiguredProjectReferencedFromExternalProject(configFile); closeExternalProject(uncheckedFileName: string, suppressRefresh?: boolean): void; openExternalProjects(projects: protocol.ExternalProject[]): void; /** Makes a filename safe to insert in a RegExp */