mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 02:26:04 +00:00
Merge pull request #70748 from Microsoft/joh/callh
Implement call hierarchy
This commit is contained in:
commit
63ce59c390
|
@ -42,6 +42,10 @@
|
|||
"name": "vs/workbench/contrib/codeinset",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/callHierarchy",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/comments",
|
||||
"project": "vscode-workbench"
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"./vs/workbench/common/**/*",
|
||||
"./vs/workbench/browser/**/*",
|
||||
"./vs/workbench/electron-browser/**/*",
|
||||
"./vs/workbench/contrib/callHierarchy/**/*",
|
||||
"./vs/workbench/contrib/emmet/**/*",
|
||||
"./vs/workbench/contrib/extensions/**/*",
|
||||
"./vs/workbench/contrib/externalTerminal/**/*",
|
||||
|
@ -391,4 +392,4 @@
|
|||
"./typings/require-monaco.d.ts",
|
||||
"./vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
|||
import { DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
|
||||
export function getDocumentSymbols(model: ITextModel, flat: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
|
||||
|
||||
|
@ -70,8 +71,20 @@ registerLanguageCommand('_executeDocumentSymbolProvider', function (accessor, ar
|
|||
throw illegalArgument('resource');
|
||||
}
|
||||
const model = accessor.get(IModelService).getModel(resource);
|
||||
if (!model) {
|
||||
throw illegalArgument('resource');
|
||||
if (model) {
|
||||
return getDocumentSymbols(model, false, CancellationToken.None);
|
||||
}
|
||||
return getDocumentSymbols(model, false, CancellationToken.None);
|
||||
|
||||
return accessor.get(ITextModelService).createModelReference(resource).then(reference => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const result = getDocumentSymbols(reference.object.textEditorModel, false, CancellationToken.None);
|
||||
resolve(result);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}).finally(() => {
|
||||
reference.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
40
src/vs/vscode.proposed.d.ts
vendored
40
src/vs/vscode.proposed.d.ts
vendored
|
@ -16,6 +16,46 @@
|
|||
|
||||
declare module 'vscode' {
|
||||
|
||||
//#region Joh - call hierarchy
|
||||
|
||||
export enum CallHierarchyDirection {
|
||||
CallsFrom = 1,
|
||||
CallsTo = 2,
|
||||
}
|
||||
|
||||
export class CallHierarchyItem {
|
||||
kind: SymbolKind;
|
||||
name: string;
|
||||
detail?: string;
|
||||
uri: Uri;
|
||||
range: Range;
|
||||
selectionRange: Range;
|
||||
|
||||
constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range);
|
||||
}
|
||||
|
||||
export interface CallHierarchyItemProvider {
|
||||
|
||||
provideCallHierarchyItem(
|
||||
document: TextDocument,
|
||||
postion: Position,
|
||||
token: CancellationToken
|
||||
): ProviderResult<CallHierarchyItem>;
|
||||
|
||||
resolveCallHierarchyItem(
|
||||
item: CallHierarchyItem,
|
||||
direction: CallHierarchyDirection,
|
||||
token: CancellationToken
|
||||
): ProviderResult<[CallHierarchyItem, Location[]][]>;
|
||||
}
|
||||
|
||||
export namespace languages {
|
||||
export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyItemProvider): Disposable;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Alex - resolvers
|
||||
|
||||
export class ResolvedAuthority {
|
||||
|
|
|
@ -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 } from 'vs/editor/common/core/range';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto } from '../node/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IHeapService } from './mainThreadHeapService';
|
||||
|
@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
|
||||
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
|
||||
|
@ -114,6 +115,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
|||
return <modes.ILink>data;
|
||||
}
|
||||
|
||||
private static _reviveCallHierarchyItemDto(data: CallHierarchyDto | undefined): callh.CallHierarchyItem {
|
||||
if (data) {
|
||||
data.uri = URI.revive(data.uri);
|
||||
}
|
||||
return data as callh.CallHierarchyItem;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
// --- outline
|
||||
|
@ -471,6 +479,30 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
|||
});
|
||||
}
|
||||
|
||||
// --- call hierarchy
|
||||
|
||||
$registerCallHierarchyProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = callh.CallHierarchyProviderRegistry.register(typeConverters.LanguageSelector.from(selector), {
|
||||
provideCallHierarchyItem: (document, position, token) => {
|
||||
return this._proxy.$provideCallHierarchyItem(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)
|
||||
];
|
||||
}
|
||||
}
|
||||
return data as [callh.CallHierarchyItem, modes.Location[]][];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
private static _reviveRegExp(regExp: ISerializedRegExp): RegExp {
|
||||
|
|
|
@ -357,6 +357,10 @@ export function createApiFactory(
|
|||
registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider);
|
||||
},
|
||||
registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider);
|
||||
},
|
||||
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
|
||||
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
|
||||
}
|
||||
|
@ -831,7 +835,9 @@ export function createApiFactory(
|
|||
Uri: URI,
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
// functions
|
||||
// proposed
|
||||
CallHierarchyDirection: extHostTypes.CallHierarchyDirection,
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityReso
|
|||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IRemoteConsoleLog } from 'vs/base/node/console';
|
||||
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
|
||||
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
|
@ -337,6 +338,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
|||
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
|
||||
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
|
||||
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
|
||||
$registerCallHierarchyProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
|
||||
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
|
||||
}
|
||||
|
||||
|
@ -920,6 +922,16 @@ export interface CodeLensDto extends ObjectIdentifier {
|
|||
|
||||
export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol;
|
||||
|
||||
export interface CallHierarchyDto {
|
||||
_id: number;
|
||||
kind: modes.SymbolKind;
|
||||
name: string;
|
||||
detail?: string;
|
||||
uri: UriComponents;
|
||||
range: IRange;
|
||||
selectionRange: IRange;
|
||||
}
|
||||
|
||||
export interface ExtHostLanguageFeaturesShape {
|
||||
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
|
||||
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
|
||||
|
@ -952,6 +964,8 @@ 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<CallHierarchyDto | undefined>;
|
||||
$resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[CallHierarchyDto, modes.Location[]][]>;
|
||||
}
|
||||
|
||||
export interface ExtHostQuickOpenShape {
|
||||
|
|
|
@ -28,6 +28,8 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
|
|||
import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview';
|
||||
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
|
||||
// --- adapter
|
||||
|
||||
|
@ -963,11 +965,66 @@ class SelectionRangeAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
class CallHierarchyAdapter {
|
||||
|
||||
// todo@joh keep object (heap service, lifecycle)
|
||||
private readonly _cache = new LRUCache<number, vscode.CallHierarchyItem>(1000, 0.8);
|
||||
private _idPool = 0;
|
||||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.CallHierarchyItemProvider
|
||||
) { }
|
||||
|
||||
provideCallHierarchyItem(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);
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _fromItem(item: vscode.CallHierarchyItem, _id: number = this._idPool++): callHierarchy.CallHierarchyItem {
|
||||
const res = <callHierarchy.CallHierarchyItem>{
|
||||
_id,
|
||||
name: item.name,
|
||||
detail: item.detail,
|
||||
kind: typeConvert.SymbolKind.from(item.kind),
|
||||
uri: item.uri,
|
||||
range: typeConvert.Range.from(item.range),
|
||||
selectionRange: typeConvert.Range.from(item.selectionRange),
|
||||
};
|
||||
this._cache.set(_id, item);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
|
||||
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
|
||||
| ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter;
|
||||
| ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
|
||||
|
||||
class AdapterData {
|
||||
constructor(
|
||||
|
@ -1415,6 +1472,22 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
|
|||
return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), positions, token));
|
||||
}
|
||||
|
||||
// --- call hierarchy
|
||||
|
||||
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new CallHierarchyAdapter(this._documents, provider), extension);
|
||||
this._proxy.$registerCallHierarchyProvider(handle, this._transformDocumentSelector(selector));
|
||||
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));
|
||||
}
|
||||
|
||||
$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));
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {
|
||||
|
|
|
@ -883,7 +883,6 @@ export namespace TextDocumentSaveReason {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace EndOfLine {
|
||||
|
||||
export function from(eol: vscode.EndOfLine): EndOfLineSequence | undefined {
|
||||
|
|
|
@ -1111,6 +1111,29 @@ export class SelectionRange {
|
|||
}
|
||||
|
||||
|
||||
export enum CallHierarchyDirection {
|
||||
CallsFrom = 1,
|
||||
CallsTo = 2,
|
||||
}
|
||||
|
||||
export class CallHierarchyItem {
|
||||
kind: SymbolKind;
|
||||
name: string;
|
||||
detail?: string;
|
||||
uri: URI;
|
||||
range: Range;
|
||||
selectionRange: Range;
|
||||
|
||||
constructor(kind: SymbolKind, name: string, detail: string, uri: URI, range: Range, selectionRange: Range) {
|
||||
this.kind = kind;
|
||||
this.name = name;
|
||||
this.detail = detail;
|
||||
this.uri = uri;
|
||||
this.range = range;
|
||||
this.selectionRange = selectionRange;
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class CodeLens {
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { CallHierarchyProviderRegistry, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CallHierarchyTreePeekWidget } from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { registerEditorContribution, registerEditorAction, EditorAction, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContextKeyService, RawContextKey, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
|
||||
|
||||
const _ctxHasCompletionItemProvider = new RawContextKey<boolean>('editorHasCallHierarchyProvider', false);
|
||||
const _ctxCallHierarchyVisible = new RawContextKey<boolean>('callHierarchyVisible', false);
|
||||
|
||||
class CallHierarchyController extends Disposable implements IEditorContribution {
|
||||
|
||||
static Id = 'callHierarchy';
|
||||
|
||||
static get(editor: ICodeEditor): CallHierarchyController {
|
||||
return editor.getContribution<CallHierarchyController>(CallHierarchyController.Id);
|
||||
}
|
||||
|
||||
private readonly _ctxHasProvider: IContextKey<boolean>;
|
||||
private readonly _ctxIsVisible: IContextKey<boolean>;
|
||||
|
||||
private _sessionDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._ctxIsVisible = _ctxCallHierarchyVisible.bindTo(this._contextKeyService);
|
||||
this._ctxHasProvider = _ctxHasCompletionItemProvider.bindTo(this._contextKeyService);
|
||||
this._register(Event.any<any>(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, CallHierarchyProviderRegistry.onDidChange)(() => {
|
||||
this._ctxHasProvider.set(_editor.hasModel() && CallHierarchyProviderRegistry.has(_editor.getModel()));
|
||||
}));
|
||||
|
||||
this._register({ dispose: () => dispose(this._sessionDispose) });
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._ctxHasProvider.reset();
|
||||
this._ctxIsVisible.reset();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return CallHierarchyController.Id;
|
||||
}
|
||||
|
||||
async startCallHierarchy(): Promise<void> {
|
||||
this._sessionDispose = dispose(this._sessionDispose);
|
||||
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
const position = this._editor.getPosition();
|
||||
const [provider] = CallHierarchyProviderRegistry.ordered(model);
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDispose);
|
||||
const widget = this._instantiationService.createInstance(
|
||||
CallHierarchyTreePeekWidget,
|
||||
this._editor,
|
||||
position,
|
||||
provider,
|
||||
CallHierarchyDirection.CallsTo
|
||||
);
|
||||
|
||||
widget.showLoading();
|
||||
this._ctxIsVisible.set(true);
|
||||
|
||||
const cancel = new CancellationTokenSource();
|
||||
|
||||
this._sessionDispose.push(widget.onDidClose(() => this.endCallHierarchy()));
|
||||
this._sessionDispose.push({ dispose() { cancel.cancel(); } });
|
||||
this._sessionDispose.push(widget);
|
||||
|
||||
Promise.resolve(provider.provideCallHierarchyItem(model, position, cancel.token)).then(item => {
|
||||
if (cancel.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
if (!item) {
|
||||
widget.showMessage(localize('no.item', "No results"));
|
||||
return;
|
||||
}
|
||||
|
||||
widget.showItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
endCallHierarchy(): void {
|
||||
this._sessionDispose = dispose(this._sessionDispose);
|
||||
this._ctxIsVisible.set(false);
|
||||
this._editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(CallHierarchyController);
|
||||
|
||||
registerEditorAction(class extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.showCallHierarchy',
|
||||
label: localize('title', "Call Hierarchy"),
|
||||
alias: 'Call Hierarchy',
|
||||
menuOpts: {
|
||||
group: 'navigation',
|
||||
order: 111
|
||||
},
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H
|
||||
},
|
||||
precondition: _ctxHasCompletionItemProvider
|
||||
});
|
||||
}
|
||||
|
||||
async run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
|
||||
return CallHierarchyController.get(editor).startCallHierarchy();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
registerEditorCommand(new class extends EditorCommand {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.closeCallHierarchy',
|
||||
kbOpts: {
|
||||
weight: KeybindingWeight.WorkbenchContrib + 10,
|
||||
primary: KeyCode.Escape
|
||||
},
|
||||
precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, ContextKeyExpr.not('config.editor.stablePeek'))
|
||||
});
|
||||
}
|
||||
|
||||
runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
return CallHierarchyController.get(editor).endCallHierarchy();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,425 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/callHierarchy';
|
||||
import { PeekViewWidget } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CallHierarchyItem, CallHierarchyProvider, 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';
|
||||
import { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { SplitView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Dimension, addClass } from 'vs/base/browser/dom';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { registerThemingParticipant, themeColorFromId, IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as referencesWidget from 'vs/editor/contrib/referenceSearch/referencesWidget';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
const enum State {
|
||||
Loading = 'loading',
|
||||
Message = 'message',
|
||||
Data = 'data'
|
||||
}
|
||||
|
||||
class ToggleHierarchyDirectionAction extends Action {
|
||||
|
||||
constructor(public direction: () => CallHierarchyDirection, callback: () => void) {
|
||||
super('toggle.dir', undefined, 'call-hierarchy-toggle', true, () => {
|
||||
callback();
|
||||
this._update();
|
||||
return Promise.resolve();
|
||||
});
|
||||
this._update();
|
||||
}
|
||||
|
||||
private _update() {
|
||||
if (this.direction() === CallHierarchyDirection.CallsFrom) {
|
||||
this.label = localize('toggle.from', "Calls From...");
|
||||
this.checked = true;
|
||||
} else {
|
||||
this.label = localize('toggle.to', "Calls To...");
|
||||
this.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutInfo {
|
||||
|
||||
static store(info: LayoutInfo, storageService: IStorageService): void {
|
||||
storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
static retrieve(storageService: IStorageService): LayoutInfo {
|
||||
const value = storageService.get('callHierarchyPeekLayout', StorageScope.GLOBAL, '{}');
|
||||
const defaultInfo: LayoutInfo = { ratio: 0.7, height: 17 };
|
||||
try {
|
||||
return { ...defaultInfo, ...JSON.parse(value) };
|
||||
} catch {
|
||||
return defaultInfo;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
public ratio: number,
|
||||
public height: number
|
||||
) { }
|
||||
}
|
||||
|
||||
export class CallHierarchyTreePeekWidget extends PeekViewWidget {
|
||||
|
||||
private _toggleDirection: ToggleHierarchyDirectionAction;
|
||||
private _parent: HTMLElement;
|
||||
private _message: HTMLElement;
|
||||
private _splitView: SplitView;
|
||||
private _tree: WorkbenchAsyncDataTree<CallHierarchyItem, callHTree.Call, FuzzyScore>;
|
||||
private _editor: EmbeddedCodeEditorWidget;
|
||||
private _dim: Dimension;
|
||||
private _layoutInfo: LayoutInfo;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
private readonly _where: IPosition,
|
||||
private readonly _provider: CallHierarchyProvider,
|
||||
private _direction: CallHierarchyDirection,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ITextModelService private readonly _textModelService: ITextModelService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super(editor, { showFrame: true, showArrow: true, isResizeable: true, isAccessible: true });
|
||||
this.create();
|
||||
this._applyTheme(themeService.getTheme());
|
||||
themeService.onThemeChange(this._applyTheme, this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
LayoutInfo.store(this._layoutInfo, this._storageService);
|
||||
this._splitView.dispose();
|
||||
this._tree.dispose();
|
||||
this._editor.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
const borderColor = theme.getColor(referencesWidget.peekViewBorder) || Color.transparent;
|
||||
this.style({
|
||||
arrowColor: borderColor,
|
||||
frameColor: borderColor,
|
||||
headerBackgroundColor: theme.getColor(referencesWidget.peekViewTitleBackground) || Color.transparent,
|
||||
primaryHeadingColor: theme.getColor(referencesWidget.peekViewTitleForeground),
|
||||
secondaryHeadingColor: theme.getColor(referencesWidget.peekViewTitleInfoForeground)
|
||||
});
|
||||
}
|
||||
|
||||
protected _getActionBarOptions(): IActionBarOptions {
|
||||
return {
|
||||
orientation: ActionsOrientation.HORIZONTAL_REVERSE
|
||||
};
|
||||
}
|
||||
|
||||
protected _fillBody(parent: HTMLElement): void {
|
||||
|
||||
this._layoutInfo = LayoutInfo.retrieve(this._storageService);
|
||||
this._dim = { height: 0, width: 0 };
|
||||
|
||||
this._parent = parent;
|
||||
addClass(parent, 'call-hierarchy');
|
||||
|
||||
const message = document.createElement('div');
|
||||
addClass(message, 'message');
|
||||
parent.appendChild(message);
|
||||
this._message = message;
|
||||
|
||||
const container = document.createElement('div');
|
||||
addClass(container, 'results');
|
||||
parent.appendChild(container);
|
||||
|
||||
this._splitView = new SplitView(container, { orientation: Orientation.HORIZONTAL });
|
||||
|
||||
// editor stuff
|
||||
const editorContainer = document.createElement('div');
|
||||
addClass(editorContainer, 'editor');
|
||||
container.appendChild(editorContainer);
|
||||
let editorOptions: IEditorOptions = {
|
||||
scrollBeyondLastLine: false,
|
||||
scrollbar: {
|
||||
verticalScrollbarSize: 14,
|
||||
horizontal: 'auto',
|
||||
useShadows: true,
|
||||
verticalHasArrows: false,
|
||||
horizontalHasArrows: false
|
||||
},
|
||||
overviewRulerLanes: 2,
|
||||
fixedOverflowWidgets: true,
|
||||
minimap: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
this._editor = this._instantiationService.createInstance(
|
||||
EmbeddedCodeEditorWidget,
|
||||
editorContainer,
|
||||
editorOptions,
|
||||
this.editor
|
||||
);
|
||||
|
||||
// tree stuff
|
||||
const treeContainer = document.createElement('div');
|
||||
addClass(treeContainer, 'tree');
|
||||
container.appendChild(treeContainer);
|
||||
const options: IAsyncDataTreeOptions<callHTree.Call, FuzzyScore> = {
|
||||
identityProvider: new callHTree.IdentityProvider(),
|
||||
ariaLabel: localize('tree.aria', "Call Hierarchy"),
|
||||
expandOnlyOnTwistieClick: true,
|
||||
};
|
||||
this._tree = <any>this._instantiationService.createInstance(
|
||||
WorkbenchAsyncDataTree,
|
||||
treeContainer,
|
||||
new callHTree.VirtualDelegate(),
|
||||
[this._instantiationService.createInstance(callHTree.CallRenderer)],
|
||||
new callHTree.SingleDirectionDataSource(this._provider, () => this._direction),
|
||||
options
|
||||
);
|
||||
|
||||
// split stuff
|
||||
this._splitView.addView({
|
||||
onDidChange: Event.None,
|
||||
element: editorContainer,
|
||||
minimumSize: 200,
|
||||
maximumSize: Number.MAX_VALUE,
|
||||
layout: (width) => {
|
||||
this._editor.layout({ height: this._dim.height, width });
|
||||
}
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this._splitView.addView({
|
||||
onDidChange: Event.None,
|
||||
element: treeContainer,
|
||||
minimumSize: 100,
|
||||
maximumSize: Number.MAX_VALUE,
|
||||
layout: (width) => {
|
||||
this._tree.layout(this._dim.height, width);
|
||||
}
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this._splitView.onDidSashChange(() => {
|
||||
if (this._dim.width) {
|
||||
this._layoutInfo.ratio = this._splitView.getViewSize(0) / this._dim.width;
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
// session state
|
||||
let localDispose: IDisposable[] = [];
|
||||
this._disposables.push({ dispose() { dispose(localDispose); } });
|
||||
|
||||
// update editor
|
||||
this._tree.onDidChangeFocus(e => {
|
||||
const [element] = e.elements;
|
||||
if (element && isNonEmptyArray(element.locations)) {
|
||||
|
||||
localDispose = dispose(localDispose);
|
||||
|
||||
const options: IModelDecorationOptions = {
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'call-decoration',
|
||||
overviewRuler: {
|
||||
color: themeColorFromId(referencesWidget.peekViewEditorMatchHighlight),
|
||||
position: OverviewRulerLane.Center
|
||||
},
|
||||
};
|
||||
let decorations: IModelDeltaDecoration[] = [];
|
||||
let fullRange: IRange | undefined;
|
||||
for (const { range } of element.locations) {
|
||||
decorations.push({ range, options });
|
||||
fullRange = !fullRange ? range : Range.plusRange(range, fullRange);
|
||||
}
|
||||
|
||||
this._textModelService.createModelReference(element.item.uri).then(value => {
|
||||
this._editor.setModel(value.object.textEditorModel);
|
||||
this._editor.revealRangeInCenter(fullRange!, ScrollType.Smooth);
|
||||
this._editor.revealLine(element.item.range.startLineNumber, ScrollType.Smooth);
|
||||
const ids = this._editor.deltaDecorations([], decorations);
|
||||
localDispose.push({ dispose: () => this._editor.deltaDecorations(ids, []) });
|
||||
localDispose.push(value);
|
||||
});
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._editor.onMouseDown(e => {
|
||||
const { event, target } = e;
|
||||
if (event.detail !== 2) {
|
||||
return;
|
||||
}
|
||||
const [focus] = this._tree.getFocus();
|
||||
if (!focus) {
|
||||
return;
|
||||
}
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: focus.item.uri,
|
||||
options: { selection: target.range! }
|
||||
});
|
||||
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._tree.onMouseDblClick(e => {
|
||||
if (e.element && isNonEmptyArray(e.element.locations)) {
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: e.element.item.uri,
|
||||
options: { selection: e.element.locations[0].range }
|
||||
});
|
||||
}
|
||||
}, undefined, this._disposables);
|
||||
|
||||
this._tree.onDidChangeSelection(e => {
|
||||
const [element] = e.elements;
|
||||
// don't close on click
|
||||
if (element && !(e.browserEvent instanceof MouseEvent)) {
|
||||
this.dispose();
|
||||
this._editorService.openEditor({
|
||||
resource: element.item.uri,
|
||||
options: { selection: element.locations[0].range }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showLoading(): void {
|
||||
this._parent.dataset['state'] = State.Loading;
|
||||
this.setTitle(localize('title.loading', "Loading..."));
|
||||
this._show();
|
||||
}
|
||||
|
||||
showMessage(message: string): void {
|
||||
this._parent.dataset['state'] = State.Message;
|
||||
this.setTitle('');
|
||||
this.setMetaTitle('');
|
||||
this._message.innerText = message;
|
||||
this._show();
|
||||
}
|
||||
|
||||
showItem(item: CallHierarchyItem) {
|
||||
this._parent.dataset['state'] = State.Data;
|
||||
|
||||
this._show();
|
||||
this._tree.setInput(item).then(() => {
|
||||
|
||||
if (!this._tree.getFirstElementChild(item)) {
|
||||
//
|
||||
this.showMessage(this._direction === CallHierarchyDirection.CallsFrom
|
||||
? localize('empt.callsFrom', "No calls from '{0}'", item.name)
|
||||
: localize('empt.callsTo', "No calls to '{0}'", item.name));
|
||||
|
||||
} else {
|
||||
this._tree.domFocus();
|
||||
this._tree.focusFirst();
|
||||
this.setTitle(
|
||||
item.name,
|
||||
item.detail || this._labelService.getUriLabel(item.uri, { relative: true }),
|
||||
);
|
||||
this.setMetaTitle(this._direction === CallHierarchyDirection.CallsFrom
|
||||
? localize('title.from', " – calls from '{0}'", item.name)
|
||||
: localize('title.to', " – calls to '{0}'", item.name));
|
||||
}
|
||||
});
|
||||
|
||||
if (!this._toggleDirection) {
|
||||
this._toggleDirection = new ToggleHierarchyDirectionAction(
|
||||
() => this._direction,
|
||||
() => {
|
||||
let newDirection = this._direction === CallHierarchyDirection.CallsFrom ? CallHierarchyDirection.CallsTo : CallHierarchyDirection.CallsFrom;
|
||||
this._direction = newDirection;
|
||||
this.showItem(item);
|
||||
}
|
||||
);
|
||||
this._actionbarWidget.push(this._toggleDirection, { label: false, icon: true });
|
||||
this._disposables.push(this._toggleDirection);
|
||||
}
|
||||
}
|
||||
|
||||
private _show() {
|
||||
if (!this._isShowing) {
|
||||
this.editor.revealLineInCenterIfOutsideViewport(this._where.lineNumber, ScrollType.Smooth);
|
||||
super.show(Range.fromPositions(this._where), this._layoutInfo.height);
|
||||
}
|
||||
}
|
||||
|
||||
protected _onWidth(width: number) {
|
||||
if (this._dim) {
|
||||
this._doLayoutBody(this._dim.height, width);
|
||||
}
|
||||
}
|
||||
|
||||
protected _doLayoutBody(height: number, width: number): void {
|
||||
super._doLayoutBody(height, width);
|
||||
this._dim = { height, width };
|
||||
this._layoutInfo.height = this._viewZone ? this._viewZone.heightInLines : this._layoutInfo.height;
|
||||
this._splitView.layout(width);
|
||||
this._splitView.resizeView(0, width * this._layoutInfo.ratio);
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const referenceHighlightColor = theme.getColor(referencesWidget.peekViewEditorMatchHighlight);
|
||||
if (referenceHighlightColor) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .call-decoration { background-color: ${referenceHighlightColor}; }`);
|
||||
}
|
||||
const referenceHighlightBorder = theme.getColor(referencesWidget.peekViewEditorMatchHighlightBorder);
|
||||
if (referenceHighlightBorder) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .call-decoration { border: 2px solid ${referenceHighlightBorder}; box-sizing: border-box; }`);
|
||||
}
|
||||
const resultsBackground = theme.getColor(referencesWidget.peekViewResultsBackground);
|
||||
if (resultsBackground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree { background-color: ${resultsBackground}; }`);
|
||||
}
|
||||
const resultsMatchForeground = theme.getColor(referencesWidget.peekViewResultsFileForeground);
|
||||
if (resultsMatchForeground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree { color: ${resultsMatchForeground}; }`);
|
||||
}
|
||||
const resultsSelectedBackground = theme.getColor(referencesWidget.peekViewResultsSelectionBackground);
|
||||
if (resultsSelectedBackground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`);
|
||||
}
|
||||
const resultsSelectedForeground = theme.getColor(referencesWidget.peekViewResultsSelectionForeground);
|
||||
if (resultsSelectedForeground) {
|
||||
collector.addRule(`.monaco-editor .call-hierarchy .tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`);
|
||||
}
|
||||
const editorBackground = theme.getColor(referencesWidget.peekViewEditorBackground);
|
||||
if (editorBackground) {
|
||||
collector.addRule(
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .monaco-editor-background,` +
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .inputarea.ime-input {` +
|
||||
` background-color: ${editorBackground};` +
|
||||
`}`
|
||||
);
|
||||
}
|
||||
const editorGutterBackground = theme.getColor(referencesWidget.peekViewEditorGutterBackground);
|
||||
if (editorGutterBackground) {
|
||||
collector.addRule(
|
||||
`.monaco-editor .call-hierarchy .editor .monaco-editor .margin {` +
|
||||
` background-color: ${editorGutterBackground};` +
|
||||
`}`
|
||||
);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,96 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CallHierarchyItem, CallHierarchyDirection, CallHierarchyProvider } 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';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { symbolKindToCssClass, Location } from 'vs/editor/common/modes';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export class Call {
|
||||
constructor(
|
||||
readonly direction: CallHierarchyDirection,
|
||||
readonly item: CallHierarchyItem,
|
||||
readonly locations: Location[]
|
||||
) { }
|
||||
}
|
||||
|
||||
export class SingleDirectionDataSource implements IAsyncDataSource<CallHierarchyItem, Call> {
|
||||
|
||||
constructor(
|
||||
public provider: CallHierarchyProvider,
|
||||
public direction: () => CallHierarchyDirection
|
||||
) { }
|
||||
|
||||
hasChildren(_element: CallHierarchyItem): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async getChildren(element: CallHierarchyItem | Call): Promise<Call[]> {
|
||||
if (element instanceof Call) {
|
||||
element = element.item;
|
||||
}
|
||||
const direction = this.direction();
|
||||
const calls = await this.provider.resolveCallHierarchyItem(element, direction, CancellationToken.None);
|
||||
return calls
|
||||
? calls.map(([item, locations]) => new Call(direction, item, locations))
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
export class IdentityProvider implements IIdentityProvider<Call> {
|
||||
getId(element: Call): { toString(): string; } {
|
||||
return element.item._id;
|
||||
}
|
||||
}
|
||||
|
||||
class CallRenderingTemplate {
|
||||
iconLabel: IconLabel;
|
||||
}
|
||||
|
||||
export class CallRenderer implements ITreeRenderer<Call, FuzzyScore, CallRenderingTemplate> {
|
||||
|
||||
static id = 'CallRenderer';
|
||||
|
||||
templateId: string = CallRenderer.id;
|
||||
|
||||
constructor(@ILabelService private readonly _labelService: ILabelService) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): CallRenderingTemplate {
|
||||
const iconLabel = new IconLabel(container, { supportHighlights: true });
|
||||
return { iconLabel };
|
||||
}
|
||||
renderElement(node: ITreeNode<Call, FuzzyScore>, _index: number, template: CallRenderingTemplate): void {
|
||||
const { element, filterData } = node;
|
||||
const detail = element.item.detail || this._labelService.getUriLabel(element.item.uri, { relative: true });
|
||||
|
||||
template.iconLabel.setLabel(
|
||||
element.item.name,
|
||||
detail,
|
||||
{
|
||||
labelEscapeNewLines: true,
|
||||
matches: createMatches(filterData),
|
||||
extraClasses: [symbolKindToCssClass(element.item.kind, true)]
|
||||
}
|
||||
);
|
||||
}
|
||||
disposeTemplate(template: CallRenderingTemplate): void {
|
||||
template.iconLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualDelegate implements IListVirtualDelegate<Call> {
|
||||
|
||||
getHeight(_element: Call): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(_element: Call): string {
|
||||
return CallRenderer.id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6l-3.989-3.989v2.287l-.497.289c-.464.27-.983.413-1.503.413-1.654 0-3-1.346-3-3v-6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413l.497.289v4.298h-2v2h2v1.989l3.989-3.989h-3l2-2h-2v-4h2l-2-2h6l3 3 .011 1.989-3.011 3.011h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277v2.723h-2v4h2v2.723c-.295.171-.634.277-1 .277-1.104 0-2-.896-2-2v-6c0-1.104.896-2 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>
|
After Width: | Height: | Size: 898 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6L6 12.011v2.287l-.497.289C5.039 14.857 4.52 15 4 15c-1.654 0-3-1.346-3-3V6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413L6 3.702V8H4v2h2v1.989L9.989 8h-3l2-2h-2V2h2l-2-2h6l3 3L16 4.989 12.989 8h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277V7H3v4h2v2.723A1.987 1.987 0 0 1 4 14a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>
|
After Width: | Height: | Size: 831 B |
|
@ -0,0 +1,39 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .call-hierarchy .results,
|
||||
.monaco-workbench .call-hierarchy .message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy[data-state="data"] .results {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy[data-state="message"] .message {
|
||||
display: inherit;
|
||||
text-align: center;
|
||||
padding-top: 3em;
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy-toggle {
|
||||
background-image: url(CallerOrCalleeView_16x.svg);
|
||||
background-size: 14px 14px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .call-hierarchy-toggle,
|
||||
.hc-dark .monaco-workbench .call-hierarchy-toggle {
|
||||
background-image: url(CallerOrCalleeView_16x_dark.svg);
|
||||
}
|
||||
|
||||
.monaco-workbench .call-hierarchy .monaco-split-view2.horizontal > .split-view-container > .split-view-view{
|
||||
/* this is a little bizare.. */
|
||||
height: unset;
|
||||
}
|
||||
.monaco-workbench .call-hierarchy .tree{
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* 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 { 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';
|
||||
|
||||
export const enum CallHierarchyDirection {
|
||||
CallsFrom = 1,
|
||||
CallsTo = 2
|
||||
}
|
||||
|
||||
export interface CallHierarchyItem {
|
||||
_id: number;
|
||||
kind: SymbolKind;
|
||||
name: string;
|
||||
detail?: string;
|
||||
uri: URI;
|
||||
range: IRange;
|
||||
selectionRange: IRange;
|
||||
}
|
||||
|
||||
export interface CallHierarchyProvider {
|
||||
|
||||
provideCallHierarchyItem(
|
||||
document: ITextModel,
|
||||
postion: IPosition,
|
||||
token: CancellationToken
|
||||
): ProviderResult<CallHierarchyItem>;
|
||||
|
||||
resolveCallHierarchyItem(
|
||||
item: CallHierarchyItem,
|
||||
direction: CallHierarchyDirection,
|
||||
token: CancellationToken
|
||||
): ProviderResult<[CallHierarchyItem, Location[]][]>;
|
||||
}
|
||||
|
||||
export const CallHierarchyProviderRegistry = new LanguageFeatureRegistry<CallHierarchyProvider>();
|
||||
|
|
@ -309,6 +309,9 @@ import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStar
|
|||
import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay';
|
||||
import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution';
|
||||
|
||||
// Call Hierarchy
|
||||
import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution';
|
||||
|
||||
// Outline
|
||||
import 'vs/workbench/contrib/outline/browser/outline.contribution';
|
||||
|
||||
|
|
Loading…
Reference in a new issue