UNPKG

monaco-editor-core

Version:

A browser based code editor

202 lines (201 loc) • 9.33 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { AbstractTree } from './abstractTree.js'; import { CompressibleObjectTreeModel } from './compressedObjectTreeModel.js'; import { ObjectTreeModel } from './objectTreeModel.js'; import { memoize } from '../../../common/decorators.js'; import { Iterable } from '../../../common/iterator.js'; export class ObjectTree extends AbstractTree { get onDidChangeCollapseState() { return this.model.onDidChangeCollapseState; } constructor(user, container, delegate, renderers, options = {}) { super(user, container, delegate, renderers, options); this.user = user; } setChildren(element, children = Iterable.empty(), options) { this.model.setChildren(element, children, options); } rerender(element) { if (element === undefined) { this.view.rerender(); return; } this.model.rerender(element); } hasElement(element) { return this.model.has(element); } createModel(user, view, options) { return new ObjectTreeModel(user, view, options); } } class CompressibleRenderer { get compressedTreeNodeProvider() { return this._compressedTreeNodeProvider(); } constructor(_compressedTreeNodeProvider, stickyScrollDelegate, renderer) { this._compressedTreeNodeProvider = _compressedTreeNodeProvider; this.stickyScrollDelegate = stickyScrollDelegate; this.renderer = renderer; this.templateId = renderer.templateId; if (renderer.onDidChangeTwistieState) { this.onDidChangeTwistieState = renderer.onDidChangeTwistieState; } } renderTemplate(container) { const data = this.renderer.renderTemplate(container); return { compressedTreeNode: undefined, data }; } renderElement(node, index, templateData, height) { let compressedTreeNode = this.stickyScrollDelegate.getCompressedNode(node); if (!compressedTreeNode) { compressedTreeNode = this.compressedTreeNodeProvider.getCompressedTreeNode(node.element); } if (compressedTreeNode.element.elements.length === 1) { templateData.compressedTreeNode = undefined; this.renderer.renderElement(node, index, templateData.data, height); } else { templateData.compressedTreeNode = compressedTreeNode; this.renderer.renderCompressedElements(compressedTreeNode, index, templateData.data, height); } } disposeElement(node, index, templateData, height) { if (templateData.compressedTreeNode) { this.renderer.disposeCompressedElements?.(templateData.compressedTreeNode, index, templateData.data, height); } else { this.renderer.disposeElement?.(node, index, templateData.data, height); } } disposeTemplate(templateData) { this.renderer.disposeTemplate(templateData.data); } renderTwistie(element, twistieElement) { if (this.renderer.renderTwistie) { return this.renderer.renderTwistie(element, twistieElement); } return false; } } __decorate([ memoize ], CompressibleRenderer.prototype, "compressedTreeNodeProvider", null); class CompressibleStickyScrollDelegate { constructor(modelProvider) { this.modelProvider = modelProvider; this.compressedStickyNodes = new Map(); } getCompressedNode(node) { return this.compressedStickyNodes.get(node); } constrainStickyScrollNodes(stickyNodes, stickyScrollMaxItemCount, maxWidgetHeight) { this.compressedStickyNodes.clear(); if (stickyNodes.length === 0) { return []; } for (let i = 0; i < stickyNodes.length; i++) { const stickyNode = stickyNodes[i]; const stickyNodeBottom = stickyNode.position + stickyNode.height; const followingReachesMaxHeight = i + 1 < stickyNodes.length && stickyNodeBottom + stickyNodes[i + 1].height > maxWidgetHeight; if (followingReachesMaxHeight || i >= stickyScrollMaxItemCount - 1 && stickyScrollMaxItemCount < stickyNodes.length) { const uncompressedStickyNodes = stickyNodes.slice(0, i); const overflowingStickyNodes = stickyNodes.slice(i); const compressedStickyNode = this.compressStickyNodes(overflowingStickyNodes); return [...uncompressedStickyNodes, compressedStickyNode]; } } return stickyNodes; } compressStickyNodes(stickyNodes) { if (stickyNodes.length === 0) { throw new Error('Can\'t compress empty sticky nodes'); } const compressionModel = this.modelProvider(); if (!compressionModel.isCompressionEnabled()) { return stickyNodes[0]; } // Collect all elements to be compressed const elements = []; for (let i = 0; i < stickyNodes.length; i++) { const stickyNode = stickyNodes[i]; const compressedNode = compressionModel.getCompressedTreeNode(stickyNode.node.element); if (compressedNode.element) { // if an element is incompressible, it can't be compressed with it's parent element if (i !== 0 && compressedNode.element.incompressible) { break; } elements.push(...compressedNode.element.elements); } } if (elements.length < 2) { return stickyNodes[0]; } // Compress the elements const lastStickyNode = stickyNodes[stickyNodes.length - 1]; const compressedElement = { elements, incompressible: false }; const compressedNode = { ...lastStickyNode.node, children: [], element: compressedElement }; const stickyTreeNode = new Proxy(stickyNodes[0].node, {}); const compressedStickyNode = { node: stickyTreeNode, startIndex: stickyNodes[0].startIndex, endIndex: lastStickyNode.endIndex, position: stickyNodes[0].position, height: stickyNodes[0].height, }; this.compressedStickyNodes.set(stickyTreeNode, compressedNode); return compressedStickyNode; } } function asObjectTreeOptions(compressedTreeNodeProvider, options) { return options && { ...options, keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { getKeyboardNavigationLabel(e) { let compressedTreeNode; try { compressedTreeNode = compressedTreeNodeProvider().getCompressedTreeNode(e); } catch { return options.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(e); } if (compressedTreeNode.element.elements.length === 1) { return options.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(e); } else { return options.keyboardNavigationLabelProvider.getCompressedNodeKeyboardNavigationLabel(compressedTreeNode.element.elements); } } } }; } export class CompressibleObjectTree extends ObjectTree { constructor(user, container, delegate, renderers, options = {}) { const compressedTreeNodeProvider = () => this; const stickyScrollDelegate = new CompressibleStickyScrollDelegate(() => this.model); const compressibleRenderers = renderers.map(r => new CompressibleRenderer(compressedTreeNodeProvider, stickyScrollDelegate, r)); super(user, container, delegate, compressibleRenderers, { ...asObjectTreeOptions(compressedTreeNodeProvider, options), stickyScrollDelegate }); } setChildren(element, children = Iterable.empty(), options) { this.model.setChildren(element, children, options); } createModel(user, view, options) { return new CompressibleObjectTreeModel(user, view, options); } updateOptions(optionsUpdate = {}) { super.updateOptions(optionsUpdate); if (typeof optionsUpdate.compressionEnabled !== 'undefined') { this.model.setCompressionEnabled(optionsUpdate.compressionEnabled); } } getCompressedTreeNode(element = null) { return this.model.getCompressedTreeNode(element); } }