Skip to content

Commit

Permalink
Merge pull request #415 from rcjsuen/definitionLink
Browse files Browse the repository at this point in the history
Provide metadata about symbols with DefinitionLink
  • Loading branch information
dbaeumer authored Dec 6, 2018
2 parents c80ead2 + f45a0ce commit 0cc3b8a
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 22 deletions.
12 changes: 7 additions & 5 deletions client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -334,7 +334,7 @@ export interface ProvideSignatureHelpSignature {
}

export interface ProvideDefinitionSignature {
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition>;
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]>;
}

export interface ProvideReferencesSignature {
Expand Down Expand Up @@ -424,7 +424,7 @@ export interface _Middleware {
resolveCompletionItem?: (this: void, item: VCompletionItem, token: CancellationToken, next: ResolveCompletionItemSignature) => ProviderResult<VCompletionItem>;
provideHover?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideHoverSignature) => ProviderResult<VHover>;
provideSignatureHelp?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideSignatureHelpSignature) => ProviderResult<VSignatureHelp>;
provideDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDefinitionSignature) => ProviderResult<VDefinition>;
provideDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDefinitionSignature) => ProviderResult<VDefinition | VDefinitionLink[]>;
provideReferences?: (this: void, document: TextDocument, position: VPosition, options: { includeDeclaration: boolean; }, token: CancellationToken, next: ProvideReferencesSignature) => ProviderResult<VLocation[]>;
provideDocumentHighlights?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDocumentHighlightsSignature) => ProviderResult<VDocumentHighlight[]>;
provideDocumentSymbols?: (this: void, document: TextDocument, token: CancellationToken, next: ProvideDocumentSymbolsSignature) => ProviderResult<VSymbolInformation[] | VDocumentSymbol[]>;
Expand Down Expand Up @@ -1475,7 +1475,9 @@ class DefinitionFeature extends TextDocumentFeature<TextDocumentRegistrationOpti
}

public fillClientCapabilities(capabilites: ClientCapabilities): void {
ensure(ensure(capabilites, 'textDocument')!, 'definition')!.dynamicRegistration = true;
let definitionSupport = ensure(ensure(capabilites, 'textDocument')!, 'definition')!;
definitionSupport.dynamicRegistration = true;
definitionSupport.locationLinkSupport = true;
}

public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
Expand All @@ -1501,7 +1503,7 @@ class DefinitionFeature extends TextDocumentFeature<TextDocumentRegistrationOpti
};
let middleware = client.clientOptions.middleware!;
return Languages.registerDefinitionProvider(options.documentSelector!, {
provideDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition> => {
provideDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]> => {
return middleware.provideDefinition
? middleware.provideDefinition(document, position, token, provideDefinition)
: provideDefinition(document, position, token);
Expand Down
12 changes: 7 additions & 5 deletions client/src/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,11 +23,11 @@ function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
}

export interface ProvideImplementationSignature {
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition>;
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]>;
}

export interface ImplementationMiddleware {
provideImplementation?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideImplementationSignature) => ProviderResult<VDefinition>;
provideImplementation?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideImplementationSignature) => ProviderResult<VDefinition | VDefinitionLink[]>;
}

export class ImplementationFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {
Expand All @@ -37,7 +37,9 @@ export class ImplementationFeature extends TextDocumentFeature<TextDocumentRegis
}

public fillClientCapabilities(capabilites: ClientCapabilities): void {
ensure(ensure(capabilites, 'textDocument')!, 'implementation')!.dynamicRegistration = true;
let implementationSupport = ensure(ensure(capabilites, 'textDocument')!, 'implementation')!;
implementationSupport.dynamicRegistration = true;
implementationSupport.locationLinkSupport = true;
}

public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
Expand Down Expand Up @@ -78,7 +80,7 @@ export class ImplementationFeature extends TextDocumentFeature<TextDocumentRegis
};
let middleware = client.clientOptions.middleware!;
return Languages.registerImplementationProvider(options.documentSelector!, {
provideImplementation: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition> => {
provideImplementation: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]> => {
return middleware.provideImplementation
? middleware.provideImplementation(document, position, token, provideImplementation)
: provideImplementation(document, position, token);
Expand Down
35 changes: 31 additions & 4 deletions client/src/protocolConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions client/src/test/servers/testInitializeResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 7 additions & 4 deletions client/src/typeDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,11 +23,11 @@ function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
}

export interface ProvideTypeDefinitionSignature {
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition>;
(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]>;
}

export interface TypeDefinitionMiddleware {
provideTypeDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideTypeDefinitionSignature) => ProviderResult<VDefinition>;
provideTypeDefinition?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideTypeDefinitionSignature) => ProviderResult<VDefinition | VDefinitionLink[]>;
}

export class TypeDefinitionFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {
Expand All @@ -38,6 +38,9 @@ export class TypeDefinitionFeature extends TextDocumentFeature<TextDocumentRegis

public fillClientCapabilities(capabilites: ClientCapabilities): void {
ensure(ensure(capabilites, 'textDocument')!, 'typeDefinition')!.dynamicRegistration = true;
let typeDefinitionSupport = ensure(ensure(capabilites, 'textDocument')!, 'typeDefinition')!;
typeDefinitionSupport.dynamicRegistration = true;
typeDefinitionSupport.locationLinkSupport = true;
}

public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
Expand Down Expand Up @@ -78,7 +81,7 @@ export class TypeDefinitionFeature extends TextDocumentFeature<TextDocumentRegis
};
let middleware = client.clientOptions.middleware!;
return Languages.registerTypeDefinitionProvider(options.documentSelector!, {
provideTypeDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition> => {
provideTypeDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VDefinition | VDefinitionLink[]> => {
return middleware.provideTypeDefinition
? middleware.provideTypeDefinition(document, position, token, provideTypeDefinition)
: provideTypeDefinition(document, position, token);
Expand Down
5 changes: 5 additions & 0 deletions protocol/src/protocol.implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
}
Expand Down
14 changes: 10 additions & 4 deletions protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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<TextDocumentPositionParams, Definition | null, void, TextDocumentRegistrationOptions>('textDocument/definition');
export const type = new RequestType<TextDocumentPositionParams, Definition | LocationLink[] | null, void, TextDocumentRegistrationOptions>('textDocument/definition');
}

//---- Reference Provider ----------------------------------
Expand Down
5 changes: 5 additions & 0 deletions protocol/src/protocol.typeDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
}
Expand Down
43 changes: 43 additions & 0 deletions types/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit 0cc3b8a

Please sign in to comment.