From 4d3a8ae210376ccbed854ffbe0364e33adcffef3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 10:57:11 -0700 Subject: [PATCH] Add tag closing completions Part of #34307. Add manual completions for closing jsx tags. Requires TS 3.0 --- .../src/features/tagCompletion.ts | 66 +++++++++++++++++++ .../src/languageProvider.ts | 1 + .../src/protocol.d.ts | 2 +- .../src/typescriptService.ts | 6 ++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 extensions/typescript-language-features/src/features/tagCompletion.ts diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts new file mode 100644 index 0000000000000..8a8495180ca37 --- /dev/null +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import * as typeConverters from '../utils/typeConverters'; + +class TypeScriptTagCompletion implements vscode.CompletionItemProvider { + constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + _context: vscode.CompletionContext + ): Promise { + const filepath = this.client.toPath(document.uri); + if (!filepath) { + return undefined; + } + + const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + let body: Proto.TextInsertion | undefined = undefined; + try { + const response = await this.client.execute('jsxClosingTag', args, token); + body = response && response.body; + if (!body) { + return undefined; + } + } catch { + return undefined; + } + + return [this.getCompletion(body)]; + } + + private getCompletion(body: Proto.TextInsertion) { + const completion = new vscode.CompletionItem(body.newText); + completion.insertText = this.getTagSnippet(body); + return completion; + } + + private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString { + const snippet = new vscode.SnippetString(); + snippet.appendPlaceholder('', 0); + snippet.appendText(closingTag.newText); + return snippet; + } +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient, +) { + return new VersionDependentRegistration(client, API.v300, () => + vscode.languages.registerCompletionItemProvider(selector, + new TypeScriptTagCompletion(client), + '>')); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index a00f7e9a47045..1776fd1b8fc5b 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -91,6 +91,7 @@ export default class LanguageProvider { this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); this.disposables.push((await import('./features/rename')).register(selector, this.client)); this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client)); + this.disposables.push((await import('./features/tagCompletion')).register(selector, this.client)); this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client)); this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); } diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index 0015b80d9276e..6e926eb8d7ee2 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,2 +1,2 @@ import * as Proto from 'typescript/lib/protocol'; -export = Proto; \ No newline at end of file +export = Proto; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index ef58af1e6350a..8c5b240f2acb1 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -11,6 +11,11 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import BufferSyncSupport from './features/bufferSyncSupport'; +declare module './protocol' { + export type JsxClosingTagRequestArgs = any; + export type JsxClosingTagResponse = any; +} + export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). @@ -78,6 +83,7 @@ export interface ITypeScriptServiceClient { execute(command: 'organizeImports', args: Proto.OrganizeImportsRequestArgs, token?: CancellationToken): Promise; execute(command: 'getOutliningSpans', args: Proto.FileRequestArgs, token: CancellationToken): Promise; execute(command: 'getEditsForFileRename', args: Proto.GetEditsForFileRenameRequestArgs): Promise; + execute(command: 'jsxClosingTag', args: Proto.JsxClosingTagRequestArgs, token: CancellationToken): Promise; execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise; executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: CancellationToken): Promise;