SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* 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 { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
export function testCommand(
lines: string[],
languageIdentifier: LanguageIdentifier,
selection: Selection,
commandFactory: (selection: Selection) => editorCommon.ICommand,
expectedLines: string[],
expectedSelection: Selection
): void {
let model = Model.createFromString(lines.join('\n'), undefined, languageIdentifier);
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('tests', [selection]);
cursor.trigger('tests', editorCommon.Handler.ExecuteCommand, commandFactory(cursor.getSelection()));
assert.deepEqual(model.getLinesContent(), expectedLines);
let actualSelection = cursor.getSelection();
assert.deepEqual(actualSelection.toString(), expectedSelection.toString());
});
model.dispose();
}
/**
* Extract edit operations if command `command` were to execute on model `model`
*/
export function getEditOperation(model: editorCommon.IModel, command: editorCommon.ICommand): editorCommon.IIdentifiedSingleEditOperation[] {
var operations: editorCommon.IIdentifiedSingleEditOperation[] = [];
var editOperationBuilder: editorCommon.IEditOperationBuilder = {
addEditOperation: (range: Range, text: string) => {
operations.push({
identifier: null,
range: range,
text: text,
forceMoveMarkers: false
});
},
addTrackedEditOperation: (range: Range, text: string) => {
operations.push({
identifier: null,
range: range,
text: text,
forceMoveMarkers: false
});
},
trackSelection: (selection: Selection) => {
return null;
}
};
command.getEditOperations(model, editOperationBuilder);
return operations;
}
/**
* Create single edit operation
*/
export function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): editorCommon.IIdentifiedSingleEditOperation {
return {
identifier: null,
range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),
text: text,
forceMoveMarkers: false
};
}
/**
* Create single edit operation
*/
export function createInsertDeleteSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): editorCommon.IIdentifiedSingleEditOperation {
return {
identifier: null,
range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),
text: text,
forceMoveMarkers: true
};
}

View File

@@ -0,0 +1,987 @@
/*---------------------------------------------------------------------------------------------
* 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 { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { Selection } from 'vs/editor/common/core/selection';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { IndentAction } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { createSingleEditOp, getEditOperation, testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
import { withEditorModel } from 'vs/editor/test/common/editorTestUtils';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageIdentifier } from 'vs/editor/common/modes';
class DocBlockCommentMode extends MockMode {
private static _id = new LanguageIdentifier('commentMode', 3);
constructor() {
super(DocBlockCommentMode._id);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
brackets: [
['(', ')'],
['{', '}'],
['[', ']']
],
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 }
}
]
}));
}
}
function testShiftCommand(lines: string[], languageIdentifier: LanguageIdentifier, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, {
isUnshift: false,
tabSize: 4,
oneIndent: '\t',
useTabStops: useTabStops,
}), expectedLines, expectedSelection);
}
function testUnshiftCommand(lines: string[], languageIdentifier: LanguageIdentifier, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, {
isUnshift: true,
tabSize: 4,
oneIndent: '\t',
useTabStops: useTabStops,
}), expectedLines, expectedSelection);
}
function withDockBlockCommentMode(callback: (mode: DocBlockCommentMode) => void): void {
let mode = new DocBlockCommentMode();
callback(mode);
mode.dispose();
}
suite('Editor Commands - ShiftCommand', () => {
// --------- shift
test('Bug 9503: Shifting without any selection', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 1, 1),
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 1, 1, 2)
);
});
test('shift on single line selection 1', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 3, 1, 1),
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 4, 1, 1)
);
});
test('shift on single line selection 2', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 1, 3),
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 1, 1, 4)
);
});
test('simple shift', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 2, 1),
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 1, 2, 1)
);
});
test('shifting on two separate lines', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 2, 1),
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 1, 2, 1)
);
testShiftCommand(
[
'\tMy First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 1, 3, 1),
[
'\tMy First Line',
'\t\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 1, 3, 1)
);
});
test('shifting on two lines', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 2, 2, 2),
[
'\tMy First Line',
'\t\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 3, 2, 2)
);
});
test('shifting on two lines again', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 2, 1, 2),
[
'\tMy First Line',
'\t\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 2, 1, 3)
);
});
test('shifting at end of file', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(4, 1, 5, 2),
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'\t123'
],
new Selection(4, 1, 5, 3)
);
});
test('issue #1120 TAB should not indent empty lines in a multi-line selection', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 5, 2),
[
'\tMy First Line',
'\t\t\tMy Second Line',
'\t\tThird Line',
'',
'\t123'
],
new Selection(1, 1, 5, 3)
);
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(4, 1, 5, 1),
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'\t',
'123'
],
new Selection(4, 1, 5, 1)
);
});
// --------- unshift
test('unshift on single line selection 1', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 3, 2, 1),
[
'My First Line',
'\t\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 3, 2, 1)
);
});
test('unshift on single line selection 2', () => {
testShiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 1, 2, 3),
[
'My First Line',
'\t\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 1, 2, 3)
);
});
test('simple unshift', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 2, 1),
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 1, 2, 1)
);
});
test('unshifting on two lines 1', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 2, 2, 2),
[
'My First Line',
'\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(1, 2, 2, 2)
);
});
test('unshifting on two lines 2', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 3, 2, 1),
[
'My First Line',
'\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 2, 2, 1)
);
});
test('unshifting at the end of the file', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(4, 1, 5, 2),
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(4, 1, 5, 2)
);
});
test('unshift many times + shift', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 5, 4),
[
'My First Line',
'\tMy Second Line',
'Third Line',
'',
'123'
],
new Selection(1, 1, 5, 4)
);
testUnshiftCommand(
[
'My First Line',
'\tMy Second Line',
'Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 5, 4),
[
'My First Line',
'My Second Line',
'Third Line',
'',
'123'
],
new Selection(1, 1, 5, 4)
);
testShiftCommand(
[
'My First Line',
'My Second Line',
'Third Line',
'',
'123'
],
null,
true,
new Selection(1, 1, 5, 4),
[
'\tMy First Line',
'\tMy Second Line',
'\tThird Line',
'',
'\t123'
],
new Selection(1, 1, 5, 5)
);
});
test('Bug 9119: Unshift from first column doesn\'t work', () => {
testUnshiftCommand(
[
'My First Line',
'\t\tMy Second Line',
' Third Line',
'',
'123'
],
null,
true,
new Selection(2, 1, 2, 1),
[
'My First Line',
'\tMy Second Line',
' Third Line',
'',
'123'
],
new Selection(2, 1, 2, 1)
);
});
test('issue #348: indenting around doc block comments', () => {
withDockBlockCommentMode((mode) => {
testShiftCommand(
[
'',
'/**',
' * a doc comment',
' */',
'function hello() {}'
],
mode.getLanguageIdentifier(),
true,
new Selection(1, 1, 5, 20),
[
'',
'\t/**',
'\t * a doc comment',
'\t */',
'\tfunction hello() {}'
],
new Selection(1, 1, 5, 21)
);
testUnshiftCommand(
[
'',
'/**',
' * a doc comment',
' */',
'function hello() {}'
],
mode.getLanguageIdentifier(),
true,
new Selection(1, 1, 5, 20),
[
'',
'/**',
' * a doc comment',
' */',
'function hello() {}'
],
new Selection(1, 1, 5, 20)
);
testUnshiftCommand(
[
'\t',
'\t/**',
'\t * a doc comment',
'\t */',
'\tfunction hello() {}'
],
mode.getLanguageIdentifier(),
true,
new Selection(1, 1, 5, 21),
[
'',
'/**',
' * a doc comment',
' */',
'function hello() {}'
],
new Selection(1, 1, 5, 20)
);
});
});
test('issue #1609: Wrong indentation of block comments', () => {
withDockBlockCommentMode((mode) => {
testShiftCommand(
[
'',
'/**',
' * test',
' *',
' * @type {number}',
' */',
'var foo = 0;'
],
mode.getLanguageIdentifier(),
true,
new Selection(1, 1, 7, 13),
[
'',
'\t/**',
'\t * test',
'\t *',
'\t * @type {number}',
'\t */',
'\tvar foo = 0;'
],
new Selection(1, 1, 7, 14)
);
});
});
test('issue #1620: a) Line indent doesn\'t handle leading whitespace properly', () => {
testCommand(
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
null,
new Selection(1, 1, 13, 1),
(sel) => new ShiftCommand(sel, {
isUnshift: false,
tabSize: 4,
oneIndent: ' ',
useTabStops: false
}),
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
new Selection(1, 1, 13, 1)
);
});
test('issue #1620: b) Line indent doesn\'t handle leading whitespace properly', () => {
testCommand(
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
null,
new Selection(1, 1, 13, 1),
(sel) => new ShiftCommand(sel, {
isUnshift: true,
tabSize: 4,
oneIndent: ' ',
useTabStops: false
}),
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
new Selection(1, 1, 13, 1)
);
});
test('issue #1620: c) Line indent doesn\'t handle leading whitespace properly', () => {
testCommand(
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
null,
new Selection(1, 1, 13, 1),
(sel) => new ShiftCommand(sel, {
isUnshift: true,
tabSize: 4,
oneIndent: '\t',
useTabStops: false
}),
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
new Selection(1, 1, 13, 1)
);
});
test('issue #1620: d) Line indent doesn\'t handle leading whitespace properly', () => {
testCommand(
[
'\t Written | Numeric',
'\t one | 1',
'\t two | 2',
'\t three | 3',
'\t four | 4',
'\t five | 5',
'\t six | 6',
'\t seven | 7',
'\t eight | 8',
'\t nine | 9',
'\t ten | 10',
'\t eleven | 11',
'',
],
null,
new Selection(1, 1, 13, 1),
(sel) => new ShiftCommand(sel, {
isUnshift: true,
tabSize: 4,
oneIndent: ' ',
useTabStops: false
}),
[
' Written | Numeric',
' one | 1',
' two | 2',
' three | 3',
' four | 4',
' five | 5',
' six | 6',
' seven | 7',
' eight | 8',
' nine | 9',
' ten | 10',
' eleven | 11',
'',
],
new Selection(1, 1, 13, 1)
);
});
test('issue Microsoft/monaco-editor#443: Indentation of a single row deletes selected text in some cases', () => {
testCommand(
[
'Hello world!',
'another line'
],
null,
new Selection(1, 1, 1, 13),
(sel) => new ShiftCommand(sel, {
isUnshift: false,
tabSize: 4,
oneIndent: '\t',
useTabStops: true
}),
[
'\tHello world!',
'another line'
],
new Selection(1, 1, 1, 14)
);
});
test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => {
var repeatStr = (str: string, cnt: number): string => {
var r = '';
for (var i = 0; i < cnt; i++) {
r += str;
}
return r;
};
var testOutdent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => {
var expectedIndent = repeatStr(oneIndent, expectedIndents);
if (lineText.length > 0) {
_assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]);
} else {
_assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], []);
}
};
var testIndent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => {
var expectedIndent = repeatStr(oneIndent, expectedIndents);
_assertShiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]);
};
var testIndentation = (tabSize: number, lineText: string, expectedOnOutdent: number, expectedOnIndent: number) => {
var spaceIndent = '';
for (var i = 0; i < tabSize; i++) {
spaceIndent += ' ';
}
testOutdent(tabSize, spaceIndent, lineText, expectedOnOutdent);
testOutdent(tabSize, '\t', lineText, expectedOnOutdent);
testIndent(tabSize, spaceIndent, lineText, expectedOnIndent);
testIndent(tabSize, '\t', lineText, expectedOnIndent);
};
// insertSpaces: true
// 0 => 0
testIndentation(4, '', 0, 1);
// 1 => 0
testIndentation(4, '\t', 0, 2);
testIndentation(4, ' ', 0, 1);
testIndentation(4, ' \t', 0, 2);
testIndentation(4, ' ', 0, 1);
testIndentation(4, ' \t', 0, 2);
testIndentation(4, ' ', 0, 1);
testIndentation(4, ' \t', 0, 2);
testIndentation(4, ' ', 0, 2);
// 2 => 1
testIndentation(4, '\t\t', 1, 3);
testIndentation(4, '\t ', 1, 2);
testIndentation(4, '\t \t', 1, 3);
testIndentation(4, '\t ', 1, 2);
testIndentation(4, '\t \t', 1, 3);
testIndentation(4, '\t ', 1, 2);
testIndentation(4, '\t \t', 1, 3);
testIndentation(4, '\t ', 1, 3);
testIndentation(4, ' \t\t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 3);
testIndentation(4, ' \t\t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 3);
testIndentation(4, ' \t\t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 2);
testIndentation(4, ' \t \t', 1, 3);
testIndentation(4, ' \t ', 1, 3);
testIndentation(4, ' \t', 1, 3);
testIndentation(4, ' ', 1, 2);
testIndentation(4, ' \t', 1, 3);
testIndentation(4, ' ', 1, 2);
testIndentation(4, ' \t', 1, 3);
testIndentation(4, ' ', 1, 2);
testIndentation(4, ' \t', 1, 3);
testIndentation(4, ' ', 1, 3);
// 3 => 2
testIndentation(4, ' ', 2, 3);
function _assertUnshiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void {
return withEditorModel(text, (model) => {
var op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), {
isUnshift: true,
tabSize: tabSize,
oneIndent: oneIndent,
useTabStops: true
});
var actual = getEditOperation(model, op);
assert.deepEqual(actual, expected);
});
}
function _assertShiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void {
return withEditorModel(text, (model) => {
var op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), {
isUnshift: false,
tabSize: tabSize,
oneIndent: oneIndent,
useTabStops: true
});
var actual = getEditOperation(model, op);
assert.deepEqual(actual, expected);
});
}
});
});

View File

