Allows the delayed update graph and project structure which helps in batching the updates when there are multiple files added/removed/changed

This commit is contained in:
Sheetal Nandi
2017-07-12 23:15:03 -07:00
parent 8fedcf78c7
commit e568976239
5 changed files with 161 additions and 98 deletions

View File

@@ -639,6 +639,11 @@ namespace ts.projectSystem {
this.timeoutCallbacks.unregister(timeoutId);
}
checkTimeoutQueueLengthAndRun(expected: number) {
this.checkTimeoutQueueLength(expected);
this.runQueuedTimeoutCallbacks();
}
checkTimeoutQueueLength(expected: number) {
const callbacksCount = this.timeoutCallbacks.count();
assert.equal(callbacksCount, expected, `expected ${expected} timeout callbacks queued but found ${callbacksCount}.`);
@@ -899,6 +904,9 @@ namespace ts.projectSystem {
// remove the tsconfig file
host.reloadFS(filesWithoutConfig);
checkNumberOfInferredProjects(projectService, 1);
host.checkTimeoutQueueLengthAndRun(1); // Refresh inferred projects
checkNumberOfInferredProjects(projectService, 2);
checkNumberOfConfiguredProjects(projectService, 0);
checkWatchedDirectories(host, ["/a/b", "/a"]);
@@ -920,7 +928,7 @@ namespace ts.projectSystem {
// add a new ts file
host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLengthAndRun(2);
// project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
});
@@ -1022,12 +1030,12 @@ namespace ts.projectSystem {
// delete commonFile2
host.reloadFS([commonFile1, configFile]);
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLengthAndRun(2);
checkProjectRootFiles(project, [commonFile1.path]);
// re-add commonFile2
host.reloadFS([commonFile1, commonFile2, configFile]);
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLengthAndRun(2);
checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
});
@@ -1086,6 +1094,9 @@ namespace ts.projectSystem {
}`;
host.reloadFS(files);
checkNumberOfConfiguredProjects(projectService, 1);
checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
host.checkTimeoutQueueLengthAndRun(2); // Update the configured project + refresh inferred projects
checkNumberOfConfiguredProjects(projectService, 1);
checkProjectRootFiles(project, [commonFile1.path]);
@@ -1158,6 +1169,7 @@ namespace ts.projectSystem {
"files": ["${file1.path}"]
}`;
host.reloadFS(files);
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
checkNumberOfInferredProjects(projectService, 1);
});
@@ -1587,7 +1599,7 @@ namespace ts.projectSystem {
};
host.reloadFS([file1, modifiedFile2, file3]);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfInferredProjects(projectService, 1);
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, modifiedFile2.path, file3.path]);
});
@@ -1618,6 +1630,7 @@ namespace ts.projectSystem {
checkNumberOfProjects(projectService, { inferredProjects: 1 });
host.reloadFS([file1, file3]);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { inferredProjects: 2 });
@@ -1779,7 +1792,7 @@ namespace ts.projectSystem {
host.reloadFS([file1, file2, configFile]);
host.checkTimeoutQueueLength(0); // TODO: update graph scheduling (instead of instant update graph)
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
@@ -1814,6 +1827,7 @@ namespace ts.projectSystem {
host.reloadFS([file1, file2, modifiedConfigFile]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
});
@@ -1923,7 +1937,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
host.reloadFS([file1, file2]);
host.checkTimeoutQueueLengthAndRun(1);
checkNumberOfProjects(projectService, { inferredProjects: 2 });
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path]);
checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]);
@@ -2217,6 +2231,7 @@ namespace ts.projectSystem {
checkNumberOfProjects(projectService, { inferredProjects: 2 });
projectService.setCompilerOptionsForInferredProjects({ moduleResolution: ModuleResolutionKind.Classic });
host.checkTimeoutQueueLengthAndRun(3);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
});
@@ -2406,7 +2421,7 @@ namespace ts.projectSystem {
assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state");
host.reloadFS([f1, f2, configWithExclude]);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.isTrue(project.languageServiceEnabled, "Language service enabled");
assert.equal(lastEvent.data.project, project, "project");
@@ -2783,6 +2798,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, app.path, config1.path]);
host.reloadFS([libES5, libES2015Promise, app, config2]);
host.checkTimeoutQueueLengthAndRun(2);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, libES2015Promise.path, app.path, config2.path]);

