Skip to content

Commit ff5dcda

Browse files
author
Keen Yee Liau
committed
fix: mark project as dirty after enabling language service
This commit fixes a bug in which the program becomes unavailable after the language service is re-enabled, resulting in a crash. When the language service is disabled, the program gets discarded. However, when the language service is re-enabled, the program is not recreated. This bug was difficult to reproduce because it was masked by the fact that during normal startup, ngcc gets run, and in the process, ngcc generates a lockfile named `__ngcc_lock_file__` in the `node_modules` directory. This triggers the directory watcher, and through a long chain of events, reloads the Configured project, therefore re-creating the program. Without ngcc run (make it a no-op), the extension will crash because the language service is unable to return the program.
1 parent bede95a commit ff5dcda

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

integration/lsp/ivy_spec.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {MessageConnection} from 'vscode-jsonrpc';
10-
10+
import * as lsp from 'vscode-languageserver-protocol';
1111
import {APP_COMPONENT, createConnection, initializeServer, openTextDocument} from './test_utils';
1212

1313
describe('Angular Ivy language server', () => {
@@ -43,13 +43,25 @@ describe('Angular Ivy language server', () => {
4343
it('should re-enable language service once ngcc has completed', async () => {
4444
await initializeServer(client);
4545
openTextDocument(client, APP_COMPONENT);
46-
const configFilePath = await onRunNgccNotification(client);
47-
client.sendNotification('angular/ngccComplete', {
48-
success: true,
49-
configFilePath,
50-
});
51-
const languageServiceEnabled = await onLanguageServiceStateNotification(client);
46+
const languageServiceEnabled = await waitForNgcc(client);
47+
expect(languageServiceEnabled).toBeTrue();
48+
});
49+
50+
it('should handle hover on inline template', async () => {
51+
await initializeServer(client);
52+
openTextDocument(client, APP_COMPONENT);
53+
const languageServiceEnabled = await waitForNgcc(client);
5254
expect(languageServiceEnabled).toBeTrue();
55+
const response = await client.sendRequest(lsp.HoverRequest.type, {
56+
textDocument: {
57+
uri: `file://${APP_COMPONENT}`,
58+
},
59+
position: {line: 4, character: 25},
60+
});
61+
expect(response?.contents).toContain({
62+
language: 'typescript',
63+
value: '(property) AppComponent.name: string',
64+
});
5365
});
5466
});
5567

@@ -74,3 +86,13 @@ function onLanguageServiceStateNotification(client: MessageConnection): Promise<
7486
});
7587
});
7688
}
89+
90+
async function waitForNgcc(client: MessageConnection): Promise<boolean> {
91+
const configFilePath = await onRunNgccNotification(client);
92+
// We run ngcc before the test, so no need to do anything here.
93+
client.sendNotification('angular/ngccComplete', {
94+
success: true,
95+
configFilePath,
96+
});
97+
return onLanguageServiceStateNotification(client);
98+
}

scripts/test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set -ex -o pipefail
66
(
77
cd integration/project
88
yarn
9+
yarn ngcc
910
)
1011

1112
# Server unit tests

server/src/session.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ export class Session {
125125
return;
126126
}
127127
project.enableLanguageService();
128+
// When the language service got disabled, the program was discarded via
129+
// languageService.cleanupSemanticCache(). However, the program is not
130+
// recreated when the language service is re-enabled. We manually mark the
131+
// project as dirty to force update the graph.
132+
project.markAsDirty();
128133
this.info(`Enabling Ivy language service for ${project.projectName}.`);
129134
}
130135

0 commit comments

Comments
 (0)