提交 a010ff74 编写于 作者: A Alex Dima

Add TextModel.createSnapshot (#32503)

上级 680f9b19
......@@ -14,6 +14,7 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ModelRawContentChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent, IModelTokensChangedEvent, IModelContentChange } from 'vs/editor/common/model/textModelEvents';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { ITextSnapshot } from 'vs/platform/files/common/files';
/**
* Vertical Lane in the overview ruler of the editor.
......@@ -499,6 +500,14 @@ export interface ITextModel {
*/
getValue(eol?: EndOfLinePreference, preserveBOM?: boolean): string;
/**
* Get the text stored in this model.
* @param preserverBOM Preserve a BOM character if it was detected when the model was constructed.
* @return The text snapshot (it is safe to consume it asynchronously).
* @internal
*/
createSnapshot(preserveBOM?: boolean): ITextSnapshot;
/**
* Get the length of the text stored in this model.
*/
......@@ -1078,6 +1087,7 @@ export interface ITextBuffer {
getRangeAt(offset: number, length: number): Range;
getValueInRange(range: Range, eol: EndOfLinePreference): string;
createSnapshot(preserveBOM: boolean): ITextSnapshot;
getValueLengthInRange(range: Range, eol: EndOfLinePreference): number;
getLength(): number;
getLineCount(): number;
......
......@@ -10,6 +10,7 @@ import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { ISingleEditOperationIdentifier, IIdentifiedSingleEditOperation, EndOfLinePreference, ITextBuffer, ApplyEditsResult, IInternalModelContentChange } from 'vs/editor/common/model';
import { ITextSnapshot } from 'vs/platform/files/common/files';
export interface IValidatedEditOperation {
sortIndex: number;
......@@ -48,6 +49,45 @@ export interface ITextSource {
readonly isBasicASCII: boolean;
}
class LinesTextBufferSnapshot implements ITextSnapshot {
private readonly _lines: string[];
private readonly _linesLength: number;
private readonly _eol: string;
private readonly _bom: string;
private _lineIndex: number;
constructor(lines: string[], eol: string, bom: string) {
this._lines = lines;
this._linesLength = this._lines.length;
this._eol = eol;
this._bom = bom;
this._lineIndex = 0;
}
public read(): string {
if (this._lineIndex >= this._linesLength) {
return null;
}
let result: string = null;
if (this._lineIndex === 0) {
result = this._bom + this._lines[this._lineIndex];
} else {
result = this._lines[this._lineIndex];
}
this._lineIndex++;
if (this._lineIndex < this._linesLength) {
result += this._eol;
}
return result;
}
}
export class LinesTextBuffer implements ITextBuffer {
private _lines: string[];
......@@ -176,6 +216,10 @@ export class LinesTextBuffer implements ITextBuffer {
return resultLines.join(lineEnding);
}
public createSnapshot(preserveBOM: boolean): ITextSnapshot {
return new LinesTextBufferSnapshot(this._lines.slice(0), this._EOL, preserveBOM ? this._BOM : '');
}
public getValueLengthInRange(range: Range, eol: EndOfLinePreference): number {
if (range.isEmpty()) {
return 0;
......
......@@ -32,7 +32,7 @@ import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
import { TPromise } from 'vs/base/common/winjs.base';
import { IStringStream } from 'vs/platform/files/common/files';
import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files';
import { LinesTextBufferBuilder } from 'vs/editor/common/model/linesTextBuffer/linesTextBufferBuilder';
// Here is the master switch for the text buffer implementation:
......@@ -96,6 +96,48 @@ function singleLetter(result: number): string {
const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 10000;
class TextModelSnapshot implements ITextSnapshot {
private readonly _source: ITextSnapshot;
private _eos: boolean;
constructor(source: ITextSnapshot) {
this._source = source;
this._eos = false;
}
public read(): string {
if (this._eos) {
return null;
}
let result: string[] = [], resultCnt = 0, resultLength = 0;
do {
let tmp = this._source.read();
if (tmp === null) {
// end-of-stream
this._eos = true;
if (resultCnt === 0) {
return null;
} else {
return result.join('');
}
}
if (tmp.length > 0) {
result[resultCnt++] = tmp;
resultLength += tmp.length;
}
if (resultLength >= 64 * 1024) {
return result.join('');
}
} while (true);
}
}
export class TextModel extends Disposable implements model.ITextModel {
private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB
......@@ -665,6 +707,10 @@ export class TextModel extends Disposable implements model.ITextModel {
return fullModelValue;
}
public createSnapshot(preserveBOM: boolean = false): ITextSnapshot {
return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM));
}
public getValueLength(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): number {
this._assertNotDisposed();
const fullModelRange = this.getFullModelRange();
......
......@@ -9,6 +9,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { UTF8_BOM_CHARACTER } from 'vs/base/common/strings';
function testGuessIndentation(defaultInsertSpaces: boolean, defaultTabSize: number, expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void {
var m = TextModel.createFromString(
......@@ -852,3 +853,58 @@ suite('TextModel.mightContainRTL', () => {
});
});
suite('TextModel.createSnapshot', () => {
test('empty file', () => {
let model = TextModel.createFromString('');
let snapshot = model.createSnapshot();
assert.equal(snapshot.read(), null);
model.dispose();
});
test('file with BOM', () => {
let model = TextModel.createFromString(UTF8_BOM_CHARACTER + 'Hello');
assert.equal(model.getLineContent(1), 'Hello');
let snapshot = model.createSnapshot(true);
assert.equal(snapshot.read(), UTF8_BOM_CHARACTER + 'Hello');
assert.equal(snapshot.read(), null);
model.dispose();
});
test('regular file', () => {
let model = TextModel.createFromString('My First Line\n\t\tMy Second Line\n Third Line\n\n1');
let snapshot = model.createSnapshot();
assert.equal(snapshot.read(), 'My First Line\n\t\tMy Second Line\n Third Line\n\n1');
assert.equal(snapshot.read(), null);
model.dispose();
});
test('large file', () => {
let lines: string[] = [];
for (let i = 0; i < 1000; i++) {
lines[i] = 'Just some text that is a bit long such that it can consume some memory';
}
const text = lines.join('\n');
let model = TextModel.createFromString(text);
let snapshot = model.createSnapshot();
let actual = '';
// 70999 length => 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);
assert.equal(actual, text);
model.dispose();
});
});
......@@ -459,6 +459,15 @@ export interface IStringStream {
on(event: string, callback: any): void;
}
/**
* Text snapshot that works like an iterator.
* Will try to return chunks of roughly ~64KB size.
* Will return null when finished.
*/
export interface ITextSnapshot {
read(): string;
}
/**
* Streamable content and meta information of a file.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册