diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 6018c1221d8bfa5453585f515a610c6eae5a9d9c..0aa4cedeb334b76c90a34273377b4e835a0a71cd 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -8,20 +8,14 @@ import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterato import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; -import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { - identityProvider?: IIdentityProvider; -} +export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { } export class ObjectTreeModel, TFilterData = void> implements ITreeModel { private model: IndexTreeModel; private nodes = new Map>(); - private identityProvider: IIdentityProvider | undefined = undefined; - private nodesByIdentity: Map> | undefined = undefined; - readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; @@ -29,39 +23,23 @@ export class ObjectTreeModel, TFilterData = void> imp constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { this.model = new IndexTreeModel(list, options); - this.identityProvider = options.identityProvider; - - if (this.identityProvider) { - this.nodesByIdentity = new Map(); - } - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; } setChildren( element: T | null, - // TODO@joao also use identity provider to preserve children when `children` is undefined!!! children: ISequence> | undefined, onDidCreateNode?: (node: ITreeNode) => void, onDidDeleteNode?: (node: ITreeNode) => void ): Iterator> { const location = this.getElementLocation(element); const insertedElements = new Set(); - let createdNodesByIdentity: Map> | undefined; - - if (this.identityProvider) { - createdNodesByIdentity = new Map(); - } const _onDidCreateNode = (node: ITreeNode) => { insertedElements.add(node.element); this.nodes.set(node.element, node); - if (createdNodesByIdentity) { - createdNodesByIdentity.set(this.identityProvider!.getId(node.element).toString(), node); - } - if (onDidCreateNode) { onDidCreateNode(node); } @@ -72,43 +50,40 @@ export class ObjectTreeModel, TFilterData = void> imp this.nodes.delete(node.element); } - if (this.nodesByIdentity) { - this.nodesByIdentity.delete(this.identityProvider!.getId(node.element).toString()); - } - if (onDidDeleteNode) { onDidDeleteNode(node); } }; - const preserveCollapseState = (elements: ISequence>): ISequence> => { - const iterator = getSequenceIterator(elements); - - return Iterator.map(iterator, treeElement => { - const identity = this.identityProvider!.getId(treeElement.element).toString(); - const node = this.nodesByIdentity!.get(identity); - const collapsible = typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'); - - return { - ...treeElement, - collapsible, - collapsed: typeof treeElement.collapsed === 'undefined' ? (node ? (collapsible && node.collapsed) : undefined) : treeElement.collapsed, - children: treeElement.children && preserveCollapseState(treeElement.children) - }; - }); - }; + return this.model.splice( + [...location, 0], + Number.MAX_VALUE, + this.preserveCollapseState(children), + _onDidCreateNode, + _onDidDeleteNode + ); + } - if (children && this.identityProvider) { - children = preserveCollapseState(children); - } + private preserveCollapseState(elements: ISequence> | undefined): ISequence> { + const iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); - const result = this.model.splice([...location, 0], Number.MAX_VALUE, children, _onDidCreateNode, _onDidDeleteNode); + return Iterator.map(iterator, treeElement => { + const node = this.nodes.get(treeElement.element); - if (createdNodesByIdentity) { - createdNodesByIdentity.forEach((node, identity) => this.nodesByIdentity!.set(identity, node)); - } + if (!node) { + return treeElement; + } + + const collapsible = typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : node.collapsible; + const collapsed = typeof treeElement.collapsed !== 'undefined' ? treeElement.collapsed : (collapsible && node.collapsed); - return result; + return { + ...treeElement, + collapsible, + collapsed, + children: this.preserveCollapseState(treeElement.children) + }; + }); } getParentElement(ref: T | null = null): T | null { diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 587441e1b4af90f8333ee671933784957bc15c66..3e6a1797b01525cea9c0bc5808e71a5909ebd3e9 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -141,7 +141,7 @@ suite('ObjectTreeModel', function () { assert.deepEqual(toArray(list), [1, 11, 111, 112, 2]); }); - test('collapse state is lost without an identity provider', () => { + test('collapse state is preserved with strict identity', () => { const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list), { collapseByDefault: true }); const data = [{ element: 'father', children: [{ element: 'child' }] }]; @@ -153,22 +153,19 @@ suite('ObjectTreeModel', function () { assert.deepEqual(toArray(list), ['father', 'child']); model.setChildren(null, data); - assert.deepEqual(toArray(list), ['father']); - }); + assert.deepEqual(toArray(list), ['father', 'child']); - test('collapse state is preserved with an identity provider', () => { - const list: ITreeNode[] = []; - const identityProvider = { getId: (name: string) => name }; - const model = new ObjectTreeModel(toSpliceable(list), { collapseByDefault: true, identityProvider }); - const data = [{ element: 'father', children: [{ element: 'child' }] }]; + const data2 = [{ element: 'father', children: [{ element: 'child' }] }, { element: 'uncle' }]; + model.setChildren(null, data2); + assert.deepEqual(toArray(list), ['father', 'child', 'uncle']); - model.setChildren(null, data); - assert.deepEqual(toArray(list), ['father']); + model.setChildren(null, [{ element: 'uncle' }]); + assert.deepEqual(toArray(list), ['uncle']); - model.setCollapsed('father', false); - assert.deepEqual(toArray(list), ['father', 'child']); + model.setChildren(null, data2); + assert.deepEqual(toArray(list), ['father', 'uncle']); model.setChildren(null, data); - assert.deepEqual(toArray(list), ['father', 'child']); + assert.deepEqual(toArray(list), ['father']); }); }); \ No newline at end of file