/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. 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 {EditOperation} from 'vs/editor/common/core/editOperation'; import {Position} from 'vs/editor/common/core/position'; import {Range} from 'vs/editor/common/core/range'; import {EndOfLinePreference, EventType, Handler, IPosition, ISelection, DefaultEndOfLine} from 'vs/editor/common/editorCommon'; import {Model} from 'vs/editor/common/model/model'; import {IMode, IRichEditSupport, IndentAction} from 'vs/editor/common/modes'; import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration'; import {BracketMode} from 'vs/editor/test/common/testModes'; let H = Handler; // --------- utils function cursorCommand(cursor: Cursor, command: string, extraData?: any, sizeProvider?: { pageSize: number; }, overwriteSource?: string) { if (sizeProvider) { cursor.configuration.editor.pageSize = sizeProvider.pageSize; } cursor.configuration.handlerDispatcher.trigger(overwriteSource || 'tests', command, extraData); } function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.MoveToSelect : H.MoveTo, { position: new Position(lineNumber, column) }); } function moveLeft(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorLeftSelect : H.CursorLeft); } function moveWordLeft(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorWordLeftSelect : H.CursorWordLeft); } function moveRight(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorRightSelect : H.CursorRight); } function moveWordRight(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorWordRightSelect : H.CursorWordRight); } function moveDown(cursor: Cursor, linesCount: number, inSelectionMode: boolean = false) { if (linesCount === 1) { cursorCommand(cursor, inSelectionMode ? H.CursorDownSelect : H.CursorDown); } else { cursorCommand(cursor, inSelectionMode ? H.CursorPageDownSelect : H.CursorPageDown, null, { pageSize: linesCount }); } } function moveUp(cursor: Cursor, linesCount: number, inSelectionMode: boolean = false) { if (linesCount === 1) { cursorCommand(cursor, inSelectionMode ? H.CursorUpSelect : H.CursorUp); } else { cursorCommand(cursor, inSelectionMode ? H.CursorPageUpSelect : H.CursorPageUp, null, { pageSize: linesCount }); } } function moveToBeginningOfLine(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorHomeSelect : H.CursorHome); } function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorEndSelect : H.CursorEnd); } function moveToBeginningOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorTopSelect : H.CursorTop); } function moveToEndOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { cursorCommand(cursor, inSelectionMode ? H.CursorBottomSelect : H.CursorBottom); } function deleteWordLeft(cursor: Cursor) { cursorCommand(cursor, H.DeleteWordLeft); } function deleteWordRight(cursor: Cursor) { cursorCommand(cursor, H.DeleteWordRight); } function positionEqual(position:IPosition, lineNumber: number, column: number) { assert.deepEqual({ lineNumber: position.lineNumber, column: position.column }, { lineNumber: lineNumber, column: column }, 'position equal'); } function selectionEqual(selection:ISelection, 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 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); } suite('Editor Controller - Cursor', () => { const LINE1 = ' \tMy First Line\t '; const LINE2 = '\tMy Second Line'; const LINE3 = ' Third Line💩'; const LINE4 = ''; const LINE5 = '1'; let thisModel: Model; let thisConfiguration: MockConfiguration; let thisCursor: Cursor; setup(() => { let text = LINE1 + '\r\n' + LINE2 + '\n' + LINE3 + '\n' + LINE4 + '\r\n' + LINE5; thisModel = new Model(text, DefaultEndOfLine.LF, null); thisConfiguration = new MockConfiguration(null); thisCursor = new Cursor(1, thisConfiguration, thisModel, null, false); }); teardown(() => { thisCursor.dispose(); thisModel.dispose(); thisConfiguration.dispose(); }); test('cursor initialized', () => { cursorEqual(thisCursor, 1, 1); }); // --------- absolute move test('no move', () => { moveTo(thisCursor, 1, 1); cursorEqual(thisCursor, 1, 1); }); test('move', () => { moveTo(thisCursor, 1, 2); cursorEqual(thisCursor, 1, 2); }); test('move in selection mode', () => { moveTo(thisCursor, 1, 2, true); cursorEqual(thisCursor, 1, 2, 1, 1); }); test('move beyond line end', () => { moveTo(thisCursor, 1, 25); cursorEqual(thisCursor, 1, LINE1.length + 1); }); test('move empty line', () => { moveTo(thisCursor, 4, 20); cursorEqual(thisCursor, 4, 1); }); test('move one char line', () => { moveTo(thisCursor, 5, 20); cursorEqual(thisCursor, 5, 2); }); test('selection down', () => { moveTo(thisCursor, 2, 1, true); cursorEqual(thisCursor, 2, 1, 1, 1); }); test('move and then select', () => { moveTo(thisCursor, 2, 3); cursorEqual(thisCursor, 2, 3); moveTo(thisCursor, 2, 15, true); cursorEqual(thisCursor, 2, 15, 2, 3); moveTo(thisCursor, 1, 2, true); cursorEqual(thisCursor, 1, 2, 2, 3); }); // --------- move left test('move left on top left position', () => { moveLeft(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move left', () => { moveTo(thisCursor, 1, 3); cursorEqual(thisCursor, 1, 3); moveLeft(thisCursor); cursorEqual(thisCursor, 1, 2); }); test('move left with surrogate pair', () => { moveTo(thisCursor, 3, 17); cursorEqual(thisCursor, 3, 17); moveLeft(thisCursor); cursorEqual(thisCursor, 3, 15); }); test('move left goes to previous row', () => { moveTo(thisCursor, 2, 1); cursorEqual(thisCursor, 2, 1); moveLeft(thisCursor); cursorEqual(thisCursor, 1, 21); }); test('move left selection', () => { moveTo(thisCursor, 2, 1); cursorEqual(thisCursor, 2, 1); moveLeft(thisCursor, true); cursorEqual(thisCursor, 1, 21, 2, 1); }); // --------- move word left test('move word left', () => { moveTo(thisCursor, 5, 2); let expectedStops = [ [5, 1], [4, 1], [3, 11], [3, 5], [3, 1], [2, 12], [2, 5], [2, 2], [2, 1], [1, 15], [1, 9], [1, 6], [1, 1], [1, 1], ]; let actualStops:number[][] = []; for (let i = 0; i < expectedStops.length; i++) { moveWordLeft(thisCursor); let pos = thisCursor.getPosition(); actualStops.push([pos.lineNumber, pos.column]); } assert.deepEqual(actualStops, expectedStops); }); test('move word left selection', () => { moveTo(thisCursor, 5, 2); cursorEqual(thisCursor, 5, 2); moveWordLeft(thisCursor, true); cursorEqual(thisCursor, 5, 1, 5, 2); }); // --------- move right test('move right on bottom right position', () => { moveTo(thisCursor, 5, 2); cursorEqual(thisCursor, 5, 2); moveRight(thisCursor); cursorEqual(thisCursor, 5, 2); }); test('move right', () => { moveTo(thisCursor, 1, 3); cursorEqual(thisCursor, 1, 3); moveRight(thisCursor); cursorEqual(thisCursor, 1, 4); }); test('move right with surrogate pair', () => { moveTo(thisCursor, 3, 15); cursorEqual(thisCursor, 3, 15); moveRight(thisCursor); cursorEqual(thisCursor, 3, 17); }); test('move right goes to next row', () => { moveTo(thisCursor, 1, 21); cursorEqual(thisCursor, 1, 21); moveRight(thisCursor); cursorEqual(thisCursor, 2, 1); }); test('move right selection', () => { moveTo(thisCursor, 1, 21); cursorEqual(thisCursor, 1, 21); moveRight(thisCursor, true); cursorEqual(thisCursor, 2, 1, 1, 21); }); // --------- move word right test('move word right', () => { moveTo(thisCursor, 1, 1); let expectedStops = [ [1, 8], [1, 14], [1, 19], [1, 21], [2, 4], [2, 11], [2, 16], [3, 10], [3, 17], [4, 1], [5, 2], [5, 2], ]; let actualStops:number[][] = []; for (let i = 0; i < expectedStops.length; i++) { moveWordRight(thisCursor); let pos = thisCursor.getPosition(); actualStops.push([pos.lineNumber, pos.column]); } assert.deepEqual(actualStops, expectedStops); }); test('move word right selection', () => { moveTo(thisCursor, 1, 1); cursorEqual(thisCursor, 1, 1); moveWordRight(thisCursor, true); cursorEqual(thisCursor, 1, 8, 1, 1); }); // --------- move down test('move down', () => { moveDown(thisCursor, 1); cursorEqual(thisCursor, 2, 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 3, 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 4, 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 5, 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 5, 2); }); test('move down with selection', () => { moveDown(thisCursor, 1, true); cursorEqual(thisCursor, 2, 1, 1, 1); moveDown(thisCursor, 1, true); cursorEqual(thisCursor, 3, 1, 1, 1); moveDown(thisCursor, 1, true); cursorEqual(thisCursor, 4, 1, 1, 1); moveDown(thisCursor, 1, true); cursorEqual(thisCursor, 5, 1, 1, 1); moveDown(thisCursor, 1, true); cursorEqual(thisCursor, 5, 2, 1, 1); }); test('move down with tabs', () => { moveTo(thisCursor, 1, 5); cursorEqual(thisCursor, 1, 5); moveDown(thisCursor, 1); cursorEqual(thisCursor, 2, 2); moveDown(thisCursor, 1); cursorEqual(thisCursor, 3, 5); moveDown(thisCursor, 1); cursorEqual(thisCursor, 4, 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 5, 2); }); // --------- move up test('move up', () => { moveTo(thisCursor, 3, 5); cursorEqual(thisCursor, 3, 5); moveUp(thisCursor, 1); cursorEqual(thisCursor, 2, 2); moveUp(thisCursor, 1); cursorEqual(thisCursor, 1, 5); }); test('move up with selection', () => { 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', () => { 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', () => { moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length - 1); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length + 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 2, LINE2.length + 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 3, LINE3.length + 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 4, LINE4.length + 1); moveDown(thisCursor, 1); cursorEqual(thisCursor, 5, LINE5.length + 1); moveUp(thisCursor, 4); cursorEqual(thisCursor, 1, LINE1.length + 1); }); // --------- move to beginning of line test('move to beginning of line', () => { moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 6); moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move to beginning of line from within line', () => { moveTo(thisCursor, 1, 8); moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 6); moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move to beginning of line from whitespace at beginning of line', () => { moveTo(thisCursor, 1, 2); moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 1); moveToBeginningOfLine(thisCursor); cursorEqual(thisCursor, 1, 6); }); test('move to beginning of line from within line selection', () => { moveTo(thisCursor, 1, 8); moveToBeginningOfLine(thisCursor, true); cursorEqual(thisCursor, 1, 6, 1, 8); moveToBeginningOfLine(thisCursor, true); cursorEqual(thisCursor, 1, 1, 1, 8); }); // --------- move to end of line test('move to end of line', () => { moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length - 1); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length + 1); }); test('move to end of line from within line', () => { moveTo(thisCursor, 1, 6); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length - 1); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length + 1); }); test('move to end of line from whitespace at end of line', () => { moveTo(thisCursor, 1, 20); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length + 1); moveToEndOfLine(thisCursor); cursorEqual(thisCursor, 1, LINE1.length - 1); }); test('move to end of line from within line selection', () => { moveTo(thisCursor, 1, 6); moveToEndOfLine(thisCursor, true); cursorEqual(thisCursor, 1, LINE1.length - 1, 1, 6); moveToEndOfLine(thisCursor, true); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 6); }); // --------- move to beginning of buffer test('move to beginning of buffer', () => { moveToBeginningOfBuffer(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move to beginning of buffer from within first line', () => { moveTo(thisCursor, 1, 3); moveToBeginningOfBuffer(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move to beginning of buffer from within another line', () => { moveTo(thisCursor, 3, 3); moveToBeginningOfBuffer(thisCursor); cursorEqual(thisCursor, 1, 1); }); test('move to beginning of buffer from within first line selection', () => { moveTo(thisCursor, 1, 3); moveToBeginningOfBuffer(thisCursor, true); cursorEqual(thisCursor, 1, 1, 1, 3); }); test('move to beginning of buffer from within another line selection', () => { moveTo(thisCursor, 3, 3); moveToBeginningOfBuffer(thisCursor, true); cursorEqual(thisCursor, 1, 1, 3, 3); }); // --------- move to end of buffer test('move to end of buffer', () => { moveToEndOfBuffer(thisCursor); cursorEqual(thisCursor, 5, LINE5.length + 1); }); test('move to end of buffer from within last line', () => { moveTo(thisCursor, 5, 1); moveToEndOfBuffer(thisCursor); cursorEqual(thisCursor, 5, LINE5.length + 1); }); test('move to end of buffer from within another line', () => { moveTo(thisCursor, 3, 3); moveToEndOfBuffer(thisCursor); cursorEqual(thisCursor, 5, LINE5.length + 1); }); test('move to end of buffer from within last line selection', () => { moveTo(thisCursor, 5, 1); moveToEndOfBuffer(thisCursor, true); cursorEqual(thisCursor, 5, LINE5.length + 1, 5, 1); }); test('move to end of buffer from within another line selection', () => { moveTo(thisCursor, 3, 3); moveToEndOfBuffer(thisCursor, true); cursorEqual(thisCursor, 5, LINE5.length + 1, 3, 3); }); // --------- delete word left/right test('delete word left for non-empty selection', () => { moveTo(thisCursor, 3, 7); moveRight(thisCursor, true); moveRight(thisCursor, true); deleteWordLeft(thisCursor); assert.equal(thisModel.getLineContent(3), ' Thd Line💩'); cursorEqual(thisCursor, 3, 7); }); test('delete word left for caret at beginning of document', () => { moveTo(thisCursor, 1, 1); deleteWordLeft(thisCursor); assert.equal(thisModel.getLineContent(1), ' \tMy First Line\t '); cursorEqual(thisCursor, 1, 1); }); test('delete word left for caret at end of whitespace', () => { moveTo(thisCursor, 3, 11); deleteWordLeft(thisCursor); assert.equal(thisModel.getLineContent(3), ' Line💩'); cursorEqual(thisCursor, 3, 5); }); test('delete word left for caret just behind a word', () => { moveTo(thisCursor, 2, 11); deleteWordLeft(thisCursor); assert.equal(thisModel.getLineContent(2), '\tMy Line'); cursorEqual(thisCursor, 2, 5); }); test('delete word left for caret inside of a word', () => { moveTo(thisCursor, 1, 12); deleteWordLeft(thisCursor); assert.equal(thisModel.getLineContent(1), ' \tMy st Line\t '); cursorEqual(thisCursor, 1, 9); }); test('delete word right for non-empty selection', () => { moveTo(thisCursor, 3, 7); moveRight(thisCursor, true); moveRight(thisCursor, true); deleteWordRight(thisCursor); assert.equal(thisModel.getLineContent(3), ' Thd Line💩'); cursorEqual(thisCursor, 3, 7); }); test('delete word right for caret at end of document', () => { moveTo(thisCursor, 5, 3); deleteWordRight(thisCursor); assert.equal(thisModel.getLineContent(5), '1'); cursorEqual(thisCursor, 5, 2); }); test('delete word right for caret at beggining of whitespace', () => { moveTo(thisCursor, 3, 1); deleteWordRight(thisCursor); assert.equal(thisModel.getLineContent(3), 'Third Line💩'); cursorEqual(thisCursor, 3, 1); }); test('delete word right for caret just before a word', () => { moveTo(thisCursor, 2, 5); deleteWordRight(thisCursor); assert.equal(thisModel.getLineContent(2), '\tMy Line'); cursorEqual(thisCursor, 2, 5); }); test('delete word right for caret inside of a word', () => { moveTo(thisCursor, 1, 11); deleteWordRight(thisCursor); assert.equal(thisModel.getLineContent(1), ' \tMy Fi Line\t '); cursorEqual(thisCursor, 1, 11); }); // --------- misc test('select all', () => { cursorCommand(thisCursor, H.SelectAll); cursorEqual(thisCursor, 5, LINE5.length + 1, 1, 1); }); test('expandLineSelection', () => { // 0 1 2 // 01234 56789012345678 0 // let LINE1 = ' \tMy First Line\t '; moveTo(thisCursor, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); moveTo(thisCursor, 1, 2); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); moveTo(thisCursor, 1, 5); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); moveTo(thisCursor, 1, 19); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); moveTo(thisCursor, 1, 20); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); moveTo(thisCursor, 1, 21); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 1, LINE1.length + 1, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 2, LINE2.length + 1, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 3, LINE3.length + 1, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 4, LINE4.length + 1, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 5, LINE5.length + 1, 1, 1); cursorCommand(thisCursor, H.ExpandLineSelection); cursorEqual(thisCursor, 5, LINE5.length + 1, 1, 1); }); // --------- eventing test('no move doesn\'t trigger event', () => { thisCursor.addListener(EventType.CursorPositionChanged, (e) => { assert.ok(false, 'was not expecting event'); }); thisCursor.addListener(EventType.CursorSelectionChanged, (e) => { assert.ok(false, 'was not expecting event'); }); moveTo(thisCursor, 1, 1); }); test('move eventing', () => { let events = 0; thisCursor.addListener(EventType.CursorPositionChanged, (e) => { events++; positionEqual(e.position, 1, 2); }); thisCursor.addListener(EventType.CursorSelectionChanged, (e) => { events++; selectionEqual(e.selection, 1, 2, 1, 2); }); moveTo(thisCursor, 1, 2); assert.equal(events, 2, 'receives 2 events'); }); test('move in selection mode eventing', () => { let events = 0; thisCursor.addListener(EventType.CursorPositionChanged, (e) => { events++; positionEqual(e.position, 1, 2); }); thisCursor.addListener(EventType.CursorSelectionChanged, (e) => { events++; selectionEqual(e.selection, 1, 2, 1, 1); }); moveTo(thisCursor, 1, 2, true); assert.equal(events, 2, 'receives 2 events'); }); // --------- state save & restore test('saveState & restoreState', () => { moveTo(thisCursor, 2, 1, true); cursorEqual(thisCursor, 2, 1, 1, 1); let savedState = JSON.stringify(thisCursor.saveState()); moveTo(thisCursor, 1, 1, false); cursorEqual(thisCursor, 1, 1); thisCursor.restoreState(JSON.parse(savedState)); cursorEqual(thisCursor, 2, 1, 1, 1); }); // --------- updating cursor test('Independent model edit 1', () => { moveTo(thisCursor, 2, 16, true); thisModel.applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]); cursorEqual(thisCursor, 2, 15, 1, 1); }); }); class TestMode { public getId():string { return 'testing'; } public toSimplifiedMode(): IMode { return this; } } class SurroundingMode extends TestMode { public richEditSupport: IRichEditSupport; constructor() { super(); this.richEditSupport = new RichEditSupport(this.getId(), null, { __characterPairSupport: { autoClosingPairs: [{ open: '(', close: ')' }] } }); } } class OnEnterMode extends TestMode { public richEditSupport: IRichEditSupport; constructor(indentAction: IndentAction) { super(); this.richEditSupport = { onEnter: { onEnter: (model, position) => { return { indentAction: indentAction }; } } }; } } suite('Editor Controller - Regression tests', () => { test('Bug 9121: Auto indent + undo + redo is funky', () => { usingCursor({ text: [ '' ], mode: null, config: { insertSpaces: false, tabSize: 4 } }, (model, cursor) => { cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n', 'assert1'); cursorCommand(cursor, H.Tab, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2'); cursorCommand(cursor, H.Type, { text: '\n'}, null, 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\t', 'assert3'); cursorCommand(cursor, H.Type, { text: 'x' }); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert4'); cursorCommand(cursor, H.CursorLeft, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert5'); cursorCommand(cursor, H.DeleteLeft, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert6'); cursorCommand(cursor, H.DeleteLeft, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert7'); cursorCommand(cursor, H.DeleteLeft, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert8'); cursorCommand(cursor, H.DeleteLeft, {}); assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert9'); cursorCommand(cursor, H.Undo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert10'); cursorCommand(cursor, H.Undo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert11'); cursorCommand(cursor, H.Undo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert12'); cursorCommand(cursor, H.Redo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert13'); cursorCommand(cursor, H.Redo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert14'); cursorCommand(cursor, H.Redo, {}); assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert15'); }); }); test('issue #183: jump to matching bracket position', () => { usingCursor({ text: [ 'var x = (3 + (5-7));' ], mode: new BracketMode(), config: null }, (model, cursor) => { // ensure is tokenized model.getLineContext(1); moveTo(cursor, 1, 20); cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); cursorEqual(cursor, 1, 10); cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); cursorEqual(cursor, 1, 20); cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); cursorEqual(cursor, 1, 10); }); }); test('bug #16543: Tab should indent to correct indentation spot immediately', () => { usingCursor({ text: [ 'function baz() {', '\tfunction hello() { // something here', '\t', '', '\t}', '}' ], mode: new OnEnterMode(IndentAction.Indent), config: { insertSpaces: false, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 4, 1, false); cursorEqual(cursor, 4, 1, 4, 1); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(4), '\t\t'); }); }); test('Bug 18276:[editor] Indentation broken when selection is empty', () => { usingCursor({ text: [ 'function baz() {' ], mode: null, config: { insertSpaces: false, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 2, false); cursorEqual(cursor, 1, 2, 1, 2); cursorCommand(cursor, H.Indent, null, null, 'keyboard'); assert.equal(model.getLineContent(1), '\tfunction baz() {'); cursorEqual(cursor, 1, 3, 1, 3); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(1), '\tf\tunction baz() {'); }); }); test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => { usingCursor({ text: [ ' function baz() {' ], mode: new OnEnterMode(IndentAction.IndentOutdent), config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 6, false); cursorEqual(cursor, 1, 6, 1, 6); cursorCommand(cursor, H.Outdent, null, null, 'keyboard'); assert.equal(model.getLineContent(1), ' function baz() {'); cursorEqual(cursor, 1, 5, 1, 5); }); }); test('Bug #18293:[regression][editor] Can\'t outdent whitespace line', () => { usingCursor({ text: [ ' ' ], mode: null, config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 7, false); cursorEqual(cursor, 1, 7, 1, 7); cursorCommand(cursor, H.Outdent, null, null, 'keyboard'); assert.equal(model.getLineContent(1), ' '); cursorEqual(cursor, 1, 5, 1, 5); }); }); test('Bug #16657: [editor] Tab on empty line of zero indentation moves cursor to position (1,1)', () => { usingCursor({ text: [ 'function baz() {', '\tfunction hello() { // something here', '\t', '', '\t}', '}', '' ], mode: null, config: { insertSpaces: false, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 7, 1, false); cursorEqual(cursor, 7, 1, 7, 1); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(7), '\t'); cursorEqual(cursor, 7, 2, 7, 2); }); }); test('bug #16740: [editor] Cut line doesn\'t quite cut the last line', () => { // Part 1 => there is text on the last line let text = [ 'asdasd', 'qwerty' ]; let model = new Model(text.join('\n'), DefaultEndOfLine.LF, null); let cursor = new Cursor(1, new MockConfiguration({ insertSpaces: false, tabSize: 4 }), model, null, true); moveTo(cursor, 2, 1, false); cursorEqual(cursor, 2, 1, 2, 1); cursorCommand(cursor, H.Cut, null, null, 'keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'asdasd'); cursor.dispose(); model.dispose(); // Part 2 => there is no text on the last line text = [ 'asdasd', '' ]; model = new Model(text.join('\n'), DefaultEndOfLine.LF, null); cursor = new Cursor(1, new MockConfiguration({ insertSpaces: false, tabSize: 4 }), model, null, true); moveTo(cursor, 2, 1, false); cursorEqual(cursor, 2, 1, 2, 1); cursorCommand(cursor, H.Cut, null, null, 'keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'asdasd'); cursorCommand(cursor, H.Cut, null, null, 'keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), ''); cursor.dispose(); model.dispose(); }); test('Bug #11476: Double bracket surrounding + undo is broken', () => { usingCursor({ text: [ 'hello' ], mode: new SurroundingMode(), config: { insertSpaces: true } }, (model, cursor) => { moveTo(cursor, 1, 3, false); moveTo(cursor, 1, 5, true); cursorEqual(cursor, 1, 5, 1, 3); cursorCommand(cursor, H.Type, { text: '(' }, null, 'keyboard'); cursorEqual(cursor, 1, 6, 1, 4); cursorCommand(cursor, H.Type, { text: '(' }, null, 'keyboard'); cursorEqual(cursor, 1, 7, 1, 5); }); }); test('issue #1140: Backspace stops prematurely', () => { usingCursor({ text: [ 'function baz() {', ' return 1;', '};' ], mode: new SurroundingMode(), config: { insertSpaces: true } }, (model, cursor) => { moveTo(cursor, 3, 2, false); moveTo(cursor, 1, 14, true); cursorEqual(cursor, 1, 14, 3, 2); cursorCommand(cursor, H.DeleteLeft); cursorEqual(cursor, 1, 14, 1, 14); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'function baz(;'); }); }); test('issue #1336: Insert cursor below on last line adds a cursor to the end of the current line', () => { usingCursor({ text: [ 'abc' ], mode: null, config: null }, (model, cursor) => { cursorCommand(cursor, H.AddCursorDown); assert.equal(cursor.getSelections().length, 1); }); }); test('issue #2205: Multi-cursor pastes in reverse order', () => { usingCursor({ text: [ 'abc', 'def' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 2, 1, false); cursorCommand(cursor, H.AddCursorUp); assert.equal(cursor.getSelections().length, 2); cursorCommand(cursor, H.Paste, { text: '1\n2' }); assert.equal(model.getLineContent(1), '1abc'); assert.equal(model.getLineContent(2), '2def'); }); }); test('issue #3071: Investigate why undo stack gets corrupted', () => { usingCursor({ text: [ 'some lines', 'and more lines', 'just some text', ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 1, false); moveTo(cursor, 3, 4, true); let isFirst = true; model.addListener2(EventType.ModelContentChanged, (e) => { if (isFirst) { isFirst = false; cursorCommand(cursor, H.Type, { text: '\t' }, null, 'keyboard'); } }); cursorCommand(cursor, H.Tab); assert.equal(model.getValue(), [ '\t just some text' ].join('\n'), '001'); cursorCommand(cursor, H.Undo); assert.equal(model.getValue(), [ 'some lines', 'and more lines', 'just some text', ].join('\n'), '002'); cursorCommand(cursor, H.Undo); assert.equal(model.getValue(), [ 'some lines', 'and more lines', 'just some text', ].join('\n'), '003'); }); }); test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => { usingCursor({ text: [ 'function a() {', '\tvar a = {', '\t\tx: 3', '\t};', '}', ], mode: null, config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 3, 2, false); cursorCommand(cursor, H.Tab); assert.equal(model.getLineContent(3), '\t \tx: 3'); }); }); test('issue #832: deleteWordLeft', () => { usingCursor({ text: [ ' /* Just some text a+= 3 +5 */ ' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 37, false); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 */', '001'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 ', '002'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '003'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '004'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '005'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text a', '006'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some text ', '007'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just some ', '008'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* Just ', '009'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' /* ', '010'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), ' ', '011'); deleteWordLeft(cursor); assert.equal(model.getLineContent(1), '', '012'); }); }); test('issue #832: deleteWordRight', () => { usingCursor({ text: [ ' /* Just some text a+= 3 +5-3 */ ' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 1, false); deleteWordRight(cursor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ ', '001'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ ', '002'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ ', '003'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ ', '004'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ ', '005'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ ', '006'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ ', '007'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' +5-3 */ ', '008'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), '5-3 */ ', '009'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), '-3 */ ', '010'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), '3 */ ', '011'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' */ ', '012'); deleteWordRight(cursor); assert.equal(model.getLineContent(1), ' ', '013'); }); }); test('issue #832: moveWordLeft', () => { usingCursor({ text: [ ' /* Just some more text a+= 3 +5-3 + 7 */ ' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 50, false); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1, '001'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + '.length + 1, '002'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 '.length + 1, '003'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '004'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '005'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '006'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 '.length + 1, '007'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= '.length + 1, '008'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a'.length + 1, '009'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text '.length + 1, '010'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more '.length + 1, '011'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just some '.length + 1, '012'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* Just '.length + 1, '013'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' /* '.length + 1, '014'); moveWordLeft(cursor); assert.equal(cursor.getPosition().column, ' '.length + 1, '015'); }); }); test('issue #832: moveWordLeft', () => { usingCursor({ text: [ ' /* Just some more text a+= 3 +5-3 + 7 */ ' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 1, false); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /*'.length + 1, '001'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just'.length + 1, '003'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some'.length + 1, '004'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more'.length + 1, '005'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text'.length + 1, '006'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a'.length + 1, '007'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+='.length + 1, '008'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3'.length + 1, '009'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '010'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '011'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '012'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3'.length + 1, '013'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 +'.length + 1, '014'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1, '015'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1, '016'); moveWordRight(cursor); assert.equal(cursor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1, '016'); }); }); test('issue #832: word right', () => { usingCursor({ text: [ ' /* Just some more text a+= 3 +5-3 + 7 */ ' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, 1, 1, false); function assertWordRight(col, expectedCol) { cursorCommand(cursor, col === 1 ? H.WordSelect : H.WordSelectDrag, { position: { lineNumber: 1, column: col }, preference: 'right' }); assert.equal(cursor.getSelection().startColumn, 1, 'TEST FOR ' + col); assert.equal(cursor.getSelection().endColumn, expectedCol, 'TEST FOR ' + col); } assertWordRight( 1, ' '.length + 1); assertWordRight( 2, ' '.length + 1); assertWordRight( 3, ' '.length + 1); assertWordRight( 4, ' '.length + 1); assertWordRight( 5, ' /'.length + 1); assertWordRight( 6, ' /*'.length + 1); assertWordRight( 7, ' /* '.length + 1); assertWordRight( 8, ' /* Just'.length + 1); assertWordRight( 9, ' /* Just'.length + 1); assertWordRight(10, ' /* Just'.length + 1); assertWordRight(11, ' /* Just'.length + 1); assertWordRight(12, ' /* Just '.length + 1); assertWordRight(13, ' /* Just some'.length + 1); assertWordRight(14, ' /* Just some'.length + 1); assertWordRight(15, ' /* Just some'.length + 1); assertWordRight(16, ' /* Just some'.length + 1); assertWordRight(17, ' /* Just some '.length + 1); assertWordRight(18, ' /* Just some '.length + 1); assertWordRight(19, ' /* Just some '.length + 1); assertWordRight(20, ' /* Just some more'.length + 1); assertWordRight(21, ' /* Just some more'.length + 1); assertWordRight(22, ' /* Just some more'.length + 1); assertWordRight(23, ' /* Just some more'.length + 1); assertWordRight(24, ' /* Just some more '.length + 1); assertWordRight(25, ' /* Just some more '.length + 1); assertWordRight(26, ' /* Just some more '.length + 1); assertWordRight(27, ' /* Just some more text'.length + 1); assertWordRight(28, ' /* Just some more text'.length + 1); assertWordRight(29, ' /* Just some more text'.length + 1); assertWordRight(30, ' /* Just some more text'.length + 1); assertWordRight(31, ' /* Just some more text '.length + 1); assertWordRight(32, ' /* Just some more text a'.length + 1); assertWordRight(33, ' /* Just some more text a+'.length + 1); assertWordRight(34, ' /* Just some more text a+='.length + 1); assertWordRight(35, ' /* Just some more text a+= '.length + 1); assertWordRight(36, ' /* Just some more text a+= 3'.length + 1); assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1); assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1); assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1); assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1); assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1); assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1); assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1); assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1); assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1); assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1); assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1); assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1); assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1); assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1); }); }); }); suite('Editor Controller - Cursor Configuration', () => { test('Cursor honors insertSpaces configuration on new line', () => { usingCursor({ text: [ ' \tMy First Line\t ', '\tMy Second Line', ' Third Line', '', '1' ], mode: null, config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { cursorCommand(cursor, H.MoveTo, { position: new Position(1, 21) }, null, 'keyboard'); cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getLineContent(1), ' \tMy First Line\t '); assert.equal(model.getLineContent(2), ' '); }); }); test('Cursor honors insertSpaces configuration on tab', () => { usingCursor({ text: [ ' \tMy First Line\t ', 'My Second Line123', ' Third Line', '', '1' ], mode: null, config: { insertSpaces: true, tabSize: 13 } }, (model, cursor) => { // Tab on column 1 cursorCommand(cursor, H.MoveTo, { position: new Position(2, 1) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), ' My Second Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 2 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 2) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'M y Second Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 3 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 3) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 4 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 4) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 5 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 5) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My S econd Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 5 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 5) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My S econd Line123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 13 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 13) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My Second Li ne123'); cursorCommand(cursor, H.Undo, null, null, 'keyboard'); // Tab on column 14 assert.equal(model.getLineContent(2), 'My Second Line123'); cursorCommand(cursor, H.MoveTo, { position: new Position(2, 14) }, null, 'keyboard'); cursorCommand(cursor, H.Tab, null, null, 'keyboard'); assert.equal(model.getLineContent(2), 'My Second Lin e123'); }); }); test('Enter auto-indents with insertSpaces setting 1', () => { usingCursor({ text: [ '\thello' ], mode: new OnEnterMode(IndentAction.Indent), config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 7, false); cursorEqual(cursor, 1, 7, 1, 7); cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); }); }); test('Enter auto-indents with insertSpaces setting 2', () => { usingCursor({ text: [ '\thello' ], mode: new OnEnterMode(IndentAction.None), config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 7, false); cursorEqual(cursor, 1, 7, 1, 7); cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); }); }); test('Enter auto-indents with insertSpaces setting 3', () => { usingCursor({ text: [ '\thell()' ], mode: new OnEnterMode(IndentAction.IndentOutdent), config: { insertSpaces: true, tabSize: 4 } }, (model, cursor) => { moveTo(cursor, 1, 7, false); cursorEqual(cursor, 1, 7, 1, 7); cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )'); }); }); test('Insert line before', () => { let testInsertLineBefore = (lineNumber:number, column:number, callback:(model:Model, cursor:Cursor) => void) => { usingCursor({ text: [ 'First line', 'Second line', 'Third line' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, lineNumber, column, false); cursorEqual(cursor, lineNumber, column, lineNumber, column); cursorCommand(cursor, H.LineInsertBefore, null, null, 'keyboard'); callback(model, cursor); }); }; testInsertLineBefore(1, 3, (model, cursor) => { cursorEqual(cursor, 1, 1, 1, 1); assert.equal(model.getLineContent(1), ''); assert.equal(model.getLineContent(2), 'First line'); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); testInsertLineBefore(2, 3, (model, cursor) => { cursorEqual(cursor, 2, 1, 2, 1); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); testInsertLineBefore(3, 3, (model, cursor) => { cursorEqual(cursor, 3, 1, 3, 1); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); assert.equal(model.getLineContent(4), 'Third line'); }); }); test('Insert line after', () => { let testInsertLineAfter = (lineNumber:number, column:number, callback:(model:Model, cursor:Cursor) => void) => { usingCursor({ text: [ 'First line', 'Second line', 'Third line' ], mode: null, config: null }, (model, cursor) => { moveTo(cursor, lineNumber, column, false); cursorEqual(cursor, lineNumber, column, lineNumber, column); cursorCommand(cursor, H.LineInsertAfter, null, null, 'keyboard'); callback(model, cursor); }); }; testInsertLineAfter(1, 3, (model, cursor) => { cursorEqual(cursor, 2, 1, 2, 1); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); testInsertLineAfter(2, 3, (model, cursor) => { cursorEqual(cursor, 3, 1, 3, 1); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); assert.equal(model.getLineContent(4), 'Third line'); }); testInsertLineAfter(3, 3, (model, cursor) => { cursorEqual(cursor, 4, 1, 4, 1); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), 'Third line'); assert.equal(model.getLineContent(4), ''); }); }); }); interface ICursorOpts { text: string[]; mode?: IMode; config?: any; } function usingCursor(opts:ICursorOpts, callback:(model:Model, cursor:Cursor)=>void): void { let model = new Model(opts.text.join('\n'), DefaultEndOfLine.LF, opts.mode); let config = new MockConfiguration(opts.config); let cursor = new Cursor(1, config, model, null, false); callback(model, cursor); cursor.dispose(); config.dispose(); model.dispose(); }