indexTreeModel.ts 14.9 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import { ISpliceable } from 'vs/base/common/sequence';
J
Joao Moreno 已提交
7
import { Iterator, ISequence } from 'vs/base/common/iterator';
J
Joao Moreno 已提交
8
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
J
Joao Moreno 已提交
9
import { tail2 } from 'vs/base/common/arrays';
10
import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
J
Joao Moreno 已提交
11

J
Joao Moreno 已提交
12
interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
13 14
	readonly parent: IMutableTreeNode<T, TFilterData> | undefined;
	readonly children: IMutableTreeNode<T, TFilterData>[];
15
	collapsible: boolean;
J
Joao Moreno 已提交
16
	collapsed: boolean;
J
Joao Moreno 已提交
17
	renderNodeCount: number;
J
Joao Moreno 已提交
18
	visible: boolean;
J
Joao Moreno 已提交
19
	filterData: TFilterData | undefined;
J
Joao Moreno 已提交
20
}
J
Joao Moreno 已提交
21

J
Joao Moreno 已提交
22
function isFilterResult<T>(obj: any): obj is ITreeFilterDataResult<T> {
23 24 25
	return typeof obj === 'object' && 'visibility' in obj && 'data' in obj;
}

J
Joao Moreno 已提交
26
function treeNodeToElement<T>(node: IMutableTreeNode<T, any>): ITreeElement<T> {
J
Joao Moreno 已提交
27
	const { element, collapsed } = node;
J
Joao Moreno 已提交
28
	const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement);
J
Joao Moreno 已提交
29 30

	return { element, children, collapsed };
J
Joao Moreno 已提交
31 32
}

33
function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility {
J
Joao Moreno 已提交
34
	switch (visibility) {
35 36 37
		case true: return TreeVisibility.Visible;
		case false: return TreeVisibility.Hidden;
		default: return visibility;
J
Joao Moreno 已提交
38 39 40
	}
}

J
Joao Moreno 已提交
41
export interface IIndexTreeModelOptions<T, TFilterData> {
J
Joao Moreno 已提交
42
	collapseByDefault?: boolean; // defaults to false
J
Joao Moreno 已提交
43 44 45
	filter?: ITreeFilter<T, TFilterData>;
}

J
Joao Moreno 已提交
46
export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = void> implements ITreeModel<T, TFilterData, number[]> {
47

J
Joao Moreno 已提交
48 49
	readonly rootRef = [];

J
Joao Moreno 已提交
50
	private root: IMutableTreeNode<T, TFilterData>;
51
	private eventBufferer = new EventBufferer();
J
Joao Moreno 已提交
52

53 54
	private _onDidChangeCollapseState = new Emitter<ICollapseStateChangeEvent<T, TFilterData>>();
	readonly onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event);
J
Joao Moreno 已提交
55 56 57

	private _onDidChangeRenderNodeCount = new Emitter<ITreeNode<T, TFilterData>>();
	readonly onDidChangeRenderNodeCount: Event<ITreeNode<T, TFilterData>> = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event);
58

J
Joao Moreno 已提交
59
	private collapseByDefault: boolean;
J
Joao Moreno 已提交
60
	private filter?: ITreeFilter<T, TFilterData>;
61

J
Joao Moreno 已提交
62
	constructor(private list: ISpliceable<ITreeNode<T, TFilterData>>, rootElement: T, options: IIndexTreeModelOptions<T, TFilterData> = {}) {
J
Joao Moreno 已提交
63
		this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault;
64
		this.filter = options.filter;
J
Joao Moreno 已提交
65

66 67
		// this.onDidChangeCollapseState(node => console.log(node.collapsed, node));

J
Joao Moreno 已提交
68 69 70 71 72 73 74 75 76 77 78
		this.root = {
			parent: undefined,
			element: rootElement,
			children: [],
			depth: 0,
			collapsible: false,
			collapsed: false,
			renderNodeCount: 0,
			visible: true,
			filterData: undefined
		};
79
	}
J
Joao Moreno 已提交
80

J
Joao Moreno 已提交
81 82 83 84 85 86 87
	splice(
		location: number[],
		deleteCount: number,
		toInsert?: ISequence<ITreeElement<T>>,
		onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
		onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
	): Iterator<ITreeElement<T>> {
J
Joao Moreno 已提交
88
		if (location.length === 0) {
J
Joao Moreno 已提交
89 90 91
			throw new Error('Invalid tree location');
		}

J
Joao Moreno 已提交
92
		const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location);
