mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
1369
src/vs/editor/common/model/intervalTree.ts
Normal file
1369
src/vs/editor/common/model/intervalTree.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user