From 0428c31faca3b977140191cba6cd9a90ff7fdf7c Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 15 Oct 2021 03:22:40 +0800 Subject: [PATCH] feat: provide snippets for attribute (#1509) --- client/src/client.ts | 6 ++++ integration/lsp/ivy_spec.ts | 54 +++++++++++++++++++++++++++++++++++ integration/lsp/test_utils.ts | 15 +++++++++- package.json | 5 ++++ server/src/cmdline_utils.ts | 2 ++ server/src/completion.ts | 3 ++ server/src/server.ts | 3 +- server/src/session.ts | 14 +++++++-- 8 files changed, 98 insertions(+), 4 deletions(-) diff --git a/client/src/client.ts b/client/src/client.ts index 36416613f8..4a9d409039 100644 --- a/client/src/client.ts +++ b/client/src/client.ts @@ -454,6 +454,12 @@ function constructArgs(ctx: vscode.ExtensionContext): string[] { args.push('--includeAutomaticOptionalChainCompletions'); } + const includeCompletionsWithSnippetText = + config.get('angular.suggest.includeCompletionsWithSnippetText'); + if (includeCompletionsWithSnippetText) { + args.push('--includeCompletionsWithSnippetText'); + } + const tsdk: string|null = config.get('typescript.tsdk', null); const tsProbeLocations = getProbeLocations(tsdk, ctx.extensionPath); args.push('--tsProbeLocations', tsProbeLocations.join(',')); diff --git a/integration/lsp/ivy_spec.ts b/integration/lsp/ivy_spec.ts index 9193e78a9c..f8e11b0dd1 100644 --- a/integration/lsp/ivy_spec.ts +++ b/integration/lsp/ivy_spec.ts @@ -572,6 +572,60 @@ describe('auto-apply optional chaining', () => { }); }); +describe('insert snippet text', () => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; /* 10 seconds */ + + let client: MessageConnection; + beforeEach(async () => { + client = createConnection({ + ivy: true, + includeCompletionsWithSnippetText: true, + }); + // If debugging, set to + // - lsp.Trace.Messages to inspect request/response/notification, or + // - lsp.Trace.Verbose to inspect payload + client.trace(lsp.Trace.Off, createTracer()); + client.listen(); + await initializeServer(client); + }); + + afterEach(() => { + client.dispose(); + }); + + it('should be able to complete for an attribute with the value is empty', async () => { + openTextDocument(client, FOO_TEMPLATE, ``); + const languageServiceEnabled = await waitForNgcc(client); + expect(languageServiceEnabled).toBeTrue(); + const response = await client.sendRequest(lsp.CompletionRequest.type, { + textDocument: { + uri: FOO_TEMPLATE_URI, + }, + position: {line: 0, character: 14}, + }) as lsp.CompletionItem[]; + const completion = response.find(i => i.label === '(appOutput)')!; + expect(completion.kind).toEqual(lsp.CompletionItemKind.Property); + expect(completion.insertTextFormat).toEqual(lsp.InsertTextFormat.Snippet); + expect((completion.textEdit as lsp.TextEdit).newText).toEqual('(appOutput)="$1"'); + }); + + it('should not be included in the completion for an attribute with a value', async () => { + openTextDocument(client, FOO_TEMPLATE, ``); + const languageServiceEnabled = await waitForNgcc(client); + expect(languageServiceEnabled).toBeTrue(); + const response = await client.sendRequest(lsp.CompletionRequest.type, { + textDocument: { + uri: FOO_TEMPLATE_URI, + }, + position: {line: 0, character: 17}, + }) as lsp.CompletionItem[]; + const completion = response.find(i => i.label === 'appInput')!; + expect(completion.kind).toEqual(lsp.CompletionItemKind.Property); + expect(completion.insertTextFormat).toBeUndefined; + expect((completion.textEdit as lsp.TextEdit).newText).toEqual('appInput'); + }); +}); + function onNgccProgress(client: MessageConnection): Promise { return new Promise(resolve => { client.onProgress(NgccProgressType, NgccProgressToken, (params: NgccProgress) => { diff --git a/integration/lsp/test_utils.ts b/integration/lsp/test_utils.ts index b868d03f13..0d6a28befe 100644 --- a/integration/lsp/test_utils.ts +++ b/integration/lsp/test_utils.ts @@ -17,6 +17,7 @@ import {PROJECT_PATH, SERVER_PATH} from '../test_constants'; export interface ServerOptions { ivy: boolean; includeAutomaticOptionalChainCompletions?: boolean; + includeCompletionsWithSnippetText?: boolean; } export function createConnection(serverOptions: ServerOptions): MessageConnection { @@ -33,6 +34,9 @@ export function createConnection(serverOptions: ServerOptions): MessageConnectio if (serverOptions.includeAutomaticOptionalChainCompletions) { argv.push('--includeAutomaticOptionalChainCompletions'); } + if (serverOptions.includeCompletionsWithSnippetText) { + argv.push('--includeCompletionsWithSnippetText'); + } const server = fork(SERVER_PATH, argv, { cwd: PROJECT_PATH, // uncomment to debug server process @@ -61,7 +65,16 @@ export function initializeServer(client: MessageConnection): Promise = []; @@ -71,6 +74,7 @@ export class Session { constructor(options: SessionOptions) { this.includeAutomaticOptionalChainCompletions = options.includeAutomaticOptionalChainCompletions; + this.includeCompletionsWithSnippetText = options.includeCompletionsWithSnippetText; this.logger = options.logger; this.ivy = options.ivy; this.logToConsole = options.logToConsole; @@ -605,6 +609,8 @@ export class Session { } private onInitialize(params: lsp.InitializeParams): lsp.InitializeResult { + this.snippetSupport = + params.capabilities.textDocument?.completion?.completionItem?.snippetSupport; const serverOptions: ServerOptions = { logFile: this.logger.getLogFileName(), }; @@ -1007,10 +1013,14 @@ export class Session { const offset = lspPositionToTsPosition(scriptInfo, params.position); let options: ts.GetCompletionsAtPositionOptions = {}; - if (this.includeAutomaticOptionalChainCompletions) { + const includeCompletionsWithSnippetText = + this.includeCompletionsWithSnippetText && this.snippetSupport; + if (this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText) { options = { includeAutomaticOptionalChainCompletions: this.includeAutomaticOptionalChainCompletions, - includeCompletionsWithInsertText: this.includeAutomaticOptionalChainCompletions, + includeCompletionsWithSnippetText: includeCompletionsWithSnippetText, + includeCompletionsWithInsertText: + this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText, }; }