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,7 +7,7 @@
import * as assert from 'assert';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { EditableTextModel, IValidatedEditOperation } from 'vs/editor/common/model/editableTextModel';
import { MirrorModel } from 'vs/editor/common/model/mirrorModel';
import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils';
@@ -15,12 +15,13 @@ import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvent
suite('EditorModel - EditableTextModel._getInverseEdits', () => {
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation {
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IValidatedEditOperation {
return {
sortIndex: 0,
identifier: null,
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
rangeLength: rangeLength,
rangeOffset: 0,
rangeLength: 0,
lines: text,
forceMoveMarkers: false,
isAutoWhitespaceEdit: false
@@ -39,7 +40,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('single insert', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello'])
editOp(1, 1, 1, 1, ['hello'])
],
[
inverseEditOp(1, 1, 1, 6)
@@ -50,8 +51,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('Bug 19872: Undo is funky', () => {
assertInverseEdits(
[
editOp(2, 1, 2, 2, 0, ['']),
editOp(3, 1, 4, 2, 0, [''])
editOp(2, 1, 2, 2, ['']),
editOp(3, 1, 4, 2, [''])
],
[
inverseEditOp(2, 1, 2, 1),
@@ -63,8 +64,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single unrelated inserts', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello']),
editOp(2, 1, 2, 1, 0, ['world'])
editOp(1, 1, 1, 1, ['hello']),
editOp(2, 1, 2, 1, ['world'])
],
[
inverseEditOp(1, 1, 1, 6),
@@ -76,8 +77,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single inserts 1', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello']),
editOp(1, 2, 1, 2, 0, ['world'])
editOp(1, 1, 1, 1, ['hello']),
editOp(1, 2, 1, 2, ['world'])
],
[
inverseEditOp(1, 1, 1, 6),
@@ -89,8 +90,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single inserts 2', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello']),
editOp(1, 4, 1, 4, 0, ['world'])
editOp(1, 1, 1, 1, ['hello']),
editOp(1, 4, 1, 4, ['world'])
],
[
inverseEditOp(1, 1, 1, 6),
@@ -102,7 +103,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('multiline insert', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello', 'world'])
editOp(1, 1, 1, 1, ['hello', 'world'])
],
[
inverseEditOp(1, 1, 2, 6)
@@ -113,8 +114,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two unrelated multiline inserts', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello', 'world']),
editOp(2, 1, 2, 1, 0, ['how', 'are', 'you?']),
editOp(1, 1, 1, 1, ['hello', 'world']),
editOp(2, 1, 2, 1, ['how', 'are', 'you?']),
],
[
inverseEditOp(1, 1, 2, 6),
@@ -126,8 +127,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two multiline inserts 1', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 1, 0, ['hello', 'world']),
editOp(1, 2, 1, 2, 0, ['how', 'are', 'you?']),
editOp(1, 1, 1, 1, ['hello', 'world']),
editOp(1, 2, 1, 2, ['how', 'are', 'you?']),
],
[
inverseEditOp(1, 1, 2, 6),
@@ -139,7 +140,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('single delete', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, null)
editOp(1, 1, 1, 6, null)
],
[
inverseEditOp(1, 1, 1, 1)
@@ -150,8 +151,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single unrelated deletes', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, null),
editOp(2, 1, 2, 6, 0, null)
editOp(1, 1, 1, 6, null),
editOp(2, 1, 2, 6, null)
],
[
inverseEditOp(1, 1, 1, 1),
@@ -163,8 +164,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single deletes 1', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, null),
editOp(1, 7, 1, 12, 0, null)
editOp(1, 1, 1, 6, null),
editOp(1, 7, 1, 12, null)
],
[
inverseEditOp(1, 1, 1, 1),
@@ -176,8 +177,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two single deletes 2', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, null),
editOp(1, 9, 1, 14, 0, null)
editOp(1, 1, 1, 6, null),
editOp(1, 9, 1, 14, null)
],
[
inverseEditOp(1, 1, 1, 1),
@@ -189,7 +190,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('multiline delete', () => {
assertInverseEdits(
[
editOp(1, 1, 2, 6, 0, null)
editOp(1, 1, 2, 6, null)
],
[
inverseEditOp(1, 1, 1, 1)
@@ -200,8 +201,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two unrelated multiline deletes', () => {
assertInverseEdits(
[
editOp(1, 1, 2, 6, 0, null),
editOp(3, 1, 5, 5, 0, null),
editOp(1, 1, 2, 6, null),
editOp(3, 1, 5, 5, null),
],
[
inverseEditOp(1, 1, 1, 1),
@@ -213,8 +214,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two multiline deletes 1', () => {
assertInverseEdits(
[
editOp(1, 1, 2, 6, 0, null),
editOp(2, 7, 4, 5, 0, null),
editOp(1, 1, 2, 6, null),
editOp(2, 7, 4, 5, null),
],
[
inverseEditOp(1, 1, 1, 1),
@@ -226,7 +227,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('single replace', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, ['Hello world'])
editOp(1, 1, 1, 6, ['Hello world'])
],
[
inverseEditOp(1, 1, 1, 12)
@@ -237,8 +238,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('two replaces', () => {
assertInverseEdits(
[
editOp(1, 1, 1, 6, 0, ['Hello world']),
editOp(1, 7, 1, 8, 0, ['How are you?']),
editOp(1, 1, 1, 6, ['Hello world']),
editOp(1, 7, 1, 8, ['How are you?']),
],
[
inverseEditOp(1, 1, 1, 12),
@@ -250,9 +251,9 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
test('many edits', () => {
assertInverseEdits(
[
editOp(1, 2, 1, 2, 0, ['', ' ']),
editOp(1, 5, 1, 6, 0, ['']),
editOp(1, 9, 1, 9, 0, ['', ''])
editOp(1, 2, 1, 2, ['', ' ']),
editOp(1, 5, 1, 6, ['']),
editOp(1, 9, 1, 9, ['', ''])
],
[
inverseEditOp(1, 2, 2, 3),
@@ -265,11 +266,12 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => {
suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation {
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeOffset: number, rangeLength: number, text: string[]): IValidatedEditOperation {
return {
sortIndex: 0,
identifier: null,
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
rangeOffset: rangeOffset,
rangeLength: rangeLength,
lines: text,
forceMoveMarkers: false,
@@ -297,9 +299,9 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'1'
],
[
editOp(1, 3, 1, 3, 0, [' new line', 'No longer'])
editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer'])
],
editOp(1, 3, 1, 3, 0, [' new line', 'No longer'])
editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer'])
);
});
@@ -311,11 +313,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'',
'1'
], [
editOp(1, 1, 1, 3, 0, ['Your']),
editOp(1, 4, 1, 4, 0, ['Interesting ']),
editOp(2, 3, 2, 6, 0, null)
editOp(1, 1, 1, 3, 0, 2, ['Your']),
editOp(1, 4, 1, 4, 3, 0, ['Interesting ']),
editOp(2, 3, 2, 6, 16, 3, null)
],
editOp(1, 1, 2, 6, 19, [
editOp(1, 1, 2, 6, 0, 19, [
'Your Interesting First Line',
'\t\t'
]));
@@ -331,10 +333,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'1'
],
[
editOp(1, 3, 1, 3, 0, ['', '', '', '', '']),
editOp(3, 15, 3, 15, 0, ['a', 'b'])
editOp(1, 3, 1, 3, 2, 0, ['', '', '', '', '']),
editOp(3, 15, 3, 15, 45, 0, ['a', 'b'])
],
editOp(1, 3, 3, 15, 43, [
editOp(1, 3, 3, 15, 2, 43, [
'',
'',
'',
@@ -357,9 +359,9 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'1'
],
[
editOp(1, 1, 1, 1, 0, [''])
editOp(1, 1, 1, 1, 0, 0, [''])
],
editOp(1, 1, 1, 1, 0, [''])
editOp(1, 1, 1, 1, 0, 0, [''])
);
});
@@ -373,10 +375,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'123'
],
[
editOp(2, 1, 2, 3, 0, ['\t']),
editOp(3, 1, 3, 5, 0, [''])
editOp(2, 1, 2, 3, 14, 2, ['\t']),
editOp(3, 1, 3, 5, 31, 4, [''])
],
editOp(2, 1, 3, 5, 21, ['\tMy Second Line', ''])
editOp(2, 1, 3, 5, 14, 21, ['\tMy Second Line', ''])
);
});
@@ -386,11 +388,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'{"x" : 1}'
],
[
editOp(1, 2, 1, 2, 0, ['\n ']),
editOp(1, 5, 1, 6, 0, ['']),
editOp(1, 9, 1, 9, 0, ['\n'])
editOp(1, 2, 1, 2, 1, 0, ['\n ']),
editOp(1, 5, 1, 6, 4, 1, ['']),
editOp(1, 9, 1, 9, 8, 0, ['\n'])
],
editOp(1, 2, 1, 9, 7, [
editOp(1, 2, 1, 9, 1, 7, [
'',
' "x": 1',
''
@@ -406,11 +408,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'}'
],
[
editOp(1, 2, 2, 3, 0, ['']),
editOp(2, 6, 2, 6, 0, [' ']),
editOp(2, 9, 3, 1, 0, [''])
editOp(1, 2, 2, 3, 1, 3, ['']),
editOp(2, 6, 2, 6, 7, 0, [' ']),
editOp(2, 9, 3, 1, 10, 1, [''])
],
editOp(1, 2, 3, 1, 10, ['"x" : 1'])
editOp(1, 2, 3, 1, 1, 10, ['"x" : 1'])
);
});
@@ -424,10 +426,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'}'
],
[
editOp(1, 2, 2, 1, 0, ['', '\t']),
editOp(2, 11, 4, 1, 0, ['', '\t'])
editOp(1, 2, 2, 1, 1, 1, ['', '\t']),
editOp(2, 11, 4, 1, 12, 2, ['', '\t'])
],
editOp(1, 2, 4, 1, 13, [
editOp(1, 2, 4, 1, 1, 13, [
'',
'\t"a": true,',
'\t'
@@ -446,12 +448,12 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
'and the last line'
],
[
editOp(1, 5, 3, 1, 0, [' text', 'some more text', 'some more text']),
editOp(3, 2, 4, 1, 0, ['o more lines', 'asd', 'asd', 'asd']),
editOp(5, 1, 5, 6, 0, ['zzzzzzzz']),
editOp(5, 11, 6, 16, 0, ['1', '2', '3', '4'])
editOp(1, 5, 3, 1, 4, 21, [' text', 'some more text', 'some more text']),
editOp(3, 2, 4, 1, 26, 23, ['o more lines', 'asd', 'asd', 'asd']),
editOp(5, 1, 5, 6, 50, 5, ['zzzzzzzz']),
editOp(5, 11, 6, 16, 60, 22, ['1', '2', '3', '4'])
],
editOp(1, 5, 6, 16, 78, [
editOp(1, 5, 6, 16, 4, 78, [
' text',
'some more text',
'some more textno more lines',
@@ -475,17 +477,17 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
' ,"e": /*comment*/ [null] }',
],
[
editOp(1, 1, 1, 2, 0, ['']),
editOp(1, 3, 1, 10, 0, ['', ' ']),
editOp(1, 16, 2, 14, 0, ['', ' ']),
editOp(2, 18, 3, 9, 0, ['', ' ']),
editOp(3, 22, 4, 9, 0, ['']),
editOp(4, 10, 4, 10, 0, ['', ' ']),
editOp(4, 28, 4, 28, 0, ['', ' ']),
editOp(4, 32, 4, 32, 0, ['', ' ']),
editOp(4, 33, 4, 34, 0, ['', ''])
editOp(1, 1, 1, 2, 0, 1, ['']),
editOp(1, 3, 1, 10, 2, 7, ['', ' ']),
editOp(1, 16, 2, 14, 15, 14, ['', ' ']),
editOp(2, 18, 3, 9, 33, 9, ['', ' ']),
editOp(3, 22, 4, 9, 55, 9, ['']),
editOp(4, 10, 4, 10, 65, 0, ['', ' ']),
editOp(4, 28, 4, 28, 83, 0, ['', ' ']),
editOp(4, 32, 4, 32, 87, 0, ['', ' ']),
editOp(4, 33, 4, 34, 88, 1, ['', ''])
],
editOp(1, 1, 4, 34, 89, [
editOp(1, 1, 4, 34, 0, 89, [
'{',
' "d": [',
' null',
@@ -505,11 +507,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => {
' ,def'
],
[
editOp(1, 1, 1, 4, 0, ['']),
editOp(1, 7, 2, 2, 0, ['']),
editOp(2, 3, 2, 3, 0, ['', ''])
editOp(1, 1, 1, 4, 0, 3, ['']),
editOp(1, 7, 2, 2, 6, 2, ['']),
editOp(2, 3, 2, 3, 9, 0, ['', ''])
],
editOp(1, 1, 2, 3, 9, [
editOp(1, 1, 2, 3, 0, 9, [
'abc,',
''
])
@@ -1567,7 +1569,6 @@ suite('EditorModel - EditableTextModel.applyEdits', () => {
});
let assertMirrorModels = () => {
model._assertLineNumbersOK();
assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
};
@@ -1579,257 +1580,3 @@ suite('EditorModel - EditableTextModel.applyEdits', () => {
mirrorModel2.dispose();
});
});
interface ILightWeightMarker {
id: string;
lineNumber: number;
column: number;
stickToPreviousCharacter: boolean;
}
suite('EditorModel - EditableTextModel.applyEdits & markers', () => {
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation {
return {
identifier: null,
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
text: text.join('\n'),
forceMoveMarkers: false
};
}
function marker(id: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): ILightWeightMarker {
return {
id: id,
lineNumber: lineNumber,
column: column,
stickToPreviousCharacter: stickToPreviousCharacter
};
}
function toMarkersMap(markers: ILightWeightMarker[]): { [markerId: string]: ILightWeightMarker } {
var result: { [markerId: string]: ILightWeightMarker } = {};
markers.forEach(m => {
result[m.id] = m;
});
return result;
}
function testApplyEditsAndMarkers(text: string[], markers: ILightWeightMarker[], edits: IIdentifiedSingleEditOperation[], changedMarkers: string[], expectedText: string[], expectedMarkers: ILightWeightMarker[]): void {
var textStr = text.join('\n');
var expectedTextStr = expectedText.join('\n');
var markersMap = toMarkersMap(markers);
// var expectedMarkersMap = toMarkersMap(expectedMarkers);
var markerId2ModelMarkerId = Object.create(null);
var model = EditableTextModel.createFromString(textStr);
model.setEOL(EndOfLineSequence.LF);
// Add markers
markers.forEach((m) => {
let modelMarkerId = model._addMarker(0, m.lineNumber, m.column, m.stickToPreviousCharacter);
markerId2ModelMarkerId[m.id] = modelMarkerId;
});
// Apply edits & collect inverse edits
model.applyEdits(edits);
model._assertLineNumbersOK();
// Assert edits produced expected result
assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedTextStr);
let actualChangedMarkers: string[] = [];
for (let i = 0, len = expectedMarkers.length; i < len; i++) {
let expectedMarker = expectedMarkers[i];
let initialMarker = markersMap[expectedMarker.id];
let expectedMarkerModelMarkerId = markerId2ModelMarkerId[expectedMarker.id];
let actualMarker = model._getMarker(expectedMarkerModelMarkerId);
if (actualMarker.lineNumber !== initialMarker.lineNumber || actualMarker.column !== initialMarker.column) {
actualChangedMarkers.push(initialMarker.id);
}
assert.equal(actualMarker.lineNumber, expectedMarker.lineNumber, 'marker lineNumber of marker ' + expectedMarker.id);
assert.equal(actualMarker.column, expectedMarker.column, 'marker column of marker ' + expectedMarker.id);
}
changedMarkers.sort();
actualChangedMarkers.sort();
assert.deepEqual(actualChangedMarkers, changedMarkers, 'changed markers');
model.dispose();
}
test('no markers changed', () => {
testApplyEditsAndMarkers(
[
'Hello world,',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false)
],
[
editOp(1, 13, 1, 13, [' how are you?'])
],
[],
[
'Hello world, how are you?',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false)
]
);
});
test('first line changes', () => {
testApplyEditsAndMarkers(
[
'Hello world,',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false)
],
[
editOp(1, 7, 1, 12, ['friends'])
],
[],
[
'Hello friends,',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false)
]
);
});
test('inserting lines', () => {
testApplyEditsAndMarkers(
[
'Hello world,',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false)
],
[
editOp(1, 7, 1, 12, ['friends']),
editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,'])
],
['e', 'f', 'g', 'h'],
[
'Hello friends,',
'this is an inserted line',
'and another one. By the way,',
'this is a short text',
'that is used in testing'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 4, 1, false),
marker('f', 4, 16, true),
marker('g', 4, 21, true),
marker('h', 5, 24, false)
]
);
});
test('replacing a lot', () => {
testApplyEditsAndMarkers(
[
'Hello world,',
'this is a short text',
'that is used in testing',
'more lines...',
'more lines...',
'more lines...',
'more lines...'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 2, 1, false),
marker('f', 2, 16, true),
marker('g', 2, 21, true),
marker('h', 3, 24, false),
marker('i', 5, 1, false),
marker('j', 6, 1, false),
marker('k', 7, 14, false),
],
[
editOp(1, 7, 1, 12, ['friends']),
editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,', 'This is another line']),
editOp(2, 1, 7, 14, ['Some new text here'])
],
['e', 'f', 'g', 'h', 'i', 'j', 'k'],
[
'Hello friends,',
'this is an inserted line',
'and another one. By the way,',
'This is another line',
'Some new text here'
],
[
marker('a', 1, 1, true),
marker('b', 1, 1, false),
marker('c', 1, 7, false),
marker('d', 1, 12, true),
marker('e', 5, 1, false),
marker('f', 5, 16, true),
marker('g', 5, 19, true),
marker('h', 5, 19, false),
marker('i', 5, 19, false),
marker('j', 5, 19, false),
marker('k', 5, 19, false),
]
);
});
});

View File

@@ -105,7 +105,6 @@ export function assertSyncedModels(text: string, callback: (model: EditableTextM
var assertMirrorModels = () => {
assertLineMapping(model, 'model');
model._assertLineNumbersOK();
assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
};

View File

@@ -7,62 +7,76 @@
import * as assert from 'assert';
import { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/common/model/indentRanges';
import { computeRanges, MAX_FOLDING_REGIONS } from 'vs/editor/common/model/indentRanges';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
export interface IndentRange {
export interface ExpectedIndentRange {
startLineNumber: number;
endLineNumber: number;
indent: number;
parentIndex: number;
}
function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside: boolean, markers?: FoldingMarkers): void {
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model, offside, markers);
let actualRanges = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.getIndent(i), actual.getParentIndex(i));
}
assert.deepEqual(actualRanges, expected);
model.dispose();
}
function r(startLineNumber: number, endLineNumber: number, indent: number, parentIndex: number, marker = false): ExpectedIndentRange {
return { startLineNumber, endLineNumber, indent, parentIndex };
}
suite('Indentation Folding', () => {
function assertRanges(lines: string[], expected: IndentRange[]): void {
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model);
actual.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber);
assert.deepEqual(actual, expected);
model.dispose();
}
function r(startLineNumber: number, endLineNumber: number, indent: number): IndentRange {
return { startLineNumber, endLineNumber, indent };
}
test('Fold one level', () => {
assertRanges([
let range = [
'A',
' A',
' A',
' A'
], [r(1, 4, 0)]);
];
assertRanges(range, [r(1, 4, 0, -1)], true);
assertRanges(range, [r(1, 4, 0, -1)], false);
});
test('Fold two levels', () => {
assertRanges([
let range = [
'A',
' A',
' A',
' A',
' A'
], [r(1, 5, 0), r(3, 5, 2)]);
];
assertRanges(range, [r(1, 5, 0, -1), r(3, 5, 2, 0)], true);
assertRanges(range, [r(1, 5, 0, -1), r(3, 5, 2, 0)], false);
});
test('Fold three levels', () => {
assertRanges([
let range = [
'A',
' A',
' A',
' A',
'A'
], [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)]);
];
assertRanges(range, [r(1, 4, 0, -1), r(2, 4, 2, 0), r(3, 4, 4, 1)], true);
assertRanges(range, [r(1, 4, 0, -1), r(2, 4, 2, 0), r(3, 4, 4, 1)], false);
});
test('Fold decreasing indent', () => {
assertRanges([
let range = [
' A',
' A',
'A'
], []);
];
assertRanges(range, [], true);
assertRanges(range, [], false);
});
test('Fold Java', () => {
@@ -80,7 +94,7 @@ suite('Indentation Folding', () => {
/*11*/ 'interface B {',
/*12*/ ' void bar();',
/*13*/ '}',
], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)]);
], [r(1, 9, 0, -1), r(2, 4, 2, 0), r(7, 8, 2, 0), r(11, 12, 0, -1)], false);
});
test('Fold Javadoc', () => {
@@ -92,9 +106,9 @@ suite('Indentation Folding', () => {
/* 5*/ ' void foo() {',
/* 6*/ ' }',
/* 7*/ '}',
], [r(1, 3, 0), r(4, 6, 0)]);
], [r(1, 3, 0, -1), r(4, 6, 0, -1)], false);
});
test('Fold Whitespace', () => {
test('Fold Whitespace Java', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '',
@@ -104,7 +118,20 @@ suite('Indentation Folding', () => {
/* 6*/ ' }',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, 0), r(3, 5, 2)]);
], [r(1, 7, 0, -1), r(3, 5, 2, 0)], false);
});
test('Fold Whitespace Python', () => {
assertRanges([
/* 1*/ 'def a:',
/* 2*/ ' pass',
/* 3*/ ' ',
/* 4*/ ' def b:',
/* 5*/ ' pass',
/* 6*/ ' ',
/* 7*/ ' ',
/* 8*/ 'def c: # since there was a deintent here'
], [r(1, 5, 0, -1), r(4, 5, 2, 0)], true);
});
test('Fold Tabs', () => {
@@ -117,6 +144,245 @@ suite('Indentation Folding', () => {
/* 6*/ ' \t}',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, 0), r(3, 5, 4)]);
], [r(1, 7, 0, -1), r(3, 5, 4, 0)], false);
});
});
let markers: FoldingMarkers = {
start: /^\s*#region\b/,
end: /^\s*#endregion\b/
};
suite('Folding with regions', () => {
test('Inside region, indented', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' #region',
/* 3*/ ' void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ ' #endregion',
/* 8*/ '}',
], [r(1, 7, 0, -1), r(2, 7, 2, 0, true), r(3, 5, 2, 1)], false, markers);
});
test('Inside region, not indented', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ 'void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ '#endregion',
/* 8*/ '',
], [r(2, 7, 0, -1, true), r(3, 6, 0, 0)], false, markers);
});
test('Empty Regions', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ '#endregion',
/* 4*/ '#region',
/* 5*/ '',
/* 6*/ '#endregion',
/* 7*/ 'var y;',
], [r(2, 3, 0, -1, true), r(4, 6, 0, -1, true)], false, markers);
});
test('Nested Regions', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ '#region',
/* 4*/ '',
/* 5*/ '#endregion',
/* 6*/ '#endregion',
/* 7*/ 'var y;',
], [r(2, 6, 0, -1, true), r(3, 5, 0, 0, true)], false, markers);
});
test('Nested Regions 2', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' #region',
/* 3*/ '',
/* 4*/ ' #region',
/* 5*/ '',
/* 6*/ ' #endregion',
/* 7*/ ' // comment',
/* 8*/ ' #endregion',
/* 9*/ '}',
], [r(1, 8, 0, -1), r(2, 8, 2, 0, true), r(4, 6, 2, 1, true)], false, markers);
});
test('Incomplete Regions', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '#region',
/* 3*/ ' // comment',
/* 4*/ '}',
], [r(2, 3, 0, -1)], false, markers);
});
test('Incomplete Regions 2', () => {
assertRanges([
/* 1*/ '',
/* 2*/ '#region',
/* 3*/ '#region',
/* 4*/ '#region',
/* 5*/ ' // comment',
/* 6*/ '#endregion',
/* 7*/ '#endregion',
/* 8*/ ' // hello',
], [r(3, 7, 0, -1, true), r(4, 6, 0, 0, true)], false, markers);
});
test('Indented region before', () => {
assertRanges([
/* 1*/ 'if (x)',
/* 2*/ ' return;',
/* 3*/ '',
/* 4*/ '#region',
/* 5*/ ' // comment',
/* 6*/ '#endregion',
], [r(1, 3, 0, -1), r(4, 6, 0, -1, true)], false, markers);
});
test('Indented region before 2', () => {
assertRanges([
/* 1*/ 'if (x)',
/* 2*/ ' log();',
/* 3*/ '',
/* 4*/ ' #region',
/* 5*/ ' // comment',
/* 6*/ ' #endregion',
], [r(1, 6, 0, -1), r(2, 6, 2, 0), r(4, 6, 4, 1, true)], false, markers);
});
test('Indented region in-between', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' // comment',
/* 3*/ ' if (x)',
/* 4*/ ' return;',
/* 5*/ '',
/* 6*/ '#endregion',
], [r(1, 6, 0, -1, true), r(3, 5, 2, 0)], false, markers);
});
test('Indented region after', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' // comment',
/* 3*/ '',
/* 4*/ '#endregion',
/* 5*/ ' if (x)',
/* 6*/ ' return;',
], [r(1, 4, 0, -1, true), r(5, 6, 2, -1)], false, markers);
});
test('With off-side', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' ',
/* 3*/ '',
/* 4*/ '#endregion',
/* 5*/ '',
], [r(1, 4, 0, -1, true)], true, markers);
});
test('Nested with off-side', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' ',
/* 3*/ '#region',
/* 4*/ '',
/* 5*/ '#endregion',
/* 6*/ '',
/* 7*/ '#endregion',
/* 8*/ '',
], [r(1, 7, 0, -1, true), r(3, 5, 0, 0, true)], true, markers);
});
test('Issue 35981', () => {
assertRanges([
/* 1*/ 'function thisFoldsToEndOfPage() {',
/* 2*/ ' const variable = []',
/* 3*/ ' // #region',
/* 4*/ ' .reduce((a, b) => a,[]);',
/* 5*/ '}',
/* 6*/ '',
/* 7*/ 'function thisFoldsProperly() {',
/* 8*/ ' const foo = "bar"',
/* 9*/ '}',
], [r(1, 4, 0, -1), r(2, 4, 2, 0), r(7, 8, 0, -1)], false, markers);
});
test('Misspelled Markers', () => {
assertRanges([
/* 1*/ '#Region',
/* 2*/ '#endregion',
/* 3*/ '#regionsandmore',
/* 4*/ '#endregion',
/* 5*/ '#region',
/* 6*/ '#end region',
/* 7*/ '#region',
/* 8*/ '#endregionff',
], [], true, markers);
});
test('test max folding regions', () => {
let lines = [];
let nRegions = MAX_FOLDING_REGIONS;
for (let i = 0; i < nRegions; i++) {
lines.push('#region');
}
for (let i = 0; i < nRegions; i++) {
lines.push('#endregion');
}
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
assert.equal(actual.length, nRegions, 'len');
for (let i = 0; i < nRegions; i++) {
assert.equal(actual.getStartLineNumber(i), i + 1, 'start' + i);
assert.equal(actual.getEndLineNumber(i), nRegions * 2 - i, 'end' + i);
assert.equal(actual.getParentIndex(i), i - 1, 'parent' + i);
}
});
test('findRange', () => {
let lines = [
/* 1*/ '#region',
/* 2*/ '#endregion',
/* 3*/ 'class A {',
/* 4*/ ' void foo() {',
/* 5*/ ' if (true) {',
/* 6*/ ' return;',
/* 7*/ ' }',
/* 8*/ '',
/* 9*/ ' if (true) {',
/* 10*/ ' return;',
/* 11*/ ' }',
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let actual = computeRanges(textModel, false, markers);
// let r0 = r(1, 2);
// let r1 = r(3, 12);
// let r2 = r(4, 11);
// let r3 = r(5, 6);
// let r4 = r(9, 10);
assert.equal(actual.findRange(1), 0, '1');
assert.equal(actual.findRange(2), 0, '2');
assert.equal(actual.findRange(3), 1, '3');
assert.equal(actual.findRange(4), 2, '4');
assert.equal(actual.findRange(5), 3, '5');
assert.equal(actual.findRange(6), 3, '6');
assert.equal(actual.findRange(7), 2, '7');
assert.equal(actual.findRange(8), 2, '8');
assert.equal(actual.findRange(9), 4, '9');
assert.equal(actual.findRange(10), 4, '10');
assert.equal(actual.findRange(11), 2, '11');
assert.equal(actual.findRange(12), 1, '12');
assert.equal(actual.findRange(13), -1, '13');
} finally {
textModel.dispose();
}
});
});

