Skip to content

Commit a943874

Browse files
andrius-praayazhafiz
authored andcommitted
fix: reset loading status when the language service fails to load the project. (#468)
Currently, the loading indicator remains in pending state when the language service fails to load the project. Typescript project server doesn't send any event when the language service fails to load the project. This commit refactors the loading indicator to reset state when any error occurs while opening the document.
1 parent caff692 commit a943874

File tree

5 files changed

+72
-54
lines changed

5 files changed

+72
-54
lines changed

client/src/extension.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,32 +50,33 @@ export function activate(context: vscode.ExtensionContext) {
5050

5151
// Push the disposable to the context's subscriptions so that the
5252
// client can be deactivated on extension deactivation
53-
context.subscriptions.push(
54-
...registerCommands(client),
55-
client.start(),
56-
);
57-
58-
client.onReady().then(() => {
59-
const projectLoadingTasks = new Map<string, {resolve: () => void}>();
60-
61-
client.onNotification(projectLoadingNotification.start, (projectName: string) => {
62-
vscode.window.withProgress(
63-
{
64-
location: vscode.ProgressLocation.Window,
65-
title: 'Initializing Angular language features',
66-
},
67-
() => new Promise((resolve) => {
68-
projectLoadingTasks.set(projectName, {resolve});
69-
}));
70-
});
71-
72-
client.onNotification(projectLoadingNotification.finish, (projectName: string) => {
73-
const task = projectLoadingTasks.get(projectName);
74-
if (task) {
75-
task.resolve();
76-
projectLoadingTasks.delete(projectName);
77-
}
78-
});
53+
context.subscriptions.push(...registerCommands(client), client.start());
54+
55+
client.onDidChangeState((e) => {
56+
let task: {resolve: () => void}|undefined;
57+
if (e.newState == lsp.State.Running) {
58+
client.onNotification(projectLoadingNotification.start, () => {
59+
if (task) {
60+
task.resolve();
61+
task = undefined;
62+
}
63+
vscode.window.withProgress(
64+
{
65+
location: vscode.ProgressLocation.Window,
66+
title: 'Initializing Angular language features',
67+
},
68+
() => new Promise((resolve) => {
69+
task = {resolve};
70+
}));
71+
});
72+
73+
client.onNotification(projectLoadingNotification.finish, () => {
74+
if (task) {
75+
task.resolve();
76+
task = undefined;
77+
}
78+
});
79+
}
7980
});
8081
}
8182

client/src/protocol.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {NotificationType} from 'vscode-languageclient';
9+
import {NotificationType0} from 'vscode-languageclient';
1010

1111
export const projectLoadingNotification = {
12-
start: new NotificationType<string, string>('angular-language-service/projectLoadingStart'),
13-
finish: new NotificationType<string, string>('angular-language-service/projectLoadingFinish')
14-
};
12+
start: new NotificationType0<string>('angular-language-service/projectLoadingStart'),
13+
finish: new NotificationType0<string>('angular-language-service/projectLoadingFinish')
14+
};

integration/lsp/smoke_spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ describe('Angular Language Service', () => {
5454
});
5555
server.on('message', (data: Message) => {
5656
if (isNotificationMessage(data)) {
57-
console.log('[server]', data.params.message);
57+
const toLog = ['[server]', data.method];
58+
if (data.params && data.params.message) toLog.push(data.params.message);
59+
console.log(...toLog);
5860
} else {
5961
responseEmitter.emit('response', data);
6062
}

server/src/protocol.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {NotificationType} from 'vscode-languageserver';
9+
import {NotificationType0} from 'vscode-languageserver';
1010

1111
export const projectLoadingNotification = {
12-
start: new NotificationType<string, string>('angular-language-service/projectLoadingStart'),
13-
finish: new NotificationType<string, string>('angular-language-service/projectLoadingFinish')
12+
start: new NotificationType0<string>('angular-language-service/projectLoadingStart'),
13+
finish: new NotificationType0<string>('angular-language-service/projectLoadingFinish')
1414
};

server/src/session.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class Session {
3939
private readonly connection: lsp.IConnection;
4040
private readonly projectService: ProjectService;
4141
private diagnosticsTimeout: NodeJS.Timeout|null = null;
42+
private isProjectLoading = false;
4243

4344
constructor(options: SessionOptions) {
4445
// Create a connection for the server. The connection uses Node's IPC as a transport.
@@ -79,14 +80,17 @@ export class Session {
7980
private handleProjectServiceEvent(event: ts.server.ProjectServiceEvent) {
8081
switch (event.eventName) {
8182
case ts.server.ProjectLoadingStartEvent:
82-
this.connection.sendNotification(
83-
projectLoadingNotification.start, event.data.project.projectName);
83+
this.isProjectLoading = true;
84+
this.connection.sendNotification(projectLoadingNotification.start);
8485
break;
8586
case ts.server.ProjectLoadingFinishEvent: {
8687
const {project} = event.data;
8788
// Disable language service if project is not Angular
8889
this.checkIsAngularProject(project);
89-
this.connection.sendNotification(projectLoadingNotification.finish, project.projectName);
90+
if (this.isProjectLoading) {
91+
this.isProjectLoading = false;
92+
this.connection.sendNotification(projectLoadingNotification.finish);
93+
}
9094
break;
9195
}
9296
case ts.server.ProjectsUpdatedInBackgroundEvent:
@@ -172,24 +176,35 @@ export class Session {
172176
// is up-to-date when a file is first opened in the editor.
173177
// In this case, we should not pass fileContent to projectService.
174178
const fileContent = undefined;
175-
const result = this.projectService.openClientFile(filePath, fileContent, scriptKind);
179+
try {
180+
const result = this.projectService.openClientFile(filePath, fileContent, scriptKind);
176181

177-
const {configFileName, configFileErrors} = result;
178-
if (configFileErrors && configFileErrors.length) {
179-
// configFileErrors is an empty array even if there's no error, so check length.
180-
this.connection.console.error(configFileErrors.map(e => e.messageText).join('\n'));
181-
}
182-
if (!configFileName) {
183-
this.connection.console.error(`No config file for ${filePath}`);
184-
return;
185-
}
186-
const project = this.projectService.findProject(configFileName);
187-
if (!project) {
188-
this.connection.console.error(`Failed to find project for ${filePath}`);
189-
return;
190-
}
191-
if (project.languageServiceEnabled) {
192-
project.refreshDiagnostics(); // Show initial diagnostics
182+
const {configFileName, configFileErrors} = result;
183+
if (configFileErrors && configFileErrors.length) {
184+
// configFileErrors is an empty array even if there's no error, so check length.
185+
this.connection.console.error(configFileErrors.map(e => e.messageText).join('\n'));
186+
}
187+
if (!configFileName) {
188+
this.connection.console.error(`No config file for ${filePath}`);
189+
return;
190+
}
191+
const project = this.projectService.findProject(configFileName);
192+
if (!project) {
193+
this.connection.console.error(`Failed to find project for ${filePath}`);
194+
return;
195+
}
196+
if (project.languageServiceEnabled) {
197+
project.refreshDiagnostics(); // Show initial diagnostics
198+
}
199+
} catch (error) {
200+
if (this.isProjectLoading) {
201+
this.isProjectLoading = false;
202+
this.connection.sendNotification(projectLoadingNotification.finish);
203+
}
204+
if (error.stack) {
205+
this.error(error.stack);
206+
}
207+
throw error;
193208
}
194209
}
195210

0 commit comments

Comments
 (0)