J
Joao Moreno 已提交
93
		const treeListElementsToInsert: ITreeNode<T, TFilterData>[] = [];
94
		const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode));
J
Joao Moreno 已提交
95 96

		const nodesToInsert: IMutableTreeNode<T, TFilterData>[] = [];
J
Joao Moreno 已提交
97
		let renderNodeCount = 0;
J
Joao Moreno 已提交
98 99 100

		Iterator.forEach(nodesToInsertIterator, node => {
			nodesToInsert.push(node);
J
Joao Moreno 已提交
101
			renderNodeCount += node.renderNodeCount;
J
Joao Moreno 已提交
102 103
		});

J
Joao Moreno 已提交
104 105
		const lastIndex = location[location.length - 1];
		const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert);
J
Joao Moreno 已提交
106

107
		if (revealed) {
J
Joao Moreno 已提交
108
			const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0);
109

J
Joao Moreno 已提交
110
			this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount);
111
			this.list.splice(listIndex, visibleDeleteCount, treeListElementsToInsert);
J
Joao Moreno 已提交
112
		}
J
Joao Moreno 已提交
113

114
		if (deletedNodes.length > 0 && onDidDeleteNode) {
J
Joao Moreno 已提交
115 116 117 118 119 120 121 122
			const visit = (node: ITreeNode<T, TFilterData>) => {
				onDidDeleteNode(node);
				node.children.forEach(visit);
			};

			deletedNodes.forEach(visit);
		}

J
Joao Moreno 已提交
123
		return Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement);
J
Joao Moreno 已提交
124 125
	}

J
Joao Moreno 已提交
126
	getListIndex(location: number[]): number {
J
Joao Moreno 已提交
127
		return this.getTreeNodeWithListIndex(location).listIndex;
J
Joao Moreno 已提交
128 129
	}

J
Joao Moreno 已提交
130 131 132 133 134 135 136 137
	isCollapsible(location: number[]): boolean {
		return this.getTreeNode(location).collapsible;
	}

	isCollapsed(location: number[]): boolean {
		return this.getTreeNode(location).collapsed;
	}

138
	setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean {
J
Joao Moreno 已提交
139
		const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
J
Joao Moreno 已提交
140 141 142 143 144

		if (typeof collapsed === 'undefined') {
			collapsed = !node.collapsed;
		}

145 146 147
		return this.eventBufferer.bufferEvents(() => {
			return this._setCollapsed(node, listIndex, revealed, collapsed!, recursive || false);
		});
J
Joao Moreno 已提交
148 149
	}

J
Joao Moreno 已提交
150
	private _setCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean {
151
		const result = this._setNodeCollapsed(node, collapsed, recursive, false);
J
Joao Moreno 已提交
152 153 154 155 156 157 158 159 160 161 162

		if (!revealed || !node.visible) {
			return result;
		}

		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;
J
Joao Moreno 已提交
163
	}
J
Joao Moreno 已提交
164

165
	private _setNodeCollapsed(node: IMutableTreeNode<T, TFilterData>, collapsed: boolean, recursive: boolean, deep: boolean): boolean {
J
Joao Moreno 已提交
166
		let result = node.collapsible && node.collapsed !== collapsed;
J
Joao Moreno 已提交
167

J
Joao Moreno 已提交
168 169
		if (node.collapsible) {
			node.collapsed = collapsed;
170 171 172 173

			if (result) {
				this._onDidChangeCollapseState.fire({ node, deep });
			}
J
Joao Moreno 已提交
174
		}
J
Joao Moreno 已提交
175

J
Joao Moreno 已提交
176 177
		if (recursive) {
			for (const child of node.children) {
178
				result = this._setNodeCollapsed(child, collapsed, true, true) || result;
J
Joao Moreno 已提交
179
			}
J
Joao Moreno 已提交
180 181 182 183 184
		}

		return result;
	}

J
Joao Moreno 已提交
185
	refilter(): void {
J
Joao Moreno 已提交
186
		const previousRenderNodeCount = this.root.renderNodeCount;
J
Joao Moreno 已提交
187
		const toInsert = this.updateNodeAfterFilterChange(this.root);
J
Joao Moreno 已提交
188
		this.list.splice(0, previousRenderNodeCount, toInsert);
J
Joao Moreno 已提交
189 190
	}

