Merge VS Code 1.21 source code (#1067)

* Initial VS Code 1.21 file copy with patches

* A few more merges

* Post npm install

* Fix batch of build breaks

* Fix more build breaks

* Fix more build errors

* Fix more build breaks

* Runtime fixes 1

* Get connection dialog working with some todos

* Fix a few packaging issues

* Copy several node_modules to package build to fix loader issues

* Fix breaks from master

* A few more fixes

* Make tests pass

* First pass of license header updates

* Second pass of license header updates

* Fix restore dialog issues

* Remove add additional themes menu items

* fix select box issues where the list doesn't show up

* formatting

* Fix editor dispose issue

* Copy over node modules to correct location on all platforms
This commit is contained in:
Karl Burtram
2018-04-04 15:27:51 -07:00
committed by GitHub
parent 5fba3e31b4
commit dafb780987
9412 changed files with 141255 additions and 98813 deletions

View File

@@ -6,6 +6,7 @@
import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import { basename } from 'vs/base/common/paths';
import URI from 'vs/base/common/uri';
import { debounceEvent } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -14,6 +15,8 @@ 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 { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
import { isUndefinedOrNull } from 'vs/base/common/types';
type TreeItemHandle = string;
@@ -35,10 +38,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
});
}
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
const treeView = new ExtHostTreeView<T>(id, treeDataProvider, this._proxy, this.commands.converter);
this.treeViews.set(id, treeView);
registerTreeDataProvider<T>(id: string, dataProvider: vscode.TreeDataProvider<T>, proposedApiFunction: <U>(fn: U) => U): vscode.TreeView<T> {
const treeView = this.createExtHostTreeViewer(id, dataProvider);
return {
reveal: proposedApiFunction((element: T, options?: { select?: boolean }): Thenable<void> => {
return treeView.reveal(element, options);
}),
dispose: () => {
this.treeViews.delete(id);
treeView.dispose();
@@ -46,14 +51,6 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
};
}
$getElements(treeViewId: 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.getTreeItems();
}
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeItem[]> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
@@ -62,6 +59,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.getChildren(treeItemHandle);
}
private createExtHostTreeViewer<T>(id: string, dataProvider: vscode.TreeDataProvider<T>): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter);
this.treeViews.set(id, treeView);
return treeView;
}
private convertArgument(arg: TreeViewItemHandleArg): any {
const treeView = this.treeViews.get(arg.$treeViewId);
return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
@@ -69,124 +72,254 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
}
interface TreeNode {
index: number;
handle: TreeItemHandle;
parent: TreeItemHandle;
children: TreeItemHandle[];
item: ITreeItem;
parent: TreeNode;
children: TreeNode[];
}
class ExtHostTreeView<T> extends Disposable {
private static ROOT_HANDLE = '0';
private static LABEL_HANDLE_PREFIX = '0';
private static ID_HANDLE_PREFIX = '1';
private roots: TreeNode[] = null;
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, 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();
this.proxy.$registerView(viewId);
if (dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this._refresh(elements)));
this.proxy.$registerTreeViewDataProvider(viewId);
if (this.dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
}
}
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[]> {
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0;
if (parentHandle && !parentElement) {
console.error(`No tree item with id \'${parentHandle}\' found.`);
return TPromise.as([]);
}
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;
});
const childrenNodes = this.getChildrenNodes(parentHandle); // Get it from cache
return (childrenNodes ? TPromise.as(childrenNodes) : this.fetchChildrenNodes(parentElement))
.then(nodes => nodes.map(n => n.item));
}
getExtensionElement(treeItemHandle: TreeItemHandle): T {
return this.elements.get(treeItemHandle);
}
private _refresh(elements: T[]): void {
reveal(element: T, options?: { select?: boolean }): TPromise<void> {
if (typeof this.dataProvider.getParent !== 'function') {
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' mehtod`));
}
return this.resolveUnknownParentChain(element)
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), options)));
}
private resolveUnknownParentChain(element: T): TPromise<TreeNode[]> {
return this.resolveParent(element)
.then((parent) => {
if (!parent) {
return TPromise.as([]);
}
return this.resolveUnknownParentChain(parent)
.then(result => this.resolveTreeNode(parent, result[result.length - 1])
.then(parentNode => {
result.push(parentNode);
return result;
}));
});
}
private resolveParent(element: T): TPromise<T> {
const node = this.nodes.get(element);
if (node) {
return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null);
}
return asWinJsPromise(() => this.dataProvider.getParent(element));
}
private resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
const cachedElement = this.getExtensionElement(handle);
if (cachedElement) {
const node = this.nodes.get(cachedElement);
if (node) {
return TPromise.as(node);
}
}
throw new Error(`Cannot resolve tree item for element ${handle}`);
}));
}
private getChildrenNodes(parentNodeOrHandle?: TreeNode | TreeItemHandle): TreeNode[] {
if (parentNodeOrHandle) {
let parentNode: TreeNode;
if (typeof parentNodeOrHandle === 'string') {
const parentElement = this.getExtensionElement(parentNodeOrHandle);
parentNode = parentElement ? this.nodes.get(parentElement) : null;
} else {
parentNode = parentNodeOrHandle;
}
return parentNode ? parentNode.children : null;
}
return this.roots;
}
private fetchChildrenNodes(parentElement?: T): TPromise<TreeNode[]> {
// clear children cache
this.clearChildren(parentElement);
const parentNode = parentElement ? this.nodes.get(parentElement) : void 0;
return asWinJsPromise(() => this.dataProvider.getChildren(parentElement))
.then(elements => TPromise.join(
(elements || [])
.filter(element => !!element)
.map(element => asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => extTreeItem ? this.createAndRegisterTreeNode(element, extTreeItem, parentNode) : null))))
.then(nodes => nodes.filter(n => !!n));
}
private refresh(elements: T[]): void {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
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));
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.item.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.item.handle)) {
const parentElement = this.elements.get(currentNode.parent.item.handle);
currentNode = this.nodes.get(parentElement);
}
if (!currentNode.parent) {
elementsToUpdate.add(elementNode.item.handle);
}
}
}
return TPromise.as([]);
const handlesToUpdate: TreeItemHandle[] = [];
// Take only top level elements
elementsToUpdate.forEach((handle) => {
const element = this.elements.get(handle);
let node = this.nodes.get(element);
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
handlesToUpdate.push(handle);
}
});
return handlesToUpdate;
}
private resolveElement(element: T, index: number, parentHandle?: TreeItemHandle): TPromise<ITreeItem> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.massageTreeItem(element, extTreeItem, index, parentHandle));
private refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {};
return TPromise.join(itemHandles.map(treeItemHandle =>
this.refreshNode(treeItemHandle)
.then(node => {
if (node) {
itemsToRefresh[treeItemHandle] = node.item;
}
})))
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
}
private massageTreeItem(element: T, extensionTreeItem: vscode.TreeItem, index: number, parentHandle: TreeItemHandle): ITreeItem {
if (!extensionTreeItem) {
return null;
private refreshNode(treeItemHandle: TreeItemHandle): TPromise<TreeNode> {
const extElement = this.getExtensionElement(treeItemHandle);
const existing = this.nodes.get(extElement);
this.clearChildren(extElement); // clear children cache
return asWinJsPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
this.updateNodeCache(extElement, newNode, existing, existing.parent);
return newNode;
}
return null;
});
}
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode): TreeNode {
const node = this.createTreeNode(element, extTreeItem, parentNode);
if (extTreeItem.id && this.elements.has(node.item.handle)) {
throw new Error(localize('treeView.duplicateElement', 'Element with id {0} is already registered', extTreeItem.id));
}
const icon = this.getLightIconPath(extensionTreeItem);
const label = extensionTreeItem.label;
const handle = typeof element === 'string' ? element : this.generateHandle(label, index, parentHandle);
this.addNodeToCache(element, node);
this.addNodeToParentCache(node, parentNode);
return node;
}
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
return {
item: this.createTreeItem(element, extensionTreeItem, parent),
parent,
children: void 0
};
}
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent?: TreeNode): ITreeItem {
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
const item = {
handle,
parentHandle,
label,
parentHandle: parent ? parent.item.handle : void 0,
label: extensionTreeItem.label,
resourceUri: extensionTreeItem.resourceUri,
tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : void 0,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0,
contextValue: extensionTreeItem.contextValue,
icon,
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
collapsibleState: extensionTreeItem.collapsibleState
themeIcon: extensionTreeItem.iconPath instanceof ThemeIcon ? { id: extensionTreeItem.iconPath.id } : void 0,
collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState
};
return item;
}
private generateHandle(label: string, index: number, parentHandle: TreeItemHandle): TreeItemHandle {
parentHandle = parentHandle ? parentHandle : ExtHostTreeView.ROOT_HANDLE;
label = label.indexOf('/') !== -1 ? label.replace('/', '//') : label;
return `${parentHandle}/${index}:${label}`;
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent?: TreeNode): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX;
let elementId = label ? label : resourceUri ? basename(resourceUri.path) : '';
elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : void 0;
const childrenNodes = (this.getChildrenNodes(parent) || []);
for (let counter = 0; counter <= childrenNodes.length; counter++) {
const handle = `${prefix}/${counter}:${elementId}`;
if (!this.elements.has(handle) || existingHandle === handle) {
return handle;
}
}
throw new Error('This should not be reached');
}
private getLightIconPath(extensionTreeItem: vscode.TreeItem): string {
if (extensionTreeItem.iconPath) {
if (typeof extensionTreeItem.iconPath === 'string' || extensionTreeItem.iconPath instanceof URI) {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) {
if (typeof extensionTreeItem.iconPath === 'string'
|| extensionTreeItem.iconPath instanceof URI) {
return this.getIconPath(extensionTreeItem.iconPath);
}
return this.getIconPath(extensionTreeItem.iconPath['light']);
@@ -195,7 +328,7 @@ class ExtHostTreeView<T> extends Disposable {
}
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): string {
if (extensionTreeItem.iconPath && extensionTreeItem.iconPath['dark']) {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
}
return void 0;
@@ -208,102 +341,77 @@ class ExtHostTreeView<T> extends Disposable {
return URI.file(iconPath).toString();
}
private getHandlesToUpdate(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);
currentNode = this.nodes.get(parentElement);
}
if (!currentNode.parent) {
elementsToUpdate.add(elementNode.handle);
}
}
}
const handlesToUpdate: TreeItemHandle[] = [];
// Take only top level elements
elementsToUpdate.forEach((handle) => {
const element = this.elements.get(handle);
let node = this.nodes.get(element);
if (node && !elementsToUpdate.has(node.parent)) {
handlesToUpdate.push(handle);
}
});
return handlesToUpdate;
private addNodeToCache(element: T, node: TreeNode): void {
this.elements.set(node.item.handle, element);
this.nodes.set(element, node);
}
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)
.then(treeItem => {
if (treeItemHandle !== treeItem.handle) {
// Update caches if handle changes
this.updateCaches(node, treeItem, extElement);
private updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
// Remove from the cache
this.elements.delete(newNode.item.handle);
this.nodes.delete(element);
if (newNode.item.handle !== existing.item.handle) {
this.elements.delete(existing.item.handle);
}
// Add the new node to the cache
this.addNodeToCache(element, newNode);
// Replace the node in parent's children nodes
const childrenNodes = (this.getChildrenNodes(parentNode) || []);
const childNode = childrenNodes.filter(c => c.item.handle === existing.item.handle)[0];
if (childNode) {
childrenNodes.splice(childrenNodes.indexOf(childNode), 1, newNode);
}
}
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode): void {
if (parentNode) {
if (!parentNode.children) {
parentNode.children = [];
}
parentNode.children.push(node);
} else {
if (!this.roots) {
this.roots = [];
}
this.roots.push(node);
}
}
private clearChildren(parentElement?: T): void {
if (parentElement) {
let node = this.nodes.get(parentElement);
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
itemsToRefresh[treeItemHandle] = treeItem;
});
promises.push(promise);
});
return TPromise.join(promises)
.then(treeItems => {
this.proxy.$refresh(this.viewId, itemsToRefresh);
});
}
private updateCaches(node: TreeNode, treeItem: ITreeItem, element: T): void {
if (node.parent) {
// Update parent's children handles
const parentElement = this.getExtensionElement(node.parent);
const parentNode = this.nodes.get(parentElement);
parentNode.children[node.index] = treeItem.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);
}
}
node.children = [];
} else {
this.clearAll();
}
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.elements.get(childHandle);
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
}
}
this.nodes.delete(element);
this.elements.delete(node.handle);
this.elements.delete(node.item.handle);
}
private clearAll(): void {
this.roots = null;
this.elements.clear();
this.nodes.clear();
}