mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-26 02:06:38 +01:00
Merge pull request #41687 from Microsoft/alex/edcore
Chunks based text buffer
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export class LeafOffsetLenEdit {
|
||||
constructor(
|
||||
public readonly start: number,
|
||||
public readonly length: number,
|
||||
public readonly text: string
|
||||
) { }
|
||||
}
|
||||
|
||||
export class BufferPiece {
|
||||
private readonly _str: string;
|
||||
public get text(): string { return this._str; }
|
||||
|
||||
private readonly _lineStarts: Uint32Array;
|
||||
|
||||
constructor(str: string, lineStarts: Uint32Array = null) {
|
||||
this._str = str;
|
||||
if (lineStarts === null) {
|
||||
this._lineStarts = createLineStartsFast(str);
|
||||
} else {
|
||||
this._lineStarts = lineStarts;
|
||||
}
|
||||
}
|
||||
|
||||
public length(): number {
|
||||
return this._str.length;
|
||||
}
|
||||
|
||||
public newLineCount(): number {
|
||||
return this._lineStarts.length;
|
||||
}
|
||||
|
||||
public lineStartFor(relativeLineIndex: number): number {
|
||||
return this._lineStarts[relativeLineIndex];
|
||||
}
|
||||
|
||||
public charCodeAt(index: number): number {
|
||||
return this._str.charCodeAt(index);
|
||||
}
|
||||
|
||||
public substr(from: number, length: number): string {
|
||||
return this._str.substr(from, length);
|
||||
}
|
||||
|
||||
public findLineStartBeforeOffset(offset: number): number {
|
||||
if (this._lineStarts.length === 0 || offset < this._lineStarts[0]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let low = 0, high = this._lineStarts.length - 1;
|
||||
|
||||
while (low < high) {
|
||||
let mid = low + Math.ceil((high - low) / 2);
|
||||
let lineStart = this._lineStarts[mid];
|
||||
|
||||
if (offset === lineStart) {
|
||||
return mid;
|
||||
} else if (offset < lineStart) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
public findLineFirstNonWhitespaceIndex(searchStartOffset: number): number {
|
||||
for (let i = searchStartOffset, len = this._str.length; i < len; i++) {
|
||||
const chCode = this._str.charCodeAt(i);
|
||||
if (chCode === CharCode.CarriageReturn || chCode === CharCode.LineFeed) {
|
||||
// Reached EOL
|
||||
return -2;
|
||||
}
|
||||
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public findLineLastNonWhitespaceIndex(searchStartOffset: number): number {
|
||||
for (let i = searchStartOffset - 1; i >= 0; i--) {
|
||||
const chCode = this._str.charCodeAt(i);
|
||||
if (chCode === CharCode.CarriageReturn || chCode === CharCode.LineFeed) {
|
||||
// Reached EOL
|
||||
return -2;
|
||||
}
|
||||
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static normalizeEOL(target: BufferPiece, eol: '\r\n' | '\n'): BufferPiece {
|
||||
return new BufferPiece(target._str.replace(/\r\n|\r|\n/g, eol));
|
||||
}
|
||||
|
||||
public static deleteLastChar(target: BufferPiece): BufferPiece {
|
||||
const targetCharsLength = target.length();
|
||||
const targetLineStartsLength = target.newLineCount();
|
||||
const targetLineStarts = target._lineStarts;
|
||||
|
||||
let newLineStartsLength;
|
||||
if (targetLineStartsLength > 0 && targetLineStarts[targetLineStartsLength - 1] === targetCharsLength) {
|
||||
newLineStartsLength = targetLineStartsLength - 1;
|
||||
} else {
|
||||
newLineStartsLength = targetLineStartsLength;
|
||||
}
|
||||
|
||||
let newLineStarts = new Uint32Array(newLineStartsLength);
|
||||
newLineStarts.set(targetLineStarts);
|
||||
|
||||
return new BufferPiece(
|
||||
target._str.substr(0, targetCharsLength - 1),
|
||||
newLineStarts
|
||||
);
|
||||
}
|
||||
|
||||
public static insertFirstChar(target: BufferPiece, character: number): BufferPiece {
|
||||
const targetLineStartsLength = target.newLineCount();
|
||||
const targetLineStarts = target._lineStarts;
|
||||
const insertLineStart = ((character === CharCode.CarriageReturn && (targetLineStartsLength === 0 || targetLineStarts[0] !== 1 || target.charCodeAt(0) !== CharCode.LineFeed)) || (character === CharCode.LineFeed));
|
||||
|
||||
const newLineStartsLength = (insertLineStart ? targetLineStartsLength + 1 : targetLineStartsLength);
|
||||
let newLineStarts = new Uint32Array(newLineStartsLength);
|
||||
|
||||
if (insertLineStart) {
|
||||
newLineStarts[0] = 1;
|
||||
for (let i = 0; i < targetLineStartsLength; i++) {
|
||||
newLineStarts[i + 1] = targetLineStarts[i] + 1;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < targetLineStartsLength; i++) {
|
||||
newLineStarts[i] = targetLineStarts[i] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return new BufferPiece(
|
||||
String.fromCharCode(character) + target._str,
|
||||
newLineStarts
|
||||
);
|
||||
}
|
||||
|
||||
public static join(first: BufferPiece, second: BufferPiece): BufferPiece {
|
||||
const firstCharsLength = first._str.length;
|
||||
|
||||
const firstLineStartsLength = first._lineStarts.length;
|
||||
const secondLineStartsLength = second._lineStarts.length;
|
||||
|
||||
const firstLineStarts = first._lineStarts;
|
||||
const secondLineStarts = second._lineStarts;
|
||||
|
||||
const newLineStartsLength = firstLineStartsLength + secondLineStartsLength;
|
||||
let newLineStarts = new Uint32Array(newLineStartsLength);
|
||||
newLineStarts.set(firstLineStarts, 0);
|
||||
for (let i = 0; i < secondLineStartsLength; i++) {
|
||||
newLineStarts[i + firstLineStartsLength] = secondLineStarts[i] + firstCharsLength;
|
||||
}
|
||||
|
||||
return new BufferPiece(first._str + second._str, newLineStarts);
|
||||
}
|
||||
|
||||
public static replaceOffsetLen(target: BufferPiece, edits: LeafOffsetLenEdit[], idealLeafLength: number, maxLeafLength: number, result: BufferPiece[]): void {
|
||||
const editsSize = edits.length;
|
||||
const originalCharsLength = target.length();
|
||||
if (editsSize === 1 && edits[0].text.length === 0 && edits[0].start === 0 && edits[0].length === originalCharsLength) {
|
||||
// special case => deleting everything
|
||||
return;
|
||||
}
|
||||
|
||||
let pieces: string[] = new Array<string>(2 * editsSize + 1);
|
||||
let originalFromIndex = 0;
|
||||
let piecesTextLength = 0;
|
||||
for (let i = 0; i < editsSize; i++) {
|
||||
const edit = edits[i];
|
||||
|
||||
const originalText = target._str.substr(originalFromIndex, edit.start - originalFromIndex);
|
||||
pieces[2 * i] = originalText;
|
||||
piecesTextLength += originalText.length;
|
||||
|
||||
originalFromIndex = edit.start + edit.length;
|
||||
pieces[2 * i + 1] = edit.text;
|
||||
piecesTextLength += edit.text.length;
|
||||
}
|
||||
|
||||
// maintain the chars that survive to the right of the last edit
|
||||
let text = target._str.substr(originalFromIndex, originalCharsLength - originalFromIndex);
|
||||
pieces[2 * editsSize] = text;
|
||||
piecesTextLength += text.length;
|
||||
|
||||
let targetDataLength = piecesTextLength > maxLeafLength ? idealLeafLength : piecesTextLength;
|
||||
let targetDataOffset = 0;
|
||||
|
||||
let data: string = '';
|
||||
|
||||
for (let pieceIndex = 0, pieceCount = pieces.length; pieceIndex < pieceCount; pieceIndex++) {
|
||||
const pieceText = pieces[pieceIndex];
|
||||
const pieceLength = pieceText.length;
|
||||
if (pieceLength === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pieceOffset = 0;
|
||||
while (pieceOffset < pieceLength) {
|
||||
if (targetDataOffset >= targetDataLength) {
|
||||
result.push(new BufferPiece(data));
|
||||
targetDataLength = piecesTextLength > maxLeafLength ? idealLeafLength : piecesTextLength;
|
||||
targetDataOffset = 0;
|
||||
data = '';
|
||||
}
|
||||
|
||||
let writingCnt = min(pieceLength - pieceOffset, targetDataLength - targetDataOffset);
|
||||
data += pieceText.substr(pieceOffset, writingCnt);
|
||||
pieceOffset += writingCnt;
|
||||
targetDataOffset += writingCnt;
|
||||
piecesTextLength -= writingCnt;
|
||||
|
||||
// check that the buffer piece does not end in a \r or high surrogate
|
||||
if (targetDataOffset === targetDataLength && piecesTextLength > 0) {
|
||||
const lastChar = data.charCodeAt(targetDataLength - 1);
|
||||
if (lastChar === CharCode.CarriageReturn || (0xD800 <= lastChar && lastChar <= 0xDBFF)) {
|
||||
// move lastChar over to next buffer piece
|
||||
targetDataLength -= 1;
|
||||
pieceOffset -= 1;
|
||||
targetDataOffset -= 1;
|
||||
piecesTextLength += 1;
|
||||
data = data.substr(0, data.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(new BufferPiece(data));
|
||||
}
|
||||
}
|
||||
|
||||
function min(a: number, b: number): number {
|
||||
return (a < b ? a : b);
|
||||
}
|
||||
|
||||
export function createUint32Array(arr: number[]): Uint32Array {
|
||||
let r = new Uint32Array(arr.length);
|
||||
r.set(arr, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
export class LineStarts {
|
||||
constructor(
|
||||
public readonly lineStarts: Uint32Array,
|
||||
public readonly cr: number,
|
||||
public readonly lf: number,
|
||||
public readonly crlf: number,
|
||||
public readonly isBasicASCII: boolean
|
||||
) { }
|
||||
}
|
||||
|
||||
export function createLineStartsFast(str: string): Uint32Array {
|
||||
let r: number[] = [], rLength = 0;
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
const chr = str.charCodeAt(i);
|
||||
|
||||
if (chr === CharCode.CarriageReturn) {
|
||||
if (i + 1 < len && str.charCodeAt(i + 1) === CharCode.LineFeed) {
|
||||
// \r\n... case
|
||||
r[rLength++] = i + 2;
|
||||
i++; // skip \n
|
||||
} else {
|
||||
// \r... case
|
||||
r[rLength++] = i + 1;
|
||||
}
|
||||
} else if (chr === CharCode.LineFeed) {
|
||||
r[rLength++] = i + 1;
|
||||
}
|
||||
}
|
||||
return createUint32Array(r);
|
||||
}
|
||||
|
||||
export function createLineStarts(r: number[], str: string): LineStarts {
|
||||
r.length = 0;
|
||||
|
||||
let rLength = 0;
|
||||
let cr = 0, lf = 0, crlf = 0;
|
||||
let isBasicASCII = true;
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
const chr = str.charCodeAt(i);
|
||||
|
||||
if (chr === CharCode.CarriageReturn) {
|
||||
if (i + 1 < len && str.charCodeAt(i + 1) === CharCode.LineFeed) {
|
||||
// \r\n... case
|
||||
crlf++;
|
||||
r[rLength++] = i + 2;
|
||||
i++; // skip \n
|
||||
} else {
|
||||
cr++;
|
||||
// \r... case
|
||||
r[rLength++] = i + 1;
|
||||
}
|
||||
} else if (chr === CharCode.LineFeed) {
|
||||
lf++;
|
||||
r[rLength++] = i + 1;
|
||||
} else {
|
||||
if (isBasicASCII) {
|
||||
if (chr !== CharCode.Tab && (chr < 32 || chr > 126)) {
|
||||
isBasicASCII = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = new LineStarts(createUint32Array(r), cr, lf, crlf, isBasicASCII);
|
||||
r.length = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as strings from 'vs/base/common/strings';
|
||||
import { ITextBufferBuilder, ITextBufferFactory, ITextBuffer, DefaultEndOfLine } from 'vs/editor/common/model';
|
||||
import { BufferPiece, createLineStarts } from 'vs/editor/common/model/chunksTextBuffer/bufferPiece';
|
||||
import { ChunksTextBuffer } from 'vs/editor/common/model/chunksTextBuffer/chunksTextBuffer';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export class TextBufferFactory implements ITextBufferFactory {
|
||||
|
||||
constructor(
|
||||
private readonly _pieces: BufferPiece[],
|
||||
private readonly _averageChunkSize: number,
|
||||
private readonly _BOM: string,
|
||||
private readonly _cr: number,
|
||||
private readonly _lf: number,
|
||||
private readonly _crlf: number,
|
||||
private readonly _containsRTL: boolean,
|
||||
private readonly _isBasicASCII: boolean,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* if text source is empty or with precisely one line, returns null. No end of line is detected.
|
||||
* if text source contains more lines ending with '\r\n', returns '\r\n'.
|
||||
* Otherwise returns '\n'. More lines end with '\n'.
|
||||
*/
|
||||
private _getEOL(defaultEOL: DefaultEndOfLine): '\r\n' | '\n' {
|
||||
const totalEOLCount = this._cr + this._lf + this._crlf;
|
||||
const totalCRCount = this._cr + this._crlf;
|
||||
if (totalEOLCount === 0) {
|
||||
// This is an empty file or a file with precisely one line
|
||||
return (defaultEOL === DefaultEndOfLine.LF ? '\n' : '\r\n');
|
||||
}
|
||||
if (totalCRCount > totalEOLCount / 2) {
|
||||
// More than half of the file contains \r\n ending lines
|
||||
return '\r\n';
|
||||
}
|
||||
// At least one line more ends in \n
|
||||
return '\n';
|
||||
}
|
||||
|
||||
public create(defaultEOL: DefaultEndOfLine): ITextBuffer {
|
||||
const eol = this._getEOL(defaultEOL);
|
||||
let pieces = this._pieces;
|
||||
|
||||
if (
|
||||
(eol === '\r\n' && (this._cr > 0 || this._lf > 0))
|
||||
|| (eol === '\n' && (this._cr > 0 || this._crlf > 0))
|
||||
) {
|
||||
// Normalize pieces
|
||||
for (let i = 0, len = pieces.length; i < len; i++) {
|
||||
pieces[i] = BufferPiece.normalizeEOL(pieces[i], eol);
|
||||
}
|
||||
}
|
||||
return new ChunksTextBuffer(pieces, this._averageChunkSize, this._BOM, eol, this._containsRTL, this._isBasicASCII);
|
||||
}
|
||||
|
||||
public getFirstLineText(lengthLimit: number): string {
|
||||
const firstPiece = this._pieces[0];
|
||||
if (firstPiece.newLineCount() === 0) {
|
||||
return firstPiece.substr(0, lengthLimit);
|
||||
}
|
||||
|
||||
const firstEOLOffset = firstPiece.lineStartFor(0);
|
||||
return firstPiece.substr(0, Math.min(lengthLimit, firstEOLOffset));
|
||||
}
|
||||
}
|
||||
|
||||
export class ChunksTextBufferBuilder implements ITextBufferBuilder {
|
||||
|
||||
private _rawPieces: BufferPiece[];
|
||||
private _hasPreviousChar: boolean;
|
||||
private _previousChar: number;
|
||||
private _averageChunkSize: number;
|
||||
private _tmpLineStarts: number[];
|
||||
|
||||
private BOM: string;
|
||||
private cr: number;
|
||||
private lf: number;
|
||||
private crlf: number;
|
||||
private containsRTL: boolean;
|
||||
private isBasicASCII: boolean;
|
||||
|
||||
constructor() {
|
||||
this._rawPieces = [];
|
||||
this._hasPreviousChar = false;
|
||||
this._previousChar = 0;
|
||||
this._averageChunkSize = 0;
|
||||
this._tmpLineStarts = [];
|
||||
|
||||
this.BOM = '';
|
||||
this.cr = 0;
|
||||
this.lf = 0;
|
||||
this.crlf = 0;
|
||||
this.containsRTL = false;
|
||||
this.isBasicASCII = true;
|
||||
}
|
||||
|
||||
public acceptChunk(chunk: string): void {
|
||||
if (chunk.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._rawPieces.length === 0) {
|
||||
if (strings.startsWithUTF8BOM(chunk)) {
|
||||
this.BOM = strings.UTF8_BOM_CHARACTER;
|
||||
chunk = chunk.substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
this._averageChunkSize = (this._averageChunkSize * this._rawPieces.length + chunk.length) / (this._rawPieces.length + 1);
|
||||
|
||||
const lastChar = chunk.charCodeAt(chunk.length - 1);
|
||||
if (lastChar === CharCode.CarriageReturn || (lastChar >= 0xd800 && lastChar <= 0xdbff)) {
|
||||
// last character is \r or a high surrogate => keep it back
|
||||
this._acceptChunk1(chunk.substr(0, chunk.length - 1), false);
|
||||
this._hasPreviousChar = true;
|
||||
this._previousChar = lastChar;
|
||||
} else {
|
||||
this._acceptChunk1(chunk, false);
|
||||
this._hasPreviousChar = false;
|
||||
this._previousChar = lastChar;
|
||||
}
|
||||
}
|
||||
|
||||
private _acceptChunk1(chunk: string, allowEmptyStrings: boolean): void {
|
||||
if (!allowEmptyStrings && chunk.length === 0) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._hasPreviousChar) {
|
||||
this._acceptChunk2(chunk + String.fromCharCode(this._previousChar));
|
||||
} else {
|
||||
this._acceptChunk2(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
private _acceptChunk2(chunk: string): void {
|
||||
const lineStarts = createLineStarts(this._tmpLineStarts, chunk);
|
||||
|
||||
this._rawPieces.push(new BufferPiece(chunk, lineStarts.lineStarts));
|
||||
this.cr += lineStarts.cr;
|
||||
this.lf += lineStarts.lf;
|
||||
this.crlf += lineStarts.crlf;
|
||||
|
||||
if (this.isBasicASCII) {
|
||||
this.isBasicASCII = lineStarts.isBasicASCII;
|
||||
}
|
||||
if (!this.isBasicASCII && !this.containsRTL) {
|
||||
// No need to check if is basic ASCII
|
||||
this.containsRTL = strings.containsRTL(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public finish(): TextBufferFactory {
|
||||
this._finish();
|
||||
return new TextBufferFactory(this._rawPieces, this._averageChunkSize, this.BOM, this.cr, this.lf, this.crlf, this.containsRTL, this.isBasicASCII);
|
||||
}
|
||||
|
||||
private _finish(): void {
|
||||
if (this._rawPieces.length === 0) {
|
||||
// no chunks => forcefully go through accept chunk
|
||||
this._acceptChunk1('', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._hasPreviousChar) {
|
||||
this._hasPreviousChar = false;
|
||||
|
||||
// recreate last chunk
|
||||
const lastPiece = this._rawPieces[this._rawPieces.length - 1];
|
||||
const tmp = new BufferPiece(String.fromCharCode(this._previousChar));
|
||||
const newLastPiece = BufferPiece.join(lastPiece, tmp);
|
||||
this._rawPieces[this._rawPieces.length - 1] = newLastPiece;
|
||||
if (this._previousChar === CharCode.CarriageReturn) {
|
||||
this.cr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,14 @@ import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelS
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
import { LinesTextBufferBuilder } from 'vs/editor/common/model/linesTextBuffer/linesTextBufferBuilder';
|
||||
import { ChunksTextBufferBuilder } from 'vs/editor/common/model/chunksTextBuffer/chunksTextBufferBuilder';
|
||||
|
||||
// Here is the master switch for the text buffer implementation:
|
||||
const USE_CHUNKS_TEXT_BUFFER = false;
|
||||
function createTextBufferBuilder() {
|
||||
if (USE_CHUNKS_TEXT_BUFFER) {
|
||||
return new ChunksTextBufferBuilder();
|
||||
}
|
||||
return new LinesTextBufferBuilder();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as assert from 'assert';
|
||||
import { BufferPiece } from 'vs/editor/common/model/chunksTextBuffer/bufferPiece';
|
||||
|
||||
suite('BufferPiece', () => {
|
||||
test('findLineStartBeforeOffset', () => {
|
||||
let piece = new BufferPiece([
|
||||
'Line1\r\n',
|
||||
'l2\n',
|
||||
'another\r',
|
||||
'and\r\n',
|
||||
'finally\n',
|
||||
'last'
|
||||
].join(''));
|
||||
|
||||
assert.equal(piece.length(), 35);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(0), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(1), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(2), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(3), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(4), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(5), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(6), -1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(7), 0);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(8), 0);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(9), 0);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(10), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(11), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(12), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(13), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(14), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(15), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(16), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(17), 1);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(18), 2);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(19), 2);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(20), 2);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(21), 2);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(22), 2);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(23), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(24), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(25), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(26), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(27), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(28), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(29), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(30), 3);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(31), 4);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(32), 4);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(33), 4);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(34), 4);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(35), 4);
|
||||
assert.deepEqual(piece.findLineStartBeforeOffset(36), 4);
|
||||
});
|
||||
});
|
||||
@@ -891,16 +891,18 @@ suite('TextModel.createSnapshot', () => {
|
||||
let snapshot = model.createSnapshot();
|
||||
let actual = '';
|
||||
|
||||
// 70999 length => 2 read calls are necessary
|
||||
// 70999 length => at most 2 read calls are necessary
|
||||
let tmp1 = snapshot.read();
|
||||
assert.ok(tmp1);
|
||||
actual += tmp1;
|
||||
|
||||
let tmp2 = snapshot.read();
|
||||
assert.ok(tmp2);
|
||||
actual += tmp2;
|
||||
|
||||
assert.equal(snapshot.read(), null);
|
||||
if (tmp2 === null) {
|
||||
// all good
|
||||
} else {
|
||||
actual += tmp2;
|
||||
assert.equal(snapshot.read(), null);
|
||||
}
|
||||
|
||||
assert.equal(actual, text);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user