Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -3,15 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISpliceable } from 'vs/base/common/sequence';
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { tail2 } from 'vs/base/common/arrays';
import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree';
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
import { ISequence, Iterator } from 'vs/base/common/iterator';
import { ISpliceable } from 'vs/base/common/sequence';
interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
readonly parent: IMutableTreeNode<T, TFilterData> | undefined;
readonly children: IMutableTreeNode<T, TFilterData>[];
visibleChildrenCount: number;
visibleChildIndex: number;
collapsible: boolean;
collapsed: boolean;
renderNodeCount: number;
@@ -19,10 +21,18 @@ interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
filterData: TFilterData | undefined;
}
function isFilterResult<T>(obj: any): obj is ITreeFilterDataResult<T> {
export function isFilterResult<T>(obj: any): obj is ITreeFilterDataResult<T> {
return typeof obj === 'object' && 'visibility' in obj && 'data' in obj;
}
export function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility {
switch (visibility) {
case true: return TreeVisibility.Visible;
case false: return TreeVisibility.Hidden;
default: return visibility;
}
}
function treeNodeToElement<T>(node: IMutableTreeNode<T, any>): ITreeElement<T> {
const { element, collapsed } = node;
const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement);
@@ -30,42 +40,46 @@ function treeNodeToElement<T>(node: IMutableTreeNode<T, any>): ITreeElement<T> {
return { element, children, collapsed };
}
function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility {
switch (visibility) {
case true: return TreeVisibility.Visible;
case false: return TreeVisibility.Hidden;
default: return visibility;
}
}
export interface IIndexTreeModelOptions<T, TFilterData> {
collapseByDefault?: boolean; // defaults to false
filter?: ITreeFilter<T, TFilterData>;
readonly collapseByDefault?: boolean; // defaults to false
readonly filter?: ITreeFilter<T, TFilterData>;
readonly autoExpandSingleChildren?: boolean;
}
export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = void> implements ITreeModel<T, TFilterData, number[]> {
readonly rootRef = [];
private root: IMutableTreeNode<T, TFilterData>;
private eventBufferer = new EventBufferer();
private _onDidChangeCollapseState = new Emitter<ITreeNode<T, TFilterData>>();
readonly onDidChangeCollapseState: Event<ITreeNode<T, TFilterData>> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event);
private _onDidChangeCollapseState = new Emitter<ICollapseStateChangeEvent<T, TFilterData>>();
readonly onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event);
private _onDidChangeRenderNodeCount = new Emitter<ITreeNode<T, TFilterData>>();
readonly onDidChangeRenderNodeCount: Event<ITreeNode<T, TFilterData>> = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event);
private collapseByDefault: boolean;
private filter?: ITreeFilter<T, TFilterData>;
private autoExpandSingleChildren: boolean;
private _onDidSplice = new Emitter<ITreeModelSpliceEvent<T, TFilterData>>();
readonly onDidSplice = this._onDidSplice.event;
constructor(private list: ISpliceable<ITreeNode<T, TFilterData>>, rootElement: T, options: IIndexTreeModelOptions<T, TFilterData> = {}) {
this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault;
this.filter = options.filter;
this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
// this.onDidChangeCollapseState(node => console.log(node.collapsed, node));
this.root = {
parent: undefined,
element: rootElement,
children: [],
depth: 0,
visibleChildrenCount: 0,
visibleChildIndex: -1,
collapsible: false,
collapsed: false,
renderNodeCount: 0,
@@ -85,22 +99,64 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
throw new Error('Invalid tree location');
}
const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location);
const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location);
const treeListElementsToInsert: ITreeNode<T, TFilterData>[] = [];
const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode));
const lastIndex = location[location.length - 1];
// figure out what's the visible child start index right before the
// splice point
let visibleChildStartIndex = 0;
for (let i = lastIndex; i >= 0 && i < parentNode.children.length; i--) {
const child = parentNode.children[i];
if (child.visible) {
visibleChildStartIndex = child.visibleChildIndex;
break;
}
}
const nodesToInsert: IMutableTreeNode<T, TFilterData>[] = [];
let insertedVisibleChildrenCount = 0;
let renderNodeCount = 0;
Iterator.forEach(nodesToInsertIterator, node => {
nodesToInsert.push(node);
renderNodeCount += node.renderNodeCount;
Iterator.forEach(nodesToInsertIterator, child => {
nodesToInsert.push(child);
renderNodeCount += child.renderNodeCount;
if (child.visible) {
child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++;
}
});
const lastIndex = location[location.length - 1];
const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert);
if (revealed) {
// figure out what is the count of deleted visible children
let deletedVisibleChildrenCount = 0;
for (const child of deletedNodes) {
if (child.visible) {
deletedVisibleChildrenCount++;
}
}
// and adjust for all visible children after the splice point
if (deletedVisibleChildrenCount !== 0) {
for (let i = lastIndex + nodesToInsert.length; i < parentNode.children.length; i++) {
const child = parentNode.children[i];
if (child.visible) {
child.visibleChildIndex -= deletedVisibleChildrenCount;
}
}
}
// update parent's visible children count
parentNode.visibleChildrenCount += insertedVisibleChildrenCount - deletedVisibleChildrenCount;
if (revealed && visible) {
const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0);
this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount);
@@ -116,37 +172,30 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
deletedNodes.forEach(visit);
}
return Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement);
const result = Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement);
this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes });
return result;
}
refresh(location: number[]): void {
if (location.length === 0) {
throw new Error('Invalid tree location');
}
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
if (revealed) {
this.list.splice(listIndex, 1, [node]);
}
}
getListIndex(location: number[]): number {
return this.getTreeNodeWithListIndex(location).listIndex;
const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location);
return visible && revealed ? listIndex : -1;
}
setCollapsed(location: number[], collapsed: boolean): boolean {
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
return this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed, collapsed));
}
toggleCollapsed(location: number[]): void {
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed));
}
collapseAll(): void {
const queue = [...this.root.children];
let listIndex = 0;
this.eventBufferer.bufferEvents(() => {
while (queue.length > 0) {
const node = queue.shift()!;
const revealed = listIndex < this.root.children.length;
this._setCollapsed(node, listIndex, revealed, true);
queue.push(...node.children);
listIndex++;
}
});
getListRenderCount(location: number[]): number {
return this.getTreeNode(location).renderNodeCount;
}
isCollapsible(location: number[]): boolean {
@@ -157,36 +206,84 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
return this.getTreeNode(location).collapsed;
}
refilter(): void {
const previousRenderNodeCount = this.root.renderNodeCount;
const toInsert = this.updateNodeAfterFilterChange(this.root);
this.list.splice(0, previousRenderNodeCount, toInsert);
}
private _setCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, collapsed?: boolean | undefined): boolean {
if (!node.collapsible) {
return false;
}
setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean {
const node = this.getTreeNode(location);
if (typeof collapsed === 'undefined') {
collapsed = !node.collapsed;
}
if (node.collapsed === collapsed) {
return false;
return this.eventBufferer.bufferEvents(() => this._setCollapsed(location, collapsed!, recursive));
}
private _setCollapsed(location: number[], collapsed: boolean, recursive?: boolean): boolean {
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false);
if (this.autoExpandSingleChildren && !collapsed! && !recursive) {
let onlyVisibleChildIndex = -1;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
if (child.visible) {
if (onlyVisibleChildIndex > -1) {
onlyVisibleChildIndex = -1;
break;
} else {
onlyVisibleChildIndex = i;
}
}
}
if (onlyVisibleChildIndex > -1) {
this._setCollapsed([...location, onlyVisibleChildIndex], false, false);
}
}
node.collapsed = collapsed;
return result;
}
if (revealed) {
const previousRenderNodeCount = node.renderNodeCount;
const toInsert = this.updateNodeAfterCollapseChange(node);
private _setListNodeCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean {
const result = this._setNodeCollapsed(node, collapsed, recursive, false);
this.list.splice(listIndex + 1, previousRenderNodeCount - 1, toInsert.slice(1));
this._onDidChangeCollapseState.fire(node);
if (!revealed || !node.visible) {
return result;
}
return true;
const previousRenderNodeCount = node.renderNodeCount;
const toInsert = this.updateNodeAfterCollapseChange(node);
const deleteCount = previousRenderNodeCount - (listIndex === -1 ? 0 : 1);
this.list.splice(listIndex + 1, deleteCount, toInsert.slice(1));
return result;
}
private _setNodeCollapsed(node: IMutableTreeNode<T, TFilterData>, collapsed: boolean, recursive: boolean, deep: boolean): boolean {
let result = node.collapsible && node.collapsed !== collapsed;
if (node.collapsible) {
node.collapsed = collapsed;
if (result) {
this._onDidChangeCollapseState.fire({ node, deep });
}
}
if (recursive) {
for (const child of node.children) {
result = this._setNodeCollapsed(child, collapsed, true, true) || result;
}
}
return result;
}
refilter(): void {
const previousRenderNodeCount = this.root.renderNodeCount;
const toInsert = this.updateNodeAfterFilterChange(this.root);
this.list.splice(0, previousRenderNodeCount, toInsert);
}
private createTreeNode(
@@ -202,6 +299,8 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
element: treeElement.element,
children: [],
depth: parent.depth + 1,
visibleChildrenCount: 0,
visibleChildIndex: -1,
collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'),
collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed,
renderNodeCount: 1,
@@ -219,17 +318,21 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed;
const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode));
let hasVisibleDescendants = false;
let visibleChildrenCount = 0;
let renderNodeCount = 1;
Iterator.forEach(childNodes, child => {
node.children.push(child);
hasVisibleDescendants = hasVisibleDescendants || child.visible;
renderNodeCount += child.renderNodeCount;
if (child.visible) {
child.visibleChildIndex = visibleChildrenCount++;
}
});
node.collapsible = node.collapsible || node.children.length > 0;
node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible);
node.visibleChildrenCount = visibleChildrenCount;
node.visible = visibility === TreeVisibility.Recurse ? visibleChildrenCount > 0 : (visibility === TreeVisibility.Visible);
if (!node.visible) {
node.renderNodeCount = 0;
@@ -307,9 +410,19 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
let hasVisibleDescendants = false;
if (!node.collapsed || visibility! !== TreeVisibility.Hidden) {
let visibleChildIndex = 0;
for (const child of node.children) {
hasVisibleDescendants = this._updateNodeAfterFilterChange(child, visibility!, result, revealed && !node.collapsed) || hasVisibleDescendants;
if (child.visible) {
child.visibleChildIndex = visibleChildIndex++;
}
}
node.visibleChildrenCount = visibleChildIndex;
} else {
node.visibleChildrenCount = 0;
}
if (node !== this.root) {
@@ -373,8 +486,12 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
// expensive
private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean } {
const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location);
private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, visible: boolean } {
if (location.length === 0) {
return { node: this.root, listIndex: -1, revealed: true, visible: false };
}
const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location);
const index = location[location.length - 1];
if (index < 0 || index > parentNode.children.length) {
@@ -383,10 +500,10 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
const node = parentNode.children[index];
return { node, listIndex, revealed };
return { node, listIndex, revealed, visible: visible && node.visible };
}
private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root, listIndex: number = 0, revealed = true): { parentNode: IMutableTreeNode<T, TFilterData>; listIndex: number; revealed: boolean; } {
private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root, listIndex: number = 0, revealed = true, visible = true): { parentNode: IMutableTreeNode<T, TFilterData>; listIndex: number; revealed: boolean; visible: boolean; } {
const [index, ...rest] = location;
if (index < 0 || index > node.children.length) {
@@ -399,12 +516,13 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
revealed = revealed && !node.collapsed;
visible = visible && node.visible;
if (rest.length === 0) {
return { parentNode: node, listIndex, revealed };
return { parentNode: node, listIndex, revealed, visible };
}
return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed);
return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed, visible);
}
getNode(location: number[] = []): ITreeNode<T, TFilterData> {