J
Joao Moreno 已提交
191 192 193
	private createTreeNode(
		treeElement: ITreeElement<T>,
		parent: IMutableTreeNode<T, TFilterData>,
194
		parentVisibility: TreeVisibility,
J
Joao Moreno 已提交
195 196 197 198
		revealed: boolean,
		treeListElements: ITreeNode<T, TFilterData>[],
		onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void
	): IMutableTreeNode<T, TFilterData> {
J
Joao Moreno 已提交
199 200 201 202 203
		const node: IMutableTreeNode<T, TFilterData> = {
			parent,
			element: treeElement.element,
			children: [],
			depth: parent.depth + 1,
J
Joao Moreno 已提交
204 205
			collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'),
			collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed,
J
Joao Moreno 已提交
206
			renderNodeCount: 1,
J
Joao Moreno 已提交
207 208 209
			visible: true,
			filterData: undefined
		};
J
Joao Moreno 已提交
210

211
		const visibility = this._filterNode(node, parentVisibility);
J
Joao Moreno 已提交
212

J
Joao Moreno 已提交
213
		if (revealed) {
214 215 216
			treeListElements.push(node);
		}

J
Joao Moreno 已提交
217
		const childElements = Iterator.from(treeElement.children);
218 219
		const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed;
		const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode));
J
Joao Moreno 已提交
220

221
		let hasVisibleDescendants = false;
J
Joao Moreno 已提交
222
		let renderNodeCount = 1;
223 224 225 226

		Iterator.forEach(childNodes, child => {
			node.children.push(child);
			hasVisibleDescendants = hasVisibleDescendants || child.visible;
J
Joao Moreno 已提交
227
			renderNodeCount += child.renderNodeCount;
228 229
		});

230
		node.collapsible = node.collapsible || node.children.length > 0;
231
		node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible);
J
Joao Moreno 已提交
232

J
Joao Moreno 已提交
233
		if (!node.visible) {
J
Joao Moreno 已提交
234
			node.renderNodeCount = 0;
J
Joao Moreno 已提交
235 236 237 238

			if (revealed) {
				treeListElements.pop();
			}
J
Joao Moreno 已提交
239
		} else if (!node.collapsed) {
J
Joao Moreno 已提交
240
			node.renderNodeCount = renderNodeCount;
241 242
		}

J
Joao Moreno 已提交
243 244 245 246
		if (onDidCreateNode) {
			onDidCreateNode(node);
		}

247 248 249
		return node;
	}

J
Joao Moreno 已提交
250
	private updateNodeAfterCollapseChange(node: IMutableTreeNode<T, TFilterData>): ITreeNode<T, TFilterData>[] {
J
Joao Moreno 已提交
251
		const previousRenderNodeCount = node.renderNodeCount;
252 253
		const result: ITreeNode<T, TFilterData>[] = [];

J
Joao Moreno 已提交
254
		this._updateNodeAfterCollapseChange(node, result);
J
Joao Moreno 已提交
255
		this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount);
J
Joao Moreno 已提交
256 257 258 259 260 261 262 263 264 265

		return result;
	}

	private _updateNodeAfterCollapseChange(node: IMutableTreeNode<T, TFilterData>, result: ITreeNode<T, TFilterData>[]): number {
		if (node.visible === false) {
			return 0;
		}

		result.push(node);
J
Joao Moreno 已提交
266
		node.renderNodeCount = 1;
J
Joao Moreno 已提交
267 268 269

		if (!node.collapsed) {
			for (const child of node.children) {
J
Joao Moreno 已提交
270
				node.renderNodeCount += this._updateNodeAfterCollapseChange(child, result);
271
			}
J
Joao Moreno 已提交
272 273
		}

J
Joao Moreno 已提交
274
		this._onDidChangeRenderNodeCount.fire(node);
J
Joao Moreno 已提交
275
		return node.renderNodeCount;
J
Joao Moreno 已提交
276 277 278
	}

	private updateNodeAfterFilterChange(node: IMutableTreeNode<T, TFilterData>): ITreeNode<T, TFilterData>[] {
J
Joao Moreno 已提交
279
		const previousRenderNodeCount = node.renderNodeCount;
J
Joao Moreno 已提交
280 281
		const result: ITreeNode<T, TFilterData>[] = [];

282
		this._updateNodeAfterFilterChange(node, node.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, result);
J
Joao Moreno 已提交
283
		this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount);
J
Joao Moreno 已提交
284 285 286 287

		return result;
	}

