Archived
1
0

chore(vscode): update to 1.53.2

These conflicts will be resolved in the following commits. We do it this way so
that PR review is possible.
This commit is contained in:
Joe Previte
2021-02-25 11:27:27 -07:00
1900 changed files with 83066 additions and 64589 deletions

View File

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions, IObjectTreeSetChildrenOptions } from 'vs/base/browser/ui/tree/objectTree';
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list';
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
@ -279,6 +279,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
}
export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { }
export interface IAsyncDataTreeUpdateChildrenOptions<T> extends IObjectTreeSetChildrenOptions<T> { }
export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptionsUpdate, Pick<IAbstractTreeOptions<T, TFilterData>, Exclude<keyof IAbstractTreeOptions<T, TFilterData>, 'collapseByDefault'>> {
readonly collapseByDefault?: { (e: T): boolean; };
@ -499,11 +500,11 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
}
}
async updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false): Promise<void> {
await this._updateChildren(element, recursive, rerender);
async updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false, options?: IAsyncDataTreeUpdateChildrenOptions<T>): Promise<void> {
await this._updateChildren(element, recursive, rerender, undefined, options);
}
private async _updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
private async _updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>, options?: IAsyncDataTreeUpdateChildrenOptions<T>): Promise<void> {
if (typeof this.root.element === 'undefined') {
throw new TreeError(this.user, 'Tree input not set');
}
@ -514,7 +515,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
}
const node = this.getDataNode(element);
await this.refreshAndRenderNode(node, recursive, viewStateContext);
await this.refreshAndRenderNode(node, recursive, viewStateContext, options);
if (rerender) {
try {
@ -704,9 +705,9 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return node;
}
private async refreshAndRenderNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
private async refreshAndRenderNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>, options?: IAsyncDataTreeUpdateChildrenOptions<T>): Promise<void> {
await this.refreshNode(node, recursive, viewStateContext);
this.render(node, viewStateContext);
this.render(node, viewStateContext, options);
}
private async refreshNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
@ -768,8 +769,8 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
const children = await childrenPromise;
return this.setChildren(node, children, recursive, viewStateContext);
} catch (err) {
if (node !== this.root) {
this.tree.collapse(node === this.root ? null : node);
if (node !== this.root && this.tree.hasElement(node)) {
this.tree.collapse(node);
}
if (isPromiseCanceledError(err)) {
@ -921,9 +922,18 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return childrenToRefresh;
}
protected render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
protected render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>, options?: IAsyncDataTreeUpdateChildrenOptions<T>): void {
const children = node.children.map(node => this.asTreeElement(node, viewStateContext));
this.tree.setChildren(node === this.root ? null : node, children);
const objectTreeOptions: IObjectTreeSetChildrenOptions<IAsyncDataTreeNode<TInput, T>> | undefined = options && {
...options,
diffIdentityProvider: options!.diffIdentityProvider && {
getId(node: IAsyncDataTreeNode<TInput, T>): { toString(): string; } {
return options!.diffIdentityProvider!.getId(node.element as T);
}
}
};
this.tree.setChildren(node === this.root ? null : node, children, objectTreeOptions);
if (node !== this.root) {
this.tree.setCollapsible(node, node.hasChildren);

View File

@ -6,8 +6,9 @@
import { Iterable } from 'vs/base/common/iterator';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree';
import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel, IObjectTreeModelSetChildrenOptions } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IIndexTreeModelSpliceOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
// Exported only for test reasons, do not use directly
export interface ICompressedTreeElement<T> extends ITreeElement<T> {
@ -108,6 +109,12 @@ interface ICompressedObjectTreeModelOptions<T, TFilterData> extends IObjectTreeM
readonly compressionEnabled?: boolean;
}
const wrapIdentityProvider = <T>(base: IIdentityProvider<T>): IIdentityProvider<ICompressedTreeNode<T>> => ({
getId(node) {
return node.elements.map(e => base.getId(e).toString()).join('\0');
}
});
// Exported only for test reasons, do not use directly
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
@ -120,6 +127,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
private model: ObjectTreeModel<ICompressedTreeNode<T>, TFilterData>;
private nodes = new Map<T | null, ICompressedTreeNode<T>>();
private enabled: boolean;
private readonly identityProvider?: IIdentityProvider<ICompressedTreeNode<T>>;
get size(): number { return this.nodes.size; }
@ -130,15 +138,21 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
) {
this.model = new ObjectTreeModel(user, list, options);
this.enabled = typeof options.compressionEnabled === 'undefined' ? true : options.compressionEnabled;
this.identityProvider = options.identityProvider;
}
setChildren(
element: T | null,
children: Iterable<ICompressedTreeElement<T>> = Iterable.empty()
children: Iterable<ICompressedTreeElement<T>> = Iterable.empty(),
options: IObjectTreeModelSetChildrenOptions<T, TFilterData>,
): void {
// Diffs must be deem, since the compression can affect nested elements.
// @see https://github.com/microsoft/vscode/pull/114237#issuecomment-759425034
const diffIdentityProvider = options.diffIdentityProvider && wrapIdentityProvider(options.diffIdentityProvider);
if (element === null) {
const compressedChildren = Iterable.map(children, this.enabled ? compress : noCompress);
this._setChildren(null, compressedChildren);
this._setChildren(null, compressedChildren, { diffIdentityProvider, diffDepth: Infinity });
return;
}
@ -159,7 +173,10 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
const parentChildren = parent.children
.map(child => child === node ? recompressedElement : child);
this._setChildren(parent.element, parentChildren);
this._setChildren(parent.element, parentChildren, {
diffIdentityProvider,
diffDepth: node.depth - parent.depth,
});
}
isCompressionEnabled(): boolean {
@ -177,22 +194,29 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
const rootChildren = root.children as ITreeNode<ICompressedTreeNode<T>>[];
const decompressedRootChildren = Iterable.map(rootChildren, decompress);
const recompressedRootChildren = Iterable.map(decompressedRootChildren, enabled ? compress : noCompress);
this._setChildren(null, recompressedRootChildren);
// it should be safe to always use deep diff mode here if an identity
// provider is available, since we know the raw nodes are unchanged.
this._setChildren(null, recompressedRootChildren, {
diffIdentityProvider: this.identityProvider,
diffDepth: Infinity,
});
}
private _setChildren(
node: ICompressedTreeNode<T> | null,
children: Iterable<ITreeElement<ICompressedTreeNode<T>>>
children: Iterable<ITreeElement<ICompressedTreeNode<T>>>,
options: IIndexTreeModelSpliceOptions<ICompressedTreeNode<T>, TFilterData>,
): void {
const insertedElements = new Set<T | null>();
const _onDidCreateNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
const onDidCreateNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
for (const element of node.element.elements) {
insertedElements.add(element);
this.nodes.set(element, node.element);
}
};
const _onDidDeleteNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
const onDidDeleteNode = (node: ITreeNode<ICompressedTreeNode<T>, TFilterData>) => {
for (const element of node.element.elements) {
if (!insertedElements.has(element)) {
this.nodes.delete(element);
@ -200,7 +224,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
}
};
this.model.setChildren(node, children, _onDidCreateNode, _onDidDeleteNode);
this.model.setChildren(node, children, { ...options, onDidCreateNode, onDidDeleteNode });
}
has(element: T | null): boolean {
@ -363,16 +387,16 @@ function mapList<T, TFilterData>(nodeMapper: CompressedNodeWeakMapper<T, TFilter
function mapOptions<T, TFilterData>(compressedNodeUnwrapper: CompressedNodeUnwrapper<T>, options: ICompressibleObjectTreeModelOptions<T, TFilterData>): ICompressedObjectTreeModelOptions<T, TFilterData> {
return {
...options,
sorter: options.sorter && {
compare(node: ICompressedTreeNode<T>, otherNode: ICompressedTreeNode<T>): number {
return options.sorter!.compare(node.elements[0], otherNode.elements[0]);
}
},
identityProvider: options.identityProvider && {
getId(node: ICompressedTreeNode<T>): { toString(): string; } {
return options.identityProvider!.getId(compressedNodeUnwrapper(node));
}
},
sorter: options.sorter && {
compare(node: ICompressedTreeNode<T>, otherNode: ICompressedTreeNode<T>): number {
return options.sorter!.compare(node.elements[0], otherNode.elements[0]);
}
},
filter: options.filter && {
filter(node: ICompressedTreeNode<T>, parentVisibility: TreeVisibility): TreeFilterResult<TFilterData> {
return options.filter!.filter(compressedNodeUnwrapper(node), parentVisibility);
@ -424,8 +448,12 @@ export class CompressibleObjectTreeModel<T extends NonNullable<any>, TFilterData
this.model = new CompressedObjectTreeModel(user, mapList(this.nodeMapper, list), mapOptions(compressedNodeUnwrapper, options));
}
setChildren(element: T | null, children: Iterable<ICompressedTreeElement<T>> = Iterable.empty()): void {
this.model.setChildren(element, children);
setChildren(
element: T | null,
children: Iterable<ICompressedTreeElement<T>> = Iterable.empty(),
options: IObjectTreeModelSetChildrenOptions<T, TFilterData> = {},
): void {
this.model.setChildren(element, children, options);
}
isCompressionEnabled(): boolean {

View File

@ -47,13 +47,19 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
return this.input;
}
setInput(input: TInput, viewState?: IDataTreeViewState): void {
setInput(input: TInput | undefined, viewState?: IDataTreeViewState): void {
if (viewState && !this.identityProvider) {
throw new TreeError(this.user, 'Can\'t restore tree view state without an identity provider');
}
this.input = input;
if (!input) {
this.nodesByIdentity.clear();
this.model.setChildren(null, Iterable.empty());
return;
}
if (!viewState) {
this._refresh(input);
return;
@ -155,7 +161,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
};
}
this.model.setChildren((element === this.input ? null : element) as T, this.iterate(element, isCollapsed).elements, onDidCreateNode, onDidDeleteNode);
this.model.setChildren((element === this.input ? null : element) as T, this.iterate(element, isCollapsed).elements, { onDidCreateNode, onDidDeleteNode });
}
private iterate(element: TInput | T, isCollapsed?: (el: T) => boolean | undefined): { elements: Iterable<ITreeElement<T>>, size: number } {

View File

@ -3,8 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree';
import { tail2 } from 'vs/base/common/arrays';
import { LcsDiff } from 'vs/base/common/diff/diff';
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
import { Iterable } from 'vs/base/common/iterator';
import { ISpliceable } from 'vs/base/common/sequence';
@ -41,6 +43,34 @@ export interface IIndexTreeModelOptions<T, TFilterData> {
readonly autoExpandSingleChildren?: boolean;
}
export interface IIndexTreeModelSpliceOptions<T, TFilterData> {
/**
* If set, child updates will recurse the given number of levels even if
* items in the splice operation are unchanged. `Infinity` is a valid value.
*/
readonly diffDepth?: number;
/**
* Identity provider used to optimize splice() calls in the IndexTree. If
* this is not present, optimized splicing is not enabled.
*
* Warning: if this is present, calls to `setChildren()` will not replace
* or update nodes if their identity is the same, even if the elements are
* different. For this, you should call `rerender()`.
*/
readonly diffIdentityProvider?: IIdentityProvider<T>;
/**
* Callback for when a node is created.
*/
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void;
/**
* Callback for when a node is deleted.
*/
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
}
interface CollapsibleStateUpdate {
readonly collapsible: boolean;
}
@ -110,18 +140,95 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
location: number[],
deleteCount: number,
toInsert: Iterable<ITreeElement<T>> = Iterable.empty(),
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
options: IIndexTreeModelSpliceOptions<T, TFilterData> = {},
): void {
if (location.length === 0) {
throw new TreeError(this.user, 'Invalid tree location');
}
if (options.diffIdentityProvider) {
this.spliceSmart(options.diffIdentityProvider, location, deleteCount, toInsert, options);
} else {
this.spliceSimple(location, deleteCount, toInsert, options);
}
}
private spliceSmart(
identity: IIdentityProvider<T>,
location: number[],
deleteCount: number,
toInsertIterable: Iterable<ITreeElement<T>> = Iterable.empty(),
options: IIndexTreeModelSpliceOptions<T, TFilterData>,
recurseLevels = options.diffDepth ?? 0,
) {
const { parentNode } = this.getParentNodeWithListIndex(location);
const toInsert = [...toInsertIterable];
const index = location[location.length - 1];
const diff = new LcsDiff(
{ getElements: () => parentNode.children.map(e => identity.getId(e.element).toString()) },
{
getElements: () => [
...parentNode.children.slice(0, index),
...toInsert,
...parentNode.children.slice(index + deleteCount),
].map(e => identity.getId(e.element).toString())
},
).ComputeDiff(false);
// if we were given a 'best effort' diff, use default behavior
if (diff.quitEarly) {
return this.spliceSimple(location, deleteCount, toInsert, options);
}
const locationPrefix = location.slice(0, -1);
const recurseSplice = (fromOriginal: number, fromModified: number, count: number) => {
if (recurseLevels > 0) {
for (let i = 0; i < count; i++) {
fromOriginal--;
fromModified--;
this.spliceSmart(
identity,
[...locationPrefix, fromOriginal, 0],
Number.MAX_SAFE_INTEGER,
toInsert[fromModified].children,
options,
recurseLevels - 1,
);
}
}
};
let lastStartO = Math.min(parentNode.children.length, index + deleteCount);
let lastStartM = toInsert.length;
for (const change of diff.changes.sort((a, b) => b.originalStart - a.originalStart)) {
recurseSplice(lastStartO, lastStartM, lastStartO - (change.originalStart + change.originalLength));
lastStartO = change.originalStart;
lastStartM = change.modifiedStart - index;
this.spliceSimple(
[...locationPrefix, lastStartO],
change.originalLength,
Iterable.slice(toInsert, lastStartM, lastStartM + change.modifiedLength),
options,
);
}
// at this point, startO === startM === count since any remaining prefix should match
recurseSplice(lastStartO, lastStartM, lastStartO);
}
private spliceSimple(
location: number[],
deleteCount: number,
toInsert: Iterable<ITreeElement<T>> = Iterable.empty(),
{ onDidCreateNode, onDidDeleteNode }: IIndexTreeModelSpliceOptions<T, TFilterData>,
) {
const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location);
const treeListElementsToInsert: ITreeNode<T, TFilterData>[] = [];
const nodesToInsertIterator = Iterable.map(toInsert, el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode));
const lastIndex = location[location.length - 1];
const lastHadChildren = parentNode.children.length > 0;
// figure out what's the visible child start index right before the
// splice point
@ -190,6 +297,11 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
deletedNodes.forEach(visit);
}
const currentlyHasChildren = parentNode.children.length > 0;
if (lastHadChildren !== currentlyHasChildren) {
this.setCollapsible(location.slice(0, -1), currentlyHasChildren);
}
this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes });
let node: IIndexTreeNode<T, TFilterData> | undefined = parentNode;

View File

@ -7,7 +7,7 @@ import { Iterable } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event';
import { CompressibleObjectTreeModel, ElementMapper, ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { memoize } from 'vs/base/common/decorators';
@ -17,6 +17,25 @@ export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTree
readonly sorter?: ITreeSorter<T>;
}
export interface IObjectTreeSetChildrenOptions<T> {
/**
* If set, child updates will recurse the given number of levels even if
* items in the splice operation are unchanged. `Infinity` is a valid value.
*/
readonly diffDepth?: number;
/**
* Identity provider used to optimize splice() calls in the IndexTree. If
* this is not present, optimized splicing is not enabled.
*
* Warning: if this is present, calls to `setChildren()` will not replace
* or update nodes if their identity is the same, even if the elements are
* different. For this, you should call `rerender()`.
*/
readonly diffIdentityProvider?: IIdentityProvider<T>;
}
export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
protected model!: IObjectTreeModel<T, TFilterData>;
@ -33,8 +52,8 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
super(user, container, delegate, renderers, options as IObjectTreeOptions<T | null, TFilterData>);
}
setChildren(element: T | null, children: Iterable<ITreeElement<T>> = Iterable.empty()): void {
this.model.setChildren(element, children);
setChildren(element: T | null, children: Iterable<ITreeElement<T>> = Iterable.empty(), options?: IObjectTreeSetChildrenOptions<T>): void {
this.model.setChildren(element, children, options);
}
rerender(element?: T): void {
@ -189,8 +208,8 @@ export class CompressibleObjectTree<T extends NonNullable<any>, TFilterData = vo
super(user, container, delegate, compressibleRenderers, asObjectTreeOptions<T, TFilterData>(compressedTreeNodeProvider, options));
}
setChildren(element: T | null, children: Iterable<ICompressedTreeElement<T>> = Iterable.empty()): void {
this.model.setChildren(element, children);
setChildren(element: T | null, children: Iterable<ICompressedTreeElement<T>> = Iterable.empty(), options?: IObjectTreeSetChildrenOptions<T>): void {
this.model.setChildren(element, children, options);
}
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: ICompressibleObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {

View File

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Iterable } from 'vs/base/common/iterator';
import { IndexTreeModel, IIndexTreeModelOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IndexTreeModel, IIndexTreeModelOptions, IList, IIndexTreeModelSpliceOptions } from 'vs/base/browser/ui/tree/indexTreeModel';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
@ -13,11 +13,14 @@ import { mergeSort } from 'vs/base/common/arrays';
export type ITreeNodeCallback<T, TFilterData> = (node: ITreeNode<T, TFilterData>) => void;
export interface IObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> extends ITreeModel<T | null, TFilterData, T | null> {
setChildren(element: T | null, children: Iterable<ITreeElement<T>> | undefined): void;
setChildren(element: T | null, children: Iterable<ITreeElement<T>> | undefined, options?: IObjectTreeModelSetChildrenOptions<T, TFilterData>): void;
resort(element?: T | null, recursive?: boolean): void;
updateElementHeight(element: T, height: number): void;
}
export interface IObjectTreeModelSetChildrenOptions<T, TFilterData> extends IIndexTreeModelSpliceOptions<T, TFilterData> {
}
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>;
readonly identityProvider?: IIdentityProvider<T>;
@ -63,23 +66,21 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
setChildren(
element: T | null,
children: Iterable<ITreeElement<T>> = Iterable.empty(),
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
options: IObjectTreeModelSetChildrenOptions<T, TFilterData> = {},
): void {
const location = this.getElementLocation(element);
this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode);
this._setChildren(location, this.preserveCollapseState(children), options);
}
private _setChildren(
location: number[],
children: Iterable<ITreeElement<T>> = Iterable.empty(),
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
options: IObjectTreeModelSetChildrenOptions<T, TFilterData>,
): void {
const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>();
const _onDidCreateNode = (node: ITreeNode<T | null, TFilterData>) => {
const onDidCreateNode = (node: ITreeNode<T | null, TFilterData>) => {
if (node.element === null) {
return;
}
@ -95,12 +96,10 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
this.nodesByIdentity.set(id, tnode);
}
if (onDidCreateNode) {
onDidCreateNode(tnode);
}
options.onDidCreateNode?.(tnode);
};
const _onDidDeleteNode = (node: ITreeNode<T | null, TFilterData>) => {
const onDidDeleteNode = (node: ITreeNode<T | null, TFilterData>) => {
if (node.element === null) {
return;
}
@ -118,17 +117,14 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
}
}
if (onDidDeleteNode) {
onDidDeleteNode(tnode);
}
options.onDidDeleteNode?.(tnode);
};
this.model.splice(
[...location, 0],
Number.MAX_VALUE,
children,
_onDidCreateNode,
_onDidDeleteNode
{ ...options, onDidCreateNode, onDidDeleteNode }
);
}
@ -182,7 +178,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
const location = this.getElementLocation(element);
const node = this.model.getNode(location);
this._setChildren(location, this.resortChildren(node, recursive));
this._setChildren(location, this.resortChildren(node, recursive), {});
}
private resortChildren(node: ITreeNode<T | null, TFilterData>, recursive: boolean, first = true): Iterable<ITreeElement<T>> {