@@ -0,0 +1,208 @@
/*---------------------------------------------------------------------------------------------
* 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 { EditOperation } from 'vs/editor/common/core/editOperation';
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;
function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void {
withMockCodeEditor(lines, {}, (editor, cursor) => {
const model = editor.getModel();
cursor.setSelections('tests', selections);
model.applyEdits(edits);
assert.deepEqual(model.getLinesContent(), expectedLines);
let actualSelections = cursor.getSelections();
assert.deepEqual(actualSelections.map(s => s.toString()), expectedSelections.map(s => s.toString()));
});
}
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', () => {
testCommand(
[
'first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 1, 1, 1)],
[
EditOperation.replace(new Selection(1, 1, 1, 1), 'something ')
],
[
'something first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 1, 1, 11)]
);
});
test('replace at selection 2', () => {
testCommand(
[
'first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 1, 1, 6)],
[
EditOperation.replace(new Selection(1, 1, 1, 6), 'something')
],
[
'something',
'second line',
'third line',
'fourth'
],
[new Selection(1, 1, 1, 10)]
);
});
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(
[
'first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 1, 1, 1)],
[
EditOperation.insert(new Position(1, 1), 'something ')
],
[
'something first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 11, 1, 11)]
);
});
test('insert at selection sitting on max column', () => {
testCommand(
[
'first',
'second line',
'third line',
'fourth'
],
[new Selection(1, 6, 1, 6)],
[
EditOperation.insert(new Position(1, 6), ' something\nnew ')
],
[
'first something',
'new ',
'second line',
'third line',
'fourth'
],
[new Selection(2, 5, 2, 5)]
);
});
test('issue #3994: replace on top of selection', () => {
testCommand(
[
'$obj = New-Object "system.col"'
],
[new Selection(1, 30, 1, 30)],
[
EditOperation.replaceMove(new Range(1, 19, 1, 31), '"System.Collections"')
],
[
'$obj = New-Object "System.Collections"'
],
[new Selection(1, 39, 1, 39)]
);
});
test('issue #15267: Suggestion that adds a line - cursor goes to the wrong line ', () => {
testCommand(
[
'package main',
'',
'import (',
' "fmt"',
')',
'',
'func main(',
' fmt.Println(strings.Con)',
'}'
],
[new Selection(8, 25, 8, 25)],
[
EditOperation.replaceMove(new Range(5, 1, 5, 1), '\t\"strings\"\n')
],
[
'package main',
'',
'import (',
' "fmt"',
' "strings"',
')',
'',
'func main(',
' fmt.Println(strings.Con)',
'}'
],
[new Selection(9, 25, 9, 25)]
);
});
test('issue #15236: Selections broke after deleting text using vscode.TextEditor.edit ', () => {
testCommand(
[
'foofoofoo, foofoofoo, bar'
],
[new Selection(1, 1, 1, 10), new Selection(1, 12, 1, 21)],
[
EditOperation.replace(new Range(1, 1, 1, 10), ''),
EditOperation.replace(new Range(1, 12, 1, 21), ''),
],
[
', , bar'
],
[new Selection(1, 1, 1, 1), new Selection(1, 3, 1, 3)]
);
});
});

View File

@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* 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 { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { createInsertDeleteSingleEditOp, createSingleEditOp, getEditOperation } from 'vs/editor/test/common/commands/commandTestUtils';
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 actual = getEditOperation(model, op);
assert.deepEqual(actual, expected);
});
}
function assertTrimTrailingWhitespace(text: string[], cursors: Position[], expected: IIdentifiedSingleEditOperation[]): void {
return withEditorModel(text, (model) => {
var actual = trimTrailingWhitespace(model, cursors);
assert.deepEqual(actual, expected);
});
}
suite('Editor Commands - Trim Trailing Whitespace Command', () => {
test('remove trailing whitespace', function () {
assertTrimTrailingWhitespaceCommand([''], []);
assertTrimTrailingWhitespaceCommand(['text'], []);
assertTrimTrailingWhitespaceCommand(['text '], [createSingleEditOp(null, 1, 5, 1, 8)]);
assertTrimTrailingWhitespaceCommand(['text\t '], [createSingleEditOp(null, 1, 5, 1, 9)]);
assertTrimTrailingWhitespaceCommand(['\t '], [createSingleEditOp(null, 1, 1, 1, 5)]);
assertTrimTrailingWhitespaceCommand(['text\t'], [createSingleEditOp(null, 1, 5, 1, 6)]);
assertTrimTrailingWhitespaceCommand([
'some text\t',
'some more text',
'\t ',
'even more text ',
'and some mixed\t \t'
], [
createSingleEditOp(null, 1, 10, 1, 11),
createSingleEditOp(null, 3, 1, 3, 4),
createSingleEditOp(null, 4, 15, 4, 17),
createSingleEditOp(null, 5, 15, 5, 20)
]);
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 2), new Position(1, 3)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5), new Position(1, 6)], [createInsertDeleteSingleEditOp(null, 1, 6, 1, 8)]);
assertTrimTrailingWhitespace([
'some text\t',
'some more text',
'\t ',
'even more text ',
'and some mixed\t \t'
], [], [
createInsertDeleteSingleEditOp(null, 1, 10, 1, 11),
createInsertDeleteSingleEditOp(null, 3, 1, 3, 4),
createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),
createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)
]);
assertTrimTrailingWhitespace([
'some text\t',
'some more text',
'\t ',
'even more text ',
'and some mixed\t \t'
], [new Position(1, 11), new Position(3, 2), new Position(5, 1), new Position(4, 1), new Position(5, 10)], [
createInsertDeleteSingleEditOp(null, 3, 2, 3, 4),
createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),
createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)
]);
});
});

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* 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 { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { CommentRule } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
export class CommentMode extends MockMode {
private static _id = new LanguageIdentifier('commentMode', 3);
constructor(commentsConfig: CommentRule) {
super(CommentMode._id);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
comments: commentsConfig
}));
}
}

View File

@@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------------------------
* 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 { EditorZoom } from 'vs/editor/common/config/editorZoom';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { AccessibilitySupport } from 'vs/base/common/platform';
suite('Common Editor Config', () => {
test('Zoom Level', () => {
//Zoom levels are defined to go between -9, 9 inclusive
var zoom = EditorZoom;
zoom.setZoomLevel(0);
assert.equal(zoom.getZoomLevel(), 0);
zoom.setZoomLevel(-0);
assert.equal(zoom.getZoomLevel(), 0);
zoom.setZoomLevel(5);
assert.equal(zoom.getZoomLevel(), 5);
zoom.setZoomLevel(-1);
assert.equal(zoom.getZoomLevel(), -1);
zoom.setZoomLevel(9);
assert.equal(zoom.getZoomLevel(), 9);
zoom.setZoomLevel(-9);
assert.equal(zoom.getZoomLevel(), -9);
zoom.setZoomLevel(10);
assert.equal(zoom.getZoomLevel(), 9);
zoom.setZoomLevel(-10);
assert.equal(zoom.getZoomLevel(), -9);
zoom.setZoomLevel(9.1);
assert.equal(zoom.getZoomLevel(), 9);
zoom.setZoomLevel(-9.1);
assert.equal(zoom.getZoomLevel(), -9);
zoom.setZoomLevel(Infinity);
assert.equal(zoom.getZoomLevel(), 9);
zoom.setZoomLevel(Number.NEGATIVE_INFINITY);
assert.equal(zoom.getZoomLevel(), -9);
});
class TestWrappingConfiguration extends TestConfiguration {
protected _getEnvConfiguration(): IEnvConfiguration {
return {
extraEditorClassName: '',
outerWidth: 1000,
outerHeight: 100,
emptySelectionClipboard: true,
pixelRatio: 1,
zoomLevel: 0,
accessibilitySupport: AccessibilitySupport.Unknown
};
}
}
function assertWrapping(config: TestConfiguration, isViewportWrapping: boolean, wrappingColumn: number): void {
assert.equal(config.editor.wrappingInfo.isViewportWrapping, isViewportWrapping);
assert.equal(config.editor.wrappingInfo.wrappingColumn, wrappingColumn);
}
test('wordWrap default', () => {
let config = new TestWrappingConfiguration({});
assertWrapping(config, false, -1);
});
test('wordWrap compat false', () => {
let config = new TestWrappingConfiguration({
wordWrap: <any>false
});
assertWrapping(config, false, -1);
});
test('wordWrap compat true', () => {
let config = new TestWrappingConfiguration({
wordWrap: <any>true
});
// {{SQL CARBON EDIT}}
assertWrapping(config, true, 89);
});
test('wordWrap on', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'on'
});
// {{SQL CARBON EDIT}}
assertWrapping(config, true, 89);
});
test('wordWrap on without minimap', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'on',
minimap: {
enabled: false
}
});
assertWrapping(config, true, 89);
});
test('wordWrap on does not use wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'on',
wordWrapColumn: 10
});
// {{SQL CARBON EDIT}}
assertWrapping(config, true, 89);
});
test('wordWrap off', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'off'
});
assertWrapping(config, false, -1);
});
test('wordWrap off does not use wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'off',
wordWrapColumn: 10
});
assertWrapping(config, false, -1);
});
test('wordWrap wordWrapColumn uses default wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'wordWrapColumn'
});
assertWrapping(config, false, 80);
});
test('wordWrap wordWrapColumn uses wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'wordWrapColumn',
wordWrapColumn: 100
});
assertWrapping(config, false, 100);
});
test('wordWrap wordWrapColumn validates wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'wordWrapColumn',
wordWrapColumn: -1
});
assertWrapping(config, false, 1);
});
test('wordWrap bounded uses default wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'bounded'
});
assertWrapping(config, true, 80);
});
test('wordWrap bounded uses wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'bounded',
wordWrapColumn: 40
});
assertWrapping(config, true, 40);
});
test('wordWrap bounded validates wordWrapColumn', () => {
let config = new TestWrappingConfiguration({
wordWrap: 'bounded',
wordWrapColumn: -1
});
assertWrapping(config, true, 1);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,512 @@
/*---------------------------------------------------------------------------------------------
* 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 { Cursor } from 'vs/editor/common/controller/cursor';
import { Position } from 'vs/editor/common/core/position';
import { Model } from 'vs/editor/common/model/model';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { CursorMove } from 'vs/editor/common/controller/cursorMoveCommands';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { CoreNavigationCommands } from 'vs/editor/common/controller/coreCommands';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
suite('Cursor move command test', () => {
let thisModel: Model;
let thisConfiguration: TestConfiguration;
let thisViewModel: ViewModel;
let thisCursor: Cursor;
setup(() => {
let text = [
' \tMy First Line\t ',
'\tMy Second Line',
' Third Line🐶',
'',
'1'
].join('\n');
thisModel = Model.createFromString(text);
thisConfiguration = new TestConfiguration(null);
thisViewModel = new ViewModel(0, thisConfiguration, thisModel, null);
thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel);
});
teardown(() => {
thisCursor.dispose();
thisViewModel.dispose();
thisModel.dispose();
thisConfiguration.dispose();
});
test('move left should move to left character', () => {
moveTo(thisCursor, 1, 8);
moveLeft(thisCursor);
cursorEqual(thisCursor, 1, 7);
});
test('move left should move to left by n characters', () => {
moveTo(thisCursor, 1, 8);
moveLeft(thisCursor, 3);
cursorEqual(thisCursor, 1, 5);
});
test('move left should move to left by half line', () => {
moveTo(thisCursor, 1, 8);
moveLeft(thisCursor, 1, CursorMove.RawUnit.HalfLine);
cursorEqual(thisCursor, 1, 1);
});
test('move left moves to previous line', () => {
moveTo(thisCursor, 2, 3);
moveLeft(thisCursor, 10);
cursorEqual(thisCursor, 1, 21);
});
test('move right should move to right character', () => {
moveTo(thisCursor, 1, 5);
moveRight(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
test('move right should move to right by n characters', () => {
moveTo(thisCursor, 1, 2);
moveRight(thisCursor, 6);
cursorEqual(thisCursor, 1, 8);
});
test('move right should move to right by half line', () => {
moveTo(thisCursor, 1, 4);
moveRight(thisCursor, 1, CursorMove.RawUnit.HalfLine);
cursorEqual(thisCursor, 1, 14);
});
test('move right moves to next line', () => {
moveTo(thisCursor, 1, 8);
moveRight(thisCursor, 100);
cursorEqual(thisCursor, 2, 1);
});
test('move to first character of line from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineStart(thisCursor);
cursorEqual(thisCursor, 1, 1);
});
test('move to first character of line from first non white space character', () => {
moveTo(thisCursor, 1, 6);
moveToLineStart(thisCursor);
cursorEqual(thisCursor, 1, 1);
});
test('move to first character of line from first character', () => {
moveTo(thisCursor, 1, 1);
moveToLineStart(thisCursor);
cursorEqual(thisCursor, 1, 1);
});
test('move to first non white space character of line from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
test('move to first non white space character of line from first non white space character', () => {
moveTo(thisCursor, 1, 6);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
test('move to first non white space character of line from first character', () => {
moveTo(thisCursor, 1, 1);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
test('move to end of line from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineEnd(thisCursor);
cursorEqual(thisCursor, 1, 21);
});
test('move to end of line from last non white space character', () => {
moveTo(thisCursor, 1, 19);
moveToLineEnd(thisCursor);
cursorEqual(thisCursor, 1, 21);
});
test('move to end of line from line end', () => {
moveTo(thisCursor, 1, 21);
moveToLineEnd(thisCursor);
cursorEqual(thisCursor, 1, 21);
});
test('move to last non white space character from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
test('move to last non white space character from last non white space character', () => {
moveTo(thisCursor, 1, 19);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
test('move to last non white space character from line end', () => {
moveTo(thisCursor, 1, 21);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
test('move to center of line not from center', () => {
moveTo(thisCursor, 1, 8);
moveToLineCenter(thisCursor);
cursorEqual(thisCursor, 1, 11);
});
test('move to center of line from center', () => {
moveTo(thisCursor, 1, 11);
moveToLineCenter(thisCursor);
cursorEqual(thisCursor, 1, 11);
});
test('move to center of line from start', () => {
moveToLineStart(thisCursor);
moveToLineCenter(thisCursor);
cursorEqual(thisCursor, 1, 11);
});
test('move to center of line from end', () => {
moveToLineEnd(thisCursor);
moveToLineCenter(thisCursor);
cursorEqual(thisCursor, 1, 11);
});
test('move up by cursor move command', () => {
moveTo(thisCursor, 3, 5);
cursorEqual(thisCursor, 3, 5);
moveUp(thisCursor, 2);
cursorEqual(thisCursor, 1, 5);
moveUp(thisCursor, 1);
cursorEqual(thisCursor, 1, 1);
});
test('move up by model line cursor move command', () => {
moveTo(thisCursor, 3, 5);
cursorEqual(thisCursor, 3, 5);
moveUpByModelLine(thisCursor, 2);
cursorEqual(thisCursor, 1, 5);
moveUpByModelLine(thisCursor, 1);
cursorEqual(thisCursor, 1, 1);
});
test('move down by model line cursor move command', () => {
moveTo(thisCursor, 3, 5);
cursorEqual(thisCursor, 3, 5);
moveDownByModelLine(thisCursor, 2);
cursorEqual(thisCursor, 5, 2);
moveDownByModelLine(thisCursor, 1);
cursorEqual(thisCursor, 5, 2);
});
test('move up with selection by cursor move command', () => {
moveTo(thisCursor, 3, 5);
cursorEqual(thisCursor, 3, 5);
moveUp(thisCursor, 1, true);
cursorEqual(thisCursor, 2, 2, 3, 5);
moveUp(thisCursor, 1, true);
cursorEqual(thisCursor, 1, 5, 3, 5);
});
test('move up and down with tabs by cursor move command', () => {
moveTo(thisCursor, 1, 5);
cursorEqual(thisCursor, 1, 5);
moveDown(thisCursor, 4);
cursorEqual(thisCursor, 5, 2);
moveUp(thisCursor, 1);
cursorEqual(thisCursor, 4, 1);
moveUp(thisCursor, 1);
cursorEqual(thisCursor, 3, 5);
moveUp(thisCursor, 1);
cursorEqual(thisCursor, 2, 2);
moveUp(thisCursor, 1);
cursorEqual(thisCursor, 1, 5);
});
test('move up and down with end of lines starting from a long one by cursor move command', () => {
moveToEndOfLine(thisCursor);
cursorEqual(thisCursor, 1, 21);
moveToEndOfLine(thisCursor);
cursorEqual(thisCursor, 1, 21);
moveDown(thisCursor, 2);
cursorEqual(thisCursor, 3, 17);
moveDown(thisCursor, 1);
cursorEqual(thisCursor, 4, 1);
moveDown(thisCursor, 1);
cursorEqual(thisCursor, 5, 2);
moveUp(thisCursor, 4);
cursorEqual(thisCursor, 1, 21);
});
test('move to view top line moves to first visible line if it is first line', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);
moveTo(thisCursor, 2, 2);
moveToTop(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
test('move to view top line moves to top visible line when first line is not visible', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1);
moveTo(thisCursor, 4, 1);
moveToTop(thisCursor);
cursorEqual(thisCursor, 2, 2);
});
test('move to view top line moves to nth line from top', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);
moveTo(thisCursor, 4, 1);
moveToTop(thisCursor, 3);
cursorEqual(thisCursor, 3, 5);
});
test('move to view top line moves to last line if n is greater than last visible line number', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1);
moveTo(thisCursor, 2, 2);
moveToTop(thisCursor, 4);
cursorEqual(thisCursor, 3, 5);
});
test('move to view center line moves to the center line', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1);
moveTo(thisCursor, 2, 2);
moveToCenter(thisCursor);
cursorEqual(thisCursor, 3, 5);
});
test('move to view bottom line moves to last visible line if it is last line', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);
moveTo(thisCursor, 2, 2);
moveToBottom(thisCursor);
cursorEqual(thisCursor, 5, 1);
});
test('move to view bottom line moves to last visible line when last line is not visible', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1);
moveTo(thisCursor, 2, 2);
moveToBottom(thisCursor);
cursorEqual(thisCursor, 3, 5);
});
test('move to view bottom line moves to nth line from bottom', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);
moveTo(thisCursor, 4, 1);
moveToBottom(thisCursor, 3);
cursorEqual(thisCursor, 3, 5);
});
test('move to view bottom line moves to first line if n is lesser than first visible line number', () => {
thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1);
moveTo(thisCursor, 4, 1);
moveToBottom(thisCursor, 5);
cursorEqual(thisCursor, 2, 2);
});
});
// Move command
function move(cursor: Cursor, args: any) {
CoreNavigationCommands.CursorMove.runCoreEditorCommand(cursor, args);
}
function moveToLineStart(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineStart });
}
function moveToLineFirstNonWhiteSpaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter });
}
function moveToLineCenter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineColumnCenter });
}
function moveToLineEnd(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd });
}
function moveToLineLastNonWhiteSpaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter });
}
function moveLeft(cursor: Cursor, value?: number, by?: string, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select });
}
function moveRight(cursor: Cursor, value?: number, by?: string, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select });
}
function moveUp(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
}
function moveUpByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select });
}
function moveDown(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
}
function moveDownByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select });
}
function moveToTop(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select });
}
function moveToCenter(cursor: Cursor, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.ViewPortCenter, select: select });
}
function moveToBottom(cursor: Cursor, noOfLines: number = 1, select?: boolean) {
move(cursor, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select });
}
function cursorEqual(cursor: Cursor, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) {
positionEqual(cursor.getPosition(), posLineNumber, posColumn);
selectionEqual(cursor.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn);
}
function positionEqual(position: Position, lineNumber: number, column: number) {
assert.deepEqual(position, new Position(lineNumber, column), 'position equal');
}
function selectionEqual(selection: Selection, posLineNumber: number, posColumn: number, selLineNumber: number, selColumn: number) {
assert.deepEqual({
selectionStartLineNumber: selection.selectionStartLineNumber,
selectionStartColumn: selection.selectionStartColumn,
positionLineNumber: selection.positionLineNumber,
positionColumn: selection.positionColumn
}, {
selectionStartLineNumber: selLineNumber,
selectionStartColumn: selColumn,
positionLineNumber: posLineNumber,
positionColumn: posColumn
}, 'selection equal');
}
function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) {
if (inSelectionMode) {
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(cursor, {
position: new Position(lineNumber, column)
});
} else {
CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, {
position: new Position(lineNumber, column)
});
}
}
function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) {
if (inSelectionMode) {
CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(cursor, {});
} else {
CoreNavigationCommands.CursorEnd.runCoreEditorCommand(cursor, {});
}
}

View File

@@ -0,0 +1,178 @@
/*---------------------------------------------------------------------------------------------
* 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 { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
suite('CursorMove', () => {
test('nextTabStop', () => {
assert.equal(CursorColumns.nextTabStop(0, 4), 4);
assert.equal(CursorColumns.nextTabStop(1, 4), 4);
assert.equal(CursorColumns.nextTabStop(2, 4), 4);
assert.equal(CursorColumns.nextTabStop(3, 4), 4);
assert.equal(CursorColumns.nextTabStop(4, 4), 8);
assert.equal(CursorColumns.nextTabStop(5, 4), 8);
assert.equal(CursorColumns.nextTabStop(6, 4), 8);
assert.equal(CursorColumns.nextTabStop(7, 4), 8);
assert.equal(CursorColumns.nextTabStop(8, 4), 12);
assert.equal(CursorColumns.nextTabStop(0, 2), 2);
assert.equal(CursorColumns.nextTabStop(1, 2), 2);
assert.equal(CursorColumns.nextTabStop(2, 2), 4);
assert.equal(CursorColumns.nextTabStop(3, 2), 4);
assert.equal(CursorColumns.nextTabStop(4, 2), 6);
assert.equal(CursorColumns.nextTabStop(5, 2), 6);
assert.equal(CursorColumns.nextTabStop(6, 2), 8);
assert.equal(CursorColumns.nextTabStop(7, 2), 8);
assert.equal(CursorColumns.nextTabStop(8, 2), 10);
assert.equal(CursorColumns.nextTabStop(0, 1), 1);
assert.equal(CursorColumns.nextTabStop(1, 1), 2);
assert.equal(CursorColumns.nextTabStop(2, 1), 3);
assert.equal(CursorColumns.nextTabStop(3, 1), 4);
assert.equal(CursorColumns.nextTabStop(4, 1), 5);
assert.equal(CursorColumns.nextTabStop(5, 1), 6);
assert.equal(CursorColumns.nextTabStop(6, 1), 7);
assert.equal(CursorColumns.nextTabStop(7, 1), 8);
assert.equal(CursorColumns.nextTabStop(8, 1), 9);
});
test('visibleColumnFromColumn', () => {
function testVisibleColumnFromColumn(text: string, tabSize: number, column: number, expected: number): void {
assert.equal(CursorColumns.visibleColumnFromColumn(text, column, tabSize), expected);
}
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 1, 0);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 2, 4);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 3, 8);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 4, 9);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 5, 10);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 6, 11);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 7, 12);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 8, 13);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 9, 14);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 10, 15);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 11, 16);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 12, 17);
testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 13, 18);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 1, 0);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 2, 4);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 3, 5);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 4, 8);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 5, 9);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 6, 10);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 7, 11);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 8, 12);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 9, 13);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 10, 14);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 11, 15);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 12, 16);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 13, 17);
testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 14, 18);
testVisibleColumnFromColumn('\t \tx\t', 4, -1, 0);
testVisibleColumnFromColumn('\t \tx\t', 4, 0, 0);
testVisibleColumnFromColumn('\t \tx\t', 4, 1, 0);
testVisibleColumnFromColumn('\t \tx\t', 4, 2, 4);
testVisibleColumnFromColumn('\t \tx\t', 4, 3, 5);
testVisibleColumnFromColumn('\t \tx\t', 4, 4, 6);
testVisibleColumnFromColumn('\t \tx\t', 4, 5, 8);
testVisibleColumnFromColumn('\t \tx\t', 4, 6, 9);
testVisibleColumnFromColumn('\t \tx\t', 4, 7, 12);
testVisibleColumnFromColumn('\t \tx\t', 4, 8, 12);
testVisibleColumnFromColumn('\t \tx\t', 4, 9, 12);
testVisibleColumnFromColumn('baz', 4, 1, 0);
testVisibleColumnFromColumn('baz', 4, 2, 1);
testVisibleColumnFromColumn('baz', 4, 3, 2);
testVisibleColumnFromColumn('baz', 4, 4, 3);
testVisibleColumnFromColumn('📚az', 4, 1, 0);
testVisibleColumnFromColumn('📚az', 4, 2, 1);
testVisibleColumnFromColumn('📚az', 4, 3, 2);
testVisibleColumnFromColumn('📚az', 4, 4, 3);
testVisibleColumnFromColumn('📚az', 4, 5, 4);
});
test('columnFromVisibleColumn', () => {
function testColumnFromVisibleColumn(text: string, tabSize: number, visibleColumn: number, expected: number): void {
assert.equal(CursorColumns.columnFromVisibleColumn(text, visibleColumn, tabSize), expected);
}
// testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 0, 1);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 1, 1);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 2, 1);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 3, 2);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 4, 2);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 5, 2);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 6, 2);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 7, 3);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 8, 3);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 9, 4);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 10, 5);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 11, 6);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 12, 7);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 13, 8);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 14, 9);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 15, 10);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 16, 11);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 17, 12);
testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 18, 13);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 0, 1);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 1, 1);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 2, 1);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 3, 2);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 4, 2);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 5, 3);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 6, 3);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 7, 4);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 8, 4);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 9, 5);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 10, 6);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 11, 7);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 12, 8);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 13, 9);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 14, 10);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 15, 11);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 16, 12);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 17, 13);
testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 18, 14);
testColumnFromVisibleColumn('\t \tx\t', 4, -2, 1);
testColumnFromVisibleColumn('\t \tx\t', 4, -1, 1);
testColumnFromVisibleColumn('\t \tx\t', 4, 0, 1);
testColumnFromVisibleColumn('\t \tx\t', 4, 1, 1);
testColumnFromVisibleColumn('\t \tx\t', 4, 2, 1);
testColumnFromVisibleColumn('\t \tx\t', 4, 3, 2);
testColumnFromVisibleColumn('\t \tx\t', 4, 4, 2);
testColumnFromVisibleColumn('\t \tx\t', 4, 5, 3);
testColumnFromVisibleColumn('\t \tx\t', 4, 6, 4);
testColumnFromVisibleColumn('\t \tx\t', 4, 7, 4);
testColumnFromVisibleColumn('\t \tx\t', 4, 8, 5);
testColumnFromVisibleColumn('\t \tx\t', 4, 9, 6);
testColumnFromVisibleColumn('\t \tx\t', 4, 10, 6);
testColumnFromVisibleColumn('\t \tx\t', 4, 11, 7);
testColumnFromVisibleColumn('\t \tx\t', 4, 12, 7);
testColumnFromVisibleColumn('\t \tx\t', 4, 13, 7);
testColumnFromVisibleColumn('\t \tx\t', 4, 14, 7);
testColumnFromVisibleColumn('baz', 4, 0, 1);
testColumnFromVisibleColumn('baz', 4, 1, 2);
testColumnFromVisibleColumn('baz', 4, 2, 3);
testColumnFromVisibleColumn('baz', 4, 3, 4);
testColumnFromVisibleColumn('📚az', 4, 0, 1);
testColumnFromVisibleColumn('📚az', 4, 1, 2);
testColumnFromVisibleColumn('📚az', 4, 2, 3);
testColumnFromVisibleColumn('📚az', 4, 3, 4);
testColumnFromVisibleColumn('📚az', 4, 4, 5);
});
});

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* 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 { CharacterClassifier } from 'vs/editor/common/core/characterClassifier';
import { CharCode } from 'vs/base/common/charCode';
suite('CharacterClassifier', () => {
test('works', () => {
let classifier = new CharacterClassifier<number>(0);
assert.equal(classifier.get(-1), 0);
assert.equal(classifier.get(0), 0);
assert.equal(classifier.get(CharCode.a), 0);
assert.equal(classifier.get(CharCode.b), 0);
assert.equal(classifier.get(CharCode.z), 0);
assert.equal(classifier.get(255), 0);
assert.equal(classifier.get(1000), 0);
assert.equal(classifier.get(2000), 0);
classifier.set(CharCode.a, 1);
classifier.set(CharCode.z, 2);
classifier.set(1000, 3);
assert.equal(classifier.get(-1), 0);
assert.equal(classifier.get(0), 0);
assert.equal(classifier.get(CharCode.a), 1);
assert.equal(classifier.get(CharCode.b), 0);
assert.equal(classifier.get(CharCode.z), 2);
assert.equal(classifier.get(255), 0);
assert.equal(classifier.get(1000), 3);
assert.equal(classifier.get(2000), 0);
});
});

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import { ICommonCodeEditor, IModel } from 'vs/editor/common/editorCommon';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
interface IStubEditorState {
model?: { uri?: URI, version?: number };
position?: Position;
selection?: Selection;
scroll?: { left?: number, top?: number };
}
suite('Editor Core - Editor State', () => {
const allFlags = (
CodeEditorStateFlag.Value
| CodeEditorStateFlag.Selection
| CodeEditorStateFlag.Position
| CodeEditorStateFlag.Scroll
);
test('empty editor state should be valid', () => {
let result = validate({}, {});
assert.equal(result, true);
});
test('different model URIs should be invalid', () => {
let result = validate(
{ model: { uri: URI.parse('http://test1') } },
{ model: { uri: URI.parse('http://test2') } }
);
assert.equal(result, false);
});
test('different model versions should be invalid', () => {
let result = validate(
{ model: { version: 1 } },
{ model: { version: 2 } }
);
assert.equal(result, false);
});
test('different positions should be invalid', () => {
let result = validate(
{ position: new Position(1, 2) },
{ position: new Position(2, 3) }
);
assert.equal(result, false);
});
test('different selections should be invalid', () => {
let result = validate(
{ selection: new Selection(1, 2, 3, 4) },
{ selection: new Selection(5, 2, 3, 4) }
);
assert.equal(result, false);
});
test('different scroll positions should be invalid', () => {
let result = validate(
{ scroll: { left: 1, top: 2 } },
{ scroll: { left: 3, top: 2 } }
);
assert.equal(result, false);
});
function validate(source: IStubEditorState, target: IStubEditorState) {
let sourceEditor = createEditor(source),
targetEditor = createEditor(target);
let result = new EditorState(sourceEditor, allFlags).validate(targetEditor);
return result;
}
function createEditor({ model, position, selection, scroll }: IStubEditorState = {}): ICommonCodeEditor {
let mappedModel = model ? { uri: model.uri ? model.uri : URI.parse('http://dummy.org'), getVersionId: () => model.version } : null;
return <any>{
getModel: (): IModel => <any>mappedModel,
getPosition: (): Position => position,
getSelection: (): Selection => selection,
getScrollLeft: (): number => scroll && scroll.left,
getScrollTop: (): number => scroll && scroll.top
};
}
});

View File

@@ -0,0 +1,292 @@
/*---------------------------------------------------------------------------------------------
* 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 { LineTokens } from 'vs/editor/common/core/lineTokens';
import { MetadataConsts } from 'vs/editor/common/modes';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
suite('LineTokens', () => {
interface ILineToken {
startIndex: number;
foreground: number;
}
function createLineTokens(text: string, tokens: ILineToken[]): LineTokens {
let binTokens = new Uint32Array(tokens.length << 1);
for (let i = 0, len = tokens.length; i < len; i++) {
let token = tokens[i];
binTokens[(i << 1)] = token.startIndex;
binTokens[(i << 1) + 1] = (
token.foreground << MetadataConsts.FOREGROUND_OFFSET
) >>> 0;
}
return new LineTokens(binTokens, text);
}
function createTestLineTokens(): LineTokens {
return createLineTokens(
'Hello world, this is a lovely day',
[
{ startIndex: 0, foreground: 1 }, // Hello_
{ startIndex: 6, foreground: 2 }, // world,_
{ startIndex: 13, foreground: 3 }, // this_
{ startIndex: 18, foreground: 4 }, // is_
{ startIndex: 21, foreground: 5 }, // a_
{ startIndex: 23, foreground: 6 }, // lovely_
{ startIndex: 30, foreground: 7 }, // day
]
);
}
test('basics', () => {
const lineTokens = createTestLineTokens();
assert.equal(lineTokens.getLineContent(), 'Hello world, this is a lovely day');
assert.equal(lineTokens.getLineLength(), 33);
assert.equal(lineTokens.getTokenCount(), 7);
assert.equal(lineTokens.getTokenStartOffset(0), 0);
assert.equal(lineTokens.getTokenEndOffset(0), 6);
assert.equal(lineTokens.getTokenStartOffset(1), 6);
assert.equal(lineTokens.getTokenEndOffset(1), 13);
assert.equal(lineTokens.getTokenStartOffset(2), 13);
assert.equal(lineTokens.getTokenEndOffset(2), 18);
assert.equal(lineTokens.getTokenStartOffset(3), 18);
assert.equal(lineTokens.getTokenEndOffset(3), 21);
assert.equal(lineTokens.getTokenStartOffset(4), 21);
assert.equal(lineTokens.getTokenEndOffset(4), 23);
assert.equal(lineTokens.getTokenStartOffset(5), 23);
assert.equal(lineTokens.getTokenEndOffset(5), 30);
assert.equal(lineTokens.getTokenStartOffset(6), 30);
assert.equal(lineTokens.getTokenEndOffset(6), 33);
});
test('findToken', () => {
const lineTokens = createTestLineTokens();
assert.equal(lineTokens.findTokenIndexAtOffset(0), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(1), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(2), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(3), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(4), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(5), 0);
assert.equal(lineTokens.findTokenIndexAtOffset(6), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(7), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(8), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(9), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(10), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(11), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(12), 1);
assert.equal(lineTokens.findTokenIndexAtOffset(13), 2);
assert.equal(lineTokens.findTokenIndexAtOffset(14), 2);
assert.equal(lineTokens.findTokenIndexAtOffset(15), 2);
assert.equal(lineTokens.findTokenIndexAtOffset(16), 2);
assert.equal(lineTokens.findTokenIndexAtOffset(17), 2);
assert.equal(lineTokens.findTokenIndexAtOffset(18), 3);
assert.equal(lineTokens.findTokenIndexAtOffset(19), 3);
assert.equal(lineTokens.findTokenIndexAtOffset(20), 3);
assert.equal(lineTokens.findTokenIndexAtOffset(21), 4);
assert.equal(lineTokens.findTokenIndexAtOffset(22), 4);
assert.equal(lineTokens.findTokenIndexAtOffset(23), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(24), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(25), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(26), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(27), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(28), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(29), 5);
assert.equal(lineTokens.findTokenIndexAtOffset(30), 6);
assert.equal(lineTokens.findTokenIndexAtOffset(31), 6);
assert.equal(lineTokens.findTokenIndexAtOffset(32), 6);
assert.equal(lineTokens.findTokenIndexAtOffset(33), 6);
assert.equal(lineTokens.findTokenIndexAtOffset(34), 6);
assert.equal(lineTokens.findTokenAtOffset(7).startOffset, 6);
assert.equal(lineTokens.findTokenAtOffset(7).endOffset, 13);
assert.equal(lineTokens.findTokenAtOffset(7).foregroundId, 2);
assert.equal(lineTokens.findTokenAtOffset(30).startOffset, 30);
assert.equal(lineTokens.findTokenAtOffset(30).endOffset, 33);
assert.equal(lineTokens.findTokenAtOffset(30).foregroundId, 7);
});
test('iterate forward', () => {
const lineTokens = createTestLineTokens();
let token = lineTokens.firstToken();
assert.equal(token.startOffset, 0);
assert.equal(token.endOffset, 6);
assert.equal(token.foregroundId, 1);
token = token.next();
assert.equal(token.startOffset, 6);
assert.equal(token.endOffset, 13);
assert.equal(token.foregroundId, 2);
token = token.next();
assert.equal(token.startOffset, 13);
assert.equal(token.endOffset, 18);
assert.equal(token.foregroundId, 3);
token = token.next();
assert.equal(token.startOffset, 18);
assert.equal(token.endOffset, 21);
assert.equal(token.foregroundId, 4);
token = token.next();
assert.equal(token.startOffset, 21);
assert.equal(token.endOffset, 23);
assert.equal(token.foregroundId, 5);
token = token.next();
assert.equal(token.startOffset, 23);
assert.equal(token.endOffset, 30);
assert.equal(token.foregroundId, 6);
token = token.next();
assert.equal(token.startOffset, 30);
assert.equal(token.endOffset, 33);
assert.equal(token.foregroundId, 7);
token = token.next();
assert.equal(token, null);
});
test('iterate backward', () => {
const lineTokens = createTestLineTokens();
let token = lineTokens.lastToken();
assert.equal(token.startOffset, 30);
assert.equal(token.endOffset, 33);
assert.equal(token.foregroundId, 7);
token = token.prev();
assert.equal(token.startOffset, 23);
assert.equal(token.endOffset, 30);
assert.equal(token.foregroundId, 6);
token = token.prev();
assert.equal(token.startOffset, 21);
assert.equal(token.endOffset, 23);
assert.equal(token.foregroundId, 5);
token = token.prev();
assert.equal(token.startOffset, 18);
assert.equal(token.endOffset, 21);
assert.equal(token.foregroundId, 4);
token = token.prev();
assert.equal(token.startOffset, 13);
assert.equal(token.endOffset, 18);
assert.equal(token.foregroundId, 3);
token = token.prev();
assert.equal(token.startOffset, 6);
assert.equal(token.endOffset, 13);
assert.equal(token.foregroundId, 2);
token = token.prev();
assert.equal(token.startOffset, 0);
assert.equal(token.endOffset, 6);
assert.equal(token.foregroundId, 1);
token = token.prev();
assert.equal(token, null);
});
interface ITestViewLineToken {
endIndex: number;
foreground: number;
}
function assertViewLineTokens(actual: ViewLineToken[], expected: ITestViewLineToken[]): void {
assert.deepEqual(actual.map(token => {
return {
endIndex: token.endIndex,
foreground: token.getForeground()
};
}), expected);
}
test('inflate', () => {
const lineTokens = createTestLineTokens();
assertViewLineTokens(lineTokens.inflate(), [
{ endIndex: 6, foreground: 1 },
{ endIndex: 13, foreground: 2 },
{ endIndex: 18, foreground: 3 },
{ endIndex: 21, foreground: 4 },
{ endIndex: 23, foreground: 5 },
{ endIndex: 30, foreground: 6 },
{ endIndex: 33, foreground: 7 },
]);
});
test('sliceAndInflate', () => {
const lineTokens = createTestLineTokens();
assertViewLineTokens(lineTokens.sliceAndInflate(0, 33, 0), [
{ endIndex: 6, foreground: 1 },
{ endIndex: 13, foreground: 2 },
{ endIndex: 18, foreground: 3 },
{ endIndex: 21, foreground: 4 },
{ endIndex: 23, foreground: 5 },
{ endIndex: 30, foreground: 6 },
{ endIndex: 33, foreground: 7 },
]);
assertViewLineTokens(lineTokens.sliceAndInflate(0, 32, 0), [
{ endIndex: 6, foreground: 1 },
{ endIndex: 13, foreground: 2 },
{ endIndex: 18, foreground: 3 },
{ endIndex: 21, foreground: 4 },
{ endIndex: 23, foreground: 5 },
{ endIndex: 30, foreground: 6 },
{ endIndex: 32, foreground: 7 },
]);
assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 0), [
{ endIndex: 6, foreground: 1 },
{ endIndex: 13, foreground: 2 },
{ endIndex: 18, foreground: 3 },
{ endIndex: 21, foreground: 4 },
{ endIndex: 23, foreground: 5 },
{ endIndex: 30, foreground: 6 }
]);
assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 1), [
{ endIndex: 7, foreground: 1 },
{ endIndex: 14, foreground: 2 },
{ endIndex: 19, foreground: 3 },
{ endIndex: 22, foreground: 4 },
{ endIndex: 24, foreground: 5 },
{ endIndex: 31, foreground: 6 }
]);
assertViewLineTokens(lineTokens.sliceAndInflate(6, 18, 0), [
{ endIndex: 7, foreground: 2 },
{ endIndex: 12, foreground: 3 }
]);
assertViewLineTokens(lineTokens.sliceAndInflate(7, 18, 0), [
{ endIndex: 6, foreground: 2 },
{ endIndex: 11, foreground: 3 }
]);
assertViewLineTokens(lineTokens.sliceAndInflate(6, 17, 0), [
{ endIndex: 7, foreground: 2 },
{ endIndex: 11, foreground: 3 }
]);
assertViewLineTokens(lineTokens.sliceAndInflate(6, 19, 0), [
{ endIndex: 7, foreground: 2 },
{ endIndex: 12, foreground: 3 },
{ endIndex: 13, foreground: 4 },
]);
});
});

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* 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 { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
suite('Editor Core - Range', () => {
test('empty range', () => {
var s = new Range(1, 1, 1, 1);
assert.equal(s.startLineNumber, 1);
assert.equal(s.startColumn, 1);
assert.equal(s.endLineNumber, 1);
assert.equal(s.endColumn, 1);
assert.equal(s.isEmpty(), true);
});
test('swap start and stop same line', () => {
var s = new Range(1, 2, 1, 1);
assert.equal(s.startLineNumber, 1);
assert.equal(s.startColumn, 1);
assert.equal(s.endLineNumber, 1);
assert.equal(s.endColumn, 2);
assert.equal(s.isEmpty(), false);
});
test('swap start and stop', () => {
var s = new Range(2, 1, 1, 2);
assert.equal(s.startLineNumber, 1);
assert.equal(s.startColumn, 2);
assert.equal(s.endLineNumber, 2);
assert.equal(s.endColumn, 1);
assert.equal(s.isEmpty(), false);
});
test('no swap same line', () => {
var s = new Range(1, 1, 1, 2);
assert.equal(s.startLineNumber, 1);
assert.equal(s.startColumn, 1);
assert.equal(s.endLineNumber, 1);
assert.equal(s.endColumn, 2);
assert.equal(s.isEmpty(), false);
});
test('no swap', () => {
var s = new Range(1, 1, 2, 1);
assert.equal(s.startLineNumber, 1);
assert.equal(s.startColumn, 1);
assert.equal(s.endLineNumber, 2);
assert.equal(s.endColumn, 1);
assert.equal(s.isEmpty(), false);
});
test('compareRangesUsingEnds', () => {
var a, b;
a = new Range(1, 1, 1, 3);
b = new Range(1, 2, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start < b.start, a.end < b.end');
a = new Range(1, 1, 1, 3);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start = b.start, a.end < b.end');
a = new Range(1, 2, 1, 3);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start > b.start, a.end < b.end');
a = new Range(1, 1, 1, 4);
b = new Range(1, 2, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start < b.start, a.end = b.end');
a = new Range(1, 1, 1, 4);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) === 0, 'a.start = b.start, a.end = b.end');
a = new Range(1, 2, 1, 4);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start > b.start, a.end = b.end');
a = new Range(1, 1, 1, 5);
b = new Range(1, 2, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start < b.start, a.end > b.end');
a = new Range(1, 1, 2, 4);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end');
a = new Range(1, 1, 5, 1);
b = new Range(1, 1, 1, 4);
assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end');
});
test('containsPosition', () => {
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(1, 3)), false);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 1)), false);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 2)), true);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 3)), true);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(3, 1)), true);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 9)), true);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 10)), true);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 11)), false);
assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(6, 1)), false);
});
test('containsRange', () => {
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(1, 3, 2, 2)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 1, 2, 2)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 5, 11)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 6, 1)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(5, 9, 6, 1)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(5, 10, 6, 1)), false);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 5, 10)), true);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 3, 5, 9)), true);
assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(3, 100, 4, 100)), true);
});
});

View File

@@ -0,0 +1,677 @@
/*---------------------------------------------------------------------------------------------
* 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 { DiffComputer } from 'vs/editor/common/diff/diffComputer';
import { IChange, ICharChange, ILineChange } from 'vs/editor/common/editorCommon';
function extractCharChangeRepresentation(change: ICharChange, expectedChange: ICharChange): ICharChange {
var hasOriginal = expectedChange && expectedChange.originalStartLineNumber > 0;
var hasModified = expectedChange && expectedChange.modifiedStartLineNumber > 0;
return {
originalStartLineNumber: hasOriginal ? change.originalStartLineNumber : 0,
originalStartColumn: hasOriginal ? change.originalStartColumn : 0,
originalEndLineNumber: hasOriginal ? change.originalEndLineNumber : 0,
originalEndColumn: hasOriginal ? change.originalEndColumn : 0,
modifiedStartLineNumber: hasModified ? change.modifiedStartLineNumber : 0,
modifiedStartColumn: hasModified ? change.modifiedStartColumn : 0,
modifiedEndLineNumber: hasModified ? change.modifiedEndLineNumber : 0,
modifiedEndColumn: hasModified ? change.modifiedEndColumn : 0,
};
}
function extractLineChangeRepresentation(change: ILineChange, expectedChange: ILineChange): IChange | ILineChange {
if (change.charChanges) {
let charChanges: ICharChange[] = [];
for (let i = 0; i < change.charChanges.length; i++) {
charChanges.push(
extractCharChangeRepresentation(
change.charChanges[i],
expectedChange && expectedChange.charChanges && i < expectedChange.charChanges.length ? expectedChange.charChanges[i] : null
)
);
}
return {
originalStartLineNumber: change.originalStartLineNumber,
originalEndLineNumber: change.originalEndLineNumber,
modifiedStartLineNumber: change.modifiedStartLineNumber,
modifiedEndLineNumber: change.modifiedEndLineNumber,
charChanges: charChanges
};
}
return {
originalStartLineNumber: change.originalStartLineNumber,
originalEndLineNumber: change.originalEndLineNumber,
modifiedStartLineNumber: change.modifiedStartLineNumber,
modifiedEndLineNumber: change.modifiedEndLineNumber
};
}
function assertDiff(originalLines: string[], modifiedLines: string[], expectedChanges: IChange[], shouldPostProcessCharChanges: boolean = false, shouldIgnoreTrimWhitespace: boolean = false) {
var diffComputer = new DiffComputer(originalLines, modifiedLines, {
shouldPostProcessCharChanges: shouldPostProcessCharChanges || false,
shouldIgnoreTrimWhitespace: shouldIgnoreTrimWhitespace || false,
shouldConsiderTrimWhitespaceInEmptyCase: true,
shouldMakePrettyDiff: true
});
var changes = diffComputer.computeDiff();
var extracted = [];
for (var i = 0; i < changes.length; i++) {
extracted.push(extractLineChangeRepresentation(changes[i], <ILineChange>(i < expectedChanges.length ? expectedChanges[i] : null)));
}
assert.deepEqual(extracted, expectedChanges);
}
function createLineDeletion(startLineNumber: number, endLineNumber: number, modifiedLineNumber: number): IChange {
return {
originalStartLineNumber: startLineNumber,
originalEndLineNumber: endLineNumber,
modifiedStartLineNumber: modifiedLineNumber,
modifiedEndLineNumber: 0
};
}
function createLineInsertion(startLineNumber: number, endLineNumber: number, originalLineNumber: number): IChange {
return {
originalStartLineNumber: originalLineNumber,
originalEndLineNumber: 0,
modifiedStartLineNumber: startLineNumber,
modifiedEndLineNumber: endLineNumber
};
}
function createLineChange(originalStartLineNumber: number, originalEndLineNumber: number, modifiedStartLineNumber: number, modifiedEndLineNumber: number, charChanges: ICharChange[]): ILineChange {
return {
originalStartLineNumber: originalStartLineNumber,
originalEndLineNumber: originalEndLineNumber,
modifiedStartLineNumber: modifiedStartLineNumber,
modifiedEndLineNumber: modifiedEndLineNumber,
charChanges: charChanges
};
}
function createCharInsertion(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) {
return {
originalStartLineNumber: 0,
originalStartColumn: 0,
originalEndLineNumber: 0,
originalEndColumn: 0,
modifiedStartLineNumber: startLineNumber,
modifiedStartColumn: startColumn,
modifiedEndLineNumber: endLineNumber,
modifiedEndColumn: endColumn
};
}
function createCharDeletion(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) {
return {
originalStartLineNumber: startLineNumber,
originalStartColumn: startColumn,
originalEndLineNumber: endLineNumber,
originalEndColumn: endColumn,
modifiedStartLineNumber: 0,
modifiedStartColumn: 0,
modifiedEndLineNumber: 0,
modifiedEndColumn: 0
};
}
function createCharChange(
originalStartLineNumber: number, originalStartColumn: number, originalEndLineNumber: number, originalEndColumn: number,
modifiedStartLineNumber: number, modifiedStartColumn: number, modifiedEndLineNumber: number, modifiedEndColumn: number
) {
return {
originalStartLineNumber: originalStartLineNumber,
originalStartColumn: originalStartColumn,
originalEndLineNumber: originalEndLineNumber,
originalEndColumn: originalEndColumn,
modifiedStartLineNumber: modifiedStartLineNumber,
modifiedStartColumn: modifiedStartColumn,
modifiedEndLineNumber: modifiedEndLineNumber,
modifiedEndColumn: modifiedEndColumn
};
}
suite('Editor Diff - DiffComputer', () => {
// ---- insertions
test('one inserted line below', () => {
var original = ['line'];
var modified = ['line', 'new line'];
var expected = [createLineInsertion(2, 2, 1)];
assertDiff(original, modified, expected);
});
test('two inserted lines below', () => {
var original = ['line'];
var modified = ['line', 'new line', 'another new line'];
var expected = [createLineInsertion(2, 3, 1)];
assertDiff(original, modified, expected);
});
test('one inserted line above', () => {
var original = ['line'];
var modified = ['new line', 'line'];
var expected = [createLineInsertion(1, 1, 0)];
assertDiff(original, modified, expected);
});
test('two inserted lines above', () => {
var original = ['line'];
var modified = ['new line', 'another new line', 'line'];
var expected = [createLineInsertion(1, 2, 0)];
assertDiff(original, modified, expected);
});
test('one inserted line in middle', () => {
var original = ['line1', 'line2', 'line3', 'line4'];
var modified = ['line1', 'line2', 'new line', 'line3', 'line4'];
var expected = [createLineInsertion(3, 3, 2)];
assertDiff(original, modified, expected);
});
test('two inserted lines in middle', () => {
var original = ['line1', 'line2', 'line3', 'line4'];
var modified = ['line1', 'line2', 'new line', 'another new line', 'line3', 'line4'];
var expected = [createLineInsertion(3, 4, 2)];
assertDiff(original, modified, expected);
});
test('two inserted lines in middle interrupted', () => {
var original = ['line1', 'line2', 'line3', 'line4'];
var modified = ['line1', 'line2', 'new line', 'line3', 'another new line', 'line4'];
var expected = [createLineInsertion(3, 3, 2), createLineInsertion(5, 5, 3)];
assertDiff(original, modified, expected);
});
// ---- deletions
test('one deleted line below', () => {
var original = ['line', 'new line'];
var modified = ['line'];
var expected = [createLineDeletion(2, 2, 1)];
assertDiff(original, modified, expected);
});
test('two deleted lines below', () => {
var original = ['line', 'new line', 'another new line'];
var modified = ['line'];
var expected = [createLineDeletion(2, 3, 1)];
assertDiff(original, modified, expected);
});
test('one deleted lines above', () => {
var original = ['new line', 'line'];
var modified = ['line'];
var expected = [createLineDeletion(1, 1, 0)];
assertDiff(original, modified, expected);
});
test('two deleted lines above', () => {
var original = ['new line', 'another new line', 'line'];
var modified = ['line'];
var expected = [createLineDeletion(1, 2, 0)];
assertDiff(original, modified, expected);
});
test('one deleted line in middle', () => {
var original = ['line1', 'line2', 'new line', 'line3', 'line4'];
var modified = ['line1', 'line2', 'line3', 'line4'];
var expected = [createLineDeletion(3, 3, 2)];
assertDiff(original, modified, expected);
});
test('two deleted lines in middle', () => {
var original = ['line1', 'line2', 'new line', 'another new line', 'line3', 'line4'];
var modified = ['line1', 'line2', 'line3', 'line4'];
var expected = [createLineDeletion(3, 4, 2)];
assertDiff(original, modified, expected);
});
test('two deleted lines in middle interrupted', () => {
var original = ['line1', 'line2', 'new line', 'line3', 'another new line', 'line4'];
var modified = ['line1', 'line2', 'line3', 'line4'];
var expected = [createLineDeletion(3, 3, 2), createLineDeletion(5, 5, 3)];
assertDiff(original, modified, expected);
});
// ---- changes
test('one line changed: chars inserted at the end', () => {
var original = ['line'];
var modified = ['line changed'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharInsertion(1, 5, 1, 13)
])
];
assertDiff(original, modified, expected);
});
test('one line changed: chars inserted at the beginning', () => {
var original = ['line'];
var modified = ['my line'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharInsertion(1, 1, 1, 4)
])
];
assertDiff(original, modified, expected);
});
test('one line changed: chars inserted in the middle', () => {
var original = ['abba'];
var modified = ['abzzba'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharInsertion(1, 3, 1, 5)
])
];
assertDiff(original, modified, expected);
});
test('one line changed: chars inserted in the middle (two spots)', () => {
var original = ['abba'];
var modified = ['abzzbzza'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharInsertion(1, 3, 1, 5),
createCharInsertion(1, 6, 1, 8)
])
];
assertDiff(original, modified, expected);
});
test('one line changed: chars deleted 1', () => {
var original = ['abcdefg'];
var modified = ['abcfg'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharDeletion(1, 4, 1, 6)
])
];
assertDiff(original, modified, expected);
});
test('one line changed: chars deleted 2', () => {
var original = ['abcdefg'];
var modified = ['acfg'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharDeletion(1, 2, 1, 3),
createCharDeletion(1, 4, 1, 6)
])
];
assertDiff(original, modified, expected);
});
test('two lines changed 1', () => {
var original = ['abcd', 'efgh'];
var modified = ['abcz'];
var expected = [
createLineChange(1, 2, 1, 1, [
createCharChange(1, 4, 2, 5, 1, 4, 1, 5)
])
];
assertDiff(original, modified, expected);
});
test('two lines changed 2', () => {
var original = ['foo', 'abcd', 'efgh', 'BAR'];
var modified = ['foo', 'abcz', 'BAR'];
var expected = [
createLineChange(2, 3, 2, 2, [
createCharChange(2, 4, 3, 5, 2, 4, 2, 5)
])
];
assertDiff(original, modified, expected);
});
test('two lines changed 3', () => {
var original = ['foo', 'abcd', 'efgh', 'BAR'];
var modified = ['foo', 'abcz', 'zzzzefgh', 'BAR'];
var expected = [
createLineChange(2, 3, 2, 3, [
createCharChange(2, 4, 2, 5, 2, 4, 3, 5)
])
];
assertDiff(original, modified, expected);
});
test('three lines changed', () => {
var original = ['foo', 'abcd', 'efgh', 'BAR'];
var modified = ['foo', 'zzzefgh', 'xxx', 'BAR'];
var expected = [
createLineChange(2, 3, 2, 3, [
createCharChange(2, 1, 2, 5, 2, 1, 2, 4),
createCharInsertion(3, 1, 3, 4)
])
];
assertDiff(original, modified, expected);
});
test('big change part 1', () => {
var original = ['foo', 'abcd', 'efgh', 'BAR'];
var modified = ['hello', 'foo', 'zzzefgh', 'xxx', 'BAR'];
var expected = [
createLineInsertion(1, 1, 0),
createLineChange(2, 3, 3, 4, [
createCharChange(2, 1, 2, 5, 3, 1, 3, 4),
createCharInsertion(4, 1, 4, 4)
])
];
assertDiff(original, modified, expected);
});
test('big change part 2', () => {
var original = ['foo', 'abcd', 'efgh', 'BAR', 'RAB'];
var modified = ['hello', 'foo', 'zzzefgh', 'xxx', 'BAR'];
var expected = [
createLineInsertion(1, 1, 0),
createLineChange(2, 3, 3, 4, [
createCharChange(2, 1, 2, 5, 3, 1, 3, 4),
createCharInsertion(4, 1, 4, 4)
]),
createLineDeletion(5, 5, 5)
];
assertDiff(original, modified, expected);
});
test('char change postprocessing merges', () => {
var original = ['abba'];
var modified = ['azzzbzzzbzzza'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharChange(1, 2, 1, 4, 1, 2, 1, 13)
])
];
assertDiff(original, modified, expected, true);
});
test('ignore trim whitespace', () => {
var original = ['\t\t foo ', 'abcd', 'efgh', '\t\t BAR\t\t'];
var modified = [' hello\t', '\t foo \t', 'zzzefgh', 'xxx', ' BAR \t'];
var expected = [
createLineInsertion(1, 1, 0),
createLineChange(2, 3, 3, 4, [
createCharChange(2, 1, 2, 5, 3, 1, 3, 4),
createCharInsertion(4, 1, 4, 4)
])
];
assertDiff(original, modified, expected, false, true);
});
test('issue #12122 r.hasOwnProperty is not a function', () => {
var original = ['hasOwnProperty'];
var modified = ['hasOwnProperty', 'and another line'];
var expected = [
createLineInsertion(2, 2, 1)
];
assertDiff(original, modified, expected);
});
test('empty diff 1', () => {
var original = [''];
var modified = ['something'];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharChange(0, 0, 0, 0, 0, 0, 0, 0)
])
];
assertDiff(original, modified, expected, false, true);
});
test('empty diff 2', () => {
var original = [''];
var modified = ['something', 'something else'];
var expected = [
createLineChange(1, 1, 1, 2, [
createCharChange(0, 0, 0, 0, 0, 0, 0, 0)
])
];
assertDiff(original, modified, expected, false, true);
});
test('empty diff 3', () => {
var original = ['something', 'something else'];
var modified = [''];
var expected = [
createLineChange(1, 2, 1, 1, [
createCharChange(0, 0, 0, 0, 0, 0, 0, 0)
])
];
assertDiff(original, modified, expected, false, true);
});
test('empty diff 4', () => {
var original = ['something'];
var modified = [''];
var expected = [
createLineChange(1, 1, 1, 1, [
createCharChange(0, 0, 0, 0, 0, 0, 0, 0)
])
];
assertDiff(original, modified, expected, false, true);
});
test('pretty diff 1', () => {
var original = [
'suite(function () {',
' test1() {',
' assert.ok(true);',
' }',
'',
' test2() {',
' assert.ok(true);',
' }',
'});',
'',
];
var modified = [
'// An insertion',
'suite(function () {',
' test1() {',
' assert.ok(true);',
' }',
'',
' test2() {',
' assert.ok(true);',
' }',
'',
' test3() {',
' assert.ok(true);',
' }',
'});',
'',
];
var expected = [
createLineInsertion(1, 1, 0),
createLineInsertion(10, 13, 8)
];
assertDiff(original, modified, expected, false, true);
});
test('pretty diff 2', () => {
var original = [
'// Just a comment',
'',
'function compute(a, b, c, d) {',
' if (a) {',
' if (b) {',
' if (c) {',
' return 5;',
' }',
' }',
' // These next lines will be deleted',
' if (d) {',
' return -1;',
' }',
' return 0;',
' }',
'}',
];
var modified = [
'// Here is an inserted line',
'// and another inserted line',
'// and another one',
'// Just a comment',
'',
'function compute(a, b, c, d) {',
' if (a) {',
' if (b) {',
' if (c) {',
' return 5;',
' }',
' }',
' return 0;',
' }',
'}',
];
var expected = [
createLineInsertion(1, 3, 0),
createLineDeletion(10, 13, 12),
];
assertDiff(original, modified, expected, false, true);
});
test('pretty diff 3', () => {
var original = [
'class A {',
' /**',
' * m1',
' */',
' method1() {}',
'',
' /**',
' * m3',
' */',
' method3() {}',
'}',
];
var modified = [
'class A {',
' /**',
' * m1',
' */',
' method1() {}',
'',
' /**',
' * m2',
' */',
' method2() {}',
'',
' /**',
' * m3',
' */',
' method3() {}',
'}',
];
var expected = [
createLineInsertion(7, 11, 6)
];
assertDiff(original, modified, expected, false, true);
});
test('issue #23636', () => {
let original = [
'if(!TextDrawLoad[playerid])',
'{',
'',
' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);',
' TextDrawHideForPlayer(playerid,TD_AppleJob[4]);',
' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[5+i]);',
' }',
' else',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[15+i]);',
' }',
'}',
'else',
'{',
' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);',
' TextDrawHideForPlayer(playerid,TD_AppleJob[27]);',
' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[28+i]);',
' }',
' else',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[38+i]);',
' }',
'}',
];
let modified = [
' if(!TextDrawLoad[playerid])',
' {',
' ',
' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);',
' TextDrawHideForPlayer(playerid,TD_AppleJob[4]);',
' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[5+i]);',
' }',
' else',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[15+i]);',
' }',
' }',
' else',
' {',
' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);',
' TextDrawHideForPlayer(playerid,TD_AppleJob[27]);',
' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[28+i]);',
' }',
' else',
' {',
' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[38+i]);',
' }',
' }',
];
var expected = [
createLineChange(
1, 27, 1, 27,
[
createCharChange(1, 1, 1, 1, 1, 1, 1, 2),
createCharChange(2, 1, 2, 1, 2, 1, 2, 2),
createCharChange(3, 1, 3, 1, 3, 1, 3, 2),
createCharChange(4, 1, 4, 1, 4, 1, 4, 2),
createCharChange(5, 1, 5, 1, 5, 1, 5, 2),
createCharChange(6, 1, 6, 1, 6, 1, 6, 2),
createCharChange(7, 1, 7, 1, 7, 1, 7, 2),
createCharChange(8, 1, 8, 1, 8, 1, 8, 2),
createCharChange(9, 1, 9, 1, 9, 1, 9, 2),
createCharChange(10, 1, 10, 1, 10, 1, 10, 2),
createCharChange(11, 1, 11, 1, 11, 1, 11, 2),
createCharChange(12, 1, 12, 1, 12, 1, 12, 2),
createCharChange(13, 1, 13, 1, 13, 1, 13, 2),
createCharChange(14, 1, 14, 1, 14, 1, 14, 2),
createCharChange(15, 1, 15, 1, 15, 1, 15, 2),
createCharChange(16, 1, 16, 1, 16, 1, 16, 2),
createCharChange(17, 1, 17, 1, 17, 1, 17, 2),
createCharChange(18, 1, 18, 1, 18, 1, 18, 2),
createCharChange(19, 1, 19, 1, 19, 1, 19, 2),
createCharChange(20, 1, 20, 1, 20, 1, 20, 2),
createCharChange(21, 1, 21, 1, 21, 1, 21, 2),
createCharChange(22, 1, 22, 1, 22, 1, 22, 2),
createCharChange(23, 1, 23, 1, 23, 1, 23, 2),
createCharChange(24, 1, 24, 1, 24, 1, 24, 2),
createCharChange(25, 1, 25, 1, 25, 1, 25, 2),
createCharChange(26, 1, 26, 1, 26, 1, 26, 2),
createCharChange(27, 1, 27, 1, 27, 1, 27, 2),
]
)
// createLineInsertion(7, 11, 6)
];
assertDiff(original, modified, expected, true, false);
});
});

View File

@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* 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 { Model } from 'vs/editor/common/model/model';
export function withEditorModel(text: string[], callback: (model: Model) => void): void {
var model = Model.createFromString(text.join('\n'));
callback(model);
model.dispose();
}

View File