View File

@@ -0,0 +1,555 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { IntervalTree, IntervalNode } from 'vs/editor/common/model/intervalTree';
const GENERATE_TESTS = false;
let TEST_COUNT = GENERATE_TESTS ? 10000 : 0;
let PRINT_TREE = false;
const MIN_INTERVAL_START = 1;
const MAX_INTERVAL_END = 100;
const MIN_INSERTS = 1;
const MAX_INSERTS = 30;
const MIN_CHANGE_CNT = 10;
const MAX_CHANGE_CNT = 20;
suite('IntervalTree', () => {
class Interval {
_intervalBrand: void;
public start: number;
public end: number;
constructor(start: number, end: number) {
this.start = start;
this.end = end;
}
}
class Oracle {
public intervals: Interval[];
constructor() {
this.intervals = [];
}
public insert(interval: Interval): Interval {
this.intervals.push(interval);
this.intervals.sort((a, b) => {
if (a.start === b.start) {
return a.end - b.end;
}
return a.start - b.start;
});
return interval;
}
public delete(interval: Interval): void {
for (let i = 0, len = this.intervals.length; i < len; i++) {
if (this.intervals[i] === interval) {
this.intervals.splice(i, 1);
return;
}
}
}
public search(interval: Interval): Interval[] {
let result: Interval[] = [];
for (let i = 0, len = this.intervals.length; i < len; i++) {
let int = this.intervals[i];
if (int.start <= interval.end && int.end >= interval.start) {
result.push(int);
}
}
return result;
}
}
class TestState {
private _oracle: Oracle = new Oracle();
private _tree: IntervalTree = new IntervalTree();
private _lastNodeId = -1;
private _treeNodes: IntervalNode[] = [];
private _oracleNodes: Interval[] = [];
public acceptOp(op: IOperation): void {
if (op.type === 'insert') {
if (PRINT_TREE) {
console.log(`insert: {${JSON.stringify(new Interval(op.begin, op.end))}}`);
}
let nodeId = (++this._lastNodeId);
this._treeNodes[nodeId] = new IntervalNode(null, op.begin, op.end);
this._tree.insert(this._treeNodes[nodeId]);
this._oracleNodes[nodeId] = this._oracle.insert(new Interval(op.begin, op.end));
} else if (op.type === 'delete') {
if (PRINT_TREE) {
console.log(`delete: {${JSON.stringify(this._oracleNodes[op.id])}}`);
}
this._tree.delete(this._treeNodes[op.id]);
this._oracle.delete(this._oracleNodes[op.id]);
this._treeNodes[op.id] = null;
this._oracleNodes[op.id] = null;
} else if (op.type === 'change') {
this._tree.delete(this._treeNodes[op.id]);
this._treeNodes[op.id].reset(0, op.begin, op.end, null);
this._tree.insert(this._treeNodes[op.id]);
this._oracle.delete(this._oracleNodes[op.id]);
this._oracleNodes[op.id].start = op.begin;
this._oracleNodes[op.id].end = op.end;
this._oracle.insert(this._oracleNodes[op.id]);
} else {
let actualNodes = this._tree.intervalSearch(op.begin, op.end, 0, false, 0);
let actual = actualNodes.map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd));
let expected = this._oracle.search(new Interval(op.begin, op.end));
assert.deepEqual(actual, expected);
return;
}
if (PRINT_TREE) {
this._tree.print();
}
this._tree.assertInvariants();
let actual = this._tree.getAllInOrder().map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd));
let expected = this._oracle.intervals;
assert.deepEqual(actual, expected);
}
public getExistingNodeId(index: number): number {
let currIndex = -1;
for (let i = 0; i < this._treeNodes.length; i++) {
if (this._treeNodes[i] === null) {
continue;
}
currIndex++;
if (currIndex === index) {
return i;
}
}
throw new Error('unexpected');
}
}
interface IInsertOperation {
type: 'insert';
begin: number;
end: number;
}
interface IDeleteOperation {
type: 'delete';
id: number;
}
interface IChangeOperation {
type: 'change';
id: number;
begin: number;
end: number;
}
interface ISearchOperation {
type: 'search';
begin: number;
end: number;
}
type IOperation = IInsertOperation | IDeleteOperation | IChangeOperation | ISearchOperation;
function testIntervalTree(ops: IOperation[]): void {
let state = new TestState();
for (let i = 0; i < ops.length; i++) {
state.acceptOp(ops[i]);
}
}
function getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomRange(min: number, max: number): [number, number] {
let begin = getRandomInt(min, max);
let length: number;
if (getRandomInt(1, 10) <= 2) {
// large range
length = getRandomInt(0, max - begin);
} else {
// small range
length = getRandomInt(0, Math.min(max - begin, 10));
}
return [begin, begin + length];
}
class AutoTest {
private _ops: IOperation[] = [];
private _state: TestState = new TestState();
private _insertCnt: number;
private _deleteCnt: number;
private _changeCnt: number;
constructor() {
this._insertCnt = getRandomInt(MIN_INSERTS, MAX_INSERTS);
this._changeCnt = getRandomInt(MIN_CHANGE_CNT, MAX_CHANGE_CNT);
this._deleteCnt = 0;
}
private _doRandomInsert(): void {
let range = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END);
this._run({
type: 'insert',
begin: range[0],
end: range[1]
});
}
private _doRandomDelete(): void {
let idx = getRandomInt(Math.floor(this._deleteCnt / 2), this._deleteCnt - 1);
this._run({
type: 'delete',
id: this._state.getExistingNodeId(idx)
});
}
private _doRandomChange(): void {
let idx = getRandomInt(0, this._deleteCnt - 1);
let range = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END);
this._run({
type: 'change',
id: this._state.getExistingNodeId(idx),
begin: range[0],
end: range[1]
});
}
public run() {
while (this._insertCnt > 0 || this._deleteCnt > 0 || this._changeCnt > 0) {
if (this._insertCnt > 0) {
this._doRandomInsert();
this._insertCnt--;
this._deleteCnt++;
} else if (this._changeCnt > 0) {
this._doRandomChange();
this._changeCnt--;
} else {
this._doRandomDelete();
this._deleteCnt--;
}
// Let's also search for something...
let searchRange = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END);
this._run({
type: 'search',
begin: searchRange[0],
end: searchRange[1]
});
}
}
private _run(op: IOperation): void {
this._ops.push(op);
this._state.acceptOp(op);
}
public print(): void {
console.log(`testIntervalTree(${JSON.stringify(this._ops)})`);
}
}
suite('generated', () => {
test('gen01', () => {
testIntervalTree([
{ type: 'insert', begin: 28, end: 35 },
{ type: 'insert', begin: 52, end: 54 },
{ type: 'insert', begin: 63, end: 69 }
]);
});
test('gen02', () => {
testIntervalTree([
{ type: 'insert', begin: 80, end: 89 },
{ type: 'insert', begin: 92, end: 100 },
{ type: 'insert', begin: 99, end: 99 }
]);
});
test('gen03', () => {
testIntervalTree([
{ type: 'insert', begin: 89, end: 96 },
{ type: 'insert', begin: 71, end: 74 },
{ type: 'delete', id: 1 }
]);
});
test('gen04', () => {
testIntervalTree([
{ type: 'insert', begin: 44, end: 46 },
{ type: 'insert', begin: 85, end: 88 },
{ type: 'delete', id: 0 }
]);
});
test('gen05', () => {
testIntervalTree([
{ type: 'insert', begin: 82, end: 90 },
{ type: 'insert', begin: 69, end: 73 },
{ type: 'delete', id: 0 },
{ type: 'delete', id: 1 }
]);
});
test('gen06', () => {
testIntervalTree([
{ type: 'insert', begin: 41, end: 63 },
{ type: 'insert', begin: 98, end: 98 },
{ type: 'insert', begin: 47, end: 51 },
{ type: 'delete', id: 2 }
]);
});
test('gen07', () => {
testIntervalTree([
{ type: 'insert', begin: 24, end: 26 },
{ type: 'insert', begin: 11, end: 28 },
{ type: 'insert', begin: 27, end: 30 },
{ type: 'insert', begin: 80, end: 85 },
{ type: 'delete', id: 1 }
]);
});
test('gen08', () => {
testIntervalTree([
{ type: 'insert', begin: 100, end: 100 },
{ type: 'insert', begin: 100, end: 100 }
]);
});
test('gen09', () => {
testIntervalTree([
{ type: 'insert', begin: 58, end: 65 },
{ type: 'insert', begin: 82, end: 96 },
{ type: 'insert', begin: 58, end: 65 }
]);
});
test('gen10', () => {
testIntervalTree([
{ type: 'insert', begin: 32, end: 40 },
{ type: 'insert', begin: 25, end: 29 },
{ type: 'insert', begin: 24, end: 32 }
]);
});
test('gen11', () => {
testIntervalTree([
{ type: 'insert', begin: 25, end: 70 },
{ type: 'insert', begin: 99, end: 100 },
{ type: 'insert', begin: 46, end: 51 },
{ type: 'insert', begin: 57, end: 57 },
{ type: 'delete', id: 2 }
]);
});
test('gen12', () => {
testIntervalTree([
{ type: 'insert', begin: 20, end: 26 },
{ type: 'insert', begin: 10, end: 18 },
{ type: 'insert', begin: 99, end: 99 },
{ type: 'insert', begin: 37, end: 59 },
{ type: 'delete', id: 2 }
]);
});
test('gen13', () => {
testIntervalTree([
{ type: 'insert', begin: 3, end: 91 },
{ type: 'insert', begin: 57, end: 57 },
{ type: 'insert', begin: 35, end: 44 },
{ type: 'insert', begin: 72, end: 81 },
{ type: 'delete', id: 2 }
]);
});
test('gen14', () => {
testIntervalTree([
{ type: 'insert', begin: 58, end: 61 },
{ type: 'insert', begin: 34, end: 35 },
{ type: 'insert', begin: 56, end: 62 },
{ type: 'insert', begin: 69, end: 78 },
{ type: 'delete', id: 0 }
]);
});
test('gen15', () => {
testIntervalTree([
{ type: 'insert', begin: 63, end: 69 },
{ type: 'insert', begin: 17, end: 24 },
{ type: 'insert', begin: 3, end: 13 },
{ type: 'insert', begin: 84, end: 94 },
{ type: 'insert', begin: 18, end: 23 },
{ type: 'insert', begin: 96, end: 98 },
{ type: 'delete', id: 1 }
]);
});
test('gen16', () => {
testIntervalTree([
{ type: 'insert', begin: 27, end: 27 },
{ type: 'insert', begin: 42, end: 87 },
{ type: 'insert', begin: 42, end: 49 },
{ type: 'insert', begin: 69, end: 71 },
{ type: 'insert', begin: 20, end: 27 },
{ type: 'insert', begin: 8, end: 9 },
{ type: 'insert', begin: 42, end: 49 },
{ type: 'delete', id: 1 }
]);
});
test('gen17', () => {
testIntervalTree([
{ type: 'insert', begin: 21, end: 23 },
{ type: 'insert', begin: 83, end: 87 },
{ type: 'insert', begin: 56, end: 58 },
{ type: 'insert', begin: 1, end: 55 },
{ type: 'insert', begin: 56, end: 59 },
{ type: 'insert', begin: 58, end: 60 },
{ type: 'insert', begin: 56, end: 65 },
{ type: 'delete', id: 1 },
{ type: 'delete', id: 0 },
{ type: 'delete', id: 6 }
]);
});
test('gen18', () => {
testIntervalTree([
{ type: 'insert', begin: 25, end: 25 },
{ type: 'insert', begin: 67, end: 79 },
{ type: 'delete', id: 0 },
{ type: 'search', begin: 65, end: 75 }
]);
});
test('force delta overflow', () => {
// Search the IntervalNode ctor for FORCE_OVERFLOWING_TEST
// to force that this test leads to a delta normalization
testIntervalTree([
{ type: 'insert', begin: 686081138593427, end: 733009856502260 },
{ type: 'insert', begin: 591031326181669, end: 591031326181672 },
{ type: 'insert', begin: 940037682731896, end: 940037682731903 },
{ type: 'insert', begin: 598413641151120, end: 598413641151128 },
{ type: 'insert', begin: 800564156553344, end: 800564156553351 },
{ type: 'insert', begin: 894198957565481, end: 894198957565491 }
]);
});
});
// TEST_COUNT = 0;
// PRINT_TREE = true;
for (let i = 0; i < TEST_COUNT; i++) {
if (i % 100 === 0) {
console.log(`TEST ${i + 1}/${TEST_COUNT}`);
}
let test = new AutoTest();
try {
test.run();
} catch (err) {
console.log(err);
test.print();
return;
}
}
suite('searching', () => {
function createCormenTree(): IntervalTree {
let r = new IntervalTree();
let data: [number, number][] = [
[16, 21],
[8, 9],
[25, 30],
[5, 8],
[15, 23],
[17, 19],
[26, 26],
[0, 3],
[6, 10],
[19, 20]
];
data.forEach((int) => {
let node = new IntervalNode(null, int[0], int[1]);
r.insert(node);
});
return r;
}
const T = createCormenTree();
function assertIntervalSearch(start: number, end: number, expected: [number, number][]): void {
let actualNodes = T.intervalSearch(start, end, 0, false, 0);
let actual = actualNodes.map((n) => <[number, number]>[n.cachedAbsoluteStart, n.cachedAbsoluteEnd]);
assert.deepEqual(actual, expected);
}
test('cormen 1->2', () => {
assertIntervalSearch(
1, 2,
[
[0, 3],
]
);
});
test('cormen 4->8', () => {
assertIntervalSearch(
4, 8,
[
[5, 8],
[6, 10],
[8, 9],
]
);
});
test('cormen 10->15', () => {
assertIntervalSearch(
10, 15,
[
[6, 10],
[15, 23],
]
);
});
test('cormen 21->25', () => {
assertIntervalSearch(
21, 25,
[
[15, 23],
[16, 21],
[25, 30],
]
);
});
test('cormen 24->24', () => {
assertIntervalSearch(
24, 24,
[
]
);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ import * as assert from 'assert';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration, TrackedRangeStickiness, EndOfLineSequence } from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
// --------- utils
@@ -27,7 +27,8 @@ function modelHasDecorations(model: Model, decorations: ILightWeightDecoration2[
className: actualDecorations[i].options.className
});
}
assert.deepEqual(modelDecorations, decorations, 'Model decorations');
modelDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
assert.deepEqual(modelDecorations, decorations);
}
function modelHasDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string) {
@@ -168,13 +169,13 @@ suite('Editor Model - Model Decorations', () => {
var decId1 = addDecoration(thisModel, 1, 2, 3, 2, 'myType1');
var decId2 = addDecoration(thisModel, 1, 2, 3, 1, 'myType2');
modelHasDecorations(thisModel, [
{
range: new Range(1, 2, 3, 2),
className: 'myType1'
},
{
range: new Range(1, 2, 3, 1),
className: 'myType2'
},
{
range: new Range(1, 2, 3, 2),
className: 'myType1'
}
]);
thisModel.changeDecorations((changeAccessor) => {
@@ -207,9 +208,6 @@ suite('Editor Model - Model Decorations', () => {
let listenerCalled = 0;
thisModel.onDidChangeDecorations((e) => {
listenerCalled++;
assert.equal(e.addedDecorations.length, 1);
assert.equal(e.changedDecorations.length, 0);
assert.equal(e.removedDecorations.length, 0);
});
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
assert.equal(listenerCalled, 1, 'listener called');
@@ -220,10 +218,6 @@ suite('Editor Model - Model Decorations', () => {
let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.onDidChangeDecorations((e) => {
listenerCalled++;
assert.equal(e.addedDecorations.length, 0);
assert.equal(e.changedDecorations.length, 1);
assert.equal(e.changedDecorations[0], decId);
assert.equal(e.removedDecorations.length, 0);
});
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.changeDecoration(decId, new Range(1, 1, 1, 2));
@@ -236,10 +230,6 @@ suite('Editor Model - Model Decorations', () => {
let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.onDidChangeDecorations((e) => {
listenerCalled++;
assert.equal(e.addedDecorations.length, 0);
assert.equal(e.changedDecorations.length, 0);
assert.equal(e.removedDecorations.length, 1);
assert.equal(e.removedDecorations[0], decId);
});
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.removeDecoration(decId);
@@ -249,20 +239,31 @@ suite('Editor Model - Model Decorations', () => {
test('decorations emit event when inserting one line text before it', () => {
let listenerCalled = 0;
let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType');
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.onDidChangeDecorations((e) => {
listenerCalled++;
assert.equal(e.addedDecorations.length, 0);
assert.equal(e.changedDecorations.length, 1);
assert.equal(e.changedDecorations[0], decId);
assert.equal(e.removedDecorations.length, 0);
});
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]);
assert.equal(listenerCalled, 1, 'listener called');
});
test('decorations do not emit event on no-op deltaDecorations', () => {
let listenerCalled = 0;
thisModel.onDidChangeDecorations((e) => {
listenerCalled++;
});
thisModel.deltaDecorations([], []);
thisModel.changeDecorations((accessor) => {
accessor.deltaDecorations([], []);
});
assert.equal(listenerCalled, 0, 'listener not called');
});
// --------- editing text & effects on decorations
test('decorations are updated when inserting one line text before it', () => {
@@ -365,6 +366,740 @@ suite('Editor Model - Model Decorations', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]);
modelHasDecoration(thisModel, 1, 1, 2, 1, 'myType');
});
test('decorations are updated when changing EOL', () => {
addDecoration(thisModel, 1, 2, 4, 1, 'myType1');
addDecoration(thisModel, 1, 3, 4, 1, 'myType2');
addDecoration(thisModel, 1, 4, 4, 1, 'myType3');
addDecoration(thisModel, 1, 5, 4, 1, 'myType4');
addDecoration(thisModel, 1, 6, 4, 1, 'myType5');
addDecoration(thisModel, 1, 7, 4, 1, 'myType6');
addDecoration(thisModel, 1, 8, 4, 1, 'myType7');
addDecoration(thisModel, 1, 9, 4, 1, 'myType8');
addDecoration(thisModel, 1, 10, 4, 1, 'myType9');
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'x')]);
thisModel.setEOL(EndOfLineSequence.CRLF);
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'x')]);
modelHasDecorations(thisModel, [
{ range: new Range(1, 4, 4, 1), className: 'myType1' },
{ range: new Range(1, 5, 4, 1), className: 'myType2' },
{ range: new Range(1, 6, 4, 1), className: 'myType3' },
{ range: new Range(1, 7, 4, 1), className: 'myType4' },
{ range: new Range(1, 8, 4, 1), className: 'myType5' },
{ range: new Range(1, 9, 4, 1), className: 'myType6' },
{ range: new Range(1, 10, 4, 1), className: 'myType7' },
{ range: new Range(1, 11, 4, 1), className: 'myType8' },
{ range: new Range(1, 12, 4, 1), className: 'myType9' },
]);
});
test('an apparently simple edit', () => {
addDecoration(thisModel, 1, 2, 4, 1, 'myType1');
thisModel.applyEdits([EditOperation.replace(new Range(1, 14, 2, 1), 'x')]);
modelHasDecorations(thisModel, [
{ range: new Range(1, 2, 3, 1), className: 'myType1' },
]);
});
test('removeAllDecorationsWithOwnerId can be called after model dispose', () => {
let model = Model.createFromString('asd');
model.dispose();
model.removeAllDecorationsWithOwnerId(1);
});
test('removeAllDecorationsWithOwnerId works', () => {
thisModel.deltaDecorations([], [{ range: new Range(1, 2, 4, 1), options: { className: 'myType1' } }], 1);
thisModel.removeAllDecorationsWithOwnerId(1);
modelHasNoDecorations(thisModel);
});
});
suite('Decorations and editing', () => {
function _runTest(decRange: Range, stickiness: TrackedRangeStickiness, editRange: Range, editText: string, editForceMoveMarkers: boolean, expectedDecRange: Range, msg: string): void {
let model = Model.createFromString([
'My First Line',
'My Second Line',
'Third Line'
].join('\n'));
const id = model.deltaDecorations([], [{ range: decRange, options: { stickiness: stickiness } }])[0];
model.applyEdits([{ range: editRange, text: editText, forceMoveMarkers: editForceMoveMarkers, identifier: null }]);
const actual = model.getDecorationRange(id);
assert.deepEqual(actual, expectedDecRange, msg);
model.dispose();
}
function runTest(decRange: Range, editRange: Range, editText: string, expectedDecRange: Range[][]): void {
_runTest(decRange, 0, editRange, editText, false, expectedDecRange[0][0], 'no-0-AlwaysGrowsWhenTypingAtEdges');
_runTest(decRange, 1, editRange, editText, false, expectedDecRange[0][1], 'no-1-NeverGrowsWhenTypingAtEdges');
_runTest(decRange, 2, editRange, editText, false, expectedDecRange[0][2], 'no-2-GrowsOnlyWhenTypingBefore');
_runTest(decRange, 3, editRange, editText, false, expectedDecRange[0][3], 'no-3-GrowsOnlyWhenTypingAfter');
_runTest(decRange, 0, editRange, editText, true, expectedDecRange[1][0], 'force-0-AlwaysGrowsWhenTypingAtEdges');
_runTest(decRange, 1, editRange, editText, true, expectedDecRange[1][1], 'force-1-NeverGrowsWhenTypingAtEdges');
_runTest(decRange, 2, editRange, editText, true, expectedDecRange[1][2], 'force-2-GrowsOnlyWhenTypingBefore');
_runTest(decRange, 3, editRange, editText, true, expectedDecRange[1][3], 'force-3-GrowsOnlyWhenTypingAfter');
}
suite('insert', () => {
suite('collapsed dec', () => {
test('before', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 3, 1, 3), 'xx',
[
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
]
);
});
test('equal', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 4), 'xx',
[
[new Range(1, 4, 1, 6), new Range(1, 6, 1, 6), new Range(1, 4, 1, 4), new Range(1, 6, 1, 6)],
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
]
);
});
test('after', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 5), 'xx',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
});
suite('non-collapsed dec', () => {
test('before', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 3), 'xx',
[
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
]
);
});
test('start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 4), 'xx',
[
[new Range(1, 4, 1, 11), new Range(1, 6, 1, 11), new Range(1, 4, 1, 11), new Range(1, 6, 1, 11)],
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
]
);
});
test('inside', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 5), 'xx',
[
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
]
);
});
test('end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 9), 'xx',
[
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 11)],
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
]
);
});
test('after', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 10), 'xx',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
});
});
suite('delete', () => {
suite('collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 1, 1, 3), '',
[
[new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)],
[new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), '',
[
[new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)],
[new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 3, 1, 5), '',
[
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), '',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), '',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
});
suite('non-collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 1, 1, 3), '',
[
[new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)],
[new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), '',
[
[new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)],
[new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), '',
[
[new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7)],
[new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), '',
[
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 10), '',
[
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
]
);
});
test('edit.start == range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 6), '',
[
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), '',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start == range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 10), '',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 7), '',
[
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 9), '',
[
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 10), '',
[
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), '',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), '',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
});
});
suite('replace short', () => {
suite('collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 1, 1, 3), 'c',
[
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), 'c',
[
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
[new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 3, 1, 5), 'c',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), 'c',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), 'c',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
});
suite('non-collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 1, 1, 3), 'c',
[
[new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)],
[new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), 'c',
[
[new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)],
[new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), 'c',
[
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), 'c',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 10), 'c',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
test('edit.start == range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 6), 'c',
[
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
[new Range(1, 5, 1, 8), new Range(1, 5, 1, 8), new Range(1, 5, 1, 8), new Range(1, 5, 1, 8)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), 'c',
[
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
[new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)],
]
);
});
test('edit.start == range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 10), 'c',
[
[new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)],
[new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 7), 'c',
[
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 9), 'c',
[
[new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)],
[new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 10), 'c',
[
[new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)],
[new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), 'c',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 10), new Range(1, 4, 1, 10), new Range(1, 4, 1, 10), new Range(1, 4, 1, 10)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), 'c',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
});
});
suite('replace long', () => {
suite('collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 1, 1, 3), 'cccc',
[
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), 'cccc',
[
[new Range(1, 4, 1, 6), new Range(1, 6, 1, 6), new Range(1, 4, 1, 4), new Range(1, 6, 1, 6)],
[new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 3, 1, 5), 'cccc',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), 'cccc',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), 'cccc',
[
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
[new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)],
]
);
});
});
suite('non-collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 1, 1, 3), 'cccc',
[
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), 'cccc',
[
[new Range(1, 4, 1, 11), new Range(1, 6, 1, 11), new Range(1, 4, 1, 11), new Range(1, 6, 1, 11)],
[new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), 'cccc',
[
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
[new Range(1, 7, 1, 11), new Range(1, 7, 1, 11), new Range(1, 7, 1, 11), new Range(1, 7, 1, 11)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), 'cccc',
[
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
[new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)],
]
);
});
test('edit.start < range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 10), 'cccc',
[
[new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)],
[new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)],
]
);
});
test('edit.start == range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 6), 'cccc',
[
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
[new Range(1, 8, 1, 11), new Range(1, 8, 1, 11), new Range(1, 8, 1, 11), new Range(1, 8, 1, 11)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), 'cccc',
[
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
[new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)],
]
);
});
test('edit.start == range.start && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 10), 'cccc',
[
[new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)],
[new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 7), 'cccc',
[
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
[new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 9), 'cccc',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 10), 'cccc',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), 'cccc',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 13), new Range(1, 4, 1, 13), new Range(1, 4, 1, 13), new Range(1, 4, 1, 13)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), 'cccc',
[
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
[new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)],
]
);
});
});
});
});
interface ILightWeightDecoration {
@@ -420,7 +1155,6 @@ suite('deltaDecorations', () => {
assert.equal(initialIds.length, decorations.length, 'returns expected cnt of ids');
assert.equal(initialIds.length, model.getAllDecorations().length, 'does not leak decorations');
assert.equal(initialIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges');
assert.equal(2 * initialIds.length, model._getMarkersCount(), 'does not leak markers');
actualDecorations.sort((a, b) => strcmp(a.id, b.id));
decorations.sort((a, b) => strcmp(a.id, b.id));
assert.deepEqual(actualDecorations, decorations);
@@ -431,7 +1165,6 @@ suite('deltaDecorations', () => {
assert.equal(newIds.length, newDecorations.length, 'returns expected cnt of ids');
assert.equal(newIds.length, model.getAllDecorations().length, 'does not leak decorations');
assert.equal(newIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges');
assert.equal(2 * newIds.length, model._getMarkersCount(), 'does not leak markers');
actualNewDecorations.sort((a, b) => strcmp(a.id, b.id));
newDecorations.sort((a, b) => strcmp(a.id, b.id));
assert.deepEqual(actualDecorations, decorations);

View File

@@ -820,178 +820,4 @@ suite('TextModel.mightContainRTL', () => {
assert.equal(model.mightContainRTL(), false);
});
});
suite('TextModel.getLineIndentGuide', () => {
function assertIndentGuides(lines: [number, string][]): void {
let text = lines.map(l => l[1]).join('\n');
let model = TextModel.createFromString(text);
let actual: [number, string][] = [];
for (let line = 1; line <= model.getLineCount(); line++) {
actual[line - 1] = [model.getLineIndentGuide(line), model.getLineContent(line)];
}
// let expected = lines.map(l => l[0]);
assert.deepEqual(actual, lines);
model.dispose();
}
test('getLineIndentGuide one level', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
]);
});
test('getLineIndentGuide two levels', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
]);
});
test('getLineIndentGuide three levels', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[2, ' A'],
[0, 'A'],
]);
});
test('getLineIndentGuide decreasing indent', () => {
assertIndentGuides([
[0, ' A'],
[0, ' A'],
[0, 'A'],
]);
});
test('getLineIndentGuide Java', () => {
assertIndentGuides([
/* 1*/[0, 'class A {'],
/* 2*/[1, ' void foo() {'],
/* 3*/[1, ' console.log(1);'],
/* 4*/[1, ' console.log(2);'],
/* 5*/[1, ' }'],
/* 6*/[1, ''],
/* 7*/[1, ' void bar() {'],
/* 8*/[1, ' console.log(3);'],
/* 9*/[1, ' }'],
/*10*/[0, '}'],
/*11*/[0, 'interface B {'],
/*12*/[1, ' void bar();'],
/*13*/[0, '}'],
]);
});
test('getLineIndentGuide Javadoc', () => {
assertIndentGuides([
[0, '/**'],
[1, ' * Comment'],
[1, ' */'],
[0, 'class A {'],
[1, ' void foo() {'],
[1, ' }'],
[0, '}'],
]);
});
test('getLineIndentGuide Whitespace', () => {
assertIndentGuides([
[0, 'class A {'],
[1, ''],
[1, ' void foo() {'],
[1, ' '],
[1, ' return 1;'],
[1, ' }'],
[1, ' '],
[0, '}'],
]);
});
test('getLineIndentGuide Tabs', () => {
assertIndentGuides([
[0, 'class A {'],
[1, '\t\t'],
[1, '\tvoid foo() {'],
[2, '\t \t//hello'],
[2, '\t return 2;'],
[1, ' \t}'],
[1, ' '],
[0, '}'],
]);
});
test('getLineIndentGuide checker.ts', () => {
assertIndentGuides([
/* 1*/[0, '/// <reference path="binder.ts"/>'],
/* 2*/[0, ''],
/* 3*/[0, '/* @internal */'],
/* 4*/[0, 'namespace ts {'],
/* 5*/[1, ' let nextSymbolId = 1;'],
/* 6*/[1, ' let nextNodeId = 1;'],
/* 7*/[1, ' let nextMergeId = 1;'],
/* 8*/[1, ' let nextFlowId = 1;'],
/* 9*/[1, ''],
/*10*/[1, ' export function getNodeId(node: Node): number {'],
/*11*/[2, ' if (!node.id) {'],
/*12*/[3, ' node.id = nextNodeId;'],
/*13*/[3, ' nextNodeId++;'],
/*14*/[2, ' }'],
/*15*/[2, ' return node.id;'],
/*16*/[1, ' }'],
/*17*/[0, '}'],
]);
});
test('issue #8425 - Missing indentation lines for first level indentation', () => {
assertIndentGuides([
[1, '\tindent1'],
[2, '\t\tindent2'],
[2, '\t\tindent2'],
[1, '\tindent1'],
]);
});
test('issue #8952 - Indentation guide lines going through text on .yml file', () => {
assertIndentGuides([
[0, 'properties:'],
[1, ' emailAddress:'],
[2, ' - bla'],
[2, ' - length:'],
[3, ' max: 255'],
[0, 'getters:'],
]);
});
test('issue #11892 - Indent guides look funny', () => {
assertIndentGuides([
[0, 'function test(base) {'],
[1, '\tswitch (base) {'],
[2, '\t\tcase 1:'],
[3, '\t\t\treturn 1;'],
[2, '\t\tcase 2:'],
[3, '\t\t\treturn 2;'],
[1, '\t}'],
[0, '}'],
]);
});
test('issue #12398 - Problem in indent guidelines', () => {
assertIndentGuides([
[2, '\t\t.bla'],
[3, '\t\t\tlabel(for)'],
[0, 'include script'],
]);
});
});
});

