Merge from vscode e1d3dd53d17fb1529a002e4d6fb066db0a0bd385 (#6460)

* Merge from vscode e1d3dd53d17fb1529a002e4d6fb066db0a0bd385

* fix servers icon

* fix tests
This commit is contained in:
Anthony Dresser
2019-07-22 18:28:21 -07:00
committed by GitHub
parent f2afacd8b2
commit 15fc7a077a
91 changed files with 2562 additions and 972 deletions

View File

@@ -34,7 +34,7 @@ export function isInDOM(node: Node | null): boolean {
if (node === document.body) {
return true;
}
node = node.parentNode;
node = node.parentNode || (node as ShadowRoot).host;
}
return false;
}
@@ -806,7 +806,8 @@ export function createCSSRule(selector: string, cssText: string, style: HTMLStyl
if (!style || !cssText) {
return;
}
style.textContent = `${selector}{${cssText}}\n${style.textContent}`;
(<CSSStyleSheet>style.sheet).insertRule(selector + '{' + cssText + '}', 0);
}
export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLStyleElement = getSharedStyleSheet()): void {

View File

@@ -102,6 +102,22 @@ class BranchNode implements ISplitView, IDisposable {
return Math.min(...this.children.map(c => c.maximumOrthogonalSize));
}
get priority(): LayoutPriority {
if (this.children.length === 0) {
return LayoutPriority.Normal;
}
const priorities = this.children.map(c => typeof c.priority === 'undefined' ? LayoutPriority.Normal : c.priority);
if (priorities.some(p => p === LayoutPriority.High)) {
return LayoutPriority.High;
} else if (priorities.some(p => p === LayoutPriority.Low)) {
return LayoutPriority.Low;
}
return LayoutPriority.Normal;
}
get minimumOrthogonalSize(): number {
return this.splitview.minimumSize;
}

View File

@@ -325,13 +325,13 @@ export class SplitView extends Disposable {
container.appendChild(view.element);
let highPriorityIndex: number | undefined;
let highPriorityIndexes: number[] | undefined;
if (typeof size !== 'number' && size.type === 'split') {
highPriorityIndex = size.index;
highPriorityIndexes = [size.index];
}
this.relayout(index, highPriorityIndex);
this.relayout([index], highPriorityIndexes);
this.state = State.Idle;
if (typeof size !== 'number' && size.type === 'distribute') {
@@ -420,17 +420,6 @@ export class SplitView extends Disposable {
this.layoutViews();
}
private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
const lowPriorityIndexes = typeof lowPriorityIndex === 'number' ? [lowPriorityIndex] : undefined;
const highPriorityIndexes = typeof highPriorityIndex === 'number' ? [highPriorityIndex] : undefined;
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndexes, highPriorityIndexes);
this.distributeEmptySpace();
this.layoutViews();
this.saveProportions();
}
layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
@@ -582,7 +571,7 @@ export class SplitView extends Disposable {
this.layoutViews();
} else {
item.size = size;
this.relayout(index, undefined);
this.relayout([index], undefined);
}
}
@@ -597,42 +586,32 @@ export class SplitView extends Disposable {
return;
}
const indexes = range(this.viewItems.length).filter(i => i !== index);
const lowPriorityIndexes = [...indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low), index];
const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);
const item = this.viewItems[index];
size = Math.round(size);
size = clamp(size, item.minimumSize, item.maximumSize);
let delta = size - item.size;
size = clamp(size, item.minimumSize, Math.min(item.maximumSize, this.size));
if (delta !== 0 && index < this.viewItems.length - 1) {
const downIndexes = range(index + 1, this.viewItems.length);
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].minimumSize), 0);
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - this.viewItems[i].size), 0);
const deltaDown = clamp(delta, -expandDown, collapseDown);
this.resize(index, deltaDown);
delta -= deltaDown;
}
if (delta !== 0 && index > 0) {
const upIndexes = range(index - 1, -1);
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].minimumSize), 0);
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - this.viewItems[i].size), 0);
const deltaUp = clamp(-delta, -collapseUp, expandUp);
this.resize(index - 1, deltaUp);
}
this.distributeEmptySpace();
this.layoutViews();
this.saveProportions();
item.size = size;
this.relayout(lowPriorityIndexes, highPriorityIndexes);
this.state = State.Idle;
}
distributeViewSizes(): void {
const size = Math.floor(this.size / this.viewItems.length);
for (let i = 0; i < this.viewItems.length - 1; i++) {
this.resizeView(i, size);
for (let i = 0; i < this.viewItems.length; i++) {
const item = this.viewItems[i];
item.size = clamp(size, item.minimumSize, item.maximumSize);
}
const indexes = range(this.viewItems.length);
const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low);
const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);
this.relayout(lowPriorityIndexes, highPriorityIndexes);
}
getViewSize(index: number): number {
@@ -643,6 +622,15 @@ export class SplitView extends Disposable {
return this.viewItems[index].size;
}
private relayout(lowPriorityIndexes?: number[], highPriorityIndexes?: number[]): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndexes, highPriorityIndexes);
this.distributeEmptySpace();
this.layoutViews();
this.saveProportions();
}
private resize(
index: number,
delta: number,
@@ -830,18 +818,26 @@ export class SplitView extends Disposable {
private findFirstSnapIndex(indexes: number[]): number | undefined {
// visible views first
for (const index of indexes) {
if (!this.viewItems[index].visible) {
const viewItem = this.viewItems[index];
if (!viewItem.visible) {
continue;
}
if (this.viewItems[index].snap) {
if (viewItem.snap) {
return index;
}
}
// then, hidden views
for (const index of indexes) {
if (!this.viewItems[index].visible && this.viewItems[index].snap) {
const viewItem = this.viewItems[index];
if (viewItem.visible && viewItem.maximumSize - viewItem.minimumSize > 0) {
return undefined;
}
if (!viewItem.visible && viewItem.snap) {
return index;
}
}

View File

@@ -0,0 +1,404 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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 { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { IObjectTreeModelOptions, ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
export interface ICompressedTreeElement<T> extends ITreeElement<T> {
readonly children?: Iterator<ICompressedTreeElement<T>> | ICompressedTreeElement<T>[];
readonly incompressible?: boolean;
}
export interface ICompressedTreeNode<T> {
readonly elements: T[];
readonly incompressible: boolean;
}
export function compress<T>(element: ICompressedTreeElement<T>): ITreeElement<ICompressedTreeNode<T>> {
const elements = [element.element];
const incompressible = element.incompressible || false;
let childrenIterator: Iterator<ITreeElement<T>>;
let children: ITreeElement<T>[];
while (true) {
childrenIterator = Iterator.from(element.children);
children = Iterator.collect(childrenIterator, 2);
if (children.length !== 1) {
break;
}
element = children[0];
if (element.incompressible) {
break;
}
elements.push(element.element);
}
return {
element: { elements, incompressible },
children: Iterator.map(Iterator.concat(Iterator.fromArray(children), childrenIterator), compress)
};
}
export function _decompress<T>(element: ITreeElement<ICompressedTreeNode<T>>, index = 0): ICompressedTreeElement<T> {
let children: Iterator<ICompressedTreeElement<T>>;
if (index < element.element.elements.length - 1) {
children = Iterator.single(_decompress(element, index + 1));
} else {
children = Iterator.map(Iterator.from(element.children), el => _decompress(el, 0));
}
if (index === 0 && element.element.incompressible) {
return { element: element.element.elements[index], children, incompressible: true };
}
return { element: element.element.elements[index], children };
}
export function decompress<T>(element: ITreeElement<ICompressedTreeNode<T>>): ICompressedTreeElement<T> {
return _decompress(element, 0);
}
export function splice<T>(treeElement: ICompressedTreeElement<T>, element: T, children: Iterator<ICompressedTreeElement<T>>): ICompressedTreeElement<T> {
if (treeElement.element === element) {
return { element, children };
}
return {
...treeElement,
children: Iterator.map(Iterator.from(treeElement.children), e => splice(e, element, children))
};
}
export interface ICompressedObjectTreeModelOptions<T, TFilterData> extends IObjectTreeModelOptions<ICompressedTreeNode<T>, TFilterData> { }
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
readonly rootRef = null;
get onDidSplice(): Event<ITreeModelSpliceEvent<ICompressedTreeNode<T> | null, TFilterData>> { return this.model.onDidSplice; }
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<ICompressedTreeNode<T>, TFilterData>> { return this.model.onDidChangeCollapseState; }
get onDidChangeRenderNodeCount(): Event<ITreeNode<ICompressedTreeNode<T>, TFilterData>> { return this.model.onDidChangeRenderNodeCount; }
private model: ObjectTreeModel<ICompressedTreeNode<T>, TFilterData>;
private nodes = new Map<T | null, ICompressedTreeNode<T>>();
get size(): number { return this.nodes.size; }
constructor(list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}) {
this.model = new ObjectTreeModel(list, options);
}
setChildren(
element: T | null,
children: ISequence<ICompressedTreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
const insertedElements = new Set<T | null>();
const _onDidCreateNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
for (const element of node.element.elements) {
insertedElements.add(element);
this.nodes.set(element, node.element);
}
// if (this.identityProvider) {
// const id = this.identityProvider.getId(node.element).toString();
// insertedElementIds.add(id);
// this.nodesByIdentity.set(id, node);
// }
if (onDidCreateNode) {
onDidCreateNode(node);
}
};
const _onDidDeleteNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
for (const element of node.element.elements) {
if (!insertedElements.has(element)) {
this.nodes.delete(element);
}
}
// if (this.identityProvider) {
// const id = this.identityProvider.getId(node.element).toString();
// if (!insertedElementIds.has(id)) {
// this.nodesByIdentity.delete(id);
// }
// }
if (onDidDeleteNode) {
onDidDeleteNode(node);
}
};
if (element === null) {
const compressedChildren = Iterator.map(Iterator.from(children), compress);
const result = this.model.setChildren(null, compressedChildren, _onDidCreateNode, _onDidDeleteNode);
return Iterator.map(result, decompress);
}
const compressedNode = this.nodes.get(element);
const node = this.model.getNode(compressedNode) as ITreeNode<ICompressedTreeNode<T>, TFilterData>;
const parent = node.parent!;
const decompressedElement = decompress(node);
const splicedElement = splice(decompressedElement, element, Iterator.from(children));
const recompressedElement = compress(splicedElement);
const parentChildren = parent.children
.map(child => child === node ? recompressedElement : child);
this.model.setChildren(parent.element, parentChildren, _onDidCreateNode, _onDidDeleteNode);
// TODO
return Iterator.empty();
}
getListIndex(location: T | null): number {
const node = this.getCompressedNode(location);
return this.model.getListIndex(node);
}
getListRenderCount(location: T | null): number {
const node = this.getCompressedNode(location);
return this.model.getListRenderCount(node);
}
getNode(location?: T | null | undefined): ITreeNode<ICompressedTreeNode<T> | null, TFilterData> {
if (typeof location === 'undefined') {
return this.model.getNode();
}
const node = this.getCompressedNode(location);
return this.model.getNode(node);
}
// TODO: review this
getNodeLocation(node: ITreeNode<ICompressedTreeNode<T>, TFilterData>): T | null {
const compressedNode = this.model.getNodeLocation(node);
if (compressedNode === null) {
return null;
}
return compressedNode.elements[compressedNode.elements.length - 1];
}
// TODO: review this
getParentNodeLocation(location: T | null): T | null {
const compressedNode = this.getCompressedNode(location);
const parentNode = this.model.getParentNodeLocation(compressedNode);
if (parentNode === null) {
return null;
}
return parentNode.elements[parentNode.elements.length - 1];
}
getParentElement(location: T | null): ICompressedTreeNode<T> | null {
const compressedNode = this.getCompressedNode(location);
return this.model.getParentElement(compressedNode);
}
getFirstElementChild(location: T | null): ICompressedTreeNode<T> | null | undefined {
const compressedNode = this.getCompressedNode(location);
return this.model.getFirstElementChild(compressedNode);
}
getLastElementAncestor(location?: T | null | undefined): ICompressedTreeNode<T> | null | undefined {
const compressedNode = typeof location === 'undefined' ? undefined : this.getCompressedNode(location);
return this.model.getLastElementAncestor(compressedNode);
}
isCollapsible(location: T | null): boolean {
const compressedNode = this.getCompressedNode(location);
return this.model.isCollapsible(compressedNode);
}
isCollapsed(location: T | null): boolean {
const compressedNode = this.getCompressedNode(location);
return this.model.isCollapsed(compressedNode);
}
setCollapsed(location: T | null, collapsed?: boolean | undefined, recursive?: boolean | undefined): boolean {
const compressedNode = this.getCompressedNode(location);
return this.model.setCollapsed(compressedNode, collapsed, recursive);
}
expandTo(location: T | null): void {
const compressedNode = this.getCompressedNode(location);
this.model.expandTo(compressedNode);
}
rerender(location: T | null): void {
const compressedNode = this.getCompressedNode(location);
this.model.rerender(compressedNode);
}
refilter(): void {
this.model.refilter();
}
resort(location: T | null = null, recursive = true): void {
const compressedNode = this.getCompressedNode(location);
this.model.resort(compressedNode, recursive);
}
private getCompressedNode(element: T | null): ICompressedTreeNode<T> | null {
if (element === null) {
return null;
}
const node = this.nodes.get(element);
if (!node) {
throw new Error(`Tree element not found: ${element}`);
}
return node;
}
}
export type ElementMapper<T> = (elements: T[]) => T;
export const DefaultElementMapper: ElementMapper<any> = elements => elements[elements.length - 1];
export type NodeMapper<T, TFilterData> = (node: ITreeNode<ICompressedTreeNode<T> | null, TFilterData>) => ITreeNode<T | null, TFilterData>;
function mapNode<T, TFilterData>(elementMapper: ElementMapper<T>, node: ITreeNode<ICompressedTreeNode<T> | null, TFilterData>): ITreeNode<T | null, TFilterData> {
return {
...node,
element: node.element === null ? null : elementMapper(node.element.elements),
children: node.children.map(child => mapNode(elementMapper, child)),
parent: typeof node.parent === 'undefined' ? node.parent : mapNode(elementMapper, node.parent)
};
}
function createNodeMapper<T, TFilterData>(elementMapper: ElementMapper<T>): NodeMapper<T, TFilterData> {
return node => mapNode(elementMapper, node);
}
export interface ILinearCompressedObjectTreeModelOptions<T, TFilterData> extends ICompressedObjectTreeModelOptions<T, TFilterData> {
readonly elementMapper?: ElementMapper<T>;
}
export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
readonly rootRef = null;
get onDidSplice(): Event<ITreeModelSpliceEvent<T | null, TFilterData>> {
return Event.map(this.model.onDidSplice, ({ insertedNodes, deletedNodes }) => ({
insertedNodes: insertedNodes.map(this.mapNode),
deletedNodes: deletedNodes.map(this.mapNode),
}));
}
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T | null, TFilterData>> {
return Event.map(this.model.onDidChangeCollapseState, ({ node, deep }) => ({
node: this.mapNode(node),
deep
}));
}
get onDidChangeRenderNodeCount(): Event<ITreeNode<T | null, TFilterData>> {
return Event.map(this.model.onDidChangeRenderNodeCount, this.mapNode);
}
private mapElement: ElementMapper<T | null>;
private mapNode: NodeMapper<T | null, TFilterData>;
private model: CompressedObjectTreeModel<T, TFilterData>;
constructor(
list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
options: ILinearCompressedObjectTreeModelOptions<T, TFilterData> = {}
) {
this.mapElement = options.elementMapper || DefaultElementMapper;
this.mapNode = createNodeMapper(this.mapElement);
this.model = new CompressedObjectTreeModel(list, options);
}
getListIndex(location: T | null): number {
return this.model.getListIndex(location);
}
getListRenderCount(location: T | null): number {
return this.model.getListRenderCount(location);
}
getNode(location?: T | null | undefined): ITreeNode<T | null, any> {
return this.mapNode(this.model.getNode(location));
}
getNodeLocation(node: ITreeNode<T | null, any>): T | null {
return node.element;
}
getParentNodeLocation(location: T | null): T | null {
return this.model.getParentNodeLocation(location);
}
getParentElement(location: T | null): T | null {
const result = this.model.getParentElement(location);
if (result === null) {
return null; // {{SQL CARBON EDIT}} strict-null-check
}
return this.mapElement(result.elements);
}
getFirstElementChild(location: T | null): T | null | undefined {
const result = this.model.getFirstElementChild(location);
if (result === null || typeof result === 'undefined') {
return null; // {{SQL CARBON EDIT}} strict-null-check
}
return this.mapElement(result.elements);
}
getLastElementAncestor(location?: T | null | undefined): T | null | undefined {
const result = this.model.getLastElementAncestor(location);
if (result === null || typeof result === 'undefined') {
return null; // {{SQL CARBON EDIT}} strict-null-check
}
return this.mapElement(result.elements);
}
isCollapsible(location: T | null): boolean {
return this.model.isCollapsible(location);
}
isCollapsed(location: T | null): boolean {
return this.model.isCollapsed(location);
}
setCollapsed(location: T | null, collapsed?: boolean | undefined, recursive?: boolean | undefined): boolean {
return this.model.setCollapsed(location, collapsed, recursive);
}
expandTo(location: T | null): void {
return this.model.expandTo(location);
}
rerender(location: T | null): void {
return this.model.rerender(location);
}
refilter(): void {
return this.model.refilter();
}
}

View File

@@ -53,7 +53,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
): Iterator<ITreeElement<T>> {
const location = this.getElementLocation(element);
return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode);
}
@@ -63,7 +63,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
): Iterator<ITreeElement<T>> {
const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>();
@@ -107,7 +107,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
_onDidDeleteNode
);
return result;
return result as Iterator<ITreeElement<T>>;
}
private preserveCollapseState(elements: ISequence<ITreeElement<T>> | undefined): ISequence<ITreeElement<T>> {
@@ -144,7 +144,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
});
}
rerender(element: T): void {
rerender(element: T | null): void {
const location = this.getElementLocation(element);
this.model.rerender(location);
}
@@ -190,32 +190,32 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
return this.model.getLastElementAncestor(location);
}
getListIndex(element: T): number {
getListIndex(element: T | null): number {
const location = this.getElementLocation(element);
return this.model.getListIndex(location);
}
getListRenderCount(element: T): number {
getListRenderCount(element: T | null): number {
const location = this.getElementLocation(element);
return this.model.getListRenderCount(location);
}
isCollapsible(element: T): boolean {
isCollapsible(element: T | null): boolean {
const location = this.getElementLocation(element);
return this.model.isCollapsible(location);
}
isCollapsed(element: T): boolean {
isCollapsed(element: T | null): boolean {
const location = this.getElementLocation(element);
return this.model.isCollapsed(location);
}
setCollapsed(element: T, collapsed?: boolean, recursive?: boolean): boolean {
setCollapsed(element: T | null, collapsed?: boolean, recursive?: boolean): boolean {
const location = this.getElementLocation(element);
return this.model.setCollapsed(location, collapsed, recursive);
}
expandTo(element: T): void {
expandTo(element: T | null): void {
const location = this.getElementLocation(element);
this.model.expandTo(location);
}
@@ -238,11 +238,15 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
return node;
}
getNodeLocation(node: ITreeNode<T, TFilterData>): T {
getNodeLocation(node: ITreeNode<T, TFilterData>): T | null {
return node.element;
}
getParentNodeLocation(element: T): T | null {
getParentNodeLocation(element: T | null): T | null {
if (element === null) {
throw new Error(`Invalid getParentNodeLocation call`);
}
const node = this.nodes.get(element);
if (!node) {

View File

@@ -124,6 +124,7 @@ export interface ITreeModel<T, TFilterData, TRef> {
setCollapsed(location: TRef, collapsed?: boolean, recursive?: boolean): boolean;
expandTo(location: TRef): void;
rerender(location: TRef): void;
refilter(): void;
}

View File

@@ -29,6 +29,21 @@ export module Iterator {
return _empty;
}
export function single<T>(value: T): Iterator<T> {
let done = false;
return {
next(): IteratorResult<T> {
if (done) {
return FIN;
}
done = true;
return { done: false, value };
}
};
}
export function fromArray<T>(array: T[], index = 0, length = array.length): Iterator<T> {
return {
next(): IteratorResult<T> {
@@ -86,11 +101,47 @@ export module Iterator {
}
}
export function collect<T>(iterator: Iterator<T>): T[] {
export function collect<T>(iterator: Iterator<T>, atMost: number = Number.POSITIVE_INFINITY): T[] {
const result: T[] = [];
forEach(iterator, value => result.push(value));
if (atMost === 0) {
return result;
}
let i = 0;
for (let next = iterator.next(); !next.done; next = iterator.next()) {
result.push(next.value);
if (++i >= atMost) {
break;
}
}
return result;
}
export function concat<T>(...iterators: Iterator<T>[]): Iterator<T> {
let i = 0;
return {
next() {
if (i >= iterators.length) {
return FIN;
}
const iterator = iterators[i];
const result = iterator.next();
if (result.done) {
i++;
return this.next();
}
return result;
}
};
}
}
export type ISequence<T> = Iterator<T> | T[];

View File

@@ -107,19 +107,19 @@ suite('Gridview', function () {
test('simple layout', function () {
gridview.layout(800, 600);
const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
assert.deepEqual(view1.size, [800, 600]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 600 });
const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
assert.deepEqual(view1.size, [800, 400]);
assert.deepEqual(gridview.getViewSize([1]), { width: 800, height: 400 });
assert.deepEqual(view2.size, [800, 200]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 200 });
const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [1, 1]);
assert.deepEqual(view1.size, [600, 400]);
assert.deepEqual(gridview.getViewSize([1, 0]), { width: 600, height: 400 });
@@ -128,7 +128,7 @@ suite('Gridview', function () {
assert.deepEqual(view3.size, [200, 400]);
assert.deepEqual(gridview.getViewSize([1, 1]), { width: 200, height: 400 });
const view4 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view4 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view4, 200, [0, 0]);
assert.deepEqual(view1.size, [600, 400]);
assert.deepEqual(gridview.getViewSize([1, 0]), { width: 600, height: 400 });
@@ -139,7 +139,7 @@ suite('Gridview', function () {
assert.deepEqual(view4.size, [200, 200]);
assert.deepEqual(gridview.getViewSize([0, 0]), { width: 200, height: 200 });
const view5 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view5 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view5, 100, [1, 0, 1]);
assert.deepEqual(view1.size, [600, 300]);
assert.deepEqual(gridview.getViewSize([1, 0, 0]), { width: 600, height: 300 });
@@ -156,30 +156,30 @@ suite('Gridview', function () {
test('simple layout with automatic size distribution', function () {
gridview.layout(800, 600);
const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, Sizing.Distribute, [0]);
assert.deepEqual(view1.size, [800, 600]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 600 });
const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, Sizing.Distribute, [0]);
assert.deepEqual(view1.size, [800, 300]);
assert.deepEqual(view2.size, [800, 300]);
const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
assert.deepEqual(view1.size, [400, 300]);
assert.deepEqual(view2.size, [800, 300]);
assert.deepEqual(view3.size, [400, 300]);
const view4 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view4 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view4, Sizing.Distribute, [0, 0]);
assert.deepEqual(view1.size, [400, 300]);
assert.deepEqual(view2.size, [400, 300]);
assert.deepEqual(view3.size, [400, 300]);
assert.deepEqual(view4.size, [400, 300]);
const view5 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view5 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view5, Sizing.Distribute, [1, 0, 1]);
assert.deepEqual(view1.size, [400, 150]);
assert.deepEqual(view2.size, [400, 300]);
@@ -190,13 +190,13 @@ suite('Gridview', function () {
test('addviews before layout call 1', function () {
const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [1, 1]);
gridview.layout(800, 600);
@@ -207,14 +207,13 @@ suite('Gridview', function () {
});
test('addviews before layout call 2', function () {
const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [0, 0]);
gridview.layout(800, 600);

View File

@@ -196,8 +196,8 @@ suite('Splitview', () => {
splitview.resizeView(0, 70);
assert.equal(view1.size, 70, 'view1 is collapsed');
assert.equal(view2.size, 110, 'view2 is expanded');
assert.equal(view3.size, 20, 'view3 stays the same');
assert.equal(view2.size, 40, 'view2 stays the same');
assert.equal(view3.size, 90, 'view3 is stretched');
splitview.resizeView(2, 40);
@@ -474,10 +474,10 @@ suite('Splitview', () => {
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
splitview.layout(180);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
splitview.layout(124);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 20, 38]);
@@ -504,13 +504,13 @@ suite('Splitview', () => {
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
splitview.layout(180);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
splitview.layout(132);
assert.deepEqual([view1.size, view2.size, view3.size], [44, 20, 68]);
assert.deepEqual([view1.size, view2.size, view3.size], [46, 20, 66]);
splitview.layout(60);
assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]);

View File

@@ -0,0 +1,430 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { Iterator } from 'vs/base/common/iterator';
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
interface IResolvedCompressedTreeElement<T> extends ICompressedTreeElement<T> {
readonly element: T;
readonly children?: ICompressedTreeElement<T>[];
}
function resolve<T>(treeElement: ICompressedTreeElement<T>): IResolvedCompressedTreeElement<T> {
const result: any = { element: treeElement.element };
const children = Iterator.collect(Iterator.map(Iterator.from(treeElement.children), resolve));
if (treeElement.incompressible) {
result.incompressible = true;
}
if (children.length > 0) {
result.children = children;
}
return result;
}
suite('CompressedObjectTree', function () {
suite('compress & decompress', function () {
test('small', function () {
const decompressed: ICompressedTreeElement<number> = { element: 1 };
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> =
{ element: { elements: [1], incompressible: false } };
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('no compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{ element: 11 },
{ element: 12 },
{ element: 13 }
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{ element: { elements: [11], incompressible: false } },
{ element: { elements: [12], incompressible: false } },
{ element: { elements: [13], incompressible: false } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('single hierarchy', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111 }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111, 1111], incompressible: false }
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('deep compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
{ element: 1114 },
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111], incompressible: false },
children: [
{ element: { elements: [1111], incompressible: false } },
{ element: { elements: [1112], incompressible: false } },
{ element: { elements: [1113], incompressible: false } },
{ element: { elements: [1114], incompressible: false } },
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('double deep compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1112 },
{ element: 1113 },
]
}
]
},
{
element: 12, children: [
{
element: 121, children: [
{ element: 1212 },
{ element: 1213 },
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{
element: { elements: [11, 111], incompressible: false },
children: [
{ element: { elements: [1112], incompressible: false } },
{ element: { elements: [1113], incompressible: false } },
]
},
{
element: { elements: [12, 121], incompressible: false },
children: [
{ element: { elements: [1212], incompressible: false } },
{ element: { elements: [1213], incompressible: false } },
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible leaf', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111], incompressible: false },
children: [
{ element: { elements: [1111], incompressible: true } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible branch', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111 }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11], incompressible: false },
children: [
{ element: { elements: [111, 1111], incompressible: true } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible chain', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11], incompressible: false },
children: [
{
element: { elements: [111], incompressible: true },
children: [
{ element: { elements: [1111], incompressible: true } }
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible tree', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, incompressible: true, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{
element: { elements: [11], incompressible: true },
children: [
{
element: { elements: [111], incompressible: true },
children: [
{ element: { elements: [1111], incompressible: true } }
]
}
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
});
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
arr.splice(start, deleteCount, ...elements);
}
};
}
function toArray<T>(list: ITreeNode<ICompressedTreeNode<T>>[]): T[][] {
return list.map(i => i.element.elements);
}
suite('CompressedObjectTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
});
test('flat', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{ element: 0 },
{ element: 1 },
{ element: 2 }
]));
assert.deepEqual(toArray(list), [[0], [1], [2]]);
assert.equal(model.size, 3);
model.setChildren(null, Iterator.fromArray([
{ element: 3 },
{ element: 4 },
{ element: 5 },
]));
assert.deepEqual(toArray(list), [[3], [4], [5]]);
assert.equal(model.size, 3);
model.setChildren(null, Iterator.empty());
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('nested', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{
element: 0, children: Iterator.fromArray([
{ element: 10 },
{ element: 11 },
{ element: 12 },
])
},
{ element: 1 },
{ element: 2 }
]));
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [1], [2]]);
assert.equal(model.size, 6);
model.setChildren(12, Iterator.fromArray([
{ element: 120 },
{ element: 121 }
]));
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [120], [121], [1], [2]]);
assert.equal(model.size, 8);
model.setChildren(0, Iterator.empty());
assert.deepEqual(toArray(list), [[0], [1], [2]]);
assert.equal(model.size, 3);
model.setChildren(null, Iterator.empty());
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('compressed', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{
element: 1, children: Iterator.fromArray([{
element: 11, children: Iterator.fromArray([{
element: 111, children: Iterator.fromArray([
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
])
}])
}])
}
]));
assert.deepEqual(toArray(list), [[1, 11, 111], [1111], [1112], [1113]]);
assert.equal(model.size, 6);
model.setChildren(11, Iterator.fromArray([
{ element: 111 },
{ element: 112 },
{ element: 113 },
]));
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113]]);
assert.equal(model.size, 5);
model.setChildren(113, Iterator.fromArray([
{ element: 1131 }
]));
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131]]);
assert.equal(model.size, 6);
model.setChildren(1131, Iterator.fromArray([
{ element: 1132 }
]));
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131, 1132]]);
assert.equal(model.size, 7);
model.setChildren(1131, Iterator.fromArray([
{ element: 1132 },
{ element: 1133 },
]));
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131], [1132], [1133]]);
assert.equal(model.size, 8);
});
});
});

View File

@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Iterator } from 'vs/base/common/iterator';
suite('Iterator', () => {
test('concat', () => {
const first = Iterator.fromArray([1, 2, 3]);
const second = Iterator.fromArray([4, 5, 6]);
const third = Iterator.fromArray([7, 8, 9]);
const actualIterator = Iterator.concat(first, second, third);
const actual = Iterator.collect(actualIterator);
assert.deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
});
});