Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AstNode, AstNodeKind, ListAstNode } from './ast.js';67/**8* Concatenates a list of (2,3) AstNode's into a single (2,3) AstNode.9* This mutates the items of the input array!10* If all items have the same height, this method has runtime O(items.length).11* Otherwise, it has runtime O(items.length * max(log(items.length), items.max(i => i.height))).12*/13export function concat23Trees(items: AstNode[]): AstNode | null {14if (items.length === 0) {15return null;16}17if (items.length === 1) {18return items[0];19}2021let i = 0;22/**23* Reads nodes of same height and concatenates them to a single node.24*/25function readNode(): AstNode | null {26if (i >= items.length) {27return null;28}29const start = i;30const height = items[start].listHeight;3132i++;33while (i < items.length && items[i].listHeight === height) {34i++;35}3637if (i - start >= 2) {38return concat23TreesOfSameHeight(start === 0 && i === items.length ? items : items.slice(start, i), false);39} else {40return items[start];41}42}4344// The items might not have the same height.45// We merge all items by using a binary concat operator.46let first = readNode()!; // There must be a first item47let second = readNode();48if (!second) {49return first;50}5152for (let item = readNode(); item; item = readNode()) {53// Prefer concatenating smaller trees, as the runtime of concat depends on the tree height.54if (heightDiff(first, second) <= heightDiff(second, item)) {55first = concat(first, second);56second = item;57} else {58second = concat(second, item);59}60}6162const result = concat(first, second);63return result;64}6566export function concat23TreesOfSameHeight(items: AstNode[], createImmutableLists: boolean = false): AstNode | null {67if (items.length === 0) {68return null;69}70if (items.length === 1) {71return items[0];72}7374let length = items.length;75// All trees have same height, just create parent nodes.76while (length > 3) {77const newLength = length >> 1;78for (let i = 0; i < newLength; i++) {79const j = i << 1;80items[i] = ListAstNode.create23(items[j], items[j + 1], j + 3 === length ? items[j + 2] : null, createImmutableLists);81}82length = newLength;83}84return ListAstNode.create23(items[0], items[1], length >= 3 ? items[2] : null, createImmutableLists);85}8687function heightDiff(node1: AstNode, node2: AstNode): number {88return Math.abs(node1.listHeight - node2.listHeight);89}9091function concat(node1: AstNode, node2: AstNode): AstNode {92if (node1.listHeight === node2.listHeight) {93return ListAstNode.create23(node1, node2, null, false);94}95else if (node1.listHeight > node2.listHeight) {96// node1 is the tree we want to insert into97return append(node1 as ListAstNode, node2);98} else {99return prepend(node2 as ListAstNode, node1);100}101}102103/**104* Appends the given node to the end of this (2,3) tree.105* Returns the new root.106*/107function append(list: ListAstNode, nodeToAppend: AstNode): AstNode {108list = list.toMutable() as ListAstNode;109let curNode: AstNode = list;110const parents: ListAstNode[] = [];111let nodeToAppendOfCorrectHeight: AstNode | undefined;112while (true) {113// assert nodeToInsert.listHeight <= curNode.listHeight114if (nodeToAppend.listHeight === curNode.listHeight) {115nodeToAppendOfCorrectHeight = nodeToAppend;116break;117}118// assert 0 <= nodeToInsert.listHeight < curNode.listHeight119if (curNode.kind !== AstNodeKind.List) {120throw new Error('unexpected');121}122parents.push(curNode);123// assert 2 <= curNode.childrenLength <= 3124curNode = curNode.makeLastElementMutable()!;125}126// assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight127for (let i = parents.length - 1; i >= 0; i--) {128const parent = parents[i];129if (nodeToAppendOfCorrectHeight) {130// Can we take the element?131if (parent.childrenLength >= 3) {132// assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1133134// we need to split to maintain (2,3)-tree property.135// Send the third element + the new element to the parent.136nodeToAppendOfCorrectHeight = ListAstNode.create23(parent.unappendChild()!, nodeToAppendOfCorrectHeight, null, false);137} else {138parent.appendChildOfSameHeight(nodeToAppendOfCorrectHeight);139nodeToAppendOfCorrectHeight = undefined;140}141} else {142parent.handleChildrenChanged();143}144}145if (nodeToAppendOfCorrectHeight) {146return ListAstNode.create23(list, nodeToAppendOfCorrectHeight, null, false);147} else {148return list;149}150}151152/**153* Prepends the given node to the end of this (2,3) tree.154* Returns the new root.155*/156function prepend(list: ListAstNode, nodeToAppend: AstNode): AstNode {157list = list.toMutable() as ListAstNode;158let curNode: AstNode = list;159const parents: ListAstNode[] = [];160// assert nodeToInsert.listHeight <= curNode.listHeight161while (nodeToAppend.listHeight !== curNode.listHeight) {162// assert 0 <= nodeToInsert.listHeight < curNode.listHeight163if (curNode.kind !== AstNodeKind.List) {164throw new Error('unexpected');165}166parents.push(curNode);167// assert 2 <= curNode.childrenFast.length <= 3168curNode = curNode.makeFirstElementMutable()!;169}170let nodeToPrependOfCorrectHeight: AstNode | undefined = nodeToAppend;171// assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight172for (let i = parents.length - 1; i >= 0; i--) {173const parent = parents[i];174if (nodeToPrependOfCorrectHeight) {175// Can we take the element?176if (parent.childrenLength >= 3) {177// assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1178179// we need to split to maintain (2,3)-tree property.180// Send the third element + the new element to the parent.181nodeToPrependOfCorrectHeight = ListAstNode.create23(nodeToPrependOfCorrectHeight, parent.unprependChild()!, null, false);182} else {183parent.prependChildOfSameHeight(nodeToPrependOfCorrectHeight);184nodeToPrependOfCorrectHeight = undefined;185}186} else {187parent.handleChildrenChanged();188}189}190if (nodeToPrependOfCorrectHeight) {191return ListAstNode.create23(nodeToPrependOfCorrectHeight, list, null, false);192} else {193return list;194}195}196197198