Refresh only top level elements

This commit is contained in:
Sandeep Somavarapu
2017-12-01 17:12:19 +01:00
parent a903c11790
commit 59b091af5b
4 changed files with 149 additions and 78 deletions

View File

@@ -495,6 +495,7 @@ export interface ExtHostDocumentsAndEditorsShape {
export interface ExtHostTreeViewsShape {
$getElements(treeViewId: string): TPromise<ITreeItem[]>;
$getChildren(treeViewId: string, treeItemHandle: string): TPromise<ITreeItem[]>;
// $resolveHandles(treeViewId: string, treeItemHandles: string[]): TPromise<ITreeItem[]>;
}
export interface ExtHostWorkspaceShape {

View File

@@ -7,13 +7,11 @@
import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { distinct } from 'vs/base/common/arrays';
import { debounceEvent } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views';
import { TreeItemCollapsibleState } from './extHostTypes';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
@@ -64,17 +62,30 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.getChildren(treeItemHandle);
}
/* $resolveHandles(treeViewId: string, treeItemHandles: string[]): TPromise<ITreeItem[]> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
}
return treeView.resolveHandles(treeItemHandles);
} */
private convertArgument(arg: TreeViewItemHandleArg): any {
const treeView = this.treeViews.get(arg.$treeViewId);
return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
}
}
interface TreeNode {
handle: TreeItemHandle;
parent: TreeItemHandle;
children: TreeItemHandle[];
}
class ExtHostTreeView<T> extends Disposable {
private extElementsMap: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
private itemHandlesMap: Map<T, TreeItemHandle> = new Map<T, TreeItemHandle>();
private extChildrenElementsMap: Map<T, T[]> = new Map<T, T[]>();
private nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
super();
@@ -85,12 +96,9 @@ class ExtHostTreeView<T> extends Disposable {
}
getTreeItems(): TPromise<ITreeItem[]> {
this.extChildrenElementsMap.clear();
this.extElementsMap.clear();
this.itemHandlesMap.clear();
this.clearAll();
return asWinJsPromise(() => this.dataProvider.getChildren())
.then(elements => this.processAndMapElements(elements, '0'));
.then(elements => this.resolveElements(elements, '0'));
}
getChildren(treeItemHandle: TreeItemHandle): TPromise<ITreeItem[]> {
@@ -102,9 +110,24 @@ class ExtHostTreeView<T> extends Disposable {
}
return asWinJsPromise(() => this.dataProvider.getChildren(extElement))
.then(childrenElements => this.processAndMapElements(childrenElements, treeItemHandle));
.then(childrenElements => this.resolveElements(childrenElements, treeItemHandle))
.then(childrenItems => {
this.nodes.get(extElement).children = childrenItems.map(c => c.handle);
return childrenItems;
});
}
/* resolveHandles(treeItemHandles: TreeItemHandle[]): TPromise<ITreeItem[]> {
return TPromise.join(treeItemHandles.map(treeItemHandle => {
let extElement = this.getExtensionElement(treeItemHandle);
if (!extElement) {
return TPromise.wrapError<ITreeItem>(new Error(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', treeItemHandle)));
}
const node = this.nodes.get(extElement);
return this.resolveElement(extElement, node.handle);
})).then(treeItems => coalesce(treeItems));
} */
getExtensionElement(treeItemHandle: TreeItemHandle): T {
return this.extElementsMap.get(treeItemHandle);
}
@@ -114,23 +137,30 @@ class ExtHostTreeView<T> extends Disposable {
if (hasRoot) {
this.proxy.$refresh(this.viewId, []);
} else {
const itemHandles = distinct(elements.map(element => this.itemHandlesMap.get(element))
.filter(itemHandle => !!itemHandle));
if (itemHandles.length) {
this.proxy.$refresh(this.viewId, itemHandles);
const handlesToUpdate = this.getElementsToUpdate(elements);
if (handlesToUpdate.length) {
this.proxy.$refresh(this.viewId, handlesToUpdate);
}
}
}
private processAndMapElements(elements: T[], parentHandle: TreeItemHandle): TPromise<ITreeItem[]> {
private resolveElements(elements: T[], parentHandle: TreeItemHandle): TPromise<ITreeItem[]> {
if (elements && elements.length) {
return TPromise.join(
elements.filter(element => !!element)
.map((element, index) => {
if (this.extChildrenElementsMap.has(element)) {
return TPromise.wrapError<ITreeItem>(new Error(localize('treeView.duplicateElement', 'Element {0} is already registered', element)));
}
return this.resolveElement(element, index, parentHandle);
return this.resolveElement(element, index, parentHandle)
.then(treeItem => {
if (treeItem) {
this.nodes.set(element, {
handle: treeItem.handle,
parent: parentHandle,
children: void 0
});
this.extElementsMap.set(treeItem.handle, element);
}
return treeItem;
});
}))
.then(treeItems => treeItems.filter(treeItem => !!treeItem));
}
@@ -139,22 +169,7 @@ class ExtHostTreeView<T> extends Disposable {
private resolveElement(element: T, index: number, parentHandle: TreeItemHandle): TPromise<ITreeItem> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => {
const treeItem = this.massageTreeItem(extTreeItem, index, parentHandle);
if (treeItem) {
this.itemHandlesMap.set(element, treeItem.handle);
this.extElementsMap.set(treeItem.handle, element);
if (treeItem.collapsibleState === TreeItemCollapsibleState.Expanded) {
return this.getChildren(treeItem.handle).then(children => {
treeItem.children = children;
return treeItem;
});
} else {
return treeItem;
}
}
return null;
});
.then(extTreeItem => this.massageTreeItem(extTreeItem, index, parentHandle));
}
private massageTreeItem(extensionTreeItem: vscode.TreeItem, index: number, parentHandle: TreeItemHandle): ITreeItem {
@@ -169,7 +184,7 @@ class ExtHostTreeView<T> extends Disposable {
contextValue: extensionTreeItem.contextValue,
icon,
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
collapsibleState: extensionTreeItem.collapsibleState,
collapsibleState: extensionTreeItem.collapsibleState
};
}
@@ -197,29 +212,69 @@ class ExtHostTreeView<T> extends Disposable {
return URI.file(iconPath).toString();
}
private clearChildren(extElement: T): void {
const children = this.extChildrenElementsMap.get(extElement);
if (children) {
for (const child of children) {
this.clearElement(child);
private getElementsToUpdate(elements: T[]): TreeItemHandle[] {
const elementsToUpdate = new Set<TreeItemHandle>();
for (const element of elements) {
let elementNode = this.nodes.get(element);
if (elementNode && !elementsToUpdate.has(elementNode.handle)) {
// check if an ancestor of extElement is already in the elements to update list
let currentNode = elementNode;
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent)) {
const parentElement = this.extElementsMap.get(currentNode.parent);
currentNode = this.nodes.get(parentElement);
}
if (!currentNode) {
elementsToUpdate.add(elementNode.handle);
}
}
this.extChildrenElementsMap.delete(extElement);
}
const handlesToUpdate: TreeItemHandle[] = [];
// Take only top level elements
elementsToUpdate.forEach((handle) => {
const element = this.extElementsMap.get(handle);
let node = this.nodes.get(element);
if (node && !elementsToUpdate.has(node.parent)) {
handlesToUpdate.push(handle);
}
});
return handlesToUpdate;
}
private clearElement(extElement: T): void {
this.clearChildren(extElement);
const treeItemhandle = this.itemHandlesMap.get(extElement);
this.itemHandlesMap.delete(extElement);
if (treeItemhandle) {
this.extElementsMap.delete(treeItemhandle);
private clearChildren(element: T): void {
let node = this.nodes.get(element);
if (node.children) {
for (const childHandle of node.children) {
const childEleement = this.extElementsMap.get(childHandle);
if (childEleement) {
this.clear(childEleement);
}
}
}
node.children = void 0;
}
private clear(element: T): void {
let node = this.nodes.get(element);
if (node.children) {
for (const childHandle of node.children) {
const childEleement = this.extElementsMap.get(childHandle);
if (childEleement) {
this.clear(childEleement);
}
}
}
this.nodes.delete(element);
this.extElementsMap.delete(node.handle);
}
private clearAll(): void {
this.extElementsMap.clear();
this.nodes.clear();
}
dispose() {
this.extElementsMap.clear();
this.itemHandlesMap.clear();
this.extChildrenElementsMap.clear();
this.clearAll();
}
}