rework function snippet

This commit is contained in:
chemzqm 2018-12-04 13:38:12 +08:00
parent ec6e406594
commit 51893f971d
3 changed files with 112 additions and 48 deletions

View file

@ -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
}

View file

@ -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'
}

View file

@ -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 }
}