mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 19:18:59 +01:00
Fix #40018
This commit is contained in:
@@ -14,6 +14,7 @@ import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.proto
|
||||
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views';
|
||||
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
|
||||
type TreeItemHandle = string;
|
||||
|
||||
@@ -51,7 +52,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
if (!treeView) {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
|
||||
}
|
||||
return treeView.getTreeItems();
|
||||
return treeView.getChildren();
|
||||
}
|
||||
|
||||
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeItem[]> {
|
||||
@@ -69,10 +70,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
}
|
||||
|
||||
interface TreeNode {
|
||||
index: number;
|
||||
handle: TreeItemHandle;
|
||||
parent: TreeItemHandle;
|
||||
children: TreeItemHandle[];
|
||||
parentHandle: TreeItemHandle;
|
||||
childrenHandles: TreeItemHandle[];
|
||||
}
|
||||
|
||||
class ExtHostTreeView<T> extends Disposable {
|
||||
@@ -89,25 +89,37 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
getTreeItems(): TPromise<ITreeItem[]> {
|
||||
this.clearAll();
|
||||
return asWinJsPromise(() => this.dataProvider.getChildren())
|
||||
.then(elements => this.resolveElements(elements));
|
||||
}
|
||||
|
||||
getChildren(treeItemHandle: TreeItemHandle): TPromise<ITreeItem[]> {
|
||||
let extElement = this.getExtensionElement(treeItemHandle);
|
||||
if (extElement) {
|
||||
this.clearChildren(extElement);
|
||||
} else {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', treeItemHandle)));
|
||||
getChildren(parentHandle?: TreeItemHandle): TPromise<ITreeItem[]> {
|
||||
let parentElement;
|
||||
if (parentHandle) {
|
||||
parentElement = this.getExtensionElement(parentHandle);
|
||||
if (!parentElement) {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', parentHandle)));
|
||||
}
|
||||
}
|
||||
|
||||
return asWinJsPromise(() => this.dataProvider.getChildren(extElement))
|
||||
.then(childrenElements => this.resolveElements(childrenElements, treeItemHandle))
|
||||
.then(childrenItems => {
|
||||
this.nodes.get(extElement).children = childrenItems.map(c => c.handle);
|
||||
return childrenItems;
|
||||
return asWinJsPromise(() => this.dataProvider.getChildren(parentElement))
|
||||
.then(elements => {
|
||||
elements = coalesce(elements || []);
|
||||
return TPromise.join(elements.map((element, index) => {
|
||||
const node = this.nodes.get(element);
|
||||
const currentHandle = node && node.parentHandle === parentHandle ? node.handle : void 0;
|
||||
return this.resolveElement(element, currentHandle ? currentHandle : index, parentHandle)
|
||||
.then(treeItem => {
|
||||
if (treeItem) {
|
||||
if (!currentHandle) {
|
||||
// update the caches if current handle is not used
|
||||
this.nodes.set(element, {
|
||||
handle: treeItem.handle,
|
||||
parentHandle,
|
||||
childrenHandles: void 0
|
||||
});
|
||||
this.elements.set(treeItem.handle, element);
|
||||
}
|
||||
}
|
||||
return treeItem;
|
||||
});
|
||||
})).then(treeItems => this.updateChildren(coalesce(treeItems), parentElement));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,52 +132,29 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
if (hasRoot) {
|
||||
this.proxy.$refresh(this.viewId);
|
||||
} else {
|
||||
const handlesToUpdate = this.getHandlesToUpdate(elements);
|
||||
if (handlesToUpdate.length) {
|
||||
this._refreshHandles(handlesToUpdate);
|
||||
const handlesToRefresh = this.getHandlesToRefresh(elements);
|
||||
if (handlesToRefresh.length) {
|
||||
this.refreshHandles(handlesToRefresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private resolveElements(elements: T[], parentHandle?: TreeItemHandle): TPromise<ITreeItem[]> {
|
||||
if (elements && elements.length) {
|
||||
return TPromise.join(
|
||||
elements.filter(element => !!element)
|
||||
.map((element, index) => {
|
||||
return this.resolveElement(element, index, parentHandle)
|
||||
.then(treeItem => {
|
||||
if (treeItem) {
|
||||
this.nodes.set(element, {
|
||||
index,
|
||||
handle: treeItem.handle,
|
||||
parent: parentHandle,
|
||||
children: void 0
|
||||
});
|
||||
if (this.elements.has(treeItem.handle)) {
|
||||
return TPromise.wrapError<ITreeItem>(new Error(localize('treeView.duplicateElement', 'Element {0} is already registered', element)));
|
||||
}
|
||||
this.elements.set(treeItem.handle, element);
|
||||
}
|
||||
return treeItem;
|
||||
});
|
||||
}))
|
||||
.then(treeItems => treeItems.filter(treeItem => !!treeItem));
|
||||
}
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
private resolveElement(element: T, index: number, parentHandle?: TreeItemHandle): TPromise<ITreeItem> {
|
||||
private resolveElement(element: T, handleOrIndex: TreeItemHandle | number, parentHandle: TreeItemHandle): TPromise<ITreeItem> {
|
||||
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
|
||||
.then(extTreeItem => this.massageTreeItem(element, extTreeItem, index, parentHandle));
|
||||
.then(extTreeItem => this.massageTreeItem(element, extTreeItem, handleOrIndex, parentHandle));
|
||||
}
|
||||
|
||||
private massageTreeItem(element: T, extensionTreeItem: vscode.TreeItem, index: number, parentHandle: TreeItemHandle): ITreeItem {
|
||||
private massageTreeItem(element: T, extensionTreeItem: vscode.TreeItem, handleOrIndex: TreeItemHandle | number, parentHandle: TreeItemHandle): ITreeItem {
|
||||
if (!extensionTreeItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const icon = this.getLightIconPath(extensionTreeItem);
|
||||
const label = extensionTreeItem.label;
|
||||
const handle = typeof element === 'string' ? element : this.generateHandle(label, index, parentHandle);
|
||||
const handle = typeof handleOrIndex === 'number' ?
|
||||
this.generateHandle(label, handleOrIndex, parentHandle) // create the handle
|
||||
: handleOrIndex; // reuse the passed handle
|
||||
|
||||
return {
|
||||
handle,
|
||||
parentHandle,
|
||||
@@ -208,18 +197,18 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
return URI.file(iconPath).toString();
|
||||
}
|
||||
|
||||
private getHandlesToUpdate(elements: T[]): TreeItemHandle[] {
|
||||
private getHandlesToRefresh(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.elements.get(currentNode.parent);
|
||||
while (currentNode && currentNode.parentHandle && !elementsToUpdate.has(currentNode.parentHandle)) {
|
||||
const parentElement = this.elements.get(currentNode.parentHandle);
|
||||
currentNode = this.nodes.get(parentElement);
|
||||
}
|
||||
if (!currentNode.parent) {
|
||||
if (!currentNode.parentHandle) {
|
||||
elementsToUpdate.add(elementNode.handle);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +219,7 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
elementsToUpdate.forEach((handle) => {
|
||||
const element = this.elements.get(handle);
|
||||
let node = this.nodes.get(element);
|
||||
if (node && !elementsToUpdate.has(node.parent)) {
|
||||
if (node && !elementsToUpdate.has(node.parentHandle)) {
|
||||
handlesToUpdate.push(handle);
|
||||
}
|
||||
});
|
||||
@@ -238,21 +227,16 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
return handlesToUpdate;
|
||||
}
|
||||
|
||||
private _refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
|
||||
private refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
|
||||
const itemsToRefresh: { [handle: string]: ITreeItem } = {};
|
||||
const promises: TPromise<void>[] = [];
|
||||
itemHandles.forEach(treeItemHandle => {
|
||||
const extElement = this.getExtensionElement(treeItemHandle);
|
||||
const node = this.nodes.get(extElement);
|
||||
const promise = this.resolveElement(extElement, node.index, node.parent)
|
||||
promises.push(this.resolveElement(extElement, treeItemHandle, node.parentHandle)
|
||||
.then(treeItem => {
|
||||
if (treeItemHandle !== treeItem.handle) {
|
||||
// Update caches if handle changes
|
||||
this.updateCaches(node, treeItem, extElement);
|
||||
}
|
||||
itemsToRefresh[treeItemHandle] = treeItem;
|
||||
});
|
||||
promises.push(promise);
|
||||
}));
|
||||
});
|
||||
return TPromise.join(promises)
|
||||
.then(treeItems => {
|
||||
@@ -260,39 +244,34 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private updateCaches(node: TreeNode, treeItem: ITreeItem, element: T): void {
|
||||
if (node.parent) {
|
||||
// Update parent's children handles
|
||||
const parentElement = this.getExtensionElement(node.parent);
|
||||
private updateChildren(newChildren: ITreeItem[], parentElement?: T): ITreeItem[] {
|
||||
let existingChildrenHandles: TreeItemHandle[] = [];
|
||||
if (parentElement) {
|
||||
const parentNode = this.nodes.get(parentElement);
|
||||
parentNode.children[node.index] = treeItem.handle;
|
||||
existingChildrenHandles = parentNode.childrenHandles || [];
|
||||
parentNode.childrenHandles = newChildren.map(c => c.handle);
|
||||
} else {
|
||||
this.nodes.forEach(node => {
|
||||
if (!node.parentHandle) {
|
||||
existingChildrenHandles.push(node.handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update elements map
|
||||
this.elements.delete(node.handle);
|
||||
this.elements.set(treeItem.handle, element);
|
||||
|
||||
// Update node
|
||||
node.handle = treeItem.handle;
|
||||
}
|
||||
|
||||
private clearChildren(element: T): void {
|
||||
let node = this.nodes.get(element);
|
||||
if (node.children) {
|
||||
for (const childHandle of node.children) {
|
||||
const childEleement = this.elements.get(childHandle);
|
||||
if (childEleement) {
|
||||
this.clear(childEleement);
|
||||
}
|
||||
for (const existingChildHandle of existingChildrenHandles) {
|
||||
const existingChildElement = this.elements.get(existingChildHandle);
|
||||
if (existingChildElement && newChildren.every(c => c.handle !== existingChildHandle)) {
|
||||
this.clear(existingChildElement);
|
||||
}
|
||||
}
|
||||
node.children = void 0;
|
||||
|
||||
return newChildren;
|
||||
}
|
||||
|
||||
private clear(element: T): void {
|
||||
let node = this.nodes.get(element);
|
||||
if (node.children) {
|
||||
for (const childHandle of node.children) {
|
||||
if (node.childrenHandles) {
|
||||
for (const childHandle of node.childrenHandles) {
|
||||
const childEleement = this.elements.get(childHandle);
|
||||
if (childEleement) {
|
||||
this.clear(childEleement);
|
||||
@@ -303,12 +282,8 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
this.elements.delete(node.handle);
|
||||
}
|
||||
|
||||
private clearAll(): void {
|
||||
dispose() {
|
||||
this.elements.clear();
|
||||
this.nodes.clear();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.clearAll();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user