deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: [
'../../configs/build.eslintrc.json'
],
parserOptions: {
tsconfigRootDir: __dirname,
project: 'tsconfig.json'
}
};

View File

@@ -0,0 +1,31 @@
<div align='center'>
<br />
<img src='https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/theia.svg?sanitize=true' alt='theia-ext-logo' width='100px' />
<h2>ECLIPSE THEIA - CALL HIERARCHY EXTENSION</h2>
<hr />
</div>
## Description
The `@theia/callhierarchy` extension contributes a `call hierarchy` view which displays the caller hierarchy for a selected callable.
## Additional Information
- [API documentation for `@theia/callhierarchy`](https://eclipse-theia.github.io/theia/docs/next/modules/_theia_callhierarchy.html)
- [Theia - GitHub](https://github.com/eclipse-theia/theia)
- [Theia - Website](https://theia-ide.org/)
## License
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
## Trademark
"Theia" is a trademark of the Eclipse Foundation
<https://www.eclipse.org/theia>

View File

@@ -0,0 +1,50 @@
{
"name": "@theia/callhierarchy",
"version": "1.68.0",
"description": "Theia - Call Hierarchy Extension",
"dependencies": {
"@theia/core": "1.68.0",
"@theia/editor": "1.68.0",
"ts-md5": "^1.2.2",
"tslib": "^2.6.2"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"frontend": "lib/browser/callhierarchy-frontend-module"
}
],
"keywords": [
"theia-extension"
],
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
"repository": {
"type": "git",
"url": "https://github.com/eclipse-theia/theia.git"
},
"bugs": {
"url": "https://github.com/eclipse-theia/theia/issues"
},
"homepage": "https://github.com/eclipse-theia/theia",
"files": [
"lib",
"src"
],
"scripts": {
"build": "theiaext build",
"clean": "theiaext clean",
"compile": "theiaext compile",
"lint": "theiaext lint",
"test": "theiaext test",
"watch": "theiaext watch"
},
"devDependencies": {
"@theia/ext-scripts": "1.68.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
},
"gitHead": "21358137e41342742707f660b8e222f940a27652"
}

View File

@@ -0,0 +1,103 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core/lib/common';
import { AbstractViewContribution, OpenViewArguments, KeybindingRegistry } from '@theia/core/lib/browser';
import { EDITOR_CONTEXT_MENU, CurrentEditorAccess, EditorManager } from '@theia/editor/lib/browser';
import { CallHierarchyTreeWidget } from './callhierarchy-tree/callhierarchy-tree-widget';
import { CALLHIERARCHY_ID, CALL_HIERARCHY_LABEL, CALL_HIERARCHY_TOGGLE_COMMAND_ID } from './callhierarchy';
import { CallHierarchyServiceProvider } from './callhierarchy-service';
import URI from '@theia/core/lib/common/uri';
import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
export { CALL_HIERARCHY_LABEL, CALL_HIERARCHY_TOGGLE_COMMAND_ID };
export namespace CallHierarchyCommands {
export const OPEN = Command.toLocalizedCommand({
id: 'callhierarchy:open',
label: 'Open Call Hierarchy'
}, 'theia/callhierarchy/open');
}
@injectable()
export class CallHierarchyContribution extends AbstractViewContribution<CallHierarchyTreeWidget> {
@inject(CurrentEditorAccess) protected readonly editorAccess: CurrentEditorAccess;
@inject(EditorManager) protected readonly editorManager: EditorManager;
@inject(CallHierarchyServiceProvider) protected readonly callHierarchyServiceProvider: CallHierarchyServiceProvider;
@inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
protected editorHasCallHierarchyProvider!: ContextKey<boolean>;
constructor() {
super({
widgetId: CALLHIERARCHY_ID,
widgetName: CALL_HIERARCHY_LABEL,
defaultWidgetOptions: {
area: 'bottom'
},
toggleCommandId: CALL_HIERARCHY_TOGGLE_COMMAND_ID,
toggleKeybinding: 'ctrlcmd+shift+f1'
});
}
@postConstruct()
protected init(): void {
this.editorHasCallHierarchyProvider = this.contextKeyService.createKey('editorHasCallHierarchyProvider', false);
this.editorManager.onCurrentEditorChanged(() => this.editorHasCallHierarchyProvider.set(this.isCallHierarchyAvailable()));
this.callHierarchyServiceProvider.onDidChange(() => this.editorHasCallHierarchyProvider.set(this.isCallHierarchyAvailable()));
}
protected isCallHierarchyAvailable(): boolean {
const { selection, languageId } = this.editorAccess;
return !!selection && !!languageId && !!this.callHierarchyServiceProvider.get(languageId, new URI(selection.uri));
}
override async openView(args?: Partial<OpenViewArguments>): Promise<CallHierarchyTreeWidget> {
const widget = await super.openView(args);
const { selection, languageId } = this.editorAccess;
widget.initializeModel(selection, languageId);
return widget;
}
override registerCommands(commands: CommandRegistry): void {
commands.registerCommand(CallHierarchyCommands.OPEN, {
execute: () => this.openView({
toggle: false,
activate: true
}),
isEnabled: this.isCallHierarchyAvailable.bind(this)
});
super.registerCommands(commands);
}
override registerMenus(menus: MenuModelRegistry): void {
const menuPath = [...EDITOR_CONTEXT_MENU, 'navigation'];
menus.registerMenuAction(menuPath, {
commandId: CallHierarchyCommands.OPEN.id,
label: CALL_HIERARCHY_LABEL
});
super.registerMenus(menus);
}
override registerKeybindings(keybindings: KeybindingRegistry): void {
super.registerKeybindings(keybindings);
keybindings.registerKeybinding({
command: CallHierarchyCommands.OPEN.id,
keybinding: 'ctrlcmd+f1'
});
}
}

View File

@@ -0,0 +1,37 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { CallHierarchyContribution } from './callhierarchy-contribution';
import { bindContributionProvider } from '@theia/core/lib/common';
import { CallHierarchyService, CallHierarchyServiceProvider } from './callhierarchy-service';
import { WidgetFactory, bindViewContribution } from '@theia/core/lib/browser';
import { CALLHIERARCHY_ID } from './callhierarchy';
import { createHierarchyTreeWidget } from './callhierarchy-tree';
import { ContainerModule } from '@theia/core/shared/inversify';
import '../../src/browser/style/index.css';
export default new ContainerModule(bind => {
bindContributionProvider(bind, CallHierarchyService);
bind(CallHierarchyServiceProvider).to(CallHierarchyServiceProvider).inSingletonScope();
bindViewContribution(bind, CallHierarchyContribution);
bind(WidgetFactory).toDynamicValue(context => ({
id: CALLHIERARCHY_ID,
createWidget: () => createHierarchyTreeWidget(context.container)
}));
});

View File

@@ -0,0 +1,89 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject, named, postConstruct } from '@theia/core/shared/inversify';
import { Position, DocumentUri } from '@theia/core/shared/vscode-languageserver-protocol';
import { CancellationToken } from '@theia/core';
import URI from '@theia/core/lib/common/uri';
import { ContributionProvider, Disposable, Emitter, Event } from '@theia/core/lib/common';
import { CallHierarchyItem, CallHierarchyIncomingCall, CallHierarchyOutgoingCall } from './callhierarchy';
import { LanguageSelector, score } from '@theia/editor/lib/common/language-selector';
export const CallHierarchyService = Symbol('CallHierarchyService');
export interface CallHierarchySession {
items: CallHierarchyItem[];
dispose(): void;
}
export interface CallHierarchyService {
readonly selector: LanguageSelector;
getRootDefinition(uri: DocumentUri, position: Position, cancellationToken: CancellationToken): Promise<CallHierarchySession | undefined>
getCallers(definition: CallHierarchyItem, cancellationToken: CancellationToken): Promise<CallHierarchyIncomingCall[] | undefined>
getCallees?(definition: CallHierarchyItem, cancellationToken: CancellationToken): Promise<CallHierarchyOutgoingCall[] | undefined>
}
@injectable()
export class CallHierarchyServiceProvider {
@inject(ContributionProvider) @named(CallHierarchyService)
protected readonly contributions: ContributionProvider<CallHierarchyService>;
protected readonly onDidChangeEmitter = new Emitter<void>();
get onDidChange(): Event<void> {
return this.onDidChangeEmitter.event;
}
private services: CallHierarchyService[] = [];
@postConstruct()
init(): void {
this.services = this.services.concat(this.contributions.getContributions());
}
get(languageId: string, uri: URI): CallHierarchyService | undefined {
return this.services
.filter(service => this.score(service, languageId, uri) > 0)
.sort((left, right) => this.score(right, languageId, uri) - this.score(left, languageId, uri))[0];
}
protected score(service: CallHierarchyService, languageId: string, uri: URI): number {
return score(service.selector, uri.scheme, uri.path.toString(), languageId, true);
}
add(service: CallHierarchyService): Disposable {
this.services.push(service);
const that = this;
this.onDidChangeEmitter.fire();
return {
dispose: () => {
that.remove(service);
}
};
}
private remove(service: CallHierarchyService): boolean {
const length = this.services.length;
this.services = this.services.filter(value => value !== service);
const serviceWasRemoved = length !== this.services.length;
if (serviceWasRemoved) {
this.onDidChangeEmitter.fire();
}
return serviceWasRemoved;
}
}

View File

@@ -0,0 +1,35 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { interfaces, Container } from '@theia/core/shared/inversify';
import { createTreeContainer } from '@theia/core/lib/browser';
import { CallHierarchyTree } from './callhierarchy-tree';
import { CallHierarchyTreeModel } from './callhierarchy-tree-model';
import { CallHierarchyTreeWidget } from './callhierarchy-tree-widget';
function createHierarchyTreeContainer(parent: interfaces.Container): Container {
const child = createTreeContainer(parent, {
tree: CallHierarchyTree,
model: CallHierarchyTreeModel,
widget: CallHierarchyTreeWidget,
});
return child;
}
export function createHierarchyTreeWidget(parent: interfaces.Container): CallHierarchyTreeWidget {
return createHierarchyTreeContainer(parent).get<CallHierarchyTreeWidget>(CallHierarchyTreeWidget);
}

View File

@@ -0,0 +1,71 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject } from '@theia/core/shared/inversify';
import { CompositeTreeNode, TreeModelImpl, TreeNode } from '@theia/core/lib/browser';
import { CallHierarchyTree, ItemNode } from './callhierarchy-tree';
import { CallHierarchyServiceProvider, CallHierarchySession } from '../callhierarchy-service';
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
import URI from '@theia/core/lib/common/uri';
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
@injectable()
export class CallHierarchyTreeModel extends TreeModelImpl {
protected _languageId: string | undefined;
protected currentSession?: CallHierarchySession;
@inject(CallHierarchyTree) protected override readonly tree: CallHierarchyTree;
@inject(CallHierarchyServiceProvider) protected readonly callHierarchyServiceProvider: CallHierarchyServiceProvider;
getTree(): CallHierarchyTree {
return this.tree;
}
get languageId(): string | undefined {
return this._languageId;
}
async initializeCallHierarchy(languageId: string | undefined, uri: string | undefined, position: Position | undefined): Promise<void> {
this.tree.root = undefined;
this.tree.callHierarchyService = undefined;
this._languageId = languageId;
if (languageId && uri && position) {
const callHierarchyService = this.callHierarchyServiceProvider.get(languageId, new URI(uri));
if (callHierarchyService) {
this.tree.callHierarchyService = callHierarchyService;
const cancellationSource = new CancellationTokenSource();
const rootDefinition = await callHierarchyService.getRootDefinition(uri, position, cancellationSource.token);
if (rootDefinition) {
this.currentSession?.dispose();
this.currentSession = rootDefinition;
const root: CompositeTreeNode = {
id: 'call-hierarchy-tree-root',
parent: undefined,
children: [],
visible: false,
};
rootDefinition.items.forEach(definition => CompositeTreeNode.addChild(root, ItemNode.create(definition, root)));
this.tree.root = root;
}
}
}
}
protected override doOpenNode(node: TreeNode): void {
// do nothing (in particular do not expand the node)
}
}

