extHostTreeViews.ts 7.3 KB
Newer Older
S
Sandeep Somavarapu 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
S
Sandeep Somavarapu 已提交
13 14
import { MainContext, ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/parts/views/common/views';
S
Sandeep Somavarapu 已提交
15
import { TreeItemCollapsibleState } from './extHostTypes';
S
Sandeep Somavarapu 已提交
16
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
S
Sandeep Somavarapu 已提交
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
import { asWinJsPromise } from 'vs/base/common/async';

type TreeItemHandle = number;

export class ExtHostTreeViews extends ExtHostTreeViewsShape {

	private treeViews: Map<string, ExtHostTreeView<any>> = new Map<string, ExtHostTreeView<any>>();
	private _proxy: MainThreadTreeViewsShape;

	constructor(
		threadService: IThreadService,
		private commands: ExtHostCommands
	) {
		super();
		this._proxy = threadService.get(MainContext.MainThreadTreeViews);
		commands.registerArgumentProcessor({
			processArgument: arg => {
S
Sandeep Somavarapu 已提交
34
				if (arg && arg.$treeViewId && arg.$treeItemHandle) {
S
Sandeep Somavarapu 已提交
35 36 37 38 39 40 41
					return this.convertArgument(arg);
				}
				return arg;
			}
		});
	}

S
Sandeep Somavarapu 已提交
42
	registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
S
Sandeep Somavarapu 已提交
43
		const treeView = new ExtHostTreeView<T>(id, treeDataProvider, this._proxy, this.commands.converter);
S
Sandeep Somavarapu 已提交
44 45 46 47 48 49 50 51 52
		this.treeViews.set(id, treeView);
		return {
			dispose: () => {
				this.treeViews.delete(id);
				treeView.dispose();
			}
		};
	}

S
Sandeep Somavarapu 已提交
53
	$getElements(treeViewId: string): TPromise<ITreeItem[]> {
S
Sandeep Somavarapu 已提交
54 55
		const treeView = this.treeViews.get(treeViewId);
		if (!treeView) {
S
Sandeep Somavarapu 已提交
56
			return TPromise.wrapError<ITreeItem[]>(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
S
Sandeep Somavarapu 已提交
57 58 59 60
		}
		return treeView.getTreeItems();
	}

S
Sandeep Somavarapu 已提交
61
	$getChildren(treeViewId: string, treeItemHandle?: number): TPromise<ITreeItem[]> {
S
Sandeep Somavarapu 已提交
62 63
		const treeView = this.treeViews.get(treeViewId);
		if (!treeView) {
S
Sandeep Somavarapu 已提交
64
			return TPromise.wrapError<ITreeItem[]>(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
S
Sandeep Somavarapu 已提交
65 66 67 68
		}
		return treeView.getChildren(treeItemHandle);
	}

S
Sandeep Somavarapu 已提交
69 70 71
	private convertArgument(arg: TreeViewItemHandleArg): any {
		const treeView = this.treeViews.get(arg.$treeViewId);
		return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
S
Sandeep Somavarapu 已提交
72 73 74 75 76 77 78 79 80 81 82
	}
}

class ExtHostTreeView<T> extends Disposable {

	private _itemHandlePool = 0;

	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[]>();

S
Sandeep Somavarapu 已提交
83
	constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, ) {
S
Sandeep Somavarapu 已提交
84 85
		super();
		this.proxy.$registerView(viewId);
S
Sandeep Somavarapu 已提交
86 87
		if (dataProvider.onDidChangeTreeData) {
			this._register(dataProvider.onDidChangeTreeData(element => this._refresh(element)));
S
Sandeep Somavarapu 已提交
88
		}
S
Sandeep Somavarapu 已提交
89 90
	}

S
Sandeep Somavarapu 已提交
91
	getTreeItems(): TPromise<ITreeItem[]> {
S
Sandeep Somavarapu 已提交
92 93 94 95 96 97 98 99
		this.extChildrenElementsMap.clear();
		this.extElementsMap.clear();
		this.itemHandlesMap.clear();

		return asWinJsPromise(() => this.dataProvider.getChildren())
			.then(elements => this.processAndMapElements(elements));
	}

S
Sandeep Somavarapu 已提交
100
	getChildren(treeItemHandle: TreeItemHandle): TPromise<ITreeItem[]> {
S
Sandeep Somavarapu 已提交
101 102 103 104
		let extElement = this.getExtensionElement(treeItemHandle);
		if (extElement) {
			this.clearChildren(extElement);
		} else {
S
Sandeep Somavarapu 已提交
105
			return TPromise.wrapError<ITreeItem[]>(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', treeItemHandle));
S
Sandeep Somavarapu 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
		}

		return asWinJsPromise(() => this.dataProvider.getChildren(extElement))
			.then(childrenElements => this.processAndMapElements(childrenElements));
	}

	getExtensionElement(treeItemHandle: TreeItemHandle): T {
		return this.extElementsMap.get(treeItemHandle);
	}

	private _refresh(element: T): void {
		if (element) {
			const itemHandle = this.itemHandlesMap.get(element);
			if (itemHandle) {
				this.proxy.$refresh(this.viewId, itemHandle);
			}
		} else {
			this.proxy.$refresh(this.viewId);
		}
	}

S
Sandeep Somavarapu 已提交
127 128
	private processAndMapElements(elements: T[]): TPromise<ITreeItem[]> {
		const treeItemsPromises: TPromise<ITreeItem>[] = [];
S
Sandeep Somavarapu 已提交
129
		for (const element of elements) {
S
Sandeep Somavarapu 已提交
130 131
			if (element) {
				if (this.extChildrenElementsMap.has(element)) {
S
Sandeep Somavarapu 已提交
132
					return TPromise.wrapError<ITreeItem[]>(localize('treeView.duplicateElement', 'Element {0} is already registered', element));
S
Sandeep Somavarapu 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146
				}
				const treeItem = this.massageTreeItem(this.dataProvider.getTreeItem(element));
				if (treeItem) {
					this.itemHandlesMap.set(element, treeItem.handle);
					this.extElementsMap.set(treeItem.handle, element);
					if (treeItem.collapsibleState === TreeItemCollapsibleState.Expanded) {
						treeItemsPromises.push(this.getChildren(treeItem.handle).then(children => {
							treeItem.children = children;
							return treeItem;
						}));
					} else {
						treeItemsPromises.push(TPromise.as(treeItem));
					}
				}
S
Sandeep Somavarapu 已提交
147 148 149 150 151
			}
		}
		return TPromise.join(treeItemsPromises);
	}

S
Sandeep Somavarapu 已提交
152
	private massageTreeItem(extensionTreeItem: vscode.TreeItem): ITreeItem {
S
Sandeep Somavarapu 已提交
153 154 155
		if (!extensionTreeItem) {
			return null;
		}
156
		const icon = this.getLightIconPath(extensionTreeItem);
S
Sandeep Somavarapu 已提交
157 158 159
		return {
			handle: ++this._itemHandlePool,
			label: extensionTreeItem.label,
S
Sandeep Somavarapu 已提交
160
			command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0,
S
Sandeep Somavarapu 已提交
161
			contextValue: extensionTreeItem.contextValue,
162 163
			icon,
			iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
S
Sandeep Somavarapu 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
			collapsibleState: extensionTreeItem.collapsibleState,
		};
	}

	private getLightIconPath(extensionTreeItem: vscode.TreeItem) {
		if (extensionTreeItem.iconPath) {
			if (typeof extensionTreeItem.iconPath === 'string' || extensionTreeItem.iconPath instanceof URI) {
				return this.getIconPath(extensionTreeItem.iconPath);
			}
			return this.getIconPath(extensionTreeItem.iconPath['light']);
		}
		return void 0;
	}

	private getDarkIconPath(extensionTreeItem: vscode.TreeItem) {
		if (extensionTreeItem.iconPath && extensionTreeItem.iconPath['dark']) {
			return this.getIconPath(extensionTreeItem.iconPath['dark']);
		}
		return void 0;
	}

	private getIconPath(iconPath: string | URI): string {
		if (iconPath instanceof URI) {
			return iconPath.toString();
		}
		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);
			}
			this.extChildrenElementsMap.delete(extElement);
		}
	}

	private clearElement(extElement: T): void {
		this.clearChildren(extElement);

		const treeItemhandle = this.itemHandlesMap.get(extElement);
		this.itemHandlesMap.delete(extElement);
		if (treeItemhandle) {
			this.extElementsMap.delete(treeItemhandle);
		}
	}

	dispose() {
		this.extElementsMap.clear();
		this.itemHandlesMap.clear();
		this.extChildrenElementsMap.clear();
	}
}