288 289
	private _updateNodeAfterFilterChange(node: IMutableTreeNode<T, TFilterData>, parentVisibility: TreeVisibility, result: ITreeNode<T, TFilterData>[], revealed = true): boolean {
		let visibility: TreeVisibility;
J
Joao Moreno 已提交
290

J
Joao Moreno 已提交
291
		if (node !== this.root) {
292
			visibility = this._filterNode(node, parentVisibility);
293

294
			if (visibility === TreeVisibility.Hidden) {
J
Joao Moreno 已提交
295
				node.visible = false;
J
Joao Moreno 已提交
296
				return false;
297 298
			}

J
Joao Moreno 已提交
299 300 301
			if (revealed) {
				result.push(node);
			}
J
Joao Moreno 已提交
302
		}
J
Joao Moreno 已提交
303

J
Joao Moreno 已提交
304
		const resultStartLength = result.length;
J
Joao Moreno 已提交
305
		node.renderNodeCount = node === this.root ? 0 : 1;
306

J
Joao Moreno 已提交
307
		let hasVisibleDescendants = false;
308
		if (!node.collapsed || visibility! !== TreeVisibility.Hidden) {
J
Joao Moreno 已提交
309
			for (const child of node.children) {
310
				hasVisibleDescendants = this._updateNodeAfterFilterChange(child, visibility!, result, revealed && !node.collapsed) || hasVisibleDescendants;
J
Joao Moreno 已提交
311
			}
J
Joao Moreno 已提交
312
		}
J
Joao Moreno 已提交
313

J
Joao Moreno 已提交
314
		if (node !== this.root) {
315
			node.visible = visibility! === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility! === TreeVisibility.Visible);
J
Joao Moreno 已提交
316
		}
J
Joao Moreno 已提交
317

J
Joao Moreno 已提交
318
		if (!node.visible) {
J
Joao Moreno 已提交
319
			node.renderNodeCount = 0;
320

J
Joao Moreno 已提交
321 322 323 324
			if (revealed) {
				result.pop();
			}
		} else if (!node.collapsed) {
J
Joao Moreno 已提交
325
			node.renderNodeCount += result.length - resultStartLength;
J
Joao Moreno 已提交
326
		}
327

J
Joao Moreno 已提交
328
		this._onDidChangeRenderNodeCount.fire(node);
J
Joao Moreno 已提交
329 330
		return node.visible;
	}
J
Joao Moreno 已提交
331

332
	private _updateAncestorsRenderNodeCount(node: IMutableTreeNode<T, TFilterData> | undefined, diff: number): void {
J
Joao Moreno 已提交
333 334
		if (diff === 0) {
			return;
J
Joao Moreno 已提交
335 336
		}

337
		while (node) {
J
Joao Moreno 已提交
338
			node.renderNodeCount += diff;
J
Joao Moreno 已提交
339
			this._onDidChangeRenderNodeCount.fire(node);
340
			node = node.parent;
J
Joao Moreno 已提交
341 342 343
		}
	}

344 345
	private _filterNode(node: IMutableTreeNode<T, TFilterData>, parentVisibility: TreeVisibility): TreeVisibility {
		const result = this.filter ? this.filter.filter(node.element, parentVisibility) : TreeVisibility.Visible;
346

J
Joao Moreno 已提交
347
		if (typeof result === 'boolean') {
J
Joao Moreno 已提交
348
			node.filterData = undefined;
S
Sandeep Somavarapu 已提交
349
			return result ? TreeVisibility.Visible : TreeVisibility.Hidden;
J
Joao Moreno 已提交
350 351
		} else if (isFilterResult<TFilterData>(result)) {
			node.filterData = result.data;
J
Joao Moreno 已提交
352
			return getVisibleState(result.visibility);
353 354
		} else {
			node.filterData = undefined;
J
Joao Moreno 已提交
355
			return getVisibleState(result);
356
		}
J
Joao Moreno 已提交
357 358
	}

J
Joao Moreno 已提交
359
	// cheap
J
Joao Moreno 已提交
360
	private getTreeNode(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root): IMutableTreeNode<T, TFilterData> {
361
		if (!location || location.length === 0) {
J
Joao Moreno 已提交
362 363 364 365 366 367 368 369 370
			return node;
		}

		const [index, ...rest] = location;

		if (index < 0 || index > node.children.length) {
			throw new Error('Invalid tree location');
		}

J
Joao Moreno 已提交
371
		return this.getTreeNode(rest, node.children[index]);
J
Joao Moreno 已提交
372 373
	}

