Skip to content

Commit 1191dcc

Browse files
committed
Fix microsoft#45564. Piece should be immutable as snapshot can kick in anytime in anyways.
1 parent fa6a57b commit 1191dcc

1 file changed

Lines changed: 144 additions & 74 deletions

File tree

src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts

Lines changed: 144 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ export interface BufferCursor {
123123
}
124124

125125
export class Piece {
126-
bufferIndex: number;
127-
start: BufferCursor;
128-
end: BufferCursor;
129-
length: number;
130-
lineFeedCnt: number;
126+
readonly bufferIndex: number;
127+
readonly start: BufferCursor;
128+
readonly end: BufferCursor;
129+
readonly length: number;
130+
readonly lineFeedCnt: number;
131131

132132
constructor(bufferIndex: number, start: BufferCursor, end: BufferCursor, lineFeedCnt: number, length: number) {
133133
this.bufferIndex = bufferIndex;
@@ -155,28 +155,28 @@ export class StringBuffer {
155155
* 2. TreeNode/Buffers normalization should not happen during snapshot reading.
156156
*/
157157
class PieceTreeSnapshot implements ITextSnapshot {
158-
private _nodes: TreeNode[]; // pieces/tree nodes in order
158+
private _pieces: Piece[];
159159
private _index: number;
160160
private _tree: PieceTreeBase;
161161
private _BOM: string;
162162

163163
constructor(tree: PieceTreeBase, BOM: string) {
164-
this._nodes = [];
164+
this._pieces = [];
165165
this._tree = tree;
166166
this._BOM = BOM;
167167
this._index = 0;
168168
if (tree.root !== SENTINEL) {
169169
tree.iterate(tree.root, node => {
170170
if (node !== SENTINEL) {
171-
this._nodes.push(node);
171+
this._pieces.push(node.piece);
172172
}
173173
return true;
174174
});
175175
}
176176
}
177177

178178
read(): string {
179-
if (this._nodes.length === 0) {
179+
if (this._pieces.length === 0) {
180180
if (this._index === 0) {
181181
this._index++;
182182
return this._BOM;
@@ -185,14 +185,14 @@ class PieceTreeSnapshot implements ITextSnapshot {
185185
}
186186
}
187187

188-
if (this._index > this._nodes.length - 1) {
188+
if (this._index > this._pieces.length - 1) {
189189
return null;
190190
}
191191

192192
if (this._index === 0) {
193-
return this._BOM + this._tree.getNodeContent(this._nodes[this._index++]);
193+
return this._BOM + this._tree.getPieceContent(this._pieces[this._index++]);
194194
}
195-
return this._tree.getNodeContent(this._nodes[this._index++]);
195+
return this._tree.getPieceContent(this._pieces[this._index++]);
196196
}
197197
}
198198

@@ -578,9 +578,14 @@ export class PieceTreeBase {
578578

579579
if (headOfRight === 10 /** \n */) {
580580
let newStart: BufferCursor = { line: newRightPiece.start.line + 1, column: 0 };
581-
newRightPiece.start = newStart;
582-
newRightPiece.length -= 1;
583-
newRightPiece.lineFeedCnt = this.getLineFeedCnt(newRightPiece.bufferIndex, newRightPiece.start, newRightPiece.end);
581+
newRightPiece = new Piece(
582+
newRightPiece.bufferIndex,
583+
newStart,
584+
newRightPiece.end,
585+
this.getLineFeedCnt(newRightPiece.bufferIndex, newStart, newRightPiece.end),
586+
newRightPiece.length - 1
587+
);
588+
584589
value += '\n';
585590
}
586591
}
@@ -703,9 +708,15 @@ export class PieceTreeBase {
703708

704709
let piece = node.piece;
705710
let newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
706-
piece.start = newStart;
707-
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
708-
piece.length -= 1;
711+
let nPiece = new Piece(
712+
piece.bufferIndex,
713+
newStart,
714+
piece.end,
715+
this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end),
716+
piece.length - 1
717+
);
718+
719+
node.piece = nPiece;
709720

710721
value += '\n';
711722
updateTreeMetadata(this, node, -1, -1);
@@ -987,46 +998,72 @@ export class PieceTreeBase {
987998
}
988999

9891000
deleteNodeTail(node: TreeNode, pos: BufferCursor) {
990-
let piece = node.piece;
991-
let originalLFCnt = piece.lineFeedCnt;
992-
let originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
993-
piece.end = pos;
994-
let newEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
995-
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
996-
let lf_delta = piece.lineFeedCnt - originalLFCnt;
997-
let size_delta = newEndOffset - originalEndOffset;
998-
piece.length += size_delta;
1001+
const piece = node.piece;
1002+
const originalLFCnt = piece.lineFeedCnt;
1003+
const originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
1004+
1005+
const newEnd = pos;
1006+
const newEndOffset = this.offsetInBuffer(piece.bufferIndex, newEnd);
1007+
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
1008+
1009+
const lf_delta = newLineFeedCnt - originalLFCnt;
1010+
const size_delta = newEndOffset - originalEndOffset;
1011+
const newLength = piece.length + size_delta;
1012+
1013+
node.piece = new Piece(
1014+
piece.bufferIndex,
1015+
piece.start,
1016+
newEnd,
1017+
newLineFeedCnt,
1018+
newLength
1019+
);
1020+
9991021
updateTreeMetadata(this, node, size_delta, lf_delta);
10001022
}
10011023

10021024
deleteNodeHead(node: TreeNode, pos: BufferCursor) {
1003-
let piece = node.piece;
1004-
let originalLFCnt = piece.lineFeedCnt;
1005-
let originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
1006-
1007-
piece.start = pos;
1008-
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
1009-
let newStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
1010-
let lf_delta = piece.lineFeedCnt - originalLFCnt;
1011-
let size_delta = originalStartOffset - newStartOffset;
1012-
piece.length += size_delta;
1025+
const piece = node.piece;
1026+
const originalLFCnt = piece.lineFeedCnt;
1027+
const originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
1028+
1029+
const newStart = pos;
1030+
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
1031+
const newStartOffset = this.offsetInBuffer(piece.bufferIndex, newStart);
1032+
const lf_delta = newLineFeedCnt - originalLFCnt;
1033+
const size_delta = originalStartOffset - newStartOffset;
1034+
const newLength = piece.length + size_delta;
1035+
node.piece = new Piece(
1036+
piece.bufferIndex,
1037+
newStart,
1038+
piece.end,
1039+
newLineFeedCnt,
1040+
newLength
1041+
);
1042+
10131043
updateTreeMetadata(this, node, size_delta, lf_delta);
10141044
}
10151045

10161046
shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) {
1017-
let piece = node.piece;
1018-
let originalStartPos = piece.start;
1019-
let originalEndPos = piece.end;
1047+
const piece = node.piece;
1048+
const originalStartPos = piece.start;
1049+
const originalEndPos = piece.end;
10201050

10211051
// old piece, originalStartPos, start
1022-
let oldLength = piece.length;
1023-
let oldLFCnt = piece.lineFeedCnt;
1024-
piece.end = start;
1025-
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
1026-
let newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos);
1027-
let newLFCnt = piece.lineFeedCnt;
1028-
piece.length = newLength;
1029-
updateTreeMetadata(this, node, newLength - oldLength, newLFCnt - oldLFCnt);
1052+
const oldLength = piece.length;
1053+
const oldLFCnt = piece.lineFeedCnt;
1054+
const newEnd = start;
1055+
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
1056+
const newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos);
1057+
1058+
node.piece = new Piece(
1059+
piece.bufferIndex,
1060+
piece.start,
1061+
newEnd,
1062+
newLineFeedCnt,
1063+
newLength
1064+
);
1065+
1066+
updateTreeMetadata(this, node, newLength - oldLength, newLineFeedCnt - oldLFCnt);
10301067

10311068
// new right piece, end, originalEndPos
10321069
let newPiece = new Piece(
@@ -1046,7 +1083,7 @@ export class PieceTreeBase {
10461083
value += '\n';
10471084
}
10481085

1049-
let hitCRLF = this.shouldCheckCRLF() && this.startWithLF(value) && this.endWithCR(node);
1086+
const hitCRLF = this.shouldCheckCRLF() && this.startWithLF(value) && this.endWithCR(node);
10501087
const startOffset = this._buffers[0].buffer.length;
10511088
this._buffers[0].buffer += value;
10521089
const lineStarts = createLineStartsFast(value, false);
@@ -1061,16 +1098,23 @@ export class PieceTreeBase {
10611098
}
10621099

10631100
this._buffers[0].lineStarts = (<number[]>this._buffers[0].lineStarts).concat(<number[]>lineStarts.slice(1));
1064-
let endIndex = this._buffers[0].lineStarts.length - 1;
1065-
let endColumn = this._buffers[0].buffer.length - this._buffers[0].lineStarts[endIndex];
1066-
let endPos = { line: endIndex, column: endColumn };
1067-
node.piece.end = endPos;
1068-
node.piece.length += value.length;
1069-
let oldLineFeedCnt = node.piece.lineFeedCnt;
1070-
let newLineFeedCnt = this.getLineFeedCnt(0, node.piece.start, endPos);
1071-
node.piece.lineFeedCnt = newLineFeedCnt;
1072-
let lf_delta = newLineFeedCnt - oldLineFeedCnt;
1073-
this._lastChangeBufferPos = endPos;
1101+
const endIndex = this._buffers[0].lineStarts.length - 1;
1102+
const endColumn = this._buffers[0].buffer.length - this._buffers[0].lineStarts[endIndex];
1103+
const newEnd = { line: endIndex, column: endColumn };
1104+
const newLength = node.piece.length + value.length;
1105+
const oldLineFeedCnt = node.piece.lineFeedCnt;
1106+
const newLineFeedCnt = this.getLineFeedCnt(0, node.piece.start, newEnd);
1107+
const lf_delta = newLineFeedCnt - oldLineFeedCnt;
1108+
1109+
node.piece = new Piece(
1110+
node.piece.bufferIndex,
1111+
node.piece.start,
1112+
newEnd,
1113+
newLineFeedCnt,
1114+
newLength
1115+
);
1116+
1117+
this._lastChangeBufferPos = newEnd;
10741118
updateTreeMetadata(this, node, value.length, lf_delta);
10751119
}
10761120

@@ -1266,18 +1310,24 @@ export class PieceTreeBase {
12661310
let nodesToDel = [];
12671311
// update node
12681312
let lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts;
1313+
let newEnd: BufferCursor;
12691314
if (prev.piece.end.column === 0) {
12701315
// it means, last line ends with \r, not \r\n
1271-
let newEnd: BufferCursor = { line: prev.piece.end.line - 1, column: lineStarts[prev.piece.end.line] - lineStarts[prev.piece.end.line - 1] - 1 };
1272-
prev.piece.end = newEnd;
1316+
newEnd = { line: prev.piece.end.line - 1, column: lineStarts[prev.piece.end.line] - lineStarts[prev.piece.end.line - 1] - 1 };
12731317
} else {
12741318
// \r\n
1275-
let newEnd: BufferCursor = { line: prev.piece.end.line, column: prev.piece.end.column - 1 };
1276-
prev.piece.end = newEnd;
1319+
newEnd = { line: prev.piece.end.line, column: prev.piece.end.column - 1 };
12771320
}
12781321

1279-
prev.piece.length -= 1;
1280-
prev.piece.lineFeedCnt -= 1;
1322+
const prevNewLength = prev.piece.length - 1;
1323+
const prevNewLFCnt = prev.piece.lineFeedCnt - 1;
1324+
prev.piece = new Piece(
1325+
prev.piece.bufferIndex,
1326+
prev.piece.start,
1327+
newEnd,
1328+
prevNewLFCnt,
1329+
prevNewLength
1330+
);
12811331

12821332
updateTreeMetadata(this, prev, - 1, -1);
12831333
if (prev.piece.length === 0) {
@@ -1286,10 +1336,15 @@ export class PieceTreeBase {
12861336

12871337
// update nextNode
12881338
let newStart: BufferCursor = { line: next.piece.start.line + 1, column: 0 };
1289-
next.piece.start = newStart;
1290-
next.piece.length -= 1;
1291-
next.piece.lineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, next.piece.start, next.piece.end);
1292-
// }
1339+
const newLength = next.piece.length - 1;
1340+
const newLineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, newStart, next.piece.end);
1341+
next.piece = new Piece(
1342+
next.piece.bufferIndex,
1343+
newStart,
1344+
next.piece.end,
1345+
newLineFeedCnt,
1346+
newLength
1347+
);
12931348

12941349
updateTreeMetadata(this, next, - 1, -1);
12951350
if (next.piece.length === 0) {
@@ -1317,11 +1372,18 @@ export class PieceTreeBase {
13171372
rbDelete(this, nextNode);
13181373
} else {
13191374

1320-
let piece = nextNode.piece;
1321-
let newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
1322-
piece.start = newStart;
1323-
piece.length -= 1;
1324-
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
1375+
const piece = nextNode.piece;
1376+
const newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
1377+
const newLength = piece.length - 1;
1378+
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
1379+
nextNode.piece = new Piece(
1380+
piece.bufferIndex,
1381+
newStart,
1382+
piece.end,
1383+
newLineFeedCnt,
1384+
newLength
1385+
);
1386+
13251387
updateTreeMetadata(this, nextNode, -1, -1);
13261388
}
13271389
return true;
@@ -1362,6 +1424,14 @@ export class PieceTreeBase {
13621424
return currentContent;
13631425
}
13641426

1427+
getPieceContent(piece: Piece) {
1428+
let buffer = this._buffers[piece.bufferIndex];
1429+
let startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
1430+
let endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
1431+
let currentContent = buffer.buffer.substring(startOffset, endOffset);
1432+
return currentContent;
1433+
}
1434+
13651435
/**
13661436
* node node
13671437
* / \ / \

0 commit comments

Comments
 (0)