View File

@@ -138,6 +138,7 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(p, [file1.path, jquery.path, tsconfig.path]);
});
@@ -337,6 +338,8 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { externalProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
});
@@ -460,6 +463,8 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { externalProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]);
});
@@ -538,7 +543,7 @@ namespace ts.projectSystem {
for (const f of typingFiles) {
assert.isTrue(host.fileExists(f.path), `expected file ${f.path} to exist`);
}
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path, commander.path, express.path, jquery.path, moment.path, lodash.path]);
});
@@ -641,7 +646,7 @@ namespace ts.projectSystem {
assert.equal(installer.pendingRunRequests.length, 0, "expected no throttled requests");
installer.executePendingCommands();
host.checkTimeoutQueueLengthAndRun(3); // for 2 projects and 1 refreshing inferred project
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]);
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path]);
});
@@ -695,6 +700,7 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
});
@@ -742,6 +748,7 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
});
@@ -788,6 +795,7 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
});
@@ -826,9 +834,10 @@ namespace ts.projectSystem {
installer.checkPendingCommands(/*expectedCount*/ 0);
host.reloadFS([f, fixedPackageJson]);
host.checkTimeoutQueueLengthAndRun(2); // To refresh the project and refresh inferred projects
// expected install request
installer.installAll(/*expectedCount*/ 1);
host.checkTimeoutQueueLengthAndRun(2);
service.checkNumberOfProjects({ inferredProjects: 1 });
checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]);
});
@@ -950,8 +959,7 @@ namespace ts.projectSystem {
endOffset: 0
}
});
host.checkTimeoutQueueLength(1);
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects
const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
assert.equal(version1, version2, "set of unresolved imports should not change");
});
@@ -1132,6 +1140,7 @@ namespace ts.projectSystem {
installer.installAll(/*expectedCount*/ 1);
assert.isTrue(seenTelemetryEvent);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
});
@@ -1185,6 +1194,7 @@ namespace ts.projectSystem {
assert.isTrue(!!endEvent);
assert.isTrue(beginEvent.eventId === endEvent.eventId);
assert.isTrue(endEvent.installSuccess);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
});

View File

