monaco-editor-core
Version:
A browser based code editor
197 lines (196 loc) • 8.12 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IndexTreeModel } from './indexTreeModel.js';
import { ObjectTreeElementCollapseState, TreeError } from './tree.js';
import { Iterable } from '../../../common/iterator.js';
export class ObjectTreeModel {
constructor(user, list, options = {}) {
this.user = user;
this.rootRef = null;
this.nodes = new Map();
this.nodesByIdentity = new Map();
this.model = new IndexTreeModel(user, list, null, options);
this.onDidSplice = this.model.onDidSplice;
this.onDidChangeCollapseState = this.model.onDidChangeCollapseState;
this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount;
if (options.sorter) {
this.sorter = {
compare(a, b) {
return options.sorter.compare(a.element, b.element);
}
};
}
this.identityProvider = options.identityProvider;
}
setChildren(element, children = Iterable.empty(), options = {}) {
const location = this.getElementLocation(element);
this._setChildren(location, this.preserveCollapseState(children), options);
}
_setChildren(location, children = Iterable.empty(), options) {
const insertedElements = new Set();
const insertedElementIds = new Set();
const onDidCreateNode = (node) => {
if (node.element === null) {
return;
}
const tnode = node;
insertedElements.add(tnode.element);
this.nodes.set(tnode.element, tnode);
if (this.identityProvider) {
const id = this.identityProvider.getId(tnode.element).toString();
insertedElementIds.add(id);
this.nodesByIdentity.set(id, tnode);
}
options.onDidCreateNode?.(tnode);
};
const onDidDeleteNode = (node) => {
if (node.element === null) {
return;
}
const tnode = node;
if (!insertedElements.has(tnode.element)) {
this.nodes.delete(tnode.element);
}
if (this.identityProvider) {
const id = this.identityProvider.getId(tnode.element).toString();
if (!insertedElementIds.has(id)) {
this.nodesByIdentity.delete(id);
}
}
options.onDidDeleteNode?.(tnode);
};
this.model.splice([...location, 0], Number.MAX_VALUE, children, { ...options, onDidCreateNode, onDidDeleteNode });
}
preserveCollapseState(elements = Iterable.empty()) {
if (this.sorter) {
elements = [...elements].sort(this.sorter.compare.bind(this.sorter));
}
return Iterable.map(elements, treeElement => {
let node = this.nodes.get(treeElement.element);
if (!node && this.identityProvider) {
const id = this.identityProvider.getId(treeElement.element).toString();
node = this.nodesByIdentity.get(id);
}
if (!node) {
let collapsed;
if (typeof treeElement.collapsed === 'undefined') {
collapsed = undefined;
}
else if (treeElement.collapsed === ObjectTreeElementCollapseState.Collapsed || treeElement.collapsed === ObjectTreeElementCollapseState.PreserveOrCollapsed) {
collapsed = true;
}
else if (treeElement.collapsed === ObjectTreeElementCollapseState.Expanded || treeElement.collapsed === ObjectTreeElementCollapseState.PreserveOrExpanded) {
collapsed = false;
}
else {
collapsed = Boolean(treeElement.collapsed);
}
return {
...treeElement,
children: this.preserveCollapseState(treeElement.children),
collapsed
};
}
const collapsible = typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : node.collapsible;
let collapsed;
if (typeof treeElement.collapsed === 'undefined' || treeElement.collapsed === ObjectTreeElementCollapseState.PreserveOrCollapsed || treeElement.collapsed === ObjectTreeElementCollapseState.PreserveOrExpanded) {
collapsed = node.collapsed;
}
else if (treeElement.collapsed === ObjectTreeElementCollapseState.Collapsed) {
collapsed = true;
}
else if (treeElement.collapsed === ObjectTreeElementCollapseState.Expanded) {
collapsed = false;
}
else {
collapsed = Boolean(treeElement.collapsed);
}
return {
...treeElement,
collapsible,
collapsed,
children: this.preserveCollapseState(treeElement.children)
};
});
}
rerender(element) {
const location = this.getElementLocation(element);
this.model.rerender(location);
}
getFirstElementChild(ref = null) {
const location = this.getElementLocation(ref);
return this.model.getFirstElementChild(location);
}
has(element) {
return this.nodes.has(element);
}
getListIndex(element) {
const location = this.getElementLocation(element);
return this.model.getListIndex(location);
}
getListRenderCount(element) {
const location = this.getElementLocation(element);
return this.model.getListRenderCount(location);
}
isCollapsible(element) {
const location = this.getElementLocation(element);
return this.model.isCollapsible(location);
}
setCollapsible(element, collapsible) {
const location = this.getElementLocation(element);
return this.model.setCollapsible(location, collapsible);
}
isCollapsed(element) {
const location = this.getElementLocation(element);
return this.model.isCollapsed(location);
}
setCollapsed(element, collapsed, recursive) {
const location = this.getElementLocation(element);
return this.model.setCollapsed(location, collapsed, recursive);
}
expandTo(element) {
const location = this.getElementLocation(element);
this.model.expandTo(location);
}
refilter() {
this.model.refilter();
}
getNode(element = null) {
if (element === null) {
return this.model.getNode(this.model.rootRef);
}
const node = this.nodes.get(element);
if (!node) {
throw new TreeError(this.user, `Tree element not found: ${element}`);
}
return node;
}
getNodeLocation(node) {
return node.element;
}
getParentNodeLocation(element) {
if (element === null) {
throw new TreeError(this.user, `Invalid getParentNodeLocation call`);
}
const node = this.nodes.get(element);
if (!node) {
throw new TreeError(this.user, `Tree element not found: ${element}`);
}
const location = this.model.getNodeLocation(node);
const parentLocation = this.model.getParentNodeLocation(location);
const parent = this.model.getNode(parentLocation);
return parent.element;
}
getElementLocation(element) {
if (element === null) {
return [];
}
const node = this.nodes.get(element);
if (!node) {
throw new TreeError(this.user, `Tree element not found: ${element}`);
}
return this.model.getNodeLocation(node);
}
}