Skip to content
This repository was archived by the owner on Nov 21, 2025. It is now read-only.

Commit e1a7137

Browse files
authored
fix: only give html provider completions for inline templates (#1369)
Our client-side middleware retrieves html provider completions for inline templates because the template string is not otherwise detected as HTML by the registered HTML providers. We should only create this virtual document and retrieve HTML provider results for typescript files. Fixes #1357
1 parent 601e2f9 commit e1a7137

File tree

5 files changed

+70
-17
lines changed

5 files changed

+70
-17
lines changed

client/src/client.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,18 @@ export class AngularLanguageClient implements vscode.Disposable {
104104

105105
const angularResultsPromise = next(document, position, token);
106106

107-
const vdocUri = this.createVirtualHtmlDoc(document);
108-
const htmlProviderResultsPromise = vscode.commands.executeCommand<vscode.Hover[]>(
109-
'vscode.executeHoverProvider', vdocUri, position);
107+
// Include results for inline HTML via virtual document and native html providers.
108+
if (document.languageId === 'typescript') {
109+
const vdocUri = this.createVirtualHtmlDoc(document);
110+
const htmlProviderResultsPromise = vscode.commands.executeCommand<vscode.Hover[]>(
111+
'vscode.executeHoverProvider', vdocUri, position);
112+
113+
const [angularResults, htmlProviderResults] =
114+
await Promise.all([angularResultsPromise, htmlProviderResultsPromise]);
115+
return angularResults ?? htmlProviderResults?.[0];
116+
}
110117

111-
const [angularResults, htmlProviderResults] =
112-
await Promise.all([angularResultsPromise, htmlProviderResultsPromise]);
113-
return angularResults ?? htmlProviderResults?.[0];
118+
return angularResultsPromise;
114119
},
115120
provideSignatureHelp: async (
116121
document: vscode.TextDocument, position: vscode.Position,
@@ -133,17 +138,21 @@ export class AngularLanguageClient implements vscode.Disposable {
133138
const angularCompletionsPromise = next(document, position, context, token) as
134139
Promise<vscode.CompletionItem[]|null|undefined>;
135140

136-
const vdocUri = this.createVirtualHtmlDoc(document);
137-
// This will not include angular stuff because the vdoc is not associated with an angular
138-
// component
139-
const htmlProviderCompletionsPromise =
140-
vscode.commands.executeCommand<vscode.CompletionList>(
141-
'vscode.executeCompletionItemProvider', vdocUri, position,
142-
context.triggerCharacter);
143-
const [angularCompletions, htmlProviderCompletions] =
144-
await Promise.all([angularCompletionsPromise, htmlProviderCompletionsPromise]);
145-
146-
return [...(angularCompletions ?? []), ...(htmlProviderCompletions?.items ?? [])];
141+
// Include results for inline HTML via virtual document and native html providers.
142+
if (document.languageId === 'typescript') {
143+
const vdocUri = this.createVirtualHtmlDoc(document);
144+
// This will not include angular stuff because the vdoc is not associated with an
145+
// angular component
146+
const htmlProviderCompletionsPromise =
147+
vscode.commands.executeCommand<vscode.CompletionList>(
148+
'vscode.executeCompletionItemProvider', vdocUri, position,
149+
context.triggerCharacter);
150+
const [angularCompletions, htmlProviderCompletions] =
151+
await Promise.all([angularCompletionsPromise, htmlProviderCompletionsPromise]);
152+
return [...(angularCompletions ?? []), ...(htmlProviderCompletions?.items ?? [])];
153+
}
154+
155+
return angularCompletionsPromise;
147156
}
148157
}
149158
};

integration/e2e/completion_spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import * as vscode from 'vscode';
3+
4+
import {activate, COMPLETION_COMMAND, FOO_TEMPLATE_URI} from './helper';
5+
6+
describe('Angular Ivy LS completions', () => {
7+
beforeAll(async () => {
8+
await activate(FOO_TEMPLATE_URI);
9+
});
10+
11+
it(`does not duplicate HTML completions in external templates`, async () => {
12+
const position = new vscode.Position(0, 0);
13+
const completionItem = await vscode.commands.executeCommand<vscode.CompletionList>(
14+
COMPLETION_COMMAND, FOO_TEMPLATE_URI, position);
15+
const regionCompletions = (completionItem?.items?.filter(i => i.label === '#region') ?? []);
16+
expect(regionCompletions.length).toBe(1);
17+
});
18+
});

integration/e2e/helper.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import * as vscode from 'vscode';
2+
import {APP_COMPONENT, FOO_TEMPLATE} from '../test_constants';
3+
4+
export const COMPLETION_COMMAND = 'vscode.executeCompletionItemProvider';
5+
export const HOVER_COMMAND = 'vscode.executeHoverProvider';
6+
export const DEFINITION_COMMAND = 'vscode.executeDefinitionProvider';
7+
export const APP_COMPONENT_URI = vscode.Uri.file(APP_COMPONENT);
8+
export const FOO_TEMPLATE_URI = vscode.Uri.file(FOO_TEMPLATE);
29

310
function sleep(ms: number) {
411
return new Promise(resolve => setTimeout(resolve, ms));

integration/e2e/hover_spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as vscode from 'vscode';
2+
3+
import {activate, FOO_TEMPLATE_URI, HOVER_COMMAND} from './helper';
4+
5+
describe('Angular Ivy LS quick info', () => {
6+
beforeAll(async () => {
7+
await activate(FOO_TEMPLATE_URI);
8+
});
9+
10+
it(`returns quick info from built in extension for class in template`, async () => {
11+
const position = new vscode.Position(1, 8);
12+
const quickInfo = await vscode.commands.executeCommand<vscode.Hover[]>(
13+
HOVER_COMMAND, FOO_TEMPLATE_URI, position);
14+
expect(quickInfo?.length).toBe(1);
15+
});
16+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
{{title | uppercase}}
2+
<span class="subtitle">
3+
subtitle
4+
</span>

0 commit comments

Comments
 (0)