monaco-editor-core
Version:
A browser based code editor
987 lines (986 loc) • 35.5 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function getNodeColor(node) {
return ((node.metadata & 1 /* Constants.ColorMask */) >>> 0 /* Constants.ColorOffset */);
}
function setNodeColor(node, color) {
node.metadata = ((node.metadata & 254 /* Constants.ColorMaskInverse */) | (color << 0 /* Constants.ColorOffset */));
}
function getNodeIsVisited(node) {
return ((node.metadata & 2 /* Constants.IsVisitedMask */) >>> 1 /* Constants.IsVisitedOffset */) === 1;
}
function setNodeIsVisited(node, value) {
node.metadata = ((node.metadata & 253 /* Constants.IsVisitedMaskInverse */) | ((value ? 1 : 0) << 1 /* Constants.IsVisitedOffset */));
}
function getNodeIsForValidation(node) {
return ((node.metadata & 4 /* Constants.IsForValidationMask */) >>> 2 /* Constants.IsForValidationOffset */) === 1;
}
function setNodeIsForValidation(node, value) {
node.metadata = ((node.metadata & 251 /* Constants.IsForValidationMaskInverse */) | ((value ? 1 : 0) << 2 /* Constants.IsForValidationOffset */));
}
function getNodeIsInGlyphMargin(node) {
return ((node.metadata & 64 /* Constants.IsMarginMask */) >>> 6 /* Constants.IsMarginOffset */) === 1;
}
function setNodeIsInGlyphMargin(node, value) {
node.metadata = ((node.metadata & 191 /* Constants.IsMarginMaskInverse */) | ((value ? 1 : 0) << 6 /* Constants.IsMarginOffset */));
}
function getNodeStickiness(node) {
return ((node.metadata & 24 /* Constants.StickinessMask */) >>> 3 /* Constants.StickinessOffset */);
}
function _setNodeStickiness(node, stickiness) {
node.metadata = ((node.metadata & 231 /* Constants.StickinessMaskInverse */) | (stickiness << 3 /* Constants.StickinessOffset */));
}
function getCollapseOnReplaceEdit(node) {
return ((node.metadata & 32 /* Constants.CollapseOnReplaceEditMask */) >>> 5 /* Constants.CollapseOnReplaceEditOffset */) === 1;
}
function setCollapseOnReplaceEdit(node, value) {
node.metadata = ((node.metadata & 223 /* Constants.CollapseOnReplaceEditMaskInverse */) | ((value ? 1 : 0) << 5 /* Constants.CollapseOnReplaceEditOffset */));
}
export class IntervalNode {
constructor(id, start, end) {
this.metadata = 0;
this.parent = this;
this.left = this;
this.right = this;
setNodeColor(this, 1 /* NodeColor.Red */);
this.start = start;
this.end = end;
// FORCE_OVERFLOWING_TEST: this.delta = start;
this.delta = 0;
this.maxEnd = end;
this.id = id;
this.ownerId = 0;
this.options = null;
setNodeIsForValidation(this, false);
setNodeIsInGlyphMargin(this, false);
_setNodeStickiness(this, 1 /* TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges */);
setCollapseOnReplaceEdit(this, false);
this.cachedVersionId = 0;
this.cachedAbsoluteStart = start;
this.cachedAbsoluteEnd = end;
this.range = null;
setNodeIsVisited(this, false);
}
reset(versionId, start, end, range) {
this.start = start;
this.end = end;
this.maxEnd = end;
this.cachedVersionId = versionId;
this.cachedAbsoluteStart = start;
this.cachedAbsoluteEnd = end;
this.range = range;
}
setOptions(options) {
this.options = options;
const className = this.options.className;
setNodeIsForValidation(this, (className === "squiggly-error" /* ClassName.EditorErrorDecoration */
|| className === "squiggly-warning" /* ClassName.EditorWarningDecoration */
|| className === "squiggly-info" /* ClassName.EditorInfoDecoration */));
setNodeIsInGlyphMargin(this, this.options.glyphMarginClassName !== null);
_setNodeStickiness(this, this.options.stickiness);
setCollapseOnReplaceEdit(this, this.options.collapseOnReplaceEdit);
}
setCachedOffsets(absoluteStart, absoluteEnd, cachedVersionId) {
if (this.cachedVersionId !== cachedVersionId) {
this.range = null;
}
this.cachedVersionId = cachedVersionId;
this.cachedAbsoluteStart = absoluteStart;
this.cachedAbsoluteEnd = absoluteEnd;
}
detach() {
this.parent = null;
this.left = null;
this.right = null;
}
}
export const SENTINEL = new IntervalNode(null, 0, 0);
SENTINEL.parent = SENTINEL;
SENTINEL.left = SENTINEL;
SENTINEL.right = SENTINEL;
setNodeColor(SENTINEL, 0 /* NodeColor.Black */);
export class IntervalTree {
constructor() {
this.root = SENTINEL;
this.requestNormalizeDelta = false;
}
intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations) {
if (this.root === SENTINEL) {
return [];
}
return intervalSearch(this, start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations);
}
search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations) {
if (this.root === SENTINEL) {
return [];
}
return search(this, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations);
}
/**
* Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes!
*/
collectNodesFromOwner(ownerId) {
return collectNodesFromOwner(this, ownerId);
}
/**
* Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes!
*/
collectNodesPostOrder() {
return collectNodesPostOrder(this);
}
insert(node) {
rbTreeInsert(this, node);
this._normalizeDeltaIfNecessary();
}
delete(node) {
rbTreeDelete(this, node);
this._normalizeDeltaIfNecessary();
}
resolveNode(node, cachedVersionId) {
const initialNode = node;
let delta = 0;
while (node !== this.root) {
if (node === node.parent.right) {
delta += node.parent.delta;
}
node = node.parent;
}
const nodeStart = initialNode.start + delta;
const nodeEnd = initialNode.end + delta;
initialNode.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId);
}
acceptReplace(offset, length, textLength, forceMoveMarkers) {
// Our strategy is to remove all directly impacted nodes, and then add them back to the tree.
// (1) collect all nodes that are intersecting this edit as nodes of interest
const nodesOfInterest = searchForEditing(this, offset, offset + length);
// (2) remove all nodes that are intersecting this edit
for (let i = 0, len = nodesOfInterest.length; i < len; i++) {
const node = nodesOfInterest[i];
rbTreeDelete(this, node);
}
this._normalizeDeltaIfNecessary();
// (3) edit all tree nodes except the nodes of interest
noOverlapReplace(this, offset, offset + length, textLength);
this._normalizeDeltaIfNecessary();
// (4) edit the nodes of interest and insert them back in the tree
for (let i = 0, len = nodesOfInterest.length; i < len; i++) {
const node = nodesOfInterest[i];
node.start = node.cachedAbsoluteStart;
node.end = node.cachedAbsoluteEnd;
nodeAcceptEdit(node, offset, (offset + length), textLength, forceMoveMarkers);
node.maxEnd = node.end;
rbTreeInsert(this, node);
}
this._normalizeDeltaIfNecessary();
}
_normalizeDeltaIfNecessary() {
if (!this.requestNormalizeDelta) {
return;
}
this.requestNormalizeDelta = false;
normalizeDelta(this);
}
}
//#region Delta Normalization
function normalizeDelta(T) {
let node = T.root;
let delta = 0;
while (node !== SENTINEL) {
if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) {
// go left
node = node.left;
continue;
}
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
delta += node.delta;
node = node.right;
continue;
}
// handle current node
node.start = delta + node.start;
node.end = delta + node.end;
node.delta = 0;
recomputeMaxEnd(node);
setNodeIsVisited(node, true);
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
if (node === node.parent.right) {
delta -= node.parent.delta;
}
node = node.parent;
}
setNodeIsVisited(T.root, false);
}
function adjustMarkerBeforeColumn(markerOffset, markerStickToPreviousCharacter, checkOffset, moveSemantics) {
if (markerOffset < checkOffset) {
return true;
}
if (markerOffset > checkOffset) {
return false;
}
if (moveSemantics === 1 /* MarkerMoveSemantics.ForceMove */) {
return false;
}
if (moveSemantics === 2 /* MarkerMoveSemantics.ForceStay */) {
return true;
}
return markerStickToPreviousCharacter;
}
/**
* This is a lot more complicated than strictly necessary to maintain the same behaviour
* as when decorations were implemented using two markers.
*/
export function nodeAcceptEdit(node, start, end, textLength, forceMoveMarkers) {
const nodeStickiness = getNodeStickiness(node);
const startStickToPreviousCharacter = (nodeStickiness === 0 /* TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */
|| nodeStickiness === 2 /* TrackedRangeStickiness.GrowsOnlyWhenTypingBefore */);
const endStickToPreviousCharacter = (nodeStickiness === 1 /* TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges */
|| nodeStickiness === 2 /* TrackedRangeStickiness.GrowsOnlyWhenTypingBefore */);
const deletingCnt = (end - start);
const insertingCnt = textLength;
const commonLength = Math.min(deletingCnt, insertingCnt);
const nodeStart = node.start;
let startDone = false;
const nodeEnd = node.end;
let endDone = false;
if (start <= nodeStart && nodeEnd <= end && getCollapseOnReplaceEdit(node)) {
// This edit encompasses the entire decoration range
// and the decoration has asked to become collapsed
node.start = start;
startDone = true;
node.end = start;
endDone = true;
}
{
const moveSemantics = forceMoveMarkers ? 1 /* MarkerMoveSemantics.ForceMove */ : (deletingCnt > 0 ? 2 /* MarkerMoveSemantics.ForceStay */ : 0 /* MarkerMoveSemantics.MarkerDefined */);
if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start, moveSemantics)) {
startDone = true;
}
if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, start, moveSemantics)) {
endDone = true;
}
}
if (commonLength > 0 && !forceMoveMarkers) {
const moveSemantics = (deletingCnt > insertingCnt ? 2 /* MarkerMoveSemantics.ForceStay */ : 0 /* MarkerMoveSemantics.MarkerDefined */);
if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start + commonLength, moveSemantics)) {
startDone = true;
}
if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, start + commonLength, moveSemantics)) {
endDone = true;
}
}
{
const moveSemantics = forceMoveMarkers ? 1 /* MarkerMoveSemantics.ForceMove */ : 0 /* MarkerMoveSemantics.MarkerDefined */;
if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, end, moveSemantics)) {
node.start = start + insertingCnt;
startDone = true;
}
if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, end, moveSemantics)) {
node.end = start + insertingCnt;
endDone = true;
}
}
// Finish
const deltaColumn = (insertingCnt - deletingCnt);
if (!startDone) {
node.start = Math.max(0, nodeStart + deltaColumn);
}
if (!endDone) {
node.end = Math.max(0, nodeEnd + deltaColumn);
}
if (node.start > node.end) {
node.end = node.start;
}
}
function searchForEditing(T, start, end) {
// https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
// Now, it is known that two intervals A and B overlap only when both
// A.low <= B.high and A.high >= B.low. When searching the trees for
// nodes overlapping with a given interval, you can immediately skip:
// a) all nodes to the right of nodes whose low value is past the end of the given interval.
// b) all nodes that have their maximum 'high' value below the start of the given interval.
let node = T.root;
let delta = 0;
let nodeMaxEnd = 0;
let nodeStart = 0;
let nodeEnd = 0;
const result = [];
let resultLen = 0;
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
if (node === node.parent.right) {
delta -= node.parent.delta;
}
node = node.parent;
continue;
}
if (!getNodeIsVisited(node.left)) {
// first time seeing this node
nodeMaxEnd = delta + node.maxEnd;
if (nodeMaxEnd < start) {
// cover case b) from above
// there is no need to search this node or its children
setNodeIsVisited(node, true);
continue;
}
if (node.left !== SENTINEL) {
// go left
node = node.left;
continue;
}
}
// handle current node
nodeStart = delta + node.start;
if (nodeStart > end) {
// cover case a) from above
// there is no need to search this node or its right subtree
setNodeIsVisited(node, true);
continue;
}
nodeEnd = delta + node.end;
if (nodeEnd >= start) {
node.setCachedOffsets(nodeStart, nodeEnd, 0);
result[resultLen++] = node;
}
setNodeIsVisited(node, true);
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
delta += node.delta;
node = node.right;
continue;
}
}
setNodeIsVisited(T.root, false);
return result;
}
function noOverlapReplace(T, start, end, textLength) {
// https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
// Now, it is known that two intervals A and B overlap only when both
// A.low <= B.high and A.high >= B.low. When searching the trees for
// nodes overlapping with a given interval, you can immediately skip:
// a) all nodes to the right of nodes whose low value is past the end of the given interval.
// b) all nodes that have their maximum 'high' value below the start of the given interval.
let node = T.root;
let delta = 0;
let nodeMaxEnd = 0;
let nodeStart = 0;
const editDelta = (textLength - (end - start));
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
if (node === node.parent.right) {
delta -= node.parent.delta;
}
recomputeMaxEnd(node);
node = node.parent;
continue;
}
if (!getNodeIsVisited(node.left)) {
// first time seeing this node
nodeMaxEnd = delta + node.maxEnd;
if (nodeMaxEnd < start) {
// cover case b) from above
// there is no need to search this node or its children
setNodeIsVisited(node, true);
continue;
}
if (node.left !== SENTINEL) {
// go left
node = node.left;
continue;
}
}
// handle current node
nodeStart = delta + node.start;
if (nodeStart > end) {
node.start += editDelta;
node.end += editDelta;
node.delta += editDelta;
if (node.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || node.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
// cover case a) from above
// there is no need to search this node or its right subtree
setNodeIsVisited(node, true);
continue;
}
setNodeIsVisited(node, true);
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
delta += node.delta;
node = node.right;
continue;
}
}
setNodeIsVisited(T.root, false);
}
//#endregion
//#region Searching
function collectNodesFromOwner(T, ownerId) {
let node = T.root;
const result = [];
let resultLen = 0;
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
node = node.parent;
continue;
}
if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) {
// go left
node = node.left;
continue;
}
// handle current node
if (node.ownerId === ownerId) {
result[resultLen++] = node;
}
setNodeIsVisited(node, true);
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
node = node.right;
continue;
}
}
setNodeIsVisited(T.root, false);
return result;
}
function collectNodesPostOrder(T) {
let node = T.root;
const result = [];
let resultLen = 0;
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
node = node.parent;
continue;
}
if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) {
// go left
node = node.left;
continue;
}
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
node = node.right;
continue;
}
// handle current node
result[resultLen++] = node;
setNodeIsVisited(node, true);
}
setNodeIsVisited(T.root, false);
return result;
}
function search(T, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations) {
let node = T.root;
let delta = 0;
let nodeStart = 0;
let nodeEnd = 0;
const result = [];
let resultLen = 0;
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
if (node === node.parent.right) {
delta -= node.parent.delta;
}
node = node.parent;
continue;
}
if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) {
// go left
node = node.left;
continue;
}
// handle current node
nodeStart = delta + node.start;
nodeEnd = delta + node.end;
node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId);
let include = true;
if (filterOwnerId && node.ownerId && node.ownerId !== filterOwnerId) {
include = false;
}
if (filterOutValidation && getNodeIsForValidation(node)) {
include = false;
}
if (onlyMarginDecorations && !getNodeIsInGlyphMargin(node)) {
include = false;
}
if (include) {
result[resultLen++] = node;
}
setNodeIsVisited(node, true);
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
delta += node.delta;
node = node.right;
continue;
}
}
setNodeIsVisited(T.root, false);
return result;
}
function intervalSearch(T, intervalStart, intervalEnd, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations) {
// https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
// Now, it is known that two intervals A and B overlap only when both
// A.low <= B.high and A.high >= B.low. When searching the trees for
// nodes overlapping with a given interval, you can immediately skip:
// a) all nodes to the right of nodes whose low value is past the end of the given interval.
// b) all nodes that have their maximum 'high' value below the start of the given interval.
let node = T.root;
let delta = 0;
let nodeMaxEnd = 0;
let nodeStart = 0;
let nodeEnd = 0;
const result = [];
let resultLen = 0;
while (node !== SENTINEL) {
if (getNodeIsVisited(node)) {
// going up from this node
setNodeIsVisited(node.left, false);
setNodeIsVisited(node.right, false);
if (node === node.parent.right) {
delta -= node.parent.delta;
}
node = node.parent;
continue;
}
if (!getNodeIsVisited(node.left)) {
// first time seeing this node
nodeMaxEnd = delta + node.maxEnd;
if (nodeMaxEnd < intervalStart) {
// cover case b) from above
// there is no need to search this node or its children
setNodeIsVisited(node, true);
continue;
}
if (node.left !== SENTINEL) {
// go left
node = node.left;
continue;
}
}
// handle current node
nodeStart = delta + node.start;
if (nodeStart > intervalEnd) {
// cover case a) from above
// there is no need to search this node or its right subtree
setNodeIsVisited(node, true);
continue;
}
nodeEnd = delta + node.end;
if (nodeEnd >= intervalStart) {
// There is overlap
node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId);
let include = true;
if (filterOwnerId && node.ownerId && node.ownerId !== filterOwnerId) {
include = false;
}
if (filterOutValidation && getNodeIsForValidation(node)) {
include = false;
}
if (onlyMarginDecorations && !getNodeIsInGlyphMargin(node)) {
include = false;
}
if (include) {
result[resultLen++] = node;
}
}
setNodeIsVisited(node, true);
if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) {
// go right
delta += node.delta;
node = node.right;
continue;
}
}
setNodeIsVisited(T.root, false);
return result;
}
//#endregion
//#region Insertion
function rbTreeInsert(T, newNode) {
if (T.root === SENTINEL) {
newNode.parent = SENTINEL;
newNode.left = SENTINEL;
newNode.right = SENTINEL;
setNodeColor(newNode, 0 /* NodeColor.Black */);
T.root = newNode;
return T.root;
}
treeInsert(T, newNode);
recomputeMaxEndWalkToRoot(newNode.parent);
// repair tree
let x = newNode;
while (x !== T.root && getNodeColor(x.parent) === 1 /* NodeColor.Red */) {
if (x.parent === x.parent.parent.left) {
const y = x.parent.parent.right;
if (getNodeColor(y) === 1 /* NodeColor.Red */) {
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(y, 0 /* NodeColor.Black */);
setNodeColor(x.parent.parent, 1 /* NodeColor.Red */);
x = x.parent.parent;
}
else {
if (x === x.parent.right) {
x = x.parent;
leftRotate(T, x);
}
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(x.parent.parent, 1 /* NodeColor.Red */);
rightRotate(T, x.parent.parent);
}
}
else {
const y = x.parent.parent.left;
if (getNodeColor(y) === 1 /* NodeColor.Red */) {
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(y, 0 /* NodeColor.Black */);
setNodeColor(x.parent.parent, 1 /* NodeColor.Red */);
x = x.parent.parent;
}
else {
if (x === x.parent.left) {
x = x.parent;
rightRotate(T, x);
}
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(x.parent.parent, 1 /* NodeColor.Red */);
leftRotate(T, x.parent.parent);
}
}
}
setNodeColor(T.root, 0 /* NodeColor.Black */);
return newNode;
}
function treeInsert(T, z) {
let delta = 0;
let x = T.root;
const zAbsoluteStart = z.start;
const zAbsoluteEnd = z.end;
while (true) {
const cmp = intervalCompare(zAbsoluteStart, zAbsoluteEnd, x.start + delta, x.end + delta);
if (cmp < 0) {
// this node should be inserted to the left
// => it is not affected by the node's delta
if (x.left === SENTINEL) {
z.start -= delta;
z.end -= delta;
z.maxEnd -= delta;
x.left = z;
break;
}
else {
x = x.left;
}
}
else {
// this node should be inserted to the right
// => it is not affected by the node's delta
if (x.right === SENTINEL) {
z.start -= (delta + x.delta);
z.end -= (delta + x.delta);
z.maxEnd -= (delta + x.delta);
x.right = z;
break;
}
else {
delta += x.delta;
x = x.right;
}
}
}
z.parent = x;
z.left = SENTINEL;
z.right = SENTINEL;
setNodeColor(z, 1 /* NodeColor.Red */);
}
//#endregion
//#region Deletion
function rbTreeDelete(T, z) {
let x;
let y;
// RB-DELETE except we don't swap z and y in case c)
// i.e. we always delete what's pointed at by z.
if (z.left === SENTINEL) {
x = z.right;
y = z;
// x's delta is no longer influenced by z's delta
x.delta += z.delta;
if (x.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || x.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
x.start += z.delta;
x.end += z.delta;
}
else if (z.right === SENTINEL) {
x = z.left;
y = z;
}
else {
y = leftest(z.right);
x = y.right;
// y's delta is no longer influenced by z's delta,
// but we don't want to walk the entire right-hand-side subtree of x.
// we therefore maintain z's delta in y, and adjust only x
x.start += y.delta;
x.end += y.delta;
x.delta += y.delta;
if (x.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || x.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
y.start += z.delta;
y.end += z.delta;
y.delta = z.delta;
if (y.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || y.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
}
if (y === T.root) {
T.root = x;
setNodeColor(x, 0 /* NodeColor.Black */);
z.detach();
resetSentinel();
recomputeMaxEnd(x);
T.root.parent = SENTINEL;
return;
}
const yWasRed = (getNodeColor(y) === 1 /* NodeColor.Red */);
if (y === y.parent.left) {
y.parent.left = x;
}
else {
y.parent.right = x;
}
if (y === z) {
x.parent = y.parent;
}
else {
if (y.parent === z) {
x.parent = y;
}
else {
x.parent = y.parent;
}
y.left = z.left;
y.right = z.right;
y.parent = z.parent;
setNodeColor(y, getNodeColor(z));
if (z === T.root) {
T.root = y;
}
else {
if (z === z.parent.left) {
z.parent.left = y;
}
else {
z.parent.right = y;
}
}
if (y.left !== SENTINEL) {
y.left.parent = y;
}
if (y.right !== SENTINEL) {
y.right.parent = y;
}
}
z.detach();
if (yWasRed) {
recomputeMaxEndWalkToRoot(x.parent);
if (y !== z) {
recomputeMaxEndWalkToRoot(y);
recomputeMaxEndWalkToRoot(y.parent);
}
resetSentinel();
return;
}
recomputeMaxEndWalkToRoot(x);
recomputeMaxEndWalkToRoot(x.parent);
if (y !== z) {
recomputeMaxEndWalkToRoot(y);
recomputeMaxEndWalkToRoot(y.parent);
}
// RB-DELETE-FIXUP
let w;
while (x !== T.root && getNodeColor(x) === 0 /* NodeColor.Black */) {
if (x === x.parent.left) {
w = x.parent.right;
if (getNodeColor(w) === 1 /* NodeColor.Red */) {
setNodeColor(w, 0 /* NodeColor.Black */);
setNodeColor(x.parent, 1 /* NodeColor.Red */);
leftRotate(T, x.parent);
w = x.parent.right;
}
if (getNodeColor(w.left) === 0 /* NodeColor.Black */ && getNodeColor(w.right) === 0 /* NodeColor.Black */) {
setNodeColor(w, 1 /* NodeColor.Red */);
x = x.parent;
}
else {
if (getNodeColor(w.right) === 0 /* NodeColor.Black */) {
setNodeColor(w.left, 0 /* NodeColor.Black */);
setNodeColor(w, 1 /* NodeColor.Red */);
rightRotate(T, w);
w = x.parent.right;
}
setNodeColor(w, getNodeColor(x.parent));
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(w.right, 0 /* NodeColor.Black */);
leftRotate(T, x.parent);
x = T.root;
}
}
else {
w = x.parent.left;
if (getNodeColor(w) === 1 /* NodeColor.Red */) {
setNodeColor(w, 0 /* NodeColor.Black */);
setNodeColor(x.parent, 1 /* NodeColor.Red */);
rightRotate(T, x.parent);
w = x.parent.left;
}
if (getNodeColor(w.left) === 0 /* NodeColor.Black */ && getNodeColor(w.right) === 0 /* NodeColor.Black */) {
setNodeColor(w, 1 /* NodeColor.Red */);
x = x.parent;
}
else {
if (getNodeColor(w.left) === 0 /* NodeColor.Black */) {
setNodeColor(w.right, 0 /* NodeColor.Black */);
setNodeColor(w, 1 /* NodeColor.Red */);
leftRotate(T, w);
w = x.parent.left;
}
setNodeColor(w, getNodeColor(x.parent));
setNodeColor(x.parent, 0 /* NodeColor.Black */);
setNodeColor(w.left, 0 /* NodeColor.Black */);
rightRotate(T, x.parent);
x = T.root;
}
}
}
setNodeColor(x, 0 /* NodeColor.Black */);
resetSentinel();
}
function leftest(node) {
while (node.left !== SENTINEL) {
node = node.left;
}
return node;
}
function resetSentinel() {
SENTINEL.parent = SENTINEL;
SENTINEL.delta = 0; // optional
SENTINEL.start = 0; // optional
SENTINEL.end = 0; // optional
}
//#endregion
//#region Rotations
function leftRotate(T, x) {
const y = x.right; // set y.
y.delta += x.delta; // y's delta is no longer influenced by x's delta
if (y.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || y.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
y.start += x.delta;
y.end += x.delta;
x.right = y.left; // turn y's left subtree into x's right subtree.
if (y.left !== SENTINEL) {
y.left.parent = x;
}
y.parent = x.parent; // link x's parent to y.
if (x.parent === SENTINEL) {
T.root = y;
}
else if (x === x.parent.left) {
x.parent.left = y;
}
else {
x.parent.right = y;
}
y.left = x; // put x on y's left.
x.parent = y;
recomputeMaxEnd(x);
recomputeMaxEnd(y);
}
function rightRotate(T, y) {
const x = y.left;
y.delta -= x.delta;
if (y.delta < -1073741824 /* Constants.MIN_SAFE_DELTA */ || y.delta > 1073741824 /* Constants.MAX_SAFE_DELTA */) {
T.requestNormalizeDelta = true;
}
y.start -= x.delta;
y.end -= x.delta;
y.left = x.right;
if (x.right !== SENTINEL) {
x.right.parent = y;
}
x.parent = y.parent;
if (y.parent === SENTINEL) {
T.root = x;
}
else if (y === y.parent.right) {
y.parent.right = x;
}
else {
y.parent.left = x;
}
x.right = y;
y.parent = x;
recomputeMaxEnd(y);
recomputeMaxEnd(x);
}
//#endregion
//#region max end computation
function computeMaxEnd(node) {
let maxEnd = node.end;
if (node.left !== SENTINEL) {
const leftMaxEnd = node.left.maxEnd;
if (leftMaxEnd > maxEnd) {
maxEnd = leftMaxEnd;
}
}
if (node.right !== SENTINEL) {
const rightMaxEnd = node.right.maxEnd + node.delta;
if (rightMaxEnd > maxEnd) {
maxEnd = rightMaxEnd;
}
}
return maxEnd;
}
export function recomputeMaxEnd(node) {
node.maxEnd = computeMaxEnd(node);
}
function recomputeMaxEndWalkToRoot(node) {
while (node !== SENTINEL) {
const maxEnd = computeMaxEnd(node);
if (node.maxEnd === maxEnd) {
// no need to go further
return;
}
node.maxEnd = maxEnd;
node = node.parent;
}
}
//#endregion
//#region utils
export function intervalCompare(aStart, aEnd, bStart, bEnd) {
if (aStart === bStart) {
return aEnd - bEnd;
}
return aStart - bStart;
}
//#endregion