@@ -358,6 +358,8 @@ namespace ts.server {
private static safelist: SafeList = defaultTypeSafeList;
private changedFiles: ScriptInfo[];
private pendingProjectUpdates = createMap<Project>();
private pendingInferredProjectUpdate: boolean;
readonly toCanonicalFileName: (f: string) => string;
@@ -444,8 +446,44 @@ namespace ts.server {
break;
}
project.markAsDirty();
// TODO: (sheetalkamat) Schedule Update graph instead of immediate
project.updateGraph();
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
private delayInferredProjectsRefresh() {
this.pendingInferredProjectUpdate = true;
this.throttledOperations.schedule("*refreshInferredProjects*", /*delay*/ 250, () => {
if (this.pendingProjectUpdates.size !== 0) {
this.delayInferredProjectsRefresh();
}
else if (this.pendingInferredProjectUpdate) {
this.pendingInferredProjectUpdate = false;
this.refreshInferredProjects();
}
});
}
private delayUpdateProjectGraph(project: Project) {
const projectName = project.getProjectName();
this.pendingProjectUpdates.set(projectName, project);
this.throttledOperations.schedule(projectName, /*delay*/ 250, () => {
const project = this.pendingProjectUpdates.get(projectName);
if (project) {
this.pendingProjectUpdates.delete(projectName);
project.updateGraph();
}
});
}
delayUpdateProjectGraphAndInferredProjectsRefresh(project: Project) {
this.delayUpdateProjectGraph(project);
this.delayInferredProjectsRefresh();
}
private delayUpdateProjectGraphs(projects: Project[]) {
for (const project of projects) {
this.delayUpdateProjectGraph(project);
}
this.delayInferredProjectsRefresh();
}
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions): void {
@@ -457,9 +495,9 @@ namespace ts.server {
for (const proj of this.inferredProjects) {
proj.setCompilerOptions(this.compilerOptionsForInferredProjects);
proj.compileOnSaveEnabled = projectCompilerOptions.compileOnSave;
proj.markAsDirty();
}
// TODO: (sheetalkamat) (ensure updated projects)
this.updateProjectGraphs(this.inferredProjects);
this.delayUpdateProjectGraphs(this.inferredProjects);
}
stopWatchingDirectory(directory: string) {
@@ -479,14 +517,17 @@ namespace ts.server {
getDefaultProjectForFile(fileName: NormalizedPath, refreshInferredProjects: boolean) {
if (refreshInferredProjects) {
// TODO: (sheetalkamat) needs to update inferred projects too ?
this.ensureInferredProjectsUpToDate();
}
const scriptInfo = this.getScriptInfoForNormalizedPath(fileName);
return scriptInfo && scriptInfo.getDefaultProject();
}
private ensureInferredProjectsUpToDate() {
/**
* Ensures the project structures are upto date
* @param refreshInferredProjects when true updates the inferred projects even if there is no pending work
*/
private ensureInferredProjectsUpToDate(refreshInferredProjects?: boolean) {
if (this.changedFiles) {
let projectsToUpdate: Project[];
if (this.changedFiles.length === 1) {
@@ -499,10 +540,20 @@ namespace ts.server {
projectsToUpdate = projectsToUpdate.concat(f.containingProjects);
}
}
this.updateProjectGraphs(projectsToUpdate);
this.changedFiles = undefined;
this.updateProjectGraphs(projectsToUpdate);
}
if (this.pendingProjectUpdates.size !== 0) {
const projectsToUpdate = arrayFrom(this.pendingProjectUpdates.values());
this.pendingProjectUpdates.clear();
this.updateProjectGraphs(projectsToUpdate);
}
if (this.pendingInferredProjectUpdate || refreshInferredProjects) {
this.pendingInferredProjectUpdate = false;
this.refreshInferredProjects();
}
// TODO: (sheetalkamat) should also refresh inferred projects here since we are postponing the changes in closed files
}
private findContainingExternalProject(fileName: NormalizedPath): ExternalProject {
@@ -526,15 +577,11 @@ namespace ts.server {
}
private updateProjectGraphs(projects: Project[]) {
let shouldRefreshInferredProjects = false;
for (const p of projects) {
if (!p.updateGraph()) {
shouldRefreshInferredProjects = true;
this.pendingInferredProjectUpdate = true;
}
}
if (shouldRefreshInferredProjects) {
this.refreshInferredProjects();
}
}
private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) {
@@ -559,7 +606,7 @@ namespace ts.server {
// file has been changed which might affect the set of referenced files in projects that include
// this file and set of inferred projects
info.reloadFromFile();
this.recordChangedFile(info);
this.delayUpdateProjectGraphs(info.containingProjects);
}
}
}
@@ -580,36 +627,28 @@ namespace ts.server {
info.detachAllProjects();
// TODO: (sheetalkamat) Schedule this too ?
// update projects to make sure that set of referenced files is correct
this.updateProjectGraphs(containingProjects);
this.delayUpdateProjectGraphs(containingProjects);
if (!this.eventHandler) {
return;
}
// TODO: (sheetalkamat) Someway to send this event so that error checks are updated?
// if (!this.eventHandler) {
// return;
// }
for (const openFile of this.openFiles) {
this.eventHandler(<ContextEvent>{
eventName: ContextEvent,
data: { project: openFile.getDefaultProject(), fileName: openFile.fileName }
});
}
// for (const openFile of this.openFiles) {
// this.eventHandler(<ContextEvent>{
// eventName: ContextEvent,
// data: { project: openFile.getDefaultProject(), fileName: openFile.fileName }
// });
// }
}
this.printProjects();
}
private onTypeRootFileChanged(project: ConfiguredProject, fileName: NormalizedPath) {
this.logger.info(`Type root file ${fileName} changed`);
project.getCachedServerHost().addOrDeleteFileOrFolder(fileName);
project.updateTypes();
// TODO: (sheetalkamat) schedule project update and referesh inferred projects instead
//this.throttledOperations.schedule(project.getConfigFilePath() + " * type root", /*delay*/ 250, () => {
// this.reloadConfiguredProject(project); // TODO: Figure out why this is needed (should be redundant?)
project.updateGraph();
this.refreshInferredProjects();
//});
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
/**
@@ -650,10 +689,7 @@ namespace ts.server {
error.code !== Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files.code);
}
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader);
// TODO: (sheetalkamat) schedule the update graph
if (!project.updateGraph()) {
this.refreshInferredProjects();
}
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) {
@@ -664,16 +700,14 @@ namespace ts.server {
// Reload the configured projects for these open files in the project as
// they could be held up by another config file somewhere in the parent directory
const openFilesInProject = this.getOrphanFiles();
// TODO: (sheetalkamat) can this be scheduled too
this.reloadConfiguredProjectForFiles(openFilesInProject);
this.reloadConfiguredProjectForFiles(openFilesInProject, project => { project.pendingReload = true; this.delayUpdateProjectGraph(project); });
this.delayInferredProjectsRefresh();
}
else {
this.logger.info(`Config file changed: ${configFileName}`);
// TODO: (sheetalkamat) This should be scheduled and marked for reload of configured project since we dont want to updating this on every change
this.reloadConfiguredProject(project);
this.reportConfigFileDiagnostics(configFileName, project.getGlobalProjectErrors(), /*triggerFile*/ configFileName);
project.pendingReload = true;
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
this.refreshInferredProjects();
}
/**
@@ -698,6 +732,8 @@ namespace ts.server {
this.logger.info(`remove project: ${project.getRootFiles().toString()}`);
project.close();
// Remove the project from pending project updates
this.pendingProjectUpdates.delete(project.getProjectName());
switch (project.projectKind) {
case ProjectKind.External:
@@ -1249,7 +1285,8 @@ namespace ts.server {
* Read the config file of the project again and update the project
* @param project
*/
private reloadConfiguredProject(project: ConfiguredProject) {
/* @internal */
reloadConfiguredProject(project: ConfiguredProject) {
// At this point, there is no reason to not have configFile in the host
// note: the returned "success" is true does not mean the "configFileErrors" is empty.
@@ -1257,7 +1294,8 @@ namespace ts.server {
// regardless the "success" here is true or not.
const host = project.getCachedServerHost();
host.clearCache();
const { success, projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(project.getConfigFilePath(), host);
const configFileName = project.getConfigFilePath();
const { success, projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, host);
project.configFileSpecs = configFileSpecs;
if (!success) {
// reset project settings to default
@@ -1266,6 +1304,7 @@ namespace ts.server {
else {
this.updateConfiguredProject(project, projectOptions, configFileErrors);
}
this.reportConfigFileDiagnostics(configFileName, project.getGlobalProjectErrors(), /*triggerFile*/ configFileName);
}
/**
@@ -1405,11 +1444,11 @@ namespace ts.server {
*/
reloadProjects() {
this.logger.info("reload projects.");
this.reloadConfiguredProjectForFiles(this.openFiles);
this.reloadConfiguredProjectForFiles(this.openFiles, project => this.reloadConfiguredProject(project));
this.refreshInferredProjects();
}
reloadConfiguredProjectForFiles(openFiles: ScriptInfo[]) {
reloadConfiguredProjectForFiles(openFiles: ScriptInfo[], reload: (project: ConfiguredProject) => void) {
const mapUpdatedProjects = createMap<true>();
// try to reload config file for all open files
for (const info of openFiles) {
@@ -1425,7 +1464,7 @@ namespace ts.server {
mapUpdatedProjects.set(configFileName, true);
}
else if (!mapUpdatedProjects.has(configFileName)) {
this.reloadConfiguredProject(project);
reload(project);
mapUpdatedProjects.set(configFileName, true);
}
}
@@ -1437,7 +1476,7 @@ namespace ts.server {
* It is called on the premise that all the configured projects are
* up to date.
*/
refreshInferredProjects() {
private refreshInferredProjects() {
this.logger.info("updating project structure from ...");
this.printProjects();
@@ -1547,17 +1586,6 @@ namespace ts.server {
return files;
}
private recordChangedFile(scriptInfo: ScriptInfo) {
if (!this.changedFiles) {
this.changedFiles = [scriptInfo];
}
else if (this.changedFiles.indexOf(scriptInfo) < 0) {
this.changedFiles.push(scriptInfo);
}
// TODO: (sheetalkamat) Schedule the project update graphs
this.updateProjectGraphs(scriptInfo.containingProjects);
}
/* @internal */
applyChangesInOpenFiles(openFiles: protocol.ExternalFile[], changedFiles: protocol.ChangedOpenFile[], closedFiles: string[]): void {
if (openFiles) {
@@ -1578,7 +1606,12 @@ namespace ts.server {
const change = file.changes[i];
scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText);
}
this.recordChangedFile(scriptInfo);
if (!this.changedFiles) {
this.changedFiles = [scriptInfo];
}
else if (this.changedFiles.indexOf(scriptInfo) < 0) {
this.changedFiles.push(scriptInfo);
}
}
}
@@ -1590,8 +1623,7 @@ namespace ts.server {
// if files were open or closed then explicitly refresh list of inferred projects
// otherwise if there were only changes in files - record changed files in `changedFiles` and defer the update
if (openFiles || closedFiles) {
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.refreshInferredProjects();
this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true);
}
}
@@ -1614,8 +1646,7 @@ namespace ts.server {
}
this.externalProjectToConfiguredProjectMap.delete(fileName);
if (shouldRefreshInferredProjects && !suppressRefresh) {
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.refreshInferredProjects();
this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true);
}
}
else {
@@ -1624,8 +1655,7 @@ namespace ts.server {
if (externalProject) {
this.removeProject(externalProject);
if (!suppressRefresh) {
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.refreshInferredProjects();
this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true);
}
}
}
@@ -1649,8 +1679,7 @@ namespace ts.server {
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true);
});
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.refreshInferredProjects();
this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true);
}
/** Makes a filename safe to insert in a RegExp */
@@ -1844,8 +1873,7 @@ namespace ts.server {
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
}
if (!suppressRefreshOfInferredProjects) {
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.refreshInferredProjects();
this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true);
}
}
}