View File

@@ -0,0 +1,223 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject } from '@theia/core/shared/inversify';
import {
ContextMenuRenderer, TreeWidget, NodeProps, TreeProps, TreeNode,
TreeModel, DockPanel, codicon
} from '@theia/core/lib/browser';
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
import { ItemNode, CallerNode } from './callhierarchy-tree';
import { CallHierarchyTreeModel } from './callhierarchy-tree-model';
import { CALLHIERARCHY_ID, CallHierarchyItem, CallHierarchyIncomingCall, CALL_HIERARCHY_LABEL } from '../callhierarchy';
import URI from '@theia/core/lib/common/uri';
import { Location, Range, SymbolKind, DocumentUri, SymbolTag } from '@theia/core/shared/vscode-languageserver-protocol';
import { EditorManager } from '@theia/editor/lib/browser';
import { nls } from '@theia/core/lib/common/nls';
import * as React from '@theia/core/shared/react';
export const HIERARCHY_TREE_CLASS = 'theia-CallHierarchyTree';
export const DEFINITION_NODE_CLASS = 'theia-CallHierarchyTreeNode';
export const DEFINITION_ICON_CLASS = 'theia-CallHierarchyTreeNodeIcon';
@injectable()
export class CallHierarchyTreeWidget extends TreeWidget {
constructor(
@inject(TreeProps) override readonly props: TreeProps,
@inject(CallHierarchyTreeModel) override readonly model: CallHierarchyTreeModel,
@inject(ContextMenuRenderer) contextMenuRenderer: ContextMenuRenderer,
@inject(LabelProvider) protected override readonly labelProvider: LabelProvider,
@inject(EditorManager) readonly editorManager: EditorManager
) {
super(props, model, contextMenuRenderer);
this.id = CALLHIERARCHY_ID;
this.title.label = CALL_HIERARCHY_LABEL;
this.title.caption = CALL_HIERARCHY_LABEL;
this.title.iconClass = codicon('references');
this.title.closable = true;
this.addClass(HIERARCHY_TREE_CLASS);
this.toDispose.push(this.model.onSelectionChanged(selection => {
const node = selection[0];
if (node) {
this.openEditor(node, true);
}
}));
this.toDispose.push(this.model.onOpenNode((node: TreeNode) => {
this.openEditor(node, false);
}));
this.toDispose.push(
this.labelProvider.onDidChange(() => this.update())
);
}
initializeModel(selection: Location | undefined, languageId: string | undefined): void {
this.model.initializeCallHierarchy(languageId, selection ? selection.uri : undefined, selection ? selection.range.start : undefined);
}
protected override createNodeClassNames(node: TreeNode, props: NodeProps): string[] {
const classNames = super.createNodeClassNames(node, props);
if (ItemNode.is(node)) {
classNames.push(DEFINITION_NODE_CLASS);
}
return classNames;
}
protected override createNodeAttributes(node: TreeNode, props: NodeProps): React.Attributes & React.HTMLAttributes<HTMLElement> {
const elementAttrs = super.createNodeAttributes(node, props);
return {
...elementAttrs,
};
}
protected override renderTree(model: TreeModel): React.ReactNode {
return super.renderTree(model)
|| <div className='theia-widget-noInfo'>{nls.localize('theia/callhierarchy/noCallers', 'No callers have been detected.')}</div>;
}
protected override renderCaption(node: TreeNode, props: NodeProps): React.ReactNode {
if (ItemNode.is(node)) {
return this.decorateDefinitionCaption(node.definition);
}
if (CallerNode.is(node)) {
return this.decorateCallerCaption(node.caller);
}
return 'caption';
}
protected decorateDefinitionCaption(definition: CallHierarchyItem): React.ReactNode {
const symbol = definition.name;
const location = this.labelProvider.getName(URI.fromComponents(definition.uri));
const container = location;
const isDeprecated = definition.tags?.includes(SymbolTag.Deprecated);
const classNames = ['definitionNode'];
if (isDeprecated) {
classNames.push('deprecatedDefinition');
}
return <div className={classNames.join(' ')}>
<div className={'symbol-icon-center codicon codicon-symbol-' + this.toIconClass(definition.kind)}></div>
<div className='definitionNode-content'>
<span className='symbol'>
{symbol}
</span>
<span className='container'>
{container}
</span>
</div>
</div>;
}
protected decorateCallerCaption(caller: CallHierarchyIncomingCall): React.ReactNode {
const definition = caller.from;
const symbol = definition.name;
const referenceCount = caller.fromRanges.length;
const location = this.labelProvider.getName(URI.fromComponents(definition.uri));
const container = location;
const isDeprecated = definition.tags?.includes(SymbolTag.Deprecated);
const classNames = ['definitionNode'];
if (isDeprecated) {
classNames.push('deprecatedDefinition');
}
return <div className={classNames.join(' ')}>
<div className={'symbol-icon-center codicon codicon-symbol-' + this.toIconClass(definition.kind)}></div>
<div className='definitionNode-content'>
<span className='symbol'>
{symbol}
</span>
<span className='referenceCount'>
{(referenceCount > 1) ? `[${referenceCount}]` : ''}
</span>
<span className='container'>
{container}
</span>
</div>
</div>;
}
// tslint:disable-next-line:typedef
protected toIconClass(symbolKind: number) {
switch (symbolKind) {
case SymbolKind.File: return 'file';
case SymbolKind.Module: return 'module';
case SymbolKind.Namespace: return 'namespace';
case SymbolKind.Package: return 'package';
case SymbolKind.Class: return 'class';
case SymbolKind.Method: return 'method';
case SymbolKind.Property: return 'property';
case SymbolKind.Field: return 'field';
case SymbolKind.Constructor: return 'constructor';
case SymbolKind.Enum: return 'enum';
case SymbolKind.Interface: return 'interface';
case SymbolKind.Function: return 'function';
case SymbolKind.Variable: return 'variable';
case SymbolKind.Constant: return 'constant';
case SymbolKind.String: return 'string';
case SymbolKind.Number: return 'number';
case SymbolKind.Boolean: return 'boolean';
case SymbolKind.Array: return 'array';
default: return 'unknown';
}
}
private openEditor(node: TreeNode, keepFocus: boolean): void {
if (ItemNode.is(node)) {
const def = node.definition;
this.doOpenEditor(URI.fromComponents(def.uri).toString(), def.selectionRange ? def.selectionRange : def.range, keepFocus);
}
if (CallerNode.is(node)) {
this.doOpenEditor(URI.fromComponents(node.caller.from.uri).toString(), node.caller.fromRanges[0], keepFocus);
}
}
private doOpenEditor(uri: DocumentUri, range: Range, keepFocus: boolean): void {
this.editorManager.open(
new URI(uri), {
mode: keepFocus ? 'reveal' : 'activate',
selection: range
}
).then(editorWidget => {
if (editorWidget.parent instanceof DockPanel) {
editorWidget.parent.selectWidget(editorWidget);
}
});
}
override storeState(): object {
const callHierarchyService = this.model.getTree().callHierarchyService;
if (this.model.root && callHierarchyService) {
return {
root: this.deflateForStorage(this.model.root),
languageId: this.model.languageId,
};
} else {
return {};
}
}
override restoreState(oldState: object): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((oldState as any).root && (oldState as any).languageId) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const root = this.inflateFromStorage((oldState as any).root) as ItemNode;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.model.initializeCallHierarchy((oldState as any).languageId, URI.fromComponents(root.definition.uri).toString(), root.definition.range.start);
}
}
}