@@ -0,0 +1,111 @@
/*---------------------------------------------------------------------------------------------
* 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 { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { CommonCodeEditor } from 'vs/editor/common/commonCodeEditor';
import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { Cursor } from 'vs/editor/common/controller/cursor';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { IDisposable } from 'vs/base/common/lifecycle';
export class MockCodeEditor extends CommonCodeEditor {
protected _createConfiguration(options: editorOptions.IEditorOptions): CommonEditorConfiguration {
return new TestConfiguration(options);
}
public layout(dimension?: editorCommon.IDimension): void { }
public focus(): void { }
public isFocused(): boolean { return true; }
public hasWidgetFocus(): boolean { return true; };
protected _enableEmptySelectionClipboard(): boolean { return false; }
protected _scheduleAtNextAnimationFrame(callback: () => void): IDisposable { throw new Error('Notimplemented'); }
protected _createView(): void { }
protected _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void { throw new Error('NotImplemented'); }
protected _removeDecorationType(key: string): void { throw new Error('NotImplemented'); }
protected _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions { throw new Error('NotImplemented'); }
// --- test utils
getCursor(): Cursor {
return this.cursor;
}
public registerAndInstantiateContribution<T extends editorCommon.IEditorContribution>(ctor: any): T {
let r = <T>this._instantiationService.createInstance(ctor, this);
this._contributions[r.getId()] = r;
return r;
}
public dispose() {
super.dispose();
if (this.model) {
this.model.dispose();
}
this._contextKeyService.dispose();
}
}
export class MockScopeLocation implements IContextKeyServiceTarget {
parentElement: IContextKeyServiceTarget = null;
setAttribute(attr: string, value: string): void { }
removeAttribute(attr: string): void { }
hasAttribute(attr: string): boolean { return false; }
getAttribute(attr: string): string { return undefined; }
}
export interface MockCodeEditorCreationOptions extends editorOptions.IEditorOptions {
/**
* The initial model associated with this code editor.
*/
model?: editorCommon.IModel;
serviceCollection?: ServiceCollection;
}
export function withMockCodeEditor(text: string[], options: MockCodeEditorCreationOptions, callback: (editor: MockCodeEditor, cursor: Cursor) => void): void {
// create a model if necessary and remember it in order to dispose it.
let modelToDispose: Model = null;
if (!options.model) {
modelToDispose = Model.createFromString(text.join('\n'));
options.model = modelToDispose;
}
let editor = <MockCodeEditor>_mockCodeEditor(options);
callback(editor, editor.getCursor());
if (modelToDispose) {
modelToDispose.dispose();
}
editor.dispose();
}
export function mockCodeEditor(text: string[], options: MockCodeEditorCreationOptions): CommonCodeEditor {
// TODO: who owns this model now?
if (!options.model) {
options.model = Model.createFromString(text.join('\n'));
}
return _mockCodeEditor(options);
}
function _mockCodeEditor(options: MockCodeEditorCreationOptions): CommonCodeEditor {
let contextKeyService = new MockContextKeyService();
let services = options.serviceCollection || new ServiceCollection();
services.set(IContextKeyService, contextKeyService);
let instantiationService = new InstantiationService(services);
let editor = new MockCodeEditor(new MockScopeLocation(), options, instantiationService, contextKeyService);
editor.setModel(options.model);
return editor;
}

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* 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 { IDecorationRenderOptions, IModelDecorationOptions } from 'vs/editor/common/editorCommon';
import { AbstractCodeEditorService } from 'vs/editor/common/services/abstractCodeEditorService';
export class MockCodeEditorService extends AbstractCodeEditorService {
public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { }
public removeDecorationType(key: string): void { }
public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { return null; }
}

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* 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 { Disposable } from 'vs/base/common/lifecycle';
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
export class MockMode extends Disposable implements IMode {
private _languageIdentifier: LanguageIdentifier;
constructor(languageIdentifier: LanguageIdentifier) {
super();
this._languageIdentifier = languageIdentifier;
}
public getId(): string {
return this._languageIdentifier.language;
}
public getLanguageIdentifier(): LanguageIdentifier {
return this._languageIdentifier;
}
}

View File