J
Joao Moreno 已提交
374 375
	// expensive
	private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean } {
J
Joao Moreno 已提交
376 377 378 379
		if (location.length === 0) {
			return { node: this.root, listIndex: -1, revealed: true };
		}

J
Joao Moreno 已提交
380
		const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location);
J
Joao Moreno 已提交
381
		const index = location[location.length - 1];
J
Joao Moreno 已提交
382 383 384 385 386 387 388

		if (index < 0 || index > parentNode.children.length) {
			throw new Error('Invalid tree location');
		}

		const node = parentNode.children[index];

389
		return { node, listIndex, revealed };
J
Joao Moreno 已提交
390 391
	}

J
Joao Moreno 已提交
392
	private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root, listIndex: number = 0, revealed = true): { parentNode: IMutableTreeNode<T, TFilterData>; listIndex: number; revealed: boolean; } {
J
Joao Moreno 已提交
393 394 395 396 397
		const [index, ...rest] = location;

		if (index < 0 || index > node.children.length) {
			throw new Error('Invalid tree location');
		}
J
Joao Moreno 已提交
398

399
		// TODO@joao perf!
J
Joao Moreno 已提交
400
		for (let i = 0; i < index; i++) {
J
Joao Moreno 已提交
401
			listIndex += node.children[i].renderNodeCount;
402
		}
J
Joao Moreno 已提交
403

404
		revealed = revealed && !node.collapsed;
J
Joao Moreno 已提交
405 406

		if (rest.length === 0) {
407
			return { parentNode: node, listIndex, revealed };
J
Joao Moreno 已提交
408 409
		}

J
Joao Moreno 已提交
410
		return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed);
J
Joao Moreno 已提交
411
	}
J
Joao Moreno 已提交
412

J
Joao Moreno 已提交
413 414 415 416
	getNode(location: number[] = []): ITreeNode<T, TFilterData> {
		return this.getTreeNode(location);
	}

J
Joao Moreno 已提交
417 418
	// TODO@joao perf!
	getNodeLocation(node: ITreeNode<T, TFilterData>): number[] {
M
Matt Bierner 已提交
419
		const location: number[] = [];
J
Joao Moreno 已提交
420 421 422 423 424 425 426 427 428

		while (node.parent) {
			location.push(node.parent.children.indexOf(node));
			node = node.parent;
		}

		return location.reverse();
	}

J
Joao Moreno 已提交
429
	getParentNodeLocation(location: number[]): number[] {
J
Joao Moreno 已提交
430
		if (location.length <= 1) {
J
Joao Moreno 已提交
431
			return [];
J
Joao Moreno 已提交
432 433 434 435
		}

		return tail2(location)[0];
	}
J
Joao Moreno 已提交
436

J
Joao Moreno 已提交
437
	getParentElement(location: number[]): T {
J
Joao Moreno 已提交
438
		const parentLocation = this.getParentNodeLocation(location);
J
Joao Moreno 已提交
439
		const node = this.getTreeNode(parentLocation);
J
Joao Moreno 已提交
440
		return node.element;
J
Joao Moreno 已提交
441 442
	}

J
Joao Moreno 已提交
443
	getFirstElementChild(location: number[]): T | undefined {
J
Joao Moreno 已提交
444
		const node = this.getTreeNode(location);
J
Joao Moreno 已提交
445 446

		if (node.children.length === 0) {
J
Joao Moreno 已提交
447
			return undefined;
J
Joao Moreno 已提交
448 449 450 451 452
		}

		return node.children[0].element;
	}

J
Joao Moreno 已提交
453
	getLastElementAncestor(location: number[] = []): T | undefined {
J
Joao Moreno 已提交
454
		const node = this.getTreeNode(location);
J
Joao Moreno 已提交
455 456

		if (node.children.length === 0) {
J
Joao Moreno 已提交
457
			return undefined;
J
Joao Moreno 已提交
458 459 460 461 462
		}

		return this._getLastElementAncestor(node);
	}

J
Joao Moreno 已提交
463
	private _getLastElementAncestor(node: ITreeNode<T, TFilterData>): T {
J
Joao Moreno 已提交
464 465 466 467 468 469
		if (node.children.length === 0) {
			return node.element;
		}

		return this._getLastElementAncestor(node.children[node.children.length - 1]);
	}
J
Joao Moreno 已提交
470
}