View File

@@ -0,0 +1,119 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable } from '@theia/core/shared/inversify';
import { TreeNode, CompositeTreeNode, SelectableTreeNode, ExpandableTreeNode, TreeImpl } from '@theia/core/lib/browser';
import { CallHierarchyItem, CallHierarchyIncomingCall } from '../callhierarchy';
import { CallHierarchyService } from '../callhierarchy-service';
import { Md5 } from 'ts-md5';
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
@injectable()
export class CallHierarchyTree extends TreeImpl {
protected _callHierarchyService: CallHierarchyService | undefined;
set callHierarchyService(callHierarchyService: CallHierarchyService | undefined) {
this._callHierarchyService = callHierarchyService;
}
get callHierarchyService(): CallHierarchyService | undefined {
return this._callHierarchyService;
}
override async resolveChildren(parent: CompositeTreeNode): Promise<TreeNode[]> {
if (!this.callHierarchyService) {
return Promise.resolve([]);
}
if (parent.children.length > 0) {
return Promise.resolve([...parent.children]);
}
let definition: CallHierarchyItem | undefined;
if (ItemNode.is(parent)) {
definition = parent.definition;
} else if (CallerNode.is(parent)) {
definition = parent.caller.from;
}
if (definition) {
const cancellationSource = new CancellationTokenSource();
const callers = await this.callHierarchyService.getCallers(definition, cancellationSource.token);
if (!callers) {
return Promise.resolve([]);
}
return this.toNodes(callers, parent);
}
return Promise.resolve([]);
}
protected toNodes(callers: CallHierarchyIncomingCall[], parent: CompositeTreeNode): TreeNode[] {
return callers.map(caller => this.toNode(caller, parent));
}
protected toNode(caller: CallHierarchyIncomingCall, parent: CompositeTreeNode | undefined): TreeNode {
return CallerNode.create(caller, parent as TreeNode);
}
}
export interface ItemNode extends SelectableTreeNode, ExpandableTreeNode {
definition: CallHierarchyItem;
}
export namespace ItemNode {
export function is(node: TreeNode | undefined): node is ItemNode {
return !!node && 'definition' in node;
}
export function create(definition: CallHierarchyItem, parent: TreeNode | undefined): ItemNode {
const name = definition.name;
const id = createId(definition, parent);
return <ItemNode>{
id, definition, name, parent,
visible: true,
children: [],
expanded: false,
selected: false,
};
}
}
export interface CallerNode extends SelectableTreeNode, ExpandableTreeNode {
caller: CallHierarchyIncomingCall;
}
export namespace CallerNode {
export function is(node: TreeNode | undefined): node is CallerNode {
return !!node && 'caller' in node;
}
export function create(caller: CallHierarchyIncomingCall, parent: TreeNode | undefined): CallerNode {
const callerDefinition = caller.from;
const name = callerDefinition.name;
const id = createId(callerDefinition, parent);
return <CallerNode>{
id, caller, name, parent,
visible: true,
children: [],
expanded: false,
selected: false,
};
}
}
function createId(definition: CallHierarchyItem, parent: TreeNode | undefined): string {
const idPrefix = (parent) ? parent.id + '/' : '';
const id = idPrefix + Md5.hashStr(JSON.stringify(definition));
return id;
}

