simple tree UX

This commit is contained in:
Johannes Rieken
2019-03-14 17:56:45 +01:00
parent 2f89a79efe
commit c9a8a1d08f
4 changed files with 199 additions and 7 deletions

View File

@@ -991,7 +991,7 @@ class CallHierarchyAdapter {
resolveCallHierarchyItem(item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[callHierarchy.CallHierarchyItem, modes.Location[]][]> {
return asPromise(() => this._provider.resolveCallHierarchyItem(
this._cache.get(item._id),
this._cache.get(item._id)!,
direction as number, token) // todo@joh proper convert
).then(data => {
if (!data) {

View File

@@ -8,6 +8,10 @@ import { localize } from 'vs/nls';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CallHierarchyProviderRegistry, CallHierarchyDirection } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { CallHierarchyPeekWidget } from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek';
import { Range } from 'vs/editor/common/core/range';
import { Event } from 'vs/base/common/event';
registerAction({
id: 'editor.showCallHierarchy',
@@ -19,8 +23,11 @@ registerAction({
menuId: MenuId.CommandPalette
},
handler: async function (accessor) {
const editor = accessor.get(ICodeEditorService).getActiveCodeEditor();
const instaService = accessor.get(IInstantiationService);
const editorService = accessor.get(ICodeEditorService);
const editor = editorService.getActiveCodeEditor();
if (!editor || !editor.hasModel()) {
console.log('bad editor');
return;
@@ -32,14 +39,28 @@ registerAction({
return;
}
const data = await provider.provideCallHierarchyItem(editor.getModel(), editor.getPosition(), CancellationToken.None);
if (!data) {
const rootItem = await provider.provideCallHierarchyItem(editor.getModel(), editor.getPosition(), CancellationToken.None);
if (!rootItem) {
console.log('no data');
return;
}
const callsTo = await provider.resolveCallHierarchyItem(data, CallHierarchyDirection.CallsTo, CancellationToken.None);
console.log(data);
console.log(callsTo);
const widget = instaService.createInstance(CallHierarchyPeekWidget, editor, provider, CallHierarchyDirection.CallsTo, rootItem);
const listener = Event.any<any>(editor.onDidChangeModel, editor.onDidChangeModelLanguage)(_ => widget.dispose());
widget.show(Range.fromPositions(editor.getPosition()));
widget.onDidClose(() => {
console.log('DONE');
listener.dispose();
});
widget.tree.onDidOpen(e => {
const [element] = e.elements;
if (element) {
console.log(element);
}
});
}
});

View File

@@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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 callHierarchyTree 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 } from 'vs/editor/common/core/range';
export class CallHierarchyPeekWidget extends PeekViewWidget {
private _tree: WorkbenchAsyncDataTree<CallHierarchyItem, callHierarchyTree.Call, FuzzyScore>;
constructor(
editor: ICodeEditor,
private readonly _provider: CallHierarchyProvider,
private readonly _direction: CallHierarchyDirection,
private readonly _item: CallHierarchyItem,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super(editor, { showFrame: true, showArrow: true, isResizeable: true, isAccessible: true });
this.create();
}
protected _fillBody(container: HTMLElement): void {
const options: IAsyncDataTreeOptions<callHierarchyTree.Call, FuzzyScore> = {
identityProvider: new callHierarchyTree.IdentityProvider(),
ariaLabel: localize('tree.aria', "Call Hierarchy"),
expandOnlyOnTwistieClick: true,
};
this._tree = <any>this._instantiationService.createInstance(
WorkbenchAsyncDataTree,
container,
new callHierarchyTree.VirtualDelegate(),
[new callHierarchyTree.CallRenderer()],
new callHierarchyTree.SingleDirectionDataSource(this._provider, this._direction),
options
);
}
get tree(): WorkbenchAsyncDataTree<CallHierarchyItem, callHierarchyTree.Call, FuzzyScore> {
return this._tree;
}
show(where: IRange) {
this.editor.revealRangeInCenterIfOutsideViewport(where, ScrollType.Smooth);
super.show(where, 12);
this.setTitle(localize('title', "Call Hierarchy for '{0}'", this._item.name));
this._tree.setInput(this._item);
this._tree.domFocus();
this._tree.focusFirst();
}
protected _doLayoutBody(height: number, width: number): void {
super._doLayoutBody(height, width);
this._tree.layout(height, width);
}
}

View File

@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* 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 { IRange } from 'vs/editor/common/core/range';
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 } from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
export class Call {
constructor(
readonly direction: CallHierarchyDirection,
readonly item: CallHierarchyItem,
readonly ranges: IRange[] | undefined
) { }
}
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) {
const calls = await this.provider.resolveCallHierarchyItem(element.item, this.direction, CancellationToken.None);
return calls
? calls.map(([item, locations]) => new Call(this.direction, item, locations.map(l => l.range)))
: [];
} else {
return [new Call(this.direction, element, undefined)];
}
}
}
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;
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;
let detail: string | undefined;
if (!element.ranges) {
// root
detail = element.item.detail;
} else {
detail = element.ranges.length === 1
? localize('label.1', "(1 usage)")
: localize('label.n', "({0} usages)", element.ranges.length);
}
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;
}
}