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

@@ -10,10 +10,11 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { ILineEdit, ModelLine, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
const NO_TAB_SIZE = 0;
import { Model } from 'vs/editor/common/model/model';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { Cursor } from 'vs/editor/common/controller/cursor';
function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void {
withMockCodeEditor(lines, {}, (editor, cursor) => {
@@ -31,15 +32,6 @@ function testCommand(lines: string[], selections: Selection[], edits: IIdentifie
});
}
function testLineEditMarker(text: string, column: number, stickToPreviousCharacter: boolean, edit: ILineEdit, expectedColumn: number): void {
var line = new ModelLine(text, NO_TAB_SIZE);
line.addMarker(new LineMarker('1', 0, new Position(0, column), stickToPreviousCharacter));
line.applyEdits(new MarkersTracker(), [edit], NO_TAB_SIZE);
assert.equal(line.getMarkers()[0].position.column, expectedColumn);
}
suite('Editor Side Editing - collapsed selection', () => {
test('replace at selection', () => {
@@ -86,14 +78,6 @@ suite('Editor Side Editing - collapsed selection', () => {
);
});
test('ModelLine.applyEdits uses `isReplace`', () => {
testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 1);
testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4);
testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 4);
testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4);
});
test('insert at selection', () => {
testCommand(
[
@@ -204,5 +188,699 @@ suite('Editor Side Editing - collapsed selection', () => {
[new Selection(1, 1, 1, 1), new Selection(1, 3, 1, 3)]
);
});
});
suite('SideEditing', () => {
const LINES = [
'My First Line',
'My Second Line',
'Third Line'
];
function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void {
const model = Model.createFromString(LINES.join('\n'));
const config = new TestConfiguration(null);
const viewModel = new ViewModel(0, config, model, null);
const cursor = new Cursor(config, model, viewModel);
cursor.setSelections('tests', [selection]);
model.applyEdits([{ range: editRange, text: editText, forceMoveMarkers: editForceMoveMarkers, identifier: null }]);
const actual = cursor.getSelection();
assert.deepEqual(actual.toString(), expected.toString(), msg);
cursor.dispose();
viewModel.dispose();
config.dispose();
model.dispose();
}
function runTest(selection: Range, editRange: Range, editText: string, expected: Selection[][]): void {
const sel1 = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn);
_runTest(sel1, editRange, editText, false, expected[0][0], '0-0-regular-no-force');
_runTest(sel1, editRange, editText, true, expected[1][0], '1-0-regular-force');
// RTL selection
const sel2 = new Selection(selection.endLineNumber, selection.endColumn, selection.startLineNumber, selection.startColumn);
_runTest(sel2, editRange, editText, false, expected[0][1], '0-1-inverse-no-force');
_runTest(sel2, editRange, editText, true, expected[1][1], '1-1-inverse-force');
}
suite('insert', () => {
suite('collapsed sel', () => {
test('before', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 3, 1, 3), 'xx',
[
[new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)],
[new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)],
]
);
});
test('equal', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 4), 'xx',
[
[new Selection(1, 4, 1, 6), new Selection(1, 4, 1, 6)],
[new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)],
]
);
});
test('after', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 5), 'xx',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
[new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
]
);
});
test('start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 4), 'xx',
[
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
]
);
});
test('inside', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 5, 1, 5), 'xx',
[
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
]
);
});
test('end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 9), 'xx',
[
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
]
);
});
test('after', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 10), 'xx',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
});
});
suite('delete', () => {
suite('collapsed dec', () => {
test('edit.end < range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 1, 1, 3), '',
[
[new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)],
[new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), '',
[
[new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)],
[new Selection(1, 2, 1, 2), new Selection(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 Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
[new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), '',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), '',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)],
[new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), '',
[
[new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)],
[new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), '',
[
[new Selection(1, 3, 1, 7), new Selection(1, 7, 1, 3)],
[new Selection(1, 3, 1, 7), new Selection(1, 7, 1, 3)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), '',
[
[new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
[new Selection(1, 3, 1, 3), new Selection(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 Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
[new Selection(1, 3, 1, 3), new Selection(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 Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)],
[new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), '',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)],
[new Selection(1, 4, 1, 7), new Selection(1, 7, 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, 9), '',
[
[new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)],
[new Selection(1, 4, 1, 5), new Selection(1, 5, 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, 10), '',
[
[new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)],
[new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), '',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), '',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
});
});
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 Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
[new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), 'c',
[
[new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)],
[new Selection(1, 3, 1, 3), new Selection(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 Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), 'c',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 5, 1, 5), new Selection(1, 5, 1, 5)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), 'c',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)],
[new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), 'c',
[
[new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)],
[new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), 'c',
[
[new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
[new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), 'c',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
[new Selection(1, 5, 1, 8), new Selection(1, 8, 1, 5)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), 'c',
[
[new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)],
[new Selection(1, 5, 1, 5), new Selection(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 Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)],
[new Selection(1, 5, 1, 5), new Selection(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 Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
[new Selection(1, 4, 1, 8), new Selection(1, 8, 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, 9), 'c',
[
[new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)],
[new Selection(1, 4, 1, 6), new Selection(1, 6, 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, 10), 'c',
[
[new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)],
[new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), 'c',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 10), new Selection(1, 10, 1, 4)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), 'c',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
});
});
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 Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)],
[new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 2, 1, 4), 'cccc',
[
[new Selection(1, 4, 1, 6), new Selection(1, 4, 1, 6)],
[new Selection(1, 6, 1, 6), new Selection(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 Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 7, 1, 7), new Selection(1, 7, 1, 7)],
]
);
});
test('edit.start >= range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 4, 1, 6), 'cccc',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 8, 1, 8), new Selection(1, 8, 1, 8)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 4),
new Range(1, 5, 1, 7), 'cccc',
[
[new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)],
[new Selection(1, 4, 1, 4), new Selection(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 Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
[new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
]
);
});
test('edit.end <= range.start', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 2, 1, 4), 'cccc',
[
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)],
]
);
});
test('edit.start < range.start && edit.end < range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 5), 'cccc',
[
[new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 7, 1, 11), new Selection(1, 11, 1, 7)],
]
);
});
test('edit.start < range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 3, 1, 9), 'cccc',
[
[new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)],
[new Selection(1, 7, 1, 7), new Selection(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 Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)],
[new Selection(1, 7, 1, 7), new Selection(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 Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 8, 1, 11), new Selection(1, 11, 1, 8)],
]
);
});
test('edit.start == range.start && edit.end == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 4, 1, 9), 'cccc',
[
[new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
[new Selection(1, 8, 1, 8), new Selection(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 Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)],
[new Selection(1, 8, 1, 8), new Selection(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 Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)],
[new Selection(1, 4, 1, 11), new Selection(1, 11, 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, 9), 'cccc',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 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, 10), 'cccc',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
test('edit.start == range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 9, 1, 11), 'cccc',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 13), new Selection(1, 13, 1, 4)],
]
);
});
test('edit.start > range.end', () => {
runTest(
new Range(1, 4, 1, 9),
new Range(1, 10, 1, 11), 'cccc',
[
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
[new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)],
]
);
});
});
});
});

