Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -7,12 +7,11 @@
import { Range, IRange } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditStack } from 'vs/editor/common/model/editStack';
import { ILineEdit, LineMarker, MarkersTracker, IModelLine } from 'vs/editor/common/model/modelLine';
import { ILineEdit, IModelLine } from 'vs/editor/common/model/modelLine';
import { TextModelWithDecorations, ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
import { IDisposable } from 'vs/base/common/lifecycle';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource';
@@ -23,6 +22,7 @@ export interface IValidatedEditOperation {
sortIndex: number;
identifier: editorCommon.ISingleEditOperationIdentifier;
range: Range;
rangeOffset: number;
rangeLength: number;
lines: string[];
forceMoveMarkers: boolean;
@@ -249,6 +249,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
sortIndex: 0,
identifier: operations[0].identifier,
range: entireEditRange,
rangeOffset: this.getOffsetAt(entireEditRange.getStartPosition()),
rangeLength: this.getValueLengthInRange(entireEditRange),
lines: result.join('').split('\n'),
forceMoveMarkers: forceMoveMarkers,
@@ -275,15 +276,15 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] {
try {
this._eventEmitter.beginDeferredEmit();
let markersTracker = this._acquireMarkersTracker();
return this._applyEdits(markersTracker, rawOperations);
this._acquireDecorationsTracker();
return this._applyEdits(rawOperations);
} finally {
this._releaseMarkersTracker();
this._releaseDecorationsTracker();
this._eventEmitter.endDeferredEmit();
}
}
private _applyEdits(markersTracker: MarkersTracker, rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] {
private _applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] {
if (rawOperations.length === 0) {
return [];
}
@@ -310,6 +311,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
sortIndex: i,
identifier: op.identifier,
range: validatedRange,
rangeOffset: this.getOffsetAt(validatedRange.getStartPosition()),
rangeLength: this.getValueLengthInRange(validatedRange),
lines: op.text ? op.text.split(/\r\n|\r|\n/) : null,
forceMoveMarkers: op.forceMoveMarkers,
@@ -378,7 +380,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._mightContainRTL = mightContainRTL;
this._mightContainNonBasicASCII = mightContainNonBasicASCII;
this._doApplyEdits(markersTracker, operations);
this._doApplyEdits(operations);
this._trimAutoWhitespaceLines = null;
if (this._options.trimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) {
@@ -465,9 +467,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
return result;
}
private _doApplyEdits(markersTracker: MarkersTracker, operations: IValidatedEditOperation[]): void {
const tabSize = this._options.tabSize;
private _doApplyEdits(operations: IValidatedEditOperation[]): void {
// Sort operations descending
operations.sort(EditableTextModel._sortOpsDescending);
@@ -503,11 +503,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
}
this._invalidateLine(currentLineNumber - 1);
this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, i), tabSize);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
}
this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, i));
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
);
@@ -517,11 +514,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
}
this._invalidateLine(currentLineNumber - 1);
this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length), tabSize);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
}
this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length));
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
);
@@ -529,10 +523,6 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
lineEditsQueue = [];
};
let minTouchedLineNumber = operations[operations.length - 1].range.startLineNumber;
let maxTouchedLineNumber = operations[0].range.endLineNumber + 1;
let totalLinesCountDelta = 0;
for (let i = 0, len = operations.length; i < len; i++) {
const op = operations[i];
@@ -556,8 +546,6 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
const insertingLinesCnt = (op.lines ? op.lines.length - 1 : 0);
const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
totalLinesCountDelta += (insertingLinesCnt - deletingLinesCnt);
// Iterating descending to overlap with previous op
// in case there are common lines being edited in both
for (let j = editingLinesCnt; j >= 0; j--) {
@@ -567,8 +555,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
lineNumber: editLineNumber,
startColumn: (editLineNumber === startLineNumber ? startColumn : 1),
endColumn: (editLineNumber === endLineNumber ? endColumn : this.getLineMaxColumn(editLineNumber)),
text: (op.lines ? op.lines[j] : ''),
forceMoveMarkers: op.forceMoveMarkers
text: (op.lines ? op.lines[j] : '')
});
}
@@ -579,43 +566,19 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
flushLineEdits();
const spliceStartLineNumber = startLineNumber + editingLinesCnt;
const spliceStartColumn = this.getLineMaxColumn(spliceStartLineNumber);
const endLineRemains = this._lines[endLineNumber - 1].split(markersTracker, endColumn, false, tabSize);
const endLineRemains = this._lines[endLineNumber - 1].split(endColumn);
this._invalidateLine(spliceStartLineNumber - 1);
const spliceCnt = endLineNumber - spliceStartLineNumber;
// Collect all these markers
let markersOnDeletedLines: LineMarker[] = [];
for (let j = 0; j < spliceCnt; j++) {
const deleteLineIndex = spliceStartLineNumber + j;
const deleteLineMarkers = this._lines[deleteLineIndex].getMarkers();
if (deleteLineMarkers) {
markersOnDeletedLines = markersOnDeletedLines.concat(deleteLineMarkers);
}
}
this._lines.splice(spliceStartLineNumber, spliceCnt);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.removeValues(spliceStartLineNumber, spliceCnt);
}
this._lineStarts.removeValues(spliceStartLineNumber, spliceCnt);
// Reconstruct first line
this._lines[spliceStartLineNumber - 1].append(markersTracker, spliceStartLineNumber, endLineRemains, tabSize);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length);
}
this._lines[spliceStartLineNumber - 1].append(endLineRemains);
this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length);
// Update deleted markers
const deletedMarkersPosition = new Position(spliceStartLineNumber, spliceStartColumn);
for (let j = 0, lenJ = markersOnDeletedLines.length; j < lenJ; j++) {
markersOnDeletedLines[j].updatePosition(markersTracker, deletedMarkersPosition);
}
this._lines[spliceStartLineNumber - 1].addMarkers(markersOnDeletedLines);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(spliceStartLineNumber, this._lines[spliceStartLineNumber - 1].text)
);
@@ -638,11 +601,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
}
// Split last line
let leftoverLine = this._lines[spliceLineNumber - 1].split(markersTracker, spliceColumn, op.forceMoveMarkers, tabSize);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length);
}
let leftoverLine = this._lines[spliceLineNumber - 1].split(spliceColumn);
this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(spliceLineNumber, this._lines[spliceLineNumber - 1].text)
);
@@ -653,50 +613,37 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
let newLinesContent: string[] = [];
let newLinesLengths = new Uint32Array(insertingLinesCnt - editingLinesCnt);
for (let j = editingLinesCnt + 1; j <= insertingLinesCnt; j++) {
newLines.push(this._createModelLine(op.lines[j], tabSize));
newLines.push(this._createModelLine(op.lines[j]));
newLinesContent.push(op.lines[j]);
newLinesLengths[j - editingLinesCnt - 1] = op.lines[j].length + this._EOL.length;
}
this._lines = arrays.arrayInsert(this._lines, startLineNumber + editingLinesCnt, newLines);
newLinesContent[newLinesContent.length - 1] += leftoverLine.text;
if (this._lineStarts) {
// update prefix sum
this._lineStarts.insertValues(startLineNumber + editingLinesCnt, newLinesLengths);
}
this._lineStarts.insertValues(startLineNumber + editingLinesCnt, newLinesLengths);
// Last line
this._lines[startLineNumber + insertingLinesCnt - 1].append(markersTracker, startLineNumber + insertingLinesCnt, leftoverLine, tabSize);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length);
}
this._lines[startLineNumber + insertingLinesCnt - 1].append(leftoverLine);
this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLinesContent.join('\n'))
);
}
const text = (op.lines ? op.lines.join(this.getEOL()) : '');
contentChanges.push({
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
rangeLength: op.rangeLength,
text: op.lines ? op.lines.join(this.getEOL()) : ''
text: text
});
this._adjustDecorationsForEdit(op.rangeOffset, op.rangeLength, text.length, op.forceMoveMarkers);
// console.log('AFTER:');
// console.log('<<<\n' + this._lines.map(l => l.text).join('\n') + '\n>>>');
}
flushLineEdits();
maxTouchedLineNumber = Math.max(1, Math.min(this.getLineCount(), maxTouchedLineNumber + totalLinesCountDelta));
if (totalLinesCountDelta !== 0) {
// must update line numbers all the way to the bottom
maxTouchedLineNumber = this.getLineCount();
}
for (let lineNumber = minTouchedLineNumber; lineNumber <= maxTouchedLineNumber; lineNumber++) {
this._lines[lineNumber - 1].updateLineNumber(markersTracker, lineNumber);
}
if (rawContentChanges.length !== 0 || contentChanges.length !== 0) {
this._increaseVersionId();
@@ -718,35 +665,9 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e);
}
// this._assertLineNumbersOK();
this._resetIndentRanges();
}
public _assertLineNumbersOK(): void {
let foundMarkersCnt = 0;
for (let i = 0, len = this._lines.length; i < len; i++) {
let line = this._lines[i];
let lineNumber = i + 1;
let markers = line.getMarkers();
if (markers !== null) {
for (let j = 0, lenJ = markers.length; j < lenJ; j++) {
foundMarkersCnt++;
let markerId = markers[j].id;
let marker = this._markerIdToMarker[markerId];
if (marker.position.lineNumber !== lineNumber) {
throw new Error('Misplaced marker with id ' + markerId);
}
}
}
}
let totalMarkersCnt = Object.keys(this._markerIdToMarker).length;
if (totalMarkersCnt !== foundMarkersCnt) {
throw new Error('There are misplaced markers!');
}
}
private _undo(): Selection[] {
this._isUndoing = true;
let r = this._commandManager.undo();
@@ -764,10 +685,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public undo(): Selection[] {
try {
this._eventEmitter.beginDeferredEmit();
this._acquireMarkersTracker();
this._acquireDecorationsTracker();
return this._undo();
} finally {
this._releaseMarkersTracker();
this._releaseDecorationsTracker();
this._eventEmitter.endDeferredEmit();
}
}
@@ -789,10 +710,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public redo(): Selection[] {
try {
this._eventEmitter.beginDeferredEmit();
this._acquireMarkersTracker();
this._acquireDecorationsTracker();
return this._redo();
} finally {
this._releaseMarkersTracker();
this._releaseDecorationsTracker();
this._eventEmitter.endDeferredEmit();
}
}

View File

@@ -6,44 +6,243 @@
'use strict';
import { ITextModel } from 'vs/editor/common/editorCommon';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { computeIndentLevel } from 'vs/editor/common/model/modelLine';
export class IndentRange {
_indentRangeBrand: void;
startLineNumber: number;
endLineNumber: number;
indent: number;
export const MAX_FOLDING_REGIONS = 0xFFFF;
constructor(startLineNumber: number, endLineNumber: number, indent: number) {
this.startLineNumber = startLineNumber;
this.endLineNumber = endLineNumber;
this.indent = indent;
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
const MASK_LINE_NUMBER = 0xFFFFFF;
const MASK_INDENT = 0xFF000000;
export class IndentRanges {
private _startIndexes: Uint32Array;
private _endIndexes: Uint32Array;
private _model: ITextModel;
constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, model: ITextModel) {
if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
throw new Error('invalid startIndexes or endIndexes size');
}
this._startIndexes = startIndexes;
this._endIndexes = endIndexes;
this._model = model;
this._computeParentIndices();
}
public static deepCloneArr(indentRanges: IndentRange[]): IndentRange[] {
let result: IndentRange[] = [];
for (let i = 0, len = indentRanges.length; i < len; i++) {
let r = indentRanges[i];
result[i] = new IndentRange(r.startLineNumber, r.endLineNumber, r.indent);
private _computeParentIndices() {
let parentIndexes = [];
let isInsideLast = (startLineNumber: number, endLineNumber: number) => {
let index = parentIndexes[parentIndexes.length - 1];
return this.getStartLineNumber(index) <= startLineNumber && this.getEndLineNumber(index) >= endLineNumber;
};
for (let i = 0, len = this._startIndexes.length; i < len; i++) {
let startLineNumber = this._startIndexes[i];
let endLineNumber = this._endIndexes[i];
if (startLineNumber > MASK_LINE_NUMBER || endLineNumber > MASK_LINE_NUMBER) {
throw new Error('startLineNumber or endLineNumber must not exceed ' + MASK_LINE_NUMBER);
}
while (parentIndexes.length > 0 && !isInsideLast(startLineNumber, endLineNumber)) {
parentIndexes.pop();
}
let parentIndex = parentIndexes.length > 0 ? parentIndexes[parentIndexes.length - 1] : -1;
parentIndexes.push(i);
this._startIndexes[i] = startLineNumber + ((parentIndex & 0xFF) << 24);
this._endIndexes[i] = endLineNumber + ((parentIndex & 0xFF00) << 16);
}
return result;
}
public get length(): number {
return this._startIndexes.length;
}
public getStartLineNumber(index: number): number {
return this._startIndexes[index] & MASK_LINE_NUMBER;
}
public getEndLineNumber(index: number): number {
return this._endIndexes[index] & MASK_LINE_NUMBER;
}
public getParentIndex(index: number) {
let parent = ((this._startIndexes[index] & MASK_INDENT) >>> 24) + ((this._endIndexes[index] & MASK_INDENT) >>> 16);
if (parent === MAX_FOLDING_REGIONS) {
return -1;
}
return parent;
}
public getIndent(index: number) {
const lineNumber = this.getStartLineNumber(index);
const tabSize = this._model.getOptions().tabSize;
const lineContent = this._model.getLineContent(lineNumber);
return computeIndentLevel(lineContent, tabSize);
}
public contains(index: number, line: number) {
return this.getStartLineNumber(index) <= line && this.getEndLineNumber(index) >= line;
}
private findIndex(line: number) {
let low = 0, high = this._startIndexes.length;
if (high === 0) {
return -1; // no children
}
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (line < this.getStartLineNumber(mid)) {
high = mid;
} else {
low = mid + 1;
}
}
return low - 1;
}
public findRange(line: number): number {
let index = this.findIndex(line);
if (index >= 0) {
let endLineNumber = this.getEndLineNumber(index);
if (endLineNumber >= line) {
return index;
}
index = this.getParentIndex(index);
while (index !== -1) {
if (this.contains(index, line)) {
return index;
}
index = this.getParentIndex(index);
}
}
return -1;
}
}
// public only for testing
export class RangesCollector {
private _startIndexes: number[];
private _endIndexes: number[];
private _indentOccurrences: number[];
private _length: number;
private _foldingRegionsLimit: number;
constructor(foldingRegionsLimit: number) {
this._startIndexes = [];
this._endIndexes = [];
this._indentOccurrences = [];
this._length = 0;
this._foldingRegionsLimit = foldingRegionsLimit;
}
public insertFirst(startLineNumber: number, endLineNumber: number, indent: number) {
if (startLineNumber > MASK_LINE_NUMBER || endLineNumber > MASK_LINE_NUMBER) {
return;
}
let index = this._length;
this._startIndexes[index] = startLineNumber;
this._endIndexes[index] = endLineNumber;
this._length++;
if (indent < 1000) {
this._indentOccurrences[indent] = (this._indentOccurrences[indent] || 0) + 1;
}
}
public toIndentRanges(model: ITextModel) {
if (this._length <= this._foldingRegionsLimit) {
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(this._length);
let endIndexes = new Uint32Array(this._length);
for (let i = this._length - 1, k = 0; i >= 0; i-- , k++) {
startIndexes[k] = this._startIndexes[i];
endIndexes[k] = this._endIndexes[i];
}
return new IndentRanges(startIndexes, endIndexes, model);
} else {
let entries = 0;
let maxIndent = this._indentOccurrences.length;
for (let i = 0; i < this._indentOccurrences.length; i++) {
let n = this._indentOccurrences[i];
if (n) {
if (n + entries > this._foldingRegionsLimit) {
maxIndent = i;
break;
}
entries += n;
}
}
const tabSize = model.getOptions().tabSize;
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(entries);
let endIndexes = new Uint32Array(entries);
for (let i = this._length - 1, k = 0; i >= 0; i--) {
let startIndex = this._startIndexes[i];
let lineContent = model.getLineContent(startIndex);
let indent = computeIndentLevel(lineContent, tabSize);
if (indent < maxIndent) {
startIndexes[k] = startIndex;
endIndexes[k] = this._endIndexes[i];
k++;
}
}
return new IndentRanges(startIndexes, endIndexes, model);
}
}
}
export function computeRanges(model: ITextModel, minimumRangeSize: number = 1): IndentRange[] {
let result: IndentRange[] = [];
interface PreviousRegion { indent: number; line: number; marker: boolean; };
let previousRegions: { indent: number, line: number }[] = [];
previousRegions.push({ indent: -1, line: model.getLineCount() + 1 }); // sentinel, to make sure there's at least one entry
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRegionsLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): IndentRanges {
const tabSize = model.getOptions().tabSize;
let result = new RangesCollector(foldingRegionsLimit);
let pattern = void 0;
if (markers) {
pattern = new RegExp(`(${markers.start.source})|(?:${markers.end.source})`);
}
let previousRegions: PreviousRegion[] = [];
previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: false }); // sentinel, to make sure there's at least one entry
for (let line = model.getLineCount(); line > 0; line--) {
let indent = model.getIndentLevel(line);
let lineContent = model.getLineContent(line);
let indent = computeIndentLevel(lineContent, tabSize);
let previous = previousRegions[previousRegions.length - 1];
if (indent === -1) {
if (offSide && !previous.marker) {
// for offSide languages, empty lines are associated to the next block
previous.line = line;
}
continue; // only whitespace
}
let m;
if (pattern && (m = lineContent.match(pattern))) {
// folding pattern match
if (m[1]) { // start pattern match
// discard all regions until the folding pattern
let i = previousRegions.length - 1;
while (i > 0 && !previousRegions[i].marker) {
i--;
}
if (i > 0) {
previousRegions.length = i + 1;
previous = previousRegions[i];
let previous = previousRegions[previousRegions.length - 1];
// new folding range from pattern, includes the end line
result.insertFirst(line, previous.line, indent);
previous.marker = false;
previous.indent = indent;
previous.line = line;
continue;
} else {
// no end marker found, treat line as a regular line
}
} else { // end pattern match
previousRegions.push({ indent: -2, line, marker: true });
continue;
}
}
if (previous.indent > indent) {
// discard all regions with larger indent
do {
@@ -53,17 +252,16 @@ export function computeRanges(model: ITextModel, minimumRangeSize: number = 1):
// new folding range
let endLineNumber = previous.line - 1;
if (endLineNumber - line >= minimumRangeSize) {
result.push(new IndentRange(line, endLineNumber, indent));
if (endLineNumber - line >= 1) { // needs at east size 1
result.insertFirst(line, endLineNumber, indent);
}
}
if (previous.indent === indent) {
previous.line = line;
} else { // previous.indent < indent
// new region with a bigger indent
previousRegions.push({ indent, line });
previousRegions.push({ indent, line, marker: false });
}
}
return result.reverse();
return result.toIndentRanges(model);
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ import { IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
// The hierarchy is:
// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTrackedRanges -> TextModelWithMarkers -> TextModelWithTokens -> TextModel
// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTokens -> TextModel
var MODEL_ID = 0;
@@ -34,7 +34,9 @@ export class Model extends EditableTextModel implements IModel {
public onDidChangeLanguage(listener: (e: textModelEvents.IModelLanguageChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageChanged, listener);
}
public onDidChangeLanguageConfiguration(listener: (e: textModelEvents.IModelLanguageConfigurationChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, listener);
}
public static createFromString(text: string, options: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null, uri: URI = null): Model {
return new Model(RawTextSource.fromString(text), options, languageIdentifier, uri);
}

View File

@@ -7,94 +7,12 @@
import { IState, FontStyle, StandardTokenType, MetadataConsts, ColorId, LanguageId } from 'vs/editor/common/modes';
import { CharCode } from 'vs/base/common/charCode';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { Position } from 'vs/editor/common/core/position';
import { Constants } from 'vs/editor/common/core/uint';
export interface ILineEdit {
startColumn: number;
endColumn: number;
text: string;
forceMoveMarkers: boolean;
}
export class LineMarker {
_lineMarkerBrand: void;
public readonly id: string;
public readonly internalDecorationId: number;
public stickToPreviousCharacter: boolean;
public position: Position;
constructor(id: string, internalDecorationId: number, position: Position, stickToPreviousCharacter: boolean) {
this.id = id;
this.internalDecorationId = internalDecorationId;
this.position = position;
this.stickToPreviousCharacter = stickToPreviousCharacter;
}
public toString(): string {
return '{\'' + this.id + '\';' + this.position.toString() + ',' + this.stickToPreviousCharacter + '}';
}
public updateLineNumber(markersTracker: MarkersTracker, lineNumber: number): void {
if (this.position.lineNumber === lineNumber) {
return;
}
markersTracker.addChangedMarker(this);
this.position = new Position(lineNumber, this.position.column);
}
public updateColumn(markersTracker: MarkersTracker, column: number): void {
if (this.position.column === column) {
return;
}
markersTracker.addChangedMarker(this);
this.position = new Position(this.position.lineNumber, column);
}
public updatePosition(markersTracker: MarkersTracker, position: Position): void {
if (this.position.lineNumber === position.lineNumber && this.position.column === position.column) {
return;
}
markersTracker.addChangedMarker(this);
this.position = position;
}
public setPosition(position: Position) {
this.position = position;
}
public static compareMarkers(a: LineMarker, b: LineMarker): number {
if (a.position.column === b.position.column) {
return (a.stickToPreviousCharacter ? 0 : 1) - (b.stickToPreviousCharacter ? 0 : 1);
}
return a.position.column - b.position.column;
}
}
export class MarkersTracker {
_changedDecorationsBrand: void;
private _changedDecorations: number[];
private _changedDecorationsLen: number;
constructor() {
this._changedDecorations = [];
this._changedDecorationsLen = 0;
}
public addChangedMarker(marker: LineMarker): void {
let internalDecorationId = marker.internalDecorationId;
if (internalDecorationId !== 0) {
this._changedDecorations[this._changedDecorationsLen++] = internalDecorationId;
}
}
public getDecorationIds(): number[] {
return this._changedDecorations;
}
}
export interface ITokensAdjuster {
@@ -102,34 +20,17 @@ export interface ITokensAdjuster {
finish(delta: number, lineTextLength: number): void;
}
interface IMarkersAdjuster {
adjustDelta(toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics): void;
adjustSet(toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics): void;
finish(delta: number, lineTextLength: number): void;
}
var NO_OP_TOKENS_ADJUSTER: ITokensAdjuster = {
adjust: () => { },
finish: () => { }
};
var NO_OP_MARKERS_ADJUSTER: IMarkersAdjuster = {
adjustDelta: () => { },
adjustSet: () => { },
finish: () => { }
};
const enum MarkerMoveSemantics {
MarkerDefined = 0,
ForceMove = 1,
ForceStay = 2
}
/**
* Returns:
* - 0 => the line consists of whitespace
* - otherwise => the indent level is returned value - 1
* - -1 => the line consists of whitespace
* - otherwise => the indent level is returned value
*/
function computePlusOneIndentLevel(line: string, tabSize: number): number {
export function computeIndentLevel(line: string, tabSize: number): number {
let indent = 0;
let i = 0;
let len = line.length;
@@ -147,22 +48,15 @@ function computePlusOneIndentLevel(line: string, tabSize: number): number {
}
if (i === len) {
return 0; // line only consists of whitespace
return -1; // line only consists of whitespace
}
return indent + 1;
return indent;
}
export interface IModelLine {
readonly text: string;
// --- markers
addMarker(marker: LineMarker): void;
addMarkers(markers: LineMarker[]): void;
removeMarker(marker: LineMarker): void;
removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void;
getMarkers(): LineMarker[];
// --- tokenization
resetTokenizationState(): void;
isInvalid(): boolean;
@@ -172,151 +66,38 @@ export interface IModelLine {
getTokens(topLevelLanguageId: LanguageId): LineTokens;
setTokens(topLevelLanguageId: LanguageId, tokens: Uint32Array): void;
// --- indentation
updateTabSize(tabSize: number): void;
getIndentLevel(): number;
// --- editing
updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void;
applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number;
append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void;
split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine;
applyEdits(edits: ILineEdit[]): number;
append(other: IModelLine): void;
split(splitColumn: number): IModelLine;
}
export abstract class AbstractModelLine {
private _markers: LineMarker[];
constructor(initializeMarkers: boolean) {
if (initializeMarkers) {
this._markers = null;
}
constructor() {
}
///
public abstract get text(): string;
protected abstract _setText(text: string, tabSize: number);
protected abstract _setText(text: string): void;
protected abstract _createTokensAdjuster(): ITokensAdjuster;
protected abstract _createModelLine(text: string, tabSize: number): IModelLine;
protected abstract _createModelLine(text: string): IModelLine;
///
// private _printMarkers(): string {
// if (!this._markers) {
// return '[]';
// }
// if (this._markers.length === 0) {
// return '[]';
// }
// var markers = this._markers;
// var printMarker = (m:LineMarker) => {
// if (m.stickToPreviousCharacter) {
// return '|' + m.position.column;
// }
// return m.position.column + '|';
// };
// return '[' + markers.map(printMarker).join(', ') + ']';
// }
private _createMarkersAdjuster(markersTracker: MarkersTracker): IMarkersAdjuster {
if (!this._markers) {
return NO_OP_MARKERS_ADJUSTER;
}
if (this._markers.length === 0) {
return NO_OP_MARKERS_ADJUSTER;
}
this._markers.sort(LineMarker.compareMarkers);
var markers = this._markers;
var markersLength = markers.length;
var markersIndex = 0;
var marker = markers[markersIndex];
// console.log('------------- INITIAL MARKERS: ' + this._printMarkers());
let adjustMarkerBeforeColumn = (toColumn: number, moveSemantics: MarkerMoveSemantics) => {
if (marker.position.column < toColumn) {
return true;
}
if (marker.position.column > toColumn) {
return false;
}
if (moveSemantics === MarkerMoveSemantics.ForceMove) {
return false;
}
if (moveSemantics === MarkerMoveSemantics.ForceStay) {
return true;
}
return marker.stickToPreviousCharacter;
};
let adjustDelta = (toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics) => {
// console.log('------------------------------');
// console.log('adjustDelta called: toColumn: ' + toColumn + ', delta: ' + delta + ', minimumAllowedColumn: ' + minimumAllowedColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]);
// console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers());
while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) {
if (delta !== 0) {
let newColumn = Math.max(minimumAllowedColumn, marker.position.column + delta);
marker.updateColumn(markersTracker, newColumn);
}
markersIndex++;
if (markersIndex < markersLength) {
marker = markers[markersIndex];
}
}
// console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers());
};
let adjustSet = (toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics) => {
// console.log('------------------------------');
// console.log('adjustSet called: toColumn: ' + toColumn + ', newColumn: ' + newColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]);
// console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers());
while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) {
marker.updateColumn(markersTracker, newColumn);
markersIndex++;
if (markersIndex < markersLength) {
marker = markers[markersIndex];
}
}
// console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers());
};
let finish = (delta: number, lineTextLength: number) => {
adjustDelta(Constants.MAX_SAFE_SMALL_INTEGER, delta, 1, MarkerMoveSemantics.MarkerDefined);
// console.log('------------- FINAL MARKERS: ' + this._printMarkers());
};
return {
adjustDelta: adjustDelta,
adjustSet: adjustSet,
finish: finish
};
}
public applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number {
public applyEdits(edits: ILineEdit[]): number {
let deltaColumn = 0;
let resultText = this.text;
let tokensAdjuster = this._createTokensAdjuster();
let markersAdjuster = this._createMarkersAdjuster(markersTracker);
for (let i = 0, len = edits.length; i < len; i++) {
let edit = edits[i];
// console.log();
// console.log('=============================');
// console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>, forceMoveMarkers: ' + edit.forceMoveMarkers);
// console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>');
// console.log('deltaColumn: ' + deltaColumn);
let startColumn = deltaColumn + edit.startColumn;
@@ -324,193 +105,45 @@ export abstract class AbstractModelLine {
let deletingCnt = endColumn - startColumn;
let insertingCnt = edit.text.length;
// Adjust tokens & markers before this edit
// console.log('Adjust tokens & markers before this edit');
// Adjust tokens before this edit
// console.log('Adjust tokens before this edit');
tokensAdjuster.adjust(edit.startColumn - 1, deltaColumn, 1);
markersAdjuster.adjustDelta(edit.startColumn, deltaColumn, 1, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined));
// Adjust tokens & markers for the common part of this edit
// Adjust tokens for the common part of this edit
let commonLength = Math.min(deletingCnt, insertingCnt);
if (commonLength > 0) {
// console.log('Adjust tokens & markers for the common part of this edit');
// console.log('Adjust tokens for the common part of this edit');
tokensAdjuster.adjust(edit.startColumn - 1 + commonLength, deltaColumn, startColumn);
if (!edit.forceMoveMarkers) {
markersAdjuster.adjustDelta(edit.startColumn + commonLength, deltaColumn, startColumn, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > insertingCnt ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined));
}
}
// Perform the edit & update `deltaColumn`
resultText = resultText.substring(0, startColumn - 1) + edit.text + resultText.substring(endColumn - 1);
deltaColumn += insertingCnt - deletingCnt;
// Adjust tokens & markers inside this edit
// console.log('Adjust tokens & markers inside this edit');
// Adjust tokens inside this edit
// console.log('Adjust tokens inside this edit');
tokensAdjuster.adjust(edit.endColumn, deltaColumn, startColumn);
markersAdjuster.adjustSet(edit.endColumn, startColumn + insertingCnt, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : MarkerMoveSemantics.MarkerDefined);
}
// Wrap up tokens & markers; adjust remaining if needed
// Wrap up tokens; adjust remaining if needed
tokensAdjuster.finish(deltaColumn, resultText.length);
markersAdjuster.finish(deltaColumn, resultText.length);
// Save the resulting text
this._setText(resultText, tabSize);
this._setText(resultText);
return deltaColumn;
}
public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine {
// console.log('--> split @ ' + splitColumn + '::: ' + this._printMarkers());
var myText = this.text.substring(0, splitColumn - 1);
var otherText = this.text.substring(splitColumn - 1);
public split(splitColumn: number): IModelLine {
const myText = this.text.substring(0, splitColumn - 1);
const otherText = this.text.substring(splitColumn - 1);
var otherMarkers: LineMarker[] = null;
if (this._markers) {
this._markers.sort(LineMarker.compareMarkers);
for (let i = 0, len = this._markers.length; i < len; i++) {
let marker = this._markers[i];
if (
marker.position.column > splitColumn
|| (
marker.position.column === splitColumn
&& (
forceMoveMarkers
|| !marker.stickToPreviousCharacter
)
)
) {
let myMarkers = this._markers.slice(0, i);
otherMarkers = this._markers.slice(i);
this._markers = myMarkers;
break;
}
}
if (otherMarkers) {
for (let i = 0, len = otherMarkers.length; i < len; i++) {
let marker = otherMarkers[i];
marker.updateColumn(markersTracker, marker.position.column - (splitColumn - 1));
}
}
}
this._setText(myText, tabSize);
var otherLine = this._createModelLine(otherText, tabSize);
if (otherMarkers) {
otherLine.addMarkers(otherMarkers);
}
return otherLine;
this._setText(myText);
return this._createModelLine(otherText);
}
public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void {
// console.log('--> append: THIS :: ' + this._printMarkers());
// console.log('--> append: OTHER :: ' + this._printMarkers());
let thisTextLength = this.text.length;
this._setText(this.text + other.text, tabSize);
if (other instanceof AbstractModelLine) {
if (other._markers) {
// Other has markers
let otherMarkers = other._markers;
// Adjust other markers
for (let i = 0, len = otherMarkers.length; i < len; i++) {
let marker = otherMarkers[i];
marker.updatePosition(markersTracker, new Position(myLineNumber, marker.position.column + thisTextLength));
}
this.addMarkers(otherMarkers);
}
}
}
public addMarker(marker: LineMarker): void {
if (!this._markers) {
this._markers = [marker];
} else {
this._markers.push(marker);
}
}
public addMarkers(markers: LineMarker[]): void {
if (markers.length === 0) {
return;
}
if (!this._markers) {
this._markers = markers.slice(0);
} else {
this._markers = this._markers.concat(markers);
}
}
public removeMarker(marker: LineMarker): void {
if (!this._markers) {
return;
}
let index = this._indexOfMarkerId(marker.id);
if (index < 0) {
return;
}
if (this._markers.length === 1) {
// was last marker on line
this._markers = null;
} else {
this._markers.splice(index, 1);
}
}
public removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void {
if (!this._markers) {
return;
}
for (let i = 0, len = this._markers.length; i < len; i++) {
let marker = this._markers[i];
if (deleteMarkers[marker.id]) {
this._markers.splice(i, 1);
len--;
i--;
}
}
if (this._markers.length === 0) {
this._markers = null;
}
}
public getMarkers(): LineMarker[] {
if (!this._markers) {
return null;
}
return this._markers;
}
public updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void {
if (this._markers) {
let markers = this._markers;
for (let i = 0, len = markers.length; i < len; i++) {
let marker = markers[i];
marker.updateLineNumber(markersTracker, newLineNumber);
}
}
}
private _indexOfMarkerId(markerId: string): number {
let markers = this._markers;
for (let i = 0, len = markers.length; i < len; i++) {
if (markers[i].id === markerId) {
return i;
}
}
return undefined;
public append(other: IModelLine): void {
this._setText(this.text + other.text);
}
}
@@ -519,59 +152,33 @@ export class ModelLine extends AbstractModelLine implements IModelLine {
private _text: string;
public get text(): string { return this._text; }
/**
* bits 31 - 1 => indentLevel
* bit 0 => isInvalid
*/
private _metadata: number;
private _isInvalid: boolean;
public isInvalid(): boolean {
return (this._metadata & 0x00000001) ? true : false;
return this._isInvalid;
}
public setIsInvalid(isInvalid: boolean): void {
this._metadata = (this._metadata & 0xfffffffe) | (isInvalid ? 1 : 0);
}
/**
* Returns:
* - -1 => the line consists of whitespace
* - otherwise => the indent level is returned value
*/
public getIndentLevel(): number {
return ((this._metadata & 0xfffffffe) >> 1) - 1;
}
private _setPlusOneIndentLevel(value: number): void {
this._metadata = (this._metadata & 0x00000001) | ((value & 0xefffffff) << 1);
}
public updateTabSize(tabSize: number): void {
if (tabSize === 0) {
// don't care mark
this._metadata = this._metadata & 0x00000001;
} else {
this._setPlusOneIndentLevel(computePlusOneIndentLevel(this._text, tabSize));
}
this._isInvalid = isInvalid;
}
private _state: IState;
private _lineTokens: ArrayBuffer;
constructor(text: string, tabSize: number) {
super(true);
this._metadata = 0;
this._setText(text, tabSize);
constructor(text: string) {
super();
this._isInvalid = false;
this._setText(text);
this._state = null;
this._lineTokens = null;
}
protected _createModelLine(text: string, tabSize: number): IModelLine {
return new ModelLine(text, tabSize);
protected _createModelLine(text: string): IModelLine {
return new ModelLine(text);
}
public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine {
let result = super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize);
public split(splitColumn: number): IModelLine {
let result = super.split(splitColumn);
// Mark overflowing tokens for deletion & delete marked tokens
this._deleteMarkedTokens(this._markOverflowingTokensForDeletion(0, this.text.length));
@@ -579,10 +186,10 @@ export class ModelLine extends AbstractModelLine implements IModelLine {
return result;
}
public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void {
public append(other: IModelLine): void {
let thisTextLength = this.text.length;
super.append(markersTracker, myLineNumber, other, tabSize);
super.append(other);
if (other instanceof ModelLine) {
let otherRawTokens = other._lineTokens;
@@ -787,20 +394,14 @@ export class ModelLine extends AbstractModelLine implements IModelLine {
this._lineTokens = newTokens.buffer;
}
protected _setText(text: string, tabSize: number): void {
protected _setText(text: string): void {
this._text = text;
if (tabSize === 0) {
// don't care mark
this._metadata = this._metadata & 0x00000001;
} else {
this._setPlusOneIndentLevel(computePlusOneIndentLevel(text, tabSize));
}
}
}
/**
* A model line that cannot store any tokenization state, nor does it compute indentation levels.
* A model line that cannot store any tokenization state.
* It has no fields except the text.
*/
export class MinimalModelLine extends AbstractModelLine implements IModelLine {
@@ -815,33 +416,21 @@ export class MinimalModelLine extends AbstractModelLine implements IModelLine {
public setIsInvalid(isInvalid: boolean): void {
}
/**
* Returns:
* - -1 => the line consists of whitespace
* - otherwise => the indent level is returned value
*/
public getIndentLevel(): number {
return 0;
constructor(text: string) {
super();
this._setText(text);
}
public updateTabSize(tabSize: number): void {
protected _createModelLine(text: string): IModelLine {
return new MinimalModelLine(text);
}
constructor(text: string, tabSize: number) {
super(false);
this._setText(text, tabSize);
public split(splitColumn: number): IModelLine {
return super.split(splitColumn);
}
protected _createModelLine(text: string, tabSize: number): IModelLine {
return new MinimalModelLine(text, tabSize);
}
public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine {
return super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize);
}
public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void {
super.append(markersTracker, myLineNumber, other, tabSize);
public append(other: IModelLine): void {
super.append(other);
}
// --- BEGIN STATE
@@ -877,7 +466,7 @@ export class MinimalModelLine extends AbstractModelLine implements IModelLine {
return NO_OP_TOKENS_ADJUSTER;
}
protected _setText(text: string, tabSize: number): void {
protected _setText(text: string): void {
this._text = text;
}
}

View File

@@ -8,19 +8,17 @@ import { OrderGuaranteeEventEmitter, BulkListenerCallback } from 'vs/base/common
import * as strings from 'vs/base/common/strings';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ModelLine, IModelLine, MinimalModelLine } from 'vs/editor/common/model/modelLine';
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges';
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
import { TextSource, ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
const USE_MIMINAL_MODEL_LINE = true;
const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 10000;
@@ -85,7 +83,6 @@ export class TextModel implements editorCommon.ITextModel {
protected _isDisposing: boolean;
protected _options: editorCommon.TextModelResolvedOptions;
protected _lineStarts: PrefixSumComputer;
private _indentRanges: IndentRange[];
private _versionId: number;
/**
@@ -124,11 +121,11 @@ export class TextModel implements editorCommon.ITextModel {
this._isDisposing = false;
}
protected _createModelLine(text: string, tabSize: number): IModelLine {
if (USE_MIMINAL_MODEL_LINE && this._isTooLargeForTokenization) {
return new MinimalModelLine(text, tabSize);
protected _createModelLine(text: string): IModelLine {
if (this._isTooLargeForTokenization) {
return new MinimalModelLine(text);
}
return new ModelLine(text, tabSize);
return new ModelLine(text);
}
protected _assertNotDisposed(): void {
@@ -170,13 +167,6 @@ export class TextModel implements editorCommon.ITextModel {
let e = this._options.createChangeEvent(newOpts);
this._options = newOpts;
if (e.tabSize) {
let newTabSize = this._options.tabSize;
for (let i = 0, len = this._lines.length; i < len; i++) {
this._lines[i].updateTabSize(newTabSize);
}
}
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelOptionsChanged, e);
}
@@ -263,22 +253,9 @@ export class TextModel implements editorCommon.ITextModel {
return this._alternativeVersionId;
}
private _ensureLineStarts(): void {
if (!this._lineStarts) {
const eolLength = this._EOL.length;
const linesLength = this._lines.length;
const lineStartValues = new Uint32Array(linesLength);
for (let i = 0; i < linesLength; i++) {
lineStartValues[i] = this._lines[i].text.length + eolLength;
}
this._lineStarts = new PrefixSumComputer(lineStartValues);
}
}
public getOffsetAt(rawPosition: IPosition): number {
this._assertNotDisposed();
let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false);
this._ensureLineStarts();
return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + position.column - 1;
}
@@ -287,7 +264,6 @@ export class TextModel implements editorCommon.ITextModel {
offset = Math.floor(offset);
offset = Math.max(0, offset);
this._ensureLineStarts();
let out = this._lineStarts.getIndexOf(offset);
let lineLength = this._lines[out.index].text.length;
@@ -504,74 +480,6 @@ export class TextModel implements editorCommon.ITextModel {
return this._lines[lineNumber - 1].text;
}
public getIndentLevel(lineNumber: number): number {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
}
return this._lines[lineNumber - 1].getIndentLevel();
}
protected _resetIndentRanges(): void {
this._indentRanges = null;
}
private _getIndentRanges(): IndentRange[] {
if (!this._indentRanges) {
this._indentRanges = computeRanges(this);
}
return this._indentRanges;
}
public getIndentRanges(): IndentRange[] {
this._assertNotDisposed();
let indentRanges = this._getIndentRanges();
return IndentRange.deepCloneArr(indentRanges);
}
private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number {
let lineIndentLevel = this._lines[lineNumber - 1].getIndentLevel();
if (lineIndentLevel === -1) {
return indentGuide;
}
let maxIndentGuide = Math.ceil(lineIndentLevel / this._options.tabSize);
return Math.min(maxIndentGuide, indentGuide);
}
public getLineIndentGuide(lineNumber: number): number {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
}
let indentRanges = this._getIndentRanges();
for (let i = indentRanges.length - 1; i >= 0; i--) {
let rng = indentRanges[i];
if (rng.startLineNumber === lineNumber) {
return this._toValidLineIndentGuide(lineNumber, Math.ceil(rng.indent / this._options.tabSize));
}
if (rng.startLineNumber < lineNumber && lineNumber <= rng.endLineNumber) {
return this._toValidLineIndentGuide(lineNumber, 1 + Math.floor(rng.indent / this._options.tabSize));
}
if (rng.endLineNumber + 1 === lineNumber) {
let bestIndent = rng.indent;
while (i > 0) {
i--;
rng = indentRanges[i];
if (rng.endLineNumber + 1 === lineNumber) {
bestIndent = rng.indent;
}
}
return this._toValidLineIndentGuide(lineNumber, Math.ceil(bestIndent / this._options.tabSize));
}
}
return 0;
}
public getLinesContent(): string[] {
this._assertNotDisposed();
var r: string[] = [];
@@ -586,6 +494,12 @@ export class TextModel implements editorCommon.ITextModel {
return this._EOL;
}
protected _onBeforeEOLChange(): void {
}
protected _onAfterEOLChange(): void {
}
public setEOL(eol: editorCommon.EndOfLineSequence): void {
this._assertNotDisposed();
const newEOL = (eol === editorCommon.EndOfLineSequence.CRLF ? '\r\n' : '\n');
@@ -599,9 +513,11 @@ export class TextModel implements editorCommon.ITextModel {
const endLineNumber = this.getLineCount();
const endColumn = this.getLineMaxColumn(endLineNumber);
this._onBeforeEOLChange();
this._EOL = newEOL;
this._lineStarts = null;
this._constructLineStarts();
this._increaseVersionId();
this._onAfterEOLChange();
this._emitModelRawContentChangedEvent(
new textModelEvents.ModelRawContentChangedEvent(
@@ -668,6 +584,77 @@ export class TextModel implements editorCommon.ITextModel {
return lineNumber;
}
/**
* Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc.
* Will try to not allocate if possible.
*/
protected _validateRangeRelaxedNoAllocations(range: IRange): Range {
const linesCount = this._lines.length;
const initialStartLineNumber = range.startLineNumber;
const initialStartColumn = range.startColumn;
let startLineNumber: number;
let startColumn: number;
if (initialStartLineNumber < 1) {
startLineNumber = 1;
startColumn = 1;
} else if (initialStartLineNumber > linesCount) {
startLineNumber = linesCount;
startColumn = this.getLineMaxColumn(startLineNumber);
} else {
startLineNumber = initialStartLineNumber | 0;
if (initialStartColumn <= 1) {
startColumn = 1;
} else {
const maxColumn = this.getLineMaxColumn(startLineNumber);
if (initialStartColumn >= maxColumn) {
startColumn = maxColumn;
} else {
startColumn = initialStartColumn | 0;
}
}
}
const initialEndLineNumber = range.endLineNumber;
const initialEndColumn = range.endColumn;
let endLineNumber: number;
let endColumn: number;
if (initialEndLineNumber < 1) {
endLineNumber = 1;
endColumn = 1;
} else if (initialEndLineNumber > linesCount) {
endLineNumber = linesCount;
endColumn = this.getLineMaxColumn(endLineNumber);
} else {
endLineNumber = initialEndLineNumber | 0;
if (initialEndColumn <= 1) {
endColumn = 1;
} else {
const maxColumn = this.getLineMaxColumn(endLineNumber);
if (initialEndColumn >= maxColumn) {
endColumn = maxColumn;
} else {
endColumn = initialEndColumn | 0;
}
}
}
if (
initialStartLineNumber === startLineNumber
&& initialStartColumn === startColumn
&& initialEndLineNumber === endLineNumber
&& initialEndColumn === endColumn
&& range instanceof Range
&& !(range instanceof Selection)
) {
return range;
}
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
}
/**
* @param strict Do NOT allow a position inside a high-low surrogate pair
*/
@@ -772,20 +759,28 @@ export class TextModel implements editorCommon.ITextModel {
}
private _constructLines(textSource: ITextSource): void {
const tabSize = this._options.tabSize;
let rawLines = textSource.lines;
let modelLines: IModelLine[] = new Array<IModelLine>(rawLines.length);
for (let i = 0, len = rawLines.length; i < len; i++) {
modelLines[i] = this._createModelLine(rawLines[i], tabSize);
modelLines[i] = this._createModelLine(rawLines[i]);
}
this._BOM = textSource.BOM;
this._mightContainRTL = textSource.containsRTL;
this._mightContainNonBasicASCII = !textSource.isBasicASCII;
this._EOL = textSource.EOL;
this._lines = modelLines;
this._lineStarts = null;
this._resetIndentRanges();
this._constructLineStarts();
}
private _constructLineStarts(): void {
const eolLength = this._EOL.length;
const linesLength = this._lines.length;
const lineStartValues = new Uint32Array(linesLength);
for (let i = 0; i < linesLength; i++) {
lineStartValues[i] = this._lines[i].text.length + eolLength;
}
this._lineStarts = new PrefixSumComputer(lineStartValues);
}
private _getEndOfLine(eol: editorCommon.EndOfLinePreference): string {

View File

@@ -18,6 +18,7 @@ export const TextModelEventType = {
ModelContentChanged: 'contentChanged',
ModelRawContentChanged2: 'rawContentChanged2',
ModelDecorationsChanged: 'decorationsChanged',
ModelLanguageConfigurationChanged: 'modelLanguageConfigurationChanged'
};
/**
@@ -34,6 +35,12 @@ export interface IModelLanguageChangedEvent {
readonly newLanguage: string;
}
/**
* An event describing that the language configuration associated with a model has changed.
*/
export interface IModelLanguageConfigurationChangedEvent {
}
export interface IModelContentChange {
/**
* The range that got replaced.
@@ -81,18 +88,6 @@ export interface IModelContentChangedEvent {
* An event describing that model decorations have changed.
*/
export interface IModelDecorationsChangedEvent {
/**
* Lists of ids for added decorations.
*/
readonly addedDecorations: string[];
/**
* Lists of ids for changed decorations.
*/
readonly changedDecorations: string[];
/**
* List of ids for removed decorations.
*/
readonly removedDecorations: string[];
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -1,174 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { Position } from 'vs/editor/common/core/position';
import { ITextModelWithMarkers, ITextModelCreationOptions } from 'vs/editor/common/editorCommon';
import { LineMarker } from 'vs/editor/common/model/modelLine';
import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource';
export interface IMarkerIdToMarkerMap {
[key: string]: LineMarker;
}
export interface INewMarker {
internalDecorationId: number;
position: Position;
stickToPreviousCharacter: boolean;
}
var _INSTANCE_COUNT = 0;
export class TextModelWithMarkers extends TextModelWithTokens implements ITextModelWithMarkers {
private _markerIdGenerator: IdGenerator;
protected _markerIdToMarker: IMarkerIdToMarkerMap;
constructor(rawTextSource: IRawTextSource, creationOptions: ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) {
super(rawTextSource, creationOptions, languageIdentifier);
this._markerIdGenerator = new IdGenerator((++_INSTANCE_COUNT) + ';');
this._markerIdToMarker = Object.create(null);
}
public dispose(): void {
this._markerIdToMarker = null;
super.dispose();
}
protected _resetValue(newValue: ITextSource): void {
super._resetValue(newValue);
// Destroy all my markers
this._markerIdToMarker = Object.create(null);
}
_addMarker(internalDecorationId: number, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string {
var pos = this.validatePosition(new Position(lineNumber, column));
var marker = new LineMarker(this._markerIdGenerator.nextId(), internalDecorationId, pos, stickToPreviousCharacter);
this._markerIdToMarker[marker.id] = marker;
this._lines[pos.lineNumber - 1].addMarker(marker);
return marker.id;
}
protected _addMarkers(newMarkers: INewMarker[]): LineMarker[] {
if (newMarkers.length === 0) {
return [];
}
let markers: LineMarker[] = [];
for (let i = 0, len = newMarkers.length; i < len; i++) {
let newMarker = newMarkers[i];
let marker = new LineMarker(this._markerIdGenerator.nextId(), newMarker.internalDecorationId, newMarker.position, newMarker.stickToPreviousCharacter);
this._markerIdToMarker[marker.id] = marker;
markers[i] = marker;
}
let sortedMarkers = markers.slice(0);
sortedMarkers.sort((a, b) => {
return a.position.lineNumber - b.position.lineNumber;
});
let currentLineNumber = 0;
let currentMarkers: LineMarker[] = [], currentMarkersLen = 0;
for (let i = 0, len = sortedMarkers.length; i < len; i++) {
let marker = sortedMarkers[i];
if (marker.position.lineNumber !== currentLineNumber) {
if (currentLineNumber !== 0) {
this._lines[currentLineNumber - 1].addMarkers(currentMarkers);
}
currentLineNumber = marker.position.lineNumber;
currentMarkers.length = 0;
currentMarkersLen = 0;
}
currentMarkers[currentMarkersLen++] = marker;
}
this._lines[currentLineNumber - 1].addMarkers(currentMarkers);
return markers;
}
_changeMarker(id: string, lineNumber: number, column: number): void {
let marker = this._markerIdToMarker[id];
if (!marker) {
return;
}
let newPos = this.validatePosition(new Position(lineNumber, column));
if (newPos.lineNumber !== marker.position.lineNumber) {
// Move marker between lines
this._lines[marker.position.lineNumber - 1].removeMarker(marker);
this._lines[newPos.lineNumber - 1].addMarker(marker);
}
marker.setPosition(newPos);
}
_changeMarkerStickiness(id: string, newStickToPreviousCharacter: boolean): void {
let marker = this._markerIdToMarker[id];
if (!marker) {
return;
}
marker.stickToPreviousCharacter = newStickToPreviousCharacter;
}
_getMarker(id: string): Position {
let marker = this._markerIdToMarker[id];
if (!marker) {
return null;
}
return marker.position;
}
_getMarkersCount(): number {
return Object.keys(this._markerIdToMarker).length;
}
_removeMarker(id: string): void {
let marker = this._markerIdToMarker[id];
if (!marker) {
return;
}
this._lines[marker.position.lineNumber - 1].removeMarker(marker);
delete this._markerIdToMarker[id];
}
protected _removeMarkers(markers: LineMarker[]): void {
markers.sort((a, b) => {
return a.position.lineNumber - b.position.lineNumber;
});
let currentLineNumber = 0;
let currentMarkers: { [markerId: string]: boolean; } = null;
for (let i = 0, len = markers.length; i < len; i++) {
let marker = markers[i];
delete this._markerIdToMarker[marker.id];
if (marker.position.lineNumber !== currentLineNumber) {
if (currentLineNumber !== 0) {
this._lines[currentLineNumber - 1].removeMarkers(currentMarkers);
}
currentLineNumber = marker.position.lineNumber;
currentMarkers = Object.create(null);
}
currentMarkers[marker.id] = true;
}
this._lines[currentLineNumber - 1].removeMarkers(currentMarkers);
}
}

View File

@@ -22,6 +22,8 @@ import { getWordAtText } from 'vs/editor/common/model/wordHelper';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { IndentRanges, computeRanges } from 'vs/editor/common/model/indentRanges';
import { computeIndentLevel } from 'vs/editor/common/model/modelLine';
class ModelTokensChangedEventBuilder {
@@ -69,6 +71,9 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
private _invalidLineStartIndex: number;
private _lastState: IState;
private _indentRanges: IndentRanges;
private _languageRegistryListener: IDisposable;
private _revalidateTokensTimeout: number;
constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) {
@@ -95,11 +100,20 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
this._revalidateTokensTimeout = -1;
this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange((e) => {
if (e.languageIdentifier.id === this._languageIdentifier.id) {
this._resetIndentRanges();
this._emitModelLanguageConfigurationEvent({});
}
});
this._resetTokenizationState();
this._resetIndentRanges();
}
public dispose(): void {
this._tokenizationListener.dispose();
this._languageRegistryListener.dispose();
this._clearTimers();
this._lastState = null;
@@ -114,6 +128,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
super._resetValue(newValue);
// Cancel tokenization, clear all tokens and begin tokenizing
this._resetTokenizationState();
this._resetIndentRanges();
}
protected _resetTokenizationState(): void {
@@ -225,6 +240,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
// Cancel tokenization, clear all tokens and begin tokenizing
this._resetTokenizationState();
this._resetIndentRanges();
this.emitModelTokensChangedEvent({
ranges: [{
@@ -233,6 +249,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
}]
});
this._emitModelModeChangedEvent(e);
this._emitModelLanguageConfigurationEvent({});
}
public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId {
@@ -398,6 +415,12 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
}
}
private _emitModelLanguageConfigurationEvent(e: textModelEvents.IModelLanguageConfigurationChangedEvent): void {
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, e);
}
}
private _emitModelModeChangedEvent(e: textModelEvents.IModelLanguageChangedEvent): void {
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageChanged, e);
@@ -814,4 +837,118 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
isOpen: modeBrackets.textIsOpenBracket[text]
};
}
protected _resetIndentRanges(): void {
this._indentRanges = null;
}
private _getIndentRanges(): IndentRanges {
if (!this._indentRanges) {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
this._indentRanges = computeRanges(this, offSide, markers);
}
return this._indentRanges;
}
public getIndentRanges(): IndentRanges {
return this._getIndentRanges();
}
private _computeIndentLevel(lineIndex: number): number {
return computeIndentLevel(this._lines[lineIndex].text, this._options.tabSize);
}
public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] {
this._assertNotDisposed();
const lineCount = this.getLineCount();
if (startLineNumber < 1 || startLineNumber > lineCount) {
throw new Error('Illegal value ' + startLineNumber + ' for `startLineNumber`');
}
if (endLineNumber < 1 || endLineNumber > lineCount) {
throw new Error('Illegal value ' + endLineNumber + ' for `endLineNumber`');
}
const foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id);
const offSide = foldingRules && foldingRules.offSide;
let result: number[] = new Array<number>(endLineNumber - startLineNumber + 1);
let aboveContentLineIndex = -2; /* -2 is a marker for not having computed it */
let aboveContentLineIndent = -1;
let belowContentLineIndex = -2; /* -2 is a marker for not having computed it */
let belowContentLineIndent = -1;
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
let resultIndex = lineNumber - startLineNumber;
const currentIndent = this._computeIndentLevel(lineNumber - 1);
if (currentIndent >= 0) {
// This line has content (besides whitespace)
// Use the line's indent
aboveContentLineIndex = lineNumber - 1;
aboveContentLineIndent = currentIndent;
result[resultIndex] = Math.ceil(currentIndent / this._options.tabSize);
continue;
}
if (aboveContentLineIndex === -2) {
aboveContentLineIndex = -1;
aboveContentLineIndent = -1;
// must find previous line with content
for (let lineIndex = lineNumber - 2; lineIndex >= 0; lineIndex--) {
let indent = this._computeIndentLevel(lineIndex);
if (indent >= 0) {
aboveContentLineIndex = lineIndex;
aboveContentLineIndent = indent;
break;
}
}
}
if (belowContentLineIndex !== -1 && (belowContentLineIndex === -2 || belowContentLineIndex < lineNumber - 1)) {
belowContentLineIndex = -1;
belowContentLineIndent = -1;
// must find next line with content
for (let lineIndex = lineNumber; lineIndex < lineCount; lineIndex++) {
let indent = this._computeIndentLevel(lineIndex);
if (indent >= 0) {
belowContentLineIndex = lineIndex;
belowContentLineIndent = indent;
break;
}
}
}
if (aboveContentLineIndent === -1 || belowContentLineIndent === -1) {
// At the top or bottom of the file
result[resultIndex] = 0;
} else if (aboveContentLineIndent < belowContentLineIndent) {
// we are inside the region above
result[resultIndex] = (1 + Math.floor(aboveContentLineIndent / this._options.tabSize));
} else if (aboveContentLineIndent === belowContentLineIndent) {
// we are in between two regions
result[resultIndex] = Math.ceil(belowContentLineIndent / this._options.tabSize);
} else {
if (offSide) {
// same level as region below
result[resultIndex] = Math.ceil(belowContentLineIndent / this._options.tabSize);
} else {
// we are inside the region that ends below
result[resultIndex] = (1 + Math.floor(belowContentLineIndent / this._options.tabSize));
}
}
}
return result;
}
}