From 9a4130157e4517bb441723f5ebd393f8d2ae5897 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Fri, 5 Oct 2018 10:06:59 +0200 Subject: [PATCH] Proposal for call hierarchy add new `textDocument/calls` request. LSP issue: language-server-protocol#468 Signed-off-by: Alex Tugarev --- client/src/callHierarchy.proposed.ts | 62 ++++++++ client/src/main.ts | 5 + protocol/src/main.ts | 13 ++ .../src/protocol.callHierarchy.proposed.md | 123 ++++++++++++++++ .../src/protocol.callHierarchy.proposed.ts | 135 ++++++++++++++++++ 5 files changed, 338 insertions(+) create mode 100644 client/src/callHierarchy.proposed.ts create mode 100644 protocol/src/protocol.callHierarchy.proposed.md create mode 100644 protocol/src/protocol.callHierarchy.proposed.ts diff --git a/client/src/callHierarchy.proposed.ts b/client/src/callHierarchy.proposed.ts new file mode 100644 index 00000000..07b2d6f5 --- /dev/null +++ b/client/src/callHierarchy.proposed.ts @@ -0,0 +1,62 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) TypeFox. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { Disposable } from 'vscode'; +import { + TextDocumentRegistrationOptions, ClientCapabilities, ServerCapabilities, DocumentSelector, Proposed +} from 'vscode-languageserver-protocol'; + +import * as UUID from './utils/uuid'; +import * as Is from './utils/is'; +import { TextDocumentFeature, BaseLanguageClient } from './client'; + +export class CallHierarchyFeature extends TextDocumentFeature { + + constructor(client: BaseLanguageClient) { + super(client, Proposed.CallHierarchyRequest.type); + } + + fillClientCapabilities(capabilities: ClientCapabilities): void { + if (!!capabilities.textDocument) { + capabilities.textDocument = {}; + } + let callHierarchyClientCapabilities = capabilities as Proposed.CallHierarchyClientCapabilities; + callHierarchyClientCapabilities.textDocument!.callHierarchy = { + dynamicRegistration: true + }; + } + + initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void { + let callHierarchyServerCapabilities = capabilities as Proposed.CallHierarchyServerCapabilities; + if (!callHierarchyServerCapabilities.callHierarchyProvider) { + return; + } + if (callHierarchyServerCapabilities.callHierarchyProvider === true) { + if (!documentSelector) { + return; + } + this.register(this.messages, { + id: UUID.generateUuid(), + registerOptions: Object.assign({}, { documentSelector: documentSelector }) + }); + } else { + const implCapabilities = callHierarchyServerCapabilities.callHierarchyProvider; + const id = Is.string(implCapabilities.id) && implCapabilities.id.length > 0 ? implCapabilities.id : UUID.generateUuid(); + const selector = implCapabilities.documentSelector || documentSelector; + if (selector) { + this.register(this.messages, { + id, + registerOptions: Object.assign({}, { documentSelector: selector }) + }); + } + } + } + + protected registerLanguageProvider(_options: TextDocumentRegistrationOptions): Disposable { + return new Disposable(() => { }); + } + +} \ No newline at end of file diff --git a/client/src/main.ts b/client/src/main.ts index 04dafbf0..d3bd6075 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -506,9 +506,14 @@ export class SettingMonitor { // Exporting proposed protocol. +import * as callHierarchy from './callHierarchy.proposed'; + export namespace ProposedFeatures { + export const CallHierarchyFeature = callHierarchy.CallHierarchyFeature; + export function createAll(_client: BaseLanguageClient): (StaticFeature | DynamicFeature)[] { let result: (StaticFeature | DynamicFeature)[] = []; + result.push(new CallHierarchyFeature(_client)); return result; } } diff --git a/protocol/src/main.ts b/protocol/src/main.ts index 7368fa99..428ac724 100644 --- a/protocol/src/main.ts +++ b/protocol/src/main.ts @@ -35,7 +35,20 @@ export * from './protocol'; export { FoldingRangeParams as FoldingRangeRequestParam } from './protocol'; // for backward compatibility +import * as callHierarchy from './protocol.callHierarchy.proposed'; + export namespace Proposed { + export type CallHierarchyClientCapabilities = callHierarchy.CallHierarchyClientCapabilities; + export type CallHierarchyServerCapabilities = callHierarchy.CallHierarchyServerCapabilities; + + export namespace CallHierarchyRequest { + export const type = callHierarchy.CallHierarchyRequest.type; + export type HandlerSignature = callHierarchy.CallHierarchyRequest.HandlerSignature; + } + + export type CallHierarchyParams = callHierarchy.CallHierarchyParams; + export type ResolveCallHierarchyItemParams = callHierarchy.ResolveCallHierarchyItemParams; + export type CallHierarchyItem = callHierarchy.CallHierarchyItem; } export interface ProtocolConnection { diff --git a/protocol/src/protocol.callHierarchy.proposed.md b/protocol/src/protocol.callHierarchy.proposed.md new file mode 100644 index 00000000..eabdc92a --- /dev/null +++ b/protocol/src/protocol.callHierarchy.proposed.md @@ -0,0 +1,123 @@ + +#### Call Hierarchy + +The LSP provides retrieving the call hierachy information with the following request. + +_Client Capabilities_: + +```ts +CallHierarchyClientCapabilities { + /** + * The text document client capabilities + */ + textDocument?: { + /** + * Capabilities specific to the `textDocument/callHierarchy` + */ + callHierarchy?: { + /** + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + dynamicRegistration?: boolean; + }; +} +``` + +_Server Capabilities_: + +```ts +CallHierarchyServerCapabilities { + /** + * The server provides Call Hierarchy support. + */ + callHierarchyProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); +} +``` + +##### Call Hierarchy Request + +_Request_: + +The `textDocument/callHierarchy` request is sent from the client to the server to request the call hierarchy for a symbol at the given text document position. Subsequent requests include unresolved items from the previous responses. + +Returns a call hierarchy item for the requested call direction. + +* method: ‘textDocument/callHierarchy' +* params: `CallHierarchyParams | ResolveCallHierarchyItemParams` defined as follows: + +```ts +export interface CallHierarchyParams extends TextDocumentPositionParams { + resolve?: number; + direction?: 'incoming' | 'outgoing'; +} + +export interface ResolveCallHierarchyItemParams { + item: CallHierarchyItem; + resolve: number; + direction: 'incoming' | 'outgoing'; +} +``` + +_Response_: + +The server will send a `CallHierarchyItem` object containing the information about the targeted symbol. The item will be undefined, if no such symbol is found. + +The item is _unresolved_ if the lists of callers and callees are undefined. Unresolved items can be resolved via `textDocument/callHierarchy` requests. + +The resolved item includes callers or callees of type `CallHierarchyItem`. Those objects provide the actual locations of the calls. + +* result: `CallHierarchyItem` defined as follows: + +```ts +export interface CallHierarchyItem { + /** + * The name of the symbol targeted by the call hierarchy request. + */ + name: string; + /** + * More detail for this symbol, e.g the signature of a function. + */ + detail?: string; + /** + * The kind of this symbol. + */ + kind: SymbolKind; + /** + * URI of the document containing the symbol. + */ + uri: string; + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the the clients cursor is + * inside the symbol to reveal in the symbol in the UI. + */ + range: Range; + /** + * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. + * Must be contained by the the `range`. + */ + selectionRange: Range; + + /** + * The actual location of the call. + * + * Must be defined in resolved callers/callees. + */ + callLocation?: Location; + + /** + * List of incoming calls. + * + * *Note*: The items is _unresolved_ if `callers` and `callees` is undefined. + */ + callers?: CallHierarchyItem[]; + /** + * List of outgoing calls. + * + * *Note*: The items is _unresolved_ if `callers` and `callees` is undefined. + */ + callees?: CallHierarchyItem[]; +} +``` diff --git a/protocol/src/protocol.callHierarchy.proposed.ts b/protocol/src/protocol.callHierarchy.proposed.ts new file mode 100644 index 00000000..5f5e54c4 --- /dev/null +++ b/protocol/src/protocol.callHierarchy.proposed.ts @@ -0,0 +1,135 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) TypeFox and others. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { RequestType, RequestHandler } from 'vscode-jsonrpc'; +import { Location, SymbolKind, Range } from 'vscode-languageserver-types'; +import { TextDocumentRegistrationOptions, StaticRegistrationOptions, TextDocumentPositionParams } from './protocol'; + +export interface CallHierarchyClientCapabilities { + /** + * The text document client capabilities + */ + textDocument?: { + /** + * Capabilities specific to the `textDocument/callHierarchy` + */ + callHierarchy?: { + /** + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + dynamicRegistration?: boolean; + }; + } +} + +export interface CallHierarchyServerCapabilities { + /** + * The server provides Call Hierarchy support. + */ + callHierarchyProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); +} + +/** + * Request to request the call hierarchy at a given text document position. + * + * The request's parameter for the first request is of type [CallHierarchyParams](#CallHierarchyParams). The request's + * parameter for the subsequent requests is of type [ResolveCallHierarchyItemParams](#ResolveCallHierarchyItemParams). + * + * The response is of type [CallHierarchyItem](#CallHierarchyItem) or a Thenable that resolves to such. + */ +export namespace CallHierarchyRequest { + export const type = new RequestType('textDocument/callHierarchy'); + export type HandlerSignature = RequestHandler; +} + +/** + * The parameters of a `textDocument/callHierarchy` request. + */ +export interface CallHierarchyParams extends TextDocumentPositionParams { + /** + * The number of levels to resolve. + */ + resolve?: number; + /** + * Outgoing direction for callees. + * The default is incoming for callers. + */ + direction?: 'incoming' | 'outgoing'; +} + +/** + * The parameters of a `textDocument/callHierarchy` request. + */ +export interface ResolveCallHierarchyItemParams { + /** + * Unresolved item. + */ + item: CallHierarchyItem; + /** + * The number of levels to resolve. + */ + resolve: number; + /** + * Outgoing direction for callees. + * The default is incoming for callers. + */ + direction: 'incoming' | 'outgoing'; +} + +/** + * The result of a `textDocument/callHierarchy` request. + */ +export interface CallHierarchyItem { + /** + * The name of the symbol targeted by the call hierarchy request. + */ + name: string; + /** + * More detail for this symbol, e.g the signature of a function. + */ + detail?: string; + /** + * The kind of this symbol. + */ + kind: SymbolKind; + /** + * URI of the document containing the symbol. + */ + uri: string; + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the the clients cursor is + * inside the symbol to reveal in the symbol in the UI. + */ + range: Range; + /** + * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. + * Must be contained by the the `range`. + */ + selectionRange: Range; + + /** + * The actual location of the call. + * + * **Must be defined** in resolved callers/callees. + */ + callLocation?: Location; + + /** + * List of incoming calls. + * + * *Note*: The items is _unresolved_ if `callers` and `callees` is undefined. + */ + callers?: CallHierarchyItem[]; + /** + * List of outgoing calls. + * + * *Note*: The items is _unresolved_ if `callers` and `callees` is undefined. + */ + callees?: CallHierarchyItem[]; +}