parent
17fdfd9462
commit
eec50a98f2
6 changed files with 113 additions and 55 deletions
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CancellationToken, Command, CompletionContext, CompletionItem, InsertTextFormat, MarkupContent, MarkupKind, Position, TextEdit, CompletionList } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, Command, CompletionContext, Range, CompletionItem, InsertTextFormat, MarkupContent, MarkupKind, Position, TextEdit, CompletionList } from 'vscode-languageserver-protocol'
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||
import { commands, workspace } from 'coc.nvim'
|
||||
import { CompletionItemProvider } from 'coc.nvim/lib/provider'
|
||||
|
@ -11,7 +11,7 @@ import * as PConst from '../protocol.const'
|
|||
import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
import { applyCodeAction } from '../utils/codeAction'
|
||||
import { convertCompletionEntry, getParameterListParts } from '../utils/completionItem'
|
||||
import { DotAccessorContext, convertCompletionEntry, getParameterListParts } from '../utils/completionItem'
|
||||
import * as Previewer from '../utils/previewer'
|
||||
import * as typeConverters from '../utils/typeConverters'
|
||||
import TypingsStatus from '../utils/typingsStatus'
|
||||
|
@ -111,8 +111,6 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
}
|
||||
|
||||
const { completeOption } = this
|
||||
const doc = workspace.getDocument(uri)
|
||||
|
||||
const args: Proto.CompletionsRequestArgs & { includeAutomaticOptionalChainCompletions?: boolean } = {
|
||||
...typeConverters.Position.toFileLocationRequestArgs(file, position),
|
||||
includeExternalModuleExports: completeOption.autoImports,
|
||||
|
@ -121,9 +119,14 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
includeAutomaticOptionalChainCompletions: completeOption.includeAutomaticOptionalChainCompletions
|
||||
}
|
||||
|
||||
let msg: ReadonlyArray<Proto.CompletionEntry> | undefined
|
||||
let entries: ReadonlyArray<Proto.CompletionEntry> | undefined
|
||||
|
||||
let dotAccessorContext: DotAccessorContext | undefined
|
||||
let isNewIdentifierLocation = true
|
||||
let isMemberCompletion = false
|
||||
let isIncomplete = false
|
||||
const isInValidCommitCharacterContext = this.isInValidCommitCharacterContext(document, position)
|
||||
|
||||
if (this.client.apiVersion.gte(API.v300)) {
|
||||
try {
|
||||
const response = await this.client.interruptGetErr(() => this.client.execute('completionInfo', args, token))
|
||||
|
@ -131,7 +134,20 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
return null
|
||||
}
|
||||
isNewIdentifierLocation = response.body.isNewIdentifierLocation
|
||||
msg = response.body.entries
|
||||
isMemberCompletion = response.body.isMemberCompletion
|
||||
if (isMemberCompletion) {
|
||||
const dotMatch = preText.slice(0, position.character).match(/\??\.\s*$/) || undefined
|
||||
if (dotMatch) {
|
||||
const range = Range.create({
|
||||
line: position.line,
|
||||
character: position.character - dotMatch.length
|
||||
}, position)
|
||||
const text = document.getText(range)
|
||||
dotAccessorContext = { range, text }
|
||||
}
|
||||
}
|
||||
isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete
|
||||
entries = response.body.entries
|
||||
} catch (e) {
|
||||
if (e.message == 'No content available.') {
|
||||
return null
|
||||
|
@ -143,11 +159,11 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
if (response.type !== 'response' || !response.body) {
|
||||
return null
|
||||
}
|
||||
msg = response.body
|
||||
entries = response.body
|
||||
}
|
||||
|
||||
const completionItems: CompletionItem[] = []
|
||||
for (const element of msg) {
|
||||
for (const element of entries) {
|
||||
if (shouldExcludeCompletionEntry(element, completeOption)) {
|
||||
continue
|
||||
}
|
||||
|
@ -155,21 +171,17 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
element,
|
||||
uri,
|
||||
position,
|
||||
completeOption.completeFunctionCalls,
|
||||
isNewIdentifierLocation
|
||||
{
|
||||
isNewIdentifierLocation,
|
||||
isMemberCompletion,
|
||||
enableCallCompletions: completeOption.completeFunctionCalls,
|
||||
isInValidCommitCharacterContext,
|
||||
dotAccessorContext,
|
||||
}
|
||||
)
|
||||
completionItems.push(item)
|
||||
}
|
||||
let startcol: number | null = null
|
||||
if (triggerCharacter == '@' && !doc.isWord('@')) {
|
||||
startcol = option.col - 1
|
||||
}
|
||||
let res: any = {
|
||||
startcol,
|
||||
isIncomplete: false,
|
||||
items: completionItems
|
||||
}
|
||||
return res
|
||||
return { isIncomplete, items: completionItems }
|
||||
}
|
||||
|
||||
private getTsTriggerCharacter(context: CompletionContext): Proto.CompletionsTriggerCharacter | undefined {
|
||||
|
@ -229,10 +241,9 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
return item
|
||||
}
|
||||
const detail = details[0]
|
||||
item.detail = detail.displayParts.length
|
||||
? Previewer.plain(detail.displayParts)
|
||||
: undefined
|
||||
|
||||
if (!item.detail && detail.displayParts.length) {
|
||||
item.detail = Previewer.plain(detail.displayParts)
|
||||
}
|
||||
item.documentation = this.getDocumentation(detail)
|
||||
const { command, additionalTextEdits } = this.getCodeActions(detail, filepath)
|
||||
if (command) item.command = command
|
||||
|
@ -243,6 +254,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
this.createSnippetOfFunctionCall(item, detail)
|
||||
}
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
|
@ -390,6 +402,24 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private isInValidCommitCharacterContext(
|
||||
document: TextDocument,
|
||||
position: Position
|
||||
): boolean {
|
||||
if (this.client.apiVersion.lt(API.v320)) {
|
||||
// Workaround for https://github.com/Microsoft/TypeScript/issues/27742
|
||||
// Only enable dot completions when previous character not a dot preceded by whitespace.
|
||||
// Prevents incorrectly completing while typing spread operators.
|
||||
if (position.character > 1) {
|
||||
const preText = document.getText(Range.create(
|
||||
position.line, 0,
|
||||
position.line, position.character))
|
||||
return preText.match(/(\s|^)\.$/ig) === null
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function shouldExcludeCompletionEntry(
|
||||
|
|
|
@ -20,7 +20,7 @@ const getSymbolKind = (kind: string): SymbolKind => {
|
|||
return SymbolKind.Enum
|
||||
case PConst.Kind.interface:
|
||||
return SymbolKind.Interface
|
||||
case PConst.Kind.memberFunction:
|
||||
case PConst.Kind.method:
|
||||
return SymbolKind.Method
|
||||
case PConst.Kind.memberVariable:
|
||||
return SymbolKind.Property
|
||||
|
|
|
@ -87,7 +87,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
|
|||
return super.getSymbolRange(document, item)
|
||||
|
||||
case PConst.Kind.class:
|
||||
case PConst.Kind.memberFunction:
|
||||
case PConst.Kind.method:
|
||||
case PConst.Kind.memberVariable:
|
||||
case PConst.Kind.memberGetAccessor:
|
||||
case PConst.Kind.memberSetAccessor:
|
||||
|
|
|
@ -87,7 +87,7 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase
|
|||
}
|
||||
// fallthrough
|
||||
|
||||
case PConst.Kind.memberFunction:
|
||||
case PConst.Kind.method:
|
||||
case PConst.Kind.memberVariable:
|
||||
case PConst.Kind.memberGetAccessor:
|
||||
case PConst.Kind.memberSetAccessor:
|
||||
|
|
|
@ -12,6 +12,7 @@ export class Kind {
|
|||
public static readonly constructSignature = 'construct'
|
||||
public static readonly directory = 'directory'
|
||||
public static readonly enum = 'enum'
|
||||
public static readonly enumMember = 'enum member'
|
||||
public static readonly externalModuleName = 'external module name'
|
||||
public static readonly function = 'function'
|
||||
public static readonly indexSignature = 'index'
|
||||
|
@ -20,7 +21,7 @@ export class Kind {
|
|||
public static readonly let = 'let'
|
||||
public static readonly localFunction = 'local function'
|
||||
public static readonly localVariable = 'local var'
|
||||
public static readonly memberFunction = 'method'
|
||||
public static readonly method = 'method'
|
||||
public static readonly memberGetAccessor = 'getter'
|
||||
public static readonly memberSetAccessor = 'setter'
|
||||
public static readonly memberVariable = 'property'
|
||||
|
@ -32,6 +33,7 @@ export class Kind {
|
|||
public static readonly warning = 'warning'
|
||||
public static readonly string = 'string'
|
||||
public static readonly parameter = 'parameter'
|
||||
public static readonly typeParameter = 'type parameter'
|
||||
}
|
||||
|
||||
export class DiagnosticCategory {
|
||||
|
|
|
@ -2,56 +2,57 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { workspace } from 'coc.nvim'
|
||||
import { Range, CompletionItem, CompletionItemKind, InsertTextFormat, Position, TextEdit } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
|
||||
interface CommitCharactersSettings {
|
||||
readonly isNewIdentifierLocation: boolean
|
||||
readonly isInValidCommitCharacterContext: boolean
|
||||
readonly useCodeSnippetsOnMethodSuggest: boolean
|
||||
}
|
||||
|
||||
interface ParamterListParts {
|
||||
readonly parts: ReadonlyArray<Proto.SymbolDisplayPart>
|
||||
readonly hasOptionalParameters: boolean
|
||||
}
|
||||
|
||||
export interface DotAccessorContext {
|
||||
readonly range: Range
|
||||
readonly text: string
|
||||
}
|
||||
|
||||
export interface CompletionContext {
|
||||
readonly isNewIdentifierLocation: boolean
|
||||
readonly isMemberCompletion: boolean
|
||||
readonly isInValidCommitCharacterContext: boolean
|
||||
readonly enableCallCompletions: boolean
|
||||
readonly dotAccessorContext?: DotAccessorContext
|
||||
}
|
||||
|
||||
export function convertCompletionEntry(
|
||||
tsEntry: Proto.CompletionEntry,
|
||||
uri: string,
|
||||
position: Position,
|
||||
useCodeSnippetsOnMethodSuggest: boolean,
|
||||
isNewIdentifierLocation: boolean
|
||||
context: CompletionContext,
|
||||
): CompletionItem {
|
||||
let label = tsEntry.name
|
||||
let sortText = tsEntry.sortText
|
||||
let preselect = false
|
||||
let detail: string
|
||||
if (tsEntry.isRecommended) {
|
||||
// Make sure isRecommended property always comes first
|
||||
// https://github.com/Microsoft/vscode/issues/40325
|
||||
sortText = '\0' + sortText
|
||||
} else if (tsEntry.source) {
|
||||
// De-prioritze auto-imports
|
||||
// https://github.com/Microsoft/vscode/issues/40311
|
||||
preselect = true
|
||||
}
|
||||
if (tsEntry.source) {
|
||||
// De-prioritze auto-imports https://github.com/Microsoft/vscode/issues/40311
|
||||
sortText = '\uffff' + sortText
|
||||
} else {
|
||||
sortText = tsEntry.sortText
|
||||
}
|
||||
let kind = convertKind(tsEntry.kind)
|
||||
let insertTextFormat = (
|
||||
useCodeSnippetsOnMethodSuggest &&
|
||||
context.enableCallCompletions &&
|
||||
(kind === CompletionItemKind.Function ||
|
||||
kind === CompletionItemKind.Method)
|
||||
) ? InsertTextFormat.Snippet : InsertTextFormat.PlainText
|
||||
|
||||
let insertText = tsEntry.insertText
|
||||
let document = workspace.getDocument(uri)
|
||||
let preText = document.getline(position.line).slice(0, position.character)
|
||||
const isInValidCommitCharacterContext = preText.match(/(^|[a-z_$\(\)\[\]\{\}]|[^.]\.)\s*$/ig) !== null
|
||||
let commitCharacters = getCommitCharacters(tsEntry, context)
|
||||
|
||||
let commitCharacters = getCommitCharacters(tsEntry, { isNewIdentifierLocation, isInValidCommitCharacterContext, useCodeSnippetsOnMethodSuggest })
|
||||
let optional = tsEntry.kindModifiers && tsEntry.kindModifiers.match(/\boptional\b/)
|
||||
let textEdit: TextEdit | null = null
|
||||
if (tsEntry.replacementSpan) {
|
||||
let { start, end } = tsEntry.replacementSpan
|
||||
|
@ -62,17 +63,41 @@ export function convertCompletionEntry(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (tsEntry.kindModifiers) {
|
||||
const kindModifiers = new Set(tsEntry.kindModifiers.split(/,|\s+/g))
|
||||
if (kindModifiers.has(PConst.KindModifiers.optional)) {
|
||||
label += '?'
|
||||
}
|
||||
|
||||
if (kindModifiers.has(PConst.KindModifiers.color)) {
|
||||
kind = CompletionItemKind.Color
|
||||
}
|
||||
|
||||
if (tsEntry.kind === PConst.Kind.script) {
|
||||
for (const extModifier of PConst.KindModifiers.fileExtensionKindModifiers) {
|
||||
if (kindModifiers.has(extModifier)) {
|
||||
if (tsEntry.name.toLowerCase().endsWith(extModifier)) {
|
||||
detail = tsEntry.name
|
||||
} else {
|
||||
detail = tsEntry.name + extModifier
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
label,
|
||||
insertText,
|
||||
textEdit,
|
||||
kind,
|
||||
preselect,
|
||||
insertTextFormat,
|
||||
sortText,
|
||||
commitCharacters,
|
||||
detail,
|
||||
data: {
|
||||
uri,
|
||||
optional,
|
||||
position,
|
||||
source: tsEntry.source || ''
|
||||
}
|
||||
|
@ -97,7 +122,7 @@ function convertKind(kind: string): CompletionItemKind {
|
|||
return CompletionItemKind.Field
|
||||
case PConst.Kind.function:
|
||||
return CompletionItemKind.Function
|
||||
case PConst.Kind.memberFunction:
|
||||
case PConst.Kind.method:
|
||||
case PConst.Kind.constructSignature:
|
||||
case PConst.Kind.callSignature:
|
||||
case PConst.Kind.indexSignature:
|
||||
|
@ -121,8 +146,8 @@ function convertKind(kind: string): CompletionItemKind {
|
|||
return CompletionItemKind.Variable
|
||||
}
|
||||
|
||||
function getCommitCharacters(tsEntry: Proto.CompletionEntry, settings: CommitCharactersSettings): string[] | undefined {
|
||||
if (settings.isNewIdentifierLocation || !settings.isInValidCommitCharacterContext) {
|
||||
function getCommitCharacters(tsEntry: Proto.CompletionEntry, context: CompletionContext): string[] | undefined {
|
||||
if (context.isNewIdentifierLocation || !context.isInValidCommitCharacterContext) {
|
||||
return undefined
|
||||
}
|
||||
const commitCharacters: string[] = []
|
||||
|
@ -145,10 +170,11 @@ function getCommitCharacters(tsEntry: Proto.CompletionEntry, settings: CommitCha
|
|||
case PConst.Kind.memberVariable:
|
||||
case PConst.Kind.class:
|
||||
case PConst.Kind.function:
|
||||
case PConst.Kind.memberFunction:
|
||||
case PConst.Kind.method:
|
||||
case PConst.Kind.keyword:
|
||||
case PConst.Kind.parameter:
|
||||
commitCharacters.push('.', ',', ';')
|
||||
if (settings.useCodeSnippetsOnMethodSuggest) {
|
||||
if (context.enableCallCompletions) {
|
||||
commitCharacters.push('(')
|
||||
}
|
||||
break
|
||||
|
|
Loading…
Reference in a new issue