Skip to content

Commit ca7a3af

Browse files
Always update LS state through Project (microsoft#56356)
Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
1 parent 80ab111 commit ca7a3af

File tree

5 files changed

+378
-1
lines changed

5 files changed

+378
-1
lines changed

src/server/project.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,14 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
13301330
this.hasAddedOrRemovedSymlinks = true;
13311331
}
13321332

1333+
/** @internal */
1334+
updateFromProjectInProgress = false;
1335+
1336+
/** @internal */
1337+
updateFromProject() {
1338+
updateProjectIfDirty(this);
1339+
}
1340+
13331341
/**
13341342
* Updates set of files that contribute to this project
13351343
* @returns: true if set of files in the project stays the same and false - otherwise.
@@ -1523,8 +1531,10 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
15231531
this.hasInvalidatedResolutions = hasInvalidatedResolutions;
15241532
this.hasInvalidatedLibResolutions = hasInvalidatedLibResolutions;
15251533
this.resolutionCache.startCachingPerDirectoryResolution();
1526-
this.program = this.languageService.getProgram(); // TODO: GH#18217
15271534
this.dirty = false;
1535+
this.updateFromProjectInProgress = true;
1536+
this.program = this.languageService.getProgram(); // TODO: GH#18217
1537+
this.updateFromProjectInProgress = false;
15281538
tracing?.push(tracing.Phase.Session, "finishCachingPerDirectoryResolution");
15291539
this.resolutionCache.finishCachingPerDirectoryResolution(this.program, oldProgram);
15301540
tracing?.pop();

src/services/services.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,15 @@ export function createLanguageService(
16201620
}
16211621

16221622
function synchronizeHostData(): void {
1623+
if (host.updateFromProject && !host.updateFromProjectInProgress) {
1624+
host.updateFromProject();
1625+
}
1626+
else {
1627+
synchronizeHostDataWorker();
1628+
}
1629+
}
1630+
1631+
function synchronizeHostDataWorker(): void {
16231632
Debug.assert(languageServiceMode !== LanguageServiceMode.Syntactic);
16241633
// perform fast check if host supports it
16251634
if (host.getProjectVersion) {

src/services/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ export interface IncompleteCompletionsCache {
316316
export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalResolutionCacheHost {
317317
getCompilationSettings(): CompilerOptions;
318318
getNewLine?(): string;
319+
/** @internal */ updateFromProject?(): void;
320+
/** @internal */ updateFromProjectInProgress?: boolean;
321+
319322
getProjectVersion?(): string;
320323
getScriptFileNames(): string[];
321324
getScriptKind?(fileName: string): ScriptKind;

src/testRunner/unittests/tsserver/plugins.ts

+37
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
2828
create(info: ts.server.PluginCreateInfo) {
2929
info.session?.addProtocolHandler(testProtocolCommand, request => {
3030
session.logger.log(`addProtocolHandler: ${jsonToReadableText(request)}`);
31+
// Assume this one needs program
32+
info.languageService.getProgram();
3133
return {
3234
response: testProtocolCommandResponse,
3335
};
@@ -101,6 +103,41 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
101103
baselineTsserverLogs("plugins", "With session and custom protocol message", session);
102104
});
103105

106+
it("when plugins use LS to get program and update is pending", () => {
107+
const pluginName = "some-plugin";
108+
const aTs: File = {
109+
path: "/user/username/projects/project/a.ts",
110+
content: `/// <reference path="./b.ts"/>`,
111+
};
112+
const tsconfig: File = {
113+
path: "/user/username/projects/project/tsconfig.json",
114+
content: jsonToReadableText({
115+
compilerOptions: {
116+
plugins: [
117+
{ name: pluginName },
118+
],
119+
},
120+
}),
121+
};
122+
123+
const { session, host } = createHostWithPlugin([aTs, tsconfig, libFile]);
124+
125+
openFilesForSession([aTs], session);
126+
// Write the missing file (referenced by 'a.ts') to schedule an update.
127+
host.writeFile("/user/username/projects/project/b.ts", "const y = 10;");
128+
129+
// This should update the language service with a new program.
130+
session.executeCommandSeq({
131+
command: testProtocolCommand,
132+
arguments: testProtocolCommandRequest,
133+
});
134+
135+
// This results in a program update.
136+
host.runQueuedTimeoutCallbacks();
137+
138+
baselineTsserverLogs("plugins", "when plugins use LS to get program and update is pending", session);
139+
});
140+
104141
it("gets external files with config file reload", () => {
105142
const aTs: File = { path: `/user/username/projects/myproject/a.ts`, content: `export const x = 10;` };
106143
const tsconfig: File = {

0 commit comments

Comments
 (0)