View File

@@ -0,0 +1,20 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './callhierarchy-tree';
export * from './callhierarchy-tree-model';
export * from './callhierarchy-tree-widget';
export * from './callhierarchy-tree-container';

View File

@@ -0,0 +1,47 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { nls } from '@theia/core';
import { UriComponents } from '@theia/core/lib/common/uri';
import { Range, SymbolKind, SymbolTag } from '@theia/core/shared/vscode-languageserver-protocol';
export const CALLHIERARCHY_ID = 'callhierarchy';
export const CALL_HIERARCHY_TOGGLE_COMMAND_ID = 'callhierarchy:toggle';
export const CALL_HIERARCHY_LABEL = nls.localizeByDefault('Call Hierarchy');
export interface CallHierarchyItem {
_sessionId?: string;
_itemId?: string;
kind: SymbolKind;
name: string;
detail?: string;
uri: UriComponents;
range: Range;
selectionRange: Range;
tags?: readonly SymbolTag[];
data?: unknown;
}
export interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromRanges: Range[];
}
export interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromRanges: Range[];
}

View File

@@ -0,0 +1,20 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './callhierarchy';
export * from './callhierarchy-contribution';
export * from './callhierarchy-frontend-module';
export * from './callhierarchy-service';

View File

@@ -0,0 +1,65 @@
/********************************************************************************
* Copyright (C) 2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
********************************************************************************/
.theia-CallHierarchyTree {
font-size: var(--theia-ui-font-size1);
}
.theia-CallHierarchyTree .theia-TreeNode {
width: 100%;
}
.theia-CallHierarchyTree .theia-ExpansionToggle {
min-width: 9px;
padding-right: 4px;
}
.theia-CallHierarchyTree .definitionNode {
display: flex;
}
.theia-CallHierarchyTree .definitionNode {
width: calc(100% - 32px);
}
.theia-CallHierarchyTree .definitionNode div {
margin-right: 5px;
}
.theia-CallHierarchyTree .definitionNode .symbol {
padding-right: 4px;
}
.theia-CallHierarchyTree .definitionNode .referenceCount {
color: var(--theia-badge-background);
padding-right: 4px;
}
.theia-CallHierarchyTree .definitionNode .container {
color: var(--theia-descriptionForeground);
}
.theia-CallHierarchyTree .definitionNode-content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.theia-CallHierarchyTree
.definitionNode.deprecatedDefinition
.definitionNode-content {
text-decoration: line-through;
}