@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* 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 { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { AccessibilitySupport } from 'vs/base/common/platform';
export class TestConfiguration extends CommonEditorConfiguration {
constructor(opts: IEditorOptions) {
super(opts);
this._recomputeOptions();
}
protected _getEnvConfiguration(): IEnvConfiguration {
return {
extraEditorClassName: '',
outerWidth: 100,
outerHeight: 100,
emptySelectionClipboard: true,
pixelRatio: 1,
zoomLevel: 0,
accessibilitySupport: AccessibilitySupport.Unknown
};
}
protected readConfiguration(styling: BareFontInfo): FontInfo {
return new FontInfo({
zoomLevel: 0,
fontFamily: 'mockFont',
fontWeight: 'normal',
fontSize: 14,
lineHeight: 19,
letterSpacing: 1.5,
isMonospace: true,
typicalHalfwidthCharacterWidth: 10,
typicalFullwidthCharacterWidth: 20,
spaceWidth: 10,
maxDigitWidth: 10,
}, true);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
/*---------------------------------------------------------------------------------------------
* 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 { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils';
import { CharCode } from 'vs/base/common/charCode';
const GENERATE_TESTS = false;
suite('EditorModel Auto Tests', () => {
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
};
}
test('auto1', () => {
testApplyEditsWithSyncedModels(
[
'ioe',
'',
'yjct',
'',
'',
],
[
editOp(1, 2, 1, 2, ['b', 'r', 'fq']),
editOp(1, 4, 2, 1, ['', '']),
],
[
'ib',
'r',
'fqoe',
'',
'yjct',
'',
'',
]
);
});
test('auto2', () => {
testApplyEditsWithSyncedModels(
[
'f',
'littnhskrq',
'utxvsizqnk',
'lslqz',
'jxn',
'gmm',
],
[
editOp(1, 2, 1, 2, ['', 'o']),
editOp(2, 4, 2, 4, ['zaq', 'avb']),
editOp(2, 5, 6, 2, ['jlr', 'zl', 'j']),
],
[
'f',
'o',
'litzaq',
'avbtjlr',
'zl',
'jmm',
]
);
});
test('auto3', () => {
testApplyEditsWithSyncedModels(
[
'ofw',
'qsxmziuvzw',
'rp',
'qsnymek',
'elth',
'wmgzbwudxz',
'iwsdkndh',
'bujlbwb',
'asuouxfv',
'xuccnb',
],
[
editOp(4, 3, 4, 3, ['']),
],
[
'ofw',
'qsxmziuvzw',
'rp',
'qsnymek',
'elth',
'wmgzbwudxz',
'iwsdkndh',
'bujlbwb',
'asuouxfv',
'xuccnb',
]
);
});
test('auto4', () => {
testApplyEditsWithSyncedModels(
[
'fefymj',
'qum',
'vmiwxxaiqq',
'dz',
'lnqdgorosf',
],
[
editOp(1, 3, 1, 5, ['hp']),
editOp(1, 7, 2, 1, ['kcg', '', 'mpx']),
editOp(2, 2, 2, 2, ['', 'aw', '']),
editOp(2, 2, 2, 2, ['vqr', 'mo']),
editOp(4, 2, 5, 3, ['xyc']),
],
[
'fehpmjkcg',
'',
'mpxq',
'aw',
'vqr',
'moum',
'vmiwxxaiqq',
'dxycqdgorosf',
]
);
});
});
function getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomString(minLength: number, maxLength: number): string {
let length = getRandomInt(minLength, maxLength);
let r = '';
for (let i = 0; i < length; i++) {
r += String.fromCharCode(getRandomInt(CharCode.a, CharCode.z));
}
return r;
}
function generateFile(small: boolean): string {
let lineCount = getRandomInt(1, small ? 3 : 10);
let lines: string[] = [];
for (let i = 0; i < lineCount; i++) {
lines.push(getRandomString(0, small ? 3 : 10));
}
return lines.join('\n');
}
function generateEdits(content: string): ITestModelEdit[] {
let result: ITestModelEdit[] = [];
let cnt = getRandomInt(1, 5);
let maxOffset = content.length;
while (cnt > 0 && maxOffset > 0) {
let offset = getRandomInt(0, maxOffset);
let length = getRandomInt(0, maxOffset - offset);
let text = generateFile(true);
result.push({
offset: offset,
length: length,
text: text
});
maxOffset = offset;
cnt--;
}
result.reverse();
return result;
}
interface ITestModelEdit {
offset: number;
length: number;
text: string;
}
class TestModel {
public initialContent: string;
public resultingContent: string;
public edits: IIdentifiedSingleEditOperation[];
private static _generateOffsetToPosition(content: string): Position[] {
let result: Position[] = [];
let lineNumber = 1;
let column = 1;
for (let offset = 0, len = content.length; offset <= len; offset++) {
let ch = content.charAt(offset);
result[offset] = new Position(lineNumber, column);
if (ch === '\n') {
lineNumber++;
column = 1;
} else {
column++;
}
}
return result;
}
constructor() {
this.initialContent = generateFile(false);
let edits = generateEdits(this.initialContent);
let offsetToPosition = TestModel._generateOffsetToPosition(this.initialContent);
this.edits = [];
for (let i = 0; i < edits.length; i++) {
let startPosition = offsetToPosition[edits[i].offset];
let endPosition = offsetToPosition[edits[i].offset + edits[i].length];
this.edits.push({
identifier: null,
range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
text: edits[i].text,
forceMoveMarkers: false
});
}
this.resultingContent = this.initialContent;
for (let i = edits.length - 1; i >= 0; i--) {
this.resultingContent = (
this.resultingContent.substring(0, edits[i].offset) +
edits[i].text +
this.resultingContent.substring(edits[i].offset + edits[i].length)
);
}
}
public print(): string {
let r: string[] = [];
r.push('testApplyEditsWithSyncedModels(');
r.push('\t[');
let initialLines = this.initialContent.split('\n');
r = r.concat(initialLines.map((i) => `\t\t'${i}',`));
r.push('\t],');
r.push('\t[');
r = r.concat(this.edits.map((i) => {
let text = `['` + i.text.split('\n').join(`', '`) + `']`;
return `\t\teditOp(${i.range.startLineNumber}, ${i.range.startColumn}, ${i.range.endLineNumber}, ${i.range.endColumn}, ${text}),`;
}));
r.push('\t],');
r.push('\t[');
let resultLines = this.resultingContent.split('\n');
r = r.concat(resultLines.map((i) => `\t\t'${i}',`));
r.push('\t]');
r.push(');');
return r.join('\n');
}
}
if (GENERATE_TESTS) {
let number = 1;
while (true) {
console.log('------BEGIN NEW TEST: ' + number);
let testModel = new TestModel();
// console.log(testModel.print());
console.log('------END NEW TEST: ' + (number++));
try {
testApplyEditsWithSyncedModels(
testModel.initialContent.split('\n'),
testModel.edits,
testModel.resultingContent.split('\n')
);
// throw new Error('a');
} catch (err) {
console.log(err);
console.log(testModel.print());
break;
}
// break;
}
}

View File

@@ -0,0 +1,117 @@
/*---------------------------------------------------------------------------------------------
* 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 * as editorCommon from 'vs/editor/common/editorCommon';
import { EditableTextModel } from 'vs/editor/common/model/editableTextModel';
import { MirrorModel } from 'vs/editor/common/model/mirrorModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { Position } from 'vs/editor/common/core/position';
import { RawTextSource } from 'vs/editor/common/model/textSource';
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
export function testApplyEditsWithSyncedModels(original: string[], edits: editorCommon.IIdentifiedSingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void {
var originalStr = original.join('\n');
var expectedStr = expected.join('\n');
assertSyncedModels(originalStr, (model, assertMirrorModels) => {
// Apply edits & collect inverse edits
var inverseEdits = model.applyEdits(edits);
// Assert edits produced expected result
assert.deepEqual(model.getValue(editorCommon.EndOfLinePreference.LF), expectedStr);
assertMirrorModels();
// Apply the inverse edits
var inverseInverseEdits = model.applyEdits(inverseEdits);
// Assert the inverse edits brought back model to original state
assert.deepEqual(model.getValue(editorCommon.EndOfLinePreference.LF), originalStr);
if (!inputEditsAreInvalid) {
// Assert the inverse of the inverse edits are the original edits
assert.deepEqual(inverseInverseEdits, edits);
}
assertMirrorModels();
});
}
const enum AssertDocumentLineMappingDirection {
OffsetToPosition,
PositionToOffset
}
function assertOneDirectionLineMapping(model: TextModel, direction: AssertDocumentLineMappingDirection, msg: string): void {
let allText = model.getValue();
let line = 1, column = 1, previousIsCarriageReturn = false;
for (let offset = 0; offset <= allText.length; offset++) {
// The position coordinate system cannot express the position between \r and \n
let position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));
if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) {
let actualPosition = model.getPositionAt(offset);
assert.equal(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);
} else {
// The position coordinate system cannot express the position between \r and \n
let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0);
let actualOffset = model.getOffsetAt(position);
assert.equal(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());
}
if (allText.charAt(offset) === '\n') {
line++;
column = 1;
} else {
column++;
}
previousIsCarriageReturn = (allText.charAt(offset) === '\r');
}
}
function assertLineMapping(model: TextModel, msg: string): void {
assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.PositionToOffset, msg);
assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.OffsetToPosition, msg);
}
export function assertSyncedModels(text: string, callback: (model: EditableTextModel, assertMirrorModels: () => void) => void, setup: (model: EditableTextModel) => void = null): void {
var model = new EditableTextModel(RawTextSource.fromString(text), TextModel.DEFAULT_CREATION_OPTIONS, null);
model.setEOL(editorCommon.EndOfLineSequence.LF);
assertLineMapping(model, 'model');
if (setup) {
setup(model);
assertLineMapping(model, 'model');
}
var mirrorModel2 = new MirrorModel(null, model.getLinesContent(), model.getEOL(), model.getVersionId());
var mirrorModel2PrevVersionId = model.getVersionId();
model.onDidChangeContent((e: IModelContentChangedEvent) => {
let versionId = e.versionId;
if (versionId < mirrorModel2PrevVersionId) {
console.warn('Model version id did not advance between edits (2)');
}
mirrorModel2PrevVersionId = versionId;
mirrorModel2.onEvents(e);
});
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');
};
callback(model, assertMirrorModels);
model.dispose();
mirrorModel2.dispose();
}

View File

@@ -0,0 +1,122 @@
/*---------------------------------------------------------------------------------------------
* 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 { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/common/model/indentRanges';
export interface IndentRange {
startLineNumber: number;
endLineNumber: number;
indent: number;
}
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([
'A',
' A',
' A',
' A'
], [r(1, 4, 0)]);
});
test('Fold two levels', () => {
assertRanges([
'A',
' A',
' A',
' A',
' A'
], [r(1, 5, 0), r(3, 5, 2)]);
});
test('Fold three levels', () => {
assertRanges([
'A',
' A',
' A',
' A',
'A'
], [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)]);
});
test('Fold decreasing indent', () => {
assertRanges([
' A',
' A',
'A'
], []);
});
test('Fold Java', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' void foo() {',
/* 3*/ ' console.log();',
/* 4*/ ' console.log();',
/* 5*/ ' }',
/* 6*/ '',
/* 7*/ ' void bar() {',
/* 8*/ ' console.log();',
/* 9*/ ' }',
/*10*/ '}',
/*11*/ 'interface B {',
/*12*/ ' void bar();',
/*13*/ '}',
], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)]);
});
test('Fold Javadoc', () => {
assertRanges([
/* 1*/ '/**',
/* 2*/ ' * Comment',
/* 3*/ ' */',
/* 4*/ 'class A {',
/* 5*/ ' void foo() {',
/* 6*/ ' }',
/* 7*/ '}',
], [r(1, 3, 0), r(4, 6, 0)]);
});
test('Fold Whitespace', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '',
/* 3*/ ' void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, 0), r(3, 5, 2)]);
});
test('Fold Tabs', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '\t\t',
/* 3*/ '\tvoid foo() {',
/* 4*/ '\t \t//hello',
/* 5*/ '\t return 0;',
/* 6*/ ' \t}',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, 0), r(3, 5, 4)]);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,290 @@
/*---------------------------------------------------------------------------------------------
* 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 { IDisposable } from 'vs/base/common/lifecycle';
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 { Model } from 'vs/editor/common/model/model';
import * as modes from 'vs/editor/common/modes';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
// --------- utils
suite('Editor Model - Model Modes 1', () => {
let calledFor: string[] = [];
function checkAndClear(arr: string[]) {
assert.deepEqual(calledFor, arr);
calledFor = [];
}
const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => NULL_STATE,
tokenize: undefined,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => {
calledFor.push(line.charAt(0));
return new TokenizationResult2(null, state);
}
};
let thisModel: Model = null;
let languageRegistration: IDisposable = null;
setup(() => {
const TEXT =
'1\r\n' +
'2\n' +
'3\n' +
'4\r\n' +
'5';
const LANGUAGE_ID = 'modelModeTest1';
calledFor = [];
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
});
teardown(() => {
thisModel.dispose();
thisModel = null;
languageRegistration.dispose();
languageRegistration = null;
calledFor = [];
});
test('model calls syntax highlighter 1', () => {
thisModel.forceTokenization(1);
checkAndClear(['1']);
});
test('model calls syntax highlighter 2', () => {
thisModel.forceTokenization(2);
checkAndClear(['1', '2']);
thisModel.forceTokenization(2);
checkAndClear([]);
});
test('model caches states', () => {
thisModel.forceTokenization(1);
checkAndClear(['1']);
thisModel.forceTokenization(2);
checkAndClear(['2']);
thisModel.forceTokenization(3);
checkAndClear(['3']);
thisModel.forceTokenization(4);
checkAndClear(['4']);
thisModel.forceTokenization(5);
checkAndClear(['5']);
thisModel.forceTokenization(5);
checkAndClear([]);
});
test('model invalidates states for one line insert', () => {
thisModel.forceTokenization(5);
checkAndClear(['1', '2', '3', '4', '5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '-')]);
thisModel.forceTokenization(5);
checkAndClear(['-']);
thisModel.forceTokenization(5);
checkAndClear([]);
});
test('model invalidates states for many lines insert', () => {
thisModel.forceTokenization(5);
checkAndClear(['1', '2', '3', '4', '5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '0\n-\n+')]);
assert.equal(thisModel.getLineCount(), 7);
thisModel.forceTokenization(7);
checkAndClear(['0', '-', '+']);
thisModel.forceTokenization(7);
checkAndClear([]);
});
test('model invalidates states for one new line', () => {
thisModel.forceTokenization(5);
checkAndClear(['1', '2', '3', '4', '5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 2), '\n')]);
thisModel.applyEdits([EditOperation.insert(new Position(2, 1), 'a')]);
thisModel.forceTokenization(6);
checkAndClear(['1', 'a']);
});
test('model invalidates states for one line delete', () => {
thisModel.forceTokenization(5);
checkAndClear(['1', '2', '3', '4', '5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 2), '-')]);
thisModel.forceTokenization(5);
checkAndClear(['1']);
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
thisModel.forceTokenization(5);
checkAndClear(['-']);
thisModel.forceTokenization(5);
checkAndClear([]);
});
test('model invalidates states for many lines delete', () => {
thisModel.forceTokenization(5);
checkAndClear(['1', '2', '3', '4', '5']);
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]);
thisModel.forceTokenization(3);
checkAndClear(['3']);
thisModel.forceTokenization(3);
checkAndClear([]);
});
});
suite('Editor Model - Model Modes 2', () => {
class ModelState2 implements modes.IState {
prevLineContent: string;
constructor(prevLineContent: string) {
this.prevLineContent = prevLineContent;
}
clone(): modes.IState {
return new ModelState2(this.prevLineContent);
}
equals(other: modes.IState): boolean {
return (other instanceof ModelState2) && other.prevLineContent === this.prevLineContent;
}
}
const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => new ModelState2(''),
tokenize: undefined,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => {
(<ModelState2>state).prevLineContent = line;
return new TokenizationResult2(null, state);
}
};
function invalidEqual(model: Model, expected: number[]): void {
let actual: number[] = [];
for (let i = 0, len = model.getLineCount(); i < len; i++) {
if (model._lines[i].isInvalid()) {
actual.push(i);
}
}
assert.deepEqual(actual, expected);
}
function stateEqual(state: modes.IState, content: string): void {
assert.equal((<ModelState2>state).prevLineContent, content);
}
function statesEqual(model: Model, states: string[]): void {
var i, len = states.length - 1;
for (i = 0; i < len; i++) {
stateEqual(model._lines[i].getState(), states[i]);
}
stateEqual((<any>model)._lastState, states[len]);
}
let thisModel: Model = null;
let languageRegistration: IDisposable = null;
setup(() => {
const TEXT =
'Line1' + '\r\n' +
'Line2' + '\n' +
'Line3' + '\n' +
'Line4' + '\r\n' +
'Line5';
const LANGUAGE_ID = 'modelModeTest2';
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
});
teardown(() => {
thisModel.dispose();
thisModel = null;
languageRegistration.dispose();
languageRegistration = null;
});
test('getTokensForInvalidLines one text insert', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '-')]);
invalidEqual(thisModel, [0]);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3', 'Line4', 'Line5']);
});
test('getTokensForInvalidLines two text insert', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([
EditOperation.insert(new Position(1, 6), '-'),
EditOperation.insert(new Position(3, 6), '-')
]);
invalidEqual(thisModel, [0, 2]);
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3-', 'Line4', 'Line5']);
});
test('getTokensForInvalidLines one multi-line text insert, one small text insert', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '\nNew line\nAnother new line')]);
thisModel.applyEdits([EditOperation.insert(new Position(5, 6), '-')]);
invalidEqual(thisModel, [0, 4]);
thisModel.forceTokenization(7);
statesEqual(thisModel, ['', 'Line1', 'New line', 'Another new line', 'Line2', 'Line3-', 'Line4', 'Line5']);
});
test('getTokensForInvalidLines one delete text', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 5))]);
invalidEqual(thisModel, [0]);
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', '1', 'Line2', 'Line3', 'Line4', 'Line5']);
});
test('getTokensForInvalidLines one line delete text', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 1))]);
invalidEqual(thisModel, [0]);
statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.forceTokenization(4);
statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']);
});
test('getTokensForInvalidLines multiple lines delete text', () => {
thisModel.forceTokenization(5);
statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']);
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 3))]);
invalidEqual(thisModel, [0]);
statesEqual(thisModel, ['', 'Line3', 'Line4', 'Line5']);
thisModel.forceTokenization(3);
statesEqual(thisModel, ['', 'ne3', 'Line4', 'Line5']);
});
});

View File

@@ -0,0 +1,395 @@
/*---------------------------------------------------------------------------------------------
* 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 { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import {
ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged,
ModelRawLinesDeleted, ModelRawLinesInserted
} from 'vs/editor/common/model/textModelEvents';
import { Model } from 'vs/editor/common/model/model';
// --------- utils
var LINE1 = 'My First Line';
var LINE2 = '\t\tMy Second Line';
var LINE3 = ' Third Line';
var LINE4 = '';
var LINE5 = '1';
suite('Editor Model - Model', () => {
var thisModel: Model;
setup(() => {
var text =
LINE1 + '\r\n' +
LINE2 + '\n' +
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
thisModel = Model.createFromString(text);
});
teardown(() => {
thisModel.dispose();
});
// --------- insert text
test('model getValue', () => {
assert.equal(thisModel.getValue(), 'My First Line\n\t\tMy Second Line\n Third Line\n\n1');
});
test('model insert empty text', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'My First Line');
});
test('model insert text without newline 1', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'foo My First Line');
});
test('model insert text without newline 2', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' foo')]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'My foo First Line');
});
test('model insert text with one newline', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]);
assert.equal(thisModel.getLineCount(), 6);
assert.equal(thisModel.getLineContent(1), 'My new line');
assert.equal(thisModel.getLineContent(2), 'No longer First Line');
});
test('model insert text with two newlines', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nOne more line in the middle\nNo longer')]);
assert.equal(thisModel.getLineCount(), 7);
assert.equal(thisModel.getLineContent(1), 'My new line');
assert.equal(thisModel.getLineContent(2), 'One more line in the middle');
assert.equal(thisModel.getLineContent(3), 'No longer First Line');
});
test('model insert text with many newlines', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), '\n\n\n\n')]);
assert.equal(thisModel.getLineCount(), 9);
assert.equal(thisModel.getLineContent(1), 'My');
assert.equal(thisModel.getLineContent(2), '');
assert.equal(thisModel.getLineContent(3), '');
assert.equal(thisModel.getLineContent(4), '');
assert.equal(thisModel.getLineContent(5), ' First Line');
});
// --------- insert text eventing
test('model insert empty text does not trigger eventing', () => {
thisModel.onDidChangeRawContent((e) => {
assert.ok(false, 'was not expecting event');
});
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]);
});
test('model insert text without newline eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, 'foo My First Line')
],
2,
false,
false
));
});
test('model insert text with one newline eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, 'My new line First Line'),
new ModelRawLineChanged(1, 'My new line'),
new ModelRawLinesInserted(2, 2, 'No longer First Line'),
],
2,
false,
false
));
});
// --------- delete text
test('model delete empty text', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'My First Line');
});
test('model delete text from one line', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'y First Line');
});
test('model delete text from one line 2', () => {
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'a')]);
assert.equal(thisModel.getLineContent(1), 'aMy First Line');
thisModel.applyEdits([EditOperation.delete(new Range(1, 2, 1, 4))]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), 'a First Line');
});
test('model delete all text from a line', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]);
assert.equal(thisModel.getLineCount(), 5);
assert.equal(thisModel.getLineContent(1), '');
});
test('model delete text from two lines', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]);
assert.equal(thisModel.getLineCount(), 4);
assert.equal(thisModel.getLineContent(1), 'My Second Line');
});
test('model delete text from many lines', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]);
assert.equal(thisModel.getLineCount(), 3);
assert.equal(thisModel.getLineContent(1), 'My Third Line');
});
test('model delete everything', () => {
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 5, 2))]);
assert.equal(thisModel.getLineCount(), 1);
assert.equal(thisModel.getLineContent(1), '');
});
// --------- delete text eventing
test('model delete empty text does not trigger eventing', () => {
thisModel.onDidChangeRawContent((e) => {
assert.ok(false, 'was not expecting event');
});
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]);
});
test('model delete text from one line eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, 'y First Line'),
],
2,
false,
false
));
});
test('model delete all text from a line eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, ''),
],
2,
false,
false
));
});
test('model delete text from two lines eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, 'My '),
new ModelRawLineChanged(1, 'My Second Line'),
new ModelRawLinesDeleted(2, 2),
],
2,
false,
false
));
});
test('model delete text from many lines eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]);
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawLineChanged(1, 'My '),
new ModelRawLineChanged(1, 'My Third Line'),
new ModelRawLinesDeleted(2, 3),
],
2,
false,
false
));
});
// --------- getValueInRange
test('getValueInRange', () => {
assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 1)), '');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 2)), 'M');
assert.equal(thisModel.getValueInRange(new Range(1, 2, 1, 3)), 'y');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 14)), 'My First Line');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 1)), 'My First Line\n');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n');
assert.equal(thisModel.getValueInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n Third Line\n');
});
// --------- getValueLengthInRange
test('getValueLengthInRange', () => {
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\n'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n'.length);
assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n Third Line\n'.length);
});
// --------- setValue
test('setValue eventing', () => {
let e: ModelRawContentChangedEvent = null;
thisModel.onDidChangeRawContent((_e) => {
if (e !== null) {
assert.fail();
}
e = _e;
});
thisModel.setValue('new value');
assert.deepEqual(e, new ModelRawContentChangedEvent(
[
new ModelRawFlush()
],
2,
false,
false
));
});
});
// --------- Special Unicode LINE SEPARATOR character
suite('Editor Model - Model Line Separators', () => {
var thisModel: Model;
setup(() => {
var text =
LINE1 + '\u2028' +
LINE2 + '\n' +
LINE3 + '\u2028' +
LINE4 + '\r\n' +
LINE5;
thisModel = Model.createFromString(text);
});
teardown(() => {
thisModel.dispose();
});
test('model getValue', () => {
assert.equal(thisModel.getValue(), 'My First Line\u2028\t\tMy Second Line\n Third Line\u2028\n1');
});
test('model lines', () => {
assert.equal(thisModel.getLineCount(), 3);
});
test('Bug 13333:Model should line break on lonely CR too', () => {
var model = Model.createFromString('Hello\rWorld!\r\nAnother line');
assert.equal(model.getLineCount(), 3);
assert.equal(model.getValue(), 'Hello\r\nWorld!\r\nAnother line');
model.dispose();
});
});
// --------- Words
suite('Editor Model - Words', () => {
var thisModel: Model;
setup(() => {
var text = ['This text has some words. '];
thisModel = Model.createFromString(text.join('\n'));
});
teardown(() => {
thisModel.dispose();
});
test('Get word at position', () => {
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: 'This', startColumn: 1, endColumn: 5 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 2)), { word: 'This', startColumn: 1, endColumn: 5 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 4)), { word: 'This', startColumn: 1, endColumn: 5 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 5)), { word: 'This', startColumn: 1, endColumn: 5 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 6)), { word: 'text', startColumn: 6, endColumn: 10 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 19)), { word: 'some', startColumn: 15, endColumn: 19 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 20)), null);
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 21)), { word: 'words', startColumn: 21, endColumn: 26 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 26)), { word: 'words', startColumn: 21, endColumn: 26 });
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 27)), null);
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 28)), null);
});
});

View File

@@ -0,0 +1,638 @@
/*---------------------------------------------------------------------------------------------
* 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 { 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 { Model } from 'vs/editor/common/model/model';
// --------- utils
interface ILightWeightDecoration2 {
range: Range;
className: string;
}
function modelHasDecorations(model: Model, decorations: ILightWeightDecoration2[]) {
let modelDecorations: ILightWeightDecoration2[] = [];
let actualDecorations = model.getAllDecorations();
for (let i = 0, len = actualDecorations.length; i < len; i++) {
modelDecorations.push({
range: actualDecorations[i].range,
className: actualDecorations[i].options.className
});
}
assert.deepEqual(modelDecorations, decorations, 'Model decorations');
}
function modelHasDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string) {
modelHasDecorations(model, [{
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
className: className
}]);
}
function modelHasNoDecorations(model: Model) {
assert.equal(model.getAllDecorations().length, 0, 'Model has no decoration');
}
function addDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string): string {
return model.changeDecorations((changeAccessor) => {
return changeAccessor.addDecoration(new Range(startLineNumber, startColumn, endLineNumber, endColumn), {
className: className
});
});
}
function lineHasDecorations(model: Model, lineNumber: number, decorations: { start: number; end: number; className: string; }[]) {
var lineDecorations = [];
var decs = model.getLineDecorations(lineNumber);
for (var i = 0, len = decs.length; i < len; i++) {
lineDecorations.push({
start: decs[i].range.startColumn,
end: decs[i].range.endColumn,
className: decs[i].options.className
});
}
assert.deepEqual(lineDecorations, decorations, 'Line decorations');
}
function lineHasNoDecorations(model: Model, lineNumber: number) {
lineHasDecorations(model, lineNumber, []);
}
function lineHasDecoration(model: Model, lineNumber: number, start: number, end: number, className: string) {
lineHasDecorations(model, lineNumber, [{
start: start,
end: end,
className: className
}]);
}
suite('Editor Model - Model Decorations', () => {
var LINE1 = 'My First Line';
var LINE2 = '\t\tMy Second Line';
var LINE3 = ' Third Line';
var LINE4 = '';
var LINE5 = '1';
// --------- Model Decorations
var thisModel: Model;
setup(() => {
var text =
LINE1 + '\r\n' +
LINE2 + '\n' +
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
thisModel = Model.createFromString(text);
});
teardown(() => {
thisModel.dispose();
thisModel = null;
});
test('single character decoration', () => {
addDecoration(thisModel, 1, 1, 1, 2, 'myType');
lineHasDecoration(thisModel, 1, 1, 2, 'myType');
lineHasNoDecorations(thisModel, 2);
lineHasNoDecorations(thisModel, 3);
lineHasNoDecorations(thisModel, 4);
lineHasNoDecorations(thisModel, 5);
});
test('line decoration', () => {
addDecoration(thisModel, 1, 1, 1, 14, 'myType');
lineHasDecoration(thisModel, 1, 1, 14, 'myType');
lineHasNoDecorations(thisModel, 2);
lineHasNoDecorations(thisModel, 3);
lineHasNoDecorations(thisModel, 4);
lineHasNoDecorations(thisModel, 5);
});
test('full line decoration', () => {
addDecoration(thisModel, 1, 1, 2, 1, 'myType');
var line1Decorations = thisModel.getLineDecorations(1);
assert.equal(line1Decorations.length, 1);
assert.equal(line1Decorations[0].options.className, 'myType');
var line2Decorations = thisModel.getLineDecorations(1);
assert.equal(line2Decorations.length, 1);
assert.equal(line2Decorations[0].options.className, 'myType');
lineHasNoDecorations(thisModel, 3);
lineHasNoDecorations(thisModel, 4);
lineHasNoDecorations(thisModel, 5);
});
test('multiple line decoration', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
var line1Decorations = thisModel.getLineDecorations(1);
assert.equal(line1Decorations.length, 1);
assert.equal(line1Decorations[0].options.className, 'myType');
var line2Decorations = thisModel.getLineDecorations(1);
assert.equal(line2Decorations.length, 1);
assert.equal(line2Decorations[0].options.className, 'myType');
var line3Decorations = thisModel.getLineDecorations(1);
assert.equal(line3Decorations.length, 1);
assert.equal(line3Decorations[0].options.className, 'myType');
lineHasNoDecorations(thisModel, 4);
lineHasNoDecorations(thisModel, 5);
});
// --------- removing, changing decorations
test('decoration gets removed', () => {
var decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.removeDecoration(decId);
});
modelHasNoDecorations(thisModel);
});
test('decorations get removed', () => {
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'
}
]);
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.removeDecoration(decId1);
});
modelHasDecorations(thisModel, [
{
range: new Range(1, 2, 3, 1),
className: 'myType2'
}
]);
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.removeDecoration(decId2);
});
modelHasNoDecorations(thisModel);
});
test('decoration range can be changed', () => {
var decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.changeDecorations((changeAccessor) => {
changeAccessor.changeDecoration(decId, new Range(1, 1, 1, 2));
});
modelHasDecoration(thisModel, 1, 1, 1, 2, 'myType');
});
// --------- eventing
test('decorations emit event on add', () => {
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');
});
test('decorations emit event on change', () => {
let listenerCalled = 0;
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));
});
assert.equal(listenerCalled, 1, 'listener called');
});
test('decorations emit event on remove', () => {
let listenerCalled = 0;
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);
});
assert.equal(listenerCalled, 1, 'listener called');
});
test('decorations emit event when inserting one line text before it', () => {
let listenerCalled = 0;
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.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]);
assert.equal(listenerCalled, 1, 'listener called');
});
// --------- editing text & effects on decorations
test('decorations are updated when inserting one line text before it', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]);
modelHasDecoration(thisModel, 1, 8, 3, 2, 'myType');
});
test('decorations are updated when inserting one line text before it 2', () => {
addDecoration(thisModel, 1, 1, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.replace(new Range(1, 1, 1, 1), 'Hallo ')]);
modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType');
});
test('decorations are updated when inserting multiple lines text before it', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo\nI\'m inserting multiple\nlines')]);
modelHasDecoration(thisModel, 3, 7, 5, 2, 'myType');
});
test('decorations change when inserting text after them', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(3, 2), 'Hallo')]);
modelHasDecoration(thisModel, 1, 2, 3, 7, 'myType');
});
test('decorations are updated when inserting text inside', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), 'Hallo ')]);
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
});
test('decorations are updated when inserting text inside 2', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(3, 1), 'Hallo ')]);
modelHasDecoration(thisModel, 1, 2, 3, 8, 'myType');
});
test('decorations are updated when inserting text inside 3', () => {
addDecoration(thisModel, 1, 1, 2, 16, 'myType');
modelHasDecoration(thisModel, 1, 1, 2, 16, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(2, 2), '\n')]);
modelHasDecoration(thisModel, 1, 1, 3, 15, 'myType');
});
test('decorations are updated when inserting multiple lines text inside', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.insert(new Position(1, 3), 'Hallo\nI\'m inserting multiple\nlines')]);
modelHasDecoration(thisModel, 1, 2, 5, 2, 'myType');
});
test('decorations are updated when deleting one line text before it', () => {
addDecoration(thisModel, 1, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType');
});
test('decorations are updated when deleting multiple lines text before it', () => {
addDecoration(thisModel, 2, 2, 3, 2, 'myType');
modelHasDecoration(thisModel, 2, 2, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 1))]);
modelHasDecoration(thisModel, 1, 2, 2, 2, 'myType');
});
test('decorations are updated when deleting multiple lines text before it 2', () => {
addDecoration(thisModel, 2, 3, 3, 2, 'myType');
modelHasDecoration(thisModel, 2, 3, 3, 2, 'myType');
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 2))]);
modelHasDecoration(thisModel, 1, 2, 2, 2, 'myType');
});
test('decorations are updated when deleting text inside', () => {
addDecoration(thisModel, 1, 2, 4, 1, 'myType');
modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType');
thisModel.applyEdits([EditOperation.delete(new Range(1, 3, 2, 1))]);
modelHasDecoration(thisModel, 1, 2, 3, 1, 'myType');
});
test('decorations are updated when deleting text inside 2', () => {
addDecoration(thisModel, 1, 2, 4, 1, 'myType');
modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType');
thisModel.applyEdits([
EditOperation.delete(new Range(1, 1, 1, 2)),
EditOperation.delete(new Range(4, 1, 4, 1))
]);
modelHasDecoration(thisModel, 1, 1, 4, 1, 'myType');
});
test('decorations are updated when deleting multiple lines text', () => {
addDecoration(thisModel, 1, 2, 4, 1, 'myType');
modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType');
thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]);
modelHasDecoration(thisModel, 1, 1, 2, 1, 'myType');
});
});
interface ILightWeightDecoration {
id: string;
range: Range;
}
suite('deltaDecorations', () => {
function decoration(id: string, startLineNumber: number, startColumn: number, endLineNumber: number, endColum: number): ILightWeightDecoration {
return {
id: id,
range: new Range(startLineNumber, startColumn, endLineNumber, endColum)
};
}
function toModelDeltaDecoration(dec: ILightWeightDecoration): IModelDeltaDecoration {
return {
range: dec.range,
options: {
className: dec.id
}
};
}
function strcmp(a: string, b: string): number {
if (a === b) {
return 0;
}
if (a < b) {
return -1;
}
return 1;
}
function readModelDecorations(model: Model, ids: string[]): ILightWeightDecoration[] {
return ids.map((id) => {
return {
range: model.getDecorationRange(id),
id: model.getDecorationOptions(id).className
};
});
}
function testDeltaDecorations(text: string[], decorations: ILightWeightDecoration[], newDecorations: ILightWeightDecoration[]): void {
var model = Model.createFromString(text.join('\n'));
// Add initial decorations & assert they are added
var initialIds = model.deltaDecorations([], decorations.map(toModelDeltaDecoration));
var actualDecorations = readModelDecorations(model, initialIds);
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);
var newIds = model.deltaDecorations(initialIds, newDecorations.map(toModelDeltaDecoration));
var actualNewDecorations = readModelDecorations(model, newIds);
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);
model.dispose();
}
function range(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number): Range {
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
}
test('result respects input', () => {
var model = Model.createFromString([
'Hello world,',
'How are you?'
].join('\n'));
var ids = model.deltaDecorations([], [
toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)),
toModelDeltaDecoration(decoration('b', 2, 1, 2, 13))
]);
assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12));
assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13));
model.dispose();
});
test('deltaDecorations 1', () => {
testDeltaDecorations(
[
'This is a text',
'That has multiple lines',
'And is very friendly',
'Towards testing'
],
[
decoration('a', 1, 1, 1, 2),
decoration('b', 1, 1, 1, 15),
decoration('c', 1, 1, 2, 1),
decoration('d', 1, 1, 2, 24),
decoration('e', 2, 1, 2, 24),
decoration('f', 2, 1, 4, 16)
],
[
decoration('x', 1, 1, 1, 2),
decoration('b', 1, 1, 1, 15),
decoration('c', 1, 1, 2, 1),
decoration('d', 1, 1, 2, 24),
decoration('e', 2, 1, 2, 21),
decoration('f', 2, 17, 4, 16)
]
);
});
test('deltaDecorations 2', () => {
testDeltaDecorations(
[
'This is a text',
'That has multiple lines',
'And is very friendly',
'Towards testing'
],
[
decoration('a', 1, 1, 1, 2),
decoration('b', 1, 2, 1, 3),
decoration('c', 1, 3, 1, 4),
decoration('d', 1, 4, 1, 5),
decoration('e', 1, 5, 1, 6)
],
[
decoration('a', 1, 2, 1, 3),
decoration('b', 1, 3, 1, 4),
decoration('c', 1, 4, 1, 5),
decoration('d', 1, 5, 1, 6)
]
);
});
test('deltaDecorations 3', () => {
testDeltaDecorations(
[
'This is a text',
'That has multiple lines',
'And is very friendly',
'Towards testing'
],
[
decoration('a', 1, 1, 1, 2),
decoration('b', 1, 2, 1, 3),
decoration('c', 1, 3, 1, 4),
decoration('d', 1, 4, 1, 5),
decoration('e', 1, 5, 1, 6)
],
[]
);
});
test('issue #4317: editor.setDecorations doesn\'t update the hover message', () => {
let model = Model.createFromString('Hello world!');
let ids = model.deltaDecorations([], [{
range: {
startLineNumber: 1,
startColumn: 1,
endLineNumber: 100,
endColumn: 1
},
options: {
hoverMessage: { value: 'hello1' }
}
}]);
ids = model.deltaDecorations(ids, [{
range: {
startLineNumber: 1,
startColumn: 1,
endLineNumber: 100,
endColumn: 1
},
options: {
hoverMessage: { value: 'hello2' }
}
}]);
let actualDecoration = model.getDecorationOptions(ids[0]);
assert.deepEqual(actualDecoration.hoverMessage, { value: 'hello2' });
model.dispose();
});
test('model doesn\'t get confused with individual tracked ranges', () => {
var model = Model.createFromString([
'Hello world,',
'How are you?'
].join('\n'));
var trackedRangeId = model.changeDecorations((changeAcessor) => {
return changeAcessor.addDecoration(
{
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 1
}, {
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges
}
);
});
model.changeDecorations((changeAccessor) => {
changeAccessor.removeDecoration(trackedRangeId);
});
var ids = model.deltaDecorations([], [
toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)),
toModelDeltaDecoration(decoration('b', 2, 1, 2, 13))
]);
assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12));
assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13));
ids = model.deltaDecorations(ids, [
toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)),
toModelDeltaDecoration(decoration('b', 2, 1, 2, 13))
]);
assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12));
assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13));
model.dispose();
});
test('issue #16922: Clicking on link doesn\'t seem to do anything', () => {
var model = Model.createFromString([
'Hello world,',
'How are you?',
'Fine.',
'Good.',
].join('\n'));
model.deltaDecorations([], [
{ range: new Range(1, 1, 1, 1), options: { className: '1' } },
{ range: new Range(1, 13, 1, 13), options: { className: '2' } },
{ range: new Range(2, 1, 2, 1), options: { className: '3' } },
{ range: new Range(2, 1, 2, 4), options: { className: '4' } },
{ range: new Range(2, 8, 2, 13), options: { className: '5' } },
{ range: new Range(3, 1, 4, 6), options: { className: '6' } },
{ range: new Range(1, 1, 3, 6), options: { className: 'x1' } },
{ range: new Range(2, 5, 2, 8), options: { className: 'x2' } },
{ range: new Range(1, 1, 2, 8), options: { className: 'x3' } },
{ range: new Range(2, 5, 3, 1), options: { className: 'x4' } },
]);
let inRange = model.getDecorationsInRange(new Range(2, 6, 2, 6));
let inRangeClassNames = inRange.map(d => d.options.className);
inRangeClassNames.sort();
assert.deepEqual(inRangeClassNames, ['x1', 'x2', 'x3', 'x4']);
model.dispose();
});
});

View File

@@ -0,0 +1,178 @@
/*---------------------------------------------------------------------------------------------
* 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 { Range } from 'vs/editor/common/core/range';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
suite('Editor Model - Model Edit Operation', () => {
var LINE1 = 'My First Line';
var LINE2 = '\t\tMy Second Line';
var LINE3 = ' Third Line';
var LINE4 = '';
var LINE5 = '1';
var model: Model;
setup(() => {
var text =
LINE1 + '\r\n' +
LINE2 + '\n' +
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
model = Model.createFromString(text);
});
teardown(() => {
model.dispose();
model = null;
});
function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation {
var range = new Range(
selectionLineNumber,
selectionColumn,
positionLineNumber,
positionColumn
);
return {
identifier: {
major: 0,
minor: 0
},
range: range,
text: text,
forceMoveMarkers: false
};
}
function assertSingleEditOp(singleEditOp: IIdentifiedSingleEditOperation, editedLines: string[]) {
var editOp = [singleEditOp];
var inverseEditOp = model.applyEdits(editOp);
assert.equal(model.getLineCount(), editedLines.length);
for (var i = 0; i < editedLines.length; i++) {
assert.equal(model.getLineContent(i + 1), editedLines[i]);
}
var originalOp = model.applyEdits(inverseEditOp);
assert.equal(model.getLineCount(), 5);
assert.equal(model.getLineContent(1), LINE1);
assert.equal(model.getLineContent(2), LINE2);
assert.equal(model.getLineContent(3), LINE3);
assert.equal(model.getLineContent(4), LINE4);
assert.equal(model.getLineContent(5), LINE5);
assert.deepEqual(originalOp, editOp);
}
test('Insert inline', () => {
assertSingleEditOp(
createSingleEditOp('a', 1, 1),
[
'aMy First Line',
LINE2,
LINE3,
LINE4,
LINE5
]
);
});
test('Replace inline/inline 1', () => {
assertSingleEditOp(
createSingleEditOp(' incredibly awesome', 1, 3),
[
'My incredibly awesome First Line',
LINE2,
LINE3,
LINE4,
LINE5
]
);
});
test('Replace inline/inline 2', () => {
assertSingleEditOp(
createSingleEditOp(' with text at the end.', 1, 14),
[
'My First Line with text at the end.',
LINE2,
LINE3,
LINE4,
LINE5
]
);
});
test('Replace inline/inline 3', () => {
assertSingleEditOp(
createSingleEditOp('My new First Line.', 1, 1, 1, 14),
[
'My new First Line.',
LINE2,
LINE3,
LINE4,
LINE5
]
);
});
test('Replace inline/multi line 1', () => {
assertSingleEditOp(
createSingleEditOp('My new First Line.', 1, 1, 3, 15),
[
'My new First Line.',
LINE4,
LINE5
]
);
});
test('Replace inline/multi line 2', () => {
assertSingleEditOp(
createSingleEditOp('My new First Line.', 1, 2, 3, 15),
[
'MMy new First Line.',
LINE4,
LINE5
]
);
});
test('Replace inline/multi line 3', () => {
assertSingleEditOp(
createSingleEditOp('My new First Line.', 1, 2, 3, 2),
[
'MMy new First Line. Third Line',
LINE4,
LINE5
]
);
});
test('Replace muli line/multi line', () => {
assertSingleEditOp(
createSingleEditOp('1\n2\n3\n4\n', 1, 1),
[
'1',
'2',
'3',
'4',
LINE1,
LINE2,
LINE3,
LINE4,
LINE5
]
);
});
});

View File

@@ -0,0 +1,997 @@
/*---------------------------------------------------------------------------------------------
* 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 { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { TextModel, ITextModelCreationData } from 'vs/editor/common/model/textModel';
import { DefaultEndOfLine, TextModelResolvedOptions } from 'vs/editor/common/editorCommon';
import { RawTextSource } from 'vs/editor/common/model/textSource';
function testGuessIndentation(defaultInsertSpaces: boolean, defaultTabSize: number, expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void {
var m = TextModel.createFromString(
text.join('\n'),
{
tabSize: defaultTabSize,
insertSpaces: defaultInsertSpaces,
detectIndentation: true,
defaultEOL: DefaultEndOfLine.LF,
trimAutoWhitespace: true
}
);
var r = m.getOptions();
m.dispose();
assert.equal(r.insertSpaces, expectedInsertSpaces, msg);
assert.equal(r.tabSize, expectedTabSize, msg);
}
function assertGuess(expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void {
if (typeof expectedInsertSpaces === 'undefined') {
// cannot guess insertSpaces
if (typeof expectedTabSize === 'undefined') {
// cannot guess tabSize
testGuessIndentation(true, 13370, true, 13370, text, msg);
testGuessIndentation(false, 13371, false, 13371, text, msg);
} else {
// can guess tabSize
testGuessIndentation(true, 13370, true, expectedTabSize, text, msg);
testGuessIndentation(false, 13371, false, expectedTabSize, text, msg);
}
} else {
// can guess insertSpaces
if (typeof expectedTabSize === 'undefined') {
// cannot guess tabSize
testGuessIndentation(true, 13370, expectedInsertSpaces, 13370, text, msg);
testGuessIndentation(false, 13371, expectedInsertSpaces, 13371, text, msg);
} else {
// can guess tabSize
testGuessIndentation(true, 13370, expectedInsertSpaces, expectedTabSize, text, msg);
testGuessIndentation(false, 13371, expectedInsertSpaces, expectedTabSize, text, msg);
}
}
}
suite('TextModelData.fromString', () => {
function testTextModelDataFromString(text: string, expected: ITextModelCreationData): void {
const rawTextSource = RawTextSource.fromString(text);
const actual = TextModel.resolveCreationData(rawTextSource, TextModel.DEFAULT_CREATION_OPTIONS);
assert.deepEqual(actual, expected);
}
test('one line text', () => {
testTextModelDataFromString('Hello world!', {
text: {
BOM: '',
EOL: '\n',
length: 12,
'lines': [
'Hello world!'
],
containsRTL: false,
isBasicASCII: true
},
options: new TextModelResolvedOptions({
defaultEOL: DefaultEndOfLine.LF,
insertSpaces: true,
tabSize: 4,
trimAutoWhitespace: true,
})
});
});
test('multiline text', () => {
testTextModelDataFromString('Hello,\r\ndear friend\nHow\rare\r\nyou?', {
text: {
BOM: '',
EOL: '\r\n',
length: 33,
'lines': [
'Hello,',
'dear friend',
'How',
'are',
'you?'
],
containsRTL: false,
isBasicASCII: true
},
options: new TextModelResolvedOptions({
defaultEOL: DefaultEndOfLine.LF,
insertSpaces: true,
tabSize: 4,
trimAutoWhitespace: true,
})
});
});
test('Non Basic ASCII 1', () => {
testTextModelDataFromString('Hello,\nZürich', {
text: {
BOM: '',
EOL: '\n',
length: 13,
'lines': [
'Hello,',
'Zürich'
],
containsRTL: false,
isBasicASCII: false
},
options: new TextModelResolvedOptions({
defaultEOL: DefaultEndOfLine.LF,
insertSpaces: true,
tabSize: 4,
trimAutoWhitespace: true,
})
});
});
test('containsRTL 1', () => {
testTextModelDataFromString('Hello,\nזוהי עובדה מבוססת שדעתו', {
text: {
BOM: '',
EOL: '\n',
length: 30,
'lines': [
'Hello,',
'זוהי עובדה מבוססת שדעתו'
],
containsRTL: true,
isBasicASCII: false
},
options: new TextModelResolvedOptions({
defaultEOL: DefaultEndOfLine.LF,
insertSpaces: true,
tabSize: 4,
trimAutoWhitespace: true,
})
});
});
test('containsRTL 2', () => {
testTextModelDataFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل', {
text: {
BOM: '',
EOL: '\n',
length: 36,
'lines': [
'Hello,',
'هناك حقيقة مثبتة منذ زمن طويل'
],
containsRTL: true,
isBasicASCII: false
},
options: new TextModelResolvedOptions({
defaultEOL: DefaultEndOfLine.LF,
insertSpaces: true,
tabSize: 4,
trimAutoWhitespace: true,
})
});
});
});
suite('Editor Model - TextModel', () => {
test('getValueLengthInRange', () => {
var m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line');
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\r\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1)), 'y First Line\r\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 2)), 'y First Line\r\nM'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1000)), 'y First Line\r\nMy Second Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1)), 'y First Line\r\nMy Second Line\r\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\r\nMy Second Line\r\nMy Third Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\r\nMy Second Line\r\nMy Third Line'.length);
m = TextModel.createFromString('My First Line\nMy Second Line\nMy Third Line');
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1)), 'y First Line\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 2)), 'y First Line\nM'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1000)), 'y First Line\nMy Second Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1)), 'y First Line\nMy Second Line\n'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\nMy Second Line\nMy Third Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\nMy Second Line\nMy Third Line'.length);
});
test('guess indentation 1', () => {
assertGuess(undefined, undefined, [
'x',
'x',
'x',
'x',
'x',
'x',
'x'
], 'no clues');
assertGuess(false, undefined, [
'\tx',
'x',
'x',
'x',
'x',
'x',
'x'
], 'no spaces, 1xTAB');
assertGuess(true, 2, [
' x',
'x',
'x',
'x',
'x',
'x',
'x'
], '1x2');
assertGuess(false, undefined, [
'\tx',
'\tx',
'\tx',
'\tx',
'\tx',
'\tx',
'\tx'
], '7xTAB');
assertGuess(undefined, 2, [
'\tx',
' x',
'\tx',
' x',
'\tx',
' x',
'\tx',
' x',
], '4x2, 4xTAB');
assertGuess(false, undefined, [
'\tx',
' x',
'\tx',
' x',
'\tx',
' x',
'\tx',
' x'
], '4x1, 4xTAB');
assertGuess(false, 2, [
'\tx',
'\tx',
' x',
'\tx',
' x',
'\tx',
' x',
'\tx',
' x',
], '4x2, 5xTAB');
assertGuess(false, 2, [
'\tx',
'\tx',
'x',
'\tx',
'x',
'\tx',
'x',
'\tx',
' x',
], '1x2, 5xTAB');
assertGuess(false, 4, [
'\tx',
'\tx',
'x',
'\tx',
'x',
'\tx',
'x',
'\tx',
' x',
], '1x4, 5xTAB');
assertGuess(false, 2, [
'\tx',
'\tx',
'x',
'\tx',
'x',
'\tx',
' x',
'\tx',
' x',
], '1x2, 1x4, 5xTAB');
assertGuess(undefined, undefined, [
'x',
' x',
' x',
' x',
' x',
' x',
' x',
' x'
], '7x1 - 1 space is never guessed as an indentation');
assertGuess(true, undefined, [
'x',
' x',
' x',
' x',
' x',
' x',
' x',
' x'
], '1x10, 6x1');
assertGuess(undefined, undefined, [
'',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
], 'whitespace lines don\'t count');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], 'odd number is not allowed: 6x3, 3x4');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], 'odd number is not allowed: 6x5, 3x4');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], 'odd number is not allowed: 6x7, 3x4');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
' x',
], '8x2');
assertGuess(true, 2, [
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
], '8x2');
assertGuess(true, 2, [
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
], '4x2, 4x4');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], '6x2, 3x4');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
' x',
], '4x2, 4x4');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], '2x2, 4x4');
assertGuess(true, 4, [
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
'x',
' x',
' x',
], '8x4');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
' x',
], '2x2, 4x4, 2x6');
assertGuess(true, 2, [
'x',
' x',
' x',
' x',
' x',
' x',
' x',
], '1x2, 2x4, 2x6, 1x8');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
' x',
' x',
], '6x4, 2x5, 2x8');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
' x',
' x',
' x',
], '3x4, 1x5, 2x8');
assertGuess(true, 4, [
'x',
'x',
' x',
' x',
' x',
' x',
' x',
'x',
'x',
' x',
' x',
' x',
' x',
' x',
], '6x4, 2x5, 4x8');
assertGuess(true, 4, [
'x',
' x',
' x',
' x',
' x',
' x',
'x',
' x',
' x',
' x',
], '5x1, 2x0, 1x3, 2x4');
assertGuess(false, undefined, [
'\t x',
' \t x',
'\tx'
], 'mixed whitespace 1');
assertGuess(false, 4, [
'\tx',
'\t x'
], 'mixed whitespace 2');
});
test('validatePosition', () => {
let m = TextModel.createFromString('line one\nline two');
assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2));
assert.deepEqual(m.validatePosition(new Position(1, 30)), new Position(1, 9));
assert.deepEqual(m.validatePosition(new Position(2, 0)), new Position(2, 1));
assert.deepEqual(m.validatePosition(new Position(2, 1)), new Position(2, 1));
assert.deepEqual(m.validatePosition(new Position(2, 2)), new Position(2, 2));
assert.deepEqual(m.validatePosition(new Position(2, 30)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(3, 0)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(3, 1)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(3, 30)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(30, 30)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(-123.123, -0.5)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(Number.MIN_VALUE, Number.MIN_VALUE)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(Number.MAX_VALUE, Number.MAX_VALUE)), new Position(2, 9));
assert.deepEqual(m.validatePosition(new Position(123.23, 47.5)), new Position(2, 9));
});
test('validatePosition around high-low surrogate pairs 1', () => {
let m = TextModel.createFromString('a📚b');
assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(0, 7)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2));
assert.deepEqual(m.validatePosition(new Position(1, 3)), new Position(1, 2));
assert.deepEqual(m.validatePosition(new Position(1, 4)), new Position(1, 4));
assert.deepEqual(m.validatePosition(new Position(1, 5)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(1, 30)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(2, 0)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(2, 1)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(2, 2)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(2, 30)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(-123.123, -0.5)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(Number.MIN_VALUE, Number.MIN_VALUE)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(Number.MAX_VALUE, Number.MAX_VALUE)), new Position(1, 5));
assert.deepEqual(m.validatePosition(new Position(123.23, 47.5)), new Position(1, 5));
});
test('validatePosition around high-low surrogate pairs 2', () => {
let m = TextModel.createFromString('a📚📚b');
assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2));
assert.deepEqual(m.validatePosition(new Position(1, 3)), new Position(1, 2));
assert.deepEqual(m.validatePosition(new Position(1, 4)), new Position(1, 4));
assert.deepEqual(m.validatePosition(new Position(1, 5)), new Position(1, 4));
assert.deepEqual(m.validatePosition(new Position(1, 6)), new Position(1, 6));
assert.deepEqual(m.validatePosition(new Position(1, 7)), new Position(1, 7));
});
test('validateRange around high-low surrogate pairs 1', () => {
let m = TextModel.createFromString('a📚b');
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 2)), new Range(1, 1, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 3)), new Range(1, 1, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 4)), new Range(1, 1, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 5)), new Range(1, 1, 1, 5));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 2)), new Range(1, 2, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 3)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 4)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 5)), new Range(1, 2, 1, 5));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 3)), new Range(1, 2, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 4)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 5)), new Range(1, 2, 1, 5));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 4)), new Range(1, 4, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 5)), new Range(1, 4, 1, 5));
assert.deepEqual(m.validateRange(new Range(1, 5, 1, 5)), new Range(1, 5, 1, 5));
});
test('validateRange around high-low surrogate pairs 2', () => {
let m = TextModel.createFromString('a📚📚b');
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 2)), new Range(1, 1, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 3)), new Range(1, 1, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 4)), new Range(1, 1, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 5)), new Range(1, 1, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 6)), new Range(1, 1, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 1, 1, 7)), new Range(1, 1, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 2)), new Range(1, 2, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 3)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 4)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 5)), new Range(1, 2, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 6)), new Range(1, 2, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 2, 1, 7)), new Range(1, 2, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 3)), new Range(1, 2, 1, 2));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 4)), new Range(1, 2, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 5)), new Range(1, 2, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 6)), new Range(1, 2, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 3, 1, 7)), new Range(1, 2, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 4)), new Range(1, 4, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 5)), new Range(1, 4, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 6)), new Range(1, 4, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 4, 1, 7)), new Range(1, 4, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 5, 1, 5)), new Range(1, 4, 1, 4));
assert.deepEqual(m.validateRange(new Range(1, 5, 1, 6)), new Range(1, 4, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 5, 1, 7)), new Range(1, 4, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 6, 1, 6)), new Range(1, 6, 1, 6));
assert.deepEqual(m.validateRange(new Range(1, 6, 1, 7)), new Range(1, 6, 1, 7));
assert.deepEqual(m.validateRange(new Range(1, 7, 1, 7)), new Range(1, 7, 1, 7));
});
test('modifyPosition', () => {
var m = TextModel.createFromString('line one\nline two');
assert.deepEqual(m.modifyPosition(new Position(1, 1), 0), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(0, 0), 0), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(30, 1), 0), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(1, 1), 17), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(1, 1), 1), new Position(1, 2));
assert.deepEqual(m.modifyPosition(new Position(1, 1), 3), new Position(1, 4));
assert.deepEqual(m.modifyPosition(new Position(1, 2), 10), new Position(2, 3));
assert.deepEqual(m.modifyPosition(new Position(1, 5), 13), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(1, 2), 16), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(2, 9), -17), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(1, 2), -1), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(1, 4), -3), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(2, 3), -10), new Position(1, 2));
assert.deepEqual(m.modifyPosition(new Position(2, 9), -13), new Position(1, 5));
assert.deepEqual(m.modifyPosition(new Position(2, 9), -16), new Position(1, 2));
assert.deepEqual(m.modifyPosition(new Position(1, 2), 17), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(1, 2), 100), new Position(2, 9));
assert.deepEqual(m.modifyPosition(new Position(1, 2), -2), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(1, 2), -100), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(2, 2), -100), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(2, 9), -18), new Position(1, 1));
});
test('normalizeIndentation 1', () => {
let model = TextModel.createFromString('',
{
detectIndentation: false,
tabSize: 4,
insertSpaces: false,
trimAutoWhitespace: true,
defaultEOL: DefaultEndOfLine.LF
}
);
assert.equal(model.normalizeIndentation('\t'), '\t');
assert.equal(model.normalizeIndentation(' '), '\t');
assert.equal(model.normalizeIndentation(' '), ' ');
assert.equal(model.normalizeIndentation(' '), ' ');
assert.equal(model.normalizeIndentation(' '), ' ');
assert.equal(model.normalizeIndentation(''), '');
assert.equal(model.normalizeIndentation(' \t '), '\t\t');
assert.equal(model.normalizeIndentation(' \t '), '\t ');
assert.equal(model.normalizeIndentation(' \t '), '\t ');
assert.equal(model.normalizeIndentation(' \t'), '\t ');
assert.equal(model.normalizeIndentation('\ta'), '\ta');
assert.equal(model.normalizeIndentation(' a'), '\ta');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation('a'), 'a');
assert.equal(model.normalizeIndentation(' \t a'), '\t\ta');
assert.equal(model.normalizeIndentation(' \t a'), '\t a');
assert.equal(model.normalizeIndentation(' \t a'), '\t a');
assert.equal(model.normalizeIndentation(' \ta'), '\t a');
model.dispose();
});
test('normalizeIndentation 2', () => {
let model = TextModel.createFromString('',
{
detectIndentation: false,
tabSize: 4,
insertSpaces: true,
trimAutoWhitespace: true,
defaultEOL: DefaultEndOfLine.LF
}
);
assert.equal(model.normalizeIndentation('\ta'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation(' a'), ' a');
assert.equal(model.normalizeIndentation('a'), 'a');
assert.equal(model.normalizeIndentation(' \t a'), ' a');
assert.equal(model.normalizeIndentation(' \t a'), ' a');
assert.equal(model.normalizeIndentation(' \t a'), ' a');
assert.equal(model.normalizeIndentation(' \ta'), ' a');
model.dispose();
});
});
suite('TextModel.mightContainRTL', () => {
test('nope', () => {
let model = TextModel.createFromString('hello world!');
assert.equal(model.mightContainRTL(), false);
});
test('yes', () => {
let model = TextModel.createFromString('Hello,\nזוהי עובדה מבוססת שדעתו');
assert.equal(model.mightContainRTL(), true);
});
test('setValue resets 1', () => {
let model = TextModel.createFromString('hello world!');
assert.equal(model.mightContainRTL(), false);
model.setValue('Hello,\nזוהי עובדה מבוססת שדעתו');
assert.equal(model.mightContainRTL(), true);
});
test('setValue resets 2', () => {
let model = TextModel.createFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل');
assert.equal(model.mightContainRTL(), true);
model.setValue('hello world!');
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

@@ -0,0 +1,636 @@
/*---------------------------------------------------------------------------------------------
* 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 { Position } from 'vs/editor/common/core/position';
import { FindMatch, EndOfLineSequence } from 'vs/editor/common/editorCommon';
import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { TextModelSearch, SearchParams, SearchData } from 'vs/editor/common/model/textModelSearch';
import { getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
// --------- Find
suite('TextModelSearch', () => {
const usualWordSeparators = getMapForWordSeparators(USUAL_WORD_SEPARATORS);
function assertFindMatch(actual: FindMatch, expectedRange: Range, expectedMatches: string[] = null): void {
assert.deepEqual(actual, new FindMatch(expectedRange, expectedMatches));
}
function _assertFindMatches(model: TextModel, searchParams: SearchParams, expectedMatches: FindMatch[]): void {
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), false, 1000);
assert.deepEqual(actual, expectedMatches, 'findMatches OK');
// test `findNextMatch`
let startPos = new Position(1, 1);
let match = TextModelSearch.findNextMatch(model, searchParams, startPos, false);
assert.deepEqual(match, expectedMatches[0], `findNextMatch ${startPos}`);
for (let i = 0; i < expectedMatches.length; i++) {
startPos = expectedMatches[i].range.getStartPosition();
match = TextModelSearch.findNextMatch(model, searchParams, startPos, false);
assert.deepEqual(match, expectedMatches[i], `findNextMatch ${startPos}`);
}
// test `findPrevMatch`
startPos = new Position(model.getLineCount(), model.getLineMaxColumn(model.getLineCount()));
match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false);
assert.deepEqual(match, expectedMatches[expectedMatches.length - 1], `findPrevMatch ${startPos}`);
for (let i = 0; i < expectedMatches.length; i++) {
startPos = expectedMatches[i].range.getEndPosition();
match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false);
assert.deepEqual(match, expectedMatches[i], `findPrevMatch ${startPos}`);
}
}
function assertFindMatches(text: string, searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string, _expected: [number, number, number, number][]): void {
let expectedRanges = _expected.map(entry => new Range(entry[0], entry[1], entry[2], entry[3]));
let expectedMatches = expectedRanges.map(entry => new FindMatch(entry, null));
let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
let model = TextModel.createFromString(text);
_assertFindMatches(model, searchParams, expectedMatches);
model.dispose();
let model2 = TextModel.createFromString(text);
model2.setEOL(EndOfLineSequence.CRLF);
_assertFindMatches(model2, searchParams, expectedMatches);
model2.dispose();
}
let regularText = [
'This is some foo - bar text which contains foo and bar - as in Barcelona.',
'Now it begins a word fooBar and now it is caps Foo-isn\'t this great?',
'And here\'s a dull line with nothing interesting in it',
'It is also interesting if it\'s part of a word like amazingFooBar',
'Again nothing interesting here'
];
test('Simple find', () => {
assertFindMatches(
regularText.join('\n'),
'foo', false, false, null,
[
[1, 14, 1, 17],
[1, 44, 1, 47],
[2, 22, 2, 25],
[2, 48, 2, 51],
[4, 59, 4, 62]
]
);
});
test('Case sensitive find', () => {
assertFindMatches(
regularText.join('\n'),
'foo', false, true, null,
[
[1, 14, 1, 17],
[1, 44, 1, 47],
[2, 22, 2, 25]
]
);
});
test('Whole words find', () => {
assertFindMatches(
regularText.join('\n'),
'foo', false, false, USUAL_WORD_SEPARATORS,
[
[1, 14, 1, 17],
[1, 44, 1, 47],
[2, 48, 2, 51]
]
);
});
test('/^/ find', () => {
assertFindMatches(
regularText.join('\n'),
'^', true, false, null,
[
[1, 1, 1, 1],
[2, 1, 2, 1],
[3, 1, 3, 1],
[4, 1, 4, 1],
[5, 1, 5, 1]
]
);
});
test('/$/ find', () => {
assertFindMatches(
regularText.join('\n'),
'$', true, false, null,
[
[1, 74, 1, 74],
[2, 69, 2, 69],
[3, 54, 3, 54],
[4, 65, 4, 65],
[5, 31, 5, 31]
]
);
});
test('/.*/ find', () => {
assertFindMatches(
regularText.join('\n'),
'.*', true, false, null,
[
[1, 1, 1, 74],
[2, 1, 2, 69],
[3, 1, 3, 54],
[4, 1, 4, 65],
[5, 1, 5, 31]
]
);
});
test('/^$/ find', () => {
assertFindMatches(
[
'This is some foo - bar text which contains foo and bar - as in Barcelona.',
'',
'And here\'s a dull line with nothing interesting in it',
'',
'Again nothing interesting here'
].join('\n'),
'^$', true, false, null,
[
[2, 1, 2, 1],
[4, 1, 4, 1]
]
);
});
test('multiline find 1', () => {
assertFindMatches(
[
'Just some text text',
'Just some text text',
'some text again',
'again some text'
].join('\n'),
'text\\n', true, false, null,
[
[1, 16, 2, 1],
[2, 16, 3, 1],
]
);
});
test('multiline find 2', () => {
assertFindMatches(
[
'Just some text text',
'Just some text text',
'some text again',
'again some text'
].join('\n'),
'text\\nJust', true, false, null,
[
[1, 16, 2, 5]
]
);
});
test('multiline find 3', () => {
assertFindMatches(
[
'Just some text text',
'Just some text text',
'some text again',
'again some text'
].join('\n'),
'\\nagain', true, false, null,
[
[3, 16, 4, 6]
]
);
});
test('multiline find 4', () => {
assertFindMatches(
[
'Just some text text',
'Just some text text',
'some text again',
'again some text'
].join('\n'),
'.*\\nJust.*\\n', true, false, null,
[
[1, 1, 3, 1]
]
);
});
test('multiline find with line beginning regex', () => {
assertFindMatches(
[
'if',
'else',
'',
'if',
'else'
].join('\n'),
'^if\\nelse', true, false, null,
[
[1, 1, 2, 5],
[4, 1, 5, 5]
]
);
});
test('matching empty lines using boundary expression', () => {
assertFindMatches(
[
'if',
'',
'else',
' ',
'if',
' ',
'else'
].join('\n'),
'^\\s*$\\n', true, false, null,
[
[2, 1, 3, 1],
[4, 1, 5, 1],
[6, 1, 7, 1]
]
);
});
test('matching lines starting with A and ending with B', () => {
assertFindMatches(
[
'a if b',
'a',
'ab',
'eb'
].join('\n'),
'^a.*b$', true, false, null,
[
[1, 1, 1, 7],
[3, 1, 3, 3]
]
);
});
test('multiline find with line ending regex', () => {
assertFindMatches(
[
'if',
'else',
'',
'if',
'elseif',
'else'
].join('\n'),
'if\\nelse$', true, false, null,
[
[1, 1, 2, 5],
[5, 5, 6, 5]
]
);
});
test('issue #4836 - ^.*$', () => {
assertFindMatches(
[
'Just some text text',
'',
'some text again',
'',
'again some text'
].join('\n'),
'^.*$', true, false, null,
[
[1, 1, 1, 20],
[2, 1, 2, 1],
[3, 1, 3, 16],
[4, 1, 4, 1],
[5, 1, 5, 16],
]
);
});
test('multiline find for non-regex string', () => {
assertFindMatches(
[
'Just some text text',
'some text text',
'some text again',
'again some text',
'but not some'
].join('\n'),
'text\nsome', false, false, null,
[
[1, 16, 2, 5],
[2, 11, 3, 5],
]
);
});
test('issue #3623: Match whole word does not work for not latin characters', () => {
assertFindMatches(
[
'я',
'компилятор',
'обфускация',
':я-я'
].join('\n'),
'я', false, false, USUAL_WORD_SEPARATORS,
[
[1, 1, 1, 2],
[4, 2, 4, 3],
[4, 4, 4, 5],
]
);
});
test('issue #27459: Match whole words regression', () => {
assertFindMatches(
[
'this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => {',
' this._viewController.emitKeyDown(e);',
'}));',
].join('\n'),
'((e: ', false, false, USUAL_WORD_SEPARATORS,
[
[1, 45, 1, 50]
]
);
});
test('issue #27594: Search results disappear', () => {
assertFindMatches(
[
'this.server.listen(0);',
].join('\n'),
'listen(', false, false, USUAL_WORD_SEPARATORS,
[
[1, 13, 1, 20]
]
);
});
test('findNextMatch without regex', () => {
let model = TextModel.createFromString('line line one\nline two\nthree');
let searchParams = new SearchParams('line', false, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(1, 6, 1, 10));
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false);
assertFindMatch(actual, new Range(1, 6, 1, 10));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(2, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
model.dispose();
});
test('findNextMatch with beginning boundary regex', () => {
let model = TextModel.createFromString('line one\nline two\nthree');
let searchParams = new SearchParams('^line', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(2, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false);
assertFindMatch(actual, new Range(2, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
model.dispose();
});
test('findNextMatch with beginning boundary regex and line has repetitive beginnings', () => {
let model = TextModel.createFromString('line line one\nline two\nthree');
let searchParams = new SearchParams('^line', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(2, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false);
assertFindMatch(actual, new Range(2, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(1, 1, 1, 5));
model.dispose();
});
test('findNextMatch with beginning boundary multiline regex and line has repetitive beginnings', () => {
let model = TextModel.createFromString('line line one\nline two\nline three\nline four');
let searchParams = new SearchParams('^line.*\\nline', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false);
assertFindMatch(actual, new Range(1, 1, 2, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(3, 1, 4, 5));
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(2, 1), false);
assertFindMatch(actual, new Range(2, 1, 3, 5));
model.dispose();
});
test('findNextMatch with ending boundary regex', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('line$', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false);
assertFindMatch(actual, new Range(1, 10, 1, 14));
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 4), false);
assertFindMatch(actual, new Range(1, 10, 1, 14));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(2, 5, 2, 9));
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
assertFindMatch(actual, new Range(1, 10, 1, 14));
model.dispose();
});
test('findMatches with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100);
assert.deepEqual(actual, [
new FindMatch(new Range(1, 5, 1, 9), ['line', 'line', 'in']),
new FindMatch(new Range(1, 10, 1, 14), ['line', 'line', 'in']),
new FindMatch(new Range(2, 5, 2, 9), ['line', 'line', 'in']),
]);
model.dispose();
});
test('findMatches multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100);
assert.deepEqual(actual, [
new FindMatch(new Range(1, 10, 2, 1), ['line\n', 'line', 'in']),
new FindMatch(new Range(2, 5, 3, 1), ['line\n', 'line', 'in']),
]);
model.dispose();
});
test('findNextMatch with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
assertFindMatch(actual, new Range(1, 5, 1, 9), ['line', 'line', 'in']);
model.dispose();
});
test('findNextMatch multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
assertFindMatch(actual, new Range(1, 10, 2, 1), ['line\n', 'line', 'in']);
model.dispose();
});
test('findPreviousMatch with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true);
assertFindMatch(actual, new Range(2, 5, 2, 9), ['line', 'line', 'in']);
model.dispose();
});
test('findPreviousMatch multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true);
assertFindMatch(actual, new Range(2, 5, 3, 1), ['line\n', 'line', 'in']);
model.dispose();
});
test('\\n matches \\r\\n', () => {
let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
assert.equal(model.getEOL(), '\r\n');
let searchParams = new SearchParams('h\\n', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0];
assertFindMatch(actual, new Range(8, 1, 9, 1), ['h\n']);
searchParams = new SearchParams('g\\nh\\n', true, false, null);
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0];
assertFindMatch(actual, new Range(7, 1, 9, 1), ['g\nh\n']);
searchParams = new SearchParams('\\ni', true, false, null);
actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0];
assertFindMatch(actual, new Range(8, 2, 9, 2), ['\ni']);
model.dispose();
});
test('\\r can never be found', () => {
let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
assert.equal(model.getEOL(), '\r\n');
let searchParams = new SearchParams('\\r\\n', true, false, null);
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
assert.equal(actual, null);
assert.deepEqual(TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000), []);
model.dispose();
});
function assertParseSearchResult(searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string, expected: SearchData): void {
let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
let actual = searchParams.parseSearchRequest();
if (expected === null) {
assert.ok(actual === null);
} else {
assert.deepEqual(actual.regex, expected.regex);
assert.deepEqual(actual.simpleSearch, expected.simpleSearch);
if (wordSeparators) {
assert.ok(actual.wordSeparators !== null);
} else {
assert.ok(actual.wordSeparators === null);
}
}
}
test('parseSearchRequest invalid', () => {
assertParseSearchResult('', true, true, USUAL_WORD_SEPARATORS, null);
assertParseSearchResult(null, true, true, USUAL_WORD_SEPARATORS, null);
assertParseSearchResult('(', true, false, null, null);
});
test('parseSearchRequest non regex', () => {
assertParseSearchResult('foo', false, false, null, new SearchData(/foo/gi, null, null));
assertParseSearchResult('foo', false, false, USUAL_WORD_SEPARATORS, new SearchData(/foo/gi, usualWordSeparators, null));
assertParseSearchResult('foo', false, true, null, new SearchData(/foo/g, null, 'foo'));
assertParseSearchResult('foo', false, true, USUAL_WORD_SEPARATORS, new SearchData(/foo/g, usualWordSeparators, 'foo'));
assertParseSearchResult('foo\\n', false, false, null, new SearchData(/foo\\n/gi, null, null));
assertParseSearchResult('foo\\\\n', false, false, null, new SearchData(/foo\\\\n/gi, null, null));
assertParseSearchResult('foo\\r', false, false, null, new SearchData(/foo\\r/gi, null, null));
assertParseSearchResult('foo\\\\r', false, false, null, new SearchData(/foo\\\\r/gi, null, null));
});
test('parseSearchRequest regex', () => {
assertParseSearchResult('foo', true, false, null, new SearchData(/foo/gi, null, null));
assertParseSearchResult('foo', true, false, USUAL_WORD_SEPARATORS, new SearchData(/foo/gi, usualWordSeparators, null));
assertParseSearchResult('foo', true, true, null, new SearchData(/foo/g, null, null));
assertParseSearchResult('foo', true, true, USUAL_WORD_SEPARATORS, new SearchData(/foo/g, usualWordSeparators, null));
assertParseSearchResult('foo\\n', true, false, null, new SearchData(/foo\n/gim, null, null));
assertParseSearchResult('foo\\\\n', true, false, null, new SearchData(/foo\\n/gi, null, null));
assertParseSearchResult('foo\\r', true, false, null, new SearchData(/foo\r/gim, null, null));
assertParseSearchResult('foo\\\\r', true, false, null, new SearchData(/foo\\r/gi, null, null));
});
});

