diff --git a/client/src/client.ts b/client/src/client.ts index c092ba5ce..f953ada28 100644 --- a/client/src/client.ts +++ b/client/src/client.ts @@ -9,7 +9,7 @@ import { TextDocumentChangeEvent, TextDocument, Disposable, OutputChannel, FileSystemWatcher as VFileSystemWatcher, DiagnosticCollection, Diagnostic as VDiagnostic, Uri, ProviderResult, CancellationToken, Position as VPosition, Location as VLocation, Range as VRange, - CompletionItem as VCompletionItem, CompletionList as VCompletionList, SignatureHelp as VSignatureHelp, Definition as VDefinition, DocumentHighlight as VDocumentHighlight, + CompletionItem as VCompletionItem, CompletionList as VCompletionList, SignatureHelp as VSignatureHelp, Definition as VDefinition, DefinitionLink as VDefinitionLink, DocumentHighlight as VDocumentHighlight, SymbolInformation as VSymbolInformation, CodeActionContext as VCodeActionContext, Command as VCommand, CodeLens as VCodeLens, FormattingOptions as VFormattingOptions, TextEdit as VTextEdit, WorkspaceEdit as VWorkspaceEdit, MessageItem, Hover as VHover, CodeAction as VCodeAction, DocumentSymbol as VDocumentSymbol, @@ -334,7 +334,7 @@ export interface ProvideSignatureHelpSignature { } export interface ProvideDefinitionSignature { - (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; + (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; } export interface ProvideReferencesSignature { @@ -424,7 +424,7 @@ export interface _Middleware { resolveCompletionItem?: (this: void, item: VCompletionItem, token: CancellationToken, next: ResolveCompletionItemSignature) => ProviderResult; provideHover?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideHoverSignature) => ProviderResult; provideSignatureHelp?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideSignatureHelpSignature) => ProviderResult; - provideDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDefinitionSignature) => ProviderResult; + provideDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDefinitionSignature) => ProviderResult; provideReferences?: (this: void, document: TextDocument, position: VPosition, options: { includeDeclaration: boolean; }, token: CancellationToken, next: ProvideReferencesSignature) => ProviderResult; provideDocumentHighlights?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDocumentHighlightsSignature) => ProviderResult; provideDocumentSymbols?: (this: void, document: TextDocument, token: CancellationToken, next: ProvideDocumentSymbolsSignature) => ProviderResult; @@ -1475,7 +1475,9 @@ class DefinitionFeature extends TextDocumentFeature => { + provideDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult => { return middleware.provideDefinition ? middleware.provideDefinition(document, position, token, provideDefinition) : provideDefinition(document, position, token); diff --git a/client/src/implementation.ts b/client/src/implementation.ts index eb7fd7f81..7ea599835 100644 --- a/client/src/implementation.ts +++ b/client/src/implementation.ts @@ -7,7 +7,7 @@ import * as UUID from './utils/uuid'; import * as Is from './utils/is'; -import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, Definition as VDefinition } from 'vscode'; +import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, Definition as VDefinition, DefinitionLink as VDefinitionLink } from 'vscode'; import { ClientCapabilities, CancellationToken, ServerCapabilities, TextDocumentRegistrationOptions, DocumentSelector, ImplementationRequest @@ -23,11 +23,11 @@ function ensure(target: T, key: K): T[K] { } export interface ProvideImplementationSignature { - (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; + (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; } export interface ImplementationMiddleware { - provideImplementation?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideImplementationSignature) => ProviderResult; + provideImplementation?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideImplementationSignature) => ProviderResult; } export class ImplementationFeature extends TextDocumentFeature { @@ -37,7 +37,9 @@ export class ImplementationFeature extends TextDocumentFeature => { + provideImplementation: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult => { return middleware.provideImplementation ? middleware.provideImplementation(document, position, token, provideImplementation) : provideImplementation(document, position, token); diff --git a/client/src/protocolConverter.ts b/client/src/protocolConverter.ts index e40585f1d..e4daee322 100644 --- a/client/src/protocolConverter.ts +++ b/client/src/protocolConverter.ts @@ -61,8 +61,9 @@ export interface Converter { asParameterInformations(item: ls.ParameterInformation[]): code.ParameterInformation[]; asDefinitionResult(item: ls.Definition): code.Definition; + asDefinitionResult(item: ls.LocationLink[]): code.Definition; asDefinitionResult(item: undefined | null): undefined; - asDefinitionResult(item: ls.Definition | undefined | null): code.Definition | undefined; + asDefinitionResult(item: ls.Definition | ls.LocationLink[] | undefined | null): code.Definition | code.DefinitionLink[] | undefined; asLocation(item: ls.Location): code.Location; asLocation(item: undefined | null): undefined; @@ -433,14 +434,25 @@ export function createConverter(uriConverter?: URIConverter): Converter { } function asDefinitionResult(item: ls.Definition): code.Definition; + function asDefinitionResult(item: ls.LocationLink[]): code.Definition; function asDefinitionResult(item: undefined | null): undefined; - function asDefinitionResult(item: ls.Definition | undefined | null): code.Definition | undefined; - function asDefinitionResult(item: ls.Definition | undefined | null): code.Definition | undefined { + function asDefinitionResult(item: ls.Definition | ls.LocationLink[] | undefined | null): code.Definition | code.DefinitionLink[] | undefined; + function asDefinitionResult(item: ls.Definition | ls.LocationLink[] | undefined | null): code.Definition | code.DefinitionLink[] | undefined { if (!item) { return undefined; } if (Is.array(item)) { - return item.map((location) => asLocation(location)); + if (item.length === 0) { + return[]; + } else if (ls.LocationLink.is(item[0])) { + let links = item as ls.LocationLink[]; + return links.map((links) => asDefinitionLink(links)); + } else { + let locations = item as ls.Location[]; + return locations.map((location) => asLocation(location)); + } + } else if (ls.LocationLink.is(item)) { + return [ asDefinitionLink(item) ]; } else { return asLocation(item); } @@ -456,6 +468,21 @@ export function createConverter(uriConverter?: URIConverter): Converter { return new code.Location(_uriConverter(item.uri), asRange(item.range)); } + function asDefinitionLink(item: ls.LocationLink): code.DefinitionLink; + function asDefinitionLink(item: undefined | null): undefined; + function asDefinitionLink(item: ls.LocationLink | undefined | null): code.DefinitionLink | undefined; + function asDefinitionLink(item: ls.LocationLink | undefined | null): code.DefinitionLink | undefined { + if (!item) { + return undefined; + } + return { + targetUri:_uriConverter(item.targetUri), + targetRange: asRange(item.targetRange), + originSelectionRange: asRange(item.originSelectionRange), + targetSelectionRange: asRange(item.targetSelectionRange) + } + } + function asReferences(values: ls.Location[]): code.Location[]; function asReferences(values: undefined | null): code.Location[] | undefined; function asReferences(values: ls.Location[] | undefined | null): code.Location[] | undefined; diff --git a/client/src/test/servers/testInitializeResult.ts b/client/src/test/servers/testInitializeResult.ts index 045d4992c..cb542a802 100644 --- a/client/src/test/servers/testInitializeResult.ts +++ b/client/src/test/servers/testInitializeResult.ts @@ -24,6 +24,9 @@ connection.onInitialize((params: InitializeParams): any => { assert.equal(params.capabilities.workspace!.workspaceEdit!.failureHandling, FailureHandlingKind.TextOnlyTransactional); assert.equal(params.capabilities.textDocument!.completion!.completionItem!.deprecatedSupport, true); assert.equal(params.capabilities.textDocument!.completion!.completionItem!.preselectSupport, true); + assert.equal(params.capabilities.textDocument!.definition!.locationLinkSupport, true); + assert.equal(params.capabilities.textDocument!.implementation!.locationLinkSupport, true); + assert.equal(params.capabilities.textDocument!.typeDefinition!.locationLinkSupport, true); assert.equal(params.capabilities.textDocument!.rename!.prepareSupport, true); let valueSet = params.capabilities.textDocument!.completion!.completionItemKind!.valueSet!; assert.equal(valueSet[0], 1); diff --git a/client/src/typeDefinition.ts b/client/src/typeDefinition.ts index fdc8ff129..9005d9a01 100644 --- a/client/src/typeDefinition.ts +++ b/client/src/typeDefinition.ts @@ -7,7 +7,7 @@ import * as UUID from './utils/uuid'; import * as Is from './utils/is'; -import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, Definition as VDefinition } from 'vscode'; +import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, Definition as VDefinition, DefinitionLink as VDefinitionLink } from 'vscode'; import { ClientCapabilities, CancellationToken, ServerCapabilities, TextDocumentRegistrationOptions, DocumentSelector, TypeDefinitionRequest @@ -23,11 +23,11 @@ function ensure(target: T, key: K): T[K] { } export interface ProvideTypeDefinitionSignature { - (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; + (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult; } export interface TypeDefinitionMiddleware { - provideTypeDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideTypeDefinitionSignature) => ProviderResult; + provideTypeDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideTypeDefinitionSignature) => ProviderResult; } export class TypeDefinitionFeature extends TextDocumentFeature { @@ -38,6 +38,9 @@ export class TypeDefinitionFeature extends TextDocumentFeature => { + provideTypeDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult => { return middleware.provideTypeDefinition ? middleware.provideTypeDefinition(document, position, token, provideTypeDefinition) : provideTypeDefinition(document, position, token); diff --git a/protocol/src/protocol.implementation.ts b/protocol/src/protocol.implementation.ts index 715b86f8a..0f95d560a 100644 --- a/protocol/src/protocol.implementation.ts +++ b/protocol/src/protocol.implementation.ts @@ -23,6 +23,11 @@ export interface ImplementationClientCapabilities { * return value for the corresponding server capability as well. */ dynamicRegistration?: boolean; + + /** + * The client supports additional metadata in the form of location links. + */ + locationLinkSupport?: boolean; }; } } diff --git a/protocol/src/protocol.ts b/protocol/src/protocol.ts index 8ec70b198..7b2b5563f 100644 --- a/protocol/src/protocol.ts +++ b/protocol/src/protocol.ts @@ -13,7 +13,7 @@ import { TextEdit, WorkspaceEdit, WorkspaceSymbolParams, TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, TextDocumentSaveReason, CompletionItem, CompletionList, Hover, SignatureHelp, - Definition, ReferenceContext, DocumentHighlight, DocumentSymbolParams, + Definition, LocationLink, ReferenceContext, DocumentHighlight, DocumentSymbolParams, SymbolInformation, CodeLens, CodeActionContext, FormattingOptions, DocumentLink, MarkupKind, SymbolKind, CompletionItemKind, CodeAction, CodeActionKind, DocumentSymbol } from 'vscode-languageserver-types'; @@ -529,6 +529,11 @@ export interface TextDocumentClientCapabilities { * Whether definition supports dynamic registration. */ dynamicRegistration?: boolean; + + /** + * The client supports additional metadata in the form of location links. + */ + locationLinkSupport?: boolean; }; /** @@ -1570,11 +1575,12 @@ export namespace SignatureHelpRequest { /** * A request to resolve the definition location of a symbol at a given text * document position. The request's parameter is of type [TextDocumentPosition] - * (#TextDocumentPosition) the response is of type [Definition](#Definition) or a - * Thenable that resolves to such. + * (#TextDocumentPosition) the response is of either type [Definition](#Definition) + * or a typed array of [LocationLinks](#LocationLink) or a Thenable that resolves + * to such. */ export namespace DefinitionRequest { - export const type = new RequestType('textDocument/definition'); + export const type = new RequestType('textDocument/definition'); } //---- Reference Provider ---------------------------------- diff --git a/protocol/src/protocol.typeDefinition.ts b/protocol/src/protocol.typeDefinition.ts index 49ca954bc..ee4bd4999 100644 --- a/protocol/src/protocol.typeDefinition.ts +++ b/protocol/src/protocol.typeDefinition.ts @@ -23,6 +23,11 @@ export interface TypeDefinitionClientCapabilities { * return value for the corresponding server capability as well. */ dynamicRegistration?: boolean; + + /** + * The client supports additional metadata in the form of location links. + */ + locationLinkSupport?: boolean; }; } } diff --git a/types/src/main.ts b/types/src/main.ts index 7acd72c97..d0f5ca84c 100644 --- a/types/src/main.ts +++ b/types/src/main.ts @@ -149,6 +149,46 @@ export namespace Location { } } +/** + * Represents the range and location of where a symbol is defined. + * Also includes information about the originating source. + */ +export interface LocationLink { + targetUri: string; + targetRange: Range; + targetSelectionRange?: Range; + originSelectionRange?: Range; +} + +/** + * The LocationLink namespace provides helper functions to work with + * [LocationLink](#LocationLink) literals. + */ +export namespace LocationLink { + + /** + * Creates a LocationLink literal. + * @param targetUri The definition's uri. + * @param targetRange The full range of the definition. + * @param targetSelectionRange The span of the symbol definition at the target. + * @param originSelectionRange The span of the symbol being defined in the originating source file. + */ + export function create(targetUri: string, targetRange: Range, targetSelectionRange?: Range, originSelectionRange?: Range): LocationLink { + return { targetUri, targetRange, targetSelectionRange, originSelectionRange }; + } + + /** + * Checks whether the given literal conforms to the [LocationLink](#LocationLink) interface. + */ + export function is(value: any): value is LocationLink { + let candidate = value as LocationLink; + return Range.is(candidate.targetRange) && Is.defined(candidate) + && Is.string(candidate.targetUri) && Is.defined(candidate.targetUri) + && (Range.is(candidate.targetSelectionRange) || Is.undefined(candidate.targetSelectionRange)) + && (Range.is(candidate.originSelectionRange) || Is.undefined(candidate.originSelectionRange)); + } +} + /** * Represents a color in RGBA space. */ @@ -1656,6 +1696,9 @@ export interface SignatureHelp { * The definition of a symbol represented as one or many [locations](#Location). * For most programming languages there is only one location at which a symbol is * defined. If no definition can be found `null` is returned. + * + * @deprecated This type has been deprecated in favour of + * [LocationLink](#LocationLink). */ export type Definition = Location | Location[] | null;