View File

@@ -0,0 +1,102 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { Location, Range, Position } from '@theia/core/shared/vscode-languageserver-protocol';
/**
* Test if `otherRange` is in `range`. If the ranges are equal, will return true.
*/
export function containsRange(range: Range, otherRange: Range): boolean {
if (otherRange.start.line < range.start.line || otherRange.end.line < range.start.line) {
return false;
}
if (otherRange.start.line > range.end.line || otherRange.end.line > range.end.line) {
return false;
}
if (otherRange.start.line === range.start.line && otherRange.start.character < range.start.character) {
return false;
}
if (otherRange.end.line === range.end.line && otherRange.end.character > range.end.character) {
return false;
}
return true;
}
export function containsPosition(range: Range, position: Position): boolean {
return comparePosition(range.start, position) >= 0 && comparePosition(range.end, position) <= 0;
}
function sameStart(a: Range, b: Range): boolean {
const pos1 = a.start;
const pos2 = b.start;
return pos1.line === pos2.line
&& pos1.character === pos2.character;
}
export function filterSame(locations: Location[], definition: Location): Location[] {
return locations.filter(candidate => candidate.uri !== definition.uri
|| !sameStart(candidate.range, definition.range)
);
}
export function comparePosition(left: Position, right: Position): number {
const diff = right.line - left.line;
if (diff !== 0) {
return diff;
}
return right.character - left.character;
}
export function filterUnique(locations: Location[] | null): Location[] {
if (!locations) {
return [];
}
const result: Location[] = [];
const set = new Set<string>();
for (const location of locations) {
const json = JSON.stringify(location);
if (!set.has(json)) {
set.add(json);
result.push(location);
}
}
return result;
}
export function startsAfter(a: Range, b: Range): boolean {
if (a.start.line > b.start.line) {
return true;
}
if (a.start.line === b.start.line) {
if (a.start.character > b.start.character) {
return true;
}
if (a.start.character === b.start.character) {
if (a.end.line > b.end.line) {
return true;
}
}
}
return false;
}
export function isSame(a: Location, b: Location): boolean {
return a.uri === b.uri
&& a.range.start.line === b.range.start.line
&& a.range.end.line === b.range.end.line
&& a.range.start.character === b.range.start.character
&& a.range.end.character === b.range.end.character;
}

View File

@@ -0,0 +1,28 @@
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* note: this bogus test file is required so that
we are able to run mocha unit tests on this
package, without having any actual unit tests in it.
This way a coverage report will be generated,
showing 0% coverage, instead of no report.
This file can be removed once we have real unit
tests in place. */
describe('callhierarchy package', () => {
it('support code coverage statistics', () => true);
});

View File

@@ -0,0 +1,19 @@
{
"extends": "../../configs/base.tsconfig.json",
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
],
"references": [
{
"path": "../core"
},
{
"path": "../editor"
}
]
}