From 5dbfe32be316221a81f96874e61f1eb2b93f53bb Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 29 Dec 2020 16:49:46 +0100 Subject: [PATCH] Allow tree item command to be resolved later Part of #110498 --- src/vs/vscode.d.ts | 5 +++-- .../workbench/api/common/extHostTreeViews.ts | 16 ++++++++++++---- .../workbench/browser/parts/views/treeView.ts | 19 ++++++++++++++++--- src/vs/workbench/common/views.ts | 5 +++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 0def9b1af79..125efd8243e 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -8867,7 +8867,8 @@ declare module 'vscode' { getParent?(element: T): ProviderResult; /** - * Called only on hover to resolve the [TreeItem](#TreeItem.tooltip) property if it is undefined. + * Called on hover to resolve the [TreeItem](#TreeItem.tooltip) property if it is undefined. + * Called on tree item click/open to resolve the [TreeItem](#TreeItem.command) property if it is undefined. * Only properties that were undefined can be resolved in `resolveTreeItem`. * Functionality may be expanded later to include being called to resolve other missing * properties on selection and/or on open. @@ -8877,7 +8878,7 @@ declare module 'vscode' { * onDidChangeTreeData should not be triggered from within resolveTreeItem. * * *Note* that this function is called when tree items are already showing in the UI. - * Because of that, no property that changes the presentation (label, description, command, etc.) + * Because of that, no property that changes the presentation (label, description, etc.) * can be changed. * * @param element The object associated with the TreeItem diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index b60668fd080..ca1914d2e38 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -21,6 +21,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Command } from 'vs/editor/common/modes'; type TreeItemHandle = string; @@ -184,6 +185,7 @@ interface TreeNode extends IDisposable { extensionItem: vscode.TreeItem; parent: TreeNode | Root; children?: TreeNode[]; + disposableStore: DisposableStore; } class ExtHostTreeView extends Disposable { @@ -379,8 +381,9 @@ class ExtHostTreeView extends Disposable { const node = this.nodes.get(element); if (node) { const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element, token) ?? node.extensionItem; - // Resolvable elements. Currently only tooltip. + // Resolvable elements. Currently only tooltip and command. node.item.tooltip = this.getTooltip(resolve.tooltip); + node.item.command = this.getCommand(node.disposableStore, resolve.command); return node.item; } } @@ -573,8 +576,12 @@ class ExtHostTreeView extends Disposable { return tooltip; } + private getCommand(disposable: DisposableStore, command?: vscode.Command): Command | undefined { + return command ? this.commands.toInternal(command, disposable) : undefined; + } + private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode { - const disposable = new DisposableStore(); + const disposableStore = new DisposableStore(); const handle = this.createHandle(element, extensionTreeItem, parent); const icon = this.getLightIconPath(extensionTreeItem); const item: ITreeItem = { @@ -584,7 +591,7 @@ class ExtHostTreeView extends Disposable { description: extensionTreeItem.description, resourceUri: extensionTreeItem.resourceUri, tooltip: this.getTooltip(extensionTreeItem.tooltip), - command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined, + command: this.getCommand(disposableStore, extensionTreeItem.command), contextValue: extensionTreeItem.contextValue, icon, iconDark: this.getDarkIconPath(extensionTreeItem) || icon, @@ -598,7 +605,8 @@ class ExtHostTreeView extends Disposable { extensionItem: extensionTreeItem, parent, children: undefined, - dispose(): void { disposable.dispose(); } + disposableStore, + dispose(): void { disposableStore.dispose(); } }; } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9604ac0a9e5..cb6f825806c 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -52,7 +52,8 @@ import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Codicon } from 'vs/base/common/codicons'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Command } from 'vs/editor/common/modes'; export class TreeViewPane extends ViewPane { @@ -535,12 +536,13 @@ export class TreeView extends Disposable implements ITreeView { })); this.tree.setInput(this.root).then(() => this.updateContentAreas()); - this._register(this.tree.onDidOpen(e => { + this._register(this.tree.onDidOpen(async (e) => { if (!e.browserEvent) { return; } const selection = this.tree!.getSelection(); - const command = selection.length === 1 ? selection[0].command : undefined; + const command = await this.resolveCommand(selection.length === 1 ? selection[0] : undefined); + if (command) { let args = command.arguments || []; if (command.id === API_OPEN_EDITOR_COMMAND_ID || command.id === API_OPEN_DIFF_EDITOR_COMMAND_ID) { @@ -555,6 +557,17 @@ export class TreeView extends Disposable implements ITreeView { } + private async resolveCommand(element: ITreeItem | undefined): Promise { + let command = element?.command; + if (element && !command) { + if ((element instanceof ResolvableTreeItem) && element.hasResolve) { + await element.resolve(new CancellationTokenSource().token); + command = element.command; + } + } + return command; + } + private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { this.hoverService.hideHover(); const node: ITreeItem | null = treeEvent.element; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index d3e641dc4dc..70f2eb64d04 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -704,8 +704,9 @@ export class ResolvableTreeItem implements ITreeItem { if (resolve && !this.resolved) { const resolvedItem = await resolve(token); if (resolvedItem) { - // Resolvable elements. Currently only tooltip. - this.tooltip = resolvedItem.tooltip; + // Resolvable elements. Currently tooltip and command. + this.tooltip = this.tooltip ?? resolvedItem.tooltip; + this.command = this.command ?? resolvedItem.command; } } if (!token.isCancellationRequested) { -- GitLab