Skip to content

Commit 94343c8

Browse files
committed
use iterators in tree model
1 parent 66d7ae8 commit 94343c8

3 files changed

Lines changed: 108 additions & 122 deletions

File tree

src/vs/base/browser/ui/list/treeModel.ts

Lines changed: 30 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
'use strict';
77

88
import { ISpliceable } from 'vs/base/common/sequence';
9-
import { Tree } from 'vs/base/common/tree';
10-
import { tail2 } from 'vs/base/common/arrays';
11-
import { Trie } from 'vs/base/browser/ui/list/trie';
9+
import { IIterator, map, collect, forEach, iter } from 'vs/base/common/iterator';
10+
11+
/**
12+
* TODO:
13+
* remove trie
14+
* remote base tree
15+
*/
1216

1317
export interface ITreeElement<T> {
1418
readonly element: T;
15-
readonly children: ITreeElement<T>[];
19+
readonly children: IIterator<ITreeElement<T>>;
1620
}
1721

1822
export interface ITreeListElement<T> {
@@ -22,74 +26,78 @@ export interface ITreeListElement<T> {
2226

2327
class TreeNode<T> implements ITreeListElement<T> {
2428

25-
static createRoot<T>(): TreeNode<T> {
26-
const node = new TreeNode<T>();
29+
static createRoot<T>(element: T): TreeNode<T> {
30+
const node = new TreeNode<T>(element);
2731
node.children = [];
2832
node.count = 1;
2933
node.depth = 0;
3034
return node;
3135
}
3236

3337
static createNode<T>(treeElement: ITreeElement<T>, depth: number, list: ITreeListElement<T>[]): TreeNode<T> {
34-
const node = new TreeNode<T>();
38+
const node = new TreeNode<T>(treeElement.element);
3539
list.push(node);
3640

3741
let count = 1;
3842
const children: TreeNode<T>[] = [];
3943

40-
for (const childItem of treeElement.children) {
41-
const node = TreeNode.createNode<T>(childItem, depth + 1, list);
44+
forEach(treeElement.children, child => {
45+
const node = TreeNode.createNode<T>(child, depth + 1, list);
4246
children.push(node);
4347
count += node.count;
44-
}
48+
});
4549

46-
node.element = treeElement.element;
4750
node.children = children;
4851
node.count = count;
4952
node.depth = depth;
5053

5154
return node;
5255
}
5356

54-
element: T;
5557
children: TreeNode<T>[];
5658
count: number;
5759
depth: number;
5860

59-
private constructor() { }
61+
private constructor(public element: T) { }
6062

61-
splice(index: number, deleteCount: number, treeElements: ITreeElement<T>[]): { listDeleteCount: number; listElements: ITreeListElement<T>[]; deletedNodes: TreeNode<T>[]; } {
63+
splice(index: number, deleteCount: number, toInsert: IIterator<ITreeElement<T>>): { listDeleteCount: number; listElements: ITreeListElement<T>[]; deletedNodes: TreeNode<T>[]; } {
6264
const listElements = [] as ITreeListElement<T>[];
6365

64-
const added = treeElements.map(e => TreeNode.createNode<T>(e, this.depth + 1, listElements));
65-
const listAddCount = added.reduce((r, n) => r + n.count, 0);
66-
67-
const deletedNodes = this.children.splice(index, deleteCount, ...added);
66+
const nodesToInsert = collect(map(toInsert, e => TreeNode.createNode<T>(e, this.depth + 1, listElements)));
67+
const listAddCount = nodesToInsert.reduce((r, n) => r + n.count, 0);
68+
const deletedNodes = this.children.splice(index, deleteCount, ...nodesToInsert);
6869
const listDeleteCount = deletedNodes.reduce((r, n) => r + n.count, 0);
6970

7071
this.count += listAddCount - listDeleteCount;
7172
return { listDeleteCount, listElements, deletedNodes };
7273
}
7374
}
7475

76+
function asTreeElement<T>(node: TreeNode<T>): ITreeElement<T> {
77+
return {
78+
element: node.element,
79+
children: map(iter(node.children), asTreeElement)
80+
};
81+
}
82+
7583
export class TreeModel<T> {
7684

77-
private root = TreeNode.createRoot<T>();
85+
private root = TreeNode.createRoot<T>(undefined);
7886

7987
constructor(private spliceable: ISpliceable<ITreeListElement<T>>) { }
8088

81-
splice(start: number[], deleteCount: number, elements: ITreeElement<T>[] = []): ITreeElement<T>[] {
89+
splice(start: number[], deleteCount: number, toInsert: IIterator<ITreeElement<T>>): IIterator<ITreeElement<T>> {
8290
if (start.length === 0) {
8391
throw new Error('Invalid tree location');
8492
}
8593

8694
const { parentNode, parentListIndex } = this.findParentNode(start);
8795
const lastIndex = start[start.length - 1];
88-
const { listDeleteCount, listElements, deletedNodes } = parentNode.splice(lastIndex, deleteCount, elements);
96+
const { listDeleteCount, listElements, deletedNodes } = parentNode.splice(lastIndex, deleteCount, toInsert);
8997

9098
this.spliceable.splice(parentListIndex + lastIndex, listDeleteCount, listElements);
9199

92-
return deletedNodes;
100+
return map(iter(deletedNodes), asTreeElement);
93101
}
94102

95103
private findParentNode(location: number[], node: TreeNode<T> = this.root, listIndex: number = 0): { parentNode: TreeNode<T>; parentListIndex: number } {
@@ -108,61 +116,3 @@ export class TreeModel<T> {
108116
return this.findParentNode(rest, node.children[i], listIndex + 1);
109117
}
110118
}
111-
112-
export interface ICollapsibleTreeElement<T> {
113-
element: T;
114-
collapsed: boolean;
115-
}
116-
117-
export class CollapsibleTreeModel<T> {
118-
119-
private model = new Tree<ICollapsibleTreeElement<T>>();
120-
private collapsedElements = new Trie<number, ITreeElement<T>>();
121-
private visibleModel: TreeModel<T>;
122-
123-
constructor(spliceable: ISpliceable<ITreeListElement<T>>) {
124-
this.visibleModel = new TreeModel<T>(spliceable);
125-
}
126-
127-
splice(start: number[], deleteCount: number, items: ITreeElement<ICollapsibleTreeElement<T>>[]): void {
128-
// const elementPath = this.model.getElementPath(start);
129-
130-
this.model.splice(start, deleteCount, items);
131-
// this.visibleModel.splice(start, deleteCount, items);
132-
}
133-
134-
setCollapsed(location: number[], collapsed: boolean): void {
135-
const elementPath = this.model.getElementPath(location);
136-
const [pathToElement, collapsibleElement] = tail2(elementPath);
137-
138-
if (collapsibleElement.collapsed === collapsed) {
139-
return;
140-
}
141-
142-
collapsibleElement.collapsed = collapsed;
143-
144-
if (pathToElement.some(e => e.collapsed)) {
145-
return;
146-
}
147-
148-
if (collapsed) {
149-
const collapsedElement: ITreeElement<T> = {
150-
element: collapsibleElement.element,
151-
children: []
152-
};
153-
154-
const [element] = this.visibleModel.splice(location, 1, [collapsedElement]);
155-
this.collapsedElements.set(location, element);
156-
} else {
157-
const expandedElement = this.collapsedElements.delete(location);
158-
this.visibleModel.splice(location, 1, [expandedElement]);
159-
}
160-
161-
162-
// gotta reflect the state on the visibleModel
163-
}
164-
165-
isCollapsed(location: number[]): boolean {
166-
return this.model.getElement(location).collapsed;
167-
}
168-
}

src/vs/base/common/iterator.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,43 @@ export interface IIteratorResult<T> {
1010
readonly value: T | undefined;
1111
}
1212

13-
export interface IIterator<E> {
14-
next(): IIteratorResult<E>;
13+
export interface IIterator<T> {
14+
next(): IIteratorResult<T>;
15+
}
16+
17+
export function iter<T>(array: T[]): IIterator<T> {
18+
let index = 0;
19+
20+
return {
21+
next(): IIteratorResult<T> {
22+
if (index === array.length) {
23+
return { done: true, value: undefined };
24+
}
25+
26+
return { done: false, value: array[index++] };
27+
}
28+
};
29+
}
30+
31+
export function map<T, R>(iterator: IIterator<T>, fn: (t: T) => R): IIterator<R> {
32+
return {
33+
next() {
34+
const { done, value } = iterator.next();
35+
return { done, value: done ? undefined : fn(value) };
36+
}
37+
};
38+
}
39+
40+
export function forEach<T>(iterator: IIterator<T>, fn: (t: T) => void): void {
41+
for (let next = iterator.next(); !next.done; next = iterator.next()) {
42+
fn(next.value);
43+
}
44+
}
45+
46+
export function collect<T>(iterator: IIterator<T>): T[] {
47+
const result: T[] = [];
48+
forEach(iterator, value => result.push(value));
49+
return result;
1550
}
1651

1752
export interface INextIterator<T> {

src/vs/base/test/browser/ui/list/treeModel.test.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as assert from 'assert';
77
import { TreeModel, ITreeListElement } from 'vs/base/browser/ui/list/treeModel';
88
import { ISpliceable } from 'vs/base/browser/ui/list/splice';
9+
import { iter } from 'vs/base/common/iterator';
910

1011
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
1112
return {
@@ -28,11 +29,11 @@ suite('TreeModel2', () => {
2829
const list = [] as ITreeListElement<number>[];
2930
const model = new TreeModel<number>(toSpliceable(list));
3031

31-
model.splice([0], 0, [
32-
{ element: 0, children: [] },
33-
{ element: 1, children: [] },
34-
{ element: 2, children: [] }
35-
]);
32+
model.splice([0], 0, iter([
33+
{ element: 0, children: iter([]) },
34+
{ element: 1, children: iter([]) },
35+
{ element: 2, children: iter([]) }
36+
]));
3637

3738
assert.deepEqual(list.length, 3);
3839
assert.deepEqual(list[0].element, 0);
@@ -47,17 +48,17 @@ suite('TreeModel2', () => {
4748
const list = [] as ITreeListElement<number>[];
4849
const model = new TreeModel<number>(toSpliceable(list));
4950

50-
model.splice([0], 0, [
51+
model.splice([0], 0, iter([
5152
{
52-
element: 0, children: [
53-
{ element: 10, children: [] },
54-
{ element: 11, children: [] },
55-
{ element: 12, children: [] },
56-
]
53+
element: 0, children: iter([
54+
{ element: 10, children: iter([]) },
55+
{ element: 11, children: iter([]) },
56+
{ element: 12, children: iter([]) },
57+
])
5758
},
58-
{ element: 1, children: [] },
59-
{ element: 2, children: [] }
60-
]);
59+
{ element: 1, children: iter([]) },
60+
{ element: 2, children: iter([]) }
61+
]));
6162

6263
assert.deepEqual(list.length, 6);
6364
assert.deepEqual(list[0].element, 0);
@@ -78,13 +79,13 @@ suite('TreeModel2', () => {
7879
const list = [] as ITreeListElement<number>[];
7980
const model = new TreeModel<number>(toSpliceable(list));
8081

81-
model.splice([0], 0, [
82-
{ element: 0, children: [] },
83-
{ element: 1, children: [] },
84-
{ element: 2, children: [] }
85-
]);
82+
model.splice([0], 0, iter([
83+
{ element: 0, children: iter([]) },
84+
{ element: 1, children: iter([]) },
85+
{ element: 2, children: iter([]) }
86+
]));
8687

87-
model.splice([0], 3, []);
88+
model.splice([0], 3, iter([]));
8889

8990
assert.equal(list.length, 0);
9091
});
@@ -93,19 +94,19 @@ suite('TreeModel2', () => {
9394
const list = [] as ITreeListElement<number>[];
9495
const model = new TreeModel<number>(toSpliceable(list));
9596

96-
model.splice([0], 0, [
97+
model.splice([0], 0, iter([
9798
{
98-
element: 0, children: [
99-
{ element: 10, children: [] },
100-
{ element: 11, children: [] },
101-
{ element: 12, children: [] },
102-
]
99+
element: 0, children: iter([
100+
{ element: 10, children: iter([]) },
101+
{ element: 11, children: iter([]) },
102+
{ element: 12, children: iter([]) },
103+
])
103104
},
104-
{ element: 1, children: [] },
105-
{ element: 2, children: [] }
106-
]);
105+
{ element: 1, children: iter([]) },
106+
{ element: 2, children: iter([]) }
107+
]));
107108

108-
model.splice([0, 1], 1, []);
109+
model.splice([0, 1], 1, iter([]));
109110

110111
assert.deepEqual(list.length, 5, 'list has 5 elements');
111112
assert.deepEqual(list[0].element, 0);
@@ -124,19 +125,19 @@ suite('TreeModel2', () => {
124125
const list = [] as ITreeListElement<number>[];
125126
const model = new TreeModel<number>(toSpliceable(list));
126127

127-
model.splice([0], 0, [
128+
model.splice([0], 0, iter([
128129
{
129-
element: 0, children: [
130-
{ element: 10, children: [] },
131-
{ element: 11, children: [] },
132-
{ element: 12, children: [] },
133-
]
130+
element: 0, children: iter([
131+
{ element: 10, children: iter([]) },
132+
{ element: 11, children: iter([]) },
133+
{ element: 12, children: iter([]) },
134+
])
134135
},
135-
{ element: 1, children: [] },
136-
{ element: 2, children: [] }
137-
]);
136+
{ element: 1, children: iter([]) },
137+
{ element: 2, children: iter([]) }
138+
]));
138139

139-
model.splice([0], 1, []);
140+
model.splice([0], 1, iter([]));
140141

141142
assert.deepEqual(list.length, 2, 'list has 2 elements only');
142143
assert.deepEqual(list[0].element, 1);

0 commit comments

Comments
 (0)