View File

@@ -14,7 +14,7 @@ import { withEditorModel } from 'vs/editor/test/common/editorTestUtils';
function assertTrimTrailingWhitespaceCommand(text: string[], expected: IIdentifiedSingleEditOperation[]): void {
return withEditorModel(text, (model) => {
var op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1));
var op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1), []);
var actual = getEditOperation(model, op);
assert.deepEqual(actual, expected);
});

View File

@@ -1453,9 +1453,9 @@ suite('Editor Controller - Regression tests', () => {
cursorCommand(cursor, H.Undo);
assert.equal(model.getValue(), [
'some lines',
'and more lines',
'just some text',
' some lines',
' and more lines',
' just some text',
].join('\n'), '002');
cursorCommand(cursor, H.Undo);
@@ -1464,6 +1464,13 @@ suite('Editor Controller - Regression tests', () => {
'and more lines',
'just some text',
].join('\n'), '003');
cursorCommand(cursor, H.Undo);
assert.equal(model.getValue(), [
'some lines',
'and more lines',
'just some text',
].join('\n'), '004');
});
model.dispose();
@@ -1622,6 +1629,24 @@ suite('Editor Controller - Regression tests', () => {
});
});
test('issue #33788: Wrong cursor position when double click to select a word', () => {
let model = Model.createFromString(
[
'Just some text'
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, { position: new Position(1, 8) });
assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10));
CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(cursor, { position: new Position(1, 8) });
assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10));
});
model.dispose();
});
test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => {
usingCursor({
text: [
@@ -1651,7 +1676,8 @@ suite('Editor Controller - Regression tests', () => {
});
});
test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', () => {
test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', function () {
this.timeout(10000);
const LINE_CNT = 2000;
let text = [];
@@ -1715,6 +1741,49 @@ suite('Editor Controller - Regression tests', () => {
assertCursor(cursor, new Selection(1, 1, 1, 1));
});
});
test('issue #36740: wordwrap creates an extra step / character at the wrapping point', () => {
// a single model line => 4 view lines
withMockCodeEditor([
[
'Lorem ipsum ',
'dolor sit amet ',
'consectetur ',
'adipiscing elit',
].join('')
], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(1, 7, 1, 7)]);
moveRight(cursor);
assertCursor(cursor, new Selection(1, 8, 1, 8));
moveRight(cursor);
assertCursor(cursor, new Selection(1, 9, 1, 9));
moveRight(cursor);
assertCursor(cursor, new Selection(1, 10, 1, 10));
moveRight(cursor);
assertCursor(cursor, new Selection(1, 11, 1, 11));
moveRight(cursor);
assertCursor(cursor, new Selection(1, 12, 1, 12));
moveRight(cursor);
assertCursor(cursor, new Selection(1, 13, 1, 13));
// moving to view line 2
moveRight(cursor);
assertCursor(cursor, new Selection(1, 14, 1, 14));
moveLeft(cursor);
assertCursor(cursor, new Selection(1, 13, 1, 13));
// moving back to view line 1
moveLeft(cursor);
assertCursor(cursor, new Selection(1, 12, 1, 12));
});
});
});
suite('Editor Controller - Cursor Configuration', () => {
@@ -3105,6 +3174,93 @@ suite('Editor Controller - Indentation Rules', () => {
assert.equal(model.getLineContent(3), '}');
});
});
test('issue #36090: JS: editor.autoIndent seems to be broken', () => {
class JSMode extends MockMode {
private static _id = new LanguageIdentifier('indentRulesMode', 4);
constructor() {
super(JSMode._id);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
indentationRules: {
// ^(.*\*/)?\s*\}.*$
decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/,
// ^.*\{[^}"']*$
increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/
},
onEnterRules: [
{
// e.g. /** | */
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
}, {
// e.g. /** ...|
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
action: { indentAction: IndentAction.None, appendText: ' * ' }
}, {
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: IndentAction.None, appendText: '* ' }
}, {
// e.g. */|
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
},
{
// e.g. *-----*/|
beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
}
]
}));
}
}
let mode = new JSMode();
let model = Model.createFromString(
[
'class ItemCtrl {',
' getPropertiesByItemId(id) {',
' return this.fetchItem(id)',
' .then(item => {',
' return this.getPropertiesOfItem(item);',
' });',
' }',
'}',
].join('\n'),
undefined,
mode.getLanguageIdentifier()
);
withMockCodeEditor(null, { model: model, autoIndent: false }, (editor, cursor) => {
moveTo(cursor, 7, 6, false);
assertCursor(cursor, new Selection(7, 6, 7, 6));
cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard');
assert.equal(model.getValue(),
[
'class ItemCtrl {',
' getPropertiesByItemId(id) {',
' return this.fetchItem(id)',
' .then(item => {',
' return this.getPropertiesOfItem(item);',
' });',
' }',
' ',
'}',
].join('\n')
);
assertCursor(cursor, new Selection(8, 5, 8, 5));
});
model.dispose();
mode.dispose();
});
});
interface ICursorOpts {
@@ -3699,4 +3855,260 @@ suite('autoClosingPairs', () => {
model.dispose();
mode.dispose();
});
test('issue #7100: Mouse word selection is strange when non-word character is at the end of line', () => {
let model = Model.createFromString(
[
'before.a',
'before',
'hello:',
'there:',
'this is strange:',
'here',
'it',
'is',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
CoreNavigationCommands.WordSelect.runEditorCommand(null, editor, {
position: new Position(3, 7)
});
assertCursor(cursor, new Selection(3, 7, 3, 7));
CoreNavigationCommands.WordSelectDrag.runEditorCommand(null, editor, {
position: new Position(4, 7)
});
assertCursor(cursor, new Selection(3, 7, 4, 7));
});
});
});
suite('Undo stops', () => {
test('there is an undo stop between typing and deleting left', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(1, 3, 1, 3)]);
cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard');
assert.equal(model.getLineContent(1), 'A first line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), 'A fir line');
assertCursor(cursor, new Selection(1, 6, 1, 6));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A first line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A line');
assertCursor(cursor, new Selection(1, 3, 1, 3));
});
});
test('there is an undo stop between typing and deleting right', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(1, 3, 1, 3)]);
cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard');
assert.equal(model.getLineContent(1), 'A first line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), 'A firstine');
assertCursor(cursor, new Selection(1, 8, 1, 8));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A first line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A line');
assertCursor(cursor, new Selection(1, 3, 1, 3));
});
});
test('there is an undo stop between deleting left and typing', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(2, 8, 2, 8)]);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), ' line');
assertCursor(cursor, new Selection(2, 1, 2, 1));
cursorCommand(cursor, H.Type, { text: 'Second' }, 'keyboard');
assert.equal(model.getLineContent(2), 'Second line');
assertCursor(cursor, new Selection(2, 7, 2, 7));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), ' line');
assertCursor(cursor, new Selection(2, 1, 2, 1));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another line');
assertCursor(cursor, new Selection(2, 8, 2, 8));
});
});
test('there is an undo stop between deleting left and deleting right', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(2, 8, 2, 8)]);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), ' line');
assertCursor(cursor, new Selection(2, 1, 2, 1));
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), '');
assertCursor(cursor, new Selection(2, 1, 2, 1));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), ' line');
assertCursor(cursor, new Selection(2, 1, 2, 1));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another line');
assertCursor(cursor, new Selection(2, 8, 2, 8));
});
});
test('there is an undo stop between deleting right and typing', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(2, 9, 2, 9)]);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), 'Another ');
assertCursor(cursor, new Selection(2, 9, 2, 9));
cursorCommand(cursor, H.Type, { text: 'text' }, 'keyboard');
assert.equal(model.getLineContent(2), 'Another text');
assertCursor(cursor, new Selection(2, 13, 2, 13));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another ');
assertCursor(cursor, new Selection(2, 9, 2, 9));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another line');
assertCursor(cursor, new Selection(2, 9, 2, 9));
});
});
test('there is an undo stop between deleting right and deleting left', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(2, 9, 2, 9)]);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), 'Another ');
assertCursor(cursor, new Selection(2, 9, 2, 9));
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(2), 'An');
assertCursor(cursor, new Selection(2, 3, 2, 3));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another ');
assertCursor(cursor, new Selection(2, 9, 2, 9));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(2), 'Another line');
assertCursor(cursor, new Selection(2, 9, 2, 9));
});
});
test('inserts undo stop when typing space', () => {
let model = Model.createFromString(
[
'A line',
'Another line',
].join('\n')
);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(1, 3, 1, 3)]);
cursorCommand(cursor, H.Type, { text: 'first and interesting' }, 'keyboard');
assert.equal(model.getLineContent(1), 'A first and interesting line');
assertCursor(cursor, new Selection(1, 24, 1, 24));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A first and line');
assertCursor(cursor, new Selection(1, 12, 1, 12));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A first line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
cursorCommand(cursor, H.Undo, {});
assert.equal(model.getLineContent(1), 'A line');
assertCursor(cursor, new Selection(1, 3, 1, 3));
});
});
});

