Skip to content

Commit 66d7ae8

Browse files
committed
move trie out, flesh out setCollapsed
1 parent 967d43e commit 66d7ae8

5 files changed

Lines changed: 252 additions & 234 deletions

File tree

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

Lines changed: 55 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -6,59 +6,21 @@
66
'use strict';
77

88
import { ISpliceable } from 'vs/base/common/sequence';
9-
import { IIterator } from 'vs/base/common/iterator';
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';
1012

1113
export interface ITreeElement<T> {
1214
readonly element: T;
1315
readonly children: ITreeElement<T>[];
1416
}
1517

16-
export interface ITreeNode<T> {
18+
export interface ITreeListElement<T> {
1719
readonly element: T;
1820
readonly depth: number;
1921
}
2022

21-
class TreeNodeIterator<T> implements IIterator<ITreeElement<T>> {
22-
23-
private stack: TreeNode<T>[] = [];
24-
25-
constructor(node: TreeNode<T>) {
26-
this.stack.push(node);
27-
}
28-
29-
next(): { readonly done: boolean; readonly value: ITreeElement<T>; } {
30-
const value = this.stack.pop();
31-
32-
for (let i = value.children.length - 1; i >= 0; i--) {
33-
this.stack.push(value.children[i]);
34-
}
35-
36-
return { done: this.stack.length === 0, value };
37-
}
38-
}
39-
40-
class TreeNodesIterator<T> implements IIterator<ITreeElement<T>> {
41-
42-
private index = 0;
43-
44-
constructor(private iterators: TreeNodeIterator<T>[]) { }
45-
46-
next(): { readonly done: boolean; readonly value: ITreeElement<T> | undefined; } {
47-
if (this.index >= this.iterators.length) {
48-
return { done: true, value: undefined };
49-
}
50-
51-
const result = this.iterators[this.index].next();
52-
53-
if (result.done) {
54-
this.index++;
55-
}
56-
57-
return result;
58-
}
59-
}
60-
61-
class TreeNode<T> implements ITreeNode<T> {
23+
class TreeNode<T> implements ITreeListElement<T> {
6224

6325
static createRoot<T>(): TreeNode<T> {
6426
const node = new TreeNode<T>();
@@ -68,15 +30,15 @@ class TreeNode<T> implements ITreeNode<T> {
6830
return node;
6931
}
7032

71-
static createNode<T>(treeElement: ITreeElement<T>, depth: number, list: ITreeNode<T>[]): TreeNode<T> {
33+
static createNode<T>(treeElement: ITreeElement<T>, depth: number, list: ITreeListElement<T>[]): TreeNode<T> {
7234
const node = new TreeNode<T>();
7335
list.push(node);
7436

7537
let count = 1;
7638
const children: TreeNode<T>[] = [];
7739

78-
for (const element of treeElement.children) {
79-
const node = TreeNode.createNode<T>(element, depth + 1, list);
40+
for (const childItem of treeElement.children) {
41+
const node = TreeNode.createNode<T>(childItem, depth + 1, list);
8042
children.push(node);
8143
count += node.count;
8244
}
@@ -96,51 +58,38 @@ class TreeNode<T> implements ITreeNode<T> {
9658

9759
private constructor() { }
9860

99-
splice(index: number, deleteCount: number, elements: ITreeElement<T>[]): { listDeleteCount: number; listElements: ITreeNode<T>[]; deletedIterator: IIterator<ITreeElement<T>>; } {
100-
const listElements = [] as ITreeNode<T>[];
61+
splice(index: number, deleteCount: number, treeElements: ITreeElement<T>[]): { listDeleteCount: number; listElements: ITreeListElement<T>[]; deletedNodes: TreeNode<T>[]; } {
62+
const listElements = [] as ITreeListElement<T>[];
10163

102-
const added = elements.map(e => TreeNode.createNode<T>(e, this.depth + 1, listElements));
64+
const added = treeElements.map(e => TreeNode.createNode<T>(e, this.depth + 1, listElements));
10365
const listAddCount = added.reduce((r, n) => r + n.count, 0);
10466

105-
const deleted = this.children.splice(index, deleteCount, ...added);
106-
const deletedIterator = new TreeNodesIterator(deleted.map(node => new TreeNodeIterator(node)));
107-
const listDeleteCount = deleted.reduce((r, n) => r + n.count, 0);
67+
const deletedNodes = this.children.splice(index, deleteCount, ...added);
68+
const listDeleteCount = deletedNodes.reduce((r, n) => r + n.count, 0);
10869

10970
this.count += listAddCount - listDeleteCount;
110-
return { listDeleteCount, listElements, deletedIterator };
71+
return { listDeleteCount, listElements, deletedNodes };
11172
}
11273
}
11374

11475
export class TreeModel<T> {
11576

11677
private root = TreeNode.createRoot<T>();
11778

118-
constructor(private spliceable: ISpliceable<ITreeNode<T>>) { }
79+
constructor(private spliceable: ISpliceable<ITreeListElement<T>>) { }
11980

120-
splice(start: number[], deleteCount: number, elements: ITreeElement<T>[] = []): IIterator<ITreeElement<T>> {
81+
splice(start: number[], deleteCount: number, elements: ITreeElement<T>[] = []): ITreeElement<T>[] {
12182
if (start.length === 0) {
12283
throw new Error('Invalid tree location');
12384
}
12485

12586
const { parentNode, parentListIndex } = this.findParentNode(start);
12687
const lastIndex = start[start.length - 1];
127-
const { listDeleteCount, listElements, deletedIterator } = parentNode.splice(lastIndex, deleteCount, elements);
88+
const { listDeleteCount, listElements, deletedNodes } = parentNode.splice(lastIndex, deleteCount, elements);
12889

12990
this.spliceable.splice(parentListIndex + lastIndex, listDeleteCount, listElements);
13091

131-
return deletedIterator;
132-
}
133-
134-
getElement(location: number[]): T | undefined {
135-
const node = this.findNode(location);
136-
return node && node.element;
137-
}
138-
139-
private findNode(location: number[]): TreeNode<T> {
140-
const { parentNode } = this.findParentNode(location);
141-
const lastIndex = location[location.length - 1];
142-
143-
return parentNode.children[lastIndex];
92+
return deletedNodes;
14493
}
14594

14695
private findParentNode(location: number[], node: TreeNode<T> = this.root, listIndex: number = 0): { parentNode: TreeNode<T>; parentListIndex: number } {
@@ -151,6 +100,7 @@ export class TreeModel<T> {
151100
const [i, ...rest] = location;
152101
const limit = Math.min(i, node.children.length);
153102

103+
// TODO@joao perf!
154104
for (let j = 0; j < limit; j++) {
155105
listIndex += node.children[j].count;
156106
}
@@ -159,122 +109,60 @@ export class TreeModel<T> {
159109
}
160110
}
161111

162-
// type TrieNode<T> = TrieNode<T>[] | T;
163-
164-
// [[], [], T]
165-
// { key: 1, children: {} }
166-
// { 1: { 2: {} } }
167-
168-
type TrieNode<K, T> = { value: T | undefined, children: Map<K, TrieNode<K, T>> };
112+
export interface ICollapsibleTreeElement<T> {
113+
element: T;
114+
collapsed: boolean;
115+
}
169116

170-
export class Trie<K, T> {
117+
export class CollapsibleTreeModel<T> {
171118

172-
private root: TrieNode<K, T>;
119+
private model = new Tree<ICollapsibleTreeElement<T>>();
120+
private collapsedElements = new Trie<number, ITreeElement<T>>();
121+
private visibleModel: TreeModel<T>;
173122

174-
constructor() {
175-
this.clear();
123+
constructor(spliceable: ISpliceable<ITreeListElement<T>>) {
124+
this.visibleModel = new TreeModel<T>(spliceable);
176125
}
177126

178-
set(path: K[], element: T): void {
179-
if (path.length === 0) {
180-
throw new Error('Invalid path length');
181-
}
182-
183-
let node = this.root;
184-
185-
for (const key of path) {
186-
let child = node.children.get(key);
187-
188-
if (!child) {
189-
child = { value: undefined, children: new Map<K, TrieNode<K, T>>() };
190-
node.children.set(key, child);
191-
}
127+
splice(start: number[], deleteCount: number, items: ITreeElement<ICollapsibleTreeElement<T>>[]): void {
128+
// const elementPath = this.model.getElementPath(start);
192129

193-
node = child;
194-
}
195-
196-
node.value = element;
130+
this.model.splice(start, deleteCount, items);
131+
// this.visibleModel.splice(start, deleteCount, items);
197132
}
198133

199-
get(path: K[]): T | undefined {
200-
if (path.length === 0) {
201-
throw new Error('Invalid path length');
202-
}
203-
204-
let node = this.root;
134+
setCollapsed(location: number[], collapsed: boolean): void {
135+
const elementPath = this.model.getElementPath(location);
136+
const [pathToElement, collapsibleElement] = tail2(elementPath);
205137

206-
for (const key of path) {
207-
let child = node.children.get(key);
208-
209-
if (!child) {
210-
return undefined;
211-
}
212-
213-
node = child;
138+
if (collapsibleElement.collapsed === collapsed) {
139+
return;
214140
}
215141

216-
return node.value;
217-
}
218-
219-
delete(path: K[]): void {
220-
if (path.length === 0) {
221-
throw new Error('Invalid path length');
222-
}
223-
224-
let nodePath: { key: K, node: TrieNode<K, T> }[] = [];
225-
let node = this.root;
226-
227-
for (const key of path) {
228-
let child = node.children.get(key);
142+
collapsibleElement.collapsed = collapsed;
229143

230-
if (!child) {
231-
return;
232-
}
233-
234-
nodePath.push({ key, node });
235-
node = child;
144+
if (pathToElement.some(e => e.collapsed)) {
145+
return;
236146
}
237147

238-
for (let i = nodePath.length - 1; i >= 0; i--) {
239-
const { key, node } = nodePath[i];
240-
241-
node.children.delete(key);
242-
243-
if (Object.keys(node.children).length > 0) {
244-
return;
245-
}
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]);
246159
}
247-
}
248160

249-
clear(): void {
250-
this.root = { value: undefined, children: new Map<K, TrieNode<K, T>>() };
251-
}
252-
}
253-
254-
export class CollapsibleTreeModel<T> {
255161

256-
private model: TreeModel<T>;
257-
258-
constructor(spliceable: ISpliceable<ITreeNode<T>>) {
259-
this.model = new TreeModel<T>(spliceable);
260-
}
261-
262-
splice(start: number[], deleteCount: number, elements: ITreeElement<T>[]): void {
263-
this.model.splice(start, deleteCount, elements);
162+
// gotta reflect the state on the visibleModel
264163
}
265164

266-
expand(location: number[]): boolean {
267-
268-
// this.model.splice(start, deleteCount, elements);
269-
// const element = this.model.getElement()
270-
return false;
271-
}
272-
273-
collapse(location: number[]): boolean {
274-
// const element = this.model.getElement(location);
275-
// const collapsedElement: ITreeElement<T> = { element, children: [] };
276-
// const deletedIterator = this.model.splice(location, 1, [collapsedElement]);
277-
278-
return false;
165+
isCollapsed(location: number[]): boolean {
166+
return this.model.getElement(location).collapsed;
279167
}
280168
}

0 commit comments

Comments
 (0)