Skip to content

Commit cd18da8

Browse files
committed
Ensure that file updates are reflected
1 parent 3bfa3d4 commit cd18da8

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

src/server/editorServices.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace ts.server {
1111
export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
1212
export const ProjectInfoTelemetryEvent = "projectInfo";
1313
export const OpenFileInfoTelemetryEvent = "openFileInfo";
14+
const ensureProjectForOpenFileSchedule = "*ensureProjectForOpenFiles*";
1415

1516
export interface ProjectsUpdatedInBackgroundEvent {
1617
eventName: typeof ProjectsUpdatedInBackgroundEvent;
@@ -878,7 +879,7 @@ namespace ts.server {
878879
/*@internal*/
879880
delayEnsureProjectForOpenFiles() {
880881
this.pendingEnsureProjectForOpenFiles = true;
881-
this.throttledOperations.schedule("*ensureProjectForOpenFiles*", /*delay*/ 2500, () => {
882+
this.throttledOperations.schedule(ensureProjectForOpenFileSchedule, /*delay*/ 2500, () => {
882883
if (this.pendingProjectUpdates.size !== 0) {
883884
this.delayEnsureProjectForOpenFiles();
884885
}
@@ -2796,6 +2797,21 @@ namespace ts.server {
27962797
// (and would separate out below reloading of projects to be called when immediate reload is needed)
27972798
// as there is no need to load contents of the files from the disk
27982799

2800+
// Reload script infos
2801+
this.filenameToScriptInfo.forEach(info => {
2802+
if (this.openFiles.has(info.path)) return; // Skip open files
2803+
if (!info.fileWatcher) return; // not watched file
2804+
// Handle as if file is changed or deleted
2805+
this.onSourceFileChanged(info, this.host.fileExists(info.fileName) ? FileWatcherEventKind.Changed : FileWatcherEventKind.Deleted);
2806+
});
2807+
// Cancel all project updates since we will be updating them now
2808+
this.pendingProjectUpdates.forEach((_project, projectName) => {
2809+
this.throttledOperations.cancel(projectName);
2810+
this.pendingProjectUpdates.delete(projectName);
2811+
});
2812+
this.throttledOperations.cancel(ensureProjectForOpenFileSchedule);
2813+
this.pendingEnsureProjectForOpenFiles = false;
2814+
27992815
// Reload Projects
28002816
this.reloadConfiguredProjectForFiles(this.openFiles as ESMap<Path, NormalizedPath | undefined>, /*clearSemanticCache*/ true, /*delayReload*/ false, returnTrue, "User requested reload projects");
28012817
this.externalProjects.forEach(project => {

src/testRunner/unittests/tsserver/reloadProjects.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,40 @@ namespace ts.projectSystem {
99
const file1: File = {
1010
path: `${tscWatch.projectRoot}/file1.ts`,
1111
content: `import { foo } from "module1";
12-
foo();`
12+
foo();
13+
import { bar } from "./file2";
14+
bar();`
1315
};
1416
const file2: File = {
1517
path: `${tscWatch.projectRoot}/file2.ts`,
16-
content: `${file1.content}
17-
export function bar(){}`
18+
content: `export function bar(){}`
1819
};
1920
const moduleFile: File = {
2021
path: `${tscWatch.projectRoot}/node_modules/module1/index.d.ts`,
2122
content: `export function foo(): string;`
2223
};
2324

25+
function verifyFileUpdates(host: TestServerHost, service: TestProjectService, project: server.Project) {
26+
// update file
27+
const updatedText = `${file2.content}
28+
bar();`;
29+
host.writeFile(file2.path, updatedText);
30+
host.checkTimeoutQueueLength(0);
31+
service.reloadProjects();
32+
assert.equal(project.getCurrentProgram()?.getSourceFile(file2.path)?.text, updatedText);
33+
34+
// delete file
35+
host.deleteFile(file2.path);
36+
host.checkTimeoutQueueLength(0);
37+
service.reloadProjects();
38+
assert.isUndefined(project.getCurrentProgram()?.getSourceFile(file2.path)?.text);
39+
assert.isUndefined(service.getScriptInfo(file2.path));
40+
}
41+
2442
it("configured project", () => {
2543
const host = createServerHost([configFile, libFile, file1, file2]);
2644
const service = createProjectService(host);
45+
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
2746
service.openClientFile(file1.path);
2847
checkNumberOfProjects(service, { configuredProjects: 1 });
2948
const project = service.configuredProjects.get(configFile.path)!;
@@ -37,18 +56,21 @@ namespace ts.projectSystem {
3756
checkNumberOfProjects(service, { configuredProjects: 1 });
3857
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
3958
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
59+
60+
verifyFileUpdates(host, service, project);
4061
});
4162

4263
it("inferred project", () => {
4364
const host = createServerHost([libFile, file1, file2]);
4465
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, });
66+
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
4567
const timeoutId = host.getNextTimeoutId();
4668
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
4769
host.clearTimeout(timeoutId);
4870
service.openClientFile(file1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, tscWatch.projectRoot);
4971
checkNumberOfProjects(service, { inferredProjects: 1 });
5072
const project = service.inferredProjects[0];
51-
checkProjectActualFiles(project, [libFile.path, file1.path]);
73+
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
5274

5375
// Install module1
5476
host.ensureFileOrFolder(moduleFile);
@@ -57,12 +79,15 @@ namespace ts.projectSystem {
5779
service.reloadProjects();
5880
checkNumberOfProjects(service, { inferredProjects: 1 });
5981
assert.strictEqual(service.inferredProjects[0], project);
60-
checkProjectActualFiles(project, [libFile.path, file1.path, moduleFile.path]);
82+
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
83+
84+
verifyFileUpdates(host, service, project);
6185
});
6286

6387
it("external project", () => {
6488
const host = createServerHost([libFile, file1, file2]);
6589
const service = createProjectService(host);
90+
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
6691
service.openExternalProject({
6792
projectFileName: `${tscWatch.projectRoot}/project.sln`,
6893
options: { excludeDirectories: ["node_modules"] },
@@ -81,11 +106,14 @@ namespace ts.projectSystem {
81106
checkNumberOfProjects(service, { externalProjects: 1 });
82107
assert.strictEqual(service.externalProjects[0], project);
83108
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
109+
110+
verifyFileUpdates(host, service, project);
84111
});
85112

86113
it("external project with config file", () => {
87114
const host = createServerHost([libFile, file1, file2, configFile]);
88115
const service = createProjectService(host);
116+
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
89117
service.openExternalProject({
90118
projectFileName: `${tscWatch.projectRoot}/project.sln`,
91119
options: { excludeDirectories: ["node_modules"] },
@@ -104,6 +132,8 @@ namespace ts.projectSystem {
104132
checkNumberOfProjects(service, { configuredProjects: 1 });
105133
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
106134
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
135+
136+
verifyFileUpdates(host, service, project);
107137
});
108138
});
109139
}

0 commit comments

Comments
 (0)