feat: callHierarchy support (#297)
needs https://github.com/neoclide/coc.nvim/pull/2756 closes https://github.com/neoclide/coc-tsserver/issues/128
This commit is contained in:
parent
f2afebce92
commit
f76e310d42
7 changed files with 169 additions and 16 deletions
|
@ -678,7 +678,7 @@
|
|||
"coc.nvim": "^0.0.80",
|
||||
"esbuild": "^0.8.29",
|
||||
"semver": "^7.3.2",
|
||||
"vscode-languageserver-protocol": "^3.15.3",
|
||||
"vscode-languageserver-protocol": "^3.16.0",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
114
src/server/features/callHierarchy.ts
Normal file
114
src/server/features/callHierarchy.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CallHierarchyProvider, TextDocument, Uri } from 'coc.nvim'
|
||||
import path from "path"
|
||||
import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, Position, SymbolTag } from 'vscode-languageserver-protocol'
|
||||
import type * as Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
import * as typeConverters from '../utils/typeConverters'
|
||||
|
||||
export default class TypeScriptCallHierarchySupport implements CallHierarchyProvider {
|
||||
public static readonly minVersion = API.v380
|
||||
|
||||
public constructor(private readonly client: ITypeScriptServiceClient) {}
|
||||
|
||||
public async prepareCallHierarchy(
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
token: CancellationToken
|
||||
): Promise<CallHierarchyItem | CallHierarchyItem[] | undefined> {
|
||||
const filepath = this.client.toOpenedFilePath(document.uri)
|
||||
if (!filepath) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position)
|
||||
const response = await this.client.execute('prepareCallHierarchy', args, token)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return Array.isArray(response.body)
|
||||
? response.body.map(fromProtocolCallHierarchyItem)
|
||||
: fromProtocolCallHierarchyItem(response.body)
|
||||
}
|
||||
|
||||
public async provideCallHierarchyIncomingCalls(item: CallHierarchyItem, token: CancellationToken): Promise<CallHierarchyIncomingCall[] | undefined> {
|
||||
const filepath = this.client.toPath(item.uri)
|
||||
if (!filepath) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start)
|
||||
const response = await this.client.execute('provideCallHierarchyIncomingCalls', args, token)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return response.body.map(fromProtocolCallHierarchyIncomingCall)
|
||||
}
|
||||
|
||||
public async provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): Promise<CallHierarchyOutgoingCall[] | undefined> {
|
||||
const filepath = this.client.toPath(item.uri)
|
||||
if (!filepath) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start)
|
||||
const response = await this.client.execute('provideCallHierarchyOutgoingCalls', args, token)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return response.body.map(fromProtocolCallHierarchyOutgoingCall)
|
||||
}
|
||||
}
|
||||
|
||||
function isSourceFileItem(item: Proto.CallHierarchyItem) {
|
||||
return item.kind === PConst.Kind.script || item.kind === PConst.Kind.module && item.selectionSpan.start.line === 1 && item.selectionSpan.start.offset === 1
|
||||
}
|
||||
|
||||
function parseKindModifier(kindModifiers: string): Set<string> {
|
||||
return new Set(kindModifiers.split(/,|\s+/g))
|
||||
}
|
||||
|
||||
function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHierarchyItem {
|
||||
const useFileName = isSourceFileItem(item)
|
||||
const name = useFileName ? path.basename(item.file) : item.name
|
||||
// TODO
|
||||
// const detail = useFileName ? workspace.asRelativePath(path.dirname(item.file)) : item.containerName ?? ''
|
||||
const detail = item.containerName || ''
|
||||
const result: CallHierarchyItem = {
|
||||
name,
|
||||
detail,
|
||||
uri: Uri.file(item.file).toString(),
|
||||
kind: typeConverters.SymbolKind.fromProtocolScriptElementKind(item.kind),
|
||||
range: typeConverters.Range.fromTextSpan(item.span),
|
||||
selectionRange: typeConverters.Range.fromTextSpan(item.selectionSpan)
|
||||
}
|
||||
|
||||
const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined
|
||||
if (kindModifiers?.has(PConst.KindModifiers.depreacted)) {
|
||||
result.tags = [SymbolTag.Deprecated]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function fromProtocolCallHierarchyIncomingCall(item: Proto.CallHierarchyIncomingCall): CallHierarchyIncomingCall {
|
||||
return {
|
||||
from: fromProtocolCallHierarchyItem(item.from),
|
||||
fromRanges: item.fromSpans.map(typeConverters.Range.fromTextSpan)
|
||||
}
|
||||
}
|
||||
|
||||
function fromProtocolCallHierarchyOutgoingCall(item: Proto.CallHierarchyOutgoingCall): CallHierarchyOutgoingCall {
|
||||
return {
|
||||
to: fromProtocolCallHierarchyItem(item.to),
|
||||
fromRanges: item.fromSpans.map(typeConverters.Range.fromTextSpan)
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import { disposeAll, languages, TextDocument, Uri, workspace } from 'coc.nvim'
|
|||
import path from 'path'
|
||||
import { CodeActionKind, Diagnostic, DiagnosticSeverity, Disposable } from 'vscode-languageserver-protocol'
|
||||
import { CachedNavTreeResponse } from './features/baseCodeLensProvider'
|
||||
import CallHierarchyProvider from './features/callHierarchy'
|
||||
import CompletionItemProvider from './features/completionItemProvider'
|
||||
import DefinitionProvider from './features/definitionProvider'
|
||||
import { DiagnosticKind } from './features/diagnostics'
|
||||
|
@ -104,6 +105,9 @@ export default class LanguageProvider {
|
|||
this._register(languages.registerDocumentRangeFormatProvider(languageIds, formatProvider))
|
||||
this._register(languages.registerOnTypeFormattingEditProvider(languageIds, formatProvider, [';', '}', '\n', String.fromCharCode(27)]))
|
||||
this._register(languages.registerCodeActionProvider(languageIds, new InstallModuleProvider(client), 'tsserver'))
|
||||
if (typeof languages['registerCallHierarchyProvider'] === 'function') {
|
||||
this._register(languages.registerCallHierarchyProvider(languageIds, new CallHierarchyProvider(client)))
|
||||
}
|
||||
|
||||
let { fileConfigurationManager } = this
|
||||
let conf = fileConfigurationManager.getLanguageConfiguration(this.id)
|
||||
|
|
|
@ -44,6 +44,7 @@ export class DiagnosticCategory {
|
|||
|
||||
export class KindModifiers {
|
||||
public static readonly optional = 'optional'
|
||||
public static readonly depreacted = 'deprecated'
|
||||
public static readonly color = 'color'
|
||||
|
||||
public static readonly dtsFile = '.d.ts'
|
||||
|
|
|
@ -50,7 +50,6 @@ export interface TypeScriptRequestTypes {
|
|||
'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]
|
||||
'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse]
|
||||
'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse]
|
||||
'updateOpen': [Proto.UpdateOpenRequestArgs, Proto.Response]
|
||||
// tslint:disable-next-line: deprecation
|
||||
'completions': [Proto.CompletionsRequestArgs, Proto.CompletionsResponse]
|
||||
'configure': [Proto.ConfigureRequestArguments, Proto.ConfigureResponse]
|
||||
|
@ -80,6 +79,10 @@ export interface TypeScriptRequestTypes {
|
|||
'selectionRange': [Proto.SelectionRangeRequestArgs, Proto.SelectionRangeResponse]
|
||||
'signatureHelp': [Proto.SignatureHelpRequestArgs, Proto.SignatureHelpResponse]
|
||||
'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse]
|
||||
'updateOpen': [Proto.UpdateOpenRequestArgs, Proto.Response]
|
||||
'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]
|
||||
'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]
|
||||
'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]
|
||||
'fileReferences': [Proto.FileRequestArgs, Proto.FileReferencesResponse]
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
import * as language from 'vscode-languageserver-protocol'
|
||||
import Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
|
||||
export namespace Range {
|
||||
|
@ -102,3 +103,33 @@ export namespace WorkspaceEdit {
|
|||
return { changes }
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SymbolKind {
|
||||
export function fromProtocolScriptElementKind(kind: Proto.ScriptElementKind) {
|
||||
switch (kind) {
|
||||
case PConst.Kind.module: return language.SymbolKind.Module
|
||||
case PConst.Kind.class: return language.SymbolKind.Class
|
||||
case PConst.Kind.enum: return language.SymbolKind.Enum
|
||||
case PConst.Kind.enumMember: return language.SymbolKind.EnumMember
|
||||
case PConst.Kind.interface: return language.SymbolKind.Interface
|
||||
case PConst.Kind.indexSignature: return language.SymbolKind.Method
|
||||
case PConst.Kind.callSignature: return language.SymbolKind.Method
|
||||
case PConst.Kind.method: return language.SymbolKind.Method
|
||||
case PConst.Kind.memberVariable: return language.SymbolKind.Property
|
||||
case PConst.Kind.memberGetAccessor: return language.SymbolKind.Property
|
||||
case PConst.Kind.memberSetAccessor: return language.SymbolKind.Property
|
||||
case PConst.Kind.variable: return language.SymbolKind.Variable
|
||||
case PConst.Kind.let: return language.SymbolKind.Variable
|
||||
case PConst.Kind.const: return language.SymbolKind.Variable
|
||||
case PConst.Kind.localVariable: return language.SymbolKind.Variable
|
||||
case PConst.Kind.alias: return language.SymbolKind.Variable
|
||||
case PConst.Kind.function: return language.SymbolKind.Function
|
||||
case PConst.Kind.localFunction: return language.SymbolKind.Function
|
||||
case PConst.Kind.constructSignature: return language.SymbolKind.Constructor
|
||||
case PConst.Kind.constructorImplementation: return language.SymbolKind.Constructor
|
||||
case PConst.Kind.typeParameter: return language.SymbolKind.TypeParameter
|
||||
case PConst.Kind.string: return language.SymbolKind.String
|
||||
default: return language.SymbolKind.Variable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -32,23 +32,23 @@ typescript@^4.3.2:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
||||
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
||||
|
||||
vscode-jsonrpc@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
|
||||
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
|
||||
vscode-languageserver-protocol@^3.15.3:
|
||||
version "3.15.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
|
||||
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
|
||||
vscode-languageserver-protocol@^3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
dependencies:
|
||||
vscode-jsonrpc "^5.0.1"
|
||||
vscode-languageserver-types "3.15.1"
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
|
||||
vscode-languageserver-types@3.15.1:
|
||||
version "3.15.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
|
||||
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
|
||||
vscode-languageserver-types@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
|
||||
which@^2.0.2:
|
||||
version "2.0.2"
|
||||
|
|
Loading…
Reference in a new issue