diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 9598964eff93fef6ccfca4f32b957c8584ced00a..3c98e8b6b31cae17dc6082bb1c98c0772f4704a0 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -8,49 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; import { CharCode } from 'vs/base/common/charCode'; import { Range } from 'vs/editor/common/core/range'; import { ITextSnapshot } from 'vs/platform/files/common/files'; - -export const enum NodeColor { - Black = 0, - Red = 1, -} - -export function getNodeColor(node: TreeNode) { - return node.color; -} - -function leftest(node: TreeNode): TreeNode { - while (node.left !== SENTINEL) { - node = node.left; - } - return node; -} - -function righttest(node: TreeNode): TreeNode { - while (node.right !== SENTINEL) { - node = node.right; - } - return node; -} - -function calculateSize(node: TreeNode): number { - if (node === SENTINEL) { - return 0; - } - - return node.size_left + node.piece.length + calculateSize(node.right); -} - -function calculateLF(node: TreeNode): number { - if (node === SENTINEL) { - return 0; - } - - return node.lf_left + node.piece.lineFeedCnt + calculateLF(node.right); -} - -function resetSentinel(): void { - SENTINEL.parent = SENTINEL; -} +import { leftest, righttest, updateTreeMetadata, rbDelete, fixInsert, NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; // const lfRegex = new RegExp(/\r\n|\r|\n/g); @@ -138,84 +96,6 @@ export function createLineStarts(r: number[], str: string): LineStarts { return result; } -export class TreeNode { - parent: TreeNode; - left: TreeNode; - right: TreeNode; - color: NodeColor; - - // Piece - piece: Piece; - size_left: number; // size of the left subtree (not inorder) - lf_left: number; // line feeds cnt in the left subtree (not in order) - - constructor(piece: Piece, color: NodeColor) { - this.piece = piece; - this.color = color; - this.size_left = 0; - this.lf_left = 0; - this.parent = null; - this.left = null; - this.right = null; - } - - public next(): TreeNode { - if (this.right !== SENTINEL) { - return leftest(this.right); - } - - let node: TreeNode = this; - - while (node.parent !== SENTINEL) { - if (node.parent.left === node) { - break; - } - - node = node.parent; - } - - if (node.parent === SENTINEL) { - return SENTINEL; - } else { - return node.parent; - } - } - - public prev(): TreeNode { - if (this.left !== SENTINEL) { - return righttest(this.left); - } - - let node: TreeNode = this; - - while (node.parent !== SENTINEL) { - if (node.parent.right === node) { - break; - } - - node = node.parent; - } - - if (node.parent === SENTINEL) { - return SENTINEL; - } else { - return node.parent; - } - } - - public detach(): void { - this.parent = null; - this.left = null; - this.right = null; - } -} - -export const SENTINEL: TreeNode = new TreeNode(null, NodeColor.Black); -SENTINEL.parent = SENTINEL; -SENTINEL.left = SENTINEL; -SENTINEL.right = SENTINEL; -SENTINEL.color = NodeColor.Black; - export interface NodePosition { /** * Piece Index @@ -393,7 +273,6 @@ export class PieceTreeBase { this.create(chunks); } - // #region Buffer API public createSnapshot(BOM: string): ITextSnapshot { return new PieceTreeSnapshot(this, BOM); @@ -654,7 +533,7 @@ export class PieceTreeBase { if (startPosition.nodeStartOffset === offset) { if (cnt === startNode.piece.length) { // delete node let next = startNode.next(); - this.rbDelete(startNode); + rbDelete(this, startNode); this.validateCRLFWithPrevNode(next); this.computeBufferMetadata(); return; @@ -718,7 +597,7 @@ export class PieceTreeBase { piece.length -= 1; value += '\n'; - this.updateTreeMetadata(node, -1, -1); + updateTreeMetadata(this, node, -1, -1); if (node.piece.length === 0) { nodesToDel.push(node); @@ -822,7 +701,7 @@ export class PieceTreeBase { deleteNodes(nodes: TreeNode[]): void { for (let i = 0; i < nodes.length; i++) { - this.rbDelete(nodes[i]); + rbDelete(this, nodes[i]); } } @@ -978,7 +857,7 @@ export class PieceTreeBase { let lf_delta = piece.lineFeedCnt - originalLFCnt; let size_delta = newEndOffset - originalEndOffset; piece.length += size_delta; - this.updateTreeMetadata(node, size_delta, lf_delta); + updateTreeMetadata(this, node, size_delta, lf_delta); } deleteNodeHead(node: TreeNode, pos: BufferCursor) { @@ -992,7 +871,7 @@ export class PieceTreeBase { let lf_delta = piece.lineFeedCnt - originalLFCnt; let size_delta = originalStartOffset - newStartOffset; piece.length += size_delta; - this.updateTreeMetadata(node, size_delta, lf_delta); + updateTreeMetadata(this, node, size_delta, lf_delta); } shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) { @@ -1008,7 +887,7 @@ export class PieceTreeBase { let newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos); let newLFCnt = piece.lineFeedCnt; piece.length = newLength; - this.updateTreeMetadata(node, newLength - oldLength, newLFCnt - oldLFCnt); + updateTreeMetadata(this, node, newLength - oldLength, newLFCnt - oldLFCnt); // new right piece, end, originalEndPos let newPiece = new Piece( @@ -1052,7 +931,7 @@ export class PieceTreeBase { node.piece.lineFeedCnt = newLineFeedCnt; let lf_delta = newLineFeedCnt - oldLineFeedCnt; this._lastChangeBufferPos = endPos; - this.updateTreeMetadata(node, value.length, lf_delta); + updateTreeMetadata(this, node, value.length, lf_delta); } nodeAt(offset: number): NodePosition { @@ -1247,7 +1126,7 @@ export class PieceTreeBase { prev.piece.length -= 1; prev.piece.lineFeedCnt -= 1; - this.updateTreeMetadata(prev, - 1, -1); + updateTreeMetadata(this, prev, - 1, -1); if (prev.piece.length === 0) { nodesToDel.push(prev); } @@ -1259,7 +1138,7 @@ export class PieceTreeBase { next.piece.lineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, next.piece.start, next.piece.end); // @todo, we can optimize // } - this.updateTreeMetadata(next, - 1, -1); + updateTreeMetadata(this, next, - 1, -1); if (next.piece.length === 0) { nodesToDel.push(next); } @@ -1270,7 +1149,7 @@ export class PieceTreeBase { // delete empty nodes for (let i = 0; i < nodesToDel.length; i++) { - this.rbDelete(nodesToDel[i]); + rbDelete(this, nodesToDel[i]); } } @@ -1282,7 +1161,7 @@ export class PieceTreeBase { value += '\n'; if (nextNode.piece.length === 1) { - this.rbDelete(nextNode); + rbDelete(this, nextNode); } else { let piece = nextNode.piece; @@ -1290,7 +1169,7 @@ export class PieceTreeBase { piece.start = newStart; piece.length -= 1; piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end); // @todo, we can optimize - this.updateTreeMetadata(nextNode, -1, -1); + updateTreeMetadata(this, nextNode, -1, -1); } return true; } @@ -1303,7 +1182,7 @@ export class PieceTreeBase { // #endregion - // #region Red Black Tree + // #region Tree operations iterate(node: TreeNode, callback: (node: TreeNode) => boolean): boolean { if (node === SENTINEL) { return callback(SENTINEL); @@ -1330,53 +1209,6 @@ export class PieceTreeBase { return currentContent; } - leftRotate(x: TreeNode) { - let y = x.right; - - // fix size_left - y.size_left += x.size_left + (x.piece ? x.piece.length : 0); - y.lf_left += x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0); - x.right = y.left; - - if (y.left !== SENTINEL) { - y.left.parent = x; - } - y.parent = x.parent; - if (x.parent === SENTINEL) { - this.root = y; - } else if (x.parent.left === x) { - x.parent.left = y; - } else { - x.parent.right = y; - } - y.left = x; - x.parent = y; - } - - rightRotate(y: TreeNode) { - let x = y.left; - y.left = x.right; - if (x.right !== SENTINEL) { - x.right.parent = y; - } - x.parent = y.parent; - - // fix size_left - y.size_left -= x.size_left + (x.piece ? x.piece.length : 0); - y.lf_left -= x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0); - - if (y.parent === SENTINEL) { - this.root = x; - } else if (y === y.parent.right) { - y.parent.right = x; - } else { - y.parent.left = x; - } - - x.right = y; - y.parent = x; - } - /** * node node * / \ / \ @@ -1405,7 +1237,7 @@ export class PieceTreeBase { z.parent = nextNode; } - this.fixInsert(z); + fixInsert(this, z); return z; } @@ -1437,266 +1269,10 @@ export class PieceTreeBase { z.parent = prevNode; } - this.fixInsert(z); + fixInsert(this, z); return z; } - rbDelete(z: TreeNode) { - let x: TreeNode; - let y: TreeNode; - - if (z.left === SENTINEL) { - y = z; - x = y.right; - } else if (z.right === SENTINEL) { - y = z; - x = y.left; - } else { - y = leftest(z.right); - x = y.right; - } - - if (y === this.root) { - this.root = x; - - // if x is null, we are removing the only node - x.color = NodeColor.Black; - z.detach(); - resetSentinel(); - this.root.parent = SENTINEL; - - return; - } - - let yWasRed = (y.color === NodeColor.Red); - - if (y === y.parent.left) { - y.parent.left = x; - } else { - y.parent.right = x; - } - - if (y === z) { - x.parent = y.parent; - this.recomputeTreeMetadata(x); - } else { - if (y.parent === z) { - x.parent = y; - } else { - x.parent = y.parent; - } - - // as we make changes to x's hierarchy, update size_left of subtree first - this.recomputeTreeMetadata(x); - - y.left = z.left; - y.right = z.right; - y.parent = z.parent; - y.color = z.color; - - if (z === this.root) { - this.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; - } - // update metadata - // we replace z with y, so in this sub tree, the length change is z.item.length - y.size_left = z.size_left; - y.lf_left = z.lf_left; - this.recomputeTreeMetadata(y); - } - - z.detach(); - - if (x.parent.left === x) { - let newSizeLeft = calculateSize(x); - let newLFLeft = calculateLF(x); - if (newSizeLeft !== x.parent.size_left || newLFLeft !== x.parent.lf_left) { - let delta = newSizeLeft - x.parent.size_left; - let lf_delta = newLFLeft - x.parent.lf_left; - x.parent.size_left = newSizeLeft; - x.parent.lf_left = newLFLeft; - this.updateTreeMetadata(x.parent, delta, lf_delta); - } - } - - this.recomputeTreeMetadata(x.parent); - - if (yWasRed) { - resetSentinel(); - return; - } - - // RB-DELETE-FIXUP - let w: TreeNode; - while (x !== this.root && x.color === NodeColor.Black) { - if (x === x.parent.left) { - w = x.parent.right; - - if (w.color === NodeColor.Red) { - w.color = NodeColor.Black; - x.parent.color = NodeColor.Red; - this.leftRotate(x.parent); - w = x.parent.right; - } - - if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) { - w.color = NodeColor.Red; - x = x.parent; - } else { - if (w.right.color === NodeColor.Black) { - w.left.color = NodeColor.Black; - w.color = NodeColor.Red; - this.rightRotate(w); - w = x.parent.right; - } - - w.color = x.parent.color; - x.parent.color = NodeColor.Black; - w.right.color = NodeColor.Black; - this.leftRotate(x.parent); - x = this.root; - } - } else { - w = x.parent.left; - - if (w.color === NodeColor.Red) { - w.color = NodeColor.Black; - x.parent.color = NodeColor.Red; - this.rightRotate(x.parent); - w = x.parent.left; - } - - if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) { - w.color = NodeColor.Red; - x = x.parent; - - } else { - if (w.left.color === NodeColor.Black) { - w.right.color = NodeColor.Black; - w.color = NodeColor.Red; - this.leftRotate(w); - w = x.parent.left; - } - - w.color = x.parent.color; - x.parent.color = NodeColor.Black; - w.left.color = NodeColor.Black; - this.rightRotate(x.parent); - x = this.root; - } - } - } - x.color = NodeColor.Black; - resetSentinel(); - } - - fixInsert(x: TreeNode) { - this.recomputeTreeMetadata(x); - - while (x !== this.root && x.parent.color === NodeColor.Red) { - if (x.parent === x.parent.parent.left) { - const y = x.parent.parent.right; - - if (y.color === NodeColor.Red) { - x.parent.color = NodeColor.Black; - y.color = NodeColor.Black; - x.parent.parent.color = NodeColor.Red; - x = x.parent.parent; - } else { - if (x === x.parent.right) { - x = x.parent; - this.leftRotate(x); - } - - x.parent.color = NodeColor.Black; - x.parent.parent.color = NodeColor.Red; - this.rightRotate(x.parent.parent); - } - } else { - const y = x.parent.parent.left; - - if (y.color === NodeColor.Red) { - x.parent.color = NodeColor.Black; - y.color = NodeColor.Black; - x.parent.parent.color = NodeColor.Red; - x = x.parent.parent; - } else { - if (x === x.parent.left) { - x = x.parent; - this.rightRotate(x); - } - x.parent.color = NodeColor.Black; - x.parent.parent.color = NodeColor.Red; - this.leftRotate(x.parent.parent); - } - } - } - - this.root.color = NodeColor.Black; - } - - updateTreeMetadata(x: TreeNode, delta: number, lineFeedCntDelta: number): void { - // node length change or line feed count change - while (x !== this.root && x !== SENTINEL) { - if (x.parent.left === x) { - x.parent.size_left += delta; - x.parent.lf_left += lineFeedCntDelta; - } - - x = x.parent; - } - } - - recomputeTreeMetadata(x: TreeNode) { - let delta = 0; - let lf_delta = 0; - if (x === this.root) { - return; - } - - if (delta === 0) { - // go upwards till the node whose left subtree is changed. - while (x !== this.root && x === x.parent.right) { - x = x.parent; - } - - if (x === this.root) { - // well, it means we add a node to the end (inorder) - return; - } - - // x is the node whose right subtree is changed. - x = x.parent; - - delta = calculateSize(x.left) - x.size_left; - lf_delta = calculateLF(x.left) - x.lf_left; - x.size_left += delta; - x.lf_left += lf_delta; - } - - // go upwards till root. O(logN) - while (x !== this.root && (delta !== 0 || lf_delta !== 0)) { - if (x.parent.left === x) { - x.parent.size_left += delta; - x.parent.lf_left += lf_delta; - } - - x = x.parent; - } - } - getContentOfSubTree(node: TreeNode): string { let str = ''; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts new file mode 100644 index 0000000000000000000000000000000000000000..84417b947cd7b47d1d162eab284b601f1770b359 --- /dev/null +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts @@ -0,0 +1,427 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Piece, PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; + +export class TreeNode { + parent: TreeNode; + left: TreeNode; + right: TreeNode; + color: NodeColor; + + // Piece + piece: Piece; + size_left: number; // size of the left subtree (not inorder) + lf_left: number; // line feeds cnt in the left subtree (not in order) + + constructor(piece: Piece, color: NodeColor) { + this.piece = piece; + this.color = color; + this.size_left = 0; + this.lf_left = 0; + this.parent = null; + this.left = null; + this.right = null; + } + + public next(): TreeNode { + if (this.right !== SENTINEL) { + return leftest(this.right); + } + + let node: TreeNode = this; + + while (node.parent !== SENTINEL) { + if (node.parent.left === node) { + break; + } + + node = node.parent; + } + + if (node.parent === SENTINEL) { + return SENTINEL; + } else { + return node.parent; + } + } + + public prev(): TreeNode { + if (this.left !== SENTINEL) { + return righttest(this.left); + } + + let node: TreeNode = this; + + while (node.parent !== SENTINEL) { + if (node.parent.right === node) { + break; + } + + node = node.parent; + } + + if (node.parent === SENTINEL) { + return SENTINEL; + } else { + return node.parent; + } + } + + public detach(): void { + this.parent = null; + this.left = null; + this.right = null; + } +} + +export const SENTINEL: TreeNode = new TreeNode(null, NodeColor.Black); +SENTINEL.parent = SENTINEL; +SENTINEL.left = SENTINEL; +SENTINEL.right = SENTINEL; +SENTINEL.color = NodeColor.Black; + +export const enum NodeColor { + Black = 0, + Red = 1, +} + +export function leftest(node: TreeNode): TreeNode { + while (node.left !== SENTINEL) { + node = node.left; + } + return node; +} + +export function righttest(node: TreeNode): TreeNode { + while (node.right !== SENTINEL) { + node = node.right; + } + return node; +} + +export function calculateSize(node: TreeNode): number { + if (node === SENTINEL) { + return 0; + } + + return node.size_left + node.piece.length + calculateSize(node.right); +} + +export function calculateLF(node: TreeNode): number { + if (node === SENTINEL) { + return 0; + } + + return node.lf_left + node.piece.lineFeedCnt + calculateLF(node.right); +} + +export function resetSentinel(): void { + SENTINEL.parent = SENTINEL; +} + +export function leftRotate(tree: PieceTreeBase, x: TreeNode) { + let y = x.right; + + // fix size_left + y.size_left += x.size_left + (x.piece ? x.piece.length : 0); + y.lf_left += x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0); + x.right = y.left; + + if (y.left !== SENTINEL) { + y.left.parent = x; + } + y.parent = x.parent; + if (x.parent === SENTINEL) { + tree.root = y; + } else if (x.parent.left === x) { + x.parent.left = y; + } else { + x.parent.right = y; + } + y.left = x; + x.parent = y; +} + +export function rightRotate(tree: PieceTreeBase, y: TreeNode) { + let x = y.left; + y.left = x.right; + if (x.right !== SENTINEL) { + x.right.parent = y; + } + x.parent = y.parent; + + // fix size_left + y.size_left -= x.size_left + (x.piece ? x.piece.length : 0); + y.lf_left -= x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0); + + if (y.parent === SENTINEL) { + tree.root = x; + } else if (y === y.parent.right) { + y.parent.right = x; + } else { + y.parent.left = x; + } + + x.right = y; + y.parent = x; +} + +export function rbDelete(tree: PieceTreeBase, z: TreeNode) { + let x: TreeNode; + let y: TreeNode; + + if (z.left === SENTINEL) { + y = z; + x = y.right; + } else if (z.right === SENTINEL) { + y = z; + x = y.left; + } else { + y = leftest(z.right); + x = y.right; + } + + if (y === tree.root) { + tree.root = x; + + // if x is null, we are removing the only node + x.color = NodeColor.Black; + z.detach(); + resetSentinel(); + tree.root.parent = SENTINEL; + + return; + } + + let yWasRed = (y.color === NodeColor.Red); + + if (y === y.parent.left) { + y.parent.left = x; + } else { + y.parent.right = x; + } + + if (y === z) { + x.parent = y.parent; + recomputeTreeMetadata(tree, x); + } else { + if (y.parent === z) { + x.parent = y; + } else { + x.parent = y.parent; + } + + // as we make changes to x's hierarchy, update size_left of subtree first + recomputeTreeMetadata(tree, x); + + y.left = z.left; + y.right = z.right; + y.parent = z.parent; + y.color = z.color; + + if (z === tree.root) { + tree.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; + } + // update metadata + // we replace z with y, so in this sub tree, the length change is z.item.length + y.size_left = z.size_left; + y.lf_left = z.lf_left; + recomputeTreeMetadata(tree, y); + } + + z.detach(); + + if (x.parent.left === x) { + let newSizeLeft = calculateSize(x); + let newLFLeft = calculateLF(x); + if (newSizeLeft !== x.parent.size_left || newLFLeft !== x.parent.lf_left) { + let delta = newSizeLeft - x.parent.size_left; + let lf_delta = newLFLeft - x.parent.lf_left; + x.parent.size_left = newSizeLeft; + x.parent.lf_left = newLFLeft; + updateTreeMetadata(tree, x.parent, delta, lf_delta); + } + } + + recomputeTreeMetadata(tree, x.parent); + + if (yWasRed) { + resetSentinel(); + return; + } + + // RB-DELETE-FIXUP + let w: TreeNode; + while (x !== tree.root && x.color === NodeColor.Black) { + if (x === x.parent.left) { + w = x.parent.right; + + if (w.color === NodeColor.Red) { + w.color = NodeColor.Black; + x.parent.color = NodeColor.Red; + leftRotate(tree, x.parent); + w = x.parent.right; + } + + if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) { + w.color = NodeColor.Red; + x = x.parent; + } else { + if (w.right.color === NodeColor.Black) { + w.left.color = NodeColor.Black; + w.color = NodeColor.Red; + rightRotate(tree, w); + w = x.parent.right; + } + + w.color = x.parent.color; + x.parent.color = NodeColor.Black; + w.right.color = NodeColor.Black; + leftRotate(tree, x.parent); + x = tree.root; + } + } else { + w = x.parent.left; + + if (w.color === NodeColor.Red) { + w.color = NodeColor.Black; + x.parent.color = NodeColor.Red; + rightRotate(tree, x.parent); + w = x.parent.left; + } + + if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) { + w.color = NodeColor.Red; + x = x.parent; + + } else { + if (w.left.color === NodeColor.Black) { + w.right.color = NodeColor.Black; + w.color = NodeColor.Red; + leftRotate(tree, w); + w = x.parent.left; + } + + w.color = x.parent.color; + x.parent.color = NodeColor.Black; + w.left.color = NodeColor.Black; + rightRotate(tree, x.parent); + x = tree.root; + } + } + } + x.color = NodeColor.Black; + resetSentinel(); +} + +export function fixInsert(tree: PieceTreeBase, x: TreeNode) { + recomputeTreeMetadata(tree, x); + + while (x !== tree.root && x.parent.color === NodeColor.Red) { + if (x.parent === x.parent.parent.left) { + const y = x.parent.parent.right; + + if (y.color === NodeColor.Red) { + x.parent.color = NodeColor.Black; + y.color = NodeColor.Black; + x.parent.parent.color = NodeColor.Red; + x = x.parent.parent; + } else { + if (x === x.parent.right) { + x = x.parent; + leftRotate(tree, x); + } + + x.parent.color = NodeColor.Black; + x.parent.parent.color = NodeColor.Red; + rightRotate(tree, x.parent.parent); + } + } else { + const y = x.parent.parent.left; + + if (y.color === NodeColor.Red) { + x.parent.color = NodeColor.Black; + y.color = NodeColor.Black; + x.parent.parent.color = NodeColor.Red; + x = x.parent.parent; + } else { + if (x === x.parent.left) { + x = x.parent; + rightRotate(tree, x); + } + x.parent.color = NodeColor.Black; + x.parent.parent.color = NodeColor.Red; + leftRotate(tree, x.parent.parent); + } + } + } + + tree.root.color = NodeColor.Black; +} + +export function updateTreeMetadata(tree: PieceTreeBase, x: TreeNode, delta: number, lineFeedCntDelta: number): void { + // node length change or line feed count change + while (x !== tree.root && x !== SENTINEL) { + if (x.parent.left === x) { + x.parent.size_left += delta; + x.parent.lf_left += lineFeedCntDelta; + } + + x = x.parent; + } +} + +export function recomputeTreeMetadata(tree: PieceTreeBase, x: TreeNode) { + let delta = 0; + let lf_delta = 0; + if (x === tree.root) { + return; + } + + if (delta === 0) { + // go upwards till the node whose left subtree is changed. + while (x !== tree.root && x === x.parent.right) { + x = x.parent; + } + + if (x === tree.root) { + // well, it means we add a node to the end (inorder) + return; + } + + // x is the node whose right subtree is changed. + x = x.parent; + + delta = calculateSize(x.left) - x.size_left; + lf_delta = calculateLF(x.left) - x.lf_left; + x.size_left += delta; + x.lf_left += lf_delta; + } + + // go upwards till root. O(logN) + while (x !== tree.root && (delta !== 0 || lf_delta !== 0)) { + if (x.parent.left === x) { + x.parent.size_left += delta; + x.parent.lf_left += lf_delta; + } + + x = x.parent; + } +} diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index a04ab200bd61de8e0b52172fc438393172354ba7..a396f91cc406cd4e61668481e8dff10a17675f52 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -9,7 +9,8 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { PieceTreeBase, SENTINEL, NodeColor, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; +import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; +import { SENTINEL, NodeColor, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n';