提交 e6df99ae 编写于 作者: J jrieken

implement CallsFrom/CallsTo proposal

上级 e4a837a0
......@@ -18,11 +18,6 @@ declare module 'vscode' {
//#region Joh - call hierarchy
export enum CallHierarchyDirection {
CallsFrom = 1,
CallsTo = 2,
}
export class CallHierarchyItem {
kind: SymbolKind;
name: string;
......@@ -34,33 +29,29 @@ declare module 'vscode' {
constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range);
}
export class CallsTo {
source: CallHierarchyItem;
sourceRanges: Range[];
constructor(item: CallHierarchyItem, sourceRanges: Range[]);
}
export class CallsFrom {
sourceRanges: Range[];
target: CallHierarchyItem;
constructor(item: CallHierarchyItem, sourceRanges: Range[]);
}
export interface CallHierarchyItemProvider {
/**
* Given a document and position compute a call hierarchy item. This is justed as
* anchor for call hierarchy and then `resolveCallHierarchyItem` is being called.
*/
provideCallHierarchyItem(
document: TextDocument,
position: Position,
token: CancellationToken
): ProviderResult<CallHierarchyItem>;
resolveCallHierarchyItem(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<CallHierarchyItem>;
/**
* Resolve a call hierarchy item, e.g. compute all calls from or to a function.
* The result is an array of item/location-tuples. The location in the returned tuples
* is always relative to the "caller" with the caller either being the provided item or
* the returned item.
*
* @param item A call hierarchy item previously returned from `provideCallHierarchyItem` or `resolveCallHierarchyItem`
* @param direction Resolve calls from a function or calls to a function
* @param token A cancellation token
*/
resolveCallHierarchyItem(
item: CallHierarchyItem,
direction: CallHierarchyDirection,
token: CancellationToken
): ProviderResult<[CallHierarchyItem, Location[]][]>;
provideCallsTo(target: CallHierarchyItem, token: CancellationToken): ProviderResult<CallsTo[]>;
provideCallsFrom(source: CallHierarchyItem, token: CancellationToken): ProviderResult<CallsFrom[]>;
}
export namespace languages {
......
......@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyDto, ISuggestDataDto, ICodeActionDto } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -111,7 +111,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return <modes.ILink>data;
}
private static _reviveCallHierarchyItemDto(data: ICallHierarchyDto | undefined): callh.CallHierarchyItem {
private static _reviveCallHierarchyItemDto(data: ICallHierarchyItemDto | undefined): callh.CallHierarchyItem {
if (data) {
data.uri = URI.revive(data.uri);
}
......@@ -494,21 +494,31 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, callh.CallHierarchyProviderRegistry.register(selector, {
provideCallHierarchyItem: (document, position, token) => {
return this._proxy.$provideCallHierarchyItem(handle, document.uri, position, token).then(MainThreadLanguageFeatures._reviveCallHierarchyItemDto);
resolveCallHierarchyItem: (document, position, token) => {
return this._proxy.$resolveCallHierarchyItem(handle, document.uri, position, token).then(MainThreadLanguageFeatures._reviveCallHierarchyItemDto);
},
resolveCallHierarchyItem: (item, direction, token) => {
return this._proxy.$resolveCallHierarchyItem(handle, item, direction, token).then(data => {
if (data) {
for (let i = 0; i < data.length; i++) {
const [item, locations] = data[i];
data[i] = [
MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item),
MainThreadLanguageFeatures._reviveLocationDto(locations)
];
}
provideCallsFrom: async (item, token) => {
const callsFrom = await this._proxy.$provideCallHierarchyItemsFrom(handle, item, token);
if (!callsFrom) {
return callsFrom;
}
return callsFrom.map(([item, sourceRanges]): callh.CallsFrom => {
return {
target: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item),
sourceRanges
}
});
},
provideCallsTo: async (item, token) => {
const callsTo = await this._proxy.$provideCallHierarchyItemsTo(handle, item, token);
if (!callsTo) {
return callsTo;
}
return callsTo.map(([item, sourceRanges]): callh.CallsTo => {
return {
source: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item),
sourceRanges
}
return data as [callh.CallHierarchyItem, modes.Location[]][];
});
}
}));
......
......@@ -893,7 +893,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ViewColumn: extHostTypes.ViewColumn,
WorkspaceEdit: extHostTypes.WorkspaceEdit,
// proposed
CallHierarchyDirection: extHostTypes.CallHierarchyDirection,
CallsFrom: extHostTypes.CallsFrom,
CallsTo: extHostTypes.CallsTo,
CallHierarchyItem: extHostTypes.CallHierarchyItem,
Decoration: extHostTypes.Decoration
};
......
......@@ -1067,7 +1067,7 @@ export interface ICodeLensDto {
command?: ICommandDto;
}
export interface ICallHierarchyDto {
export interface ICallHierarchyItemDto {
_id: number;
kind: modes.SymbolKind;
name: string;
......@@ -1111,8 +1111,9 @@ export interface ExtHostLanguageFeaturesShape {
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[] | undefined>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[] | undefined>;
$provideSelectionRanges(handle: number, resource: UriComponents, positions: IPosition[], token: CancellationToken): Promise<modes.SelectionRange[][]>;
$provideCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ICallHierarchyDto | undefined>;
$resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[ICallHierarchyDto, modes.Location[]][]>;
$resolveCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ICallHierarchyItemDto | undefined>;
$provideCallHierarchyItemsTo(handle: number, target: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined>;
$provideCallHierarchyItemsFrom(handle: number, source: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined>;
}
export interface ExtHostQuickOpenShape {
......
......@@ -14,7 +14,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorInfo, IMainContext, IdObject, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILanguageConfigurationDto, IWorkspaceSymbolDto, ISuggestResultDto, IWorkspaceSymbolsDto, ICodeActionDto, IDocumentFilterDto, IWorkspaceEditDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICodeLensDto, ISuggestDataDto, ILinksListDto, ChainedCacheId, ICodeLensListDto, ICodeActionListDto, ISignatureHelpDto, ISignatureHelpContextDto } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorInfo, IMainContext, IdObject, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILanguageConfigurationDto, IWorkspaceSymbolDto, ISuggestResultDto, IWorkspaceSymbolsDto, ICodeActionDto, IDocumentFilterDto, IWorkspaceEditDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICodeLensDto, ISuggestDataDto, ILinksListDto, ChainedCacheId, ICodeLensListDto, ICodeActionListDto, ISignatureHelpDto, ISignatureHelpContextDto, ICallHierarchyItemDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
......@@ -1011,8 +1011,8 @@ class SelectionRangeAdapter {
class CallHierarchyAdapter {
// todo@joh keep object (heap service, lifecycle)
private readonly _cache = new LRUCache<number, vscode.CallHierarchyItem>(1000, 0.8);
// todo@joh keep object (add managed lifecycle)
private readonly _cache = new LRUCache<number, vscode.CallHierarchyItem>(3000, 0.8);
private _idPool = 0;
constructor(
......@@ -1020,33 +1020,39 @@ class CallHierarchyAdapter {
private readonly _provider: vscode.CallHierarchyItemProvider
) { }
provideCallHierarchyItem(resource: URI, pos: IPosition, token: CancellationToken): Promise<undefined | callHierarchy.CallHierarchyItem> {
async resolveCallHierarchyItem(resource: URI, pos: IPosition, token: CancellationToken): Promise<undefined | callHierarchy.CallHierarchyItem> {
const document = this._documents.getDocument(resource);
const position = typeConvert.Position.to(pos);
return asPromise(() => this._provider.provideCallHierarchyItem(document, position, token)).then(item => {
if (!item) {
return undefined;
}
return this._fromItem(item);
});
const item = await this._provider.resolveCallHierarchyItem(document, position, token);
if (!item) {
return undefined;
}
return this._fromItem(item);
}
resolveCallHierarchyItem(item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[callHierarchy.CallHierarchyItem, modes.Location[]][]> {
return asPromise(() => this._provider.resolveCallHierarchyItem(
this._cache.get(item._id)!,
direction as number, token) // todo@joh proper convert
).then(data => {
if (!data) {
return [];
}
return data.map(tuple => {
return <[callHierarchy.CallHierarchyItem, modes.Location[]]>[
this._fromItem(tuple[0]),
tuple[1].map(typeConvert.location.from)
];
});
});
async provideCallsTo(target: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined> {
const item = this._cache.get(target._id);
if (!item) {
return undefined;
}
const calls = await this._provider.provideCallsTo(item, token);
if (!calls) {
return undefined;
}
return calls.map(call => (<[ICallHierarchyItemDto, IRange[]]>[this._fromItem(call.source), call.sourceRanges.map(typeConvert.Range.from)]))
}
async provideCallsFrom(source: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined> {
const item = this._cache.get(source._id);
if (!item) {
return undefined;
}
const calls = await this._provider.provideCallsFrom(item, token);
if (!calls) {
return undefined;
}
return calls.map(call => (<[ICallHierarchyItemDto, IRange[]]>[this._fromItem(call.target), call.sourceRanges.map(typeConvert.Range.from)]))
}
private _fromItem(item: vscode.CallHierarchyItem, _id: number = this._idPool++): callHierarchy.CallHierarchyItem {
......@@ -1496,12 +1502,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<undefined | callHierarchy.CallHierarchyItem> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallHierarchyItem(URI.revive(resource), position, token), undefined);
$resolveCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ICallHierarchyItemDto | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.resolveCallHierarchyItem(URI.revive(resource), position, token), undefined);
}
$provideCallHierarchyItemsTo(handle: number, target: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsTo(target, token), undefined);
}
$resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[callHierarchy.CallHierarchyItem, modes.Location[]][]> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.resolveCallHierarchyItem(item, direction, token), []);
$provideCallHierarchyItemsFrom(handle: number, source: callHierarchy.CallHierarchyItem, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsFrom(source, token), undefined);
}
// --- configuration
......
......@@ -1146,12 +1146,6 @@ export class SelectionRange {
}
}
export enum CallHierarchyDirection {
CallsFrom = 1,
CallsTo = 2,
}
export class CallHierarchyItem {
kind: SymbolKind;
name: string;
......@@ -1170,6 +1164,27 @@ export class CallHierarchyItem {
}
}
export class CallsTo {
source: CallHierarchyItem;
sourceRanges: Range[];
constructor(item: CallHierarchyItem, sourceRanges: Range[]) {
this.sourceRanges = sourceRanges;
this.source = item;
}
}
export class CallsFrom {
target: CallHierarchyItem;
sourceRanges: Range[];
constructor(item: CallHierarchyItem, sourceRanges: Range[]) {
this.sourceRanges = sourceRanges;
this.target = item;
}
}
@es5ClassCompat
export class CodeLens {
......
......@@ -93,7 +93,7 @@ class CallHierarchyController extends Disposable implements IEditorContribution
this._sessionDispose.push({ dispose() { cancel.cancel(); } });
this._sessionDispose.push(widget);
Promise.resolve(provider.provideCallHierarchyItem(model, position, cancel.token)).then(item => {
Promise.resolve(provider.resolveCallHierarchyItem(model, position, cancel.token)).then(item => {
if (cancel.token.isCancellationRequested) {
return;
}
......
......@@ -7,7 +7,7 @@ import 'vs/css!./media/callHierarchy';
import { PeekViewWidget, IPeekViewService } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { CallHierarchyProvider, CallHierarchyDirection, CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { CallHierarchyProvider, CallHierarchyItem, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { FuzzyScore } from 'vs/base/common/filters';
import * as callHTree from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyTree';
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { CallHierarchyItem, CallHierarchyDirection, CallHierarchyProvider } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { CallHierarchyItem, CallHierarchyProvider, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
......@@ -12,6 +12,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { symbolKindToCssClass, Location } from 'vs/editor/common/modes';
import { Range } from 'vs/editor/common/core/range';
import { hash } from 'vs/base/common/hash';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
export class Call {
constructor(
......@@ -34,21 +35,55 @@ export class SingleDirectionDataSource implements IAsyncDataSource<CallHierarchy
async getChildren(element: CallHierarchyItem | Call): Promise<Call[]> {
if (element instanceof Call) {
try {
const direction = this.getDirection();
const calls = await this.provider.resolveCallHierarchyItem(element.item, direction, CancellationToken.None);
if (!calls) {
return [];
}
return calls.map(([item, locations]) => new Call(item, locations, element));
} catch {
return [];
const results: Call[] = [];
if (this.getDirection() === CallHierarchyDirection.CallsFrom) {
await this._getCallsFrom(element, results);
} else {
await this._getCallsTo(element, results);
}
return results;
} else {
// 'root'
return [new Call(element, [{ uri: element.uri, range: Range.lift(element.range).collapseToStart() }], undefined)];
}
}
private async _getCallsFrom(source: Call, bucket: Call[]): Promise<void> {
try {
const callsFrom = await this.provider.provideCallsFrom(source.item, CancellationToken.None);
if (!callsFrom) {
return;
}
for (const callFrom of callsFrom) {
bucket.push(new Call(
callFrom.target,
callFrom.sourceRanges.map(range => ({ range, uri: source.item.uri })),
source
));
}
} catch (e) {
onUnexpectedExternalError(e);
}
}
private async _getCallsTo(target: Call, bucket: Call[]): Promise<void> {
try {
const callsTo = await this.provider.provideCallsTo(target.item, CancellationToken.None);
if (!callsTo) {
return;
}
for (const callTo of callsTo) {
bucket.push(new Call(
callTo.source,
callTo.sourceRanges.map(range => ({ range, uri: callTo.source.uri })),
target
));
}
} catch (e) {
onUnexpectedExternalError(e);
}
}
}
export class IdentityProvider implements IIdentityProvider<Call> {
......
......@@ -3,17 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { SymbolKind, ProviderResult, Location } from 'vs/editor/common/modes';
import { SymbolKind, ProviderResult } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { CancellationToken } from 'vs/base/common/cancellation';
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
import { URI } from 'vs/base/common/uri';
import { IPosition } from 'vs/editor/common/core/position';
export const enum CallHierarchyDirection {
CallsFrom = 1,
CallsTo = 2
CallsTo, CallsFrom
}
export interface CallHierarchyItem {
......@@ -26,19 +25,23 @@ export interface CallHierarchyItem {
selectionRange: IRange;
}
export interface CallsTo {
source: CallHierarchyItem,
sourceRanges: IRange[]
}
export interface CallsFrom {
sourceRanges: IRange[],
target: CallHierarchyItem
}
export interface CallHierarchyProvider {
provideCallHierarchyItem(
document: ITextModel,
position: IPosition,
token: CancellationToken
): ProviderResult<CallHierarchyItem>;
resolveCallHierarchyItem(
item: CallHierarchyItem,
direction: CallHierarchyDirection,
token: CancellationToken
): ProviderResult<[CallHierarchyItem, Location[]][]>;
resolveCallHierarchyItem(document: ITextModel, postion: IPosition, token: CancellationToken): ProviderResult<CallHierarchyItem>;
provideCallsTo(target: CallHierarchyItem, token: CancellationToken): ProviderResult<CallsTo[]>;
provideCallsFrom(source: CallHierarchyItem, token: CancellationToken): ProviderResult<CallsFrom[]>;
}
export const CallHierarchyProviderRegistry = new LanguageFeatureRegistry<CallHierarchyProvider>();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册