From 51893f971d5f36dc7df61e2d74d6a1d34f631669 Mon Sep 17 00:00:00 2001 From: chemzqm <chemzqm@gmail.com> Date: Tue, 4 Dec 2018 13:38:12 +0800 Subject: [PATCH] rework function snippet --- src/server/features/completionItemProvider.ts | 59 ++++------------- src/server/protocol.const.ts | 35 +++++++++- src/server/utils/completionItem.ts | 66 ++++++++++++++++++- 3 files changed, 112 insertions(+), 48 deletions(-) diff --git a/src/server/features/completionItemProvider.ts b/src/server/features/completionItemProvider.ts index bc1c2ce..a78c36d 100644 --- a/src/server/features/completionItemProvider.ts +++ b/src/server/features/completionItemProvider.ts @@ -10,7 +10,7 @@ import * as PConst from '../protocol.const' import { ITypeScriptServiceClient } from '../typescriptService' import API from '../utils/api' import { applyCodeAction } from '../utils/codeAction' -import { convertCompletionEntry } from '../utils/completionItem' +import { convertCompletionEntry, getParameterListParts } from '../utils/completionItem' import * as Previewer from '../utils/previewer' import * as typeConverters from '../utils/typeConverters' import TypingsStatus from '../utils/typingsStatus' @@ -337,52 +337,21 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP item: CompletionItem, detail: Proto.CompletionEntryDetails ): void { - let hasOptionalParameters = false - let hasAddedParameters = false - - let snippet = '' - const methodName = detail.displayParts.find( - part => part.kind === 'methodName' - ) - snippet += item.insertText || (methodName && methodName.text) || item.label // tslint:disable-line - snippet += '(' - let holderIndex = 1 - let parenCount = 0 - let i = 0 - for (; i < detail.displayParts.length; ++i) { - const part = detail.displayParts[i] - // Only take top level paren names - if (part.kind === 'parameterName' && parenCount === 1) { - const next = detail.displayParts[i + 1] - // Skip optional parameters - const nameIsFollowedByOptionalIndicator = next && next.text === '?' - if (!nameIsFollowedByOptionalIndicator) { - if (hasAddedParameters) snippet += ', ' - hasAddedParameters = true - // tslint:disable-next-line:no-invalid-template-strings - snippet += '${' + holderIndex + ':' + part.text + '}' - holderIndex = holderIndex + 1 - } - hasOptionalParameters = - hasOptionalParameters || nameIsFollowedByOptionalIndicator - } else if (part.kind === 'punctuation') { - if (part.text === '(') { - ++parenCount - } else if (part.text === ')') { - --parenCount - } else if (part.text === '...' && parenCount === 1) { - // Found rest parmeter. Do not fill in any further arguments - hasOptionalParameters = true - break - } + let { displayParts } = detail + let snippet = (item.insertText || item.label) + '(' // tslint:disable-line + const parameterListParts = getParameterListParts(displayParts) + let { parts, hasOptionalParameters } = parameterListParts + let idx = 1 + for (let part of parts) { + snippet += '${' + idx + ':' + part.text + '}' // tslint:disable-line + if (idx == parts.length) { + if (hasOptionalParameters) snippet += '${' + (idx + 1) + '}' // tslint:disable-line + } else { + snippet += ', ' } + idx = idx + 1 } - if (hasOptionalParameters) { - // tslint:disable-next-line:no-invalid-template-strings - snippet += '${' + holderIndex + '}' - } - snippet += ')' - snippet += '$0' + snippet += ')$0' // tslint:disable-next-line:deprecation item.insertText = snippet } diff --git a/src/server/protocol.const.ts b/src/server/protocol.const.ts index 54010a6..7d047e4 100644 --- a/src/server/protocol.const.ts +++ b/src/server/protocol.const.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + export class Kind { public static readonly alias = 'alias' public static readonly callSignature = 'call' @@ -12,7 +13,6 @@ export class Kind { public static readonly directory = 'directory' public static readonly enum = 'enum' public static readonly externalModuleName = 'external module name' - public static readonly file = 'file' public static readonly function = 'function' public static readonly indexSignature = 'index' public static readonly interface = 'interface' @@ -30,10 +30,43 @@ export class Kind { public static readonly type = 'type' public static readonly variable = 'var' public static readonly warning = 'warning' + public static readonly string = 'string' + public static readonly parameter = 'parameter' } + export class DiagnosticCategory { public static readonly error = 'error' public static readonly warning = 'warning' public static readonly suggestion = 'suggestion' } + +export class KindModifiers { + public static readonly optional = 'optional' + public static readonly color = 'color' + + public static readonly dtsFile = '.d.ts' + public static readonly tsFile = '.ts' + public static readonly tsxFile = '.tsx' + public static readonly jsFile = '.js' + public static readonly jsxFile = '.jsx' + public static readonly jsonFile = '.json' + + public static readonly fileExtensionKindModifiers = [ + KindModifiers.dtsFile, + KindModifiers.tsFile, + KindModifiers.tsxFile, + KindModifiers.jsFile, + KindModifiers.jsxFile, + KindModifiers.jsonFile, + ] +} + +export class DisplayPartKind { + public static readonly functionName = 'functionName' + public static readonly methodName = 'methodName' + public static readonly parameterName = 'parameterName' + public static readonly propertyName = 'propertyName' + public static readonly punctuation = 'punctuation' + public static readonly text = 'text' +} diff --git a/src/server/utils/completionItem.ts b/src/server/utils/completionItem.ts index 70f3d64..02f8cc1 100644 --- a/src/server/utils/completionItem.ts +++ b/src/server/utils/completionItem.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { workspace } from 'coc.nvim' -import { CompletionItem, CompletionItemKind, InsertTextFormat, Position, TextEdit } from 'vscode-languageserver-protocol' +import { CompletionItem, CompletionItemKind, InsertTextFormat, Position } from 'vscode-languageserver-protocol' import * as Proto from '../protocol' import * as PConst from '../protocol.const' @@ -13,6 +13,11 @@ interface CommitCharactersSettings { readonly useCodeSnippetsOnMethodSuggest: boolean } +interface ParamterListParts { + readonly parts: ReadonlyArray<Proto.SymbolDisplayPart> + readonly hasOptionalParameters: boolean +} + export function convertCompletionEntry( tsEntry: Proto.CompletionEntry, uri: string, @@ -97,7 +102,6 @@ function convertKind(kind: string): CompletionItemKind { case PConst.Kind.interface: return CompletionItemKind.Interface case PConst.Kind.warning: - case PConst.Kind.file: case PConst.Kind.script: return CompletionItemKind.File case PConst.Kind.directory: @@ -140,3 +144,61 @@ function getCommitCharacters(tsEntry: Proto.CompletionEntry, settings: CommitCha } return commitCharacters.length === 0 ? undefined : commitCharacters } + +export function getParameterListParts( + displayParts: ReadonlyArray<Proto.SymbolDisplayPart> +): ParamterListParts { + const parts: Proto.SymbolDisplayPart[] = [] + let isInMethod = false + let hasOptionalParameters = false + let parenCount = 0 + let braceCount = 0 + + outer: for (let i = 0; i < displayParts.length; ++i) { + const part = displayParts[i] + switch (part.kind) { + case PConst.DisplayPartKind.methodName: + case PConst.DisplayPartKind.functionName: + case PConst.DisplayPartKind.text: + case PConst.DisplayPartKind.propertyName: + if (parenCount === 0 && braceCount === 0) { + isInMethod = true + } + break + + case PConst.DisplayPartKind.parameterName: + if (parenCount === 1 && isInMethod) { + // Only take top level paren names + const next = displayParts[i + 1] + // Skip optional parameters + const nameIsFollowedByOptionalIndicator = next && next.text === '?' + if (!nameIsFollowedByOptionalIndicator) { + parts.push(part) + } + hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator + } + break + + case PConst.DisplayPartKind.punctuation: + if (part.text === '(') { + ++parenCount + } else if (part.text === ')') { + --parenCount + if (parenCount <= 0 && isInMethod) { + break outer + } + } else if (part.text === '...' && parenCount === 1) { + // Found rest parmeter. Do not fill in any further arguments + hasOptionalParameters = true + break outer + } else if (part.text === '{') { + ++braceCount + } else if (part.text === '}') { + --braceCount + } + break + } + } + + return { hasOptionalParameters, parts } +}