View File

@@ -0,0 +1,361 @@
/*---------------------------------------------------------------------------------------------
* 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 { Model } from 'vs/editor/common/model/model';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { ITokenizationSupport, TokenizationRegistry, LanguageId, LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes';
import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { IFoundBracket } from 'vs/editor/common/editorCommon';
import { TextModel } from 'vs/editor/common/model/textModel';
import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { RawTextSource } from 'vs/editor/common/model/textSource';
suite('TextModelWithTokens', () => {
function testBrackets(contents: string[], brackets: CharacterPair[]): void {
function toRelaxedFoundBracket(a: IFoundBracket) {
if (!a) {
return null;
}
return {
range: a.range.toString(),
open: a.open,
close: a.close,
isOpen: a.isOpen
};
}
let charIsBracket: { [char: string]: boolean } = {};
let charIsOpenBracket: { [char: string]: boolean } = {};
let openForChar: { [char: string]: string } = {};
let closeForChar: { [char: string]: string } = {};
brackets.forEach((b) => {
charIsBracket[b[0]] = true;
charIsBracket[b[1]] = true;
charIsOpenBracket[b[0]] = true;
charIsOpenBracket[b[1]] = false;
openForChar[b[0]] = b[0];
closeForChar[b[0]] = b[1];
openForChar[b[1]] = b[0];
closeForChar[b[1]] = b[1];
});
let expectedBrackets: IFoundBracket[] = [];
for (let lineIndex = 0; lineIndex < contents.length; lineIndex++) {
let lineText = contents[lineIndex];
for (let charIndex = 0; charIndex < lineText.length; charIndex++) {
let ch = lineText.charAt(charIndex);
if (charIsBracket[ch]) {
expectedBrackets.push({
open: openForChar[ch],
close: closeForChar[ch],
isOpen: charIsOpenBracket[ch],
range: new Range(lineIndex + 1, charIndex + 1, lineIndex + 1, charIndex + 2)
});
}
}
}
const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText);
let registration = LanguageConfigurationRegistry.register(languageIdentifier, {
brackets: brackets
});
let model = new TextModelWithTokens(
RawTextSource.fromString(contents.join('\n')),
TextModel.DEFAULT_CREATION_OPTIONS,
languageIdentifier
);
// findPrevBracket
{
let expectedBracketIndex = expectedBrackets.length - 1;
let currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null;
for (let lineNumber = contents.length; lineNumber >= 1; lineNumber--) {
let lineText = contents[lineNumber - 1];
for (let column = lineText.length + 1; column >= 1; column--) {
if (currentExpectedBracket) {
if (lineNumber === currentExpectedBracket.range.startLineNumber && column < currentExpectedBracket.range.endColumn) {
expectedBracketIndex--;
currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null;
}
}
let actual = model.findPrevBracket({
lineNumber: lineNumber,
column: column
});
assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findPrevBracket of ' + lineNumber + ', ' + column);
}
}
}
// findNextBracket
{
let expectedBracketIndex = 0;
let currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null;
for (let lineNumber = 1; lineNumber <= contents.length; lineNumber++) {
let lineText = contents[lineNumber - 1];
for (let column = 1; column <= lineText.length + 1; column++) {
if (currentExpectedBracket) {
if (lineNumber === currentExpectedBracket.range.startLineNumber && column > currentExpectedBracket.range.startColumn) {
expectedBracketIndex++;
currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null;
}
}
let actual = model.findNextBracket({
lineNumber: lineNumber,
column: column
});
assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findNextBracket of ' + lineNumber + ', ' + column);
}
}
}
model.dispose();
registration.dispose();
}
test('brackets', () => {
testBrackets([
'if (a == 3) { return (7 * (a + 5)); }'
], [
['{', '}'],
['[', ']'],
['(', ')']
]);
});
});
suite('TextModelWithTokens - bracket matching', () => {
function isNotABracket(model: Model, lineNumber: number, column: number) {
let match = model.matchBracket(new Position(lineNumber, column));
assert.equal(match, null, 'is not matching brackets at ' + lineNumber + ', ' + column);
}
function isBracket2(model: Model, testPosition: Position, expected: [Range, Range]): void {
let actual = model.matchBracket(testPosition);
assert.deepEqual(actual, expected, 'matches brackets at ' + testPosition);
}
const languageIdentifier = new LanguageIdentifier('bracketMode1', LanguageId.PlainText);
let registration: IDisposable = null;
setup(() => {
registration = LanguageConfigurationRegistry.register(languageIdentifier, {
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
]
});
});
teardown(() => {
registration.dispose();
registration = null;
});
test('bracket matching 1', () => {
let text =
')]}{[(' + '\n' +
')]}{[(';
let model = Model.createFromString(text, undefined, languageIdentifier);
isNotABracket(model, 1, 1);
isNotABracket(model, 1, 2);
isNotABracket(model, 1, 3);
isBracket2(model, new Position(1, 4), [new Range(1, 4, 1, 5), new Range(2, 3, 2, 4)]);
isBracket2(model, new Position(1, 5), [new Range(1, 5, 1, 6), new Range(2, 2, 2, 3)]);
isBracket2(model, new Position(1, 6), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]);
isBracket2(model, new Position(1, 7), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]);
isBracket2(model, new Position(2, 1), [new Range(2, 1, 2, 2), new Range(1, 6, 1, 7)]);
isBracket2(model, new Position(2, 2), [new Range(2, 2, 2, 3), new Range(1, 5, 1, 6)]);
isBracket2(model, new Position(2, 3), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]);
isBracket2(model, new Position(2, 4), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]);
isNotABracket(model, 2, 5);
isNotABracket(model, 2, 6);
isNotABracket(model, 2, 7);
model.dispose();
});
test('bracket matching 2', () => {
let text =
'var bar = {' + '\n' +
'foo: {' + '\n' +
'}, bar: {hallo: [{' + '\n' +
'}, {' + '\n' +
'}]}}';
let model = Model.createFromString(text, undefined, languageIdentifier);
let brackets: [Position, Range, Range][] = [
[new Position(1, 11), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)],
[new Position(1, 12), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)],
[new Position(2, 6), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)],
[new Position(2, 7), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)],
[new Position(3, 1), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)],
[new Position(3, 2), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)],
[new Position(3, 9), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)],
[new Position(3, 10), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)],
[new Position(3, 17), new Range(3, 17, 3, 18), new Range(5, 2, 5, 3)],
[new Position(3, 18), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)],
[new Position(3, 19), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)],
[new Position(4, 1), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)],
[new Position(4, 2), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)],
[new Position(4, 4), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)],
[new Position(4, 5), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)],
[new Position(5, 1), new Range(5, 1, 5, 2), new Range(4, 4, 4, 5)],
[new Position(5, 2), new Range(5, 2, 5, 3), new Range(3, 17, 3, 18)],
[new Position(5, 3), new Range(5, 3, 5, 4), new Range(3, 9, 3, 10)],
[new Position(5, 4), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)],
[new Position(5, 5), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)],
];
let isABracket: { [lineNumber: number]: { [col: number]: boolean; }; } = { 1: {}, 2: {}, 3: {}, 4: {}, 5: {} };
for (let i = 0, len = brackets.length; i < len; i++) {
let [testPos, b1, b2] = brackets[i];
isBracket2(model, testPos, [b1, b2]);
isABracket[testPos.lineNumber][testPos.column] = true;
}
for (let i = 1, len = model.getLineCount(); i <= len; i++) {
let line = model.getLineContent(i);
for (let j = 1, lenJ = line.length + 1; j <= lenJ; j++) {
if (!isABracket[i].hasOwnProperty(<any>j)) {
isNotABracket(model, i, j);
}
}
}
model.dispose();
});
});
suite('TextModelWithTokens regression tests', () => {
test('Microsoft/monaco-editor#122: Unhandled Exception: TypeError: Unable to get property \'replace\' of undefined or null reference', () => {
function assertViewLineTokens(model: Model, lineNumber: number, forceTokenization: boolean, expected: ViewLineToken[]): void {
if (forceTokenization) {
model.forceTokenization(lineNumber);
}
let actual = model.getLineTokens(lineNumber).inflate();
let decode = (token: ViewLineToken) => {
return {
endIndex: token.endIndex,
foreground: token.getForeground()
};
};
assert.deepEqual(actual.map(decode), expected.map(decode));
}
let _tokenId = 10;
const LANG_ID1 = 'indicisiveMode1';
const LANG_ID2 = 'indicisiveMode2';
const languageIdentifier1 = new LanguageIdentifier(LANG_ID1, 3);
const languageIdentifier2 = new LanguageIdentifier(LANG_ID2, 4);
const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE,
tokenize: undefined,
tokenize2: (line, state) => {
let myId = ++_tokenId;
let tokens = new Uint32Array(2);
tokens[0] = 0;
tokens[1] = (
myId << MetadataConsts.FOREGROUND_OFFSET
) >>> 0;
return new TokenizationResult2(tokens, state);
}
};
let registration1 = TokenizationRegistry.register(LANG_ID1, tokenizationSupport);
let registration2 = TokenizationRegistry.register(LANG_ID2, tokenizationSupport);
let model = Model.createFromString('A model with\ntwo lines');
assertViewLineTokens(model, 1, true, [createViewLineToken(12, 1)]);
assertViewLineTokens(model, 2, true, [createViewLineToken(9, 1)]);
model.setMode(languageIdentifier1);
assertViewLineTokens(model, 1, true, [createViewLineToken(12, 11)]);
assertViewLineTokens(model, 2, true, [createViewLineToken(9, 12)]);
model.setMode(languageIdentifier2);
assertViewLineTokens(model, 1, false, [createViewLineToken(12, 1)]);
assertViewLineTokens(model, 2, false, [createViewLineToken(9, 1)]);
model.dispose();
registration1.dispose();
registration2.dispose();
function createViewLineToken(endIndex: number, foreground: number): ViewLineToken {
let metadata = (
(foreground << MetadataConsts.FOREGROUND_OFFSET)
) >>> 0;
return new ViewLineToken(endIndex, metadata);
}
});
test('Microsoft/monaco-editor#133: Error: Cannot read property \'modeId\' of undefined', () => {
const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText);
let registration = LanguageConfigurationRegistry.register(languageIdentifier, {
brackets: [
['module', 'end module'],
['sub', 'end sub']
]
});
let model = Model.createFromString([
'Imports System',
'Imports System.Collections.Generic',
'',
'Module m1',
'',
'\tSub Main()',
'\tEnd Sub',
'',
'End Module',
].join('\n'), undefined, languageIdentifier);
let actual = model.matchBracket(new Position(4, 1));
assert.deepEqual(actual, [new Range(4, 1, 4, 7), new Range(9, 1, 9, 11)]);
model.dispose();
registration.dispose();
});
});

View File

@@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* 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 { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
import { StandardTokenType } from 'vs/editor/common/modes';
suite('StandardAutoClosingPairConditional', () => {
test('Missing notIn', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}' });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('Empty notIn', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('Invalid notIn', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['bla'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('notIn in strings', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), false);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('notIn in comments', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['comment'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), false);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('notIn in regex', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['regex'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), false);
});
test('notIn in strings nor comments', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'comment'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), false);
assert.equal(v.isOK(StandardTokenType.String), false);
assert.equal(v.isOK(StandardTokenType.RegEx), true);
});
test('notIn in strings nor regex', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'regex'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), true);
assert.equal(v.isOK(StandardTokenType.String), false);
assert.equal(v.isOK(StandardTokenType.RegEx), false);
});
test('notIn in comments nor regex', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['comment', 'regex'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), false);
assert.equal(v.isOK(StandardTokenType.String), true);
assert.equal(v.isOK(StandardTokenType.RegEx), false);
});
test('notIn in strings, comments nor regex', () => {
let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'comment', 'regex'] });
assert.equal(v.isOK(StandardTokenType.Other), true);
assert.equal(v.isOK(StandardTokenType.Comment), false);
assert.equal(v.isOK(StandardTokenType.String), false);
assert.equal(v.isOK(StandardTokenType.RegEx), false);
});
});

View File

@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import { score } from 'vs/editor/common/modes/languageSelector';
suite('LanguageSelector', function () {
let model = {
language: 'farboo',
uri: URI.parse('file:///testbed/file.fb')
};
test('score, invalid selector', function () {
assert.equal(score({}, model.uri, model.language), 0);
assert.equal(score(undefined, model.uri, model.language), 0);
assert.equal(score(null, model.uri, model.language), 0);
assert.equal(score('', model.uri, model.language), 0);
});
test('score, any language', function () {
assert.equal(score({ language: '*' }, model.uri, model.language), 5);
assert.equal(score('*', model.uri, model.language), 5);
assert.equal(score('*', URI.parse('foo:bar'), model.language), 5);
assert.equal(score('farboo', URI.parse('foo:bar'), model.language), 10);
});
test('score, default schemes', function () {
const uri = URI.parse('git:foo/file.txt');
const language = 'farboo';
assert.equal(score('*', uri, language), 5);
assert.equal(score('farboo', uri, language), 10);
assert.equal(score({ language: 'farboo', scheme: '' }, uri, language), 10);
assert.equal(score({ language: 'farboo', scheme: 'git' }, uri, language), 10);
assert.equal(score({ language: 'farboo', scheme: '*' }, uri, language), 10);
assert.equal(score({ language: 'farboo' }, uri, language), 10);
assert.equal(score({ language: '*' }, uri, language), 5);
assert.equal(score({ scheme: '*' }, uri, language), 5);
assert.equal(score({ scheme: 'git' }, uri, language), 10);
});
test('score, filter', function () {
assert.equal(score('farboo', model.uri, model.language), 10);
assert.equal(score({ language: 'farboo' }, model.uri, model.language), 10);
assert.equal(score({ language: 'farboo', scheme: 'file' }, model.uri, model.language), 10);
assert.equal(score({ language: 'farboo', scheme: 'http' }, model.uri, model.language), 0);
assert.equal(score({ pattern: '**/*.fb' }, model.uri, model.language), 10);
assert.equal(score({ pattern: '**/*.fb', scheme: 'file' }, model.uri, model.language), 10);
assert.equal(score({ pattern: '**/*.fb' }, URI.parse('foo:bar'), model.language), 0);
assert.equal(score({ pattern: '**/*.fb', scheme: 'foo' }, URI.parse('foo:bar'), model.language), 0);
let doc = {
uri: URI.parse('git:/my/file.js'),
langId: 'javascript'
};
assert.equal(score('javascript', doc.uri, doc.langId), 10); // 0;
assert.equal(score({ language: 'javascript', scheme: 'git' }, doc.uri, doc.langId), 10); // 10;
assert.equal(score('*', doc.uri, doc.langId), 5); // 5
assert.equal(score('fooLang', doc.uri, doc.langId), 0); // 0
assert.equal(score(['fooLang', '*'], doc.uri, doc.langId), 5); // 5
});
test('score, max(filters)', function () {
let match = { language: 'farboo', scheme: 'file' };
let fail = { language: 'farboo', scheme: 'http' };
assert.equal(score(match, model.uri, model.language), 10);
assert.equal(score(fail, model.uri, model.language), 0);
assert.equal(score([match, fail], model.uri, model.language), 10);
assert.equal(score([fail, fail], model.uri, model.language), 0);
assert.equal(score(['farboo', '*'], model.uri, model.language), 10);
assert.equal(score(['*', 'farboo'], model.uri, model.language), 10);
});
});

View File

@@ -0,0 +1,193 @@
/*---------------------------------------------------------------------------------------------
* 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 { ILink } from 'vs/editor/common/modes';
import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkComputer';
class SimpleLinkComputerTarget implements ILinkComputerTarget {
constructor(private _lines: string[]) {
// Intentional Empty
}
public getLineCount(): number {
return this._lines.length;
}
public getLineContent(lineNumber: number): string {
return this._lines[lineNumber - 1];
}
}
function myComputeLinks(lines: string[]): ILink[] {
var target = new SimpleLinkComputerTarget(lines);
return computeLinks(target);
}
function assertLink(text: string, extractedLink: string): void {
var startColumn = 0,
endColumn = 0,
chr: string,
i = 0;
for (i = 0; i < extractedLink.length; i++) {
chr = extractedLink.charAt(i);
if (chr !== ' ' && chr !== '\t') {
startColumn = i + 1;
break;
}
}
for (i = extractedLink.length - 1; i >= 0; i--) {
chr = extractedLink.charAt(i);
if (chr !== ' ' && chr !== '\t') {
endColumn = i + 2;
break;
}
}
var r = myComputeLinks([text]);
assert.deepEqual(r, [{
range: {
startLineNumber: 1,
startColumn: startColumn,
endLineNumber: 1,
endColumn: endColumn
},
url: extractedLink.substring(startColumn - 1, endColumn - 1)
}]);
}
suite('Editor Modes - Link Computer', () => {
test('Null model', () => {
var r = computeLinks(null);
assert.deepEqual(r, []);
});
test('Parsing', () => {
assertLink(
'x = "http://foo.bar";',
' http://foo.bar '
);
assertLink(
'x = (http://foo.bar);',
' http://foo.bar '
);
assertLink(
'x = [http://foo.bar];',
' http://foo.bar '
);
assertLink(
'x = \'http://foo.bar\';',
' http://foo.bar '
);
assertLink(
'x = http://foo.bar ;',
' http://foo.bar '
);
assertLink(
'x = <http://foo.bar>;',
' http://foo.bar '
);
assertLink(
'x = {http://foo.bar};',
' http://foo.bar '
);
assertLink(
'(see http://foo.bar)',
' http://foo.bar '
);
assertLink(
'[see http://foo.bar]',
' http://foo.bar '
);
assertLink(
'{see http://foo.bar}',
' http://foo.bar '
);
assertLink(
'<see http://foo.bar>',
' http://foo.bar '
);
assertLink(
'<url>http://mylink.com</url>',
' http://mylink.com '
);
assertLink(
'// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409',
' https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409'
);
assertLink(
'// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx',
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx'
);
assertLink(
'// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js',
' https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js'
);
assertLink(
'<!-- !!! Do not remove !!! WebContentRef(link:https://go.microsoft.com/fwlink/?LinkId=166007, area:Admin, updated:2015, nextUpdate:2016, tags:SqlServer) !!! Do not remove !!! -->',
' https://go.microsoft.com/fwlink/?LinkId=166007 '
);
assertLink(
'For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.</value>',
' https://go.microsoft.com/fwlink/?LinkId=166007 '
);
assertLink(
'For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.</value>',
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx '
);
assertLink(
'x = "https://en.wikipedia.org/wiki/Zürich";',
' https://en.wikipedia.org/wiki/Zürich '
);
assertLink(
'請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。',
' http://go.microsoft.com/fwlink/?LinkId=761051 '
);
assertLink(
'(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051',
' http://go.microsoft.com/fwlink/?LinkId=761051 '
);
assertLink(
'x = "file:///foo.bar";',
' file:///foo.bar '
);
assertLink(
'x = "file://c:/foo.bar";',
' file://c:/foo.bar '
);
assertLink(
'x = "file://shares/foo.bar";',
' file://shares/foo.bar '
);
assertLink(
'x = "file://shäres/foo.bar";',
' file://shäres/foo.bar '
);
assertLink(
'Some text, then http://www.bing.com.',
' http://www.bing.com '
);
assertLink(
'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;',
' http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items '
);
});
});

View File

@@ -0,0 +1,122 @@
/*---------------------------------------------------------------------------------------------
* 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 { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair';
import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils';
import { StandardTokenType } from 'vs/editor/common/modes';
suite('CharacterPairSupport', () => {
test('only autoClosingPairs', () => {
let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [{ open: 'a', close: 'b' }] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]);
});
test('only empty autoClosingPairs', () => {
let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []);
});
test('only brackets', () => {
let characaterPairSupport = new CharacterPairSupport({ brackets: [['a', 'b']] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]);
});
test('only empty brackets', () => {
let characaterPairSupport = new CharacterPairSupport({ brackets: [] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []);
});
test('only surroundingPairs', () => {
let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [{ open: 'a', close: 'b' }] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b' }]);
});
test('only empty surroundingPairs', () => {
let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []);
});
test('brackets is ignored when having autoClosingPairs', () => {
let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [], brackets: [['a', 'b']] });
assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []);
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []);
});
function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean {
return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens(line), column);
}
test('shouldAutoClosePair in empty line', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] });
assert.equal(testShouldAutoClose(sup, [], 'a', 1), true);
assert.equal(testShouldAutoClose(sup, [], '{', 1), true);
});
test('shouldAutoClosePair in not interesting line 1', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] });
assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], '{', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], 'a', 3), true);
});
test('shouldAutoClosePair in not interesting line 2', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}' }] });
assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], '{', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], 'a', 3), true);
});
test('shouldAutoClosePair in interesting line 1', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] });
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 1), false);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 1), true);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 2), false);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 2), true);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 3), false);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 4), false);
assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 4), true);
});
test('shouldAutoClosePair in interesting line 2', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] });
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 1), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 1), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 2), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 2), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 4), false);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 4), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 5), false);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 5), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 6), false);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 6), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 7), true);
assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 7), true);
});
test('shouldAutoClosePair in interesting line 3', () => {
let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] });
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 1), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 1), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 2), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 2), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 3), false);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 3), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 4), false);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 4), true);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 5), false);
assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 5), true);
});
});

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* 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 { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter';
import { createFakeScopedLineTokens, TokenText } from 'vs/editor/test/common/modesTestUtils';
import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
import { LanguageIdentifier, StandardTokenType } from 'vs/editor/common/modes';
const fakeLanguageIdentifier = new LanguageIdentifier('test', 3);
suite('Editor Modes - Auto Indentation', () => {
function _testOnElectricCharacter(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number): IElectricAction {
return electricCharacterSupport.onElectricCharacter(character, createFakeScopedLineTokens(line), offset);
}
function testDoesNothing(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number): void {
let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset);
assert.deepEqual(actual, null);
}
function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, appendText: string): void {
let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset);
assert.deepEqual(actual, { appendText: appendText });
}
function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, matchOpenBracket: string): void {
let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset);
assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket });
}
test('Doc comments', () => {
var brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null);
testAppends(brackets, [
{ text: '/*', type: StandardTokenType.Other },
], '*', 3, ' */');
testDoesNothing(brackets, [
{ text: '/*', type: StandardTokenType.Other },
{ text: ' ', type: StandardTokenType.Other },
{ text: '*/', type: StandardTokenType.Other },
], '*', 3);
});
test('getElectricCharacters uses all sources and dedups', () => {
var sup = new BracketElectricCharacterSupport(
new RichEditBrackets(fakeLanguageIdentifier, [
['{', '}'],
['(', ')']
]), [
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: 'begin', close: 'end', notIn: ['string'] }
],
{ docComment: { open: '/**', close: ' */' } }
);
assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']);
});
test('auto-close', () => {
var sup = new BracketElectricCharacterSupport(
new RichEditBrackets(fakeLanguageIdentifier, [
['{', '}'],
['(', ')']
]), [
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: 'begin', close: 'end', notIn: ['string'] }
],
{ docComment: { open: '/**', close: ' */' } }
);
testDoesNothing(sup, [], 'a', 0);
testDoesNothing(sup, [{ text: 'egi', type: StandardTokenType.Other }], 'b', 1);
testDoesNothing(sup, [{ text: 'bgi', type: StandardTokenType.Other }], 'e', 2);
testDoesNothing(sup, [{ text: 'bei', type: StandardTokenType.Other }], 'g', 3);
testDoesNothing(sup, [{ text: 'beg', type: StandardTokenType.Other }], 'i', 4);
testDoesNothing(sup, [{ text: 'egin', type: StandardTokenType.Other }], 'b', 1);
testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], 'e', 2);
testDoesNothing(sup, [{ text: 'bein', type: StandardTokenType.Other }], 'g', 3);
testDoesNothing(sup, [{ text: 'begn', type: StandardTokenType.Other }], 'i', 4);
testAppends(sup, [{ text: 'begi', type: StandardTokenType.Other }], 'n', 5, 'end');
testDoesNothing(sup, [{ text: '3gin', type: StandardTokenType.Other }], 'b', 1);
testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], '3', 2);
testDoesNothing(sup, [{ text: 'b3in', type: StandardTokenType.Other }], 'g', 3);
testDoesNothing(sup, [{ text: 'b3gn', type: StandardTokenType.Other }], 'i', 4);
testDoesNothing(sup, [{ text: 'b3gi', type: StandardTokenType.Other }], 'n', 5);
testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.String }], 'n', 5);
testAppends(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.Other }], 'n', 6, 'end');
testDoesNothing(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.String }], 'n', 6);
testAppends(sup, [{ text: '/*', type: StandardTokenType.String }], '*', 3, ' */');
testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.Other }, { text: 'end', type: StandardTokenType.Other }], 'n', 5);
});
test('matchOpenBracket', () => {
var sup = new BracketElectricCharacterSupport(
new RichEditBrackets(fakeLanguageIdentifier, [
['{', '}'],
['(', ')']
]), [
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: 'begin', close: 'end', notIn: ['string'] }
],
{ docComment: { open: '/**', close: ' */' } }
);
testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 1);
testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 2);
testDoesNothing(sup, [{ text: '\t\t', type: StandardTokenType.Other }], '{', 3);
testDoesNothing(sup, [{ text: '\t}', type: StandardTokenType.Other }], '\t', 1);
testDoesNothing(sup, [{ text: '\t}', type: StandardTokenType.Other }], '\t', 2);
testMatchBracket(sup, [{ text: '\t\t', type: StandardTokenType.Other }], '}', 3, '}');
});
});

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* 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 { CharacterPair, IndentAction } from 'vs/editor/common/modes/languageConfiguration';
import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter';
suite('OnEnter', () => {
test('uses brackets', () => {
var brackets: CharacterPair[] = [
['(', ')'],
['begin', 'end']
];
var support = new OnEnterSupport({
brackets: brackets
});
var testIndentAction = (beforeText: string, afterText: string, expected: IndentAction) => {
var actual = support.onEnter('', beforeText, afterText);
if (expected === IndentAction.None) {
assert.equal(actual, null);
} else {
assert.equal(actual.indentAction, expected);
}
};
testIndentAction('a', '', IndentAction.None);
testIndentAction('', 'b', IndentAction.None);
testIndentAction('(', 'b', IndentAction.Indent);
testIndentAction('a', ')', IndentAction.None);
testIndentAction('begin', 'ending', IndentAction.Indent);
testIndentAction('abegin', 'end', IndentAction.None);
testIndentAction('begin', ')', IndentAction.Indent);
testIndentAction('begin', 'end', IndentAction.IndentOutdent);
testIndentAction('begin ', ' end', IndentAction.IndentOutdent);
testIndentAction(' begin', 'end//as', IndentAction.IndentOutdent);
testIndentAction('(', ')', IndentAction.IndentOutdent);
testIndentAction('( ', ')', IndentAction.IndentOutdent);
testIndentAction('a(', ')b', IndentAction.IndentOutdent);
testIndentAction('(', '', IndentAction.Indent);
testIndentAction('(', 'foo', IndentAction.Indent);
testIndentAction('begin', 'foo', IndentAction.Indent);
testIndentAction('begin', '', IndentAction.Indent);
});
test('uses regExpRules', () => {
var support = new OnEnterSupport({
regExpRules: [
{
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
},
{
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
action: { indentAction: IndentAction.None, appendText: ' * ' }
},
{
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: IndentAction.None, appendText: '* ' }
},
{
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
},
{
beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
}
]
});
var testIndentAction = (beforeText: string, afterText: string, expectedIndentAction: IndentAction, expectedAppendText: string, removeText: number = 0) => {
var actual = support.onEnter('', beforeText, afterText);
if (expectedIndentAction === null) {
assert.equal(actual, null, 'isNull:' + beforeText);
} else {
assert.equal(actual !== null, true, 'isNotNull:' + beforeText);
assert.equal(actual.indentAction, expectedIndentAction, 'indentAction:' + beforeText);
if (expectedAppendText !== null) {
assert.equal(actual.appendText, expectedAppendText, 'appendText:' + beforeText);
}
if (removeText !== 0) {
assert.equal(actual.removeText, removeText, 'removeText:' + beforeText);
}
}
};
testIndentAction('\t/**', ' */', IndentAction.IndentOutdent, ' * ');
testIndentAction('\t/**', '', IndentAction.None, ' * ');
testIndentAction('\t/** * / * / * /', '', IndentAction.None, ' * ');
testIndentAction('\t/** /*', '', IndentAction.None, ' * ');
testIndentAction('/**', '', IndentAction.None, ' * ');
testIndentAction('\t/**/', '', null, null);
testIndentAction('\t/***/', '', null, null);
testIndentAction('\t/*******/', '', null, null);
testIndentAction('\t/** * * * * */', '', null, null);
testIndentAction('\t/** */', '', null, null);
testIndentAction('\t/** asdfg */', '', null, null);
testIndentAction('\t/* asdfg */', '', null, null);
testIndentAction('\t/* asdfg */', '', null, null);
testIndentAction('\t/** asdfg */', '', null, null);
testIndentAction('*/', '', null, null);
testIndentAction('\t/*', '', null, null);
testIndentAction('\t*', '', null, null);
testIndentAction('\t *', '', IndentAction.None, '* ');
testIndentAction('\t */', '', IndentAction.None, null, 1);
testIndentAction('\t * */', '', IndentAction.None, null, 1);
testIndentAction('\t * * / * / * / */', '', null, null);
testIndentAction('\t * ', '', IndentAction.None, '* ');
testIndentAction(' * ', '', IndentAction.None, '* ');
testIndentAction(' * asdfsfagadfg', '', IndentAction.None, '* ');
testIndentAction(' * asdfsfagadfg * * * ', '', IndentAction.None, '* ');
testIndentAction(' * /*', '', IndentAction.None, '* ');
testIndentAction(' * asdfsfagadfg * / * / * /', '', IndentAction.None, '* ');
testIndentAction(' * asdfsfagadfg * / * / * /*', '', IndentAction.None, '* ');
testIndentAction(' */', '', IndentAction.None, null, 1);
testIndentAction('\t */', '', IndentAction.None, null, 1);
testIndentAction('\t\t */', '', IndentAction.None, null, 1);
testIndentAction(' */', '', IndentAction.None, null, 1);
testIndentAction(' */', '', IndentAction.None, null, 1);
testIndentAction('\t */', '', IndentAction.None, null, 1);
testIndentAction(' *--------------------------------------------------------------------------------------------*/', '', IndentAction.None, null, 1);
});
});

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* 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 { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets';
import { Range } from 'vs/editor/common/core/range';
suite('richEditBrackets', () => {
function findPrevBracketInToken(reversedBracketRegex: RegExp, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range {
return BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, lineText, currentTokenStart, currentTokenEnd);
}
function findNextBracketInToken(forwardBracketRegex: RegExp, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range {
return BracketsUtils.findNextBracketInToken(forwardBracketRegex, 1, lineText, currentTokenStart, currentTokenEnd);
}
test('findPrevBracketInToken one char 1', () => {
let result = findPrevBracketInToken(/(\{)|(\})/i, '{', 0, 1);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 2);
});
test('findPrevBracketInToken one char 2', () => {
let result = findPrevBracketInToken(/(\{)|(\})/i, '{{', 0, 1);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 2);
});
test('findPrevBracketInToken one char 3', () => {
let result = findPrevBracketInToken(/(\{)|(\})/i, '{hello world!', 0, 13);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 2);
});
test('findPrevBracketInToken more chars 1', () => {
let result = findPrevBracketInToken(/(olleh)/i, 'hello world!', 0, 12);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 6);
});
test('findPrevBracketInToken more chars 2', () => {
let result = findPrevBracketInToken(/(olleh)/i, 'hello world!', 0, 5);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 6);
});
test('findPrevBracketInToken more chars 3', () => {
let result = findPrevBracketInToken(/(olleh)/i, ' hello world!', 0, 6);
assert.equal(result.startColumn, 2);
assert.equal(result.endColumn, 7);
});
test('findNextBracketInToken one char', () => {
let result = findNextBracketInToken(/(\{)|(\})/i, '{', 0, 1);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 2);
});
test('findNextBracketInToken more chars', () => {
let result = findNextBracketInToken(/(world)/i, 'hello world!', 0, 12);
assert.equal(result.startColumn, 7);
assert.equal(result.endColumn, 12);
});
test('issue #3894: [Handlebars] Curly braces edit issues', () => {
let result = findPrevBracketInToken(/(\-\-!<)|(>\-\-)|(\{\{)|(\}\})/i, '{{asd}}', 0, 2);
assert.equal(result.startColumn, 1);
assert.equal(result.endColumn, 3);
});
});

View File

