Skip to content

Commit 0428c31

Browse files
authored
feat: provide snippets for attribute (#1509)
1 parent 7a3ac82 commit 0428c31

File tree

8 files changed

+98
-4
lines changed

8 files changed

+98
-4
lines changed

client/src/client.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,12 @@ function constructArgs(ctx: vscode.ExtensionContext): string[] {
454454
args.push('--includeAutomaticOptionalChainCompletions');
455455
}
456456

457+
const includeCompletionsWithSnippetText =
458+
config.get<boolean>('angular.suggest.includeCompletionsWithSnippetText');
459+
if (includeCompletionsWithSnippetText) {
460+
args.push('--includeCompletionsWithSnippetText');
461+
}
462+
457463
const tsdk: string|null = config.get('typescript.tsdk', null);
458464
const tsProbeLocations = getProbeLocations(tsdk, ctx.extensionPath);
459465
args.push('--tsProbeLocations', tsProbeLocations.join(','));

integration/lsp/ivy_spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,60 @@ describe('auto-apply optional chaining', () => {
572572
});
573573
});
574574

575+
describe('insert snippet text', () => {
576+
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; /* 10 seconds */
577+
578+
let client: MessageConnection;
579+
beforeEach(async () => {
580+
client = createConnection({
581+
ivy: true,
582+
includeCompletionsWithSnippetText: true,
583+
});
584+
// If debugging, set to
585+
// - lsp.Trace.Messages to inspect request/response/notification, or
586+
// - lsp.Trace.Verbose to inspect payload
587+
client.trace(lsp.Trace.Off, createTracer());
588+
client.listen();
589+
await initializeServer(client);
590+
});
591+
592+
afterEach(() => {
593+
client.dispose();
594+
});
595+
596+
it('should be able to complete for an attribute with the value is empty', async () => {
597+
openTextDocument(client, FOO_TEMPLATE, `<my-app appOut></my-app>`);
598+
const languageServiceEnabled = await waitForNgcc(client);
599+
expect(languageServiceEnabled).toBeTrue();
600+
const response = await client.sendRequest(lsp.CompletionRequest.type, {
601+
textDocument: {
602+
uri: FOO_TEMPLATE_URI,
603+
},
604+
position: {line: 0, character: 14},
605+
}) as lsp.CompletionItem[];
606+
const completion = response.find(i => i.label === '(appOutput)')!;
607+
expect(completion.kind).toEqual(lsp.CompletionItemKind.Property);
608+
expect(completion.insertTextFormat).toEqual(lsp.InsertTextFormat.Snippet);
609+
expect((completion.textEdit as lsp.TextEdit).newText).toEqual('(appOutput)="$1"');
610+
});
611+
612+
it('should not be included in the completion for an attribute with a value', async () => {
613+
openTextDocument(client, FOO_TEMPLATE, `<my-app [appInput]="1"></my-app>`);
614+
const languageServiceEnabled = await waitForNgcc(client);
615+
expect(languageServiceEnabled).toBeTrue();
616+
const response = await client.sendRequest(lsp.CompletionRequest.type, {
617+
textDocument: {
618+
uri: FOO_TEMPLATE_URI,
619+
},
620+
position: {line: 0, character: 17},
621+
}) as lsp.CompletionItem[];
622+
const completion = response.find(i => i.label === 'appInput')!;
623+
expect(completion.kind).toEqual(lsp.CompletionItemKind.Property);
624+
expect(completion.insertTextFormat).toBeUndefined;
625+
expect((completion.textEdit as lsp.TextEdit).newText).toEqual('appInput');
626+
});
627+
});
628+
575629
function onNgccProgress(client: MessageConnection): Promise<string> {
576630
return new Promise(resolve => {
577631
client.onProgress(NgccProgressType, NgccProgressToken, (params: NgccProgress) => {

integration/lsp/test_utils.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {PROJECT_PATH, SERVER_PATH} from '../test_constants';
1717
export interface ServerOptions {
1818
ivy: boolean;
1919
includeAutomaticOptionalChainCompletions?: boolean;
20+
includeCompletionsWithSnippetText?: boolean;
2021
}
2122

2223
export function createConnection(serverOptions: ServerOptions): MessageConnection {
@@ -33,6 +34,9 @@ export function createConnection(serverOptions: ServerOptions): MessageConnectio
3334
if (serverOptions.includeAutomaticOptionalChainCompletions) {
3435
argv.push('--includeAutomaticOptionalChainCompletions');
3536
}
37+
if (serverOptions.includeCompletionsWithSnippetText) {
38+
argv.push('--includeCompletionsWithSnippetText');
39+
}
3640
const server = fork(SERVER_PATH, argv, {
3741
cwd: PROJECT_PATH,
3842
// uncomment to debug server process
@@ -61,7 +65,16 @@ export function initializeServer(client: MessageConnection): Promise<lsp.Initial
6165
*/
6266
processId: process.pid,
6367
rootUri: `file://${PROJECT_PATH}`,
64-
capabilities: {},
68+
capabilities: {
69+
textDocument: {
70+
completion: {
71+
completionItem: {
72+
snippetSupport: true,
73+
}
74+
},
75+
moniker: {},
76+
}
77+
},
6578
/**
6679
* Options are 'off' | 'messages' | 'verbose'.
6780
* To debug test failure, set to 'verbose'.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@
118118
"type": "boolean",
119119
"default": true,
120120
"description": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires TS 3.7+ and strict null checks to be enabled."
121+
},
122+
"angular.suggest.includeCompletionsWithSnippetText": {
123+
"type": "boolean",
124+
"default": true,
125+
"markdownDescription": "Enable/disable snippet completions from Angular language server. Requires using TypeScript 4.3+ in the workspace and the `legacy View Engine` option to be disabled."
121126
}
122127
}
123128
},

server/src/cmdline_utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface CommandLineOptions {
3838
ngProbeLocations: string[];
3939
tsProbeLocations: string[];
4040
includeAutomaticOptionalChainCompletions: boolean;
41+
includeCompletionsWithSnippetText: boolean;
4142
}
4243

4344
export function parseCommandLine(argv: string[]): CommandLineOptions {
@@ -51,6 +52,7 @@ export function parseCommandLine(argv: string[]): CommandLineOptions {
5152
tsProbeLocations: parseStringArray(argv, '--tsProbeLocations'),
5253
includeAutomaticOptionalChainCompletions:
5354
hasArgument(argv, '--includeAutomaticOptionalChainCompletions'),
55+
includeCompletionsWithSnippetText: hasArgument(argv, '--includeCompletionsWithSnippetText'),
5456
};
5557
}
5658

server/src/completion.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ export function tsCompletionEntryToLspCompletionItem(
125125
// range will include the dot. the `insertText` should be assigned to the `filterText` to filter
126126
// the completion items.
127127
item.filterText = entry.insertText;
128+
if (entry.isSnippet) {
129+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
130+
}
128131
}
129132

130133
item.data = {

server/src/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ function main() {
4444
resolvedNgLsPath: ng.resolvedPath,
4545
ivy: isG3 ? true : options.ivy,
4646
logToConsole: options.logToConsole,
47-
includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions
47+
includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions,
48+
includeCompletionsWithSnippetText: options.includeCompletionsWithSnippetText,
4849
});
4950

5051
// Log initialization info

server/src/session.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface SessionOptions {
3030
ivy: boolean;
3131
logToConsole: boolean;
3232
includeAutomaticOptionalChainCompletions: boolean;
33+
includeCompletionsWithSnippetText: boolean;
3334
}
3435

3536
enum LanguageId {
@@ -54,6 +55,8 @@ export class Session {
5455
private readonly logToConsole: boolean;
5556
private readonly openFiles = new MruTracker();
5657
private readonly includeAutomaticOptionalChainCompletions: boolean;
58+
private readonly includeCompletionsWithSnippetText: boolean;
59+
private snippetSupport: boolean|undefined;
5760
// Tracks the spawn order and status of the `ngcc` processes. This allows us to ensure we enable
5861
// the LS in the same order the projects were created in.
5962
private projectNgccQueue: Array<{project: ts.server.Project, done: boolean}> = [];
@@ -71,6 +74,7 @@ export class Session {
7174
constructor(options: SessionOptions) {
7275
this.includeAutomaticOptionalChainCompletions =
7376
options.includeAutomaticOptionalChainCompletions;
77+
this.includeCompletionsWithSnippetText = options.includeCompletionsWithSnippetText;
7478
this.logger = options.logger;
7579
this.ivy = options.ivy;
7680
this.logToConsole = options.logToConsole;
@@ -605,6 +609,8 @@ export class Session {
605609
}
606610

607611
private onInitialize(params: lsp.InitializeParams): lsp.InitializeResult {
612+
this.snippetSupport =
613+
params.capabilities.textDocument?.completion?.completionItem?.snippetSupport;
608614
const serverOptions: ServerOptions = {
609615
logFile: this.logger.getLogFileName(),
610616
};
@@ -1007,10 +1013,14 @@ export class Session {
10071013
const offset = lspPositionToTsPosition(scriptInfo, params.position);
10081014

10091015
let options: ts.GetCompletionsAtPositionOptions = {};
1010-
if (this.includeAutomaticOptionalChainCompletions) {
1016+
const includeCompletionsWithSnippetText =
1017+
this.includeCompletionsWithSnippetText && this.snippetSupport;
1018+
if (this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText) {
10111019
options = {
10121020
includeAutomaticOptionalChainCompletions: this.includeAutomaticOptionalChainCompletions,
1013-
includeCompletionsWithInsertText: this.includeAutomaticOptionalChainCompletions,
1021+
includeCompletionsWithSnippetText: includeCompletionsWithSnippetText,
1022+
includeCompletionsWithInsertText:
1023+
this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText,
10141024
};
10151025
}
10161026

0 commit comments

Comments
 (0)