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