View File

@@ -18,6 +18,9 @@ import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { IDisposable } from 'vs/base/common/lifecycle';
export class MockCodeEditor extends CommonCodeEditor {
public _isFocused = true;
protected _createConfiguration(options: editorOptions.IEditorOptions): CommonEditorConfiguration {
return new TestConfiguration(options);
}
@@ -25,7 +28,7 @@ export class MockCodeEditor extends CommonCodeEditor {
public layout(dimension?: editorCommon.IDimension): void { }
public focus(): void { }
public isFocused(): boolean { return true; }
public isFocused(): boolean { return this._isFocused; }
public hasWidgetFocus(): boolean { return true; };
protected _enableEmptySelectionClipboard(): boolean { return false; }

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'],
]);
});
});

View File

@@ -190,4 +190,11 @@ suite('Editor Modes - Link Computer', () => {
' http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items '
);
});
test('issue #7855', () => {
assertLink(
'7. At this point, ServiceMain has been called. There is no functionality presently in ServiceMain, but you can consult the [MSDN documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx) to add functionality as desired!',
' https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx '
);
});
});

View File

@@ -88,7 +88,7 @@ suite('EditorSimpleWorker', () => {
test('MoreMinimal', function () {
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: 'This is line One', range: new Range(1, 1, 1, 17) }], []).then(edits => {
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: 'This is line One', range: new Range(1, 1, 1, 17) }]).then(edits => {
assert.equal(edits.length, 1);
const [first] = edits;
assert.equal(first.text, 'O');
@@ -104,7 +104,7 @@ suite('EditorSimpleWorker', () => {
'}'
], '\n');
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"a":1\r\n}', range: new Range(1, 1, 3, 2) }], []).then(edits => {
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"a":1\r\n}', range: new Range(1, 1, 3, 2) }]).then(edits => {
assert.equal(edits.length, 0);
});
});
@@ -117,7 +117,7 @@ suite('EditorSimpleWorker', () => {
'}'
], '\n');
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"b":1\r\n}', range: new Range(1, 1, 3, 2) }], []).then(edits => {
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"b":1\r\n}', range: new Range(1, 1, 3, 2) }]).then(edits => {
assert.equal(edits.length, 1);
const [first] = edits;
assert.equal(first.text, 'b');
@@ -133,7 +133,7 @@ suite('EditorSimpleWorker', () => {
'}' // 3
]);
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '\n', range: new Range(3, 2, 4, 1000) }], []).then(edits => {
return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '\n', range: new Range(3, 2, 4, 1000) }]).then(edits => {
assert.equal(edits.length, 1);
const [first] = edits;
assert.equal(first.text, '\n');

View File

@@ -17,7 +17,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('Bug 9827:Overlapping inline decorations can cause wrong inline class to be applied', () => {
var result = LineDecorationsNormalizer.normalize([
var result = LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 11, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]);
@@ -31,7 +31,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('issue #3462: no whitespace shown at the end of a decorated line', () => {
var result = LineDecorationsNormalizer.normalize([
var result = LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(15, 21, 'vs-whitespace', false),
new LineDecoration(20, 21, 'inline-folded', false),
]);
@@ -55,7 +55,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('ViewLineParts', () => {
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 2, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
@@ -63,7 +63,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 3, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
@@ -71,7 +71,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
@@ -79,7 +79,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c1 c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(3, 4, 'c2', false)
@@ -88,7 +88,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c1 c1* c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),
@@ -98,7 +98,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c1 c1* c1** c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),
@@ -109,7 +109,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),

View File

@@ -1052,6 +1052,36 @@ suite('viewLineRenderer.renderLine 2', () => {
assert.deepEqual(actual.html, expected);
});
test('issue #32436: Non-monospace font + visible whitespace + After decorator causes line to "jump"', () => {
let lineContent = '\tbla';
let actual = renderViewLine(new RenderLineInput(
false,
lineContent,
false,
0,
[createPart(4, 3)],
[new LineDecoration(2, 3, 'before', true)],
4,
10,
-1,
'all',
false,
true
));
let expected = [
'<span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtk3 before">b</span>',
'<span class="mtk3">la</span>',
'</span>'
].join('');
assert.deepEqual(actual.html, expected);
});
test('issue #30133: Empty lines don\'t render inline decorations', () => {
let lineContent = '';
@@ -1080,6 +1110,33 @@ suite('viewLineRenderer.renderLine 2', () => {
assert.deepEqual(actual.html, expected);
});
test('issue #37208: Collapsing bullet point containing emoji in Markdown document results in [??] character', () => {
let actual = renderViewLine(new RenderLineInput(
true,
' 1. 🙏',
false,
0,
[createPart(7, 3)],
[new LineDecoration(7, 8, 'inline-folded', true)],
2,
10,
10000,
'none',
false,
false
));
let expected = [
'<span>',
'<span class="mtk3">\u00a0\u00a01.\u00a0</span>',
'<span class="mtk3 inline-folded">🙏</span>',
'</span>'
].join('');
assert.deepEqual(actual.html, expected);
});
function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {
let renderLineOutput = renderViewLine(new RenderLineInput(
false,

View File

@@ -7,9 +7,9 @@
import * as assert from 'assert';
import { WrappingIndent } from 'vs/editor/common/config/editorOptions';
import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
import { ILineMapperFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
import { ILineMapperFactory, ILineMapping } from 'vs/editor/common/viewModel/splitLinesCollection';
function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None) {
function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): ILineMapping {
let rawText = '';
let currentLineIndex = 0;
@@ -42,6 +42,8 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf
}
assert.equal(actualAnnotatedText, annotatedText);
return mapper;
}
suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => {
@@ -106,4 +108,10 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => {
let factory = new CharacterHardWrappingLineMapperFactory('', ' ', '');
assertLineMapping(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent);
});
test('issue #35162: wrappingIndent not consistently working', () => {
let factory = new CharacterHardWrappingLineMapperFactory('', ' ', '');
let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent);
assert.equal(mapper.getWrappedLinesIndent(), ' \t');
});
});

View File

@@ -136,15 +136,17 @@ suite('Editor ViewModel - SplitLinesCollection', () => {
assert.equal(linesCollection.getViewLineCount(), 6);
// getOutputIndentGuide
assert.equal(linesCollection.getViewLineIndentGuide(-1), 0);
assert.equal(linesCollection.getViewLineIndentGuide(0), 0);
assert.equal(linesCollection.getViewLineIndentGuide(1), 0);
assert.equal(linesCollection.getViewLineIndentGuide(2), 1);
assert.equal(linesCollection.getViewLineIndentGuide(3), 0);
assert.equal(linesCollection.getViewLineIndentGuide(4), 0);
assert.equal(linesCollection.getViewLineIndentGuide(5), 1);
assert.equal(linesCollection.getViewLineIndentGuide(6), 0);
assert.equal(linesCollection.getViewLineIndentGuide(7), 0);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(-1, -1), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(0, 0), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(1, 1), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(2, 2), [1]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(3, 3), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(4, 4), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(5, 5), [1]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(6, 6), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(7, 7), [0]);
assert.deepEqual(linesCollection.getViewLinesIndentGuides(0, 7), [0, 1, 0, 0, 1, 0]);
// getOutputLineContent
assert.equal(linesCollection.getViewLineContent(-1), 'int main() {');

View File

@@ -91,23 +91,23 @@ suite('ViewModelDecorations', () => {
let actualDecorations = viewModel.getDecorationsInViewport(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3))
).map((dec) => {
return dec.source.id;
return dec.options.className;
});
assert.deepEqual(actualDecorations, [
dec2,
dec3,
dec4,
dec5,
dec6,
dec7,
dec8,
dec9,
dec10,
dec11,
dec12,
dec13,
dec14,
'dec2',
'dec3',
'dec4',
'dec5',
'dec6',
'dec7',
'dec8',
'dec9',
'dec10',
'dec11',
'dec12',
'dec13',
'dec14',
]);
let inlineDecorations1 = viewModel.getViewLineRenderingData(

View File

@@ -41,4 +41,148 @@ suite('ViewModel', () => {
assert.equal(viewModel.getLineCount(), 10);
});
});
function assertGetPlainTextToCopy(text: string[], ranges: Range[], emptySelectionClipboard: boolean, expected: string): void {
testViewModel(text, {}, (viewModel, model) => {
let actual = viewModel.getPlainTextToCopy(ranges, emptySelectionClipboard);
assert.equal(actual, expected);
});
}
const USUAL_TEXT = [
'',
'line2',
'line3',
'line4',
''
];
test('getPlainTextToCopy 0/1', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 2)
],
false,
''
);
});
test('getPlainTextToCopy 0/1 - emptySelectionClipboard', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 2)
],
true,
'line2\n'
);
});
test('getPlainTextToCopy 1/1', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 6)
],
false,
'ine2'
);
});
test('getPlainTextToCopy 1/1 - emptySelectionClipboard', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 6)
],
true,
'ine2'
);
});
test('getPlainTextToCopy 0/2', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 2),
new Range(3, 2, 3, 2),
],
false,
''
);
});
test('getPlainTextToCopy 0/2 - emptySelectionClipboard', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 2),
new Range(3, 2, 3, 2),
],
true,
'line2\nline3\n'
);
});
test('getPlainTextToCopy 1/2', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 6),
new Range(3, 2, 3, 2),
],
false,
'ine2'
);
});
test('getPlainTextToCopy 1/2 - emptySelectionClipboard', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 6),
new Range(3, 2, 3, 2),
],
true,
'ine2'
);
});
test('getPlainTextToCopy 2/2', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 6),
new Range(3, 2, 3, 6),
],
false,
'ine2\nine3'
);
});
test('getPlainTextToCopy 2/2 reversed', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(3, 2, 3, 6),
new Range(2, 2, 2, 6),
],
false,
'ine2\nine3'
);
});
test('getPlainTextToCopy 0/3 - emptySelectionClipboard', () => {
assertGetPlainTextToCopy(
USUAL_TEXT,
[
new Range(2, 2, 2, 2),
new Range(2, 3, 2, 3),
new Range(3, 2, 3, 2),
],
true,
'line2\nline3\n'
);
});
});