From c556e99cf1c5eaacec7b7ca1e58cb72cc253e1c6 Mon Sep 17 00:00:00 2001 From: Qiming Zhao Date: Fri, 23 Jul 2021 00:04:28 +0800 Subject: [PATCH] Support DefinitionLink for definition provider --- src/server/features/definitionProvider.ts | 42 ++++++++++++++++++++--- src/server/utils/typeConverters.ts | 5 +++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/server/features/definitionProvider.ts b/src/server/features/definitionProvider.ts index 3959bcf..ea57402 100644 --- a/src/server/features/definitionProvider.ts +++ b/src/server/features/definitionProvider.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { TextDocument } from 'coc.nvim' -import { DefinitionProvider, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim' -import { CancellationToken, Definition, Location, Position } from 'vscode-languageserver-protocol' +import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim' import * as Proto from '../protocol' import { ITypeScriptServiceClient } from '../typescriptService' +import API from '../utils/api' import * as typeConverters from '../utils/typeConverters' export default class TypeScriptDefinitionProvider implements DefinitionProvider, TypeDefinitionProvider, ImplementationProvider { @@ -41,12 +41,44 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider, } } - public provideDefinition( + public async provideDefinition( document: TextDocument, position: Position, token: CancellationToken - ): Promise { - return this.getSymbolLocations('definition', document, position, token) + ): Promise { + if (this.client.apiVersion.gte(API.v270)) { + const filepath = this.client.toOpenedFilePath(document.uri) + if (!filepath) { + return undefined + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position) + const response = await this.client.execute('definitionAndBoundSpan', args, token) + if (response.type !== 'response' || !response.body) { + return undefined + } + + const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined + return response.body.definitions + .map((location): DefinitionLink => { + const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location) + if (location.contextStart && location.contextEnd) { + return { + originSelectionRange: span, + targetRange: typeConverters.Range.fromLocations(location.contextStart, location.contextEnd), + targetUri: target.uri, + targetSelectionRange: target.range, + } + } + return { + originSelectionRange: span, + targetRange: target.range, + targetUri: target.uri, + targetSelectionRange: target.range, + } + }) + } + return await this.getSymbolLocations('definition', document, position, token) } public provideTypeDefinition( diff --git a/src/server/utils/typeConverters.ts b/src/server/utils/typeConverters.ts index 0703ce1..ace1aa1 100644 --- a/src/server/utils/typeConverters.ts +++ b/src/server/utils/typeConverters.ts @@ -23,6 +23,11 @@ export namespace Range { } } } + export const fromLocations = (start: Proto.Location, end: Proto.Location): language.Range => + language.Range.create( + Math.max(0, start.line - 1), Math.max(start.offset - 1, 0), + Math.max(0, end.line - 1), Math.max(0, end.offset - 1)) + export const toFormattingRequestArgs = (file: string, range: language.Range): Proto.FormatRequestArgs => ({ file,