Path: blob/main/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeSorter } from '../../../../base/browser/ui/tree/tree.js';6import { CallHierarchyItem, CallHierarchyDirection, CallHierarchyModel, } from '../common/callHierarchy.js';7import { CancellationToken } from '../../../../base/common/cancellation.js';8import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';9import { FuzzyScore, createMatches } from '../../../../base/common/filters.js';10import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js';11import { SymbolKinds, Location, SymbolTag } from '../../../../editor/common/languages.js';12import { compare } from '../../../../base/common/strings.js';13import { Range } from '../../../../editor/common/core/range.js';14import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';15import { localize } from '../../../../nls.js';16import { ThemeIcon } from '../../../../base/common/themables.js';1718export class Call {19constructor(20readonly item: CallHierarchyItem,21readonly locations: Location[] | undefined,22readonly model: CallHierarchyModel,23readonly parent: Call | undefined24) { }2526static compare(a: Call, b: Call): number {27let res = compare(a.item.uri.toString(), b.item.uri.toString());28if (res === 0) {29res = Range.compareRangesUsingStarts(a.item.range, b.item.range);30}31return res;32}33}3435export class DataSource implements IAsyncDataSource<CallHierarchyModel, Call> {3637constructor(38public getDirection: () => CallHierarchyDirection,39) { }4041hasChildren(): boolean {42return true;43}4445async getChildren(element: CallHierarchyModel | Call): Promise<Call[]> {46if (element instanceof CallHierarchyModel) {47return element.roots.map(root => new Call(root, undefined, element, undefined));48}4950const { model, item } = element;5152if (this.getDirection() === CallHierarchyDirection.CallsFrom) {53return (await model.resolveOutgoingCalls(item, CancellationToken.None)).map(call => {54return new Call(55call.to,56call.fromRanges.map(range => ({ range, uri: item.uri })),57model,58element59);60});6162} else {63return (await model.resolveIncomingCalls(item, CancellationToken.None)).map(call => {64return new Call(65call.from,66call.fromRanges.map(range => ({ range, uri: call.from.uri })),67model,68element69);70});71}72}73}7475export class Sorter implements ITreeSorter<Call> {7677compare(element: Call, otherElement: Call): number {78return Call.compare(element, otherElement);79}80}8182export class IdentityProvider implements IIdentityProvider<Call> {8384constructor(85public getDirection: () => CallHierarchyDirection86) { }8788getId(element: Call): { toString(): string } {89let res = this.getDirection() + JSON.stringify(element.item.uri) + JSON.stringify(element.item.range);90if (element.parent) {91res += this.getId(element.parent);92}93return res;94}95}9697class CallRenderingTemplate {98constructor(99readonly icon: HTMLDivElement,100readonly label: IconLabel101) { }102}103104export class CallRenderer implements ITreeRenderer<Call, FuzzyScore, CallRenderingTemplate> {105106static readonly id = 'CallRenderer';107108templateId: string = CallRenderer.id;109110renderTemplate(container: HTMLElement): CallRenderingTemplate {111container.classList.add('callhierarchy-element');112const icon = document.createElement('div');113container.appendChild(icon);114const label = new IconLabel(container, { supportHighlights: true });115return new CallRenderingTemplate(icon, label);116}117118renderElement(node: ITreeNode<Call, FuzzyScore>, _index: number, template: CallRenderingTemplate): void {119const { element, filterData } = node;120const deprecated = element.item.tags?.includes(SymbolTag.Deprecated);121template.icon.className = '';122template.icon.classList.add('inline', ...ThemeIcon.asClassNameArray(SymbolKinds.toIcon(element.item.kind)));123template.label.setLabel(124element.item.name,125element.item.detail,126{ labelEscapeNewLines: true, matches: createMatches(filterData), strikethrough: deprecated }127);128}129disposeTemplate(template: CallRenderingTemplate): void {130template.label.dispose();131}132}133134export class VirtualDelegate implements IListVirtualDelegate<Call> {135136getHeight(_element: Call): number {137return 22;138}139140getTemplateId(_element: Call): string {141return CallRenderer.id;142}143}144145export class AccessibilityProvider implements IListAccessibilityProvider<Call> {146147constructor(148public getDirection: () => CallHierarchyDirection149) { }150151getWidgetAriaLabel(): string {152return localize('tree.aria', "Call Hierarchy");153}154155getAriaLabel(element: Call): string | null {156if (this.getDirection() === CallHierarchyDirection.CallsFrom) {157return localize('from', "calls from {0}", element.item.name);158} else {159return localize('to', "callers of {0}", element.item.name);160}161}162}163164165