Change:
Issue Type: <bBug</bTesting Repo settings editor to the section click to switch to instead of Bug TOC is collapsed. Any scrolling causes the TOC to be expanded at the correctly again VS Code version: Code - Insiders 1.31.0-insider (, 2019-01-29T06:13:57.866Z) OS version: Darwin x64 18.2.0 <details<summarySystem Info</summary </details<!-- generated by issue reporter --
Probably another case of The TOC tree sometimes maintains its selection/expansion state, sometimes not.
Hey should expansion state be preserved using the string identity? was for focus/selection, this is for expansion state.
Adding myself so I don't forget about this.
Let's merge this early June:
Thanks

Discussion:
import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; <ins> import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; </ins> export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> { readonly sorter?: ITreeSorter<T>; <ins> readonly identityProvider?: IIdentityProvider<T>; </ins> } export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {

private model: IndexTreeModel<T | null, TFilterData>; private nodes = new Map<T | null, ITreeNode<T, TFilterData>>(); <ins> private readonly nodesByIdentity = new Map<string, ITreeNode<T, TFilterData>>(); private readonly identityProvider?: IIdentityProvider<T>; </ins> private sorter?: ITreeSorter<{ element: T; }>; readonly onDidSplice: Event<ITreeModelSpliceEvent<T | null, TFilterData>>;

} }; } <ins> this.identityProvider = options.identityProvider; </ins> } setChildren(

onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void ): Iterator<ITreeElement<T | null>> { const insertedElements = new Set<T | null>(); <ins> const insertedElementIds = new Set<string>(); </ins> const _onDidCreateNode = (node: ITreeNode<T, TFilterData>) => { insertedElements.add(node.element); this.nodes.set(node.element, node); <ins> if (this.identityProvider) { const id = this.identityProvider.getId(node.element).toString(); insertedElementIds.add(id); this.nodesByIdentity.set(id, node); } </ins> if (onDidCreateNode) { onDidCreateNode(node); }

this.nodes.delete(node.element); } <ins> if (this.identityProvider) { const id = this.identityProvider.getId(node.element).toString(); if (!insertedElementIds.has(id)) { this.nodesByIdentity.delete(id); } } </ins> if (onDidDeleteNode) { onDidDeleteNode(node); } }; <del> return this.model.splice( </del> <ins> const result = this.model.splice( </ins> [...location, 0], Number.MAX_VALUE, children, _onDidCreateNode, _onDidDeleteNode ); <ins> return result; </ins> } private preserveCollapseState(elements: ISequence<ITreeElement<T>> | undefined): ISequence<ITreeElement<T>> {

} return Iterator.map(iterator, treeElement => { <del> const node = this.nodes.get(treeElement.element); </del> <ins> let node = this.nodes.get(treeElement.element); if (!node && this.identityProvider) { const id = this.identityProvider.getId(treeElement.element).toString(); node = this.nodesByIdentity.get(id); } </ins> if (!node) { return {

const groupChildren = <SettingsTreeGroupElement[]>model.children.filter(c => c instanceof SettingsTreeGroupElement); const groupsIt = Iterator.fromArray(groupChildren); return Iterator.map(groupsIt, g => { <del> let nodeExists = true; try { tree.getNode(g); } catch (e) { nodeExists = false; } </del> const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement); return { element: g, <del> collapsed: nodeExists ? undefined : true, </del> <ins> collapsed: undefined, </ins> collapsible: hasGroupChildren, children: g instanceof SettingsTreeGroupElement ? createTOCIterator(g, tree) :

} }, styleController: new DefaultStyleController(DOM.createStyleSheet(container), treeClass), <del> accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider) </del> <ins> accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), collapseByDefault: true </ins> }; super(container,