Merge afe4caed26
into 7dfcd74e63
This commit is contained in:
commit
17b43aaea7
|
@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode';
|
|||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FindMatch, ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { NodeColor, SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
|
||||
import { NodeColor, SENTINEL, TreeNode, fixInsert, leftmost, rbDelete, rightmost, updateTreeMetadata, createTreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
|
||||
import { SearchData, Searcher, createFindMatch, isValidMatch } from 'vs/editor/common/model/textModelSearch';
|
||||
|
||||
// const lfRegex = new RegExp(/\r\n|\r|\n/g);
|
||||
|
@ -294,7 +294,7 @@ export class PieceTreeBase {
|
|||
|
||||
let lastNode: TreeNode | null = null;
|
||||
for (let i = 0, len = chunks.length; i < len; i++) {
|
||||
if (chunks[i].buffer.length > 0) {
|
||||
if (chunks[i].buffer.length > 0) { // skip emty buffers
|
||||
if (!chunks[i].lineStarts) {
|
||||
chunks[i].lineStarts = createLineStartsFast(chunks[i].buffer);
|
||||
}
|
||||
|
@ -307,7 +307,11 @@ export class PieceTreeBase {
|
|||
chunks[i].buffer.length
|
||||
);
|
||||
this._buffers.push(chunks[i]);
|
||||
lastNode = this.rbInsertRight(lastNode, piece);
|
||||
if (lastNode === null) {
|
||||
lastNode = this.initRootNode(piece);
|
||||
} else {
|
||||
lastNode = this.rbInsertRight(lastNode, piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,7 +914,7 @@ export class PieceTreeBase {
|
|||
} else {
|
||||
// insert new node
|
||||
let pieces = this.createNewPieces(value);
|
||||
let node = this.rbInsertLeft(null, pieces[0]);
|
||||
let node = this.initRootNode(pieces[0]);
|
||||
|
||||
for (let k = 1; k < pieces.length; k++) {
|
||||
node = this.rbInsertRight(node, pieces[k]);
|
||||
|
@ -1762,20 +1766,14 @@ export class PieceTreeBase {
|
|||
return callback(node) && this.iterate(node.right, callback);
|
||||
}
|
||||
|
||||
private getNodeContent(node: TreeNode) {
|
||||
private getNodeContent(node: TreeNode): string {
|
||||
if (node === SENTINEL) {
|
||||
return '';
|
||||
}
|
||||
let buffer = this._buffers[node.piece.bufferIndex];
|
||||
let currentContent;
|
||||
let piece = node.piece;
|
||||
let startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
|
||||
let endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
|
||||
currentContent = buffer.buffer.substring(startOffset, endOffset);
|
||||
return currentContent;
|
||||
return this.getPieceContent(node.piece);
|
||||
}
|
||||
|
||||
getPieceContent(piece: Piece) {
|
||||
getPieceContent(piece: Piece): string {
|
||||
let buffer = this._buffers[piece.bufferIndex];
|
||||
let startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
|
||||
let endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
|
||||
|
@ -1783,30 +1781,26 @@ export class PieceTreeBase {
|
|||
return currentContent;
|
||||
}
|
||||
|
||||
private initRootNode(p: Piece): TreeNode {
|
||||
return this.root = createTreeNode(p, NodeColor.Black);
|
||||
}
|
||||
|
||||
/**
|
||||
* node node
|
||||
* / \ / \
|
||||
* a b <---- a b
|
||||
* /
|
||||
* z
|
||||
* Require this.root !== SENTINEL
|
||||
*/
|
||||
private rbInsertRight(node: TreeNode | null, p: Piece): TreeNode {
|
||||
let z = new TreeNode(p, NodeColor.Red);
|
||||
z.left = SENTINEL;
|
||||
z.right = SENTINEL;
|
||||
z.parent = SENTINEL;
|
||||
z.size_left = 0;
|
||||
z.lf_left = 0;
|
||||
private rbInsertRight(node: TreeNode, p: Piece): TreeNode {
|
||||
let z = createTreeNode(p, NodeColor.Red);
|
||||
|
||||
let x = this.root;
|
||||
if (x === SENTINEL) {
|
||||
this.root = z;
|
||||
z.color = NodeColor.Black;
|
||||
} else if (node!.right === SENTINEL) {
|
||||
if (node!.right === SENTINEL) {
|
||||
node!.right = z;
|
||||
z.parent = node!;
|
||||
} else {
|
||||
let nextNode = leftest(node!.right);
|
||||
let nextNode = leftmost(node!.right);
|
||||
nextNode.left = z;
|
||||
z.parent = nextNode;
|
||||
}
|
||||
|
@ -1821,23 +1815,16 @@ export class PieceTreeBase {
|
|||
* a b ----> a b
|
||||
* \
|
||||
* z
|
||||
* Require this.root !== SENTINEL
|
||||
*/
|
||||
private rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode {
|
||||
let z = new TreeNode(p, NodeColor.Red);
|
||||
z.left = SENTINEL;
|
||||
z.right = SENTINEL;
|
||||
z.parent = SENTINEL;
|
||||
z.size_left = 0;
|
||||
z.lf_left = 0;
|
||||
private rbInsertLeft(node: TreeNode, p: Piece): TreeNode {
|
||||
let z = createTreeNode(p, NodeColor.Red);
|
||||
|
||||
if (this.root === SENTINEL) {
|
||||
this.root = z;
|
||||
z.color = NodeColor.Black;
|
||||
} else if (node!.left === SENTINEL) {
|
||||
if (node!.left === SENTINEL) {
|
||||
node!.left = z;
|
||||
z.parent = node!;
|
||||
} else {
|
||||
let prevNode = righttest(node!.left); // a
|
||||
let prevNode = rightmost(node!.left); // a
|
||||
prevNode.right = z;
|
||||
z.parent = prevNode;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export class TreeNode {
|
|||
// 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)
|
||||
lf_left: number; // line feeds cnt in the left subtree (not inorder)
|
||||
|
||||
constructor(piece: Piece, color: NodeColor) {
|
||||
this.piece = piece;
|
||||
|
@ -28,7 +28,7 @@ export class TreeNode {
|
|||
|
||||
public next(): TreeNode {
|
||||
if (this.right !== SENTINEL) {
|
||||
return leftest(this.right);
|
||||
return leftmost(this.right);
|
||||
}
|
||||
|
||||
let node: TreeNode = this;
|
||||
|
@ -50,7 +50,7 @@ export class TreeNode {
|
|||
|
||||
public prev(): TreeNode {
|
||||
if (this.left !== SENTINEL) {
|
||||
return righttest(this.left);
|
||||
return rightmost(this.left);
|
||||
}
|
||||
|
||||
let node: TreeNode = this;
|
||||
|
@ -88,14 +88,14 @@ SENTINEL.left = SENTINEL;
|
|||
SENTINEL.right = SENTINEL;
|
||||
SENTINEL.color = NodeColor.Black;
|
||||
|
||||
export function leftest(node: TreeNode): TreeNode {
|
||||
export function leftmost(node: TreeNode): TreeNode {
|
||||
while (node.left !== SENTINEL) {
|
||||
node = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
export function righttest(node: TreeNode): TreeNode {
|
||||
export function rightmost(node: TreeNode): TreeNode {
|
||||
while (node.right !== SENTINEL) {
|
||||
node = node.right;
|
||||
}
|
||||
|
@ -103,19 +103,25 @@ export function righttest(node: TreeNode): TreeNode {
|
|||
}
|
||||
|
||||
export function calculateSize(node: TreeNode): number {
|
||||
if (node === SENTINEL) {
|
||||
return 0;
|
||||
let sum = 0;
|
||||
|
||||
while (node !== SENTINEL) {
|
||||
sum += node.size_left + node.piece.length;
|
||||
node = node.right;
|
||||
}
|
||||
|
||||
return node.size_left + node.piece.length + calculateSize(node.right);
|
||||
return sum;
|
||||
}
|
||||
|
||||
export function calculateLF(node: TreeNode): number {
|
||||
if (node === SENTINEL) {
|
||||
return 0;
|
||||
let sum = 0;
|
||||
|
||||
while (node !== SENTINEL) {
|
||||
sum += node.lf_left + node.piece.lineFeedCnt;
|
||||
node = node.right;
|
||||
}
|
||||
|
||||
return node.lf_left + node.piece.lineFeedCnt + calculateLF(node.right);
|
||||
return sum;
|
||||
}
|
||||
|
||||
export function resetSentinel(): void {
|
||||
|
@ -180,7 +186,7 @@ export function rbDelete(tree: PieceTreeBase, z: TreeNode) {
|
|||
y = z;
|
||||
x = y.left;
|
||||
} else {
|
||||
y = leftest(z.right);
|
||||
y = leftmost(z.right);
|
||||
x = y.right;
|
||||
}
|
||||
|
||||
|
@ -375,6 +381,13 @@ export function fixInsert(tree: PieceTreeBase, x: TreeNode) {
|
|||
tree.root.color = NodeColor.Black;
|
||||
}
|
||||
|
||||
export function createTreeNode(p: Piece, color: NodeColor): TreeNode {
|
||||
const treeNode = new TreeNode(p, color);
|
||||
treeNode.parent = treeNode.left = treeNode.right = SENTINEL;
|
||||
treeNode.size_left = treeNode.lf_left = 0;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -171,6 +171,13 @@ function assertTreeInvariants(T: PieceTreeBase): void {
|
|||
assertValidTree(T);
|
||||
}
|
||||
|
||||
function treeSize(n: TreeNode): number {
|
||||
if (n === SENTINEL) {
|
||||
return 0;
|
||||
}
|
||||
return treeSize(n.left) + treeSize(n.right) + 1;
|
||||
}
|
||||
|
||||
function depth(n: TreeNode): number {
|
||||
if (n === SENTINEL) {
|
||||
// The leafs are black
|
||||
|
@ -212,6 +219,52 @@ function assertValidTree(T: PieceTreeBase): void {
|
|||
|
||||
//#endregion
|
||||
|
||||
suite('rbtree init and inserts', () => {
|
||||
test('init root node', () => {
|
||||
let pieceTree = createTextBuffer([
|
||||
'Init text.'
|
||||
]); // init root node in ctor
|
||||
assertTreeInvariants(pieceTree);
|
||||
assert.strictEqual(treeSize(pieceTree.root), 1);
|
||||
|
||||
let pieceTree2 = new PieceTreeBase([], '\r\n', false); // root node is now SENTINEL
|
||||
assert.strictEqual(pieceTree2.root, SENTINEL);
|
||||
pieceTree2.insert(0, 'Init text'); // init root node
|
||||
assertTreeInvariants(pieceTree2);
|
||||
assert.strictEqual(treeSize(pieceTree2.root), 1);
|
||||
});
|
||||
|
||||
test('insert left', () => {
|
||||
let pieceTree = createTextBuffer([
|
||||
'Test text 2.'
|
||||
]);
|
||||
pieceTree.insert(0, 'Test text 1.'); // insert left to the root node
|
||||
assertTreeInvariants(pieceTree);
|
||||
assert.strictEqual(treeSize(pieceTree.root), 2);
|
||||
|
||||
pieceTree.insert(0, 'Test text 0.');
|
||||
assertTreeInvariants(pieceTree);
|
||||
assert.strictEqual(treeSize(pieceTree.root), 3);
|
||||
|
||||
assert.strictEqual(pieceTree.getLinesRawContent(), 'Test text 0.Test text 1.Test text 2.');
|
||||
});
|
||||
|
||||
test('insert right', () => {
|
||||
let pieceTree = createTextBuffer([
|
||||
'Test text 0.', 'Test text 1.', 'Test text 2.'
|
||||
]); // init root and 2 rbInsertRight
|
||||
|
||||
assertTreeInvariants(pieceTree);
|
||||
assert.strictEqual(treeSize(pieceTree.root), 3);
|
||||
assert.strictEqual(pieceTree.getLinesRawContent(), 'Test text 0.Test text 1.Test text 2.');
|
||||
|
||||
pieceTree.insert(pieceTree.getLength(), 'Test text 3.'); // insert rigth to the last node
|
||||
assertTreeInvariants(pieceTree);
|
||||
assert.strictEqual(treeSize(pieceTree.root), 4);
|
||||
assert.strictEqual(pieceTree.getLinesRawContent(), 'Test text 0.Test text 1.Test text 2.Test text 3.');
|
||||
});
|
||||
});
|
||||
|
||||
suite('inserts and deletes', () => {
|
||||
test('basic insert/delete', () => {
|
||||
let pieceTable = createTextBuffer([
|
||||
|
|
Loading…
Reference in a new issue