Skip to content

Commit 8e0dbd5

Browse files
committed
finish resource tree
1 parent 1d3ae86 commit 8e0dbd5

2 files changed

Lines changed: 93 additions & 42 deletions

File tree

src/vs/base/common/resourceTree.ts

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,107 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { URI } from 'vs/base/common/uri';
7+
import { memoize } from 'vs/base/common/decorators';
8+
import * as paths from 'vs/base/common/path';
9+
import { Iterator } from 'vs/base/common/iterator';
710

8-
export const enum NodeType {
9-
Branch,
10-
Leaf
11+
export interface ILeafNode<T> {
12+
readonly path: string;
13+
readonly name: string;
14+
readonly element: T;
1115
}
1216

13-
export interface LeafNode<T> {
14-
readonly type: NodeType.Leaf;
15-
readonly element: T;
17+
export interface IBranchNode<T> {
18+
readonly path: string;
19+
readonly name: string;
20+
readonly size: number;
21+
readonly children: Iterator<INode<T>>;
22+
get(childName: string): INode<T> | undefined;
23+
}
24+
25+
export type INode<T> = IBranchNode<T> | ILeafNode<T>;
26+
27+
export function isBranchNode<T>(obj: any): obj is IBranchNode<T> {
28+
return obj instanceof BranchNode;
1629
}
1730

18-
export interface BranchNode<T> {
19-
readonly type: NodeType.Branch;
20-
readonly children: Map<string, Node<T>>;
31+
// Internals
32+
33+
class Node {
34+
35+
@memoize
36+
get name(): string { return paths.posix.basename(this.path); }
37+
38+
constructor(readonly path: string) { }
2139
}
2240

23-
export type Node<T> = BranchNode<T> | LeafNode<T>;
41+
class BranchNode<T> extends Node implements IBranchNode<T> {
42+
43+
private _children = new Map<string, BranchNode<T> | LeafNode<T>>();
44+
45+
get size(): number {
46+
return this._children.size;
47+
}
48+
49+
get children(): Iterator<BranchNode<T> | LeafNode<T>> {
50+
return Iterator.fromIterableIterator(this._children.values());
51+
}
52+
53+
get(path: string): BranchNode<T> | LeafNode<T> | undefined {
54+
return this._children.get(path);
55+
}
56+
57+
set(path: string, child: BranchNode<T> | LeafNode<T>): void {
58+
this._children.set(path, child);
59+
}
60+
61+
delete(path: string): void {
62+
this._children.delete(path);
63+
}
64+
}
65+
66+
class LeafNode<T> extends Node implements ILeafNode<T> {
67+
68+
constructor(path: string, readonly element: T) {
69+
super(path);
70+
}
71+
}
2472

2573
export class ResourceTree<T extends NonNullable<any>> {
2674

27-
readonly root: BranchNode<T> = { type: NodeType.Branch, children: new Map() };
75+
readonly root = new BranchNode<T>('');
2876

2977
constructor() { }
3078

3179
add(uri: URI, element: T): void {
3280
const parts = uri.fsPath.split(/[\\\/]/).filter(p => !!p);
3381
let node = this.root;
82+
let path = this.root.path;
3483

3584
for (let i = 0; i < parts.length; i++) {
3685
const name = parts[i];
37-
let child = node.children.get(name);
86+
path = path + '/' + name;
87+
88+
let child = node.get(name);
3889

3990
if (!child) {
4091
if (i < parts.length - 1) {
41-
child = { type: NodeType.Branch, children: new Map() };
42-
node.children.set(name, child);
92+
child = new BranchNode(path);
93+
node.set(name, child);
4394
} else {
44-
child = { type: NodeType.Leaf, element };
45-
node.children.set(name, child);
95+
child = new LeafNode(path, element);
96+
node.set(name, child);
4697
return;
4798
}
4899
}
49100

50-
if (child.type === NodeType.Leaf) {
101+
if (!(child instanceof BranchNode)) {
51102
if (i < parts.length - 1) {
52103
throw new Error('Inconsistent tree: can\'t override leaf with branch.');
53104
}
54105

55106
// replace
56-
node.children.set(name, { type: NodeType.Leaf, element });
107+
node.set(name, new LeafNode(path, element));
57108
return;
58109
} else if (i === parts.length - 1) {
59110
throw new Error('Inconsistent tree: can\'t override branch with leaf.');
@@ -70,34 +121,34 @@ export class ResourceTree<T extends NonNullable<any>> {
70121

71122
private _delete(node: BranchNode<T>, parts: string[], index: number): T | undefined {
72123
const name = parts[index];
73-
const child = node.children.get(name);
124+
const child = node.get(name);
74125

75126
if (!child) {
76127
return undefined;
77128
}
78129

79130
// not at end
80131
if (index < parts.length - 1) {
81-
if (child.type === NodeType.Leaf) {
82-
throw new Error('Inconsistent tree: Expected a branch, found a leaf instead.');
83-
} else {
132+
if (child instanceof BranchNode) {
84133
const result = this._delete(child, parts, index + 1);
85134

86-
if (typeof result !== 'undefined' && child.children.size === 0) {
87-
node.children.delete(name);
135+
if (typeof result !== 'undefined' && child.size === 0) {
136+
node.delete(name);
88137
}
89138

90139
return result;
140+
} else {
141+
throw new Error('Inconsistent tree: Expected a branch, found a leaf instead.');
91142
}
92143
}
93144

94145
//at end
95-
if (child.type === NodeType.Branch) {
146+
if (child instanceof BranchNode) {
96147
// TODO: maybe we can allow this
97148
throw new Error('Inconsistent tree: Expected a leaf, found a branch instead.');
98149
}
99150

100-
node.children.delete(name);
151+
node.delete(name);
101152
return child.element;
102153
}
103154
}

src/vs/base/test/common/resourceTree.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,46 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as assert from 'assert';
7-
import { ResourceTree, NodeType, BranchNode, LeafNode } from 'vs/base/common/resourceTree';
7+
import { ResourceTree, IBranchNode, ILeafNode, isBranchNode } from 'vs/base/common/resourceTree';
88
import { URI } from 'vs/base/common/uri';
99

1010
suite('ResourceTree', function () {
1111
test('ctor', function () {
1212
const tree = new ResourceTree<string>();
13-
assert.equal(tree.root.type, NodeType.Branch);
14-
assert.equal(tree.root.children.size, 0);
13+
assert(isBranchNode(tree.root));
14+
assert.equal(tree.root.size, 0);
1515
});
1616

1717
test('simple', function () {
1818
const tree = new ResourceTree<string>();
1919

2020
tree.add(URI.file('/foo/bar.txt'), 'bar contents');
21-
assert.equal(tree.root.type, NodeType.Branch);
22-
assert.equal(tree.root.children.size, 1);
21+
assert(isBranchNode(tree.root));
22+
assert.equal(tree.root.size, 1);
2323

24-
let foo = tree.root.children.get('foo') as BranchNode<string>;
24+
let foo = tree.root.get('foo') as IBranchNode<string>;
2525
assert(foo);
26-
assert.equal(foo.type, NodeType.Branch);
27-
assert.equal(foo.children.size, 1);
26+
assert(isBranchNode(foo));
27+
assert.equal(foo.size, 1);
2828

29-
let bar = foo.children.get('bar.txt') as LeafNode<string>;
29+
let bar = foo.get('bar.txt') as ILeafNode<string>;
3030
assert(bar);
31-
assert.equal(bar.type, NodeType.Leaf);
31+
assert(!isBranchNode(bar));
3232
assert.equal(bar.element, 'bar contents');
3333

3434
tree.add(URI.file('/hello.txt'), 'hello contents');
35-
assert.equal(tree.root.children.size, 2);
35+
assert.equal(tree.root.size, 2);
3636

37-
let hello = tree.root.children.get('hello.txt') as LeafNode<string>;
37+
let hello = tree.root.get('hello.txt') as ILeafNode<string>;
3838
assert(hello);
39-
assert.equal(hello.type, NodeType.Leaf);
39+
assert(!isBranchNode(hello));
4040
assert.equal(hello.element, 'hello contents');
4141

4242
tree.delete(URI.file('/foo/bar.txt'));
43-
assert.equal(tree.root.children.size, 1);
44-
hello = tree.root.children.get('hello.txt') as LeafNode<string>;
43+
assert.equal(tree.root.size, 1);
44+
hello = tree.root.get('hello.txt') as ILeafNode<string>;
4545
assert(hello);
46-
assert.equal(hello.type, NodeType.Leaf);
46+
assert(!isBranchNode(hello));
4747
assert.equal(hello.element, 'hello contents');
4848
});
4949
});

0 commit comments

Comments
 (0)