@@ -0,0 +1,333 @@
/*---------------------------------------------------------------------------------------------
* 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 { strcmp, parseTokenTheme, TokenTheme, ParsedTokenThemeRule, ColorMap, ExternalThemeTrieElement, ThemeTrieElementRule } from 'vs/editor/common/modes/supports/tokenization';
import { FontStyle } from 'vs/editor/common/modes';
suite('Token theme matching', () => {
test('gives higher priority to deeper matches', () => {
let theme = TokenTheme.createFromRawTokenTheme([
{ token: '', foreground: '100000', background: '200000' },
{ token: 'punctuation.definition.string.begin.html', foreground: '300000' },
{ token: 'punctuation.definition.string', foreground: '400000' },
]);
let colorMap = new ColorMap();
colorMap.getId('100000');
const _B = colorMap.getId('200000');
colorMap.getId('400000');
const _D = colorMap.getId('300000');
let actual = theme._match('punctuation.definition.string.begin.html');
assert.deepEqual(actual, new ThemeTrieElementRule(FontStyle.None, _D, _B));
});
test('can match', () => {
let theme = TokenTheme.createFromRawTokenTheme([
{ token: '', foreground: 'F8F8F2', background: '272822' },
{ token: 'source', background: '100000' },
{ token: 'something', background: '100000' },
{ token: 'bar', background: '200000' },
{ token: 'baz', background: '200000' },
{ token: 'bar', fontStyle: 'bold' },
{ token: 'constant', fontStyle: 'italic', foreground: '300000' },
{ token: 'constant.numeric', foreground: '400000' },
{ token: 'constant.numeric.hex', fontStyle: 'bold' },
{ token: 'constant.numeric.oct', fontStyle: 'bold italic underline' },
{ token: 'constant.numeric.dec', fontStyle: '', foreground: '500000' },
{ token: 'storage.object.bar', fontStyle: '', foreground: '600000' },
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('F8F8F2');
const _B = colorMap.getId('272822');
const _C = colorMap.getId('200000');
const _D = colorMap.getId('300000');
const _E = colorMap.getId('400000');
const _F = colorMap.getId('500000');
const _G = colorMap.getId('100000');
const _H = colorMap.getId('600000');
function assertMatch(scopeName: string, expected: ThemeTrieElementRule): void {
let actual = theme._match(scopeName);
assert.deepEqual(actual, expected, 'when matching <<' + scopeName + '>>');
}
function assertSimpleMatch(scopeName: string, fontStyle: FontStyle, foreground: number, background: number): void {
assertMatch(scopeName, new ThemeTrieElementRule(fontStyle, foreground, background));
}
function assertNoMatch(scopeName: string): void {
assertMatch(scopeName, new ThemeTrieElementRule(FontStyle.None, _A, _B));
}
// matches defaults
assertNoMatch('');
assertNoMatch('bazz');
assertNoMatch('asdfg');
// matches source
assertSimpleMatch('source', FontStyle.None, _A, _G);
assertSimpleMatch('source.ts', FontStyle.None, _A, _G);
assertSimpleMatch('source.tss', FontStyle.None, _A, _G);
// matches something
assertSimpleMatch('something', FontStyle.None, _A, _G);
assertSimpleMatch('something.ts', FontStyle.None, _A, _G);
assertSimpleMatch('something.tss', FontStyle.None, _A, _G);
// matches baz
assertSimpleMatch('baz', FontStyle.None, _A, _C);
assertSimpleMatch('baz.ts', FontStyle.None, _A, _C);
assertSimpleMatch('baz.tss', FontStyle.None, _A, _C);
// matches constant
assertSimpleMatch('constant', FontStyle.Italic, _D, _B);
assertSimpleMatch('constant.string', FontStyle.Italic, _D, _B);
assertSimpleMatch('constant.hex', FontStyle.Italic, _D, _B);
// matches constant.numeric
assertSimpleMatch('constant.numeric', FontStyle.Italic, _E, _B);
assertSimpleMatch('constant.numeric.baz', FontStyle.Italic, _E, _B);
// matches constant.numeric.hex
assertSimpleMatch('constant.numeric.hex', FontStyle.Bold, _E, _B);
assertSimpleMatch('constant.numeric.hex.baz', FontStyle.Bold, _E, _B);
// matches constant.numeric.oct
assertSimpleMatch('constant.numeric.oct', FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _E, _B);
assertSimpleMatch('constant.numeric.oct.baz', FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _E, _B);
// matches constant.numeric.dec
assertSimpleMatch('constant.numeric.dec', FontStyle.None, _F, _B);
assertSimpleMatch('constant.numeric.dec.baz', FontStyle.None, _F, _B);
// matches storage.object.bar
assertSimpleMatch('storage.object.bar', FontStyle.None, _H, _B);
assertSimpleMatch('storage.object.bar.baz', FontStyle.None, _H, _B);
// does not match storage.object.bar
assertSimpleMatch('storage.object.bart', FontStyle.None, _A, _B);
assertSimpleMatch('storage.object', FontStyle.None, _A, _B);
assertSimpleMatch('storage', FontStyle.None, _A, _B);
assertSimpleMatch('bar', FontStyle.Bold, _A, _C);
});
});
suite('Token theme parsing', () => {
test('can parse', () => {
let actual = parseTokenTheme([
{ token: '', foreground: 'F8F8F2', background: '272822' },
{ token: 'source', background: '100000' },
{ token: 'something', background: '100000' },
{ token: 'bar', background: '010000' },
{ token: 'baz', background: '010000' },
{ token: 'bar', fontStyle: 'bold' },
{ token: 'constant', fontStyle: 'italic', foreground: 'ff0000' },
{ token: 'constant.numeric', foreground: '00ff00' },
{ token: 'constant.numeric.hex', fontStyle: 'bold' },
{ token: 'constant.numeric.oct', fontStyle: 'bold italic underline' },
{ token: 'constant.numeric.dec', fontStyle: '', foreground: '0000ff' },
]);
let expected = [
new ParsedTokenThemeRule('', 0, FontStyle.NotSet, 'F8F8F2', '272822'),
new ParsedTokenThemeRule('source', 1, FontStyle.NotSet, null, '100000'),
new ParsedTokenThemeRule('something', 2, FontStyle.NotSet, null, '100000'),
new ParsedTokenThemeRule('bar', 3, FontStyle.NotSet, null, '010000'),
new ParsedTokenThemeRule('baz', 4, FontStyle.NotSet, null, '010000'),
new ParsedTokenThemeRule('bar', 5, FontStyle.Bold, null, null),
new ParsedTokenThemeRule('constant', 6, FontStyle.Italic, 'ff0000', null),
new ParsedTokenThemeRule('constant.numeric', 7, FontStyle.NotSet, '00ff00', null),
new ParsedTokenThemeRule('constant.numeric.hex', 8, FontStyle.Bold, null, null),
new ParsedTokenThemeRule('constant.numeric.oct', 9, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null),
new ParsedTokenThemeRule('constant.numeric.dec', 10, FontStyle.None, '0000ff', null),
];
assert.deepEqual(actual, expected);
});
});
suite('Token theme resolving', () => {
test('strcmp works', () => {
let actual = ['bar', 'z', 'zu', 'a', 'ab', ''].sort(strcmp);
let expected = ['', 'a', 'ab', 'bar', 'z', 'zu'];
assert.deepEqual(actual, expected);
});
test('always has defaults', () => {
let actual = TokenTheme.createFromParsedTokenTheme([]);
let colorMap = new ColorMap();
const _A = colorMap.getId('000000');
const _B = colorMap.getId('ffffff');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B)));
});
test('respects incoming defaults 1', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, null)
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('000000');
const _B = colorMap.getId('ffffff');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B)));
});
test('respects incoming defaults 2', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.None, null, null)
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('000000');
const _B = colorMap.getId('ffffff');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B)));
});
test('respects incoming defaults 3', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null)
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('000000');
const _B = colorMap.getId('ffffff');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _A, _B)));
});
test('respects incoming defaults 4', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'ff0000', null)
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('ff0000');
const _B = colorMap.getId('ffffff');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B)));
});
test('respects incoming defaults 5', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000')
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('000000');
const _B = colorMap.getId('ff0000');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B)));
});
test('can merge incoming defaults', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000'),
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, '00ff00', null),
new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null),
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('00ff00');
const _B = colorMap.getId('ff0000');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _A, _B)));
});
test('defaults are inherited', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'),
new ParsedTokenThemeRule('var', -1, FontStyle.NotSet, 'ff0000', null)
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('F8F8F2');
const _B = colorMap.getId('272822');
const _C = colorMap.getId('ff0000');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), {
'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _C, _B))
});
assert.deepEqual(actual.getThemeTrieElement(), root);
});
test('same rules get merged', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'),
new ParsedTokenThemeRule('var', 1, FontStyle.Bold, null, null),
new ParsedTokenThemeRule('var', 0, FontStyle.NotSet, 'ff0000', null),
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('F8F8F2');
const _B = colorMap.getId('272822');
const _C = colorMap.getId('ff0000');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), {
'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _C, _B))
});
assert.deepEqual(actual.getThemeTrieElement(), root);
});
test('rules are inherited 1', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'),
new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null),
new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null),
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('F8F8F2');
const _B = colorMap.getId('272822');
const _C = colorMap.getId('ff0000');
const _D = colorMap.getId('00ff00');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), {
'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _C, _B), {
'identifier': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _D, _B))
})
});
assert.deepEqual(actual.getThemeTrieElement(), root);
});
test('rules are inherited 2', () => {
let actual = TokenTheme.createFromParsedTokenTheme([
new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'),
new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null),
new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null),
new ParsedTokenThemeRule('constant', 4, FontStyle.Italic, '100000', null),
new ParsedTokenThemeRule('constant.numeric', 5, FontStyle.NotSet, '200000', null),
new ParsedTokenThemeRule('constant.numeric.hex', 6, FontStyle.Bold, null, null),
new ParsedTokenThemeRule('constant.numeric.oct', 7, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null),
new ParsedTokenThemeRule('constant.numeric.dec', 8, FontStyle.None, '300000', null),
]);
let colorMap = new ColorMap();
const _A = colorMap.getId('F8F8F2');
const _B = colorMap.getId('272822');
const _C = colorMap.getId('100000');
const _D = colorMap.getId('200000');
const _E = colorMap.getId('300000');
const _F = colorMap.getId('ff0000');
const _G = colorMap.getId('00ff00');
assert.deepEqual(actual.getColorMap(), colorMap.getColorMap());
let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), {
'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _F, _B), {
'identifier': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _G, _B))
}),
'constant': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Italic, _C, _B), {
'numeric': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Italic, _D, _B), {
'hex': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _D, _B)),
'oct': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _D, _B)),
'dec': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _E, _B)),
})
})
});
assert.deepEqual(actual.getThemeTrieElement(), root);
});
});

View File

@@ -0,0 +1,229 @@
/*---------------------------------------------------------------------------------------------
* 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 { TokenizationRegistry, IState, LanguageIdentifier, ColorId, FontStyle, MetadataConsts } from 'vs/editor/common/modes';
import { tokenizeToString, tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
suite('Editor Modes - textToHtmlTokenizer', () => {
function toStr(pieces: { className: string; text: string }[]): string {
let resultArr = pieces.map((t) => `<span class="${t.className}">${t.text}</span>`);
return resultArr.join('');
}
test('TextToHtmlTokenizer 1', () => {
let mode = new Mode();
let actual = tokenizeToString('.abc..def...gh', mode.getId());
let expected = [
{ className: 'mtk7', text: '.' },
{ className: 'mtk9', text: 'abc' },
{ className: 'mtk7', text: '..' },
{ className: 'mtk9', text: 'def' },
{ className: 'mtk7', text: '...' },
{ className: 'mtk9', text: 'gh' },
];
let expectedStr = `<div class="monaco-tokenized-source">${toStr(expected)}</div>`;
assert.equal(actual, expectedStr);
mode.dispose();
});
test('TextToHtmlTokenizer 2', () => {
let mode = new Mode();
let actual = tokenizeToString('.abc..def...gh\n.abc..def...gh', mode.getId());
let expected1 = [
{ className: 'mtk7', text: '.' },
{ className: 'mtk9', text: 'abc' },
{ className: 'mtk7', text: '..' },
{ className: 'mtk9', text: 'def' },
{ className: 'mtk7', text: '...' },
{ className: 'mtk9', text: 'gh' },
];
let expected2 = [
{ className: 'mtk7', text: '.' },
{ className: 'mtk9', text: 'abc' },
{ className: 'mtk7', text: '..' },
{ className: 'mtk9', text: 'def' },
{ className: 'mtk7', text: '...' },
{ className: 'mtk9', text: 'gh' },
];
let expectedStr1 = toStr(expected1);
let expectedStr2 = toStr(expected2);
let expectedStr = `<div class="monaco-tokenized-source">${expectedStr1}<br/>${expectedStr2}</div>`;
assert.equal(actual, expectedStr);
mode.dispose();
});
test('tokenizeLineToHTML', () => {
const text = 'Ciao hello world!';
const lineTokens = [
new ViewLineToken(
4,
(
(3 << MetadataConsts.FOREGROUND_OFFSET)
| ((FontStyle.Bold | FontStyle.Italic) << MetadataConsts.FONT_STYLE_OFFSET)
) >>> 0
),
new ViewLineToken(
5,
(
(1 << MetadataConsts.FOREGROUND_OFFSET)
) >>> 0
),
new ViewLineToken(
10,
(
(4 << MetadataConsts.FOREGROUND_OFFSET)
) >>> 0
),
new ViewLineToken(
11,
(
(1 << MetadataConsts.FOREGROUND_OFFSET)
) >>> 0
),
new ViewLineToken(
17,
(
(5 << MetadataConsts.FOREGROUND_OFFSET)
| ((FontStyle.Underline) << MetadataConsts.FONT_STYLE_OFFSET)
) >>> 0
)
];
const colorMap = [null, '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff'];
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4),
[
'<div>',
'<span style="color: #ff0000;font-style: italic;font-weight: bold;">Ciao</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #0000ff;text-decoration: underline;">world!</span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 0, 12, 4),
[
'<div>',
'<span style="color: #ff0000;font-style: italic;font-weight: bold;">Ciao</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #0000ff;text-decoration: underline;">w</span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 0, 11, 4),
[
'<div>',
'<span style="color: #ff0000;font-style: italic;font-weight: bold;">Ciao</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 1, 11, 4),
[
'<div>',
'<span style="color: #ff0000;font-style: italic;font-weight: bold;">iao</span>',
'<span style="color: #000000;"> </span>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 4, 11, 4),
[
'<div>',
'<span style="color: #000000;"> </span>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 5, 11, 4),
[
'<div>',
'<span style="color: #00ff00;">hello</span>',
'<span style="color: #000000;"> </span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 5, 10, 4),
[
'<div>',
'<span style="color: #00ff00;">hello</span>',
'</div>'
].join('')
);
assert.equal(
tokenizeLineToHTML(text, lineTokens, colorMap, 6, 9, 4),
[
'<div>',
'<span style="color: #00ff00;">ell</span>',
'</div>'
].join('')
);
});
});
class Mode extends MockMode {
private static _id = new LanguageIdentifier('textToHtmlTokenizerMode', 3);
constructor() {
super(Mode._id);
this._register(TokenizationRegistry.register(this.getId(), {
getInitialState: (): IState => null,
tokenize: undefined,
tokenize2: (line: string, state: IState): TokenizationResult2 => {
let tokensArr: number[] = [];
let prevColor: ColorId = -1;
for (let i = 0; i < line.length; i++) {
let colorId = line.charAt(i) === '.' ? 7 : 9;
if (prevColor !== colorId) {
tokensArr.push(i);
tokensArr.push((
colorId << MetadataConsts.FOREGROUND_OFFSET
) >>> 0);
}
prevColor = colorId;
}
let tokens = new Uint32Array(tokensArr.length);
for (let i = 0; i < tokens.length; i++) {
tokens[i] = tokensArr[i];
}
return new TokenizationResult2(tokens, null);
}
}));
}
}

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 { LineTokens } from 'vs/editor/common/core/lineTokens';
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports';
import { StandardTokenType, MetadataConsts } from 'vs/editor/common/modes';
export interface TokenText {
text: string;
type: StandardTokenType;
}
export function createFakeScopedLineTokens(rawTokens: TokenText[]): ScopedLineTokens {
let tokens = new Uint32Array(rawTokens.length << 1);
let line = '';
for (let i = 0, len = rawTokens.length; i < len; i++) {
let rawToken = rawTokens[i];
let startOffset = line.length;
let metadata = (
(rawToken.type << MetadataConsts.TOKEN_TYPE_OFFSET)
) >>> 0;
tokens[(i << 1)] = startOffset;
tokens[(i << 1) + 1] = metadata;
line += rawToken.text;
}
return createScopedLineTokens(new LineTokens(tokens, line), 0);
}

View File

@@ -0,0 +1,171 @@
/*---------------------------------------------------------------------------------------------
* 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 { EditorSimpleWorkerImpl, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker';
import { Range } from 'vs/editor/common/core/range';
suite('EditorSimpleWorker', () => {
class WorkerWithModels extends EditorSimpleWorkerImpl {
getModel(uri: string) {
return this._getModel(uri);
}
addModel(lines: string[], eol: string = '\n') {
const uri = 'test:file#' + Date.now();
this.acceptNewModel({
url: uri,
versionId: 1,
lines: lines,
EOL: eol
});
return this._getModel(uri);
}
}
let worker: WorkerWithModels;
let model: ICommonModel;
setup(() => {
worker = new WorkerWithModels();
model = worker.addModel([
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
]);
});
function assertPositionAt(offset: number, line: number, column: number) {
let position = model.positionAt(offset);
assert.equal(position.lineNumber, line);
assert.equal(position.column, column);
}
function assertOffsetAt(lineNumber: number, column: number, offset: number) {
let actual = model.offsetAt({ lineNumber, column });
assert.equal(actual, offset);
}
test('ICommonModel#offsetAt', function () {
assertOffsetAt(1, 1, 0);
assertOffsetAt(1, 2, 1);
assertOffsetAt(1, 17, 16);
assertOffsetAt(2, 1, 17);
assertOffsetAt(2, 4, 20);
assertOffsetAt(3, 1, 45);
assertOffsetAt(5, 30, 95);
assertOffsetAt(5, 31, 95);
assertOffsetAt(5, Number.MAX_VALUE, 95);
assertOffsetAt(6, 30, 95);
assertOffsetAt(Number.MAX_VALUE, 30, 95);
assertOffsetAt(Number.MAX_VALUE, Number.MAX_VALUE, 95);
});
test('ICommonModel#positionAt', function () {
assertPositionAt(0, 1, 1);
assertPositionAt(Number.MIN_VALUE, 1, 1);
assertPositionAt(1, 1, 2);
assertPositionAt(16, 1, 17);
assertPositionAt(17, 2, 1);
assertPositionAt(20, 2, 4);
assertPositionAt(45, 3, 1);
assertPositionAt(95, 4, 30);
assertPositionAt(96, 4, 30);
assertPositionAt(99, 4, 30);
assertPositionAt(Number.MAX_VALUE, 4, 30);
});
test('ICommonModel#validatePosition, issue #15882', function () {
let model = worker.addModel(['{"id": "0001","type": "donut","name": "Cake","image":{"url": "images/0001.jpg","width": 200,"height": 200},"thumbnail":{"url": "images/thumbnails/0001.jpg","width": 32,"height": 32}}']);
assert.equal(model.offsetAt({ lineNumber: 1, column: 2 }), 1);
});
test('MoreMinimal', function () {
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');
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 15 });
});
});
test('MoreMinimal, issue #15385 newline changes only', function () {
let model = worker.addModel([
'{',
'\t"a":1',
'}'
], '\n');
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);
});
});
test('MoreMinimal, issue #15385 newline changes and other', function () {
let model = worker.addModel([
'{',
'\t"a":1',
'}'
], '\n');
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');
assert.deepEqual(first.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 2, endColumn: 4 });
});
});
test('MoreMinimal, issue #15385 newline changes and other', function () {
let model = worker.addModel([
'package main', // 1
'func foo() {', // 2
'}' // 3
]);
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');
assert.deepEqual(first.range, { startLineNumber: 3, startColumn: 2, endLineNumber: 3, endColumn: 2 });
});
});
test('ICommonModel#getValueInRange, issue #17424', function () {
let model = worker.addModel([
'package main', // 1
'func foo() {', // 2
'}' // 3
]);
const value = model.getValueInRange({ startLineNumber: 3, startColumn: 1, endLineNumber: 4, endColumn: 1 });
assert.equal(value, '}');
});
test('textualSuggest, issue #17785', function () {
let model = worker.addModel([
'foobar', // 1
'f f' // 2
]);
return worker.textualSuggest(model.uri.toString(), { lineNumber: 2, column: 2 }, '[a-z]+', 'img').then((result) => {
const { suggestions } = result;
assert.equal(suggestions.length, 1);
assert.equal(suggestions[0].label, 'foobar');
});
});
});

View File

@@ -0,0 +1,269 @@
/*---------------------------------------------------------------------------------------------
* 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 { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry';
suite('LanguagesRegistry', () => {
test('output mode does not have a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'outputModeId',
extensions: [],
aliases: [null],
mimetypes: ['outputModeMimeType'],
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), []);
});
test('mode with alias does have a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId',
extensions: [],
aliases: ['ModeName'],
mimetypes: ['bla'],
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']);
assert.deepEqual(registry.getLanguageName('modeId'), 'ModeName');
});
test('mode without alias gets a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId',
extensions: [],
mimetypes: ['bla'],
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['modeId']);
assert.deepEqual(registry.getLanguageName('modeId'), 'modeId');
});
test('bug #4360: f# not shown in status bar', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext1'],
aliases: ['ModeName'],
mimetypes: ['bla'],
}]);
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext2'],
aliases: [],
mimetypes: ['bla'],
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']);
assert.deepEqual(registry.getLanguageName('modeId'), 'ModeName');
});
test('issue #5278: Extension cannot override language name anymore', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext1'],
aliases: ['ModeName'],
mimetypes: ['bla'],
}]);
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext2'],
aliases: ['BetterModeName'],
mimetypes: ['bla'],
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['BetterModeName']);
assert.deepEqual(registry.getLanguageName('modeId'), 'BetterModeName');
});
test('mimetypes are generated if necessary', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId'
}]);
assert.deepEqual(registry.getMimeForMode('modeId'), 'text/x-modeId');
});
test('first mimetype wins', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId',
mimetypes: ['text/modeId', 'text/modeId2']
}]);
assert.deepEqual(registry.getMimeForMode('modeId'), 'text/modeId');
});
test('first mimetype wins 2', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'modeId'
}]);
registry._registerLanguages([{
id: 'modeId',
mimetypes: ['text/modeId']
}]);
assert.deepEqual(registry.getMimeForMode('modeId'), 'text/x-modeId');
});
test('aliases', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'a'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']);
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a');
assert.deepEqual(registry.getLanguageName('a'), 'a');
registry._registerLanguages([{
id: 'a',
aliases: ['A1', 'A2']
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['A1']);
assert.deepEqual(registry.getModeIdsFromLanguageName('a'), []);
assert.deepEqual(registry.getModeIdsFromLanguageName('A1'), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('A2'), []);
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a1'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a2'), 'a');
assert.deepEqual(registry.getLanguageName('a'), 'A1');
registry._registerLanguages([{
id: 'a',
aliases: ['A3', 'A4']
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['A3']);
assert.deepEqual(registry.getModeIdsFromLanguageName('a'), []);
assert.deepEqual(registry.getModeIdsFromLanguageName('A1'), []);
assert.deepEqual(registry.getModeIdsFromLanguageName('A2'), []);
assert.deepEqual(registry.getModeIdsFromLanguageName('A3'), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('A4'), []);
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a1'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a2'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a3'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a4'), 'a');
assert.deepEqual(registry.getLanguageName('a'), 'A3');
});
test('empty aliases array means no alias', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'a'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']);
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a');
assert.deepEqual(registry.getLanguageName('a'), 'a');
registry._registerLanguages([{
id: 'b',
aliases: []
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']);
assert.deepEqual(registry.getModeIdsFromLanguageName('b'), []);
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a');
assert.deepEqual(registry.getModeIdForLanguageNameLowercase('b'), 'b');
assert.deepEqual(registry.getLanguageName('a'), 'a');
assert.deepEqual(registry.getLanguageName('b'), null);
});
test('extensions', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'a',
aliases: ['aName'],
extensions: ['aExt']
}]);
assert.deepEqual(registry.getExtensions('a'), []);
assert.deepEqual(registry.getExtensions('aname'), []);
assert.deepEqual(registry.getExtensions('aName'), ['aExt']);
registry._registerLanguages([{
id: 'a',
extensions: ['aExt2']
}]);
assert.deepEqual(registry.getExtensions('a'), []);
assert.deepEqual(registry.getExtensions('aname'), []);
assert.deepEqual(registry.getExtensions('aName'), ['aExt', 'aExt2']);
});
test('filenames', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'a',
aliases: ['aName'],
filenames: ['aFilename']
}]);
assert.deepEqual(registry.getFilenames('a'), []);
assert.deepEqual(registry.getFilenames('aname'), []);
assert.deepEqual(registry.getFilenames('aName'), ['aFilename']);
registry._registerLanguages([{
id: 'a',
filenames: ['aFilename2']
}]);
assert.deepEqual(registry.getFilenames('a'), []);
assert.deepEqual(registry.getFilenames('aname'), []);
assert.deepEqual(registry.getFilenames('aName'), ['aFilename', 'aFilename2']);
});
test('configuration', () => {
let registry = new LanguagesRegistry(false);
registry._registerLanguages([{
id: 'a',
aliases: ['aName'],
configuration: 'aFilename'
}]);
assert.deepEqual(registry.getConfigurationFiles('a'), ['aFilename']);
assert.deepEqual(registry.getConfigurationFiles('aname'), []);
assert.deepEqual(registry.getConfigurationFiles('aName'), []);
registry._registerLanguages([{
id: 'a',
configuration: 'aFilename2'
}]);
assert.deepEqual(registry.getConfigurationFiles('a'), ['aFilename', 'aFilename2']);
assert.deepEqual(registry.getConfigurationFiles('aname'), []);
assert.deepEqual(registry.getConfigurationFiles('aName'), []);
});
});

View File

@@ -0,0 +1,333 @@
/*---------------------------------------------------------------------------------------------
* 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 { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import URI from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import { DefaultEndOfLine } from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { TextSource } from 'vs/editor/common/model/textSource';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { CharCode } from 'vs/base/common/charCode';
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder';
const GENERATE_TESTS = false;
suite('ModelService', () => {
let modelService: ModelServiceImpl;
setup(() => {
const configService = new TestConfigurationService();
configService.setUserConfiguration('files', { 'eol': '\n' });
configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));
modelService = new ModelServiceImpl(null, configService);
});
teardown(() => {
modelService.dispose();
});
test('EOL setting respected depending on root', () => {
const model1 = modelService.createModel('farboo', null, null);
const model2 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\myroot\\myfile.txt' : '/myroot/myfile.txt'));
const model3 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\other\\myfile.txt' : '/other/myfile.txt'));
assert.equal(model1.getOptions().defaultEOL, DefaultEndOfLine.LF);
assert.equal(model2.getOptions().defaultEOL, DefaultEndOfLine.CRLF);
assert.equal(model3.getOptions().defaultEOL, DefaultEndOfLine.LF);
});
test('_computeEdits first line changed', function () {
const model = Model.createFromString(
[
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
].join('\n')
);
const textSource = TextSource.fromString(
[
'This is line One', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
].join('\n'),
DefaultEndOfLine.LF
);
const actual = ModelServiceImpl._computeEdits(model, textSource);
assert.deepEqual(actual, [
EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One')
]);
});
test('_computeEdits EOL changed', function () {
const model = Model.createFromString(
[
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
].join('\n')
);
const textSource = TextSource.fromString(
[
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
].join('\r\n'),
DefaultEndOfLine.LF
);
const actual = ModelServiceImpl._computeEdits(model, textSource);
assert.deepEqual(actual, []);
});
test('_computeEdits EOL and other change 1', function () {
const model = Model.createFromString(
[
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
].join('\n')
);
const textSource = TextSource.fromString(
[
'This is line One', //16
'and this is line number two', //27
'It is followed by #3', //20
'and finished with the fourth.', //29
].join('\r\n'),
DefaultEndOfLine.LF
);
const actual = ModelServiceImpl._computeEdits(model, textSource);
assert.deepEqual(actual, [
EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One'),
EditOperation.replace(new Range(3, 1, 3, 21), 'It is followed by #3')
]);
});
test('_computeEdits EOL and other change 2', function () {
const model = Model.createFromString(
[
'package main', // 1
'func foo() {', // 2
'}' // 3
].join('\n')
);
const textSource = TextSource.fromString(
[
'package main', // 1
'func foo() {', // 2
'}', // 3
''
].join('\r\n'),
DefaultEndOfLine.LF
);
const actual = ModelServiceImpl._computeEdits(model, textSource);
assert.deepEqual(actual, [
EditOperation.replace(new Range(3, 2, 3, 2), '\n')
]);
});
test('generated1', () => {
const file1 = ['pram', 'okctibad', 'pjuwtemued', 'knnnm', 'u', ''];
const file2 = ['tcnr', 'rxwlicro', 'vnzy', '', '', 'pjzcogzur', 'ptmxyp', 'dfyshia', 'pee', 'ygg'];
assertComputeEdits(file1, file2);
});
test('generated2', () => {
const file1 = ['', 'itls', 'hrilyhesv', ''];
const file2 = ['vdl', '', 'tchgz', 'bhx', 'nyl'];
assertComputeEdits(file1, file2);
});
test('generated3', () => {
const file1 = ['ubrbrcv', 'wv', 'xodspybszt', 's', 'wednjxm', 'fklajt', 'fyfc', 'lvejgge', 'rtpjlodmmk', 'arivtgmjdm'];
const file2 = ['s', 'qj', 'tu', 'ur', 'qerhjjhyvx', 't'];
assertComputeEdits(file1, file2);
});
test('generated4', () => {
const file1 = ['ig', 'kh', 'hxegci', 'smvker', 'pkdmjjdqnv', 'vgkkqqx', '', 'jrzeb'];
const file2 = ['yk', ''];
assertComputeEdits(file1, file2);
});
test('does insertions in the middle of the document', () => {
const file1 = [
'line 1',
'line 2',
'line 3'
];
const file2 = [
'line 1',
'line 2',
'line 5',
'line 3'
];
assertComputeEdits(file1, file2);
});
test('does insertions at the end of the document', () => {
const file1 = [
'line 1',
'line 2',
'line 3'
];
const file2 = [
'line 1',
'line 2',
'line 3',
'line 4'
];
assertComputeEdits(file1, file2);
});
test('does insertions at the beginning of the document', () => {
const file1 = [
'line 1',
'line 2',
'line 3'
];
const file2 = [
'line 0',
'line 1',
'line 2',
'line 3'
];
assertComputeEdits(file1, file2);
});
test('does replacements', () => {
const file1 = [
'line 1',
'line 2',
'line 3'
];
const file2 = [
'line 1',
'line 7',
'line 3'
];
assertComputeEdits(file1, file2);
});
test('does deletions', () => {
const file1 = [
'line 1',
'line 2',
'line 3'
];
const file2 = [
'line 1',
'line 3'
];
assertComputeEdits(file1, file2);
});
test('does insert, replace, and delete', () => {
const file1 = [
'line 1',
'line 2',
'line 3',
'line 4',
'line 5',
];
const file2 = [
'line 0', // insert line 0
'line 1',
'replace line 2', // replace line 2
'line 3',
// delete line 4
'line 5',
];
assertComputeEdits(file1, file2);
});
});
function assertComputeEdits(lines1: string[], lines2: string[]): void {
const model = Model.createFromString(lines1.join('\n'));
const textSource = TextSource.fromString(lines2.join('\n'), DefaultEndOfLine.LF);
// compute required edits
// let start = Date.now();
const edits = ModelServiceImpl._computeEdits(model, textSource);
// console.log(`took ${Date.now() - start} ms.`);
// apply edits
model.pushEditOperations(null, edits, null);
assert.equal(model.getValue(), lines2.join('\n'));
}
function getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomString(minLength: number, maxLength: number): string {
let length = getRandomInt(minLength, maxLength);
let t = createStringBuilder(length);
for (let i = 0; i < length; i++) {
t.appendASCII(getRandomInt(CharCode.a, CharCode.z));
}
return t.build();
}
function generateFile(small: boolean): string[] {
let lineCount = getRandomInt(1, small ? 3 : 10000);
let lines: string[] = [];
for (let i = 0; i < lineCount; i++) {
lines.push(getRandomString(0, small ? 3 : 10000));
}
return lines;
}
if (GENERATE_TESTS) {
let number = 1;
while (true) {
console.log('------TEST: ' + number++);
const file1 = generateFile(true);
const file2 = generateFile(true);
console.log('------TEST GENERATED');
try {
assertComputeEdits(file1, file2);
} catch (err) {
console.log(err);
console.log(`
const file1 = ${JSON.stringify(file1).replace(/"/g, '\'')};
const file2 = ${JSON.stringify(file2).replace(/"/g, '\'')};
assertComputeEdits(file1, file2);
`);
break;
}
}
}

View File

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* 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 { KeyCode as StandaloneKeyCode, Severity as StandaloneSeverity } from 'vs/editor/common/standalone/standaloneBase';
import { KeyCode as RuntimeKeyCode } from 'vs/base/common/keyCodes';
import RuntimeSeverity from 'vs/base/common/severity';
suite('StandaloneBase', () => {
test('exports enums correctly', () => {
assert.equal(StandaloneSeverity.Ignore, RuntimeSeverity.Ignore);
assert.equal(StandaloneSeverity.Info, RuntimeSeverity.Info);
assert.equal(StandaloneSeverity.Warning, RuntimeSeverity.Warning);
assert.equal(StandaloneSeverity.Error, RuntimeSeverity.Error);
});
});
suite('KeyCode', () => {
test('is exported correctly in standalone editor', () => {
function assertKeyCode(standalone: StandaloneKeyCode, runtime: RuntimeKeyCode): void {
assert.equal(standalone, runtime);
}
assertKeyCode(StandaloneKeyCode.Unknown, RuntimeKeyCode.Unknown);
assertKeyCode(StandaloneKeyCode.Backspace, RuntimeKeyCode.Backspace);
assertKeyCode(StandaloneKeyCode.Tab, RuntimeKeyCode.Tab);
assertKeyCode(StandaloneKeyCode.Enter, RuntimeKeyCode.Enter);
assertKeyCode(StandaloneKeyCode.Shift, RuntimeKeyCode.Shift);
assertKeyCode(StandaloneKeyCode.Ctrl, RuntimeKeyCode.Ctrl);
assertKeyCode(StandaloneKeyCode.Alt, RuntimeKeyCode.Alt);
assertKeyCode(StandaloneKeyCode.PauseBreak, RuntimeKeyCode.PauseBreak);
assertKeyCode(StandaloneKeyCode.CapsLock, RuntimeKeyCode.CapsLock);
assertKeyCode(StandaloneKeyCode.Escape, RuntimeKeyCode.Escape);
assertKeyCode(StandaloneKeyCode.Space, RuntimeKeyCode.Space);
assertKeyCode(StandaloneKeyCode.PageUp, RuntimeKeyCode.PageUp);
assertKeyCode(StandaloneKeyCode.PageDown, RuntimeKeyCode.PageDown);
assertKeyCode(StandaloneKeyCode.End, RuntimeKeyCode.End);
assertKeyCode(StandaloneKeyCode.Home, RuntimeKeyCode.Home);
assertKeyCode(StandaloneKeyCode.LeftArrow, RuntimeKeyCode.LeftArrow);
assertKeyCode(StandaloneKeyCode.UpArrow, RuntimeKeyCode.UpArrow);
assertKeyCode(StandaloneKeyCode.RightArrow, RuntimeKeyCode.RightArrow);
assertKeyCode(StandaloneKeyCode.DownArrow, RuntimeKeyCode.DownArrow);
assertKeyCode(StandaloneKeyCode.Insert, RuntimeKeyCode.Insert);
assertKeyCode(StandaloneKeyCode.Delete, RuntimeKeyCode.Delete);
assertKeyCode(StandaloneKeyCode.KEY_0, RuntimeKeyCode.KEY_0);
assertKeyCode(StandaloneKeyCode.KEY_1, RuntimeKeyCode.KEY_1);
assertKeyCode(StandaloneKeyCode.KEY_2, RuntimeKeyCode.KEY_2);
assertKeyCode(StandaloneKeyCode.KEY_3, RuntimeKeyCode.KEY_3);
assertKeyCode(StandaloneKeyCode.KEY_4, RuntimeKeyCode.KEY_4);
assertKeyCode(StandaloneKeyCode.KEY_5, RuntimeKeyCode.KEY_5);
assertKeyCode(StandaloneKeyCode.KEY_6, RuntimeKeyCode.KEY_6);
assertKeyCode(StandaloneKeyCode.KEY_7, RuntimeKeyCode.KEY_7);
assertKeyCode(StandaloneKeyCode.KEY_8, RuntimeKeyCode.KEY_8);
assertKeyCode(StandaloneKeyCode.KEY_9, RuntimeKeyCode.KEY_9);
assertKeyCode(StandaloneKeyCode.KEY_A, RuntimeKeyCode.KEY_A);
assertKeyCode(StandaloneKeyCode.KEY_B, RuntimeKeyCode.KEY_B);
assertKeyCode(StandaloneKeyCode.KEY_C, RuntimeKeyCode.KEY_C);
assertKeyCode(StandaloneKeyCode.KEY_D, RuntimeKeyCode.KEY_D);
assertKeyCode(StandaloneKeyCode.KEY_E, RuntimeKeyCode.KEY_E);
assertKeyCode(StandaloneKeyCode.KEY_F, RuntimeKeyCode.KEY_F);
assertKeyCode(StandaloneKeyCode.KEY_G, RuntimeKeyCode.KEY_G);
assertKeyCode(StandaloneKeyCode.KEY_H, RuntimeKeyCode.KEY_H);
assertKeyCode(StandaloneKeyCode.KEY_I, RuntimeKeyCode.KEY_I);
assertKeyCode(StandaloneKeyCode.KEY_J, RuntimeKeyCode.KEY_J);
assertKeyCode(StandaloneKeyCode.KEY_K, RuntimeKeyCode.KEY_K);
assertKeyCode(StandaloneKeyCode.KEY_L, RuntimeKeyCode.KEY_L);
assertKeyCode(StandaloneKeyCode.KEY_M, RuntimeKeyCode.KEY_M);
assertKeyCode(StandaloneKeyCode.KEY_N, RuntimeKeyCode.KEY_N);
assertKeyCode(StandaloneKeyCode.KEY_O, RuntimeKeyCode.KEY_O);
assertKeyCode(StandaloneKeyCode.KEY_P, RuntimeKeyCode.KEY_P);
assertKeyCode(StandaloneKeyCode.KEY_Q, RuntimeKeyCode.KEY_Q);
assertKeyCode(StandaloneKeyCode.KEY_R, RuntimeKeyCode.KEY_R);
assertKeyCode(StandaloneKeyCode.KEY_S, RuntimeKeyCode.KEY_S);
assertKeyCode(StandaloneKeyCode.KEY_T, RuntimeKeyCode.KEY_T);
assertKeyCode(StandaloneKeyCode.KEY_U, RuntimeKeyCode.KEY_U);
assertKeyCode(StandaloneKeyCode.KEY_V, RuntimeKeyCode.KEY_V);
assertKeyCode(StandaloneKeyCode.KEY_W, RuntimeKeyCode.KEY_W);
assertKeyCode(StandaloneKeyCode.KEY_X, RuntimeKeyCode.KEY_X);
assertKeyCode(StandaloneKeyCode.KEY_Y, RuntimeKeyCode.KEY_Y);
assertKeyCode(StandaloneKeyCode.KEY_Z, RuntimeKeyCode.KEY_Z);
assertKeyCode(StandaloneKeyCode.Meta, RuntimeKeyCode.Meta);
assertKeyCode(StandaloneKeyCode.ContextMenu, RuntimeKeyCode.ContextMenu);
assertKeyCode(StandaloneKeyCode.F1, RuntimeKeyCode.F1);
assertKeyCode(StandaloneKeyCode.F2, RuntimeKeyCode.F2);
assertKeyCode(StandaloneKeyCode.F3, RuntimeKeyCode.F3);
assertKeyCode(StandaloneKeyCode.F4, RuntimeKeyCode.F4);
assertKeyCode(StandaloneKeyCode.F5, RuntimeKeyCode.F5);
assertKeyCode(StandaloneKeyCode.F6, RuntimeKeyCode.F6);
assertKeyCode(StandaloneKeyCode.F7, RuntimeKeyCode.F7);
assertKeyCode(StandaloneKeyCode.F8, RuntimeKeyCode.F8);
assertKeyCode(StandaloneKeyCode.F9, RuntimeKeyCode.F9);
assertKeyCode(StandaloneKeyCode.F10, RuntimeKeyCode.F10);
assertKeyCode(StandaloneKeyCode.F11, RuntimeKeyCode.F11);
assertKeyCode(StandaloneKeyCode.F12, RuntimeKeyCode.F12);
assertKeyCode(StandaloneKeyCode.F13, RuntimeKeyCode.F13);
assertKeyCode(StandaloneKeyCode.F14, RuntimeKeyCode.F14);
assertKeyCode(StandaloneKeyCode.F15, RuntimeKeyCode.F15);
assertKeyCode(StandaloneKeyCode.F16, RuntimeKeyCode.F16);
assertKeyCode(StandaloneKeyCode.F17, RuntimeKeyCode.F17);
assertKeyCode(StandaloneKeyCode.F18, RuntimeKeyCode.F18);
assertKeyCode(StandaloneKeyCode.F19, RuntimeKeyCode.F19);
assertKeyCode(StandaloneKeyCode.NumLock, RuntimeKeyCode.NumLock);
assertKeyCode(StandaloneKeyCode.ScrollLock, RuntimeKeyCode.ScrollLock);
assertKeyCode(StandaloneKeyCode.US_SEMICOLON, RuntimeKeyCode.US_SEMICOLON);
assertKeyCode(StandaloneKeyCode.US_EQUAL, RuntimeKeyCode.US_EQUAL);
assertKeyCode(StandaloneKeyCode.US_COMMA, RuntimeKeyCode.US_COMMA);
assertKeyCode(StandaloneKeyCode.US_MINUS, RuntimeKeyCode.US_MINUS);
assertKeyCode(StandaloneKeyCode.US_DOT, RuntimeKeyCode.US_DOT);
assertKeyCode(StandaloneKeyCode.US_SLASH, RuntimeKeyCode.US_SLASH);
assertKeyCode(StandaloneKeyCode.US_BACKTICK, RuntimeKeyCode.US_BACKTICK);
assertKeyCode(StandaloneKeyCode.US_OPEN_SQUARE_BRACKET, RuntimeKeyCode.US_OPEN_SQUARE_BRACKET);
assertKeyCode(StandaloneKeyCode.US_BACKSLASH, RuntimeKeyCode.US_BACKSLASH);
assertKeyCode(StandaloneKeyCode.US_CLOSE_SQUARE_BRACKET, RuntimeKeyCode.US_CLOSE_SQUARE_BRACKET);
assertKeyCode(StandaloneKeyCode.US_QUOTE, RuntimeKeyCode.US_QUOTE);
assertKeyCode(StandaloneKeyCode.OEM_8, RuntimeKeyCode.OEM_8);
assertKeyCode(StandaloneKeyCode.OEM_102, RuntimeKeyCode.OEM_102);
assertKeyCode(StandaloneKeyCode.NUMPAD_0, RuntimeKeyCode.NUMPAD_0);
assertKeyCode(StandaloneKeyCode.NUMPAD_1, RuntimeKeyCode.NUMPAD_1);
assertKeyCode(StandaloneKeyCode.NUMPAD_2, RuntimeKeyCode.NUMPAD_2);
assertKeyCode(StandaloneKeyCode.NUMPAD_3, RuntimeKeyCode.NUMPAD_3);
assertKeyCode(StandaloneKeyCode.NUMPAD_4, RuntimeKeyCode.NUMPAD_4);
assertKeyCode(StandaloneKeyCode.NUMPAD_5, RuntimeKeyCode.NUMPAD_5);
assertKeyCode(StandaloneKeyCode.NUMPAD_6, RuntimeKeyCode.NUMPAD_6);
assertKeyCode(StandaloneKeyCode.NUMPAD_7, RuntimeKeyCode.NUMPAD_7);
assertKeyCode(StandaloneKeyCode.NUMPAD_8, RuntimeKeyCode.NUMPAD_8);
assertKeyCode(StandaloneKeyCode.NUMPAD_9, RuntimeKeyCode.NUMPAD_9);
assertKeyCode(StandaloneKeyCode.NUMPAD_MULTIPLY, RuntimeKeyCode.NUMPAD_MULTIPLY);
assertKeyCode(StandaloneKeyCode.NUMPAD_ADD, RuntimeKeyCode.NUMPAD_ADD);
assertKeyCode(StandaloneKeyCode.NUMPAD_SEPARATOR, RuntimeKeyCode.NUMPAD_SEPARATOR);
assertKeyCode(StandaloneKeyCode.NUMPAD_SUBTRACT, RuntimeKeyCode.NUMPAD_SUBTRACT);
assertKeyCode(StandaloneKeyCode.NUMPAD_DECIMAL, RuntimeKeyCode.NUMPAD_DECIMAL);
assertKeyCode(StandaloneKeyCode.NUMPAD_DIVIDE, RuntimeKeyCode.NUMPAD_DIVIDE);
assertKeyCode(StandaloneKeyCode.KEY_IN_COMPOSITION, RuntimeKeyCode.KEY_IN_COMPOSITION);
assertKeyCode(StandaloneKeyCode.ABNT_C1, RuntimeKeyCode.ABNT_C1);
assertKeyCode(StandaloneKeyCode.ABNT_C2, RuntimeKeyCode.ABNT_C2);
assertKeyCode(StandaloneKeyCode.MAX_VALUE, RuntimeKeyCode.MAX_VALUE);
});
});

View File

@@ -0,0 +1,187 @@
/*---------------------------------------------------------------------------------------------
* 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 { Constants } from 'vs/editor/common/view/minimapCharRenderer';
import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory';
import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
import { RGBA8 } from 'vs/editor/common/core/rgba';
suite('MinimapCharRenderer', () => {
let sampleData: Uint8ClampedArray = null;
suiteSetup(() => {
sampleData = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
});
suiteTeardown(() => {
sampleData = null;
});
setup(() => {
for (let i = 0; i < sampleData.length; i++) {
sampleData[i] = 0;
}
});
const sampleD = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0d, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x0e, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x69, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0e, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0x71, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
function setSampleData(charCode: number, data: number[]) {
const rowWidth = Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT;
let chIndex = charCode - Constants.START_CH_CODE;
let globalOutputOffset = chIndex * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT;
let inputOffset = 0;
for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) {
let outputOffset = globalOutputOffset;
for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) {
for (let channel = 0; channel < Constants.RGBA_CHANNELS_CNT; channel++) {
sampleData[outputOffset] = data[inputOffset];
inputOffset++;
outputOffset++;
}
}
globalOutputOffset += rowWidth;
}
}
function createFakeImageData(width: number, height: number): ImageData {
return {
width: width,
height: height,
data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT)
};
}
test('letter d @ 2x', () => {
setSampleData('d'.charCodeAt(0), sampleD);
let renderer = MinimapCharRendererFactory.create(sampleData);
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT);
// set the background color
for (let i = 0, len = imageData.data.length / 4; i < len; i++) {
imageData.data[4 * i + 0] = background.r;
imageData.data[4 * i + 1] = background.g;
imageData.data[4 * i + 2] = background.b;
imageData.data[4 * i + 3] = 255;
}
renderer.x2RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false);
let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) {
actual[i] = imageData.data[i];
}
assert.deepEqual(actual, [
0x00, 0x00, 0x00, 0xff, 0x6d, 0x6d, 0x6d, 0xff,
0xbb, 0xbb, 0xbb, 0xff, 0xbe, 0xbe, 0xbe, 0xff,
0x94, 0x94, 0x94, 0xff, 0x7e, 0x7e, 0x7e, 0xff,
0xb1, 0xb1, 0xb1, 0xff, 0xbb, 0xbb, 0xbb, 0xff,
]);
});
test('letter d @ 2x at runtime', () => {
let renderer = getOrCreateMinimapCharRenderer();
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT);
// set the background color
for (let i = 0, len = imageData.data.length / 4; i < len; i++) {
imageData.data[4 * i + 0] = background.r;
imageData.data[4 * i + 1] = background.g;
imageData.data[4 * i + 2] = background.b;
imageData.data[4 * i + 3] = 255;
}
renderer.x2RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false);
let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) {
actual[i] = imageData.data[i];
}
assert.deepEqual(actual, [
0x00, 0x00, 0x00, 0xff, 0x6d, 0x6d, 0x6d, 0xff,
0xbb, 0xbb, 0xbb, 0xff, 0xbe, 0xbe, 0xbe, 0xff,
0x94, 0x94, 0x94, 0xff, 0x7e, 0x7e, 0x7e, 0xff,
0xb1, 0xb1, 0xb1, 0xff, 0xbb, 0xbb, 0xbb, 0xff,
]);
});
test('letter d @ 1x', () => {
setSampleData('d'.charCodeAt(0), sampleD);
let renderer = MinimapCharRendererFactory.create(sampleData);
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT);
// set the background color
for (let i = 0, len = imageData.data.length / 4; i < len; i++) {
imageData.data[4 * i + 0] = background.r;
imageData.data[4 * i + 1] = background.g;
imageData.data[4 * i + 2] = background.b;
imageData.data[4 * i + 3] = 255;
}
renderer.x1RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false);
let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) {
actual[i] = imageData.data[i];
}
assert.deepEqual(actual, [
0x55, 0x55, 0x55, 0xff,
0x93, 0x93, 0x93, 0xff,
]);
});
test('letter d @ 1x at runtime', () => {
let renderer = getOrCreateMinimapCharRenderer();
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT);
// set the background color
for (let i = 0, len = imageData.data.length / 4; i < len; i++) {
imageData.data[4 * i + 0] = background.r;
imageData.data[4 * i + 1] = background.g;
imageData.data[4 * i + 2] = background.b;
imageData.data[4 * i + 3] = 255;
}
renderer.x1RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false);
let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) {
actual[i] = imageData.data[i];
}
assert.deepEqual(actual, [
0x55, 0x55, 0x55, 0xff,
0x93, 0x93, 0x93, 0xff,
]);
});
});

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* 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 { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer';
const enum InternalConstants {
CA_CHANNELS_CNT = 2,
}
export class MinimapCharRendererFactory {
public static create(source: Uint8ClampedArray): MinimapCharRenderer {
const expectedLength = (Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
if (source.length !== expectedLength) {
throw new Error('Unexpected source in MinimapCharRenderer');
}
let x2CharData = this.toGrayscale(MinimapCharRendererFactory._downsample2x(source));
let x1CharData = this.toGrayscale(MinimapCharRendererFactory._downsample1x(source));
return new MinimapCharRenderer(x2CharData, x1CharData);
}
private static toGrayscale(charData: Uint8ClampedArray): Uint8ClampedArray {
let newLength = charData.length / 2;
let result = new Uint8ClampedArray(newLength);
let sourceOffset = 0;
for (var i = 0; i < newLength; i++) {
let color = charData[sourceOffset];
let alpha = charData[sourceOffset + 1];
let newColor = Math.round((color * alpha) / 255);
result[i] = newColor;
sourceOffset += 2;
}
return result;
}
private static _extractSampledChar(source: Uint8ClampedArray, charIndex: number, dest: Uint8ClampedArray) {
let destOffset = 0;
for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) {
let sourceOffset = (
Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT * i
+ Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * charIndex
);
for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) {
for (let c = 0; c < Constants.RGBA_CHANNELS_CNT; c++) {
dest[destOffset] = source[sourceOffset];
sourceOffset++;
destOffset++;
}
}
}
}
private static _downsample2xChar(source: Uint8ClampedArray, dest: Uint8ClampedArray): void {
// chars are 2 x 4px (width x height)
const resultLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT;
const result = new Uint16Array(resultLen);
for (let i = 0; i < resultLen; i++) {
result[i] = 0;
}
let inputOffset = 0, globalOutputOffset = 0;
for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) {
let outputOffset = globalOutputOffset;
let color = 0;
let alpha = 0;
for (let j = 0; j < Constants.SAMPLED_HALF_CHAR_WIDTH; j++) {
color += source[inputOffset]; // R
alpha += source[inputOffset + 3]; // A
inputOffset += Constants.RGBA_CHANNELS_CNT;
}
result[outputOffset] += color;
result[outputOffset + 1] += alpha;
outputOffset += InternalConstants.CA_CHANNELS_CNT;
color = 0;
alpha = 0;
for (let j = 0; j < Constants.SAMPLED_HALF_CHAR_WIDTH; j++) {
color += source[inputOffset]; // R
alpha += source[inputOffset + 3]; // A
inputOffset += Constants.RGBA_CHANNELS_CNT;
}
result[outputOffset] += color;
result[outputOffset + 1] += alpha;
outputOffset += InternalConstants.CA_CHANNELS_CNT;
if (i === 2 || i === 5 || i === 8) {
globalOutputOffset = outputOffset;
}
}
for (let i = 0; i < resultLen; i++) {
dest[i] = result[i] / 12; // 15 it should be
}
}
private static _downsample2x(data: Uint8ClampedArray): Uint8ClampedArray {
const resultLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * Constants.CHAR_COUNT;
const result = new Uint8ClampedArray(resultLen);
const sampledChar = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT);
const downsampledChar = new Uint8ClampedArray(Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT);
for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) {
this._extractSampledChar(data, charIndex, sampledChar);
this._downsample2xChar(sampledChar, downsampledChar);
let resultOffset = (Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * charIndex);
for (let i = 0; i < downsampledChar.length; i++) {
result[resultOffset + i] = downsampledChar[i];
}
}
return result;
}
private static _downsample1xChar(source: Uint8ClampedArray, dest: Uint8ClampedArray): void {
// chars are 1 x 2px (width x height)
const resultLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT;
const result = new Uint16Array(resultLen);
for (let i = 0; i < resultLen; i++) {
result[i] = 0;
}
let inputOffset = 0, globalOutputOffset = 0;
for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) {
let outputOffset = globalOutputOffset;
let color = 0;
let alpha = 0;
for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) {
color += source[inputOffset]; // R
alpha += source[inputOffset + 3]; // A
inputOffset += Constants.RGBA_CHANNELS_CNT;
}
result[outputOffset] += color;
result[outputOffset + 1] += alpha;
outputOffset += InternalConstants.CA_CHANNELS_CNT;
if (i === 5) {
globalOutputOffset = outputOffset;
}
}
for (let i = 0; i < resultLen; i++) {
dest[i] = result[i] / 50; // 60 it should be
}
}
private static _downsample1x(data: Uint8ClampedArray): Uint8ClampedArray {
const resultLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * Constants.CHAR_COUNT;
const result = new Uint8ClampedArray(resultLen);
const sampledChar = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT);
const downsampledChar = new Uint8ClampedArray(Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT);
for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) {
this._extractSampledChar(data, charIndex, sampledChar);
this._downsample1xChar(sampledChar, downsampledChar);
let resultOffset = (Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * charIndex);
for (let i = 0; i < downsampledChar.length; i++) {
result[resultOffset + i] = downsampledChar[i];
}
}
return result;
}
}

View File

@@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* 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 { OverviewRulerLane } from 'vs/editor/common/editorCommon';
import { OverviewZoneManager, ColorZone, OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
import { LIGHT } from 'vs/platform/theme/common/themeService';
suite('Editor View - OverviewZoneManager', () => {
test('pixel ratio 1, dom height 600', () => {
const LINE_COUNT = 50;
const LINE_HEIGHT = 20;
let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber);
manager.setMinimumHeight(6);
manager.setMaximumHeight(6);
manager.setThemeType(LIGHT);
manager.setDOMWidth(30);
manager.setDOMHeight(600);
manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT);
manager.setLineHeight(LINE_HEIGHT);
manager.setPixelRatio(1);
manager.setZones([
new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'),
new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'),
new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'),
new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'),
]);
// one line = 12, but cap is at 6
assert.deepEqual(manager.resolveColorZones(), [
new ColorZone(12, 22, 1, OverviewRulerLane.Full), // forced height of 10
new ColorZone(123, 129, 2, OverviewRulerLane.Full), // 120 -> 132
new ColorZone(363, 369, 3, OverviewRulerLane.Full), // 360 -> 372 [360 -> 384]
new ColorZone(375, 381, 3, OverviewRulerLane.Full), // 372 -> 384 [360 -> 384]
new ColorZone(594, 600, 4, OverviewRulerLane.Full), // 588 -> 600
]);
});
test('pixel ratio 1, dom height 300', () => {
const LINE_COUNT = 50;
const LINE_HEIGHT = 20;
let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber);
manager.setMinimumHeight(6);
manager.setMaximumHeight(6);
manager.setThemeType(LIGHT);
manager.setDOMWidth(30);
manager.setDOMHeight(300);
manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT);
manager.setLineHeight(LINE_HEIGHT);
manager.setPixelRatio(1);
manager.setZones([
new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'),
new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'),
new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'),
new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'),
]);
// one line = 6, cap is at 6
assert.deepEqual(manager.resolveColorZones(), [
new ColorZone(6, 16, 1, OverviewRulerLane.Full), // forced height of 10
new ColorZone(60, 66, 2, OverviewRulerLane.Full), // 60 -> 66
new ColorZone(180, 192, 3, OverviewRulerLane.Full), // 180 -> 192
new ColorZone(294, 300, 4, OverviewRulerLane.Full), // 294 -> 300
]);
});
test('pixel ratio 2, dom height 300', () => {
const LINE_COUNT = 50;
const LINE_HEIGHT = 20;
let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber);
manager.setMinimumHeight(6);
manager.setMaximumHeight(6);
manager.setThemeType(LIGHT);
manager.setDOMWidth(30);
manager.setDOMHeight(300);
manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT);
manager.setLineHeight(LINE_HEIGHT);
manager.setPixelRatio(2);
manager.setZones([
new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'),
new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'),
new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'),
new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'),
]);
// one line = 6, cap is at 12
assert.deepEqual(manager.resolveColorZones(), [
new ColorZone(12, 32, 1, OverviewRulerLane.Full), // forced height of 10 => forced height of 20
new ColorZone(120, 132, 2, OverviewRulerLane.Full), // 120 -> 132
new ColorZone(360, 384, 3, OverviewRulerLane.Full), // 360 -> 384
new ColorZone(588, 600, 4, OverviewRulerLane.Full), // 588 -> 600
]);
});
});

View File

@@ -0,0 +1,744 @@
/*---------------------------------------------------------------------------------------------
* 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 { RenderMinimap, EditorLayoutInfo, EditorLayoutProvider, IEditorLayoutProviderOpts } from 'vs/editor/common/config/editorOptions';
suite('Editor ViewLayout - EditorLayoutProvider', () => {
function doTest(input: IEditorLayoutProviderOpts, expected: EditorLayoutInfo): void {
let actual = EditorLayoutProvider.compute(input);
assert.deepEqual(actual, expected);
}
test('EditorLayoutProvider 1', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 990,
contentHeight: 800,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 99,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 1.1', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 11,
horizontalScrollbarHeight: 12,
scrollbarArrowSize: 13,
verticalScrollbarHasArrows: true,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 990,
contentHeight: 800,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 97,
verticalScrollbarWidth: 11,
horizontalScrollbarHeight: 12,
overviewRuler: {
top: 13,
width: 11,
height: (800 - 2 * 13),
right: 0
}
});
});
test('EditorLayoutProvider 2', () => {
doTest({
outerWidth: 900,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 890,
contentHeight: 800,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 89,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 3', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 900,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 10,
contentWidth: 890,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 89,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 4', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 900,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 10,
contentWidth: 890,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 89,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 5', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: true,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 50,
lineNumbersHeight: 900,
decorationsLeft: 50,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 60,
contentWidth: 840,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 84,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 6', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: true,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 5,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 50,
lineNumbersHeight: 900,
decorationsLeft: 50,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 60,
contentWidth: 840,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 84,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 7', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: true,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 6,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 60,
lineNumbersHeight: 900,
decorationsLeft: 60,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 70,
contentWidth: 830,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 83,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 8', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: true,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 6,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 5,
maxDigitWidth: 5,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 30,
lineNumbersHeight: 900,
decorationsLeft: 30,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 40,
contentWidth: 860,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 172,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 8 - rounds floats', () => {
doTest({
outerWidth: 900,
outerHeight: 900,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: true,
lineNumbersMinChars: 5,
lineNumbersDigitCount: 6,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 5.05,
maxDigitWidth: 5.05,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: false,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 900,
height: 900,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 900,
lineNumbersLeft: 0,
lineNumbersWidth: 30,
lineNumbersHeight: 900,
decorationsLeft: 30,
decorationsWidth: 10,
decorationsHeight: 900,
contentLeft: 40,
contentWidth: 860,
contentHeight: 900,
renderMinimap: RenderMinimap.None,
minimapWidth: 0,
viewportColumn: 170,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 900,
right: 0
}
});
});
test('EditorLayoutProvider 9 - render minimap', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 1,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 900,
contentHeight: 800,
renderMinimap: RenderMinimap.Small,
minimapWidth: 90,
viewportColumn: 90,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 9 - render minimap with pixelRatio = 2', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 2,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 900,
contentHeight: 800,
renderMinimap: RenderMinimap.Large,
minimapWidth: 90,
viewportColumn: 90,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 9 - render minimap with pixelRatio = 4', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 1,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapRenderCharacters: true,
minimapMaxColumn: 150,
pixelRatio: 4,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
glyphMarginHeight: 800,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
lineNumbersHeight: 800,
decorationsLeft: 0,
decorationsWidth: 10,
decorationsHeight: 800,
contentLeft: 10,
contentWidth: 943,
contentHeight: 800,
renderMinimap: RenderMinimap.Large,
minimapWidth: 47,
viewportColumn: 94,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
});

View File

@@ -0,0 +1,124 @@
/*---------------------------------------------------------------------------------------------
* 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 { DecorationSegment, LineDecorationsNormalizer, LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
import { Range } from 'vs/editor/common/core/range';
import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel';
suite('Editor ViewLayout - ViewLineParts', () => {
function newDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, inlineClassName: string): InlineDecoration {
return new InlineDecoration(new Range(startLineNumber, startColumn, endLineNumber, endColumn), inlineClassName, false);
}
test('Bug 9827:Overlapping inline decorations can cause wrong inline class to be applied', () => {
var result = LineDecorationsNormalizer.normalize([
new LineDecoration(1, 11, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]);
assert.deepEqual(result, [
new DecorationSegment(0, 1, 'c1'),
new DecorationSegment(2, 2, 'c2 c1'),
new DecorationSegment(3, 9, 'c1'),
]);
});
test('issue #3462: no whitespace shown at the end of a decorated line', () => {
var result = LineDecorationsNormalizer.normalize([
new LineDecoration(15, 21, 'vs-whitespace', false),
new LineDecoration(20, 21, 'inline-folded', false),
]);
assert.deepEqual(result, [
new DecorationSegment(14, 18, 'vs-whitespace'),
new DecorationSegment(19, 19, 'vs-whitespace inline-folded')
]);
});
test('issue #3661: Link decoration bleeds to next line when wrapping', () => {
let result = LineDecoration.filter([
newDecoration(2, 12, 3, 30, 'detected-link')
], 3, 12, 500);
assert.deepEqual(result, [
new LineDecoration(12, 30, 'detected-link', false),
]);
});
test('ViewLineParts', () => {
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 2, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
new DecorationSegment(0, 0, 'c1'),
new DecorationSegment(2, 2, 'c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 3, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
new DecorationSegment(0, 1, 'c1'),
new DecorationSegment(2, 2, 'c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(3, 4, 'c2', false)
]), [
new DecorationSegment(0, 1, 'c1'),
new DecorationSegment(2, 2, 'c1 c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(3, 4, 'c2', false)
]), [
new DecorationSegment(0, 1, 'c1 c1*'),
new DecorationSegment(2, 2, 'c1 c1* c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),
new LineDecoration(3, 4, 'c2', false)
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),
new LineDecoration(3, 4, 'c2', false),
new LineDecoration(3, 4, 'c2*', false)
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*')
]);
assert.deepEqual(LineDecorationsNormalizer.normalize([
new LineDecoration(1, 4, 'c1', false),
new LineDecoration(1, 4, 'c1*', false),
new LineDecoration(1, 4, 'c1**', false),
new LineDecoration(3, 4, 'c2', false),
new LineDecoration(3, 5, 'c2*', false)
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*'),
new DecorationSegment(3, 3, 'c2*')
]);
});
});

View File

@@ -0,0 +1,597 @@
/*---------------------------------------------------------------------------------------------
* 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 { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout';
suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout 1', () => {
// Start off with 10 lines
var linesLayout = new LinesLayout(10, 10);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: -
assert.equal(linesLayout.getLinesTotalHeight(), 100);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 60);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 70);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 80);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 90);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(5), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(11), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(29), 3);
// Add whitespace of height 5px after 2nd line
linesLayout.insertWhitespace(2, 0, 5);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5)
assert.equal(linesLayout.getLinesTotalHeight(), 105);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 35);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 45);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(45), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(104), 10);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(105), 10);
// Add two more whitespaces of height 5px
linesLayout.insertWhitespace(3, 0, 5);
linesLayout.insertWhitespace(4, 0, 5);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5), b(3, 5), c(4, 5)
assert.equal(linesLayout.getLinesTotalHeight(), 115);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 55);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 65);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(64), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(65), 6);
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(0), 20); // 20 -> 25
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(1), 35); // 35 -> 40
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(2), 50);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(0), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(19), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(20), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(21), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(22), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(23), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(24), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(25), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(26), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(34), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(35), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(36), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(39), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(40), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(41), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(49), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(50), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(51), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(54), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(55), -1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(1000), -1);
});
test('LinesLayout 2', () => {
// Start off with 10 lines and one whitespace after line 2, of height 5
var linesLayout = new LinesLayout(10, 1);
var a = linesLayout.insertWhitespace(2, 0, 5);
// 10 lines
// whitespace: - a(2,5)
assert.equal(linesLayout.getLinesTotalHeight(), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 7);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 8);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 9);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 11);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 12);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 13);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 14);
// Change whitespace height
// 10 lines
// whitespace: - a(2,10)
linesLayout.changeWhitespace(a, 2, 10);
assert.equal(linesLayout.getLinesTotalHeight(), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 12);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 13);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 14);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19);
// Change whitespace position
// 10 lines
// whitespace: - a(5,10)
linesLayout.changeWhitespace(a, 5, 10);
assert.equal(linesLayout.getLinesTotalHeight(), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19);
// Pretend that lines 5 and 6 were deleted
// 8 lines
// whitespace: - a(4,10)
linesLayout.onLinesDeleted(5, 6);
assert.equal(linesLayout.getLinesTotalHeight(), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 14);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
// Insert two lines at the beginning
// 10 lines
// whitespace: - a(6,10)
linesLayout.onLinesInserted(1, 2);
assert.equal(linesLayout.getLinesTotalHeight(), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19);
// Remove whitespace
// 10 lines
linesLayout.removeWhitespace(a);
assert.equal(linesLayout.getLinesTotalHeight(), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 6);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 7);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 8);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 9);
});
test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => {
var linesLayout = new LinesLayout(10, 1);
linesLayout.insertWhitespace(6, 0, 10);
// 10 lines
// whitespace: - a(6,10)
assert.equal(linesLayout.getLinesTotalHeight(), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19);
// Do some hit testing
// line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19]
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(-100), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(-1), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(2), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(3), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(4), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(5), 6);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(6), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(7), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(8), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(11), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(12), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(13), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(14), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(16), 7);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(17), 8);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(18), 9);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 10);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 10);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 10);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(22), 10);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(23), 10);
});
test('LinesLayout getCenteredLineInViewport', () => {
var linesLayout = new LinesLayout(10, 1);
linesLayout.insertWhitespace(6, 0, 10);
// 10 lines
// whitespace: - a(6,10)
assert.equal(linesLayout.getLinesTotalHeight(), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19);
// Find centered line in viewport 1
// line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19]
assert.equal(linesLayout.getLinesViewportData(0, 1).centeredLineNumber, 1);
assert.equal(linesLayout.getLinesViewportData(0, 2).centeredLineNumber, 2);
assert.equal(linesLayout.getLinesViewportData(0, 3).centeredLineNumber, 2);
assert.equal(linesLayout.getLinesViewportData(0, 4).centeredLineNumber, 3);
assert.equal(linesLayout.getLinesViewportData(0, 5).centeredLineNumber, 3);
assert.equal(linesLayout.getLinesViewportData(0, 6).centeredLineNumber, 4);
assert.equal(linesLayout.getLinesViewportData(0, 7).centeredLineNumber, 4);
assert.equal(linesLayout.getLinesViewportData(0, 8).centeredLineNumber, 5);
assert.equal(linesLayout.getLinesViewportData(0, 9).centeredLineNumber, 5);
assert.equal(linesLayout.getLinesViewportData(0, 10).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 11).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 12).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 13).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 14).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 15).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 16).centeredLineNumber, 6);
assert.equal(linesLayout.getLinesViewportData(0, 17).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 18).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 19).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 21).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 22).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 23).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 24).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 25).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 26).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 27).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 28).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 29).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 30).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 31).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 32).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(0, 33).centeredLineNumber, 7);
// Find centered line in viewport 2
// line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19]
assert.equal(linesLayout.getLinesViewportData(0, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(1, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(2, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(3, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(4, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(5, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(6, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(7, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(8, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(9, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(10, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(11, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(12, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(13, 20).centeredLineNumber, 7);
assert.equal(linesLayout.getLinesViewportData(14, 20).centeredLineNumber, 8);
assert.equal(linesLayout.getLinesViewportData(15, 20).centeredLineNumber, 8);
assert.equal(linesLayout.getLinesViewportData(16, 20).centeredLineNumber, 9);
assert.equal(linesLayout.getLinesViewportData(17, 20).centeredLineNumber, 9);
assert.equal(linesLayout.getLinesViewportData(18, 20).centeredLineNumber, 10);
assert.equal(linesLayout.getLinesViewportData(19, 20).centeredLineNumber, 10);
assert.equal(linesLayout.getLinesViewportData(20, 23).centeredLineNumber, 10);
assert.equal(linesLayout.getLinesViewportData(21, 23).centeredLineNumber, 10);
assert.equal(linesLayout.getLinesViewportData(22, 23).centeredLineNumber, 10);
});
test('LinesLayout getLinesViewportData 1', () => {
var linesLayout = new LinesLayout(10, 10);
linesLayout.insertWhitespace(6, 0, 100);
// 10 lines
// whitespace: - a(6,100)
assert.equal(linesLayout.getLinesTotalHeight(), 200);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 160);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 170);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 180);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 190);
// viewport 0->50
var viewportData = linesLayout.getLinesViewportData(0, 50);
assert.equal(viewportData.startLineNumber, 1);
assert.equal(viewportData.endLineNumber, 5);
assert.equal(viewportData.completelyVisibleStartLineNumber, 1);
assert.equal(viewportData.completelyVisibleEndLineNumber, 5);
assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40]);
// viewport 1->51
viewportData = linesLayout.getLinesViewportData(1, 51);
assert.equal(viewportData.startLineNumber, 1);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 2);
assert.equal(viewportData.completelyVisibleEndLineNumber, 5);
assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40, 50]);
// viewport 5->55
viewportData = linesLayout.getLinesViewportData(5, 55);
assert.equal(viewportData.startLineNumber, 1);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 2);
assert.equal(viewportData.completelyVisibleEndLineNumber, 5);
assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40, 50]);
// viewport 10->60
viewportData = linesLayout.getLinesViewportData(10, 60);
assert.equal(viewportData.startLineNumber, 2);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 2);
assert.equal(viewportData.completelyVisibleEndLineNumber, 6);
assert.deepEqual(viewportData.relativeVerticalOffset, [10, 20, 30, 40, 50]);
// viewport 50->100
viewportData = linesLayout.getLinesViewportData(50, 100);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 6);
assert.deepEqual(viewportData.relativeVerticalOffset, [50]);
// viewport 60->110
viewportData = linesLayout.getLinesViewportData(60, 110);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [160]);
// viewport 65->115
viewportData = linesLayout.getLinesViewportData(65, 115);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [160]);
// viewport 50->159
viewportData = linesLayout.getLinesViewportData(50, 159);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 6);
assert.deepEqual(viewportData.relativeVerticalOffset, [50]);
// viewport 50->160
viewportData = linesLayout.getLinesViewportData(50, 160);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 6);
assert.deepEqual(viewportData.relativeVerticalOffset, [50]);
// viewport 51->161
viewportData = linesLayout.getLinesViewportData(51, 161);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]);
// viewport 150->169
viewportData = linesLayout.getLinesViewportData(150, 169);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [160]);
// viewport 159->169
viewportData = linesLayout.getLinesViewportData(159, 169);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [160]);
// viewport 160->169
viewportData = linesLayout.getLinesViewportData(160, 169);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [160]);
// viewport 160->1000
viewportData = linesLayout.getLinesViewportData(160, 1000);
assert.equal(viewportData.startLineNumber, 7);
assert.equal(viewportData.endLineNumber, 10);
assert.equal(viewportData.completelyVisibleStartLineNumber, 7);
assert.equal(viewportData.completelyVisibleEndLineNumber, 10);
assert.deepEqual(viewportData.relativeVerticalOffset, [160, 170, 180, 190]);
});
test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => {
var linesLayout = new LinesLayout(10, 10);
var a = linesLayout.insertWhitespace(6, 0, 100);
var b = linesLayout.insertWhitespace(7, 0, 50);
// 10 lines
// whitespace: - a(6,100), b(7, 50)
assert.equal(linesLayout.getLinesTotalHeight(), 250);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 160);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 220);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 230);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 240);
// viewport 50->160
var viewportData = linesLayout.getLinesViewportData(50, 160);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 6);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 6);
assert.deepEqual(viewportData.relativeVerticalOffset, [50]);
var whitespaceData = linesLayout.getWhitespaceViewportData(50, 160);
assert.deepEqual(whitespaceData, [{
id: a,
afterLineNumber: 6,
verticalOffset: 60,
height: 100
}]);
// viewport 50->219
viewportData = linesLayout.getLinesViewportData(50, 219);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]);
whitespaceData = linesLayout.getWhitespaceViewportData(50, 219);
assert.deepEqual(whitespaceData, [{
id: a,
afterLineNumber: 6,
verticalOffset: 60,
height: 100
}, {
id: b,
afterLineNumber: 7,
verticalOffset: 170,
height: 50
}]);
// viewport 50->220
viewportData = linesLayout.getLinesViewportData(50, 220);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 7);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 7);
assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]);
// viewport 50->250
viewportData = linesLayout.getLinesViewportData(50, 250);
assert.equal(viewportData.startLineNumber, 6);
assert.equal(viewportData.endLineNumber, 10);
assert.equal(viewportData.completelyVisibleStartLineNumber, 6);
assert.equal(viewportData.completelyVisibleEndLineNumber, 10);
assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160, 220, 230, 240]);
});
test('LinesLayout getWhitespaceAtVerticalOffset', () => {
var linesLayout = new LinesLayout(10, 10);
var a = linesLayout.insertWhitespace(6, 0, 100);
var b = linesLayout.insertWhitespace(7, 0, 50);
var whitespace = linesLayout.getWhitespaceAtVerticalOffset(0);
assert.equal(whitespace, null);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(59);
assert.equal(whitespace, null);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(60);
assert.equal(whitespace.id, a);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(61);
assert.equal(whitespace.id, a);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(159);
assert.equal(whitespace.id, a);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(160);
assert.equal(whitespace, null);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(161);
assert.equal(whitespace, null);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(169);
assert.equal(whitespace, null);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(170);
assert.equal(whitespace.id, b);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(171);
assert.equal(whitespace.id, b);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(219);
assert.equal(whitespace.id, b);
whitespace = linesLayout.getWhitespaceAtVerticalOffset(220);
assert.equal(whitespace, null);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,560 @@
/*---------------------------------------------------------------------------------------------
* 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 { WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer';
suite('Editor ViewLayout - WhitespaceComputer', () => {
test('WhitespaceComputer', () => {
var whitespaceComputer = new WhitespaceComputer();
// Insert a whitespace after line number 2, of height 10
var a = whitespaceComputer.insertWhitespace(2, 0, 10);
// whitespaces: a(2, 10)
assert.equal(whitespaceComputer.getCount(), 1);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
assert.equal(whitespaceComputer.getTotalHeight(), 10);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10);
// Insert a whitespace again after line number 2, of height 20
var b = whitespaceComputer.insertWhitespace(2, 0, 20);
// whitespaces: a(2, 10), b(2, 20)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 30);
assert.equal(whitespaceComputer.getTotalHeight(), 30);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
// Change last inserted whitespace height to 30
whitespaceComputer.changeWhitespaceHeight(b, 30);
// whitespaces: a(2, 10), b(2, 30)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 40);
assert.equal(whitespaceComputer.getTotalHeight(), 40);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 40);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 40);
// Remove last inserted whitespace
whitespaceComputer.removeWhitespace(b);
// whitespaces: a(2, 10)
assert.equal(whitespaceComputer.getCount(), 1);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10);
assert.equal(whitespaceComputer.getTotalHeight(), 10);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10);
// Add a whitespace before the first line of height 50
b = whitespaceComputer.insertWhitespace(0, 0, 50);
// whitespaces: b(0, 50), a(2, 10)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
assert.equal(whitespaceComputer.getTotalHeight(), 60);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60);
// Add a whitespace after line 4 of height 20
whitespaceComputer.insertWhitespace(4, 0, 20);
// whitespaces: b(0, 50), a(2, 10), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 3);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 80);
assert.equal(whitespaceComputer.getTotalHeight(), 80);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 80);
// Add a whitespace after line 3 of height 30
whitespaceComputer.insertWhitespace(3, 0, 30);
// whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 4);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60);
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 90);
assert.equal(whitespaceComputer.getAccumulatedHeight(3), 110);
assert.equal(whitespaceComputer.getTotalHeight(), 110);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 90);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 110);
// Change whitespace after line 2 to height of 100
whitespaceComputer.changeWhitespaceHeight(a, 100);
// whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 4);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 100);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 150);
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 180);
assert.equal(whitespaceComputer.getAccumulatedHeight(3), 200);
assert.equal(whitespaceComputer.getTotalHeight(), 200);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 150);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 180);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 200);
// Remove whitespace after line 2
whitespaceComputer.removeWhitespace(a);
// whitespaces: b(0, 50), d(3, 30), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 3);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 80);
assert.equal(whitespaceComputer.getAccumulatedHeight(2), 100);
assert.equal(whitespaceComputer.getTotalHeight(), 100);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 80);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 100);
// Remove whitespace before line 1
whitespaceComputer.removeWhitespace(b);
// whitespaces: d(3, 30), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
assert.equal(whitespaceComputer.getTotalHeight(), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
// Delete line 1
whitespaceComputer.onLinesDeleted(1, 1);
// whitespaces: d(2, 30), c(3, 20)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
assert.equal(whitespaceComputer.getTotalHeight(), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
// Insert a line before line 1
whitespaceComputer.onLinesInserted(1, 1);
// whitespaces: d(3, 30), c(4, 20)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
assert.equal(whitespaceComputer.getTotalHeight(), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
// Delete line 4
whitespaceComputer.onLinesDeleted(4, 4);
// whitespaces: d(3, 30), c(3, 20)
assert.equal(whitespaceComputer.getCount(), 2);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30);
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20);
assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30);
assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50);
assert.equal(whitespaceComputer.getTotalHeight(), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50);
assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50);
});
test('WhitespaceComputer findInsertionIndex', () => {
var makeArray = (size: number, fillValue: number) => {
var r: number[] = [];
for (var i = 0; i < size; i++) {
r[i] = fillValue;
}
return r;
};
var arr: number[];
var ordinals: number[];
arr = [];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 0);
arr = [1];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
arr = [1, 3];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
arr = [1, 3, 5];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
arr = [1, 3, 5];
ordinals = makeArray(arr.length, 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
arr = [1, 3, 5, 7];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
arr = [1, 3, 5, 7, 9];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
arr = [1, 3, 5, 7, 9, 11];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
arr = [1, 3, 5, 7, 9, 11, 13];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7);
arr = [1, 3, 5, 7, 9, 11, 13, 15];
ordinals = makeArray(arr.length, 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 15, ordinals, 0), 8);
assert.equal(WhitespaceComputer.findInsertionIndex(arr, 16, ordinals, 0), 8);
});
test('WhitespaceComputer changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => {
var whitespaceComputer = new WhitespaceComputer();
var a = whitespaceComputer.insertWhitespace(0, 0, 1);
var b = whitespaceComputer.insertWhitespace(7, 0, 1);
var c = whitespaceComputer.insertWhitespace(3, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
// Do not really move a
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 1
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
// Do not really move a
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 2);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 2
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
// Change a to conflict with c => a gets placed after c
whitespaceComputer.changeWhitespaceAfterLineNumber(a, 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
// Make a no-op
whitespaceComputer.changeWhitespaceAfterLineNumber(c, 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
// Conflict c with b => c gets placed after b
whitespaceComputer.changeWhitespaceAfterLineNumber(c, 7);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 3
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 7);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 7
assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7);
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b
assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // --
});
test('WhitespaceComputer Bug', () => {
var whitespaceComputer = new WhitespaceComputer();
var a = whitespaceComputer.insertWhitespace(0, 0, 1);
var b = whitespaceComputer.insertWhitespace(7, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7
var c = whitespaceComputer.insertWhitespace(3, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7
var d = whitespaceComputer.insertWhitespace(2, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
var e = whitespaceComputer.insertWhitespace(8, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
var f = whitespaceComputer.insertWhitespace(11, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), f); // 11
var g = whitespaceComputer.insertWhitespace(10, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), g); // 10
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), f); // 11
var h = whitespaceComputer.insertWhitespace(0, 0, 1);
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), h); // 0
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), d); // 2
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), c); // 3
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), b); // 7
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), e); // 8
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), g); // 10
assert.equal(whitespaceComputer.getIdForWhitespaceIndex(7), f); // 11
});
});

View File

@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* 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 { WrappingIndent } from 'vs/editor/common/config/editorOptions';
import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
import { ILineMapperFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None) {
let rawText = '';
let currentLineIndex = 0;
let lineIndices: number[] = [];
for (let i = 0, len = annotatedText.length; i < len; i++) {
if (annotatedText.charAt(i) === '|') {
currentLineIndex++;
} else {
rawText += annotatedText.charAt(i);
lineIndices[rawText.length - 1] = currentLineIndex;
}
}
let mapper = factory.createLineMapping(rawText, tabSize, breakAfter, 2, wrappingIndent);
let actualAnnotatedText = '';
if (mapper) {
let previousLineIndex = 0;
for (let i = 0, len = rawText.length; i < len; i++) {
let r = mapper.getOutputPositionOfInputOffset(i);
if (previousLineIndex !== r.outputLineIndex) {
previousLineIndex = r.outputLineIndex;
actualAnnotatedText += '|';
}
actualAnnotatedText += rawText.charAt(i);
}
} else {
// No wrapping
actualAnnotatedText = rawText;
}
assert.equal(actualAnnotatedText, annotatedText);
}
suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => {
test('CharacterHardWrappingLineMapper', () => {
let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.');
// Empty string
assertLineMapping(factory, 4, 5, '');
// No wrapping if not necessary
assertLineMapping(factory, 4, 5, 'aaa');
assertLineMapping(factory, 4, 5, 'aaaaa');
assertLineMapping(factory, 4, -1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
// Acts like hard wrapping if no char found
assertLineMapping(factory, 4, 5, 'aaaaa|a');
// Honors obtrusive wrapping character
assertLineMapping(factory, 4, 5, 'aaaaa|.');
assertLineMapping(factory, 4, 5, 'aaaaa|a.|aaa.|aa');
assertLineMapping(factory, 4, 5, 'aaaaa|a..|aaa.|aa');
assertLineMapping(factory, 4, 5, 'aaaaa|a...|aaa.|aa');
assertLineMapping(factory, 4, 5, 'aaaaa|a....|aaa.|aa');
// Honors tabs when computing wrapping position
assertLineMapping(factory, 4, 5, '\t');
assertLineMapping(factory, 4, 5, '\ta|aa');
assertLineMapping(factory, 4, 5, '\ta|\ta|a');
assertLineMapping(factory, 4, 5, 'aa\ta');
assertLineMapping(factory, 4, 5, 'aa\ta|a');
// Honors wrapping before characters (& gives it priority)
assertLineMapping(factory, 4, 5, 'aaa.|aa');
assertLineMapping(factory, 4, 5, 'aaa|(.aa');
// Honors wrapping after characters (& gives it priority)
assertLineMapping(factory, 4, 5, 'aaa))|).aaa');
assertLineMapping(factory, 4, 5, 'aaa))|)|.aaaa');
assertLineMapping(factory, 4, 5, 'aaa)|()|.aaa');
assertLineMapping(factory, 4, 5, 'aaa(|()|.aaa');
assertLineMapping(factory, 4, 5, 'aa.(|()|.aaa');
assertLineMapping(factory, 4, 5, 'aa.|(.)|.aaa');
});
test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => {
let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.');
assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89');
assertLineMapping(factory, 4, 5, '\u3042 \u5b89|\u5b89');
assertLineMapping(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89');
assertLineMapping(factory, 4, 5, 'aa |\u5b89)\u5b89|\u5b89');
assertLineMapping(factory, 4, 5, 'aa \u3042|\u5b89\u3042)|\u5b89');
assertLineMapping(factory, 4, 5, 'aa |(\u5b89aa|\u5b89');
});
test('CharacterHardWrappingLineMapper - WrappingIndent.Same', () => {
let factory = new CharacterHardWrappingLineMapperFactory('', ' ', '');
assertLineMapping(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same);
});
test('issue #16332: Scroll bar overlaying on top of text', () => {
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);
});
});

View File

@@ -0,0 +1,171 @@
/*---------------------------------------------------------------------------------------------
* 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 { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer';
import { toUint32Array } from 'vs/editor/common/core/uint';
suite('Editor ViewModel - PrefixSumComputer', () => {
test('PrefixSumComputer', () => {
let indexOfResult: PrefixSumIndexOfResult;
var psc = new PrefixSumComputer(toUint32Array([1, 1, 2, 1, 3]));
assert.equal(psc.getTotalValue(), 8);
assert.equal(psc.getAccumulatedValue(-1), 0);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 2);
assert.equal(psc.getAccumulatedValue(2), 4);
assert.equal(psc.getAccumulatedValue(3), 5);
assert.equal(psc.getAccumulatedValue(4), 8);
indexOfResult = psc.getIndexOf(0);
assert.equal(indexOfResult.index, 0);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(1);
assert.equal(indexOfResult.index, 1);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(2);
assert.equal(indexOfResult.index, 2);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(3);
assert.equal(indexOfResult.index, 2);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(4);
assert.equal(indexOfResult.index, 3);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(5);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(6);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(7);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 2);
indexOfResult = psc.getIndexOf(8);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 3);
// [1, 2, 2, 1, 3]
psc.changeValue(1, 2);
assert.equal(psc.getTotalValue(), 9);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 3);
assert.equal(psc.getAccumulatedValue(2), 5);
assert.equal(psc.getAccumulatedValue(3), 6);
assert.equal(psc.getAccumulatedValue(4), 9);
// [1, 0, 2, 1, 3]
psc.changeValue(1, 0);
assert.equal(psc.getTotalValue(), 7);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 1);
assert.equal(psc.getAccumulatedValue(2), 3);
assert.equal(psc.getAccumulatedValue(3), 4);
assert.equal(psc.getAccumulatedValue(4), 7);
indexOfResult = psc.getIndexOf(0);
assert.equal(indexOfResult.index, 0);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(1);
assert.equal(indexOfResult.index, 2);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(2);
assert.equal(indexOfResult.index, 2);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(3);
assert.equal(indexOfResult.index, 3);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(4);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(5);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(6);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 2);
indexOfResult = psc.getIndexOf(7);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 3);
// [1, 0, 0, 1, 3]
psc.changeValue(2, 0);
assert.equal(psc.getTotalValue(), 5);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 1);
assert.equal(psc.getAccumulatedValue(2), 1);
assert.equal(psc.getAccumulatedValue(3), 2);
assert.equal(psc.getAccumulatedValue(4), 5);
indexOfResult = psc.getIndexOf(0);
assert.equal(indexOfResult.index, 0);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(1);
assert.equal(indexOfResult.index, 3);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(2);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(3);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(4);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 2);
indexOfResult = psc.getIndexOf(5);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 3);
// [1, 0, 0, 0, 3]
psc.changeValue(3, 0);
assert.equal(psc.getTotalValue(), 4);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 1);
assert.equal(psc.getAccumulatedValue(2), 1);
assert.equal(psc.getAccumulatedValue(3), 1);
assert.equal(psc.getAccumulatedValue(4), 4);
indexOfResult = psc.getIndexOf(0);
assert.equal(indexOfResult.index, 0);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(1);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(2);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 1);
indexOfResult = psc.getIndexOf(3);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 2);
indexOfResult = psc.getIndexOf(4);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 3);
// [1, 1, 0, 1, 1]
psc.changeValue(1, 1);
psc.changeValue(3, 1);
psc.changeValue(4, 1);
assert.equal(psc.getTotalValue(), 4);
assert.equal(psc.getAccumulatedValue(0), 1);
assert.equal(psc.getAccumulatedValue(1), 2);
assert.equal(psc.getAccumulatedValue(2), 2);
assert.equal(psc.getAccumulatedValue(3), 3);
assert.equal(psc.getAccumulatedValue(4), 4);
indexOfResult = psc.getIndexOf(0);
assert.equal(indexOfResult.index, 0);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(1);
assert.equal(indexOfResult.index, 1);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(2);
assert.equal(indexOfResult.index, 3);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(3);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 0);
indexOfResult = psc.getIndexOf(4);
assert.equal(indexOfResult.index, 4);
assert.equal(indexOfResult.remainder, 1);
});
});

View File

@@ -0,0 +1,786 @@
/*---------------------------------------------------------------------------------------------
* 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 { Position } from 'vs/editor/common/core/position';
import { CharacterHardWrappingLineMapping, CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { ILineMapping, IModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { Model } from 'vs/editor/common/model/model';
import { toUint32Array } from 'vs/editor/common/core/uint';
import * as modes from 'vs/editor/common/modes';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
import { Range } from 'vs/editor/common/core/range';
suite('Editor ViewModel - SplitLinesCollection', () => {
test('SplitLine', () => {
var model1 = createModel('My First LineMy Second LineAnd another one');
var line1 = createSplitLine([13, 14, 15], '');
assert.equal(line1.getViewLineCount(), 3);
assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line');
assert.equal(line1.getViewLineContent(model1, 1, 1), 'My Second Line');
assert.equal(line1.getViewLineContent(model1, 1, 2), 'And another one');
assert.equal(line1.getViewLineMaxColumn(model1, 1, 0), 14);
assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 15);
assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 16);
for (var col = 1; col <= 14; col++) {
assert.equal(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')');
}
for (var col = 1; col <= 15; col++) {
assert.equal(line1.getModelColumnOfViewPosition(1, col), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')');
}
for (var col = 1; col <= 16; col++) {
assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')');
}
for (var col = 1; col <= 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')');
}
for (var col = 1 + 13; col <= 14 + 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, col - 13), 'getOutputPositionOfInputPosition(' + col + ')');
}
for (var col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')');
}
model1 = createModel('My First LineMy Second LineAnd another one');
line1 = createSplitLine([13, 14, 15], '\t');
assert.equal(line1.getViewLineCount(), 3);
assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line');
assert.equal(line1.getViewLineContent(model1, 1, 1), '\tMy Second Line');
assert.equal(line1.getViewLineContent(model1, 1, 2), '\tAnd another one');
assert.equal(line1.getViewLineMaxColumn(model1, 1, 0), 14);
assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 16);
assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 17);
for (var col = 1; col <= 14; col++) {
assert.equal(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')');
}
for (var col = 1; col <= 1; col++) {
assert.equal(line1.getModelColumnOfViewPosition(1, 1), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')');
}
for (var col = 2; col <= 16; col++) {
assert.equal(line1.getModelColumnOfViewPosition(1, col), 13 + col - 1, 'getInputColumnOfOutputPosition(1, ' + col + ')');
}
for (var col = 1; col <= 1; col++) {
assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')');
}
for (var col = 2; col <= 17; col++) {
assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col - 1, 'getInputColumnOfOutputPosition(2, ' + col + ')');
}
for (var col = 1; col <= 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')');
}
for (var col = 1 + 13; col <= 14 + 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, 1 + col - 13), 'getOutputPositionOfInputPosition(' + col + ')');
}
for (var col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) {
assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, 1 + col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')');
}
});
function withSplitLinesCollection(text: string, callback: (model: Model, linesCollection: SplitLinesCollection) => void): void {
let config = new TestConfiguration({});
let hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory(
config.editor.wrappingInfo.wordWrapBreakBeforeCharacters,
config.editor.wrappingInfo.wordWrapBreakAfterCharacters,
config.editor.wrappingInfo.wordWrapBreakObtrusiveCharacters
);
let model = Model.createFromString([
'int main() {',
'\tprintf("Hello world!");',
'}',
'int main() {',
'\tprintf("Hello world!");',
'}',
].join('\n'));
let linesCollection = new SplitLinesCollection(
model,
hardWrappingLineMapperFactory,
model.getOptions().tabSize,
config.editor.wrappingInfo.wrappingColumn,
config.editor.fontInfo.typicalFullwidthCharacterWidth / config.editor.fontInfo.typicalHalfwidthCharacterWidth,
config.editor.wrappingInfo.wrappingIndent
);
callback(model, linesCollection);
linesCollection.dispose();
model.dispose();
config.dispose();
}
test('Invalid line numbers', () => {
const text = [
'int main() {',
'\tprintf("Hello world!");',
'}',
'int main() {',
'\tprintf("Hello world!");',
'}',
].join('\n');
withSplitLinesCollection(text, (model, linesCollection) => {
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);
// getOutputLineContent
assert.equal(linesCollection.getViewLineContent(-1), 'int main() {');
assert.equal(linesCollection.getViewLineContent(0), 'int main() {');
assert.equal(linesCollection.getViewLineContent(1), 'int main() {');
assert.equal(linesCollection.getViewLineContent(2), '\tprintf("Hello world!");');
assert.equal(linesCollection.getViewLineContent(3), '}');
assert.equal(linesCollection.getViewLineContent(4), 'int main() {');
assert.equal(linesCollection.getViewLineContent(5), '\tprintf("Hello world!");');
assert.equal(linesCollection.getViewLineContent(6), '}');
assert.equal(linesCollection.getViewLineContent(7), '}');
// getOutputLineMinColumn
assert.equal(linesCollection.getViewLineMinColumn(-1), 1);
assert.equal(linesCollection.getViewLineMinColumn(0), 1);
assert.equal(linesCollection.getViewLineMinColumn(1), 1);
assert.equal(linesCollection.getViewLineMinColumn(2), 1);
assert.equal(linesCollection.getViewLineMinColumn(3), 1);
assert.equal(linesCollection.getViewLineMinColumn(4), 1);
assert.equal(linesCollection.getViewLineMinColumn(5), 1);
assert.equal(linesCollection.getViewLineMinColumn(6), 1);
assert.equal(linesCollection.getViewLineMinColumn(7), 1);
// getOutputLineMaxColumn
assert.equal(linesCollection.getViewLineMaxColumn(-1), 13);
assert.equal(linesCollection.getViewLineMaxColumn(0), 13);
assert.equal(linesCollection.getViewLineMaxColumn(1), 13);
assert.equal(linesCollection.getViewLineMaxColumn(2), 25);
assert.equal(linesCollection.getViewLineMaxColumn(3), 2);
assert.equal(linesCollection.getViewLineMaxColumn(4), 13);
assert.equal(linesCollection.getViewLineMaxColumn(5), 25);
assert.equal(linesCollection.getViewLineMaxColumn(6), 2);
assert.equal(linesCollection.getViewLineMaxColumn(7), 2);
// convertOutputPositionToInputPosition
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(-1, 1), new Position(1, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(0, 1), new Position(1, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(1, 1), new Position(1, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(2, 1), new Position(2, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(3, 1), new Position(3, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(4, 1), new Position(4, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(5, 1), new Position(5, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(6, 1), new Position(6, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(7, 1), new Position(6, 1));
assert.deepEqual(linesCollection.convertViewPositionToModelPosition(8, 1), new Position(6, 1));
});
});
test('issue #3662', () => {
const text = [
'int main() {',
'\tprintf("Hello world!");',
'}',
'int main() {',
'\tprintf("Hello world!");',
'}',
].join('\n');
withSplitLinesCollection(text, (model, linesCollection) => {
linesCollection.setHiddenAreas([
new Range(1, 1, 3, 1),
new Range(5, 1, 6, 1)
]);
let viewLineCount = linesCollection.getViewLineCount();
assert.equal(viewLineCount, 1, 'getOutputLineCount()');
let modelLineCount = model.getLineCount();
for (let lineNumber = 0; lineNumber <= modelLineCount + 1; lineNumber++) {
let lineMinColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMinColumn(lineNumber) : 1;
let lineMaxColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMaxColumn(lineNumber) : 1;
for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) {
let viewPosition = linesCollection.convertModelPositionToViewPosition(lineNumber, column);
// validate view position
let viewLineNumber = viewPosition.lineNumber;
let viewColumn = viewPosition.column;
if (viewLineNumber < 1) {
viewLineNumber = 1;
}
var lineCount = linesCollection.getViewLineCount();
if (viewLineNumber > lineCount) {
viewLineNumber = lineCount;
}
var viewMinColumn = linesCollection.getViewLineMinColumn(viewLineNumber);
var viewMaxColumn = linesCollection.getViewLineMaxColumn(viewLineNumber);
if (viewColumn < viewMinColumn) {
viewColumn = viewMinColumn;
}
if (viewColumn > viewMaxColumn) {
viewColumn = viewMaxColumn;
}
let validViewPosition = new Position(viewLineNumber, viewColumn);
assert.equal(viewPosition.toString(), validViewPosition.toString(), 'model->view for ' + lineNumber + ', ' + column);
}
}
for (let lineNumber = 0; lineNumber <= viewLineCount + 1; lineNumber++) {
let lineMinColumn = linesCollection.getViewLineMinColumn(lineNumber);
let lineMaxColumn = linesCollection.getViewLineMaxColumn(lineNumber);
for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) {
let modelPosition = linesCollection.convertViewPositionToModelPosition(lineNumber, column);
let validModelPosition = model.validatePosition(modelPosition);
assert.equal(modelPosition.toString(), validModelPosition.toString(), 'view->model for ' + lineNumber + ', ' + column);
}
}
});
});
});
suite('SplitLinesCollection', () => {
const _text = [
'class Nice {',
' function hi() {',
' console.log("Hello world");',
' }',
' function hello() {',
' console.log("Hello world, this is a somewhat longer line");',
' }',
'}',
];
const _tokens = [
[
{ startIndex: 0, value: 1 },
{ startIndex: 5, value: 2 },
{ startIndex: 6, value: 3 },
{ startIndex: 10, value: 4 },
],
[
{ startIndex: 0, value: 5 },
{ startIndex: 1, value: 6 },
{ startIndex: 9, value: 7 },
{ startIndex: 10, value: 8 },
{ startIndex: 12, value: 9 },
],
[
{ startIndex: 0, value: 10 },
{ startIndex: 2, value: 11 },
{ startIndex: 9, value: 12 },
{ startIndex: 10, value: 13 },
{ startIndex: 13, value: 14 },
{ startIndex: 14, value: 15 },
{ startIndex: 27, value: 16 },
],
[
{ startIndex: 0, value: 17 },
],
[
{ startIndex: 0, value: 18 },
{ startIndex: 1, value: 19 },
{ startIndex: 9, value: 20 },
{ startIndex: 10, value: 21 },
{ startIndex: 15, value: 22 },
],
[
{ startIndex: 0, value: 23 },
{ startIndex: 2, value: 24 },
{ startIndex: 9, value: 25 },
{ startIndex: 10, value: 26 },
{ startIndex: 13, value: 27 },
{ startIndex: 14, value: 28 },
{ startIndex: 59, value: 29 },
],
[
{ startIndex: 0, value: 30 },
],
[
{ startIndex: 0, value: 31 },
]
];
let model: Model = null;
let languageRegistration: IDisposable = null;
setup(() => {
let _lineIndex = 0;
const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => NULL_STATE,
tokenize: undefined,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => {
let tokens = _tokens[_lineIndex++];
let result = new Uint32Array(2 * tokens.length);
for (let i = 0; i < tokens.length; i++) {
result[2 * i] = tokens[i].startIndex;
result[2 * i + 1] = (
tokens[i].value << modes.MetadataConsts.FOREGROUND_OFFSET
);
}
return new TokenizationResult2(result, state);
}
};
const LANGUAGE_ID = 'modelModeTest1';
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
model = Model.createFromString(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
// force tokenization
model.forceTokenization(model.getLineCount());
});
teardown(() => {
model.dispose();
model = null;
languageRegistration.dispose();
languageRegistration = null;
});
interface ITestViewLineToken {
endIndex: number;
value: number;
}
function assertViewLineTokens(actual: ViewLineToken[], expected: ITestViewLineToken[]): void {
let _actual = actual.map((token) => {
return {
endIndex: token.endIndex,
value: token.getForeground()
};
});
assert.deepEqual(_actual, expected);
}
interface ITestMinimapLineRenderingData {
content: string;
minColumn: number;
maxColumn: number;
tokens: ITestViewLineToken[];
}
function assertMinimapLineRenderingData(actual: ViewLineData, expected: ITestMinimapLineRenderingData): void {
if (actual === null && expected === null) {
assert.ok(true);
return;
}
assert.equal(actual.content, expected.content);
assert.equal(actual.minColumn, expected.minColumn);
assert.equal(actual.maxColumn, expected.maxColumn);
assertViewLineTokens(actual.tokens, expected.tokens);
}
function assertMinimapLinesRenderingData(actual: ViewLineData[], expected: ITestMinimapLineRenderingData[]): void {
assert.equal(actual.length, expected.length);
for (let i = 0; i < expected.length; i++) {
assertMinimapLineRenderingData(actual[i], expected[i]);
}
}
function assertAllMinimapLinesRenderingData(splitLinesCollection: SplitLinesCollection, all: ITestMinimapLineRenderingData[]): void {
let lineCount = all.length;
for (let start = 1; start <= lineCount; start++) {
for (let end = start; end <= lineCount; end++) {
let count = end - start + 1;
for (let desired = Math.pow(2, count) - 1; desired >= 0; desired--) {
let needed: boolean[] = [];
let expected: ITestMinimapLineRenderingData[] = [];
for (let i = 0; i < count; i++) {
needed[i] = (desired & (1 << i)) ? true : false;
expected[i] = (needed[i] ? all[start - 1 + i] : null);
}
let actual = splitLinesCollection.getViewLinesData(start, end, needed);
assertMinimapLinesRenderingData(actual, expected);
// Comment out next line to test all possible combinations
break;
}
}
}
}
test('getViewLinesData - no wrapping', () => {
withSplitLinesCollection(model, 'off', 0, (splitLinesCollection) => {
assert.equal(splitLinesCollection.getViewLineCount(), 8);
assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true);
let _expected: ITestMinimapLineRenderingData[] = [
{
content: 'class Nice {',
minColumn: 1,
maxColumn: 13,
tokens: [
{ endIndex: 5, value: 1 },
{ endIndex: 6, value: 2 },
{ endIndex: 10, value: 3 },
{ endIndex: 12, value: 4 },
]
},
{
content: ' function hi() {',
minColumn: 1,
maxColumn: 17,
tokens: [
{ endIndex: 1, value: 5 },
{ endIndex: 9, value: 6 },
{ endIndex: 10, value: 7 },
{ endIndex: 12, value: 8 },
{ endIndex: 16, value: 9 },
]
},
{
content: ' console.log("Hello world");',
minColumn: 1,
maxColumn: 30,
tokens: [
{ endIndex: 2, value: 10 },
{ endIndex: 9, value: 11 },
{ endIndex: 10, value: 12 },
{ endIndex: 13, value: 13 },
{ endIndex: 14, value: 14 },
{ endIndex: 27, value: 15 },
{ endIndex: 29, value: 16 },
]
},
{
content: ' }',
minColumn: 1,
maxColumn: 3,
tokens: [
{ endIndex: 2, value: 17 },
]
},
{
content: ' function hello() {',
minColumn: 1,
maxColumn: 20,
tokens: [
{ endIndex: 1, value: 18 },
{ endIndex: 9, value: 19 },
{ endIndex: 10, value: 20 },
{ endIndex: 15, value: 21 },
{ endIndex: 19, value: 22 },
]
},
{
content: ' console.log("Hello world, this is a somewhat longer line");',
minColumn: 1,
maxColumn: 62,
tokens: [
{ endIndex: 2, value: 23 },
{ endIndex: 9, value: 24 },
{ endIndex: 10, value: 25 },
{ endIndex: 13, value: 26 },
{ endIndex: 14, value: 27 },
{ endIndex: 59, value: 28 },
{ endIndex: 61, value: 29 },
]
},
{
minColumn: 1,
maxColumn: 3,
content: ' }',
tokens: [
{ endIndex: 2, value: 30 },
]
},
{
minColumn: 1,
maxColumn: 2,
content: '}',
tokens: [
{ endIndex: 1, value: 31 },
]
}
];
assertAllMinimapLinesRenderingData(splitLinesCollection, [
_expected[0],
_expected[1],
_expected[2],
_expected[3],
_expected[4],
_expected[5],
_expected[6],
_expected[7],
]);
splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]);
assert.equal(splitLinesCollection.getViewLineCount(), 5);
assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true);
assertAllMinimapLinesRenderingData(splitLinesCollection, [
_expected[0],
_expected[4],
_expected[5],
_expected[6],
_expected[7],
]);
});
});
test('getViewLinesData - with wrapping', () => {
withSplitLinesCollection(model, 'wordWrapColumn', 30, (splitLinesCollection) => {
assert.equal(splitLinesCollection.getViewLineCount(), 12);
assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true);
let _expected: ITestMinimapLineRenderingData[] = [
{
content: 'class Nice {',
minColumn: 1,
maxColumn: 13,
tokens: [
{ endIndex: 5, value: 1 },
{ endIndex: 6, value: 2 },
{ endIndex: 10, value: 3 },
{ endIndex: 12, value: 4 },
]
},
{
content: ' function hi() {',
minColumn: 1,
maxColumn: 17,
tokens: [
{ endIndex: 1, value: 5 },
{ endIndex: 9, value: 6 },
{ endIndex: 10, value: 7 },
{ endIndex: 12, value: 8 },
{ endIndex: 16, value: 9 },
]
},
{
content: ' console.log("Hello ',
minColumn: 1,
maxColumn: 22,
tokens: [
{ endIndex: 2, value: 10 },
{ endIndex: 9, value: 11 },
{ endIndex: 10, value: 12 },
{ endIndex: 13, value: 13 },
{ endIndex: 14, value: 14 },
{ endIndex: 21, value: 15 },
]
},
{
content: ' world");',
minColumn: 4,
maxColumn: 12,
tokens: [
{ endIndex: 9, value: 15 },
{ endIndex: 11, value: 16 },
]
},
{
content: ' }',
minColumn: 1,
maxColumn: 3,
tokens: [
{ endIndex: 2, value: 17 },
]
},
{
content: ' function hello() {',
minColumn: 1,
maxColumn: 20,
tokens: [
{ endIndex: 1, value: 18 },
{ endIndex: 9, value: 19 },
{ endIndex: 10, value: 20 },
{ endIndex: 15, value: 21 },
{ endIndex: 19, value: 22 },
]
},
{
content: ' console.log("Hello ',
minColumn: 1,
maxColumn: 22,
tokens: [
{ endIndex: 2, value: 23 },
{ endIndex: 9, value: 24 },
{ endIndex: 10, value: 25 },
{ endIndex: 13, value: 26 },
{ endIndex: 14, value: 27 },
{ endIndex: 21, value: 28 },
]
},
{
content: ' world, this is a ',
minColumn: 4,
maxColumn: 21,
tokens: [
{ endIndex: 20, value: 28 },
]
},
{
content: ' somewhat longer ',
minColumn: 4,
maxColumn: 20,
tokens: [
{ endIndex: 19, value: 28 },
]
},
{
content: ' line");',
minColumn: 4,
maxColumn: 11,
tokens: [
{ endIndex: 8, value: 28 },
{ endIndex: 10, value: 29 },
]
},
{
content: ' }',
minColumn: 1,
maxColumn: 3,
tokens: [
{ endIndex: 2, value: 30 },
]
},
{
content: '}',
minColumn: 1,
maxColumn: 2,
tokens: [
{ endIndex: 1, value: 31 },
]
}
];
assertAllMinimapLinesRenderingData(splitLinesCollection, [
_expected[0],
_expected[1],
_expected[2],
_expected[3],
_expected[4],
_expected[5],
_expected[6],
_expected[7],
_expected[8],
_expected[9],
_expected[10],
_expected[11],
]);
splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]);
assert.equal(splitLinesCollection.getViewLineCount(), 8);
assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), false);
assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true);
assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true);
assertAllMinimapLinesRenderingData(splitLinesCollection, [
_expected[0],
_expected[5],
_expected[6],
_expected[7],
_expected[8],
_expected[9],
_expected[10],
_expected[11],
]);
});
});
function withSplitLinesCollection(model: Model, wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded', wordWrapColumn: number, callback: (splitLinesCollection: SplitLinesCollection) => void): void {
let configuration = new TestConfiguration({
wordWrap: wordWrap,
wordWrapColumn: wordWrapColumn,
wrappingIndent: 'indent'
});
let factory = new CharacterHardWrappingLineMapperFactory(
configuration.editor.wrappingInfo.wordWrapBreakBeforeCharacters,
configuration.editor.wrappingInfo.wordWrapBreakAfterCharacters,
configuration.editor.wrappingInfo.wordWrapBreakObtrusiveCharacters
);
let linesCollection = new SplitLinesCollection(
model,
factory,
model.getOptions().tabSize,
configuration.editor.wrappingInfo.wrappingColumn,
configuration.editor.fontInfo.typicalFullwidthCharacterWidth / configuration.editor.fontInfo.typicalHalfwidthCharacterWidth,
configuration.editor.wrappingInfo.wrappingIndent
);
callback(linesCollection);
configuration.dispose();
}
});
function pos(lineNumber: number, column: number): Position {
return new Position(lineNumber, column);
}
function createSplitLine(splitLengths: number[], wrappedLinesPrefix: string, isVisible: boolean = true): SplitLine {
return new SplitLine(createLineMapping(splitLengths, wrappedLinesPrefix), isVisible);
}
function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): ILineMapping {
return new CharacterHardWrappingLineMapping(
new PrefixSumComputer(toUint32Array(breakingLengths)),
wrappedLinesPrefix
);
}
function createModel(text: string): IModel {
return {
getLineTokens: (lineNumber: number) => {
return null;
},
getLineContent: (lineNumber: number) => {
return text;
},
getLineMinColumn: (lineNumber: number) => {
return 1;
},
getLineMaxColumn: (lineNumber: number) => {
return text.length + 1;
}
};
}

View File

@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* 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 { 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 { MockCodeEditorCreationOptions } from 'vs/editor/test/common/mocks/mockCodeEditor';
export function testViewModel(text: string[], options: MockCodeEditorCreationOptions, callback: (viewModel: ViewModel, model: Model) => void): void {
const EDITOR_ID = 1;
let configuration = new TestConfiguration(options);
let model = Model.createFromString(text.join('\n'));
let viewModel = new ViewModel(EDITOR_ID, configuration, model, null);
callback(viewModel, model);
viewModel.dispose();
model.dispose();
configuration.dispose();
}

View File

@@ -0,0 +1,311 @@
/*---------------------------------------------------------------------------------------------
* 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 { Range } from 'vs/editor/common/core/range';
import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel';
import { MockCodeEditorCreationOptions } from 'vs/editor/test/common/mocks/mockCodeEditor';
suite('ViewModelDecorations', () => {
test('getDecorationsViewportData', () => {
const text = [
'hello world, this is a buffer that will be wrapped'
];
const opts: MockCodeEditorCreationOptions = {
wordWrap: 'wordWrapColumn',
wordWrapColumn: 13
};
testViewModel(text, opts, (viewModel, model) => {
assert.equal(viewModel.getLineContent(1), 'hello world, ');
assert.equal(viewModel.getLineContent(2), 'this is a ');
assert.equal(viewModel.getLineContent(3), 'buffer that ');
assert.equal(viewModel.getLineContent(4), 'will be ');
assert.equal(viewModel.getLineContent(5), 'wrapped');
let dec1: string;
let dec2: string;
let dec3: string;
let dec4: string;
let dec5: string;
let dec6: string;
let dec7: string;
let dec8: string;
let dec9: string;
let dec10: string;
let dec11: string;
let dec12: string;
let dec13: string;
let dec14: string;
let dec15: string;
model.changeDecorations((accessor) => {
let createOpts = (id: string) => {
return {
className: id,
inlineClassName: 'i-' + id,
beforeContentClassName: 'b-' + id,
afterContentClassName: 'a-' + id
};
};
// VIEWPORT will be (1,14) -> (1,36)
// completely before viewport
dec1 = accessor.addDecoration(new Range(1, 2, 1, 3), createOpts('dec1'));
// starts before viewport, ends at viewport start
dec2 = accessor.addDecoration(new Range(1, 2, 1, 14), createOpts('dec2'));
// starts before viewport, ends inside viewport
dec3 = accessor.addDecoration(new Range(1, 2, 1, 15), createOpts('dec3'));
// starts before viewport, ends at viewport end
dec4 = accessor.addDecoration(new Range(1, 2, 1, 36), createOpts('dec4'));
// starts before viewport, ends after viewport
dec5 = accessor.addDecoration(new Range(1, 2, 1, 51), createOpts('dec5'));
// starts at viewport start, ends at viewport start
dec6 = accessor.addDecoration(new Range(1, 14, 1, 14), createOpts('dec6'));
// starts at viewport start, ends inside viewport
dec7 = accessor.addDecoration(new Range(1, 14, 1, 16), createOpts('dec7'));
// starts at viewport start, ends at viewport end
dec8 = accessor.addDecoration(new Range(1, 14, 1, 36), createOpts('dec8'));
// starts at viewport start, ends after viewport
dec9 = accessor.addDecoration(new Range(1, 14, 1, 51), createOpts('dec9'));
// starts inside viewport, ends inside viewport
dec10 = accessor.addDecoration(new Range(1, 16, 1, 18), createOpts('dec10'));
// starts inside viewport, ends at viewport end
dec11 = accessor.addDecoration(new Range(1, 16, 1, 36), createOpts('dec11'));
// starts inside viewport, ends after viewport
dec12 = accessor.addDecoration(new Range(1, 16, 1, 51), createOpts('dec12'));
// starts at viewport end, ends at viewport end
dec13 = accessor.addDecoration(new Range(1, 36, 1, 36), createOpts('dec13'));
// starts at viewport end, ends after viewport
dec14 = accessor.addDecoration(new Range(1, 36, 1, 51), createOpts('dec14'));
// starts after viewport, ends after viewport
dec15 = accessor.addDecoration(new Range(1, 40, 1, 51), createOpts('dec15'));
});
let actualDecorations = viewModel.getDecorationsInViewport(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3))
).map((dec) => {
return dec.source.id;
});
assert.deepEqual(actualDecorations, [
dec2,
dec3,
dec4,
dec5,
dec6,
dec7,
dec8,
dec9,
dec10,
dec11,
dec12,
dec13,
dec14,
]);
let inlineDecorations1 = viewModel.getViewLineRenderingData(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)),
2
).inlineDecorations;
// view line 2: (1,14 -> 1,24)
assert.deepEqual(inlineDecorations1, [
{
range: new Range(1, 2, 2, 1),
inlineClassName: 'i-dec2',
insertsBeforeOrAfter: false
},
{
range: new Range(1, 2, 2, 2),
inlineClassName: 'i-dec3',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 2),
inlineClassName: 'a-dec3',
insertsBeforeOrAfter: true
},
{
range: new Range(1, 2, 4, 1),
inlineClassName: 'i-dec4',
insertsBeforeOrAfter: false
},
{
range: new Range(1, 2, 5, 8),
inlineClassName: 'i-dec5',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 1),
inlineClassName: 'i-dec6',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 2),
inlineClassName: 'b-dec6',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 1, 2, 3),
inlineClassName: 'i-dec7',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 2),
inlineClassName: 'b-dec7',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 2, 2, 3),
inlineClassName: 'a-dec7',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 1, 4, 1),
inlineClassName: 'i-dec8',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 2),
inlineClassName: 'b-dec8',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 1, 5, 8),
inlineClassName: 'i-dec9',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 2, 2),
inlineClassName: 'b-dec9',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 3, 2, 5),
inlineClassName: 'i-dec10',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 3, 2, 4),
inlineClassName: 'b-dec10',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 4, 2, 5),
inlineClassName: 'a-dec10',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 3, 4, 1),
inlineClassName: 'i-dec11',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 3, 2, 4),
inlineClassName: 'b-dec11',
insertsBeforeOrAfter: true
},
{
range: new Range(2, 3, 5, 8),
inlineClassName: 'i-dec12',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 3, 2, 4),
inlineClassName: 'b-dec12',
insertsBeforeOrAfter: true
},
]);
let inlineDecorations2 = viewModel.getViewLineRenderingData(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)),
3
).inlineDecorations;
// view line 3 (24 -> 36)
assert.deepEqual(inlineDecorations2, [
{
range: new Range(1, 2, 4, 1),
inlineClassName: 'i-dec4',
insertsBeforeOrAfter: false
},
{
range: new Range(1, 2, 5, 8),
inlineClassName: 'i-dec5',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 4, 1),
inlineClassName: 'i-dec8',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 1, 5, 8),
inlineClassName: 'i-dec9',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 3, 4, 1),
inlineClassName: 'i-dec11',
insertsBeforeOrAfter: false
},
{
range: new Range(2, 3, 5, 8),
inlineClassName: 'i-dec12',
insertsBeforeOrAfter: false
},
]);
});
});
test('issue #17208: Problem scrolling in 1.8.0', () => {
const text = [
'hello world, this is a buffer that will be wrapped'
];
const opts: MockCodeEditorCreationOptions = {
wordWrap: 'wordWrapColumn',
wordWrapColumn: 13
};
testViewModel(text, opts, (viewModel, model) => {
assert.equal(viewModel.getLineContent(1), 'hello world, ');
assert.equal(viewModel.getLineContent(2), 'this is a ');
assert.equal(viewModel.getLineContent(3), 'buffer that ');
assert.equal(viewModel.getLineContent(4), 'will be ');
assert.equal(viewModel.getLineContent(5), 'wrapped');
let dec1: string;
model.changeDecorations((accessor) => {
dec1 = accessor.addDecoration(
new Range(1, 50, 1, 51),
{
beforeContentClassName: 'dec1'
}
);
});
let decorations = viewModel.getDecorationsInViewport(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3))
);
assert.deepEqual(decorations, []);
let inlineDecorations1 = viewModel.getViewLineRenderingData(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)),
2
).inlineDecorations;
assert.deepEqual(inlineDecorations1, []);
let inlineDecorations2 = viewModel.getViewLineRenderingData(
new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)),
3
).inlineDecorations;
assert.deepEqual(inlineDecorations2, []);
});
});
});

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* 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 { Range } from 'vs/editor/common/core/range';
import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel';
suite('ViewModel', () => {
test('issue #21073: SplitLinesCollection: attempt to access a \'newer\' model', () => {
const text = [''];
const opts = {
lineNumbersMinChars: 1
};
testViewModel(text, opts, (viewModel, model) => {
assert.equal(viewModel.getLineCount(), 1);
viewModel.setViewport(1, 1, 1);
model.applyEdits([{
identifier: null,
range: new Range(1, 1, 1, 1),
text: [
'line01',
'line02',
'line03',
'line04',
'line05',
'line06',
'line07',
'line08',
'line09',
'line10',
].join('\n'),
forceMoveMarkers: false
}]);
assert.equal(viewModel.getLineCount(), 10);
});
});
});