View File

@@ -359,3 +359,179 @@ suite('TextModelWithTokens regression tests', () => {
registration.dispose();
});
});
suite('TextModel.getLineIndentGuide', () => {
function assertIndentGuides(lines: [number, string][]): void {
let text = lines.map(l => l[1]).join('\n');
let model = Model.createFromString(text);
let actualIndents = model.getLinesIndentGuides(1, model.getLineCount());
let actual: [number, string][] = [];
for (let line = 1; line <= model.getLineCount(); line++) {
actual[line - 1] = [actualIndents[line - 1], model.getLineContent(line)];
}
// let expected = lines.map(l => l[0]);
assert.deepEqual(actual, lines);
model.dispose();
}
test('getLineIndentGuide one level', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
]);
});
test('getLineIndentGuide two levels', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
[1, ' A'],
]);
});
test('getLineIndentGuide three levels', () => {
assertIndentGuides([
[0, 'A'],
[1, ' A'],
[1, ' A'],
[2, ' A'],
[0, 'A'],
]);
});
test('getLineIndentGuide decreasing indent', () => {
assertIndentGuides([
[1, ' A'],
[1, ' A'],
[0, 'A'],
]);
});
test('getLineIndentGuide Java', () => {
assertIndentGuides([
/* 1*/[0, 'class A {'],
/* 2*/[1, ' void foo() {'],
/* 3*/[1, ' console.log(1);'],
/* 4*/[1, ' console.log(2);'],
/* 5*/[1, ' }'],
/* 6*/[1, ''],
/* 7*/[1, ' void bar() {'],
/* 8*/[1, ' console.log(3);'],
/* 9*/[1, ' }'],
/*10*/[0, '}'],
/*11*/[0, 'interface B {'],
/*12*/[1, ' void bar();'],
/*13*/[0, '}'],
]);
});
test('getLineIndentGuide Javadoc', () => {
assertIndentGuides([
[0, '/**'],
[1, ' * Comment'],
[1, ' */'],
[0, 'class A {'],
[1, ' void foo() {'],
[1, ' }'],
[0, '}'],
]);
});
test('getLineIndentGuide Whitespace', () => {
assertIndentGuides([
[0, 'class A {'],
[1, ''],
[1, ' void foo() {'],
[1, ' '],
[2, ' return 1;'],
[1, ' }'],
[1, ' '],
[0, '}'],
]);
});
test('getLineIndentGuide Tabs', () => {
assertIndentGuides([
[0, 'class A {'],
[1, '\t\t'],
[1, '\tvoid foo() {'],
[2, '\t \t//hello'],
[2, '\t return 2;'],
[1, ' \t}'],
[1, ' '],
[0, '}'],
]);
});
test('getLineIndentGuide checker.ts', () => {
assertIndentGuides([
/* 1*/[0, '/// <reference path="binder.ts"/>'],
/* 2*/[0, ''],
/* 3*/[0, '/* @internal */'],
/* 4*/[0, 'namespace ts {'],
/* 5*/[1, ' let nextSymbolId = 1;'],
/* 6*/[1, ' let nextNodeId = 1;'],
/* 7*/[1, ' let nextMergeId = 1;'],
/* 8*/[1, ' let nextFlowId = 1;'],
/* 9*/[1, ''],
/*10*/[1, ' export function getNodeId(node: Node): number {'],
/*11*/[2, ' if (!node.id) {'],
/*12*/[3, ' node.id = nextNodeId;'],
/*13*/[3, ' nextNodeId++;'],
/*14*/[2, ' }'],
/*15*/[2, ' return node.id;'],
/*16*/[1, ' }'],
/*17*/[0, '}'],
]);
});
test('issue #8425 - Missing indentation lines for first level indentation', () => {
assertIndentGuides([
[1, '\tindent1'],
[2, '\t\tindent2'],
[2, '\t\tindent2'],
[1, '\tindent1'],
]);
});
test('issue #8952 - Indentation guide lines going through text on .yml file', () => {
assertIndentGuides([
[0, 'properties:'],
[1, ' emailAddress:'],
[2, ' - bla'],
[2, ' - length:'],
[3, ' max: 255'],
[0, 'getters:'],
]);
});
test('issue #11892 - Indent guides look funny', () => {
assertIndentGuides([
[0, 'function test(base) {'],
[1, '\tswitch (base) {'],
[2, '\t\tcase 1:'],
[3, '\t\t\treturn 1;'],
[2, '\t\tcase 2:'],
[3, '\t\t\treturn 2;'],
[1, '\t}'],
[0, '}'],
]);
});
test('issue #12398 - Problem in indent guidelines', () => {
assertIndentGuides([
[2, '\t\t.bla'],
[3, '\t\t\tlabel(for)'],
[0, 'include script'],
]);
});
});