mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-23 07:07:09 -05:00
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:
@@ -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]);
|
||||
|
||||
@@ -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]);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user