Skip to content

Commit

Permalink
Proposal for call hierarchy
Browse files Browse the repository at this point in the history
add new `textDocument/calls` request.

LSP issue: language-server-protocol#468

Signed-off-by: Alex Tugarev <[email protected]>
  • Loading branch information
AlexTugarev committed Jan 22, 2019
1 parent 1631753 commit 9a41301
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 0 deletions.
62 changes: 62 additions & 0 deletions client/src/callHierarchy.proposed.ts
Original file line number Diff line number Diff line change
@@ -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<TextDocumentRegistrationOptions> {

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(() => { });
}

}
5 changes: 5 additions & 0 deletions client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>)[] {
let result: (StaticFeature | DynamicFeature<any>)[] = [];
result.push(new CallHierarchyFeature(_client));
return result;
}
}
13 changes: 13 additions & 0 deletions protocol/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
123 changes: 123 additions & 0 deletions protocol/src/protocol.callHierarchy.proposed.md
Original file line number Diff line number Diff line change
@@ -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[];
}
```
135 changes: 135 additions & 0 deletions protocol/src/protocol.callHierarchy.proposed.ts
Original file line number Diff line number Diff line change
@@ -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<CallHierarchyParams | ResolveCallHierarchyItemParams, CallHierarchyItem, void, TextDocumentRegistrationOptions>('textDocument/callHierarchy');
export type HandlerSignature = RequestHandler<CallHierarchyParams | ResolveCallHierarchyItemParams, CallHierarchyItem | null, void>;
}

/**
* 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[];
}

0 comments on commit 9a41301

Please sign in to comment.