View File

@@ -303,7 +303,11 @@ namespace ts.server {
// if we have a program - release all files that are enlisted in program
for (const f of this.program.getSourceFiles()) {
const info = this.projectService.getScriptInfo(f.fileName);
info.detachFromProject(this);
// We might not find the script info in case its not associated with the project any more
// and project graph was not updated (eg delayed update graph in case of files changed/deleted on the disk)
if (info) {
info.detachFromProject(this);
}
}
}
if (!this.program || !this.languageServiceEnabled) {
@@ -555,7 +559,6 @@ namespace ts.server {
* @returns: true if set of files in the project stays the same and false - otherwise.
*/
updateGraph(): boolean {
// TODO: (sheetalkamat) If reload scheduled do that before updating the graph
this.lsHost.startRecordingFilesWithChangedResolutions();
let hasChanges = this.updateGraphWorker();
@@ -662,8 +665,7 @@ namespace ts.server {
// When a missing file is created, we should update the graph.
this.markAsDirty();
// TODO: (sheetalkamat) schedule the update graph instead of doing it right away
this.updateGraph();
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
}
});
this.missingFilesMap.set(missingFilePath, fileWatcher);
@@ -980,6 +982,9 @@ namespace ts.server {
private typeRootsWatchers: FileWatcher[];
readonly canonicalConfigFilePath: NormalizedPath;
/* @internal */
pendingReload: boolean;
/*@internal*/
configFileSpecs: ConfigFileSpecs;
@@ -1001,6 +1006,19 @@ namespace ts.server {
this.enablePlugins();
}
/**
* Checks if the project has reload from disk pending, if thats pending, it reloads (and then updates graph as part of that) instead of just updating the graph
* @returns: true if set of files in the project stays the same and false - otherwise.
*/
updateGraph(): boolean {
if (this.pendingReload) {
this.pendingReload = false;
this.projectService.reloadConfiguredProject(this);
return true;
}
return super.updateGraph();
}
getCachedServerHost() {
return this.lsHost.host as CachedServerHost;
}

View File

@@ -459,15 +459,6 @@ namespace ts.server {
}
}
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
this.host.setTimeout(() => {
if (matchSeq(seq)) {
// TODO: (sheetalkamat) is this ensureRefereshedProjects instead ?
this.projectService.refreshInferredProjects();
}
}, ms);
}
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) {
if (followMs > ms) {
followMs = ms;
@@ -1302,7 +1293,7 @@ namespace ts.server {
scriptInfo.editContent(start, end, args.insertString);
this.changeSeq++;
}
this.updateProjectStructure(this.changeSeq, n => n === this.changeSeq);
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
}