Merge pull request #19138 from Microsoft/configuredProjectRef

Handle the configured project lifetime to account for files added to the project after config file gets reloaded
This commit is contained in:
Sheetal Nandi 2017-10-18 14:23:25 -07:00 committed by GitHub
commit f59241971d
4 changed files with 299 additions and 62 deletions

View file

@ -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());
});
});

View file

@ -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) {
((<ConfiguredProject>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
(<ConfiguredProject>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();
}
}
}

View file

@ -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() {

View file

@ -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<string>;
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 */