From 677234a7c23d5082e598820022c423944b80d37e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 23 Oct 2018 16:59:59 +0200 Subject: [PATCH] Proposals for Collapse, CollapseAll, Expand APIs and implementation --- src/vs/vscode.proposed.d.ts | 33 ++++++++++++- .../electron-browser/mainThreadTreeViews.ts | 40 +++++++++++++++- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 + src/vs/workbench/api/node/extHostTreeViews.ts | 47 ++++++++++++++++++- .../browser/parts/views/customView.ts | 27 +++++++++++ src/vs/workbench/common/views.ts | 4 ++ 7 files changed, 151 insertions(+), 4 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8639ce750ce..336c57f4453 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1132,7 +1132,37 @@ declare module 'vscode' { } //#endregion - //#region Tree Item Label Highlights + //#region Tree View + + export namespace window { + export function createTreeView(viewId: string, options: { treeDataProvider: TreeDataProvider }): TreeView2; + } + + export interface TreeView2 extends TreeView { + + /** + * Collapses an element or several elements. + * + * @param elementOrElements element(s) to collpase. + * @param recursive Controls if elements have to be collapsed recursively or not. + */ + collapse(elementOrElements: T | T[], recursive?: boolean): Thenable; + + /** + * Collapse all elements. + */ + collapseAll(): Thenable; + + /** + * Expands an element or several elements. + * + * @param elementOrElements element(s) to expand. + * @param recursive Controls if elements have to be expanded recursively or not. + */ + expand(elementOrElements: T | T[], recursive?: boolean): Thenable; + + } + /** * Label describing the [Tree item](#TreeItem) */ @@ -1162,6 +1192,7 @@ declare module 'vscode' { */ constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); } + //#endregion //#region Task diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index 50a345b39bb..1e4f90be702 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -57,6 +57,40 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie return TPromise.as(null); } + $collapse(treeViewId: string, handles: undefined | string[], recursive?: boolean): Thenable { + const viewer = this.getTreeViewer(treeViewId); + const dataProvider = this._dataProviders.get(treeViewId); + if (viewer && dataProvider) { + const items = handles ? [] : void 0; + if (handles) { + for (const handle of handles) { + const item = dataProvider.getItem(handle); + if (item) { + items.push(item); + } + } + } + viewer.collapse(items, recursive); + } + return Promise.resolve(); + } + + $expand(treeViewId: string, handles: string[], recursive?: boolean): Thenable { + const viewer = this.getTreeViewer(treeViewId); + const dataProvider = this._dataProviders.get(treeViewId); + if (viewer && dataProvider) { + const items = []; + for (const handle of handles) { + const item = dataProvider.getItem(handle); + if (item) { + items.push(item); + } + } + viewer.expand(items, recursive); + } + return Promise.resolve(); + } + private registerListeners(treeViewId: string, treeViewer: ITreeViewer): void { this._register(treeViewer.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); this._register(treeViewer.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); @@ -110,7 +144,7 @@ class TreeViewDataProvider implements ITreeViewDataProvider { const itemsToRefresh: ITreeItem[] = []; if (itemsToRefreshByHandle) { for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) { - const currentTreeItem = this.itemsMap.get(treeItemHandle); + const currentTreeItem = this.getItem(treeItemHandle); if (currentTreeItem) { // Refresh only if the item exists const treeItem = itemsToRefreshByHandle[treeItemHandle]; // Update the current item with refreshed item @@ -132,6 +166,10 @@ class TreeViewDataProvider implements ITreeViewDataProvider { return itemsToRefresh; } + getItem(handle: string): ITreeItem { + return this.itemsMap.get(handle); + } + private postGetChildren(elements: ITreeItem[]): ITreeItem[] { const result: ITreeItem[] = []; if (elements) { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index c5b4176b5bc..6fa4240f476 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -463,7 +463,7 @@ export function createApiFactory( registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider); }, - createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { + createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView2 { return extHostTreeViews.createTreeView(viewId, options); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index bb31f02ce45..e33d4a61e20 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -212,6 +212,8 @@ export interface MainThreadTextEditorsShape extends IDisposable { export interface MainThreadTreeViewsShape extends IDisposable { $registerTreeViewDataProvider(treeViewId: string): void; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Thenable; + $collapse(treeViewId: string, handles: undefined | string[], recursive?: boolean): Thenable; + $expand(treeViewId: string, handles: string[], recursive?: boolean): Thenable; $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable; } diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index 487b76fa9a7..6b36579652d 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -64,7 +64,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return { dispose: () => treeView.dispose() }; } - createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { + createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView2 { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } @@ -76,6 +76,15 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { get onDidChangeSelection() { return treeView.onDidChangeSelection; }, get visible() { return treeView.visible; }, get onDidChangeVisibility() { return treeView.onDidChangeVisibility; }, + collapse(elementOrElements: T | T[], recursive?: boolean): Thenable { + return treeView.collapse(elementOrElements, recursive); + }, + collapseAll(): Thenable { + return treeView.collapse(); + }, + expand(elementOrElements: T | T[], recursive?: boolean): Thenable { + return treeView.expand(elementOrElements, recursive); + }, reveal: (element: T, options?: { select?: boolean, focus?: boolean }): Thenable => { return treeView.reveal(element, options); }, @@ -215,6 +224,42 @@ class ExtHostTreeView extends Disposable { .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error)); } + collapse(elementOrElements?: T | T[], recursive?: boolean): Thenable { + const handles: TreeItemHandle[] = elementOrElements ? [] : void 0; + if (elementOrElements) { + const elements = Array.isArray(elementOrElements) ? elementOrElements : [elementOrElements]; + for (const element of elements) { + const node = this.nodes.get(element); + if (node) { + handles.push(node.item.handle); + } else { + console.error('Not found: ', element); + } + } + if (elements.length === 0) { + return Promise.resolve(); + } + } + return this.proxy.$collapse(this.viewId, handles, recursive); + } + + expand(elementOrElements: T | T[], recursive?: boolean): Thenable { + const handles: TreeItemHandle[] = []; + const elements = Array.isArray(elementOrElements) ? elementOrElements : [elementOrElements]; + for (const element of elements) { + const node = this.nodes.get(element); + if (node) { + handles.push(node.item.handle); + } else { + console.error('Not found: ', element); + } + } + if (elements.length) { + return this.proxy.$expand(this.viewId, handles, recursive); + } + return Promise.resolve(); + } + setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void { const element = this.getExtensionElement(treeItemHandle); if (element) { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 8da31e1218d..ce499e0265b 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -378,6 +378,33 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return Promise.resolve(null); } + async collapse(treeItems: undefined | ITreeItem[], recursive?: boolean): Promise { + if (this.dataProvider && this.tree) { + await this.tree.collapseAll(treeItems, recursive); + } + } + + async expand(treeItems: ITreeItem[], recursive?: boolean): Promise { + if (this.dataProvider && this.tree) { + const itemsToExpand: ITreeItem[] = []; + const seen: Set = new Set(); + for (const treeItem of treeItems) { + if (!seen.has(treeItem.handle)) { + if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { + itemsToExpand.push(treeItem); + } + seen.add(treeItem.handle); + } + } + if (itemsToExpand.length) { + await this.tree.expandAll(itemsToExpand); + if (recursive) { + await this.expand(itemsToExpand.reduce((result, item) => { result.push(...(item.children ? item.children : [])); return result; }, []), recursive); + } + } + } + } + reveal(item: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean, focus?: boolean }): TPromise { if (this.dataProvider && this.tree && this.isVisible) { options = options ? options : { select: false, focus: false }; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index ab8d9857d80..030011f2f41 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -250,6 +250,10 @@ export interface ITreeViewer extends IDisposable { refresh(treeItems?: ITreeItem[]): TPromise; + collapse(treeItems: undefined | ITreeItem[], recursive?: boolean): Promise; + + expand(treeItems: ITreeItem[], recursive?: boolean): Promise; + setVisibility(visible: boolean): void; focus(): void; -- GitLab