Path: blob/main/src/vs/editor/test/browser/controller/cursor.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { DisposableStore } from '../../../../base/common/lifecycle.js';7import { URI } from '../../../../base/common/uri.js';8import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';9import { CoreEditingCommands, CoreNavigationCommands } from '../../../browser/coreCommands.js';10import { IEditorOptions } from '../../../common/config/editorOptions.js';11import { EditOperation } from '../../../common/core/editOperation.js';12import { Position } from '../../../common/core/position.js';13import { Range } from '../../../common/core/range.js';14import { Selection } from '../../../common/core/selection.js';15import { ICursorPositionChangedEvent } from '../../../common/cursorEvents.js';16import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js';17import { MetadataConsts, StandardTokenType } from '../../../common/encodedTokenAttributes.js';18import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../common/languages.js';19import { ILanguageService } from '../../../common/languages/language.js';20import { IndentAction, IndentationRule } from '../../../common/languages/languageConfiguration.js';21import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';22import { NullState } from '../../../common/languages/nullTokenize.js';23import { EndOfLinePreference, EndOfLineSequence, ITextModel } from '../../../common/model.js';24import { TextModel } from '../../../common/model/textModel.js';25import { ViewModel } from '../../../common/viewModel/viewModelImpl.js';26import { OutgoingViewModelEventKind } from '../../../common/viewModelEventDispatcher.js';27import { ITestCodeEditor, TestCodeEditorInstantiationOptions, createCodeEditorServices, instantiateTestCodeEditor, withTestCodeEditor } from '../testCodeEditor.js';28import { IRelaxedTextModelCreationOptions, createTextModel, instantiateTextModel } from '../../common/testTextModel.js';29import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js';30import { InputMode } from '../../../common/inputMode.js';31import { EditSources } from '../../../common/textModelEditSource.js';3233// --------- utils3435function moveTo(editor: ITestCodeEditor, viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) {36if (inSelectionMode) {37CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, {38position: new Position(lineNumber, column)39});40} else {41CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, {42position: new Position(lineNumber, column)43});44}45}4647function moveLeft(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {48if (inSelectionMode) {49CoreNavigationCommands.CursorLeftSelect.runCoreEditorCommand(viewModel, {});50} else {51CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});52}53}5455function moveRight(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {56if (inSelectionMode) {57CoreNavigationCommands.CursorRightSelect.runCoreEditorCommand(viewModel, {});58} else {59CoreNavigationCommands.CursorRight.runCoreEditorCommand(viewModel, {});60}61}6263function moveDown(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {64if (inSelectionMode) {65CoreNavigationCommands.CursorDownSelect.runCoreEditorCommand(viewModel, {});66} else {67CoreNavigationCommands.CursorDown.runCoreEditorCommand(viewModel, {});68}69}7071function moveUp(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {72if (inSelectionMode) {73CoreNavigationCommands.CursorUpSelect.runCoreEditorCommand(viewModel, {});74} else {75CoreNavigationCommands.CursorUp.runCoreEditorCommand(viewModel, {});76}77}7879function moveToBeginningOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {80if (inSelectionMode) {81CoreNavigationCommands.CursorHomeSelect.runCoreEditorCommand(viewModel, {});82} else {83CoreNavigationCommands.CursorHome.runCoreEditorCommand(viewModel, {});84}85}8687function moveToEndOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {88if (inSelectionMode) {89CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {});90} else {91CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {});92}93}9495function moveToBeginningOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {96if (inSelectionMode) {97CoreNavigationCommands.CursorTopSelect.runCoreEditorCommand(viewModel, {});98} else {99CoreNavigationCommands.CursorTop.runCoreEditorCommand(viewModel, {});100}101}102103function moveToEndOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {104if (inSelectionMode) {105CoreNavigationCommands.CursorBottomSelect.runCoreEditorCommand(viewModel, {});106} else {107CoreNavigationCommands.CursorBottom.runCoreEditorCommand(viewModel, {});108}109}110111function assertCursor(viewModel: ViewModel, what: Position | Selection | Selection[]): void {112let selections: Selection[];113if (what instanceof Position) {114selections = [new Selection(what.lineNumber, what.column, what.lineNumber, what.column)];115} else if (what instanceof Selection) {116selections = [what];117} else {118selections = what;119}120const actual = viewModel.getSelections().map(s => s.toString());121const expected = selections.map(s => s.toString());122123assert.deepStrictEqual(actual, expected);124}125126suite('Editor Controller - Cursor', () => {127const LINE1 = ' \tMy First Line\t ';128const LINE2 = '\tMy Second Line';129const LINE3 = ' Third Line🐶';130const LINE4 = '';131const LINE5 = '1';132133const TEXT =134LINE1 + '\r\n' +135LINE2 + '\n' +136LINE3 + '\n' +137LINE4 + '\r\n' +138LINE5;139140function runTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {141withTestCodeEditor(TEXT, {}, (editor, viewModel) => {142callback(editor, viewModel);143});144}145146ensureNoDisposablesAreLeakedInTestSuite();147148test('cursor initialized', () => {149runTest((editor, viewModel) => {150assertCursor(viewModel, new Position(1, 1));151});152});153154// --------- absolute move155156test('no move', () => {157runTest((editor, viewModel) => {158moveTo(editor, viewModel, 1, 1);159assertCursor(viewModel, new Position(1, 1));160});161});162163test('move', () => {164runTest((editor, viewModel) => {165moveTo(editor, viewModel, 1, 2);166assertCursor(viewModel, new Position(1, 2));167});168});169170test('move in selection mode', () => {171runTest((editor, viewModel) => {172moveTo(editor, viewModel, 1, 2, true);173assertCursor(viewModel, new Selection(1, 1, 1, 2));174});175});176177test('move beyond line end', () => {178runTest((editor, viewModel) => {179moveTo(editor, viewModel, 1, 25);180assertCursor(viewModel, new Position(1, LINE1.length + 1));181});182});183184test('move empty line', () => {185runTest((editor, viewModel) => {186moveTo(editor, viewModel, 4, 20);187assertCursor(viewModel, new Position(4, 1));188});189});190191test('move one char line', () => {192runTest((editor, viewModel) => {193moveTo(editor, viewModel, 5, 20);194assertCursor(viewModel, new Position(5, 2));195});196});197198test('selection down', () => {199runTest((editor, viewModel) => {200moveTo(editor, viewModel, 2, 1, true);201assertCursor(viewModel, new Selection(1, 1, 2, 1));202});203});204205test('move and then select', () => {206runTest((editor, viewModel) => {207moveTo(editor, viewModel, 2, 3);208assertCursor(viewModel, new Position(2, 3));209210moveTo(editor, viewModel, 2, 15, true);211assertCursor(viewModel, new Selection(2, 3, 2, 15));212213moveTo(editor, viewModel, 1, 2, true);214assertCursor(viewModel, new Selection(2, 3, 1, 2));215});216});217218// --------- move left219220test('move left on top left position', () => {221runTest((editor, viewModel) => {222moveLeft(editor, viewModel);223assertCursor(viewModel, new Position(1, 1));224});225});226227test('move left', () => {228runTest((editor, viewModel) => {229moveTo(editor, viewModel, 1, 3);230assertCursor(viewModel, new Position(1, 3));231moveLeft(editor, viewModel);232assertCursor(viewModel, new Position(1, 2));233});234});235236test('move left with surrogate pair', () => {237runTest((editor, viewModel) => {238moveTo(editor, viewModel, 3, 17);239assertCursor(viewModel, new Position(3, 17));240moveLeft(editor, viewModel);241assertCursor(viewModel, new Position(3, 15));242});243});244245test('move left goes to previous row', () => {246runTest((editor, viewModel) => {247moveTo(editor, viewModel, 2, 1);248assertCursor(viewModel, new Position(2, 1));249moveLeft(editor, viewModel);250assertCursor(viewModel, new Position(1, 21));251});252});253254test('move left selection', () => {255runTest((editor, viewModel) => {256moveTo(editor, viewModel, 2, 1);257assertCursor(viewModel, new Position(2, 1));258moveLeft(editor, viewModel, true);259assertCursor(viewModel, new Selection(2, 1, 1, 21));260});261});262263// --------- move right264265test('move right on bottom right position', () => {266runTest((editor, viewModel) => {267moveTo(editor, viewModel, 5, 2);268assertCursor(viewModel, new Position(5, 2));269moveRight(editor, viewModel);270assertCursor(viewModel, new Position(5, 2));271});272});273274test('move right', () => {275runTest((editor, viewModel) => {276moveTo(editor, viewModel, 1, 3);277assertCursor(viewModel, new Position(1, 3));278moveRight(editor, viewModel);279assertCursor(viewModel, new Position(1, 4));280});281});282283test('move right with surrogate pair', () => {284runTest((editor, viewModel) => {285moveTo(editor, viewModel, 3, 15);286assertCursor(viewModel, new Position(3, 15));287moveRight(editor, viewModel);288assertCursor(viewModel, new Position(3, 17));289});290});291292test('move right goes to next row', () => {293runTest((editor, viewModel) => {294moveTo(editor, viewModel, 1, 21);295assertCursor(viewModel, new Position(1, 21));296moveRight(editor, viewModel);297assertCursor(viewModel, new Position(2, 1));298});299});300301test('move right selection', () => {302runTest((editor, viewModel) => {303moveTo(editor, viewModel, 1, 21);304assertCursor(viewModel, new Position(1, 21));305moveRight(editor, viewModel, true);306assertCursor(viewModel, new Selection(1, 21, 2, 1));307});308});309310// --------- move down311312test('move down', () => {313runTest((editor, viewModel) => {314moveDown(editor, viewModel);315assertCursor(viewModel, new Position(2, 1));316moveDown(editor, viewModel);317assertCursor(viewModel, new Position(3, 1));318moveDown(editor, viewModel);319assertCursor(viewModel, new Position(4, 1));320moveDown(editor, viewModel);321assertCursor(viewModel, new Position(5, 1));322moveDown(editor, viewModel);323assertCursor(viewModel, new Position(5, 2));324});325});326327test('move down with selection', () => {328runTest((editor, viewModel) => {329moveDown(editor, viewModel, true);330assertCursor(viewModel, new Selection(1, 1, 2, 1));331moveDown(editor, viewModel, true);332assertCursor(viewModel, new Selection(1, 1, 3, 1));333moveDown(editor, viewModel, true);334assertCursor(viewModel, new Selection(1, 1, 4, 1));335moveDown(editor, viewModel, true);336assertCursor(viewModel, new Selection(1, 1, 5, 1));337moveDown(editor, viewModel, true);338assertCursor(viewModel, new Selection(1, 1, 5, 2));339});340});341342test('move down with tabs', () => {343runTest((editor, viewModel) => {344moveTo(editor, viewModel, 1, 5);345assertCursor(viewModel, new Position(1, 5));346moveDown(editor, viewModel);347assertCursor(viewModel, new Position(2, 2));348moveDown(editor, viewModel);349assertCursor(viewModel, new Position(3, 5));350moveDown(editor, viewModel);351assertCursor(viewModel, new Position(4, 1));352moveDown(editor, viewModel);353assertCursor(viewModel, new Position(5, 2));354});355});356357// --------- move up358359test('move up', () => {360runTest((editor, viewModel) => {361moveTo(editor, viewModel, 3, 5);362assertCursor(viewModel, new Position(3, 5));363364moveUp(editor, viewModel);365assertCursor(viewModel, new Position(2, 2));366367moveUp(editor, viewModel);368assertCursor(viewModel, new Position(1, 5));369});370});371372test('move up with selection', () => {373runTest((editor, viewModel) => {374moveTo(editor, viewModel, 3, 5);375assertCursor(viewModel, new Position(3, 5));376377moveUp(editor, viewModel, true);378assertCursor(viewModel, new Selection(3, 5, 2, 2));379380moveUp(editor, viewModel, true);381assertCursor(viewModel, new Selection(3, 5, 1, 5));382});383});384385test('move up and down with tabs', () => {386runTest((editor, viewModel) => {387moveTo(editor, viewModel, 1, 5);388assertCursor(viewModel, new Position(1, 5));389moveDown(editor, viewModel);390moveDown(editor, viewModel);391moveDown(editor, viewModel);392moveDown(editor, viewModel);393assertCursor(viewModel, new Position(5, 2));394moveUp(editor, viewModel);395assertCursor(viewModel, new Position(4, 1));396moveUp(editor, viewModel);397assertCursor(viewModel, new Position(3, 5));398moveUp(editor, viewModel);399assertCursor(viewModel, new Position(2, 2));400moveUp(editor, viewModel);401assertCursor(viewModel, new Position(1, 5));402});403});404405test('move up and down with end of lines starting from a long one', () => {406runTest((editor, viewModel) => {407moveToEndOfLine(editor, viewModel);408assertCursor(viewModel, new Position(1, LINE1.length + 1));409moveToEndOfLine(editor, viewModel);410assertCursor(viewModel, new Position(1, LINE1.length + 1));411moveDown(editor, viewModel);412assertCursor(viewModel, new Position(2, LINE2.length + 1));413moveDown(editor, viewModel);414assertCursor(viewModel, new Position(3, LINE3.length + 1));415moveDown(editor, viewModel);416assertCursor(viewModel, new Position(4, LINE4.length + 1));417moveDown(editor, viewModel);418assertCursor(viewModel, new Position(5, LINE5.length + 1));419moveUp(editor, viewModel);420moveUp(editor, viewModel);421moveUp(editor, viewModel);422moveUp(editor, viewModel);423assertCursor(viewModel, new Position(1, LINE1.length + 1));424});425});426427test('issue #44465: cursor position not correct when move', () => {428runTest((editor, viewModel) => {429viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);430// going once up on the first line remembers the offset visual columns431moveUp(editor, viewModel);432assertCursor(viewModel, new Position(1, 1));433moveDown(editor, viewModel);434assertCursor(viewModel, new Position(2, 2));435moveUp(editor, viewModel);436assertCursor(viewModel, new Position(1, 5));437438// going twice up on the first line discards the offset visual columns439moveUp(editor, viewModel);440assertCursor(viewModel, new Position(1, 1));441moveUp(editor, viewModel);442assertCursor(viewModel, new Position(1, 1));443moveDown(editor, viewModel);444assertCursor(viewModel, new Position(2, 1));445});446});447448test('issue #144041: Cursor up/down works', () => {449const model = createTextModel(450[451'Word1 Word2 Word3 Word4',452'Word5 Word6 Word7 Word8',453].join('\n')454);455456withTestCodeEditor(model, { wrappingIndent: 'indent', wordWrap: 'wordWrapColumn', wordWrapColumn: 20 }, (editor, viewModel) => {457viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);458459const cursorPositions: any[] = [];460function reportCursorPosition() {461cursorPositions.push(viewModel.getCursorStates()[0].viewState.position.toString());462}463464reportCursorPosition();465editor.runCommand(CoreNavigationCommands.CursorDown, null);466reportCursorPosition();467editor.runCommand(CoreNavigationCommands.CursorDown, null);468reportCursorPosition();469editor.runCommand(CoreNavigationCommands.CursorDown, null);470reportCursorPosition();471editor.runCommand(CoreNavigationCommands.CursorDown, null);472473reportCursorPosition();474editor.runCommand(CoreNavigationCommands.CursorUp, null);475reportCursorPosition();476editor.runCommand(CoreNavigationCommands.CursorUp, null);477reportCursorPosition();478editor.runCommand(CoreNavigationCommands.CursorUp, null);479reportCursorPosition();480editor.runCommand(CoreNavigationCommands.CursorUp, null);481reportCursorPosition();482483assert.deepStrictEqual(cursorPositions, [484'(1,1)',485'(2,5)',486'(3,1)',487'(4,5)',488'(4,10)',489'(3,1)',490'(2,5)',491'(1,1)',492'(1,1)',493]);494});495496model.dispose();497});498499test('issue #140195: Cursor up/down makes progress', () => {500const model = createTextModel(501[502'Word1 Word2 Word3 Word4',503'Word5 Word6 Word7 Word8',504].join('\n')505);506507withTestCodeEditor(model, { wrappingIndent: 'indent', wordWrap: 'wordWrapColumn', wordWrapColumn: 20 }, (editor, viewModel) => {508editor.changeDecorations((changeAccessor) => {509changeAccessor.deltaDecorations([], [510{511range: new Range(1, 22, 1, 22),512options: {513showIfCollapsed: true,514description: 'test',515after: {516content: 'some very very very very very very very very long text',517}518}519}520]);521});522viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);523524const cursorPositions: any[] = [];525function reportCursorPosition() {526cursorPositions.push(viewModel.getCursorStates()[0].viewState.position.toString());527}528529reportCursorPosition();530editor.runCommand(CoreNavigationCommands.CursorDown, null);531reportCursorPosition();532editor.runCommand(CoreNavigationCommands.CursorDown, null);533reportCursorPosition();534editor.runCommand(CoreNavigationCommands.CursorDown, null);535reportCursorPosition();536editor.runCommand(CoreNavigationCommands.CursorDown, null);537538reportCursorPosition();539editor.runCommand(CoreNavigationCommands.CursorUp, null);540reportCursorPosition();541editor.runCommand(CoreNavigationCommands.CursorUp, null);542reportCursorPosition();543editor.runCommand(CoreNavigationCommands.CursorUp, null);544reportCursorPosition();545editor.runCommand(CoreNavigationCommands.CursorUp, null);546reportCursorPosition();547548assert.deepStrictEqual(cursorPositions, [549'(1,1)',550'(2,5)',551'(5,19)',552'(6,1)',553'(7,5)',554'(6,1)',555'(2,8)',556'(1,1)',557'(1,1)',558]);559});560561model.dispose();562});563564// --------- move to beginning of line565566test('move to beginning of line', () => {567runTest((editor, viewModel) => {568moveToBeginningOfLine(editor, viewModel);569assertCursor(viewModel, new Position(1, 6));570moveToBeginningOfLine(editor, viewModel);571assertCursor(viewModel, new Position(1, 1));572});573});574575test('move to beginning of line from within line', () => {576runTest((editor, viewModel) => {577moveTo(editor, viewModel, 1, 8);578moveToBeginningOfLine(editor, viewModel);579assertCursor(viewModel, new Position(1, 6));580moveToBeginningOfLine(editor, viewModel);581assertCursor(viewModel, new Position(1, 1));582});583});584585test('move to beginning of line from whitespace at beginning of line', () => {586runTest((editor, viewModel) => {587moveTo(editor, viewModel, 1, 2);588moveToBeginningOfLine(editor, viewModel);589assertCursor(viewModel, new Position(1, 6));590moveToBeginningOfLine(editor, viewModel);591assertCursor(viewModel, new Position(1, 1));592});593});594595test('move to beginning of line from within line selection', () => {596runTest((editor, viewModel) => {597moveTo(editor, viewModel, 1, 8);598moveToBeginningOfLine(editor, viewModel, true);599assertCursor(viewModel, new Selection(1, 8, 1, 6));600moveToBeginningOfLine(editor, viewModel, true);601assertCursor(viewModel, new Selection(1, 8, 1, 1));602});603});604605test('move to beginning of line with selection multiline forward', () => {606runTest((editor, viewModel) => {607moveTo(editor, viewModel, 1, 8);608moveTo(editor, viewModel, 3, 9, true);609moveToBeginningOfLine(editor, viewModel, false);610assertCursor(viewModel, new Selection(3, 5, 3, 5));611});612});613614test('move to beginning of line with selection multiline backward', () => {615runTest((editor, viewModel) => {616moveTo(editor, viewModel, 3, 9);617moveTo(editor, viewModel, 1, 8, true);618moveToBeginningOfLine(editor, viewModel, false);619assertCursor(viewModel, new Selection(1, 6, 1, 6));620});621});622623test('move to beginning of line with selection single line forward', () => {624runTest((editor, viewModel) => {625moveTo(editor, viewModel, 3, 2);626moveTo(editor, viewModel, 3, 9, true);627moveToBeginningOfLine(editor, viewModel, false);628assertCursor(viewModel, new Selection(3, 5, 3, 5));629});630});631632test('move to beginning of line with selection single line backward', () => {633runTest((editor, viewModel) => {634moveTo(editor, viewModel, 3, 9);635moveTo(editor, viewModel, 3, 2, true);636moveToBeginningOfLine(editor, viewModel, false);637assertCursor(viewModel, new Selection(3, 5, 3, 5));638});639});640641test('issue #15401: "End" key is behaving weird when text is selected part 1', () => {642runTest((editor, viewModel) => {643moveTo(editor, viewModel, 1, 8);644moveTo(editor, viewModel, 3, 9, true);645moveToBeginningOfLine(editor, viewModel, false);646assertCursor(viewModel, new Selection(3, 5, 3, 5));647});648});649650test('issue #17011: Shift+home/end now go to the end of the selection start\'s line, not the selection\'s end', () => {651runTest((editor, viewModel) => {652moveTo(editor, viewModel, 1, 8);653moveTo(editor, viewModel, 3, 9, true);654moveToBeginningOfLine(editor, viewModel, true);655assertCursor(viewModel, new Selection(1, 8, 3, 5));656});657});658659// --------- move to end of line660661test('move to end of line', () => {662runTest((editor, viewModel) => {663moveToEndOfLine(editor, viewModel);664assertCursor(viewModel, new Position(1, LINE1.length + 1));665moveToEndOfLine(editor, viewModel);666assertCursor(viewModel, new Position(1, LINE1.length + 1));667});668});669670test('move to end of line from within line', () => {671runTest((editor, viewModel) => {672moveTo(editor, viewModel, 1, 6);673moveToEndOfLine(editor, viewModel);674assertCursor(viewModel, new Position(1, LINE1.length + 1));675moveToEndOfLine(editor, viewModel);676assertCursor(viewModel, new Position(1, LINE1.length + 1));677});678});679680test('move to end of line from whitespace at end of line', () => {681runTest((editor, viewModel) => {682moveTo(editor, viewModel, 1, 20);683moveToEndOfLine(editor, viewModel);684assertCursor(viewModel, new Position(1, LINE1.length + 1));685moveToEndOfLine(editor, viewModel);686assertCursor(viewModel, new Position(1, LINE1.length + 1));687});688});689690test('move to end of line from within line selection', () => {691runTest((editor, viewModel) => {692moveTo(editor, viewModel, 1, 6);693moveToEndOfLine(editor, viewModel, true);694assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1));695moveToEndOfLine(editor, viewModel, true);696assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1));697});698});699700test('move to end of line with selection multiline forward', () => {701runTest((editor, viewModel) => {702moveTo(editor, viewModel, 1, 1);703moveTo(editor, viewModel, 3, 9, true);704moveToEndOfLine(editor, viewModel, false);705assertCursor(viewModel, new Selection(3, 17, 3, 17));706});707});708709test('move to end of line with selection multiline backward', () => {710runTest((editor, viewModel) => {711moveTo(editor, viewModel, 3, 9);712moveTo(editor, viewModel, 1, 1, true);713moveToEndOfLine(editor, viewModel, false);714assertCursor(viewModel, new Selection(1, 21, 1, 21));715});716});717718test('move to end of line with selection single line forward', () => {719runTest((editor, viewModel) => {720moveTo(editor, viewModel, 3, 1);721moveTo(editor, viewModel, 3, 9, true);722moveToEndOfLine(editor, viewModel, false);723assertCursor(viewModel, new Selection(3, 17, 3, 17));724});725});726727test('move to end of line with selection single line backward', () => {728runTest((editor, viewModel) => {729moveTo(editor, viewModel, 3, 9);730moveTo(editor, viewModel, 3, 1, true);731moveToEndOfLine(editor, viewModel, false);732assertCursor(viewModel, new Selection(3, 17, 3, 17));733});734});735736test('issue #15401: "End" key is behaving weird when text is selected part 2', () => {737runTest((editor, viewModel) => {738moveTo(editor, viewModel, 1, 1);739moveTo(editor, viewModel, 3, 9, true);740moveToEndOfLine(editor, viewModel, false);741assertCursor(viewModel, new Selection(3, 17, 3, 17));742});743});744745// --------- move to beginning of buffer746747test('move to beginning of buffer', () => {748runTest((editor, viewModel) => {749moveToBeginningOfBuffer(editor, viewModel);750assertCursor(viewModel, new Position(1, 1));751});752});753754test('move to beginning of buffer from within first line', () => {755runTest((editor, viewModel) => {756moveTo(editor, viewModel, 1, 3);757moveToBeginningOfBuffer(editor, viewModel);758assertCursor(viewModel, new Position(1, 1));759});760});761762test('move to beginning of buffer from within another line', () => {763runTest((editor, viewModel) => {764moveTo(editor, viewModel, 3, 3);765moveToBeginningOfBuffer(editor, viewModel);766assertCursor(viewModel, new Position(1, 1));767});768});769770test('move to beginning of buffer from within first line selection', () => {771runTest((editor, viewModel) => {772moveTo(editor, viewModel, 1, 3);773moveToBeginningOfBuffer(editor, viewModel, true);774assertCursor(viewModel, new Selection(1, 3, 1, 1));775});776});777778test('move to beginning of buffer from within another line selection', () => {779runTest((editor, viewModel) => {780moveTo(editor, viewModel, 3, 3);781moveToBeginningOfBuffer(editor, viewModel, true);782assertCursor(viewModel, new Selection(3, 3, 1, 1));783});784});785786// --------- move to end of buffer787788test('move to end of buffer', () => {789runTest((editor, viewModel) => {790moveToEndOfBuffer(editor, viewModel);791assertCursor(viewModel, new Position(5, LINE5.length + 1));792});793});794795test('move to end of buffer from within last line', () => {796runTest((editor, viewModel) => {797moveTo(editor, viewModel, 5, 1);798moveToEndOfBuffer(editor, viewModel);799assertCursor(viewModel, new Position(5, LINE5.length + 1));800});801});802803test('move to end of buffer from within another line', () => {804runTest((editor, viewModel) => {805moveTo(editor, viewModel, 3, 3);806moveToEndOfBuffer(editor, viewModel);807assertCursor(viewModel, new Position(5, LINE5.length + 1));808});809});810811test('move to end of buffer from within last line selection', () => {812runTest((editor, viewModel) => {813moveTo(editor, viewModel, 5, 1);814moveToEndOfBuffer(editor, viewModel, true);815assertCursor(viewModel, new Selection(5, 1, 5, LINE5.length + 1));816});817});818819test('move to end of buffer from within another line selection', () => {820runTest((editor, viewModel) => {821moveTo(editor, viewModel, 3, 3);822moveToEndOfBuffer(editor, viewModel, true);823assertCursor(viewModel, new Selection(3, 3, 5, LINE5.length + 1));824});825});826827// --------- misc828829test('select all', () => {830runTest((editor, viewModel) => {831CoreNavigationCommands.SelectAll.runCoreEditorCommand(viewModel, {});832assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1));833});834});835836// --------- eventing837838test('no move doesn\'t trigger event', () => {839840runTest((editor, viewModel) => {841const disposable = viewModel.onEvent((e) => {842assert.ok(false, 'was not expecting event');843});844moveTo(editor, viewModel, 1, 1);845disposable.dispose();846});847});848849test('move eventing', () => {850runTest((editor, viewModel) => {851let events = 0;852const disposable = viewModel.onEvent((e) => {853if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {854events++;855assert.deepStrictEqual(e.selections, [new Selection(1, 2, 1, 2)]);856}857});858moveTo(editor, viewModel, 1, 2);859assert.strictEqual(events, 1, 'receives 1 event');860disposable.dispose();861});862});863864test('move in selection mode eventing', () => {865runTest((editor, viewModel) => {866let events = 0;867const disposable = viewModel.onEvent((e) => {868if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {869events++;870assert.deepStrictEqual(e.selections, [new Selection(1, 1, 1, 2)]);871}872});873moveTo(editor, viewModel, 1, 2, true);874assert.strictEqual(events, 1, 'receives 1 event');875disposable.dispose();876});877});878879// --------- state save & restore880881test('saveState & restoreState', () => {882runTest((editor, viewModel) => {883moveTo(editor, viewModel, 2, 1, true);884assertCursor(viewModel, new Selection(1, 1, 2, 1));885886const savedState = JSON.stringify(viewModel.saveCursorState());887888moveTo(editor, viewModel, 1, 1, false);889assertCursor(viewModel, new Position(1, 1));890891viewModel.restoreCursorState(JSON.parse(savedState));892assertCursor(viewModel, new Selection(1, 1, 2, 1));893});894});895896// --------- updating cursor897898test('Independent model edit 1', () => {899runTest((editor, viewModel) => {900moveTo(editor, viewModel, 2, 16, true);901902editor.getModel().applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]);903assertCursor(viewModel, new Selection(1, 1, 2, 15));904});905});906907test('column select 1', () => {908withTestCodeEditor([909'\tprivate compute(a:number): boolean {',910'\t\tif (a + 3 === 0 || a + 5 === 0) {',911'\t\t\treturn false;',912'\t\t}',913'\t}'914], {}, (editor, viewModel) => {915916moveTo(editor, viewModel, 1, 7, false);917assertCursor(viewModel, new Position(1, 7));918919CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {920position: new Position(4, 4),921viewPosition: new Position(4, 4),922mouseColumn: 15,923doColumnSelect: true924});925926const expectedSelections = [927new Selection(1, 7, 1, 12),928new Selection(2, 4, 2, 9),929new Selection(3, 3, 3, 6),930new Selection(4, 4, 4, 4),931];932933assertCursor(viewModel, expectedSelections);934935});936});937938test('grapheme breaking', () => {939withTestCodeEditor([940'abcabc',941'ãããããã',942'辻󠄀辻󠄀辻󠄀',943'பு',944], {}, (editor, viewModel) => {945946viewModel.setSelections('test', [new Selection(2, 1, 2, 1)]);947moveRight(editor, viewModel);948assertCursor(viewModel, new Position(2, 3));949moveLeft(editor, viewModel);950assertCursor(viewModel, new Position(2, 1));951952viewModel.setSelections('test', [new Selection(3, 1, 3, 1)]);953moveRight(editor, viewModel);954assertCursor(viewModel, new Position(3, 4));955moveLeft(editor, viewModel);956assertCursor(viewModel, new Position(3, 1));957958viewModel.setSelections('test', [new Selection(4, 1, 4, 1)]);959moveRight(editor, viewModel);960assertCursor(viewModel, new Position(4, 3));961moveLeft(editor, viewModel);962assertCursor(viewModel, new Position(4, 1));963964viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);965moveDown(editor, viewModel);966assertCursor(viewModel, new Position(2, 5));967moveDown(editor, viewModel);968assertCursor(viewModel, new Position(3, 4));969moveUp(editor, viewModel);970assertCursor(viewModel, new Position(2, 5));971moveUp(editor, viewModel);972assertCursor(viewModel, new Position(1, 3));973974});975});976977test('issue #4905 - column select is biased to the right', () => {978withTestCodeEditor([979'var gulp = require("gulp");',980'var path = require("path");',981'var rimraf = require("rimraf");',982'var isarray = require("isarray");',983'var merge = require("merge-stream");',984'var concat = require("gulp-concat");',985'var newer = require("gulp-newer");',986].join('\n'), {}, (editor, viewModel) => {987moveTo(editor, viewModel, 1, 4, false);988assertCursor(viewModel, new Position(1, 4));989990CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {991position: new Position(4, 1),992viewPosition: new Position(4, 1),993mouseColumn: 1,994doColumnSelect: true995});996997assertCursor(viewModel, [998new Selection(1, 4, 1, 1),999new Selection(2, 4, 2, 1),1000new Selection(3, 4, 3, 1),1001new Selection(4, 4, 4, 1),1002]);1003});1004});10051006test('issue #20087: column select with mouse', () => {1007withTestCodeEditor([1008'<property id="SomeThing" key="SomeKey" value="000"/>',1009'<property id="SomeThing" key="SomeKey" value="000"/>',1010'<property id="SomeThing" Key="SomeKey" value="000"/>',1011'<property id="SomeThing" key="SomeKey" value="000"/>',1012'<property id="SomeThing" key="SoMEKEy" value="000"/>',1013'<property id="SomeThing" key="SomeKey" value="000"/>',1014'<property id="SomeThing" key="SomeKey" value="000"/>',1015'<property id="SomeThing" key="SomeKey" valuE="000"/>',1016'<property id="SomeThing" key="SomeKey" value="000"/>',1017'<property id="SomeThing" key="SomeKey" value="00X"/>',1018].join('\n'), {}, (editor, viewModel) => {10191020moveTo(editor, viewModel, 10, 10, false);1021assertCursor(viewModel, new Position(10, 10));10221023CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {1024position: new Position(1, 1),1025viewPosition: new Position(1, 1),1026mouseColumn: 1,1027doColumnSelect: true1028});1029assertCursor(viewModel, [1030new Selection(10, 10, 10, 1),1031new Selection(9, 10, 9, 1),1032new Selection(8, 10, 8, 1),1033new Selection(7, 10, 7, 1),1034new Selection(6, 10, 6, 1),1035new Selection(5, 10, 5, 1),1036new Selection(4, 10, 4, 1),1037new Selection(3, 10, 3, 1),1038new Selection(2, 10, 2, 1),1039new Selection(1, 10, 1, 1),1040]);10411042CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {1043position: new Position(1, 1),1044viewPosition: new Position(1, 1),1045mouseColumn: 1,1046doColumnSelect: true1047});1048assertCursor(viewModel, [1049new Selection(10, 10, 10, 1),1050new Selection(9, 10, 9, 1),1051new Selection(8, 10, 8, 1),1052new Selection(7, 10, 7, 1),1053new Selection(6, 10, 6, 1),1054new Selection(5, 10, 5, 1),1055new Selection(4, 10, 4, 1),1056new Selection(3, 10, 3, 1),1057new Selection(2, 10, 2, 1),1058new Selection(1, 10, 1, 1),1059]);10601061});1062});10631064test('issue #20087: column select with keyboard', () => {1065withTestCodeEditor([1066'<property id="SomeThing" key="SomeKey" value="000"/>',1067'<property id="SomeThing" key="SomeKey" value="000"/>',1068'<property id="SomeThing" Key="SomeKey" value="000"/>',1069'<property id="SomeThing" key="SomeKey" value="000"/>',1070'<property id="SomeThing" key="SoMEKEy" value="000"/>',1071'<property id="SomeThing" key="SomeKey" value="000"/>',1072'<property id="SomeThing" key="SomeKey" value="000"/>',1073'<property id="SomeThing" key="SomeKey" valuE="000"/>',1074'<property id="SomeThing" key="SomeKey" value="000"/>',1075'<property id="SomeThing" key="SomeKey" value="00X"/>',1076].join('\n'), {}, (editor, viewModel) => {10771078moveTo(editor, viewModel, 10, 10, false);1079assertCursor(viewModel, new Position(10, 10));10801081CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});1082assertCursor(viewModel, [1083new Selection(10, 10, 10, 9)1084]);10851086CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});1087assertCursor(viewModel, [1088new Selection(10, 10, 10, 8)1089]);10901091CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1092assertCursor(viewModel, [1093new Selection(10, 10, 10, 9)1094]);10951096CoreNavigationCommands.CursorColumnSelectUp.runCoreEditorCommand(viewModel, {});1097assertCursor(viewModel, [1098new Selection(10, 10, 10, 9),1099new Selection(9, 10, 9, 9),1100]);11011102CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1103assertCursor(viewModel, [1104new Selection(10, 10, 10, 9)1105]);1106});1107});11081109test('issue #118062: Column selection cannot select first position of a line', () => {1110withTestCodeEditor([1111'hello world',1112].join('\n'), {}, (editor, viewModel) => {11131114moveTo(editor, viewModel, 1, 2, false);1115assertCursor(viewModel, new Position(1, 2));11161117CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});1118assertCursor(viewModel, [1119new Selection(1, 2, 1, 1)1120]);1121});1122});11231124test('column select with keyboard', () => {1125withTestCodeEditor([1126'var gulp = require("gulp");',1127'var path = require("path");',1128'var rimraf = require("rimraf");',1129'var isarray = require("isarray");',1130'var merge = require("merge-stream");',1131'var concat = require("gulp-concat");',1132'var newer = require("gulp-newer");',1133].join('\n'), {}, (editor, viewModel) => {11341135moveTo(editor, viewModel, 1, 4, false);1136assertCursor(viewModel, new Position(1, 4));11371138CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1139assertCursor(viewModel, [1140new Selection(1, 4, 1, 5)1141]);11421143CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1144assertCursor(viewModel, [1145new Selection(1, 4, 1, 5),1146new Selection(2, 4, 2, 5)1147]);11481149CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1150assertCursor(viewModel, [1151new Selection(1, 4, 1, 5),1152new Selection(2, 4, 2, 5),1153new Selection(3, 4, 3, 5),1154]);11551156CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1157CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1158CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1159CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});1160assertCursor(viewModel, [1161new Selection(1, 4, 1, 5),1162new Selection(2, 4, 2, 5),1163new Selection(3, 4, 3, 5),1164new Selection(4, 4, 4, 5),1165new Selection(5, 4, 5, 5),1166new Selection(6, 4, 6, 5),1167new Selection(7, 4, 7, 5),1168]);11691170CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1171assertCursor(viewModel, [1172new Selection(1, 4, 1, 6),1173new Selection(2, 4, 2, 6),1174new Selection(3, 4, 3, 6),1175new Selection(4, 4, 4, 6),1176new Selection(5, 4, 5, 6),1177new Selection(6, 4, 6, 6),1178new Selection(7, 4, 7, 6),1179]);11801181// 10 times1182CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1183CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1184CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1185CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1186CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1187CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1188CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1189CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1190CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1191CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1192assertCursor(viewModel, [1193new Selection(1, 4, 1, 16),1194new Selection(2, 4, 2, 16),1195new Selection(3, 4, 3, 16),1196new Selection(4, 4, 4, 16),1197new Selection(5, 4, 5, 16),1198new Selection(6, 4, 6, 16),1199new Selection(7, 4, 7, 16),1200]);12011202// 10 times1203CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1204CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1205CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1206CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1207CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1208CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1209CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1210CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1211CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1212CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1213assertCursor(viewModel, [1214new Selection(1, 4, 1, 26),1215new Selection(2, 4, 2, 26),1216new Selection(3, 4, 3, 26),1217new Selection(4, 4, 4, 26),1218new Selection(5, 4, 5, 26),1219new Selection(6, 4, 6, 26),1220new Selection(7, 4, 7, 26),1221]);12221223// 2 times => reaching the ending of lines 1 and 21224CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1225CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1226assertCursor(viewModel, [1227new Selection(1, 4, 1, 28),1228new Selection(2, 4, 2, 28),1229new Selection(3, 4, 3, 28),1230new Selection(4, 4, 4, 28),1231new Selection(5, 4, 5, 28),1232new Selection(6, 4, 6, 28),1233new Selection(7, 4, 7, 28),1234]);12351236// 4 times => reaching the ending of line 31237CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1238CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1239CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1240CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1241assertCursor(viewModel, [1242new Selection(1, 4, 1, 28),1243new Selection(2, 4, 2, 28),1244new Selection(3, 4, 3, 32),1245new Selection(4, 4, 4, 32),1246new Selection(5, 4, 5, 32),1247new Selection(6, 4, 6, 32),1248new Selection(7, 4, 7, 32),1249]);12501251// 2 times => reaching the ending of line 41252CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1253CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1254assertCursor(viewModel, [1255new Selection(1, 4, 1, 28),1256new Selection(2, 4, 2, 28),1257new Selection(3, 4, 3, 32),1258new Selection(4, 4, 4, 34),1259new Selection(5, 4, 5, 34),1260new Selection(6, 4, 6, 34),1261new Selection(7, 4, 7, 34),1262]);12631264// 1 time => reaching the ending of line 71265CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1266assertCursor(viewModel, [1267new Selection(1, 4, 1, 28),1268new Selection(2, 4, 2, 28),1269new Selection(3, 4, 3, 32),1270new Selection(4, 4, 4, 34),1271new Selection(5, 4, 5, 35),1272new Selection(6, 4, 6, 35),1273new Selection(7, 4, 7, 35),1274]);12751276// 3 times => reaching the ending of lines 5 & 61277CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1278CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1279CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1280assertCursor(viewModel, [1281new Selection(1, 4, 1, 28),1282new Selection(2, 4, 2, 28),1283new Selection(3, 4, 3, 32),1284new Selection(4, 4, 4, 34),1285new Selection(5, 4, 5, 37),1286new Selection(6, 4, 6, 37),1287new Selection(7, 4, 7, 35),1288]);12891290// cannot go anywhere anymore1291CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1292assertCursor(viewModel, [1293new Selection(1, 4, 1, 28),1294new Selection(2, 4, 2, 28),1295new Selection(3, 4, 3, 32),1296new Selection(4, 4, 4, 34),1297new Selection(5, 4, 5, 37),1298new Selection(6, 4, 6, 37),1299new Selection(7, 4, 7, 35),1300]);13011302// cannot go anywhere anymore even if we insist1303CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1304CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1305CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1306CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});1307assertCursor(viewModel, [1308new Selection(1, 4, 1, 28),1309new Selection(2, 4, 2, 28),1310new Selection(3, 4, 3, 32),1311new Selection(4, 4, 4, 34),1312new Selection(5, 4, 5, 37),1313new Selection(6, 4, 6, 37),1314new Selection(7, 4, 7, 35),1315]);13161317// can easily go back1318CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});1319assertCursor(viewModel, [1320new Selection(1, 4, 1, 28),1321new Selection(2, 4, 2, 28),1322new Selection(3, 4, 3, 32),1323new Selection(4, 4, 4, 34),1324new Selection(5, 4, 5, 36),1325new Selection(6, 4, 6, 36),1326new Selection(7, 4, 7, 35),1327]);1328});1329});13301331test('setSelection / setPosition with source', () => {13321333const tokenizationSupport: ITokenizationSupport = {1334getInitialState: () => NullState,1335tokenize: undefined!,1336tokenizeEncoded: (line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult => {1337return new EncodedTokenizationResult(new Uint32Array(0), state);1338}1339};13401341const LANGUAGE_ID = 'modelModeTest1';1342const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);1343const model = createTextModel('Just text', LANGUAGE_ID);13441345withTestCodeEditor(model, {}, (editor1, cursor1) => {1346let event: ICursorPositionChangedEvent | undefined = undefined;1347const disposable = editor1.onDidChangeCursorPosition(e => {1348event = e;1349});13501351editor1.setSelection(new Range(1, 2, 1, 3), 'navigation');1352assert.strictEqual(event!.source, 'navigation');13531354event = undefined;1355editor1.setPosition(new Position(1, 2), 'navigation');1356assert.strictEqual(event!.source, 'navigation');1357disposable.dispose();1358});13591360languageRegistration.dispose();1361model.dispose();1362});1363});13641365suite('Editor Controller', () => {13661367const surroundingLanguageId = 'surroundingLanguage';1368const indentRulesLanguageId = 'indentRulesLanguage';1369const electricCharLanguageId = 'electricCharLanguage';1370const autoClosingLanguageId = 'autoClosingLanguage';1371const emptyClosingSurroundLanguageId = 'emptyClosingSurroundLanguage';13721373let disposables: DisposableStore;1374let instantiationService: TestInstantiationService;1375let languageConfigurationService: ILanguageConfigurationService;1376let languageService: ILanguageService;13771378setup(() => {1379disposables = new DisposableStore();1380instantiationService = createCodeEditorServices(disposables);1381languageConfigurationService = instantiationService.get(ILanguageConfigurationService);1382languageService = instantiationService.get(ILanguageService);13831384disposables.add(languageService.registerLanguage({ id: surroundingLanguageId }));1385disposables.add(languageConfigurationService.register(surroundingLanguageId, {1386autoClosingPairs: [{ open: '(', close: ')' }]1387}));13881389disposables.add(languageService.registerLanguage({ id: emptyClosingSurroundLanguageId }));1390disposables.add(languageConfigurationService.register(emptyClosingSurroundLanguageId, {1391surroundingPairs: [{ open: '<', close: '' }]1392}));13931394setupIndentRulesLanguage(indentRulesLanguageId, {1395decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,1396increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,1397indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/,1398unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/1399});14001401disposables.add(languageService.registerLanguage({ id: electricCharLanguageId }));1402disposables.add(languageConfigurationService.register(electricCharLanguageId, {1403__electricCharacterSupport: {1404docComment: { open: '/**', close: ' */' }1405},1406brackets: [1407['{', '}'],1408['[', ']'],1409['(', ')']1410]1411}));14121413setupAutoClosingLanguage();1414});14151416teardown(() => {1417disposables.dispose();1418});14191420ensureNoDisposablesAreLeakedInTestSuite();14211422function setupOnEnterLanguage(indentAction: IndentAction): string {1423const onEnterLanguageId = 'onEnterMode';14241425disposables.add(languageService.registerLanguage({ id: onEnterLanguageId }));1426disposables.add(languageConfigurationService.register(onEnterLanguageId, {1427onEnterRules: [{1428beforeText: /.*/,1429action: {1430indentAction: indentAction1431}1432}]1433}));1434return onEnterLanguageId;1435}14361437function setupIndentRulesLanguage(languageId: string, indentationRules: IndentationRule): string {1438disposables.add(languageService.registerLanguage({ id: languageId }));1439disposables.add(languageConfigurationService.register(languageId, {1440indentationRules: indentationRules1441}));1442return languageId;1443}14441445function setupAutoClosingLanguage() {1446disposables.add(languageService.registerLanguage({ id: autoClosingLanguageId }));1447disposables.add(languageConfigurationService.register(autoClosingLanguageId, {1448comments: {1449blockComment: ['/*', '*/']1450},1451autoClosingPairs: [1452{ open: '{', close: '}' },1453{ open: '[', close: ']' },1454{ open: '(', close: ')' },1455{ open: '\'', close: '\'', notIn: ['string', 'comment'] },1456{ open: '\"', close: '\"', notIn: ['string'] },1457{ open: '`', close: '`', notIn: ['string', 'comment'] },1458{ open: '/**', close: ' */', notIn: ['string'] },1459{ open: 'begin', close: 'end', notIn: ['string'] }1460],1461__electricCharacterSupport: {1462docComment: { open: '/**', close: ' */' }1463}1464}));1465}14661467function setupAutoClosingLanguageTokenization() {1468class BaseState implements IState {1469constructor(1470public readonly parent: State | null = null1471) { }1472clone(): IState { return this; }1473equals(other: IState): boolean {1474if (!(other instanceof BaseState)) {1475return false;1476}1477if (!this.parent && !other.parent) {1478return true;1479}1480if (!this.parent || !other.parent) {1481return false;1482}1483return this.parent.equals(other.parent);1484}1485}1486class StringState implements IState {1487constructor(1488public readonly char: string,1489public readonly parentState: State1490) { }1491clone(): IState { return this; }1492equals(other: IState): boolean { return other instanceof StringState && this.char === other.char && this.parentState.equals(other.parentState); }1493}1494class BlockCommentState implements IState {1495constructor(1496public readonly parentState: State1497) { }1498clone(): IState { return this; }1499equals(other: IState): boolean { return other instanceof StringState && this.parentState.equals(other.parentState); }1500}1501type State = BaseState | StringState | BlockCommentState;15021503const encodedLanguageId = languageService.languageIdCodec.encodeLanguageId(autoClosingLanguageId);1504disposables.add(TokenizationRegistry.register(autoClosingLanguageId, {1505getInitialState: () => new BaseState(),1506tokenize: undefined!,1507tokenizeEncoded: function (line: string, hasEOL: boolean, _state: IState): EncodedTokenizationResult {1508let state = <State>_state;1509const tokens: { length: number; type: StandardTokenType }[] = [];1510const generateToken = (length: number, type: StandardTokenType, newState?: State) => {1511if (tokens.length > 0 && tokens[tokens.length - 1].type === type) {1512// grow last tokens1513tokens[tokens.length - 1].length += length;1514} else {1515tokens.push({ length, type });1516}1517line = line.substring(length);1518if (newState) {1519state = newState;1520}1521};1522while (line.length > 0) {1523advance();1524}1525const result = new Uint32Array(tokens.length * 2);1526let startIndex = 0;1527for (let i = 0; i < tokens.length; i++) {1528result[2 * i] = startIndex;1529result[2 * i + 1] = (1530(encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET)1531| (tokens[i].type << MetadataConsts.TOKEN_TYPE_OFFSET)1532);1533startIndex += tokens[i].length;1534}1535return new EncodedTokenizationResult(result, state);15361537function advance(): void {1538if (state instanceof BaseState) {1539const m1 = line.match(/^[^'"`{}/]+/g);1540if (m1) {1541return generateToken(m1[0].length, StandardTokenType.Other);1542}1543if (/^['"`]/.test(line)) {1544return generateToken(1, StandardTokenType.String, new StringState(line.charAt(0), state));1545}1546if (/^{/.test(line)) {1547return generateToken(1, StandardTokenType.Other, new BaseState(state));1548}1549if (/^}/.test(line)) {1550return generateToken(1, StandardTokenType.Other, state.parent || new BaseState());1551}1552if (/^\/\//.test(line)) {1553return generateToken(line.length, StandardTokenType.Comment, state);1554}1555if (/^\/\*/.test(line)) {1556return generateToken(2, StandardTokenType.Comment, new BlockCommentState(state));1557}1558return generateToken(1, StandardTokenType.Other, state);1559} else if (state instanceof StringState) {1560const m1 = line.match(/^[^\\'"`\$]+/g);1561if (m1) {1562return generateToken(m1[0].length, StandardTokenType.String);1563}1564if (/^\\/.test(line)) {1565return generateToken(2, StandardTokenType.String);1566}1567if (line.charAt(0) === state.char) {1568return generateToken(1, StandardTokenType.String, state.parentState);1569}1570if (/^\$\{/.test(line)) {1571return generateToken(2, StandardTokenType.Other, new BaseState(state));1572}1573return generateToken(1, StandardTokenType.Other, state);1574} else if (state instanceof BlockCommentState) {1575const m1 = line.match(/^[^*]+/g);1576if (m1) {1577return generateToken(m1[0].length, StandardTokenType.String);1578}1579if (/^\*\//.test(line)) {1580return generateToken(2, StandardTokenType.Comment, state.parentState);1581}1582return generateToken(1, StandardTokenType.Other, state);1583} else {1584throw new Error(`unknown state`);1585}1586}1587}1588}));1589}15901591function setAutoClosingLanguageEnabledSet(chars: string): void {1592disposables.add(languageConfigurationService.register(autoClosingLanguageId, {1593autoCloseBefore: chars,1594autoClosingPairs: [1595{ open: '{', close: '}' },1596{ open: '[', close: ']' },1597{ open: '(', close: ')' },1598{ open: '\'', close: '\'', notIn: ['string', 'comment'] },1599{ open: '\"', close: '\"', notIn: ['string'] },1600{ open: '`', close: '`', notIn: ['string', 'comment'] },1601{ open: '/**', close: ' */', notIn: ['string'] }1602],1603}));1604}16051606function createTextModel(text: string, languageId: string | null = null, options: IRelaxedTextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, uri: URI | null = null): TextModel {1607return disposables.add(instantiateTextModel(instantiationService, text, languageId, options, uri));1608}16091610function withTestCodeEditor(text: ITextModel | string | string[], options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {1611let model: ITextModel;1612if (typeof text === 'string') {1613model = createTextModel(text);1614} else if (Array.isArray(text)) {1615model = createTextModel(text.join('\n'));1616} else {1617model = text;1618}1619const editor = disposables.add(instantiateTestCodeEditor(instantiationService, model, options));1620const viewModel = editor.getViewModel()!;1621viewModel.setHasFocus(true);1622callback(editor, viewModel);1623}16241625interface ICursorOpts {1626text: string[];1627languageId?: string | null;1628modelOpts?: IRelaxedTextModelCreationOptions;1629editorOpts?: IEditorOptions;1630}16311632function usingCursor(opts: ICursorOpts, callback: (editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel) => void): void {1633const model = createTextModel(opts.text.join('\n'), opts.languageId, opts.modelOpts);1634const editorOptions: TestCodeEditorInstantiationOptions = opts.editorOpts || {};1635withTestCodeEditor(model, editorOptions, (editor, viewModel) => {1636callback(editor, model, viewModel);1637});1638}16391640const enum AutoClosingColumnType {1641Normal = 0,1642Special1 = 1,1643Special2 = 21644}16451646function extractAutoClosingSpecialColumns(maxColumn: number, annotatedLine: string): AutoClosingColumnType[] {1647const result: AutoClosingColumnType[] = [];1648for (let j = 1; j <= maxColumn; j++) {1649result[j] = AutoClosingColumnType.Normal;1650}1651let column = 1;1652for (let j = 0; j < annotatedLine.length; j++) {1653if (annotatedLine.charAt(j) === '|') {1654result[column] = AutoClosingColumnType.Special1;1655} else if (annotatedLine.charAt(j) === '!') {1656result[column] = AutoClosingColumnType.Special2;1657} else {1658column++;1659}1660}1661return result;1662}16631664function assertType(editor: ITestCodeEditor, model: ITextModel, viewModel: ViewModel, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void {1665const lineContent = model.getLineContent(lineNumber);1666const expected = lineContent.substr(0, column - 1) + expectedInsert + lineContent.substr(column - 1);1667moveTo(editor, viewModel, lineNumber, column);1668viewModel.type(chr, 'keyboard');1669assert.deepStrictEqual(model.getLineContent(lineNumber), expected, message);1670model.undo();1671}16721673test('issue microsoft/monaco-editor#443: Indentation of a single row deletes selected text in some cases', () => {1674const model = createTextModel(1675[1676'Hello world!',1677'another line'1678].join('\n'),1679undefined,1680{1681insertSpaces: false1682},1683);1684withTestCodeEditor(model, {}, (editor, viewModel) => {1685viewModel.setSelections('test', [new Selection(1, 1, 1, 13)]);16861687// Check that indenting maintains the selection start at column 11688editor.runCommand(CoreEditingCommands.Tab, null);1689assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 14));1690});1691});16921693test('Bug 9121: Auto indent + undo + redo is funky', () => {1694const model = createTextModel(1695[1696''1697].join('\n'),1698undefined,1699{1700insertSpaces: false,1701trimAutoWhitespace: false1702},1703);17041705withTestCodeEditor(model, {}, (editor, viewModel) => {1706viewModel.type('\n', 'keyboard');1707assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');17081709editor.runCommand(CoreEditingCommands.Tab, null);1710assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');17111712viewModel.type('\n', 'keyboard');1713assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\t', 'assert3');17141715viewModel.type('x');1716assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert4');17171718CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});1719assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert5');17201721editor.runCommand(CoreEditingCommands.DeleteLeft, null);1722assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert6');17231724editor.runCommand(CoreEditingCommands.DeleteLeft, null);1725assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert7');17261727editor.runCommand(CoreEditingCommands.DeleteLeft, null);1728assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert8');17291730editor.runCommand(CoreEditingCommands.DeleteLeft, null);1731assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert9');17321733editor.runCommand(CoreEditingCommands.Undo, null);1734assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert10');17351736editor.runCommand(CoreEditingCommands.Undo, null);1737assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert11');17381739editor.runCommand(CoreEditingCommands.Undo, null);1740assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert12');17411742editor.runCommand(CoreEditingCommands.Redo, null);1743assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert13');17441745editor.runCommand(CoreEditingCommands.Redo, null);1746assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert14');17471748editor.runCommand(CoreEditingCommands.Redo, null);1749assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert15');1750});1751});17521753test('issue #23539: Setting model EOL isn\'t undoable', () => {1754withTestCodeEditor([1755'Hello',1756'world'1757], {}, (editor, viewModel) => {1758const model = editor.getModel()!;17591760assertCursor(viewModel, new Position(1, 1));1761model.setEOL(EndOfLineSequence.LF);1762assert.strictEqual(model.getValue(), 'Hello\nworld');17631764model.pushEOL(EndOfLineSequence.CRLF);1765assert.strictEqual(model.getValue(), 'Hello\r\nworld');17661767editor.runCommand(CoreEditingCommands.Undo, null);1768assert.strictEqual(model.getValue(), 'Hello\nworld');1769});1770});17711772test('issue #47733: Undo mangles unicode characters', () => {1773const languageId = 'myMode';17741775disposables.add(languageService.registerLanguage({ id: languageId }));1776disposables.add(languageConfigurationService.register(languageId, {1777surroundingPairs: [{ open: '%', close: '%' }]1778}));17791780const model = createTextModel('\'👁\'', languageId);17811782withTestCodeEditor(model, {}, (editor, viewModel) => {1783editor.setSelection(new Selection(1, 1, 1, 2));17841785viewModel.type('%', 'keyboard');1786assert.strictEqual(model.getValue(EndOfLinePreference.LF), '%\'%👁\'', 'assert1');17871788editor.runCommand(CoreEditingCommands.Undo, null);1789assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\'👁\'', 'assert2');1790});1791});17921793test('issue #46208: Allow empty selections in the undo/redo stack', () => {1794const model = createTextModel('');17951796withTestCodeEditor(model, {}, (editor, viewModel) => {1797viewModel.type('Hello', 'keyboard');1798viewModel.type(' ', 'keyboard');1799viewModel.type('world', 'keyboard');1800viewModel.type(' ', 'keyboard');1801assert.strictEqual(model.getLineContent(1), 'Hello world ');1802assertCursor(viewModel, new Position(1, 13));18031804moveLeft(editor, viewModel);1805moveRight(editor, viewModel);18061807model.pushEditOperations([], [EditOperation.replaceMove(new Range(1, 12, 1, 13), '')], () => []);1808assert.strictEqual(model.getLineContent(1), 'Hello world');1809assertCursor(viewModel, new Position(1, 12));18101811editor.runCommand(CoreEditingCommands.Undo, null);1812assert.strictEqual(model.getLineContent(1), 'Hello world ');1813assertCursor(viewModel, new Selection(1, 13, 1, 13));18141815editor.runCommand(CoreEditingCommands.Undo, null);1816assert.strictEqual(model.getLineContent(1), 'Hello world');1817assertCursor(viewModel, new Position(1, 12));18181819editor.runCommand(CoreEditingCommands.Undo, null);1820assert.strictEqual(model.getLineContent(1), 'Hello');1821assertCursor(viewModel, new Position(1, 6));18221823editor.runCommand(CoreEditingCommands.Undo, null);1824assert.strictEqual(model.getLineContent(1), '');1825assertCursor(viewModel, new Position(1, 1));18261827editor.runCommand(CoreEditingCommands.Redo, null);1828assert.strictEqual(model.getLineContent(1), 'Hello');1829assertCursor(viewModel, new Position(1, 6));18301831editor.runCommand(CoreEditingCommands.Redo, null);1832assert.strictEqual(model.getLineContent(1), 'Hello world');1833assertCursor(viewModel, new Position(1, 12));18341835editor.runCommand(CoreEditingCommands.Redo, null);1836assert.strictEqual(model.getLineContent(1), 'Hello world ');1837assertCursor(viewModel, new Position(1, 13));18381839editor.runCommand(CoreEditingCommands.Redo, null);1840assert.strictEqual(model.getLineContent(1), 'Hello world');1841assertCursor(viewModel, new Position(1, 12));18421843editor.runCommand(CoreEditingCommands.Redo, null);1844assert.strictEqual(model.getLineContent(1), 'Hello world');1845assertCursor(viewModel, new Position(1, 12));1846});1847});18481849test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => {1850const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);1851const model = createTextModel(1852[1853' function baz() {'1854].join('\n'),1855languageId1856);18571858withTestCodeEditor(model, {}, (editor, viewModel) => {1859moveTo(editor, viewModel, 1, 6, false);1860assertCursor(viewModel, new Selection(1, 6, 1, 6));18611862editor.runCommand(CoreEditingCommands.Outdent, null);1863assert.strictEqual(model.getLineContent(1), ' function baz() {');1864assertCursor(viewModel, new Selection(1, 5, 1, 5));1865});1866});18671868test('Bug #18293:[regression][editor] Can\'t outdent whitespace line', () => {1869const model = createTextModel(1870[1871' '1872].join('\n')1873);18741875withTestCodeEditor(model, {}, (editor, viewModel) => {1876moveTo(editor, viewModel, 1, 7, false);1877assertCursor(viewModel, new Selection(1, 7, 1, 7));18781879editor.runCommand(CoreEditingCommands.Outdent, null);1880assert.strictEqual(model.getLineContent(1), ' ');1881assertCursor(viewModel, new Selection(1, 5, 1, 5));1882});1883});18841885test('issue #95591: Unindenting moves cursor to beginning of line', () => {1886const model = createTextModel(1887[1888' '1889].join('\n')1890);18911892withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {1893moveTo(editor, viewModel, 1, 9, false);1894assertCursor(viewModel, new Selection(1, 9, 1, 9));18951896editor.runCommand(CoreEditingCommands.Outdent, null);1897assert.strictEqual(model.getLineContent(1), ' ');1898assertCursor(viewModel, new Selection(1, 5, 1, 5));1899});1900});19011902test('Bug #16657: [editor] Tab on empty line of zero indentation moves cursor to position (1,1)', () => {1903const model = createTextModel(1904[1905'function baz() {',1906'\tfunction hello() { // something here',1907'\t',1908'',1909'\t}',1910'}',1911''1912].join('\n'),1913undefined,1914{1915insertSpaces: false,1916},1917);19181919withTestCodeEditor(model, {}, (editor, viewModel) => {1920moveTo(editor, viewModel, 7, 1, false);1921assertCursor(viewModel, new Selection(7, 1, 7, 1));19221923editor.runCommand(CoreEditingCommands.Tab, null);1924assert.strictEqual(model.getLineContent(7), '\t');1925assertCursor(viewModel, new Selection(7, 2, 7, 2));1926});1927});19281929test('bug #16740: [editor] Cut line doesn\'t quite cut the last line', () => {19301931// Part 1 => there is text on the last line1932withTestCodeEditor([1933'asdasd',1934'qwerty'1935], {}, (editor, viewModel) => {1936const model = editor.getModel()!;19371938moveTo(editor, viewModel, 2, 1, false);1939assertCursor(viewModel, new Selection(2, 1, 2, 1));19401941viewModel.cut('keyboard');1942assert.strictEqual(model.getLineCount(), 1);1943assert.strictEqual(model.getLineContent(1), 'asdasd');19441945});19461947// Part 2 => there is no text on the last line1948withTestCodeEditor([1949'asdasd',1950''1951], {}, (editor, viewModel) => {1952const model = editor.getModel()!;19531954moveTo(editor, viewModel, 2, 1, false);1955assertCursor(viewModel, new Selection(2, 1, 2, 1));19561957viewModel.cut('keyboard');1958assert.strictEqual(model.getLineCount(), 1);1959assert.strictEqual(model.getLineContent(1), 'asdasd');19601961viewModel.cut('keyboard');1962assert.strictEqual(model.getLineCount(), 1);1963assert.strictEqual(model.getLineContent(1), '');1964});1965});19661967test('issue #128602: When cutting multiple lines (ctrl x), the last line will not be erased', () => {1968withTestCodeEditor([1969'a1',1970'a2',1971'a3'1972], {}, (editor, viewModel) => {1973const model = editor.getModel()!;19741975viewModel.setSelections('test', [1976new Selection(1, 1, 1, 1),1977new Selection(2, 1, 2, 1),1978new Selection(3, 1, 3, 1),1979]);19801981viewModel.cut('keyboard');1982assert.strictEqual(model.getLineCount(), 1);1983assert.strictEqual(model.getLineContent(1), '');1984});1985});19861987test('Bug #11476: Double bracket surrounding + undo is broken', () => {1988usingCursor({1989text: [1990'hello'1991],1992languageId: surroundingLanguageId1993}, (editor, model, viewModel) => {1994moveTo(editor, viewModel, 1, 3, false);1995moveTo(editor, viewModel, 1, 5, true);1996assertCursor(viewModel, new Selection(1, 3, 1, 5));19971998viewModel.type('(', 'keyboard');1999assertCursor(viewModel, new Selection(1, 4, 1, 6));20002001viewModel.type('(', 'keyboard');2002assertCursor(viewModel, new Selection(1, 5, 1, 7));2003});2004});20052006test('issue #206774: SurroundSelectionCommand with empty charAfterSelection should not throw', () => {2007// This test reproduces the issue where SurroundSelectionCommand throws when charAfterSelection is empty2008// The problem is that addTrackedEditOperation ignores empty strings, causing computeCursorState to fail2009// when trying to access inverseEditOperations[1].range (which is undefined)20102011usingCursor({2012text: [2013'hello world'2014],2015languageId: emptyClosingSurroundLanguageId2016}, (editor, model, viewModel) => {2017// Select "hello"2018moveTo(editor, viewModel, 1, 1, false);2019moveTo(editor, viewModel, 1, 6, true);2020assertCursor(viewModel, new Selection(1, 1, 1, 6));20212022// Type < which should surround with '<' and empty string2023// This reproduces the crash where charAfterSelection is empty2024viewModel.type('<', 'keyboard');20252026// Test passes if we don't crash - the exact cursor position depends on the fix2027// The main issue is that computeCursorState fails when charAfterSelection is empty2028assert.strictEqual(model.getValue(), '<hello world');2029});2030});20312032test('issue #1140: Backspace stops prematurely', () => {2033const model = createTextModel(2034[2035'function baz() {',2036' return 1;',2037'};'2038].join('\n')2039);20402041withTestCodeEditor(model, {}, (editor, viewModel) => {2042moveTo(editor, viewModel, 3, 2, false);2043moveTo(editor, viewModel, 1, 14, true);2044assertCursor(viewModel, new Selection(3, 2, 1, 14));20452046editor.runCommand(CoreEditingCommands.DeleteLeft, null);2047assertCursor(viewModel, new Selection(1, 14, 1, 14));2048assert.strictEqual(model.getLineCount(), 1);2049assert.strictEqual(model.getLineContent(1), 'function baz(;');2050});2051});20522053test('issue #10212: Pasting entire line does not replace selection', () => {2054usingCursor({2055text: [2056'line1',2057'line2'2058],2059}, (editor, model, viewModel) => {2060moveTo(editor, viewModel, 2, 1, false);2061moveTo(editor, viewModel, 2, 6, true);20622063viewModel.paste('line1\n', true);20642065assert.strictEqual(model.getLineContent(1), 'line1');2066assert.strictEqual(model.getLineContent(2), 'line1');2067assert.strictEqual(model.getLineContent(3), '');2068});2069});20702071test('issue #74722: Pasting whole line does not replace selection', () => {2072usingCursor({2073text: [2074'line1',2075'line sel 2',2076'line3'2077],2078}, (editor, model, viewModel) => {2079viewModel.setSelections('test', [new Selection(2, 6, 2, 9)]);20802081viewModel.paste('line1\n', true);20822083assert.strictEqual(model.getLineContent(1), 'line1');2084assert.strictEqual(model.getLineContent(2), 'line line1');2085assert.strictEqual(model.getLineContent(3), ' 2');2086assert.strictEqual(model.getLineContent(4), 'line3');2087});2088});20892090test('issue #4996: Multiple cursor paste pastes contents of all cursors', () => {2091usingCursor({2092text: [2093'line1',2094'line2',2095'line3'2096],2097}, (editor, model, viewModel) => {2098viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]);20992100viewModel.paste(2101'a\nb\nc\nd',2102false,2103[2104'a\nb',2105'c\nd'2106]2107);21082109assert.strictEqual(model.getValue(), [2110'a',2111'bline1',2112'c',2113'dline2',2114'line3'2115].join('\n'));2116});2117});21182119test('issue #16155: Paste into multiple cursors has edge case when number of lines equals number of cursors - 1', () => {2120usingCursor({2121text: [2122'test',2123'test',2124'test',2125'test'2126],2127}, (editor, model, viewModel) => {2128viewModel.setSelections('test', [2129new Selection(1, 1, 1, 5),2130new Selection(2, 1, 2, 5),2131new Selection(3, 1, 3, 5),2132new Selection(4, 1, 4, 5),2133]);21342135viewModel.paste(2136'aaa\nbbb\nccc\n',2137false,2138null2139);21402141assert.strictEqual(model.getValue(), [2142'aaa',2143'bbb',2144'ccc',2145'',2146'aaa',2147'bbb',2148'ccc',2149'',2150'aaa',2151'bbb',2152'ccc',2153'',2154'aaa',2155'bbb',2156'ccc',2157'',2158].join('\n'));2159});2160});21612162test('issue #43722: Multiline paste doesn\'t work anymore', () => {2163usingCursor({2164text: [2165'test',2166'test',2167'test',2168'test'2169],2170}, (editor, model, viewModel) => {2171viewModel.setSelections('test', [2172new Selection(1, 1, 1, 5),2173new Selection(2, 1, 2, 5),2174new Selection(3, 1, 3, 5),2175new Selection(4, 1, 4, 5),2176]);21772178viewModel.paste(2179'aaa\r\nbbb\r\nccc\r\nddd\r\n',2180false,2181null2182);21832184assert.strictEqual(model.getValue(), [2185'aaa',2186'bbb',2187'ccc',2188'ddd',2189].join('\n'));2190});2191});21922193test('issue #46440: (1) Pasting a multi-line selection pastes entire selection into every insertion point', () => {2194usingCursor({2195text: [2196'line1',2197'line2',2198'line3'2199],2200}, (editor, model, viewModel) => {2201viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]);22022203viewModel.paste(2204'a\nb\nc',2205false,2206null2207);22082209assert.strictEqual(model.getValue(), [2210'aline1',2211'bline2',2212'cline3'2213].join('\n'));2214});2215});22162217test('issue #46440: (2) Pasting a multi-line selection pastes entire selection into every insertion point', () => {2218usingCursor({2219text: [2220'line1',2221'line2',2222'line3'2223],2224}, (editor, model, viewModel) => {2225viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]);22262227viewModel.paste(2228'a\nb\nc\n',2229false,2230null2231);22322233assert.strictEqual(model.getValue(), [2234'aline1',2235'bline2',2236'cline3'2237].join('\n'));2238});2239});22402241test('issue #3071: Investigate why undo stack gets corrupted', () => {2242const model = createTextModel(2243[2244'some lines',2245'and more lines',2246'just some text',2247].join('\n')2248);22492250withTestCodeEditor(model, {}, (editor, viewModel) => {2251moveTo(editor, viewModel, 1, 1, false);2252moveTo(editor, viewModel, 3, 4, true);22532254let isFirst = true;2255const disposable = model.onDidChangeContent(() => {2256if (isFirst) {2257isFirst = false;2258viewModel.type('\t', 'keyboard');2259}2260});22612262editor.runCommand(CoreEditingCommands.Tab, null);2263assert.strictEqual(model.getValue(), [2264'\t just some text'2265].join('\n'), '001');22662267editor.runCommand(CoreEditingCommands.Undo, null);2268assert.strictEqual(model.getValue(), [2269' some lines',2270' and more lines',2271' just some text',2272].join('\n'), '002');22732274editor.runCommand(CoreEditingCommands.Undo, null);2275assert.strictEqual(model.getValue(), [2276'some lines',2277'and more lines',2278'just some text',2279].join('\n'), '003');22802281editor.runCommand(CoreEditingCommands.Undo, null);2282assert.strictEqual(model.getValue(), [2283'some lines',2284'and more lines',2285'just some text',2286].join('\n'), '004');22872288disposable.dispose();2289});2290});22912292test('issue #12950: Cannot Double Click To Insert Emoji Using OSX Emoji Panel', () => {2293usingCursor({2294text: [2295'some lines',2296'and more lines',2297'just some text',2298],2299languageId: null2300}, (editor, model, viewModel) => {2301moveTo(editor, viewModel, 3, 1, false);23022303viewModel.type('😍', 'keyboard');23042305assert.strictEqual(model.getValue(), [2306'some lines',2307'and more lines',2308'😍just some text',2309].join('\n'));2310});2311});23122313test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => {2314const model = createTextModel(2315[2316'function a() {',2317'\tvar a = {',2318'\t\tx: 3',2319'\t};',2320'}',2321].join('\n')2322);23232324withTestCodeEditor(model, {}, (editor, viewModel) => {2325moveTo(editor, viewModel, 3, 2, false);2326editor.runCommand(CoreEditingCommands.Tab, null);2327assert.strictEqual(model.getLineContent(3), '\t \tx: 3');2328});2329});23302331test('issue #4312: trying to type a tab character over a sequence of spaces results in unexpected behaviour', () => {2332const model = createTextModel(2333[2334'var foo = 123; // this is a comment',2335'var bar = 4; // another comment'2336].join('\n'),2337undefined,2338{2339insertSpaces: false,2340}2341);23422343withTestCodeEditor(model, {}, (editor, viewModel) => {2344moveTo(editor, viewModel, 1, 15, false);2345moveTo(editor, viewModel, 1, 22, true);2346editor.runCommand(CoreEditingCommands.Tab, null);2347assert.strictEqual(model.getLineContent(1), 'var foo = 123;\t// this is a comment');2348});2349});23502351test('issue #832: word right', () => {23522353usingCursor({2354text: [2355' /* Just some more text a+= 3 +5-3 + 7 */ '2356],2357}, (editor, model, viewModel) => {2358moveTo(editor, viewModel, 1, 1, false);23592360function assertWordRight(col: number, expectedCol: number) {2361const args = {2362position: {2363lineNumber: 1,2364column: col2365}2366};2367if (col === 1) {2368CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, args);2369} else {2370CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, args);2371}23722373assert.strictEqual(viewModel.getSelection().startColumn, 1, 'TEST FOR ' + col);2374assert.strictEqual(viewModel.getSelection().endColumn, expectedCol, 'TEST FOR ' + col);2375}23762377assertWordRight(1, ' '.length + 1);2378assertWordRight(2, ' '.length + 1);2379assertWordRight(3, ' '.length + 1);2380assertWordRight(4, ' '.length + 1);2381assertWordRight(5, ' /'.length + 1);2382assertWordRight(6, ' /*'.length + 1);2383assertWordRight(7, ' /* '.length + 1);2384assertWordRight(8, ' /* Just'.length + 1);2385assertWordRight(9, ' /* Just'.length + 1);2386assertWordRight(10, ' /* Just'.length + 1);2387assertWordRight(11, ' /* Just'.length + 1);2388assertWordRight(12, ' /* Just '.length + 1);2389assertWordRight(13, ' /* Just some'.length + 1);2390assertWordRight(14, ' /* Just some'.length + 1);2391assertWordRight(15, ' /* Just some'.length + 1);2392assertWordRight(16, ' /* Just some'.length + 1);2393assertWordRight(17, ' /* Just some '.length + 1);2394assertWordRight(18, ' /* Just some '.length + 1);2395assertWordRight(19, ' /* Just some '.length + 1);2396assertWordRight(20, ' /* Just some more'.length + 1);2397assertWordRight(21, ' /* Just some more'.length + 1);2398assertWordRight(22, ' /* Just some more'.length + 1);2399assertWordRight(23, ' /* Just some more'.length + 1);2400assertWordRight(24, ' /* Just some more '.length + 1);2401assertWordRight(25, ' /* Just some more '.length + 1);2402assertWordRight(26, ' /* Just some more '.length + 1);2403assertWordRight(27, ' /* Just some more text'.length + 1);2404assertWordRight(28, ' /* Just some more text'.length + 1);2405assertWordRight(29, ' /* Just some more text'.length + 1);2406assertWordRight(30, ' /* Just some more text'.length + 1);2407assertWordRight(31, ' /* Just some more text '.length + 1);2408assertWordRight(32, ' /* Just some more text a'.length + 1);2409assertWordRight(33, ' /* Just some more text a+'.length + 1);2410assertWordRight(34, ' /* Just some more text a+='.length + 1);2411assertWordRight(35, ' /* Just some more text a+= '.length + 1);2412assertWordRight(36, ' /* Just some more text a+= 3'.length + 1);2413assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1);2414assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1);2415assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1);2416assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1);2417assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1);2418assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1);2419assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1);2420assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1);2421assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1);2422assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1);2423assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1);2424assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1);2425assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);2426assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);2427});2428});24292430test('issue #33788: Wrong cursor position when double click to select a word', () => {2431const model = createTextModel(2432[2433'Just some text'2434].join('\n')2435);24362437withTestCodeEditor(model, {}, (editor, viewModel) => {2438CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });2439assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));24402441CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });2442assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));2443});2444});24452446test('issue #12887: Double-click highlighting separating white space', () => {2447const model = createTextModel(2448[2449'abc def'2450].join('\n')2451);24522453withTestCodeEditor(model, {}, (editor, viewModel) => {2454CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 5) });2455assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 5, 1, 8));2456});2457});24582459test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => {2460withTestCodeEditor([], {}, (editor, viewModel) => {2461const model = editor.getModel()!;2462assertCursor(viewModel, new Position(1, 1));24632464// Typing sennsei in Japanese - Hiragana2465viewModel.type('s', 'keyboard');2466viewModel.compositionType('せ', 1, 0, 0);2467viewModel.compositionType('せn', 1, 0, 0);2468viewModel.compositionType('せん', 2, 0, 0);2469viewModel.compositionType('せんs', 2, 0, 0);2470viewModel.compositionType('せんせ', 3, 0, 0);2471viewModel.compositionType('せんせ', 3, 0, 0);2472viewModel.compositionType('せんせい', 3, 0, 0);2473viewModel.compositionType('せんせい', 4, 0, 0);2474viewModel.compositionType('せんせい', 4, 0, 0);2475viewModel.compositionType('せんせい', 4, 0, 0);24762477assert.strictEqual(model.getLineContent(1), 'せんせい');2478assertCursor(viewModel, new Position(1, 5));24792480editor.runCommand(CoreEditingCommands.Undo, null);2481assert.strictEqual(model.getLineContent(1), '');2482assertCursor(viewModel, new Position(1, 1));2483});2484});24852486test('issue #23983: Calling model.setEOL does not reset cursor position', () => {2487usingCursor({2488text: [2489'first line',2490'second line'2491]2492}, (editor, model, viewModel) => {2493model.setEOL(EndOfLineSequence.CRLF);24942495viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);2496model.setEOL(EndOfLineSequence.LF);24972498assertCursor(viewModel, new Selection(2, 2, 2, 2));2499});2500});25012502test('issue #23983: Calling model.setValue() resets cursor position', () => {2503usingCursor({2504text: [2505'first line',2506'second line'2507]2508}, (editor, model, viewModel) => {2509model.setEOL(EndOfLineSequence.CRLF);25102511viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);2512model.setValue([2513'different first line',2514'different second line',2515'new third line'2516].join('\n'));25172518assertCursor(viewModel, new Selection(1, 1, 1, 1));2519});2520});25212522test('issue #36740: wordwrap creates an extra step / character at the wrapping point', () => {2523// a single model line => 4 view lines2524withTestCodeEditor([2525[2526'Lorem ipsum ',2527'dolor sit amet ',2528'consectetur ',2529'adipiscing elit',2530].join('')2531], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {2532viewModel.setSelections('test', [new Selection(1, 7, 1, 7)]);25332534moveRight(editor, viewModel);2535assertCursor(viewModel, new Selection(1, 8, 1, 8));25362537moveRight(editor, viewModel);2538assertCursor(viewModel, new Selection(1, 9, 1, 9));25392540moveRight(editor, viewModel);2541assertCursor(viewModel, new Selection(1, 10, 1, 10));25422543moveRight(editor, viewModel);2544assertCursor(viewModel, new Selection(1, 11, 1, 11));25452546moveRight(editor, viewModel);2547assertCursor(viewModel, new Selection(1, 12, 1, 12));25482549moveRight(editor, viewModel);2550assertCursor(viewModel, new Selection(1, 13, 1, 13));25512552// moving to view line 22553moveRight(editor, viewModel);2554assertCursor(viewModel, new Selection(1, 14, 1, 14));25552556moveLeft(editor, viewModel);2557assertCursor(viewModel, new Selection(1, 13, 1, 13));25582559// moving back to view line 12560moveLeft(editor, viewModel);2561assertCursor(viewModel, new Selection(1, 12, 1, 12));2562});2563});25642565test('issue #110376: multiple selections with wordwrap behave differently', () => {2566// a single model line => 4 view lines2567withTestCodeEditor([2568[2569'just a sentence. just a ',2570'sentence. just a sentence.',2571].join('')2572], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => {2573viewModel.setSelections('test', [2574new Selection(1, 1, 1, 16),2575new Selection(1, 18, 1, 33),2576new Selection(1, 35, 1, 50),2577]);25782579moveLeft(editor, viewModel);2580assertCursor(viewModel, [2581new Selection(1, 1, 1, 1),2582new Selection(1, 18, 1, 18),2583new Selection(1, 35, 1, 35),2584]);25852586viewModel.setSelections('test', [2587new Selection(1, 1, 1, 16),2588new Selection(1, 18, 1, 33),2589new Selection(1, 35, 1, 50),2590]);25912592moveRight(editor, viewModel);2593assertCursor(viewModel, [2594new Selection(1, 16, 1, 16),2595new Selection(1, 33, 1, 33),2596new Selection(1, 50, 1, 50),2597]);2598});2599});26002601test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => {2602// a single model line => 4 view lines2603withTestCodeEditor([2604[2605'lorem_ipsum-1993x11x13',2606'dolor_sit_amet-1998x04x27',2607'consectetur-2007x10x08',2608'adipiscing-2012x07x27',2609'elit-2015x02x27',2610].join('\n')2611], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {2612viewModel.setSelections('test', [2613new Selection(1, 13, 1, 13),2614new Selection(2, 16, 2, 16),2615new Selection(3, 13, 3, 13),2616new Selection(4, 12, 4, 12),2617new Selection(5, 6, 5, 6),2618]);2619assertCursor(viewModel, [2620new Selection(1, 13, 1, 13),2621new Selection(2, 16, 2, 16),2622new Selection(3, 13, 3, 13),2623new Selection(4, 12, 4, 12),2624new Selection(5, 6, 5, 6),2625]);26262627moveRight(editor, viewModel, true);2628assertCursor(viewModel, [2629new Selection(1, 13, 1, 14),2630new Selection(2, 16, 2, 17),2631new Selection(3, 13, 3, 14),2632new Selection(4, 12, 4, 13),2633new Selection(5, 6, 5, 7),2634]);26352636moveRight(editor, viewModel, true);2637assertCursor(viewModel, [2638new Selection(1, 13, 1, 15),2639new Selection(2, 16, 2, 18),2640new Selection(3, 13, 3, 15),2641new Selection(4, 12, 4, 14),2642new Selection(5, 6, 5, 8),2643]);26442645moveRight(editor, viewModel, true);2646assertCursor(viewModel, [2647new Selection(1, 13, 1, 16),2648new Selection(2, 16, 2, 19),2649new Selection(3, 13, 3, 16),2650new Selection(4, 12, 4, 15),2651new Selection(5, 6, 5, 9),2652]);26532654moveRight(editor, viewModel, true);2655assertCursor(viewModel, [2656new Selection(1, 13, 1, 17),2657new Selection(2, 16, 2, 20),2658new Selection(3, 13, 3, 17),2659new Selection(4, 12, 4, 16),2660new Selection(5, 6, 5, 10),2661]);2662});2663});26642665test('issue #41573 - delete across multiple lines does not shrink the selection when word wraps', () => {2666withTestCodeEditor([2667'Authorization: \'Bearer pHKRfCTFSnGxs6akKlb9ddIXcca0sIUSZJutPHYqz7vEeHdMTMh0SGN0IGU3a0n59DXjTLRsj5EJ2u33qLNIFi9fk5XF8pK39PndLYUZhPt4QvHGLScgSkK0L4gwzkzMloTQPpKhqiikiIOvyNNSpd2o8j29NnOmdTUOKi9DVt74PD2ohKxyOrWZ6oZprTkb3eKajcpnS0LABKfaw2rmv4\','2668].join('\n'), { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }, (editor, viewModel) => {2669moveTo(editor, viewModel, 1, 43, false);2670moveTo(editor, viewModel, 1, 147, true);2671assertCursor(viewModel, new Selection(1, 43, 1, 147));26722673editor.getModel().applyEdits([{2674range: new Range(1, 1, 1, 43),2675text: ''2676}]);26772678assertCursor(viewModel, new Selection(1, 1, 1, 105));2679});2680});26812682test('issue #22717: Moving text cursor cause an incorrect position in Chinese', () => {2683// a single model line => 4 view lines2684withTestCodeEditor([2685[2686'一二三四五六七八九十',2687'12345678901234567890',2688].join('\n')2689], {}, (editor, viewModel) => {2690viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);26912692moveDown(editor, viewModel);2693assertCursor(viewModel, new Selection(2, 9, 2, 9));26942695moveRight(editor, viewModel);2696assertCursor(viewModel, new Selection(2, 10, 2, 10));26972698moveRight(editor, viewModel);2699assertCursor(viewModel, new Selection(2, 11, 2, 11));27002701moveUp(editor, viewModel);2702assertCursor(viewModel, new Selection(1, 6, 1, 6));2703});2704});27052706test('issue #112301: new stickyTabStops feature interferes with word wrap', () => {2707withTestCodeEditor([2708[2709'function hello() {',2710' console.log(`this is a long console message`)',2711'}',2712].join('\n')2713], { wordWrap: 'wordWrapColumn', wordWrapColumn: 32, stickyTabStops: true }, (editor, viewModel) => {2714viewModel.setSelections('test', [2715new Selection(2, 31, 2, 31)2716]);2717moveRight(editor, viewModel, false);2718assertCursor(viewModel, new Position(2, 32));27192720moveRight(editor, viewModel, false);2721assertCursor(viewModel, new Position(2, 33));27222723moveRight(editor, viewModel, false);2724assertCursor(viewModel, new Position(2, 34));27252726moveLeft(editor, viewModel, false);2727assertCursor(viewModel, new Position(2, 33));27282729moveLeft(editor, viewModel, false);2730assertCursor(viewModel, new Position(2, 32));27312732moveLeft(editor, viewModel, false);2733assertCursor(viewModel, new Position(2, 31));2734});2735});27362737test('issue #44805: Should not be able to undo in readonly editor', () => {2738const model = createTextModel(2739[2740''2741].join('\n')2742);27432744withTestCodeEditor(model, { readOnly: true }, (editor, viewModel) => {2745model.pushEditOperations([new Selection(1, 1, 1, 1)], [{2746range: new Range(1, 1, 1, 1),2747text: 'Hello world!'2748}], () => [new Selection(1, 1, 1, 1)]);2749assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');27502751editor.runCommand(CoreEditingCommands.Undo, null);2752assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');2753});2754});27552756test('issue #46314: ViewModel is out of sync with Model!', () => {27572758const tokenizationSupport: ITokenizationSupport = {2759getInitialState: () => NullState,2760tokenize: undefined!,2761tokenizeEncoded: (line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult => {2762return new EncodedTokenizationResult(new Uint32Array(0), state);2763}2764};27652766const LANGUAGE_ID = 'modelModeTest1';2767const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);2768const model = createTextModel('Just text', LANGUAGE_ID);27692770withTestCodeEditor(model, {}, (editor1, cursor1) => {2771withTestCodeEditor(model, {}, (editor2, cursor2) => {27722773const disposable = editor1.onDidChangeCursorPosition(() => {2774model.tokenization.tokenizeIfCheap(1);2775});27762777model.applyEdits([{ range: new Range(1, 1, 1, 1), text: '-' }]);27782779disposable.dispose();2780});2781});27822783languageRegistration.dispose();2784model.dispose();2785});27862787test('issue #37967: problem replacing consecutive characters', () => {2788const model = createTextModel(2789[2790'const a = "foo";',2791'const b = ""'2792].join('\n')2793);27942795withTestCodeEditor(model, { multiCursorMergeOverlapping: false }, (editor, viewModel) => {2796editor.setSelections([2797new Selection(1, 12, 1, 12),2798new Selection(1, 16, 1, 16),2799new Selection(2, 12, 2, 12),2800new Selection(2, 13, 2, 13),2801]);28022803editor.runCommand(CoreEditingCommands.DeleteLeft, null);28042805assertCursor(viewModel, [2806new Selection(1, 11, 1, 11),2807new Selection(1, 14, 1, 14),2808new Selection(2, 11, 2, 11),2809new Selection(2, 11, 2, 11),2810]);28112812viewModel.type('\'', 'keyboard');28132814assert.strictEqual(model.getLineContent(1), 'const a = \'foo\';');2815assert.strictEqual(model.getLineContent(2), 'const b = \'\'');2816});2817});28182819test('issue #15761: Cursor doesn\'t move in a redo operation', () => {2820const model = createTextModel(2821[2822'hello'2823].join('\n')2824);28252826withTestCodeEditor(model, {}, (editor, viewModel) => {2827editor.setSelections([2828new Selection(1, 4, 1, 4)2829]);28302831editor.executeEdits('test', [{2832range: new Range(1, 1, 1, 1),2833text: '*',2834forceMoveMarkers: true2835}]);2836assertCursor(viewModel, [2837new Selection(1, 5, 1, 5),2838]);28392840editor.runCommand(CoreEditingCommands.Undo, null);2841assertCursor(viewModel, [2842new Selection(1, 4, 1, 4),2843]);28442845editor.runCommand(CoreEditingCommands.Redo, null);2846assertCursor(viewModel, [2847new Selection(1, 5, 1, 5),2848]);2849});2850});28512852test('issue #42783: API Calls with Undo Leave Cursor in Wrong Position', () => {2853const model = createTextModel(2854[2855'ab'2856].join('\n')2857);28582859withTestCodeEditor(model, {}, (editor, viewModel) => {2860editor.setSelections([2861new Selection(1, 1, 1, 1)2862]);28632864editor.executeEdits('test', [{2865range: new Range(1, 1, 1, 3),2866text: ''2867}]);2868assertCursor(viewModel, [2869new Selection(1, 1, 1, 1),2870]);28712872editor.runCommand(CoreEditingCommands.Undo, null);2873assertCursor(viewModel, [2874new Selection(1, 1, 1, 1),2875]);28762877editor.executeEdits('test', [{2878range: new Range(1, 1, 1, 2),2879text: ''2880}]);2881assertCursor(viewModel, [2882new Selection(1, 1, 1, 1),2883]);2884});2885});28862887test('issue #85712: Paste line moves cursor to start of current line rather than start of next line', () => {2888const model = createTextModel(2889[2890'abc123',2891''2892].join('\n')2893);28942895withTestCodeEditor(model, {}, (editor, viewModel) => {2896editor.setSelections([2897new Selection(2, 1, 2, 1)2898]);2899viewModel.paste('something\n', true);2900assert.strictEqual(model.getValue(), [2901'abc123',2902'something',2903''2904].join('\n'));2905assertCursor(viewModel, new Position(3, 1));2906});2907});29082909test('issue #84897: Left delete behavior in some languages is changed', () => {2910const model = createTextModel(2911[2912'สวัสดี'2913].join('\n')2914);29152916withTestCodeEditor(model, {}, (editor, viewModel) => {2917editor.setSelections([2918new Selection(1, 7, 1, 7)2919]);29202921editor.runCommand(CoreEditingCommands.DeleteLeft, null);2922assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');29232924editor.runCommand(CoreEditingCommands.DeleteLeft, null);2925assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');29262927editor.runCommand(CoreEditingCommands.DeleteLeft, null);2928assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');29292930editor.runCommand(CoreEditingCommands.DeleteLeft, null);2931assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');29322933editor.runCommand(CoreEditingCommands.DeleteLeft, null);2934assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');29352936editor.runCommand(CoreEditingCommands.DeleteLeft, null);2937assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');2938});2939});29402941test('issue #122914: Left delete behavior in some languages is changed (useTabStops: false)', () => {2942const model = createTextModel(2943[2944'สวัสดี'2945].join('\n')2946);29472948withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {2949editor.setSelections([2950new Selection(1, 7, 1, 7)2951]);29522953editor.runCommand(CoreEditingCommands.DeleteLeft, null);2954assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');29552956editor.runCommand(CoreEditingCommands.DeleteLeft, null);2957assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');29582959editor.runCommand(CoreEditingCommands.DeleteLeft, null);2960assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');29612962editor.runCommand(CoreEditingCommands.DeleteLeft, null);2963assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');29642965editor.runCommand(CoreEditingCommands.DeleteLeft, null);2966assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');29672968editor.runCommand(CoreEditingCommands.DeleteLeft, null);2969assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');2970});2971});29722973test('issue #99629: Emoji modifiers in text treated separately when using backspace', () => {2974const model = createTextModel(2975[2976'👶🏾'2977].join('\n')2978);29792980withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {2981const len = model.getValueLength();2982editor.setSelections([2983new Selection(1, 1 + len, 1, 1 + len)2984]);29852986editor.runCommand(CoreEditingCommands.DeleteLeft, null);2987assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');2988});2989});29902991test('issue #99629: Emoji modifiers in text treated separately when using backspace (ZWJ sequence)', () => {2992const model = createTextModel(2993[2994'👨👩🏽👧👦'2995].join('\n')2996);29972998withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {2999const len = model.getValueLength();3000editor.setSelections([3001new Selection(1, 1 + len, 1, 1 + len)3002]);30033004editor.runCommand(CoreEditingCommands.DeleteLeft, null);3005assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨👩🏽👧');30063007editor.runCommand(CoreEditingCommands.DeleteLeft, null);3008assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨👩🏽');30093010editor.runCommand(CoreEditingCommands.DeleteLeft, null);3011assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨');30123013editor.runCommand(CoreEditingCommands.DeleteLeft, null);3014assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');3015});3016});30173018test('issue #105730: move left behaves differently for multiple cursors', () => {3019const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, ');30203021withTestCodeEditor(3022model,3023{3024wordWrap: 'wordWrapColumn',3025wordWrapColumn: 243026},3027(editor, viewModel) => {3028viewModel.setSelections('test', [3029new Selection(1, 10, 1, 12),3030new Selection(1, 21, 1, 23),3031new Selection(1, 32, 1, 34)3032]);3033moveLeft(editor, viewModel, false);3034assertCursor(viewModel, [3035new Selection(1, 10, 1, 10),3036new Selection(1, 21, 1, 21),3037new Selection(1, 32, 1, 32)3038]);30393040viewModel.setSelections('test', [3041new Selection(1, 10, 1, 12),3042new Selection(1, 21, 1, 23),3043new Selection(1, 32, 1, 34)3044]);3045moveLeft(editor, viewModel, true);3046assertCursor(viewModel, [3047new Selection(1, 10, 1, 11),3048new Selection(1, 21, 1, 22),3049new Selection(1, 32, 1, 33)3050]);3051});3052});30533054test('issue #105730: move right should always skip wrap point', () => {3055const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, \nasdfghjkl,');30563057withTestCodeEditor(3058model,3059{3060wordWrap: 'wordWrapColumn',3061wordWrapColumn: 243062},3063(editor, viewModel) => {3064viewModel.setSelections('test', [3065new Selection(1, 22, 1, 22)3066]);3067moveRight(editor, viewModel, false);3068moveRight(editor, viewModel, false);3069assertCursor(viewModel, [3070new Selection(1, 24, 1, 24),3071]);30723073viewModel.setSelections('test', [3074new Selection(1, 22, 1, 22)3075]);3076moveRight(editor, viewModel, true);3077moveRight(editor, viewModel, true);3078assertCursor(viewModel, [3079new Selection(1, 22, 1, 24),3080]);3081}3082);3083});30843085test('issue #123178: sticky tab in consecutive wrapped lines', () => {3086const model = createTextModel(' aaaa aaaa', undefined, { tabSize: 4 });30873088withTestCodeEditor(3089model,3090{3091wordWrap: 'wordWrapColumn',3092wordWrapColumn: 8,3093stickyTabStops: true,3094},3095(editor, viewModel) => {3096viewModel.setSelections('test', [3097new Selection(1, 9, 1, 9)3098]);3099moveRight(editor, viewModel, false);3100assertCursor(viewModel, [3101new Selection(1, 10, 1, 10),3102]);31033104moveLeft(editor, viewModel, false);3105assertCursor(viewModel, [3106new Selection(1, 9, 1, 9),3107]);3108}3109);3110});31113112test('Cursor honors insertSpaces configuration on new line', () => {3113usingCursor({3114text: [3115' \tMy First Line\t ',3116'\tMy Second Line',3117' Third Line',3118'',3119'1'3120]3121}, (editor, model, viewModel) => {3122CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(1, 21), source: 'keyboard' });3123viewModel.type('\n', 'keyboard');3124assert.strictEqual(model.getLineContent(1), ' \tMy First Line\t ');3125assert.strictEqual(model.getLineContent(2), ' ');3126});3127});31283129test('Cursor honors insertSpaces configuration on tab', () => {3130const model = createTextModel(3131[3132' \tMy First Line\t ',3133'My Second Line123',3134' Third Line',3135'',3136'1'3137].join('\n'),3138undefined,3139{3140tabSize: 13,3141indentSize: 13,3142}3143);31443145withTestCodeEditor(model, {}, (editor, viewModel) => {3146// Tab on column 13147CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 1) });3148editor.runCommand(CoreEditingCommands.Tab, null);3149assert.strictEqual(model.getLineContent(2), ' My Second Line123');3150editor.runCommand(CoreEditingCommands.Undo, null);31513152// Tab on column 23153assert.strictEqual(model.getLineContent(2), 'My Second Line123');3154CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 2) });3155editor.runCommand(CoreEditingCommands.Tab, null);3156assert.strictEqual(model.getLineContent(2), 'M y Second Line123');3157editor.runCommand(CoreEditingCommands.Undo, null);31583159// Tab on column 33160assert.strictEqual(model.getLineContent(2), 'My Second Line123');3161CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 3) });3162editor.runCommand(CoreEditingCommands.Tab, null);3163assert.strictEqual(model.getLineContent(2), 'My Second Line123');3164editor.runCommand(CoreEditingCommands.Undo, null);31653166// Tab on column 43167assert.strictEqual(model.getLineContent(2), 'My Second Line123');3168CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 4) });3169editor.runCommand(CoreEditingCommands.Tab, null);3170assert.strictEqual(model.getLineContent(2), 'My Second Line123');3171editor.runCommand(CoreEditingCommands.Undo, null);31723173// Tab on column 53174assert.strictEqual(model.getLineContent(2), 'My Second Line123');3175CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });3176editor.runCommand(CoreEditingCommands.Tab, null);3177assert.strictEqual(model.getLineContent(2), 'My S econd Line123');3178editor.runCommand(CoreEditingCommands.Undo, null);31793180// Tab on column 53181assert.strictEqual(model.getLineContent(2), 'My Second Line123');3182CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });3183editor.runCommand(CoreEditingCommands.Tab, null);3184assert.strictEqual(model.getLineContent(2), 'My S econd Line123');3185editor.runCommand(CoreEditingCommands.Undo, null);31863187// Tab on column 133188assert.strictEqual(model.getLineContent(2), 'My Second Line123');3189CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 13) });3190editor.runCommand(CoreEditingCommands.Tab, null);3191assert.strictEqual(model.getLineContent(2), 'My Second Li ne123');3192editor.runCommand(CoreEditingCommands.Undo, null);31933194// Tab on column 143195assert.strictEqual(model.getLineContent(2), 'My Second Line123');3196CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 14) });3197editor.runCommand(CoreEditingCommands.Tab, null);3198assert.strictEqual(model.getLineContent(2), 'My Second Lin e123');3199});3200});32013202test('Enter auto-indents with insertSpaces setting 1', () => {3203const languageId = setupOnEnterLanguage(IndentAction.Indent);3204usingCursor({3205text: [3206'\thello'3207],3208languageId: languageId3209}, (editor, model, viewModel) => {3210moveTo(editor, viewModel, 1, 7, false);3211assertCursor(viewModel, new Selection(1, 7, 1, 7));32123213viewModel.type('\n', 'keyboard');3214assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');3215});3216});32173218test('Enter auto-indents with insertSpaces setting 2', () => {3219const languageId = setupOnEnterLanguage(IndentAction.None);3220usingCursor({3221text: [3222'\thello'3223],3224languageId: languageId3225}, (editor, model, viewModel) => {3226moveTo(editor, viewModel, 1, 7, false);3227assertCursor(viewModel, new Selection(1, 7, 1, 7));32283229viewModel.type('\n', 'keyboard');3230assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');3231});3232});32333234test('Enter auto-indents with insertSpaces setting 3', () => {3235const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);3236usingCursor({3237text: [3238'\thell()'3239],3240languageId: languageId3241}, (editor, model, viewModel) => {3242moveTo(editor, viewModel, 1, 7, false);3243assertCursor(viewModel, new Selection(1, 7, 1, 7));32443245viewModel.type('\n', 'keyboard');3246assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )');3247});3248});32493250test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: true', () => {3251usingCursor({3252text: [3253' \t'3254],3255}, (editor, model, viewModel) => {3256moveTo(editor, viewModel, 1, 4, false);3257viewModel.type('\n', 'keyboard');3258assert.strictEqual(model.getValue(), ' \t\n ');3259});3260});32613262test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: false', () => {3263usingCursor({3264text: [3265' \t'3266]3267}, (editor, model, viewModel) => {3268model.updateOptions({3269insertSpaces: false3270});3271moveTo(editor, viewModel, 1, 4, false);3272viewModel.type('\n', 'keyboard');3273assert.strictEqual(model.getValue(), ' \t\n\t');3274});3275});32763277test('removeAutoWhitespace off', () => {3278usingCursor({3279text: [3280' some line abc '3281],3282modelOpts: {3283trimAutoWhitespace: false3284}3285}, (editor, model, viewModel) => {32863287// Move cursor to the end, verify that we do not trim whitespaces if line has values3288moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3289viewModel.type('\n', 'keyboard');3290assert.strictEqual(model.getLineContent(1), ' some line abc ');3291assert.strictEqual(model.getLineContent(2), ' ');32923293// Try to enter again, we should trimmed previous line3294viewModel.type('\n', 'keyboard');3295assert.strictEqual(model.getLineContent(1), ' some line abc ');3296assert.strictEqual(model.getLineContent(2), ' ');3297assert.strictEqual(model.getLineContent(3), ' ');3298});3299});33003301test('removeAutoWhitespace on: removes only whitespace the cursor added 1', () => {3302usingCursor({3303text: [3304' '3305]3306}, (editor, model, viewModel) => {3307moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3308viewModel.type('\n', 'keyboard');3309assert.strictEqual(model.getLineContent(1), ' ');3310assert.strictEqual(model.getLineContent(2), ' ');33113312viewModel.type('\n', 'keyboard');3313assert.strictEqual(model.getLineContent(1), ' ');3314assert.strictEqual(model.getLineContent(2), '');3315assert.strictEqual(model.getLineContent(3), ' ');3316});3317});33183319test('issue #115033: indent and appendText', () => {3320const languageId = 'onEnterMode';33213322disposables.add(languageService.registerLanguage({ id: languageId }));3323disposables.add(languageConfigurationService.register(languageId, {3324onEnterRules: [{3325beforeText: /.*/,3326action: {3327indentAction: IndentAction.Indent,3328appendText: 'x'3329}3330}]3331}));3332usingCursor({3333text: [3334'text'3335],3336languageId: languageId,3337}, (editor, model, viewModel) => {33383339moveTo(editor, viewModel, 1, 5);3340viewModel.type('\n', 'keyboard');3341assert.strictEqual(model.getLineContent(1), 'text');3342assert.strictEqual(model.getLineContent(2), ' x');3343assertCursor(viewModel, new Position(2, 6));3344});3345});33463347test('issue #6862: Editor removes auto inserted indentation when formatting on type', () => {3348const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);3349usingCursor({3350text: [3351'function foo (params: string) {}'3352],3353languageId: languageId,3354}, (editor, model, viewModel) => {33553356moveTo(editor, viewModel, 1, 32);3357viewModel.type('\n', 'keyboard');3358assert.strictEqual(model.getLineContent(1), 'function foo (params: string) {');3359assert.strictEqual(model.getLineContent(2), ' ');3360assert.strictEqual(model.getLineContent(3), '}');33613362class TestCommand implements ICommand {33633364private _selectionId: string | null = null;33653366public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {3367builder.addEditOperation(new Range(1, 13, 1, 14), '');3368this._selectionId = builder.trackSelection(viewModel.getSelection());3369}33703371public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {3372return helper.getTrackedSelection(this._selectionId!);3373}33743375}33763377viewModel.executeCommand(new TestCommand(), 'autoFormat');3378assert.strictEqual(model.getLineContent(1), 'function foo(params: string) {');3379assert.strictEqual(model.getLineContent(2), ' ');3380assert.strictEqual(model.getLineContent(3), '}');3381});3382});33833384test('removeAutoWhitespace on: removes only whitespace the cursor added 2', () => {3385const languageId = 'testLang';3386const registration = languageService.registerLanguage({ id: languageId });3387const model = createTextModel(3388[3389' if (a) {',3390' ',3391'',3392'',3393' }'3394].join('\n'),3395languageId3396);33973398withTestCodeEditor(model, {}, (editor, viewModel) => {33993400moveTo(editor, viewModel, 3, 1);3401editor.runCommand(CoreEditingCommands.Tab, null);3402assert.strictEqual(model.getLineContent(1), ' if (a) {');3403assert.strictEqual(model.getLineContent(2), ' ');3404assert.strictEqual(model.getLineContent(3), ' ');3405assert.strictEqual(model.getLineContent(4), '');3406assert.strictEqual(model.getLineContent(5), ' }');34073408moveTo(editor, viewModel, 4, 1);3409editor.runCommand(CoreEditingCommands.Tab, null);3410assert.strictEqual(model.getLineContent(1), ' if (a) {');3411assert.strictEqual(model.getLineContent(2), ' ');3412assert.strictEqual(model.getLineContent(3), '');3413assert.strictEqual(model.getLineContent(4), ' ');3414assert.strictEqual(model.getLineContent(5), ' }');34153416moveTo(editor, viewModel, 5, model.getLineMaxColumn(5));3417viewModel.type('something', 'keyboard');3418assert.strictEqual(model.getLineContent(1), ' if (a) {');3419assert.strictEqual(model.getLineContent(2), ' ');3420assert.strictEqual(model.getLineContent(3), '');3421assert.strictEqual(model.getLineContent(4), '');3422assert.strictEqual(model.getLineContent(5), ' }something');3423});34243425registration.dispose();3426});34273428test('removeAutoWhitespace on: test 1', () => {3429const model = createTextModel(3430[3431' some line abc '3432].join('\n')3433);34343435withTestCodeEditor(model, {}, (editor, viewModel) => {34363437// Move cursor to the end, verify that we do not trim whitespaces if line has values3438moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3439viewModel.type('\n', 'keyboard');3440assert.strictEqual(model.getLineContent(1), ' some line abc ');3441assert.strictEqual(model.getLineContent(2), ' ');34423443// Try to enter again, we should trimmed previous line3444viewModel.type('\n', 'keyboard');3445assert.strictEqual(model.getLineContent(1), ' some line abc ');3446assert.strictEqual(model.getLineContent(2), '');3447assert.strictEqual(model.getLineContent(3), ' ');34483449// More whitespaces3450editor.runCommand(CoreEditingCommands.Tab, null);3451assert.strictEqual(model.getLineContent(1), ' some line abc ');3452assert.strictEqual(model.getLineContent(2), '');3453assert.strictEqual(model.getLineContent(3), ' ');34543455// Enter and verify that trimmed again3456viewModel.type('\n', 'keyboard');3457assert.strictEqual(model.getLineContent(1), ' some line abc ');3458assert.strictEqual(model.getLineContent(2), '');3459assert.strictEqual(model.getLineContent(3), '');3460assert.strictEqual(model.getLineContent(4), ' ');34613462// Trimmed if we will keep only text3463moveTo(editor, viewModel, 1, 5);3464viewModel.type('\n', 'keyboard');3465assert.strictEqual(model.getLineContent(1), ' ');3466assert.strictEqual(model.getLineContent(2), ' some line abc ');3467assert.strictEqual(model.getLineContent(3), '');3468assert.strictEqual(model.getLineContent(4), '');3469assert.strictEqual(model.getLineContent(5), '');34703471// Trimmed if we will keep only text by selection3472moveTo(editor, viewModel, 2, 5);3473moveTo(editor, viewModel, 3, 1, true);3474viewModel.type('\n', 'keyboard');3475assert.strictEqual(model.getLineContent(1), ' ');3476assert.strictEqual(model.getLineContent(2), ' ');3477assert.strictEqual(model.getLineContent(3), ' ');3478assert.strictEqual(model.getLineContent(4), '');3479assert.strictEqual(model.getLineContent(5), '');3480});3481});34823483test('issue #15118: remove auto whitespace when pasting entire line', () => {3484const model = createTextModel(3485[3486' function f() {',3487' // I\'m gonna copy this line',3488' return 3;',3489' }',3490].join('\n')3491);34923493withTestCodeEditor(model, {}, (editor, viewModel) => {34943495moveTo(editor, viewModel, 3, model.getLineMaxColumn(3));3496viewModel.type('\n', 'keyboard');34973498assert.strictEqual(model.getValue(), [3499' function f() {',3500' // I\'m gonna copy this line',3501' return 3;',3502' ',3503' }',3504].join('\n'));3505assertCursor(viewModel, new Position(4, model.getLineMaxColumn(4)));35063507viewModel.paste(' // I\'m gonna copy this line\n', true);3508assert.strictEqual(model.getValue(), [3509' function f() {',3510' // I\'m gonna copy this line',3511' return 3;',3512' // I\'m gonna copy this line',3513'',3514' }',3515].join('\n'));3516assertCursor(viewModel, new Position(5, 1));3517});3518});35193520test('issue #40695: maintain cursor position when copying lines using ctrl+c, ctrl+v', () => {3521const model = createTextModel(3522[3523' function f() {',3524' // I\'m gonna copy this line',3525' // Another line',3526' return 3;',3527' }',3528].join('\n')3529);35303531withTestCodeEditor(model, {}, (editor, viewModel) => {35323533editor.setSelections([new Selection(4, 10, 4, 10)]);3534viewModel.paste(' // I\'m gonna copy this line\n', true);35353536assert.strictEqual(model.getValue(), [3537' function f() {',3538' // I\'m gonna copy this line',3539' // Another line',3540' // I\'m gonna copy this line',3541' return 3;',3542' }',3543].join('\n'));3544assertCursor(viewModel, new Position(5, 10));3545});3546});35473548test('UseTabStops is off', () => {3549const model = createTextModel(3550[3551' x',3552' a ',3553' '3554].join('\n')3555);35563557withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {3558// DeleteLeft removes just one whitespace3559moveTo(editor, viewModel, 2, 9);3560editor.runCommand(CoreEditingCommands.DeleteLeft, null);3561assert.strictEqual(model.getLineContent(2), ' a ');3562});3563});35643565test('Backspace removes whitespaces with tab size', () => {3566const model = createTextModel(3567[3568' \t \t x',3569' a ',3570' '3571].join('\n')3572);35733574withTestCodeEditor(model, { useTabStops: true }, (editor, viewModel) => {3575// DeleteLeft does not remove tab size, because some text exists before3576moveTo(editor, viewModel, 2, model.getLineContent(2).length + 1);3577editor.runCommand(CoreEditingCommands.DeleteLeft, null);3578assert.strictEqual(model.getLineContent(2), ' a ');35793580// DeleteLeft removes tab size = 43581moveTo(editor, viewModel, 2, 9);3582editor.runCommand(CoreEditingCommands.DeleteLeft, null);3583assert.strictEqual(model.getLineContent(2), ' a ');35843585// DeleteLeft removes tab size = 43586editor.runCommand(CoreEditingCommands.DeleteLeft, null);3587assert.strictEqual(model.getLineContent(2), 'a ');35883589// Undo DeleteLeft - get us back to original indentation3590editor.runCommand(CoreEditingCommands.Undo, null);3591assert.strictEqual(model.getLineContent(2), ' a ');35923593// Nothing is broken when cursor is in (1,1)3594moveTo(editor, viewModel, 1, 1);3595editor.runCommand(CoreEditingCommands.DeleteLeft, null);3596assert.strictEqual(model.getLineContent(1), ' \t \t x');35973598// DeleteLeft stops at tab stops even in mixed whitespace case3599moveTo(editor, viewModel, 1, 10);3600editor.runCommand(CoreEditingCommands.DeleteLeft, null);3601assert.strictEqual(model.getLineContent(1), ' \t \t x');36023603editor.runCommand(CoreEditingCommands.DeleteLeft, null);3604assert.strictEqual(model.getLineContent(1), ' \t \tx');36053606editor.runCommand(CoreEditingCommands.DeleteLeft, null);3607assert.strictEqual(model.getLineContent(1), ' \tx');36083609editor.runCommand(CoreEditingCommands.DeleteLeft, null);3610assert.strictEqual(model.getLineContent(1), 'x');36113612// DeleteLeft on last line3613moveTo(editor, viewModel, 3, model.getLineContent(3).length + 1);3614editor.runCommand(CoreEditingCommands.DeleteLeft, null);3615assert.strictEqual(model.getLineContent(3), '');36163617// DeleteLeft with removing new line symbol3618editor.runCommand(CoreEditingCommands.DeleteLeft, null);3619assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x\n a ');36203621// In case of selection DeleteLeft only deletes selected text3622moveTo(editor, viewModel, 2, 3);3623moveTo(editor, viewModel, 2, 4, true);3624editor.runCommand(CoreEditingCommands.DeleteLeft, null);3625assert.strictEqual(model.getLineContent(2), ' a ');3626});3627});36283629test('PR #5423: Auto indent + undo + redo is funky', () => {3630const model = createTextModel(3631[3632''3633].join('\n'),3634undefined,3635{3636insertSpaces: false,3637}3638);36393640withTestCodeEditor(model, {}, (editor, viewModel) => {3641viewModel.type('\n', 'keyboard');3642assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');36433644editor.runCommand(CoreEditingCommands.Tab, null);3645assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');36463647viewModel.type('y', 'keyboard');3648assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2');36493650viewModel.type('\n', 'keyboard');3651assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3');36523653viewModel.type('x');3654assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4');36553656CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});3657assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5');36583659editor.runCommand(CoreEditingCommands.DeleteLeft, null);3660assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert6');36613662editor.runCommand(CoreEditingCommands.DeleteLeft, null);3663assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tyx', 'assert7');36643665editor.runCommand(CoreEditingCommands.DeleteLeft, null);3666assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert8');36673668editor.runCommand(CoreEditingCommands.DeleteLeft, null);3669assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert9');36703671editor.runCommand(CoreEditingCommands.DeleteLeft, null);3672assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert10');36733674editor.runCommand(CoreEditingCommands.Undo, null);3675assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert11');36763677editor.runCommand(CoreEditingCommands.Undo, null);3678assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert12');36793680editor.runCommand(CoreEditingCommands.Undo, null);3681assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert13');36823683editor.runCommand(CoreEditingCommands.Redo, null);3684assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert14');36853686editor.runCommand(CoreEditingCommands.Redo, null);3687assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert15');36883689editor.runCommand(CoreEditingCommands.Redo, null);3690assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert16');3691});3692});36933694test('issue #90973: Undo brings back model alternative version', () => {3695const model = createTextModel(3696[3697''3698].join('\n'),3699undefined,3700{3701insertSpaces: false,3702}3703);37043705withTestCodeEditor(model, {}, (editor, viewModel) => {3706const beforeVersion = model.getVersionId();3707const beforeAltVersion = model.getAlternativeVersionId();3708viewModel.type('Hello', 'keyboard');3709editor.runCommand(CoreEditingCommands.Undo, null);3710const afterVersion = model.getVersionId();3711const afterAltVersion = model.getAlternativeVersionId();37123713assert.notStrictEqual(beforeVersion, afterVersion);3714assert.strictEqual(beforeAltVersion, afterAltVersion);3715});3716});37173718test('Enter honors increaseIndentPattern', () => {3719usingCursor({3720text: [3721'if (true) {',3722'\tif (true) {'3723],3724languageId: indentRulesLanguageId,3725modelOpts: { insertSpaces: false },3726editorOpts: { autoIndent: 'full' }3727}, (editor, model, viewModel) => {3728moveTo(editor, viewModel, 1, 12, false);3729assertCursor(viewModel, new Selection(1, 12, 1, 12));37303731viewModel.type('\n', 'keyboard');3732model.tokenization.forceTokenization(model.getLineCount());3733assertCursor(viewModel, new Selection(2, 2, 2, 2));37343735moveTo(editor, viewModel, 3, 13, false);3736assertCursor(viewModel, new Selection(3, 13, 3, 13));37373738viewModel.type('\n', 'keyboard');3739assertCursor(viewModel, new Selection(4, 3, 4, 3));3740});3741});37423743test('Type honors decreaseIndentPattern', () => {3744usingCursor({3745text: [3746'if (true) {',3747'\t'3748],3749languageId: indentRulesLanguageId,3750editorOpts: { autoIndent: 'full' }3751}, (editor, model, viewModel) => {3752moveTo(editor, viewModel, 2, 2, false);3753assertCursor(viewModel, new Selection(2, 2, 2, 2));37543755viewModel.type('}', 'keyboard');3756assertCursor(viewModel, new Selection(2, 2, 2, 2));3757assert.strictEqual(model.getLineContent(2), '}', '001');3758});3759});37603761test('Enter honors unIndentedLinePattern', () => {3762usingCursor({3763text: [3764'if (true) {',3765'\t\t\treturn true'3766],3767languageId: indentRulesLanguageId,3768modelOpts: { insertSpaces: false },3769editorOpts: { autoIndent: 'full' }3770}, (editor, model, viewModel) => {3771moveTo(editor, viewModel, 2, 15, false);3772assertCursor(viewModel, new Selection(2, 15, 2, 15));37733774viewModel.type('\n', 'keyboard');3775assertCursor(viewModel, new Selection(3, 2, 3, 2));3776});3777});37783779test('Enter honors indentNextLinePattern', () => {3780usingCursor({3781text: [3782'if (true)',3783'\treturn true;',3784'if (true)',3785'\t\t\t\treturn true'3786],3787languageId: indentRulesLanguageId,3788modelOpts: { insertSpaces: false },3789editorOpts: { autoIndent: 'full' }3790}, (editor, model, viewModel) => {3791moveTo(editor, viewModel, 2, 14, false);3792assertCursor(viewModel, new Selection(2, 14, 2, 14));37933794viewModel.type('\n', 'keyboard');3795model.tokenization.forceTokenization(model.getLineCount());3796assertCursor(viewModel, new Selection(3, 1, 3, 1));37973798moveTo(editor, viewModel, 5, 16, false);3799assertCursor(viewModel, new Selection(5, 16, 5, 16));38003801viewModel.type('\n', 'keyboard');3802assertCursor(viewModel, new Selection(6, 2, 6, 2));3803});3804});38053806test('Enter honors indentNextLinePattern 2', () => {3807const model = createTextModel(3808[3809'if (true)',3810'\tif (true)'3811].join('\n'),3812indentRulesLanguageId,3813{3814insertSpaces: false,3815}3816);38173818withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {3819moveTo(editor, viewModel, 2, 11, false);3820assertCursor(viewModel, new Selection(2, 11, 2, 11));38213822viewModel.type('\n', 'keyboard');3823model.tokenization.forceTokenization(model.getLineCount());3824assertCursor(viewModel, new Selection(3, 3, 3, 3));38253826viewModel.type('console.log();', 'keyboard');3827viewModel.type('\n', 'keyboard');3828assertCursor(viewModel, new Selection(4, 1, 4, 1));3829});3830});38313832test('Enter honors intential indent', () => {3833usingCursor({3834text: [3835'if (true) {',3836'\tif (true) {',3837'return true;',3838'}}'3839],3840languageId: indentRulesLanguageId,3841editorOpts: { autoIndent: 'full' }3842}, (editor, model, viewModel) => {3843moveTo(editor, viewModel, 3, 13, false);3844assertCursor(viewModel, new Selection(3, 13, 3, 13));38453846viewModel.type('\n', 'keyboard');3847assertCursor(viewModel, new Selection(4, 1, 4, 1));3848assert.strictEqual(model.getLineContent(3), 'return true;', '001');3849});3850});38513852test('Enter supports selection 1', () => {3853usingCursor({3854text: [3855'if (true) {',3856'\tif (true) {',3857'\t\treturn true;',3858'\t}a}'3859],3860languageId: indentRulesLanguageId,3861modelOpts: { insertSpaces: false }3862}, (editor, model, viewModel) => {3863moveTo(editor, viewModel, 4, 3, false);3864moveTo(editor, viewModel, 4, 4, true);3865assertCursor(viewModel, new Selection(4, 3, 4, 4));38663867viewModel.type('\n', 'keyboard');3868assertCursor(viewModel, new Selection(5, 1, 5, 1));3869assert.strictEqual(model.getLineContent(4), '\t}', '001');3870});3871});38723873test('Enter supports selection 2', () => {3874usingCursor({3875text: [3876'if (true) {',3877'\tif (true) {'3878],3879languageId: indentRulesLanguageId,3880modelOpts: { insertSpaces: false }3881}, (editor, model, viewModel) => {3882moveTo(editor, viewModel, 2, 12, false);3883moveTo(editor, viewModel, 2, 13, true);3884assertCursor(viewModel, new Selection(2, 12, 2, 13));38853886viewModel.type('\n', 'keyboard');3887assertCursor(viewModel, new Selection(3, 3, 3, 3));38883889viewModel.type('\n', 'keyboard');3890assertCursor(viewModel, new Selection(4, 3, 4, 3));3891});3892});38933894test('Enter honors tabSize and insertSpaces 1', () => {3895usingCursor({3896text: [3897'if (true) {',3898'\tif (true) {'3899],3900languageId: indentRulesLanguageId,3901}, (editor, model, viewModel) => {3902moveTo(editor, viewModel, 1, 12, false);3903assertCursor(viewModel, new Selection(1, 12, 1, 12));39043905viewModel.type('\n', 'keyboard');3906assertCursor(viewModel, new Selection(2, 5, 2, 5));39073908model.tokenization.forceTokenization(model.getLineCount());39093910moveTo(editor, viewModel, 3, 13, false);3911assertCursor(viewModel, new Selection(3, 13, 3, 13));39123913viewModel.type('\n', 'keyboard');3914assertCursor(viewModel, new Selection(4, 9, 4, 9));3915});3916});39173918test('Enter honors tabSize and insertSpaces 2', () => {3919usingCursor({3920text: [3921'if (true) {',3922' if (true) {'3923],3924languageId: indentRulesLanguageId,3925}, (editor, model, viewModel) => {3926moveTo(editor, viewModel, 1, 12, false);3927assertCursor(viewModel, new Selection(1, 12, 1, 12));39283929viewModel.type('\n', 'keyboard');3930model.tokenization.forceTokenization(model.getLineCount());3931assertCursor(viewModel, new Selection(2, 5, 2, 5));39323933moveTo(editor, viewModel, 3, 16, false);3934assertCursor(viewModel, new Selection(3, 16, 3, 16));39353936viewModel.type('\n', 'keyboard');3937assert.strictEqual(model.getLineContent(3), ' if (true) {');3938assertCursor(viewModel, new Selection(4, 9, 4, 9));3939});3940});39413942test('Enter honors tabSize and insertSpaces 3', () => {3943usingCursor({3944text: [3945'if (true) {',3946' if (true) {'3947],3948languageId: indentRulesLanguageId,3949modelOpts: { insertSpaces: false }3950}, (editor, model, viewModel) => {3951moveTo(editor, viewModel, 1, 12, false);3952assertCursor(viewModel, new Selection(1, 12, 1, 12));39533954viewModel.type('\n', 'keyboard');3955model.tokenization.forceTokenization(model.getLineCount());3956assertCursor(viewModel, new Selection(2, 2, 2, 2));39573958moveTo(editor, viewModel, 3, 16, false);3959assertCursor(viewModel, new Selection(3, 16, 3, 16));39603961viewModel.type('\n', 'keyboard');3962assert.strictEqual(model.getLineContent(3), ' if (true) {');3963assertCursor(viewModel, new Selection(4, 3, 4, 3));3964});3965});39663967test('Enter supports intentional indentation', () => {3968usingCursor({3969text: [3970'\tif (true) {',3971'\t\tswitch(true) {',3972'\t\t\tcase true:',3973'\t\t\t\tbreak;',3974'\t\t}',3975'\t}'3976],3977languageId: indentRulesLanguageId,3978modelOpts: { insertSpaces: false },3979editorOpts: { autoIndent: 'full' }3980}, (editor, model, viewModel) => {3981moveTo(editor, viewModel, 5, 4, false);3982assertCursor(viewModel, new Selection(5, 4, 5, 4));39833984viewModel.type('\n', 'keyboard');3985assert.strictEqual(model.getLineContent(5), '\t\t}');3986assertCursor(viewModel, new Selection(6, 3, 6, 3));3987});3988});39893990test('Enter should not adjust cursor position when press enter in the middle of a line 1', () => {3991usingCursor({3992text: [3993'if (true) {',3994'\tif (true) {',3995'\t\treturn true;',3996'\t}a}'3997],3998languageId: indentRulesLanguageId,3999modelOpts: { insertSpaces: false }4000}, (editor, model, viewModel) => {4001moveTo(editor, viewModel, 3, 9, false);4002assertCursor(viewModel, new Selection(3, 9, 3, 9));40034004viewModel.type('\n', 'keyboard');4005assertCursor(viewModel, new Selection(4, 3, 4, 3));4006assert.strictEqual(model.getLineContent(4), '\t\t true;', '001');4007});4008});40094010test('Enter should not adjust cursor position when press enter in the middle of a line 2', () => {4011usingCursor({4012text: [4013'if (true) {',4014'\tif (true) {',4015'\t\treturn true;',4016'\t}a}'4017],4018languageId: indentRulesLanguageId,4019modelOpts: { insertSpaces: false }4020}, (editor, model, viewModel) => {4021moveTo(editor, viewModel, 3, 3, false);4022assertCursor(viewModel, new Selection(3, 3, 3, 3));40234024viewModel.type('\n', 'keyboard');4025assertCursor(viewModel, new Selection(4, 3, 4, 3));4026assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');4027});4028});40294030test('Enter should not adjust cursor position when press enter in the middle of a line 3', () => {4031usingCursor({4032text: [4033'if (true) {',4034' if (true) {',4035' return true;',4036' }a}'4037],4038languageId: indentRulesLanguageId4039}, (editor, model, viewModel) => {4040moveTo(editor, viewModel, 3, 11, false);4041assertCursor(viewModel, new Selection(3, 11, 3, 11));40424043viewModel.type('\n', 'keyboard');4044assertCursor(viewModel, new Selection(4, 5, 4, 5));4045assert.strictEqual(model.getLineContent(4), ' true;', '001');4046});4047});40484049test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 1', () => {4050usingCursor({4051text: [4052'if (true) {',4053'\tif (true) {',4054'\t\treturn true;',4055'\t}a}'4056],4057languageId: indentRulesLanguageId,4058modelOpts: { insertSpaces: false }4059}, (editor, model, viewModel) => {4060moveTo(editor, viewModel, 3, 2, false);4061assertCursor(viewModel, new Selection(3, 2, 3, 2));40624063viewModel.type('\n', 'keyboard');4064assertCursor(viewModel, new Selection(4, 2, 4, 2));4065assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');40664067moveTo(editor, viewModel, 4, 1, false);4068assertCursor(viewModel, new Selection(4, 1, 4, 1));40694070viewModel.type('\n', 'keyboard');4071assertCursor(viewModel, new Selection(5, 1, 5, 1));4072assert.strictEqual(model.getLineContent(5), '\t\treturn true;', '002');4073});4074});40754076test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 2', () => {4077usingCursor({4078text: [4079'\tif (true) {',4080'\t\tif (true) {',4081'\t \treturn true;',4082'\t\t}a}'4083],4084languageId: indentRulesLanguageId,4085modelOpts: { insertSpaces: false }4086}, (editor, model, viewModel) => {4087moveTo(editor, viewModel, 3, 4, false);4088assertCursor(viewModel, new Selection(3, 4, 3, 4));40894090viewModel.type('\n', 'keyboard');4091assertCursor(viewModel, new Selection(4, 3, 4, 3));4092assert.strictEqual(model.getLineContent(4), '\t\t\treturn true;', '001');40934094moveTo(editor, viewModel, 4, 1, false);4095assertCursor(viewModel, new Selection(4, 1, 4, 1));40964097viewModel.type('\n', 'keyboard');4098assertCursor(viewModel, new Selection(5, 1, 5, 1));4099assert.strictEqual(model.getLineContent(5), '\t\t\treturn true;', '002');4100});4101});41024103test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 3', () => {4104usingCursor({4105text: [4106'if (true) {',4107' if (true) {',4108' return true;',4109'}a}'4110],4111languageId: indentRulesLanguageId4112}, (editor, model, viewModel) => {4113moveTo(editor, viewModel, 3, 2, false);4114assertCursor(viewModel, new Selection(3, 2, 3, 2));41154116viewModel.type('\n', 'keyboard');4117assertCursor(viewModel, new Selection(4, 2, 4, 2));4118assert.strictEqual(model.getLineContent(4), ' return true;', '001');41194120moveTo(editor, viewModel, 4, 3, false);4121viewModel.type('\n', 'keyboard');4122assertCursor(viewModel, new Selection(5, 3, 5, 3));4123assert.strictEqual(model.getLineContent(5), ' return true;', '002');4124});4125});41264127test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 4', () => {4128usingCursor({4129text: [4130'if (true) {',4131' if (true) {',4132'\t return true;',4133'}a}',4134'',4135'if (true) {',4136' if (true) {',4137'\t return true;',4138'}a}'4139],4140languageId: indentRulesLanguageId,4141modelOpts: {4142tabSize: 2,4143indentSize: 24144}4145}, (editor, model, viewModel) => {4146moveTo(editor, viewModel, 3, 3, false);4147assertCursor(viewModel, new Selection(3, 3, 3, 3));41484149viewModel.type('\n', 'keyboard');4150assertCursor(viewModel, new Selection(4, 4, 4, 4));4151assert.strictEqual(model.getLineContent(4), ' return true;', '001');41524153moveTo(editor, viewModel, 9, 4, false);4154viewModel.type('\n', 'keyboard');4155assertCursor(viewModel, new Selection(10, 5, 10, 5));4156assert.strictEqual(model.getLineContent(10), ' return true;', '001');4157});4158});41594160test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 5', () => {4161usingCursor({4162text: [4163'if (true) {',4164' if (true) {',4165' return true;',4166' return true;',4167''4168],4169languageId: indentRulesLanguageId,4170modelOpts: { tabSize: 2 }4171}, (editor, model, viewModel) => {4172moveTo(editor, viewModel, 3, 5, false);4173moveTo(editor, viewModel, 4, 3, true);4174assertCursor(viewModel, new Selection(3, 5, 4, 3));41754176viewModel.type('\n', 'keyboard');4177assertCursor(viewModel, new Selection(4, 3, 4, 3));4178assert.strictEqual(model.getLineContent(4), ' return true;', '001');4179});4180});41814182test('issue microsoft/monaco-editor#108 part 1/2: Auto indentation on Enter with selection is half broken', () => {4183usingCursor({4184text: [4185'function baz() {',4186'\tvar x = 1;',4187'\t\t\t\t\t\t\treturn x;',4188'}'4189],4190modelOpts: {4191insertSpaces: false,4192},4193languageId: indentRulesLanguageId,4194}, (editor, model, viewModel) => {4195moveTo(editor, viewModel, 3, 8, false);4196moveTo(editor, viewModel, 2, 12, true);4197assertCursor(viewModel, new Selection(3, 8, 2, 12));41984199viewModel.type('\n', 'keyboard');4200assert.strictEqual(model.getLineContent(3), '\treturn x;');4201assertCursor(viewModel, new Position(3, 2));4202});4203});42044205test('issue microsoft/monaco-editor#108 part 2/2: Auto indentation on Enter with selection is half broken', () => {4206usingCursor({4207text: [4208'function baz() {',4209'\tvar x = 1;',4210'\t\t\t\t\t\t\treturn x;',4211'}'4212],4213modelOpts: {4214insertSpaces: false,4215},4216languageId: indentRulesLanguageId,4217}, (editor, model, viewModel) => {4218moveTo(editor, viewModel, 2, 12, false);4219moveTo(editor, viewModel, 3, 8, true);4220assertCursor(viewModel, new Selection(2, 12, 3, 8));42214222viewModel.type('\n', 'keyboard');4223assert.strictEqual(model.getLineContent(3), '\treturn x;');4224assertCursor(viewModel, new Position(3, 2));4225});4226});42274228test('onEnter works if there are no indentation rules', () => {4229usingCursor({4230text: [4231'<?',4232'\tif (true) {',4233'\t\techo $hi;',4234'\t\techo $bye;',4235'\t}',4236'?>'4237],4238modelOpts: { insertSpaces: false }4239}, (editor, model, viewModel) => {4240moveTo(editor, viewModel, 5, 3, false);4241assertCursor(viewModel, new Selection(5, 3, 5, 3));42424243viewModel.type('\n', 'keyboard');4244assert.strictEqual(model.getLineContent(6), '\t');4245assertCursor(viewModel, new Selection(6, 2, 6, 2));4246assert.strictEqual(model.getLineContent(5), '\t}');4247});4248});42494250test('onEnter works if there are no indentation rules 2', () => {4251usingCursor({4252text: [4253' if (5)',4254' return 5;',4255' '4256],4257modelOpts: { insertSpaces: false }4258}, (editor, model, viewModel) => {4259moveTo(editor, viewModel, 3, 2, false);4260assertCursor(viewModel, new Selection(3, 2, 3, 2));42614262viewModel.type('\n', 'keyboard');4263assertCursor(viewModel, new Selection(4, 2, 4, 2));4264assert.strictEqual(model.getLineContent(4), '\t');4265});4266});42674268test('bug #16543: Tab should indent to correct indentation spot immediately', () => {4269const model = createTextModel(4270[4271'function baz() {',4272'\tfunction hello() { // something here',4273'\t',4274'',4275'\t}',4276'}'4277].join('\n'),4278indentRulesLanguageId,4279{4280insertSpaces: false,4281}4282);42834284withTestCodeEditor(model, {}, (editor, viewModel) => {4285moveTo(editor, viewModel, 4, 1, false);4286assertCursor(viewModel, new Selection(4, 1, 4, 1));42874288editor.runCommand(CoreEditingCommands.Tab, null);4289assert.strictEqual(model.getLineContent(4), '\t\t');4290});4291});429242934294test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4295const model = createTextModel(4296[4297'\tfunction baz() {',4298'\t\tfunction hello() { // something here',4299'\t\t',4300'\t',4301'\t\t}',4302'\t}'4303].join('\n'),4304indentRulesLanguageId,4305{4306insertSpaces: false,4307}4308);43094310withTestCodeEditor(model, {}, (editor, viewModel) => {4311moveTo(editor, viewModel, 4, 2, false);4312assertCursor(viewModel, new Selection(4, 2, 4, 2));43134314editor.runCommand(CoreEditingCommands.Tab, null);4315assert.strictEqual(model.getLineContent(4), '\t\t\t');4316});4317});431843194320test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4321const model = createTextModel(4322[4323'\tfunction baz() {',4324'\t\tfunction hello() { // something here',4325'\t\t',4326' ',4327'\t\t}',4328'\t}'4329].join('\n'),4330indentRulesLanguageId,4331{4332insertSpaces: false,4333}4334);43354336withTestCodeEditor(model, {}, (editor, viewModel) => {4337moveTo(editor, viewModel, 4, 1, false);4338assertCursor(viewModel, new Selection(4, 1, 4, 1));43394340editor.runCommand(CoreEditingCommands.Tab, null);4341assert.strictEqual(model.getLineContent(4), '\t\t\t');4342});4343});43444345test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4346const model = createTextModel(4347[4348'\tfunction baz() {',4349'\t\tfunction hello() { // something here',4350'\t\t',4351'\t\t\t',4352'\t\t}',4353'\t}'4354].join('\n'),4355indentRulesLanguageId,4356{4357insertSpaces: false,4358}4359);43604361withTestCodeEditor(model, {}, (editor, viewModel) => {4362moveTo(editor, viewModel, 4, 3, false);4363assertCursor(viewModel, new Selection(4, 3, 4, 3));43644365editor.runCommand(CoreEditingCommands.Tab, null);4366assert.strictEqual(model.getLineContent(4), '\t\t\t\t');4367});4368});43694370test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4371const model = createTextModel(4372[4373'\tfunction baz() {',4374'\t\tfunction hello() { // something here',4375'\t\t',4376'\t\t\t\t',4377'\t\t}',4378'\t}'4379].join('\n'),4380indentRulesLanguageId,4381{4382insertSpaces: false,4383}4384);43854386withTestCodeEditor(model, {}, (editor, viewModel) => {4387moveTo(editor, viewModel, 4, 4, false);4388assertCursor(viewModel, new Selection(4, 4, 4, 4));43894390editor.runCommand(CoreEditingCommands.Tab, null);4391assert.strictEqual(model.getLineContent(4), '\t\t\t\t\t');4392});4393});43944395test('bug #31015: When pressing Tab on lines and Enter rules are avail, indent straight to the right spotTab', () => {4396const onEnterLanguageId = setupOnEnterLanguage(IndentAction.Indent);4397const model = createTextModel(4398[4399' if (a) {',4400' ',4401'',4402'',4403' }'4404].join('\n'),4405onEnterLanguageId4406);44074408withTestCodeEditor(model, {}, (editor, viewModel) => {44094410moveTo(editor, viewModel, 3, 1);4411editor.runCommand(CoreEditingCommands.Tab, null);4412assert.strictEqual(model.getLineContent(1), ' if (a) {');4413assert.strictEqual(model.getLineContent(2), ' ');4414assert.strictEqual(model.getLineContent(3), ' ');4415assert.strictEqual(model.getLineContent(4), '');4416assert.strictEqual(model.getLineContent(5), ' }');4417});4418});44194420test('type honors indentation rules: ruby keywords', () => {4421const rubyLanguageId = setupIndentRulesLanguage('ruby', {4422increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/,4423decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/4424});4425const model = createTextModel(4426[4427'class Greeter',4428' def initialize(name)',4429' @name = name',4430' en'4431].join('\n'),4432rubyLanguageId4433);44344435withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4436moveTo(editor, viewModel, 4, 7, false);4437assertCursor(viewModel, new Selection(4, 7, 4, 7));44384439viewModel.type('d', 'keyboard');4440assert.strictEqual(model.getLineContent(4), ' end');4441});4442});44434444test('Auto indent on type: increaseIndentPattern has higher priority than decreaseIndent when inheriting', () => {4445usingCursor({4446text: [4447'\tif (true) {',4448'\t\tconsole.log();',4449'\t} else if {',4450'\t\tconsole.log()',4451'\t}'4452],4453languageId: indentRulesLanguageId4454}, (editor, model, viewModel) => {4455moveTo(editor, viewModel, 5, 3, false);4456assertCursor(viewModel, new Selection(5, 3, 5, 3));44574458viewModel.type('e', 'keyboard');4459assertCursor(viewModel, new Selection(5, 4, 5, 4));4460assert.strictEqual(model.getLineContent(5), '\t}e', 'This line should not decrease indent');4461});4462});44634464test('type honors users indentation adjustment', () => {4465usingCursor({4466text: [4467'\tif (true ||',4468'\t ) {',4469'\t}',4470'if (true ||',4471') {',4472'}'4473],4474languageId: indentRulesLanguageId4475}, (editor, model, viewModel) => {4476moveTo(editor, viewModel, 2, 3, false);4477assertCursor(viewModel, new Selection(2, 3, 2, 3));44784479viewModel.type(' ', 'keyboard');4480assertCursor(viewModel, new Selection(2, 4, 2, 4));4481assert.strictEqual(model.getLineContent(2), '\t ) {', 'This line should not decrease indent');4482});4483});44844485test('bug 29972: if a line is line comment, open bracket should not indent next line', () => {4486usingCursor({4487text: [4488'if (true) {',4489'\t// {',4490'\t\t'4491],4492languageId: indentRulesLanguageId,4493editorOpts: { autoIndent: 'full' }4494}, (editor, model, viewModel) => {4495moveTo(editor, viewModel, 3, 3, false);4496assertCursor(viewModel, new Selection(3, 3, 3, 3));44974498viewModel.type('}', 'keyboard');4499assertCursor(viewModel, new Selection(3, 2, 3, 2));4500assert.strictEqual(model.getLineContent(3), '}');4501});4502});450345044505test('issue #38261: TAB key results in bizarre indentation in C++ mode ', () => {4506const languageId = 'indentRulesMode';45074508disposables.add(languageService.registerLanguage({ id: languageId }));4509disposables.add(languageConfigurationService.register(languageId, {4510brackets: [4511['{', '}'],4512['[', ']'],4513['(', ')']4514],4515indentationRules: {4516increaseIndentPattern: new RegExp("(^.*\\{[^}]*$)"),4517decreaseIndentPattern: new RegExp("^\\s*\\}")4518}4519}));45204521const model = createTextModel(4522[4523'int main() {',4524' return 0;',4525'}',4526'',4527'bool Foo::bar(const string &a,',4528' const string &b) {',4529' foo();',4530'',4531')',4532].join('\n'),4533languageId,4534{4535tabSize: 2,4536indentSize: 24537}4538);45394540withTestCodeEditor(model, { autoIndent: 'advanced' }, (editor, viewModel) => {4541moveTo(editor, viewModel, 8, 1, false);4542assertCursor(viewModel, new Selection(8, 1, 8, 1));45434544editor.runCommand(CoreEditingCommands.Tab, null);4545assert.strictEqual(model.getValue(),4546[4547'int main() {',4548' return 0;',4549'}',4550'',4551'bool Foo::bar(const string &a,',4552' const string &b) {',4553' foo();',4554' ',4555')',4556].join('\n')4557);4558assert.deepStrictEqual(viewModel.getSelection(), new Selection(8, 3, 8, 3));4559});4560});45614562test('issue #57197: indent rules regex should be stateless', () => {4563const languageId = setupIndentRulesLanguage('lang', {4564decreaseIndentPattern: /^\s*}$/gm,4565increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■⬜□☐▪▫–—≡→›✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm,4566});4567usingCursor({4568text: [4569'Project:',4570],4571languageId: languageId,4572modelOpts: { insertSpaces: false },4573editorOpts: { autoIndent: 'full' }4574}, (editor, model, viewModel) => {4575moveTo(editor, viewModel, 1, 9, false);4576assertCursor(viewModel, new Selection(1, 9, 1, 9));45774578viewModel.type('\n', 'keyboard');4579model.tokenization.forceTokenization(model.getLineCount());4580assertCursor(viewModel, new Selection(2, 2, 2, 2));45814582moveTo(editor, viewModel, 1, 9, false);4583assertCursor(viewModel, new Selection(1, 9, 1, 9));4584viewModel.type('\n', 'keyboard');4585model.tokenization.forceTokenization(model.getLineCount());4586assertCursor(viewModel, new Selection(2, 2, 2, 2));4587});4588});45894590test('typing in json', () => {4591const languageId = 'indentRulesMode';45924593disposables.add(languageService.registerLanguage({ id: languageId }));4594disposables.add(languageConfigurationService.register(languageId, {4595brackets: [4596['{', '}'],4597['[', ']'],4598['(', ')']4599],4600indentationRules: {4601increaseIndentPattern: new RegExp("({+(?=([^\"]*\"[^\"]*\")*[^\"}]*$))|(\\[+(?=([^\"]*\"[^\"]*\")*[^\"\\]]*$))"),4602decreaseIndentPattern: new RegExp("^\\s*[}\\]],?\\s*$")4603}4604}));46054606const model = createTextModel(4607[4608'{',4609' "scripts: {"',4610' "watch": "a {"',4611' "build{": "b"',4612' "tasks": []',4613' "tasks": ["a"]',4614' "}"',4615'"}"'4616].join('\n'),4617languageId,4618{4619tabSize: 2,4620indentSize: 24621}4622);46234624withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4625moveTo(editor, viewModel, 3, 19, false);4626assertCursor(viewModel, new Selection(3, 19, 3, 19));46274628viewModel.type('\n', 'keyboard');4629assert.deepStrictEqual(model.getLineContent(4), ' ');46304631moveTo(editor, viewModel, 5, 18, false);4632assertCursor(viewModel, new Selection(5, 18, 5, 18));46334634viewModel.type('\n', 'keyboard');4635assert.deepStrictEqual(model.getLineContent(6), ' ');46364637moveTo(editor, viewModel, 7, 15, false);4638assertCursor(viewModel, new Selection(7, 15, 7, 15));46394640viewModel.type('\n', 'keyboard');4641assert.deepStrictEqual(model.getLineContent(8), ' ');4642assert.deepStrictEqual(model.getLineContent(9), ' ]');46434644moveTo(editor, viewModel, 10, 18, false);4645assertCursor(viewModel, new Selection(10, 18, 10, 18));46464647viewModel.type('\n', 'keyboard');4648assert.deepStrictEqual(model.getLineContent(11), ' ]');4649});4650});46514652test('issue #111128: Multicursor `Enter` issue with indentation', () => {4653const model = createTextModel(' let a, b, c;', indentRulesLanguageId, { detectIndentation: false, insertSpaces: false, tabSize: 4 });4654withTestCodeEditor(model, {}, (editor, viewModel) => {4655editor.setSelections([4656new Selection(1, 11, 1, 11),4657new Selection(1, 14, 1, 14),4658]);4659viewModel.type('\n', 'keyboard');4660assert.strictEqual(model.getValue(), ' let a,\n\t b,\n\t c;');4661});4662});46634664test('issue #122714: tabSize=1 prevent typing a string matching decreaseIndentPattern in an empty file', () => {4665const latextLanguageId = setupIndentRulesLanguage('latex', {4666increaseIndentPattern: new RegExp('\\\\begin{(?!document)([^}]*)}(?!.*\\\\end{\\1})'),4667decreaseIndentPattern: new RegExp('^\\s*\\\\end{(?!document)')4668});4669const model = createTextModel(4670'\\end',4671latextLanguageId,4672{ tabSize: 1 }4673);46744675withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4676moveTo(editor, viewModel, 1, 5, false);4677assertCursor(viewModel, new Selection(1, 5, 1, 5));46784679viewModel.type('{', 'keyboard');4680assert.strictEqual(model.getLineContent(1), '\\end{}');4681});4682});46834684test('ElectricCharacter - does nothing if no electric char', () => {4685usingCursor({4686text: [4687' if (a) {',4688''4689],4690languageId: electricCharLanguageId4691}, (editor, model, viewModel) => {4692moveTo(editor, viewModel, 2, 1);4693viewModel.type('*', 'keyboard');4694assert.deepStrictEqual(model.getLineContent(2), '*');4695});4696});46974698test('ElectricCharacter - indents in order to match bracket', () => {4699usingCursor({4700text: [4701' if (a) {',4702''4703],4704languageId: electricCharLanguageId4705}, (editor, model, viewModel) => {4706moveTo(editor, viewModel, 2, 1);4707viewModel.type('}', 'keyboard');4708assert.deepStrictEqual(model.getLineContent(2), ' }');4709});4710});47114712test('ElectricCharacter - unindents in order to match bracket', () => {4713usingCursor({4714text: [4715' if (a) {',4716' '4717],4718languageId: electricCharLanguageId4719}, (editor, model, viewModel) => {4720moveTo(editor, viewModel, 2, 5);4721viewModel.type('}', 'keyboard');4722assert.deepStrictEqual(model.getLineContent(2), ' }');4723});4724});47254726test('ElectricCharacter - matches with correct bracket', () => {4727usingCursor({4728text: [4729' if (a) {',4730' if (b) {',4731' }',4732' '4733],4734languageId: electricCharLanguageId4735}, (editor, model, viewModel) => {4736moveTo(editor, viewModel, 4, 1);4737viewModel.type('}', 'keyboard');4738assert.deepStrictEqual(model.getLineContent(4), ' } ');4739});4740});47414742test('ElectricCharacter - does nothing if bracket does not match', () => {4743usingCursor({4744text: [4745' if (a) {',4746' if (b) {',4747' }',4748' } '4749],4750languageId: electricCharLanguageId4751}, (editor, model, viewModel) => {4752moveTo(editor, viewModel, 4, 6);4753viewModel.type('}', 'keyboard');4754assert.deepStrictEqual(model.getLineContent(4), ' } }');4755});4756});47574758test('ElectricCharacter - matches bracket even in line with content', () => {4759usingCursor({4760text: [4761' if (a) {',4762'// hello'4763],4764languageId: electricCharLanguageId4765}, (editor, model, viewModel) => {4766moveTo(editor, viewModel, 2, 1);4767viewModel.type('}', 'keyboard');4768assert.deepStrictEqual(model.getLineContent(2), ' }// hello');4769});4770});47714772test('ElectricCharacter - is no-op if bracket is lined up', () => {4773usingCursor({4774text: [4775' if (a) {',4776' '4777],4778languageId: electricCharLanguageId4779}, (editor, model, viewModel) => {4780moveTo(editor, viewModel, 2, 3);4781viewModel.type('}', 'keyboard');4782assert.deepStrictEqual(model.getLineContent(2), ' }');4783});4784});47854786test('ElectricCharacter - is no-op if there is non-whitespace text before', () => {4787usingCursor({4788text: [4789' if (a) {',4790'a'4791],4792languageId: electricCharLanguageId4793}, (editor, model, viewModel) => {4794moveTo(editor, viewModel, 2, 2);4795viewModel.type('}', 'keyboard');4796assert.deepStrictEqual(model.getLineContent(2), 'a}');4797});4798});47994800test('ElectricCharacter - is no-op if pairs are all matched before', () => {4801usingCursor({4802text: [4803'foo(() => {',4804' ( 1 + 2 ) ',4805'})'4806],4807languageId: electricCharLanguageId4808}, (editor, model, viewModel) => {4809moveTo(editor, viewModel, 2, 13);4810viewModel.type('*', 'keyboard');4811assert.deepStrictEqual(model.getLineContent(2), ' ( 1 + 2 ) *');4812});4813});48144815test('ElectricCharacter - is no-op if matching bracket is on the same line', () => {4816usingCursor({4817text: [4818'(div',4819],4820languageId: electricCharLanguageId4821}, (editor, model, viewModel) => {4822moveTo(editor, viewModel, 1, 5);4823let changeText: string | null = null;4824const disposable = model.onDidChangeContent(e => {4825changeText = e.changes[0].text;4826});4827viewModel.type(')', 'keyboard');4828assert.deepStrictEqual(model.getLineContent(1), '(div)');4829assert.deepStrictEqual(changeText, ')');4830disposable.dispose();4831});4832});48334834test('ElectricCharacter - is no-op if the line has other content', () => {4835usingCursor({4836text: [4837'Math.max(',4838'\t2',4839'\t3'4840],4841languageId: electricCharLanguageId4842}, (editor, model, viewModel) => {4843moveTo(editor, viewModel, 3, 3);4844viewModel.type(')', 'keyboard');4845assert.deepStrictEqual(model.getLineContent(3), '\t3)');4846});4847});48484849test('ElectricCharacter - appends text', () => {4850usingCursor({4851text: [4852' if (a) {',4853'/*'4854],4855languageId: electricCharLanguageId4856}, (editor, model, viewModel) => {4857moveTo(editor, viewModel, 2, 3);4858viewModel.type('*', 'keyboard');4859assert.deepStrictEqual(model.getLineContent(2), '/** */');4860});4861});48624863test('ElectricCharacter - appends text 2', () => {4864usingCursor({4865text: [4866' if (a) {',4867' /*'4868],4869languageId: electricCharLanguageId4870}, (editor, model, viewModel) => {4871moveTo(editor, viewModel, 2, 5);4872viewModel.type('*', 'keyboard');4873assert.deepStrictEqual(model.getLineContent(2), ' /** */');4874});4875});48764877test('ElectricCharacter - issue #23711: Replacing selected text with )]} fails to delete old text with backwards-dragged selection', () => {4878usingCursor({4879text: [4880'{',4881'word'4882],4883languageId: electricCharLanguageId4884}, (editor, model, viewModel) => {4885moveTo(editor, viewModel, 2, 5);4886moveTo(editor, viewModel, 2, 1, true);4887viewModel.type('}', 'keyboard');4888assert.deepStrictEqual(model.getLineContent(2), '}');4889});4890});48914892test('issue #61070: backtick (`) should auto-close after a word character', () => {4893usingCursor({4894text: ['const markup = highlight'],4895languageId: autoClosingLanguageId4896}, (editor, model, viewModel) => {4897model.tokenization.forceTokenization(1);4898assertType(editor, model, viewModel, 1, 25, '`', '``', `auto closes \` @ (1, 25)`);4899});4900});49014902test('issue #132912: quotes should not auto-close if they are closing a string', () => {4903setupAutoClosingLanguageTokenization();4904const model = createTextModel('const t2 = `something ${t1}', autoClosingLanguageId);4905withTestCodeEditor(4906model,4907{},4908(editor, viewModel) => {4909const model = viewModel.model;4910model.tokenization.forceTokenization(1);4911assertType(editor, model, viewModel, 1, 28, '`', '`', `does not auto close \` @ (1, 28)`);4912}4913);4914});49154916test('autoClosingPairs - open parens: default', () => {4917usingCursor({4918text: [4919'var a = [];',4920'var b = `asd`;',4921'var c = \'asd\';',4922'var d = "asd";',4923'var e = /*3*/ 3;',4924'var f = /** 3 */3;',4925'var g = (3+5);',4926'var h = { a: \'value\' };',4927],4928languageId: autoClosingLanguageId4929}, (editor, model, viewModel) => {49304931const autoClosePositions = [4932'var| a| |=| [|]|;|',4933'var| b| |=| |`asd|`|;|',4934'var| c| |=| |\'asd|\'|;|',4935'var| d| |=| |"asd|"|;|',4936'var| e| |=| /*3*/| 3|;|',4937'var| f| |=| /**| 3| */3|;|',4938'var| g| |=| (3+5|)|;|',4939'var| h| |=| {| a|:| |\'value|\'| |}|;|',4940];4941for (let i = 0, len = autoClosePositions.length; i < len; i++) {4942const lineNumber = i + 1;4943const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);49444945for (let column = 1; column < autoCloseColumns.length; column++) {4946model.tokenization.forceTokenization(lineNumber);4947if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {4948assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);4949} else {4950assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);4951}4952}4953}4954});4955});49564957test('autoClosingPairs - open parens: whitespace', () => {4958usingCursor({4959text: [4960'var a = [];',4961'var b = `asd`;',4962'var c = \'asd\';',4963'var d = "asd";',4964'var e = /*3*/ 3;',4965'var f = /** 3 */3;',4966'var g = (3+5);',4967'var h = { a: \'value\' };',4968],4969languageId: autoClosingLanguageId,4970editorOpts: {4971autoClosingBrackets: 'beforeWhitespace'4972}4973}, (editor, model, viewModel) => {49744975const autoClosePositions = [4976'var| a| =| [|];|',4977'var| b| =| `asd`;|',4978'var| c| =| \'asd\';|',4979'var| d| =| "asd";|',4980'var| e| =| /*3*/| 3;|',4981'var| f| =| /**| 3| */3;|',4982'var| g| =| (3+5|);|',4983'var| h| =| {| a:| \'value\'| |};|',4984];4985for (let i = 0, len = autoClosePositions.length; i < len; i++) {4986const lineNumber = i + 1;4987const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);49884989for (let column = 1; column < autoCloseColumns.length; column++) {4990model.tokenization.forceTokenization(lineNumber);4991if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {4992assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);4993} else {4994assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);4995}4996}4997}4998});4999});50005001test('autoClosingPairs - open parens disabled/enabled open quotes enabled/disabled', () => {5002usingCursor({5003text: [5004'var a = [];',5005],5006languageId: autoClosingLanguageId,5007editorOpts: {5008autoClosingBrackets: 'beforeWhitespace',5009autoClosingQuotes: 'never'5010}5011}, (editor, model, viewModel) => {50125013const autoClosePositions = [5014'var| a| =| [|];|',5015];5016for (let i = 0, len = autoClosePositions.length; i < len; i++) {5017const lineNumber = i + 1;5018const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50195020for (let column = 1; column < autoCloseColumns.length; column++) {5021model.tokenization.forceTokenization(lineNumber);5022if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5023assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5024} else {5025assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5026}5027assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5028}5029}5030});50315032usingCursor({5033text: [5034'var b = [];',5035],5036languageId: autoClosingLanguageId,5037editorOpts: {5038autoClosingBrackets: 'never',5039autoClosingQuotes: 'beforeWhitespace'5040}5041}, (editor, model, viewModel) => {50425043const autoClosePositions = [5044'var b =| [|];|',5045];5046for (let i = 0, len = autoClosePositions.length; i < len; i++) {5047const lineNumber = i + 1;5048const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50495050for (let column = 1; column < autoCloseColumns.length; column++) {5051model.tokenization.forceTokenization(lineNumber);5052if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5053assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);5054} else {5055assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5056}5057assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5058}5059}5060});5061});50625063test('autoClosingPairs - configurable open parens', () => {5064setAutoClosingLanguageEnabledSet('abc');5065usingCursor({5066text: [5067'var a = [];',5068'var b = `asd`;',5069'var c = \'asd\';',5070'var d = "asd";',5071'var e = /*3*/ 3;',5072'var f = /** 3 */3;',5073'var g = (3+5);',5074'var h = { a: \'value\' };',5075],5076languageId: autoClosingLanguageId,5077editorOpts: {5078autoClosingBrackets: 'languageDefined'5079}5080}, (editor, model, viewModel) => {50815082const autoClosePositions = [5083'v|ar |a = [|];|',5084'v|ar |b = `|asd`;|',5085'v|ar |c = \'|asd\';|',5086'v|ar d = "|asd";|',5087'v|ar e = /*3*/ 3;|',5088'v|ar f = /** 3| */3;|',5089'v|ar g = (3+5|);|',5090'v|ar h = { |a: \'v|alue\' |};|',5091];5092for (let i = 0, len = autoClosePositions.length; i < len; i++) {5093const lineNumber = i + 1;5094const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50955096for (let column = 1; column < autoCloseColumns.length; column++) {5097model.tokenization.forceTokenization(lineNumber);5098if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5099assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5100} else {5101assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5102}5103}5104}5105});5106});51075108test('autoClosingPairs - auto-pairing can be disabled', () => {5109usingCursor({5110text: [5111'var a = [];',5112'var b = `asd`;',5113'var c = \'asd\';',5114'var d = "asd";',5115'var e = /*3*/ 3;',5116'var f = /** 3 */3;',5117'var g = (3+5);',5118'var h = { a: \'value\' };',5119],5120languageId: autoClosingLanguageId,5121editorOpts: {5122autoClosingBrackets: 'never',5123autoClosingQuotes: 'never'5124}5125}, (editor, model, viewModel) => {51265127const autoClosePositions = [5128'var a = [];',5129'var b = `asd`;',5130'var c = \'asd\';',5131'var d = "asd";',5132'var e = /*3*/ 3;',5133'var f = /** 3 */3;',5134'var g = (3+5);',5135'var h = { a: \'value\' };',5136];5137for (let i = 0, len = autoClosePositions.length; i < len; i++) {5138const lineNumber = i + 1;5139const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);51405141for (let column = 1; column < autoCloseColumns.length; column++) {5142model.tokenization.forceTokenization(lineNumber);5143if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5144assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5145assertType(editor, model, viewModel, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`);5146} else {5147assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5148assertType(editor, model, viewModel, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`);5149}5150}5151}5152});5153});51545155test('autoClosingPairs - auto wrapping is configurable', () => {5156usingCursor({5157text: [5158'var a = asd'5159],5160languageId: autoClosingLanguageId5161}, (editor, model, viewModel) => {51625163viewModel.setSelections('test', [5164new Selection(1, 1, 1, 4),5165new Selection(1, 9, 1, 12),5166]);51675168// type a `5169viewModel.type('`', 'keyboard');51705171assert.strictEqual(model.getValue(), '`var` a = `asd`');51725173// type a (5174viewModel.type('(', 'keyboard');51755176assert.strictEqual(model.getValue(), '`(var)` a = `(asd)`');5177});51785179usingCursor({5180text: [5181'var a = asd'5182],5183languageId: autoClosingLanguageId,5184editorOpts: {5185autoSurround: 'never'5186}5187}, (editor, model, viewModel) => {51885189viewModel.setSelections('test', [5190new Selection(1, 1, 1, 4),5191]);51925193// type a `5194viewModel.type('`', 'keyboard');51955196assert.strictEqual(model.getValue(), '` a = asd');5197});51985199usingCursor({5200text: [5201'var a = asd'5202],5203languageId: autoClosingLanguageId,5204editorOpts: {5205autoSurround: 'quotes'5206}5207}, (editor, model, viewModel) => {52085209viewModel.setSelections('test', [5210new Selection(1, 1, 1, 4),5211]);52125213// type a `5214viewModel.type('`', 'keyboard');5215assert.strictEqual(model.getValue(), '`var` a = asd');52165217// type a (5218viewModel.type('(', 'keyboard');5219assert.strictEqual(model.getValue(), '`(` a = asd');5220});52215222usingCursor({5223text: [5224'var a = asd'5225],5226languageId: autoClosingLanguageId,5227editorOpts: {5228autoSurround: 'brackets'5229}5230}, (editor, model, viewModel) => {52315232viewModel.setSelections('test', [5233new Selection(1, 1, 1, 4),5234]);52355236// type a (5237viewModel.type('(', 'keyboard');5238assert.strictEqual(model.getValue(), '(var) a = asd');52395240// type a `5241viewModel.type('`', 'keyboard');5242assert.strictEqual(model.getValue(), '(`) a = asd');5243});5244});52455246test('autoClosingPairs - quote', () => {5247usingCursor({5248text: [5249'var a = [];',5250'var b = `asd`;',5251'var c = \'asd\';',5252'var d = "asd";',5253'var e = /*3*/ 3;',5254'var f = /** 3 */3;',5255'var g = (3+5);',5256'var h = { a: \'value\' };',5257],5258languageId: autoClosingLanguageId5259}, (editor, model, viewModel) => {52605261const autoClosePositions = [5262'var a |=| [|]|;|',5263'var b |=| `asd`|;|',5264'var c |=| \'asd\'|;|',5265'var d |=| "asd"|;|',5266'var e |=| /*3*/| 3;|',5267'var f |=| /**| 3 */3;|',5268'var g |=| (3+5)|;|',5269'var h |=| {| a:| \'value\'| |}|;|',5270];5271for (let i = 0, len = autoClosePositions.length; i < len; i++) {5272const lineNumber = i + 1;5273const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);52745275for (let column = 1; column < autoCloseColumns.length; column++) {5276model.tokenization.forceTokenization(lineNumber);5277if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5278assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);5279} else if (autoCloseColumns[column] === AutoClosingColumnType.Special2) {5280assertType(editor, model, viewModel, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`);5281} else {5282assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5283}5284}5285}5286});5287});52885289test('autoClosingPairs - multi-character autoclose', () => {5290usingCursor({5291text: [5292'',5293],5294languageId: autoClosingLanguageId5295}, (editor, model, viewModel) => {52965297model.setValue('begi');5298viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);5299viewModel.type('n', 'keyboard');5300assert.strictEqual(model.getLineContent(1), 'beginend');53015302model.setValue('/*');5303viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);5304viewModel.type('*', 'keyboard');5305assert.strictEqual(model.getLineContent(1), '/** */');5306});5307});53085309test('autoClosingPairs - doc comments can be turned off', () => {5310usingCursor({5311text: [5312'',5313],5314languageId: autoClosingLanguageId,5315editorOpts: {5316autoClosingComments: 'never'5317}5318}, (editor, model, viewModel) => {53195320model.setValue('/*');5321viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);5322viewModel.type('*', 'keyboard');5323assert.strictEqual(model.getLineContent(1), '/**');5324});5325});53265327test('issue #72177: multi-character autoclose with conflicting patterns', () => {5328const languageId = 'autoClosingModeMultiChar';53295330disposables.add(languageService.registerLanguage({ id: languageId }));5331disposables.add(languageConfigurationService.register(languageId, {5332autoClosingPairs: [5333{ open: '(', close: ')' },5334{ open: '(*', close: '*)' },5335{ open: '<@', close: '@>' },5336{ open: '<@@', close: '@@>' },5337],5338}));53395340usingCursor({5341text: [5342'',5343],5344languageId: languageId5345}, (editor, model, viewModel) => {5346viewModel.type('(', 'keyboard');5347assert.strictEqual(model.getLineContent(1), '()');5348viewModel.type('*', 'keyboard');5349assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`);53505351model.setValue('(');5352viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);5353viewModel.type('*', 'keyboard');5354assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`);53555356model.setValue('');5357viewModel.type('<@', 'keyboard');5358assert.strictEqual(model.getLineContent(1), '<@@>');5359viewModel.type('@', 'keyboard');5360assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`);5361viewModel.type('(', 'keyboard');5362assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`);5363});5364});53655366test('issue #55314: Do not auto-close when ending with open', () => {5367const languageId = 'myElectricMode';53685369disposables.add(languageService.registerLanguage({ id: languageId }));5370disposables.add(languageConfigurationService.register(languageId, {5371autoClosingPairs: [5372{ open: '{', close: '}' },5373{ open: '[', close: ']' },5374{ open: '(', close: ')' },5375{ open: '\'', close: '\'', notIn: ['string', 'comment'] },5376{ open: '\"', close: '\"', notIn: ['string'] },5377{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },5378{ open: '`', close: '`', notIn: ['string', 'comment'] },5379{ open: '/**', close: ' */', notIn: ['string'] }5380],5381}));53825383usingCursor({5384text: [5385'little goat',5386'little LAMB',5387'little sheep',5388'Big LAMB'5389],5390languageId: languageId5391}, (editor, model, viewModel) => {5392model.tokenization.forceTokenization(model.getLineCount());5393assertType(editor, model, viewModel, 1, 4, '"', '"', `does not double quote when ending with open`);5394model.tokenization.forceTokenization(model.getLineCount());5395assertType(editor, model, viewModel, 2, 4, '"', '"', `does not double quote when ending with open`);5396model.tokenization.forceTokenization(model.getLineCount());5397assertType(editor, model, viewModel, 3, 4, '"', '"', `does not double quote when ending with open`);5398model.tokenization.forceTokenization(model.getLineCount());5399assertType(editor, model, viewModel, 4, 2, '"', '"', `does not double quote when ending with open`);5400model.tokenization.forceTokenization(model.getLineCount());5401assertType(editor, model, viewModel, 4, 3, '"', '"', `does not double quote when ending with open`);5402});5403});54045405test('issue #27937: Trying to add an item to the front of a list is cumbersome', () => {5406usingCursor({5407text: [5408'var arr = ["b", "c"];'5409],5410languageId: autoClosingLanguageId5411}, (editor, model, viewModel) => {5412assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`);5413});5414});54155416test('issue #25658 - Do not auto-close single/double quotes after word characters', () => {5417usingCursor({5418text: [5419'',5420],5421languageId: autoClosingLanguageId5422}, (editor, model, viewModel) => {54235424function typeCharacters(viewModel: ViewModel, chars: string): void {5425for (let i = 0, len = chars.length; i < len; i++) {5426viewModel.type(chars[i], 'keyboard');5427}5428}54295430// First gif5431model.tokenization.forceTokenization(model.getLineCount());5432typeCharacters(viewModel, 'teste1 = teste\' ok');5433assert.strictEqual(model.getLineContent(1), 'teste1 = teste\' ok');54345435viewModel.setSelections('test', [new Selection(1, 1000, 1, 1000)]);5436typeCharacters(viewModel, '\n');5437model.tokenization.forceTokenization(model.getLineCount());5438typeCharacters(viewModel, 'teste2 = teste \'ok');5439assert.strictEqual(model.getLineContent(2), 'teste2 = teste \'ok\'');54405441viewModel.setSelections('test', [new Selection(2, 1000, 2, 1000)]);5442typeCharacters(viewModel, '\n');5443model.tokenization.forceTokenization(model.getLineCount());5444typeCharacters(viewModel, 'teste3 = teste" ok');5445assert.strictEqual(model.getLineContent(3), 'teste3 = teste" ok');54465447viewModel.setSelections('test', [new Selection(3, 1000, 3, 1000)]);5448typeCharacters(viewModel, '\n');5449model.tokenization.forceTokenization(model.getLineCount());5450typeCharacters(viewModel, 'teste4 = teste "ok');5451assert.strictEqual(model.getLineContent(4), 'teste4 = teste "ok"');54525453// Second gif5454viewModel.setSelections('test', [new Selection(4, 1000, 4, 1000)]);5455typeCharacters(viewModel, '\n');5456model.tokenization.forceTokenization(model.getLineCount());5457typeCharacters(viewModel, 'teste \'');5458assert.strictEqual(model.getLineContent(5), 'teste \'\'');54595460viewModel.setSelections('test', [new Selection(5, 1000, 5, 1000)]);5461typeCharacters(viewModel, '\n');5462model.tokenization.forceTokenization(model.getLineCount());5463typeCharacters(viewModel, 'teste "');5464assert.strictEqual(model.getLineContent(6), 'teste ""');54655466viewModel.setSelections('test', [new Selection(6, 1000, 6, 1000)]);5467typeCharacters(viewModel, '\n');5468model.tokenization.forceTokenization(model.getLineCount());5469typeCharacters(viewModel, 'teste\'');5470assert.strictEqual(model.getLineContent(7), 'teste\'');54715472viewModel.setSelections('test', [new Selection(7, 1000, 7, 1000)]);5473typeCharacters(viewModel, '\n');5474model.tokenization.forceTokenization(model.getLineCount());5475typeCharacters(viewModel, 'teste"');5476assert.strictEqual(model.getLineContent(8), 'teste"');5477});5478});54795480test('issue #37315 - overtypes only those characters that it inserted', () => {5481usingCursor({5482text: [5483'',5484'y=();'5485],5486languageId: autoClosingLanguageId5487}, (editor, model, viewModel) => {5488assertCursor(viewModel, new Position(1, 1));54895490viewModel.type('x=(', 'keyboard');5491assert.strictEqual(model.getLineContent(1), 'x=()');54925493viewModel.type('asd', 'keyboard');5494assert.strictEqual(model.getLineContent(1), 'x=(asd)');54955496// overtype!5497viewModel.type(')', 'keyboard');5498assert.strictEqual(model.getLineContent(1), 'x=(asd)');54995500// do not overtype!5501viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5502viewModel.type(')', 'keyboard');5503assert.strictEqual(model.getLineContent(2), 'y=());');55045505});5506});55075508test('issue #37315 - stops overtyping once cursor leaves area', () => {5509usingCursor({5510text: [5511'',5512'y=();'5513],5514languageId: autoClosingLanguageId5515}, (editor, model, viewModel) => {5516assertCursor(viewModel, new Position(1, 1));55175518viewModel.type('x=(', 'keyboard');5519assert.strictEqual(model.getLineContent(1), 'x=()');55205521viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);5522viewModel.type(')', 'keyboard');5523assert.strictEqual(model.getLineContent(1), 'x=())');5524});5525});55265527test('issue #37315 - it overtypes only once', () => {5528usingCursor({5529text: [5530'',5531'y=();'5532],5533languageId: autoClosingLanguageId5534}, (editor, model, viewModel) => {5535assertCursor(viewModel, new Position(1, 1));55365537viewModel.type('x=(', 'keyboard');5538assert.strictEqual(model.getLineContent(1), 'x=()');55395540viewModel.type(')', 'keyboard');5541assert.strictEqual(model.getLineContent(1), 'x=()');55425543viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5544viewModel.type(')', 'keyboard');5545assert.strictEqual(model.getLineContent(1), 'x=())');5546});5547});55485549test('issue #37315 - it can remember multiple auto-closed instances', () => {5550usingCursor({5551text: [5552'',5553'y=();'5554],5555languageId: autoClosingLanguageId5556}, (editor, model, viewModel) => {5557assertCursor(viewModel, new Position(1, 1));55585559viewModel.type('x=(', 'keyboard');5560assert.strictEqual(model.getLineContent(1), 'x=()');55615562viewModel.type('(', 'keyboard');5563assert.strictEqual(model.getLineContent(1), 'x=(())');55645565viewModel.type(')', 'keyboard');5566assert.strictEqual(model.getLineContent(1), 'x=(())');55675568viewModel.type(')', 'keyboard');5569assert.strictEqual(model.getLineContent(1), 'x=(())');5570});5571});55725573test('issue #118270 - auto closing deletes only those characters that it inserted', () => {5574usingCursor({5575text: [5576'',5577'y=();'5578],5579languageId: autoClosingLanguageId5580}, (editor, model, viewModel) => {5581assertCursor(viewModel, new Position(1, 1));55825583viewModel.type('x=(', 'keyboard');5584assert.strictEqual(model.getLineContent(1), 'x=()');55855586viewModel.type('asd', 'keyboard');5587assert.strictEqual(model.getLineContent(1), 'x=(asd)');55885589editor.runCommand(CoreEditingCommands.DeleteLeft, null);5590editor.runCommand(CoreEditingCommands.DeleteLeft, null);5591editor.runCommand(CoreEditingCommands.DeleteLeft, null);5592assert.strictEqual(model.getLineContent(1), 'x=()');55935594// delete closing char!5595editor.runCommand(CoreEditingCommands.DeleteLeft, null);5596assert.strictEqual(model.getLineContent(1), 'x=');55975598// do not delete closing char!5599viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5600editor.runCommand(CoreEditingCommands.DeleteLeft, null);5601assert.strictEqual(model.getLineContent(2), 'y=);');56025603});5604});56055606test('issue #78527 - does not close quote on odd count', () => {5607usingCursor({5608text: [5609'std::cout << \'"\' << entryMap'5610],5611languageId: autoClosingLanguageId5612}, (editor, model, viewModel) => {5613viewModel.setSelections('test', [new Selection(1, 29, 1, 29)]);56145615viewModel.type('[', 'keyboard');5616assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[]');56175618viewModel.type('"', 'keyboard');5619assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[""]');56205621viewModel.type('a', 'keyboard');5622assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');56235624viewModel.type('"', 'keyboard');5625assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');56265627viewModel.type(']', 'keyboard');5628assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');5629});5630});56315632test('issue #85983 - editor.autoClosingBrackets: beforeWhitespace is incorrect for Python', () => {5633const languageId = 'pythonMode';56345635disposables.add(languageService.registerLanguage({ id: languageId }));5636disposables.add(languageConfigurationService.register(languageId, {5637autoClosingPairs: [5638{ open: '{', close: '}' },5639{ open: '[', close: ']' },5640{ open: '(', close: ')' },5641{ open: '\"', close: '\"', notIn: ['string'] },5642{ open: 'r\"', close: '\"', notIn: ['string', 'comment'] },5643{ open: 'R\"', close: '\"', notIn: ['string', 'comment'] },5644{ open: 'u\"', close: '\"', notIn: ['string', 'comment'] },5645{ open: 'U\"', close: '\"', notIn: ['string', 'comment'] },5646{ open: 'f\"', close: '\"', notIn: ['string', 'comment'] },5647{ open: 'F\"', close: '\"', notIn: ['string', 'comment'] },5648{ open: 'b\"', close: '\"', notIn: ['string', 'comment'] },5649{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },5650{ open: '\'', close: '\'', notIn: ['string', 'comment'] },5651{ open: 'r\'', close: '\'', notIn: ['string', 'comment'] },5652{ open: 'R\'', close: '\'', notIn: ['string', 'comment'] },5653{ open: 'u\'', close: '\'', notIn: ['string', 'comment'] },5654{ open: 'U\'', close: '\'', notIn: ['string', 'comment'] },5655{ open: 'f\'', close: '\'', notIn: ['string', 'comment'] },5656{ open: 'F\'', close: '\'', notIn: ['string', 'comment'] },5657{ open: 'b\'', close: '\'', notIn: ['string', 'comment'] },5658{ open: 'B\'', close: '\'', notIn: ['string', 'comment'] },5659{ open: '`', close: '`', notIn: ['string'] }5660],5661}));56625663usingCursor({5664text: [5665'foo\'hello\''5666],5667editorOpts: {5668autoClosingBrackets: 'beforeWhitespace'5669},5670languageId: languageId5671}, (editor, model, viewModel) => {5672assertType(editor, model, viewModel, 1, 4, '(', '(', `does not auto close @ (1, 4)`);5673});5674});56755676test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => {5677usingCursor({5678text: [5679'<div id'5680],5681languageId: autoClosingLanguageId5682}, (editor, model, viewModel) => {5683viewModel.setSelections('test', [new Selection(1, 8, 1, 8)]);56845685viewModel.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)], EditSources.unknown({}));5686assert.strictEqual(model.getLineContent(1), '<div id=""');56875688viewModel.type('a', 'keyboard');5689assert.strictEqual(model.getLineContent(1), '<div id="a"');56905691viewModel.type('"', 'keyboard');5692assert.strictEqual(model.getLineContent(1), '<div id="a"');5693});5694});56955696test('issue #78833 - Add config to use old brackets/quotes overtyping', () => {5697usingCursor({5698text: [5699'',5700'y=();'5701],5702languageId: autoClosingLanguageId,5703editorOpts: {5704autoClosingOvertype: 'always'5705}5706}, (editor, model, viewModel) => {5707assertCursor(viewModel, new Position(1, 1));57085709viewModel.type('x=(', 'keyboard');5710assert.strictEqual(model.getLineContent(1), 'x=()');57115712viewModel.type(')', 'keyboard');5713assert.strictEqual(model.getLineContent(1), 'x=()');57145715viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5716viewModel.type(')', 'keyboard');5717assert.strictEqual(model.getLineContent(1), 'x=()');57185719viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5720viewModel.type(')', 'keyboard');5721assert.strictEqual(model.getLineContent(2), 'y=();');5722});5723});57245725test('issue #15825: accents on mac US intl keyboard', () => {5726usingCursor({5727text: [5728],5729languageId: autoClosingLanguageId5730}, (editor, model, viewModel) => {5731assertCursor(viewModel, new Position(1, 1));57325733// Typing ` + e on the mac US intl kb layout5734viewModel.startComposition();5735viewModel.type('`', 'keyboard');5736viewModel.compositionType('è', 1, 0, 0, 'keyboard');5737viewModel.endComposition('keyboard');57385739assert.strictEqual(model.getValue(), 'è');5740});5741});57425743test('issue #90016: allow accents on mac US intl keyboard to surround selection', () => {5744usingCursor({5745text: [5746'test'5747],5748languageId: autoClosingLanguageId5749}, (editor, model, viewModel) => {5750viewModel.setSelections('test', [new Selection(1, 1, 1, 5)]);57515752// Typing ` + e on the mac US intl kb layout5753viewModel.startComposition();5754viewModel.type('\'', 'keyboard');5755viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5756viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5757viewModel.endComposition('keyboard');57585759assert.strictEqual(model.getValue(), '\'test\'');5760});5761});57625763test('issue #53357: Over typing ignores characters after backslash', () => {5764usingCursor({5765text: [5766'console.log();'5767],5768languageId: autoClosingLanguageId5769}, (editor, model, viewModel) => {57705771viewModel.setSelections('test', [new Selection(1, 13, 1, 13)]);57725773viewModel.type('\'', 'keyboard');5774assert.strictEqual(model.getValue(), 'console.log(\'\');');57755776viewModel.type('it', 'keyboard');5777assert.strictEqual(model.getValue(), 'console.log(\'it\');');57785779viewModel.type('\\', 'keyboard');5780assert.strictEqual(model.getValue(), 'console.log(\'it\\\');');57815782viewModel.type('\'', 'keyboard');5783assert.strictEqual(model.getValue(), 'console.log(\'it\\\'\');');5784});5785});57865787test('issue #84998: Overtyping Brackets doesn\'t work after backslash', () => {5788usingCursor({5789text: [5790''5791],5792languageId: autoClosingLanguageId5793}, (editor, model, viewModel) => {57945795viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);57965797viewModel.type('\\', 'keyboard');5798assert.strictEqual(model.getValue(), '\\');57995800viewModel.type('(', 'keyboard');5801assert.strictEqual(model.getValue(), '\\()');58025803viewModel.type('abc', 'keyboard');5804assert.strictEqual(model.getValue(), '\\(abc)');58055806viewModel.type('\\', 'keyboard');5807assert.strictEqual(model.getValue(), '\\(abc\\)');58085809viewModel.type(')', 'keyboard');5810assert.strictEqual(model.getValue(), '\\(abc\\)');5811});5812});58135814test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => {5815usingCursor({5816text: [5817'hello',5818'world'5819],5820languageId: autoClosingLanguageId5821}, (editor, model, viewModel) => {5822assertCursor(viewModel, new Position(1, 1));58235824// Typing ` and pressing shift+down on the mac US intl kb layout5825// Here we're just replaying what the cursor gets5826viewModel.startComposition();5827viewModel.type('`', 'keyboard');5828moveDown(editor, viewModel, true);5829viewModel.compositionType('`', 1, 0, 0, 'keyboard');5830viewModel.compositionType('`', 1, 0, 0, 'keyboard');5831viewModel.endComposition('keyboard');58325833assert.strictEqual(model.getValue(), '`hello\nworld');5834assertCursor(viewModel, new Selection(1, 2, 2, 2));5835});5836});58375838test('issue #26820: auto close quotes when not used as accents', () => {5839usingCursor({5840text: [5841''5842],5843languageId: autoClosingLanguageId5844}, (editor, model, viewModel) => {5845assertCursor(viewModel, new Position(1, 1));58465847// on the mac US intl kb layout58485849// Typing ' + space5850viewModel.startComposition();5851viewModel.type('\'', 'keyboard');5852viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5853viewModel.endComposition('keyboard');5854assert.strictEqual(model.getValue(), '\'\'');58555856// Typing one more ' + space5857viewModel.startComposition();5858viewModel.type('\'', 'keyboard');5859viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5860viewModel.endComposition('keyboard');5861assert.strictEqual(model.getValue(), '\'\'');58625863// Typing ' as a closing tag5864model.setValue('\'abc');5865viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);5866viewModel.startComposition();5867viewModel.type('\'', 'keyboard');5868viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5869viewModel.endComposition('keyboard');58705871assert.strictEqual(model.getValue(), '\'abc\'');58725873// quotes before the newly added character are all paired.5874model.setValue('\'abc\'def ');5875viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);5876viewModel.startComposition();5877viewModel.type('\'', 'keyboard');5878viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5879viewModel.endComposition('keyboard');58805881assert.strictEqual(model.getValue(), '\'abc\'def \'\'');58825883// No auto closing if there is non-whitespace character after the cursor5884model.setValue('abc');5885viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);5886viewModel.startComposition();5887viewModel.type('\'', 'keyboard');5888viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5889viewModel.endComposition('keyboard');58905891// No auto closing if it's after a word.5892model.setValue('abc');5893viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5894viewModel.startComposition();5895viewModel.type('\'', 'keyboard');5896viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5897viewModel.endComposition('keyboard');58985899assert.strictEqual(model.getValue(), 'abc\'');5900});5901});59025903test('issue #144690: Quotes do not overtype when using US Intl PC keyboard layout', () => {5904usingCursor({5905text: [5906''5907],5908languageId: autoClosingLanguageId5909}, (editor, model, viewModel) => {5910assertCursor(viewModel, new Position(1, 1));59115912// Pressing ' + ' + ;59135914viewModel.startComposition();5915viewModel.type(`'`, 'keyboard');5916viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');5917viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');5918viewModel.endComposition('keyboard');5919viewModel.startComposition();5920viewModel.type(`'`, 'keyboard');5921viewModel.compositionType(`';`, 1, 0, 0, 'keyboard');5922viewModel.compositionType(`';`, 2, 0, 0, 'keyboard');5923viewModel.endComposition('keyboard');59245925assert.strictEqual(model.getValue(), `'';`);5926});5927});59285929test('issue #144693: Typing a quote using US Intl PC keyboard layout always surrounds words', () => {5930usingCursor({5931text: [5932'const hello = 3;'5933],5934languageId: autoClosingLanguageId5935}, (editor, model, viewModel) => {5936viewModel.setSelections('test', [new Selection(1, 7, 1, 12)]);59375938// Pressing ' + e59395940viewModel.startComposition();5941viewModel.type(`'`, 'keyboard');5942viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');5943viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');5944viewModel.endComposition('keyboard');59455946assert.strictEqual(model.getValue(), `const é = 3;`);5947});5948});59495950test('issue #82701: auto close does not execute when IME is canceled via backspace', () => {5951usingCursor({5952text: [5953'{}'5954],5955languageId: autoClosingLanguageId5956}, (editor, model, viewModel) => {5957viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);59585959// Typing a + backspace5960viewModel.startComposition();5961viewModel.type('a', 'keyboard');5962viewModel.compositionType('', 1, 0, 0, 'keyboard');5963viewModel.endComposition('keyboard');5964assert.strictEqual(model.getValue(), '{}');5965});5966});59675968test('issue #20891: All cursors should do the same thing', () => {5969usingCursor({5970text: [5971'var a = asd'5972],5973languageId: autoClosingLanguageId5974}, (editor, model, viewModel) => {59755976viewModel.setSelections('test', [5977new Selection(1, 9, 1, 9),5978new Selection(1, 12, 1, 12),5979]);59805981// type a `5982viewModel.type('`', 'keyboard');59835984assert.strictEqual(model.getValue(), 'var a = `asd`');5985});5986});59875988test('issue #41825: Special handling of quotes in surrounding pairs', () => {5989const languageId = 'myMode';59905991disposables.add(languageService.registerLanguage({ id: languageId }));5992disposables.add(languageConfigurationService.register(languageId, {5993surroundingPairs: [5994{ open: '"', close: '"' },5995{ open: '\'', close: '\'' },5996]5997}));59985999const model = createTextModel('var x = \'hi\';', languageId);60006001withTestCodeEditor(model, {}, (editor, viewModel) => {6002editor.setSelections([6003new Selection(1, 9, 1, 10),6004new Selection(1, 12, 1, 13)6005]);6006viewModel.type('"', 'keyboard');6007assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = "hi";', 'assert1');60086009editor.setSelections([6010new Selection(1, 9, 1, 10),6011new Selection(1, 12, 1, 13)6012]);6013viewModel.type('\'', 'keyboard');6014assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = \'hi\';', 'assert2');6015});6016});60176018test('All cursors should do the same thing when deleting left', () => {6019const model = createTextModel(6020[6021'var a = ()'6022].join('\n'),6023autoClosingLanguageId6024);60256026withTestCodeEditor(model, {}, (editor, viewModel) => {6027viewModel.setSelections('test', [6028new Selection(1, 4, 1, 4),6029new Selection(1, 10, 1, 10),6030]);60316032// delete left6033editor.runCommand(CoreEditingCommands.DeleteLeft, null);60346035assert.strictEqual(model.getValue(), 'va a = )');6036});6037});60386039test('issue #7100: Mouse word selection is strange when non-word character is at the end of line', () => {6040const model = createTextModel(6041[6042'before.a',6043'before',6044'hello:',6045'there:',6046'this is strange:',6047'here',6048'it',6049'is',6050].join('\n')6051);60526053withTestCodeEditor(model, {}, (editor, viewModel) => {6054editor.runCommand(CoreNavigationCommands.WordSelect, {6055position: new Position(3, 7)6056});6057assertCursor(viewModel, new Selection(3, 7, 3, 7));60586059editor.runCommand(CoreNavigationCommands.WordSelectDrag, {6060position: new Position(4, 7)6061});6062assertCursor(viewModel, new Selection(3, 7, 4, 7));6063});6064});60656066test('issue #112039: shift-continuing a double/triple-click and drag selection does not remember its starting mode', () => {6067const model = createTextModel(6068[6069'just some text',6070'and another line',6071'and another one',6072].join('\n')6073);60746075withTestCodeEditor(model, {}, (editor, viewModel) => {6076editor.runCommand(CoreNavigationCommands.WordSelect, {6077position: new Position(2, 6)6078});6079editor.runCommand(CoreNavigationCommands.MoveToSelect, {6080position: new Position(1, 8),6081});6082assertCursor(viewModel, new Selection(2, 12, 1, 6));6083});6084});60856086test('issue #158236: Shift click selection does not work on line number indicator', () => {6087const model = createTextModel(6088[6089'just some text',6090'and another line',6091'and another one',6092].join('\n')6093);60946095withTestCodeEditor(model, {}, (editor, viewModel) => {6096editor.runCommand(CoreNavigationCommands.MoveTo, {6097position: new Position(3, 5)6098});6099editor.runCommand(CoreNavigationCommands.LineSelectDrag, {6100position: new Position(2, 1)6101});6102assertCursor(viewModel, new Selection(3, 5, 2, 1));6103});6104});61056106test('issue #111513: Text gets automatically selected when typing at the same location in another editor', () => {6107const model = createTextModel(6108[6109'just',6110'',6111'some text',6112].join('\n')6113);61146115withTestCodeEditor(model, {}, (editor1, viewModel1) => {6116editor1.setSelections([6117new Selection(2, 1, 2, 1)6118]);6119withTestCodeEditor(model, {}, (editor2, viewModel2) => {6120editor2.setSelections([6121new Selection(2, 1, 2, 1)6122]);6123viewModel2.type('e', 'keyboard');6124assertCursor(viewModel2, new Position(2, 2));6125assertCursor(viewModel1, new Position(2, 2));6126});6127});6128});6129});61306131suite('Undo stops', () => {61326133ensureNoDisposablesAreLeakedInTestSuite();61346135test('there is an undo stop between typing and deleting left', () => {6136const model = createTextModel(6137[6138'A line',6139'Another line',6140].join('\n')6141);61426143withTestCodeEditor(model, {}, (editor, viewModel) => {6144viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6145viewModel.type('first', 'keyboard');6146assert.strictEqual(model.getLineContent(1), 'A first line');6147assertCursor(viewModel, new Selection(1, 8, 1, 8));61486149editor.runCommand(CoreEditingCommands.DeleteLeft, null);6150editor.runCommand(CoreEditingCommands.DeleteLeft, null);6151assert.strictEqual(model.getLineContent(1), 'A fir line');6152assertCursor(viewModel, new Selection(1, 6, 1, 6));61536154editor.runCommand(CoreEditingCommands.Undo, null);6155assert.strictEqual(model.getLineContent(1), 'A first line');6156assertCursor(viewModel, new Selection(1, 8, 1, 8));61576158editor.runCommand(CoreEditingCommands.Undo, null);6159assert.strictEqual(model.getLineContent(1), 'A line');6160assertCursor(viewModel, new Selection(1, 3, 1, 3));6161});61626163model.dispose();6164});61656166test('there is an undo stop between typing and deleting right', () => {6167const model = createTextModel(6168[6169'A line',6170'Another line',6171].join('\n')6172);61736174withTestCodeEditor(model, {}, (editor, viewModel) => {6175viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6176viewModel.type('first', 'keyboard');6177assert.strictEqual(model.getLineContent(1), 'A first line');6178assertCursor(viewModel, new Selection(1, 8, 1, 8));61796180editor.runCommand(CoreEditingCommands.DeleteRight, null);6181editor.runCommand(CoreEditingCommands.DeleteRight, null);6182assert.strictEqual(model.getLineContent(1), 'A firstine');6183assertCursor(viewModel, new Selection(1, 8, 1, 8));61846185editor.runCommand(CoreEditingCommands.Undo, null);6186assert.strictEqual(model.getLineContent(1), 'A first line');6187assertCursor(viewModel, new Selection(1, 8, 1, 8));61886189editor.runCommand(CoreEditingCommands.Undo, null);6190assert.strictEqual(model.getLineContent(1), 'A line');6191assertCursor(viewModel, new Selection(1, 3, 1, 3));6192});61936194model.dispose();6195});61966197test('there is an undo stop between deleting left and typing', () => {6198const model = createTextModel(6199[6200'A line',6201'Another line',6202].join('\n')6203);62046205withTestCodeEditor(model, {}, (editor, viewModel) => {6206viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);6207editor.runCommand(CoreEditingCommands.DeleteLeft, null);6208editor.runCommand(CoreEditingCommands.DeleteLeft, null);6209editor.runCommand(CoreEditingCommands.DeleteLeft, null);6210editor.runCommand(CoreEditingCommands.DeleteLeft, null);6211editor.runCommand(CoreEditingCommands.DeleteLeft, null);6212editor.runCommand(CoreEditingCommands.DeleteLeft, null);6213editor.runCommand(CoreEditingCommands.DeleteLeft, null);6214assert.strictEqual(model.getLineContent(2), ' line');6215assertCursor(viewModel, new Selection(2, 1, 2, 1));62166217viewModel.type('Second', 'keyboard');6218assert.strictEqual(model.getLineContent(2), 'Second line');6219assertCursor(viewModel, new Selection(2, 7, 2, 7));62206221editor.runCommand(CoreEditingCommands.Undo, null);6222assert.strictEqual(model.getLineContent(2), ' line');6223assertCursor(viewModel, new Selection(2, 1, 2, 1));62246225editor.runCommand(CoreEditingCommands.Undo, null);6226assert.strictEqual(model.getLineContent(2), 'Another line');6227assertCursor(viewModel, new Selection(2, 8, 2, 8));6228});62296230model.dispose();6231});62326233test('there is an undo stop between deleting left and deleting right', () => {6234const model = createTextModel(6235[6236'A line',6237'Another line',6238].join('\n')6239);62406241withTestCodeEditor(model, {}, (editor, viewModel) => {6242viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);6243editor.runCommand(CoreEditingCommands.DeleteLeft, null);6244editor.runCommand(CoreEditingCommands.DeleteLeft, null);6245editor.runCommand(CoreEditingCommands.DeleteLeft, null);6246editor.runCommand(CoreEditingCommands.DeleteLeft, null);6247editor.runCommand(CoreEditingCommands.DeleteLeft, null);6248editor.runCommand(CoreEditingCommands.DeleteLeft, null);6249editor.runCommand(CoreEditingCommands.DeleteLeft, null);6250assert.strictEqual(model.getLineContent(2), ' line');6251assertCursor(viewModel, new Selection(2, 1, 2, 1));62526253editor.runCommand(CoreEditingCommands.DeleteRight, null);6254editor.runCommand(CoreEditingCommands.DeleteRight, null);6255editor.runCommand(CoreEditingCommands.DeleteRight, null);6256editor.runCommand(CoreEditingCommands.DeleteRight, null);6257editor.runCommand(CoreEditingCommands.DeleteRight, null);6258assert.strictEqual(model.getLineContent(2), '');6259assertCursor(viewModel, new Selection(2, 1, 2, 1));62606261editor.runCommand(CoreEditingCommands.Undo, null);6262assert.strictEqual(model.getLineContent(2), ' line');6263assertCursor(viewModel, new Selection(2, 1, 2, 1));62646265editor.runCommand(CoreEditingCommands.Undo, null);6266assert.strictEqual(model.getLineContent(2), 'Another line');6267assertCursor(viewModel, new Selection(2, 8, 2, 8));6268});62696270model.dispose();6271});62726273test('there is an undo stop between deleting right and typing', () => {6274const model = createTextModel(6275[6276'A line',6277'Another line',6278].join('\n')6279);62806281withTestCodeEditor(model, {}, (editor, viewModel) => {6282viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);6283editor.runCommand(CoreEditingCommands.DeleteRight, null);6284editor.runCommand(CoreEditingCommands.DeleteRight, null);6285editor.runCommand(CoreEditingCommands.DeleteRight, null);6286editor.runCommand(CoreEditingCommands.DeleteRight, null);6287assert.strictEqual(model.getLineContent(2), 'Another ');6288assertCursor(viewModel, new Selection(2, 9, 2, 9));62896290viewModel.type('text', 'keyboard');6291assert.strictEqual(model.getLineContent(2), 'Another text');6292assertCursor(viewModel, new Selection(2, 13, 2, 13));62936294editor.runCommand(CoreEditingCommands.Undo, null);6295assert.strictEqual(model.getLineContent(2), 'Another ');6296assertCursor(viewModel, new Selection(2, 9, 2, 9));62976298editor.runCommand(CoreEditingCommands.Undo, null);6299assert.strictEqual(model.getLineContent(2), 'Another line');6300assertCursor(viewModel, new Selection(2, 9, 2, 9));6301});63026303model.dispose();6304});63056306test('there is an undo stop between deleting right and deleting left', () => {6307const model = createTextModel(6308[6309'A line',6310'Another line',6311].join('\n')6312);63136314withTestCodeEditor(model, {}, (editor, viewModel) => {6315viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);6316editor.runCommand(CoreEditingCommands.DeleteRight, null);6317editor.runCommand(CoreEditingCommands.DeleteRight, null);6318editor.runCommand(CoreEditingCommands.DeleteRight, null);6319editor.runCommand(CoreEditingCommands.DeleteRight, null);6320assert.strictEqual(model.getLineContent(2), 'Another ');6321assertCursor(viewModel, new Selection(2, 9, 2, 9));63226323editor.runCommand(CoreEditingCommands.DeleteLeft, null);6324editor.runCommand(CoreEditingCommands.DeleteLeft, null);6325editor.runCommand(CoreEditingCommands.DeleteLeft, null);6326editor.runCommand(CoreEditingCommands.DeleteLeft, null);6327editor.runCommand(CoreEditingCommands.DeleteLeft, null);6328editor.runCommand(CoreEditingCommands.DeleteLeft, null);6329assert.strictEqual(model.getLineContent(2), 'An');6330assertCursor(viewModel, new Selection(2, 3, 2, 3));63316332editor.runCommand(CoreEditingCommands.Undo, null);6333assert.strictEqual(model.getLineContent(2), 'Another ');6334assertCursor(viewModel, new Selection(2, 9, 2, 9));63356336editor.runCommand(CoreEditingCommands.Undo, null);6337assert.strictEqual(model.getLineContent(2), 'Another line');6338assertCursor(viewModel, new Selection(2, 9, 2, 9));6339});63406341model.dispose();6342});63436344test('inserts undo stop when typing space', () => {6345const model = createTextModel(6346[6347'A line',6348'Another line',6349].join('\n')6350);63516352withTestCodeEditor(model, {}, (editor, viewModel) => {6353viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6354viewModel.type('first and interesting', 'keyboard');6355assert.strictEqual(model.getLineContent(1), 'A first and interesting line');6356assertCursor(viewModel, new Selection(1, 24, 1, 24));63576358editor.runCommand(CoreEditingCommands.Undo, null);6359assert.strictEqual(model.getLineContent(1), 'A first and line');6360assertCursor(viewModel, new Selection(1, 12, 1, 12));63616362editor.runCommand(CoreEditingCommands.Undo, null);6363assert.strictEqual(model.getLineContent(1), 'A first line');6364assertCursor(viewModel, new Selection(1, 8, 1, 8));63656366editor.runCommand(CoreEditingCommands.Undo, null);6367assert.strictEqual(model.getLineContent(1), 'A line');6368assertCursor(viewModel, new Selection(1, 3, 1, 3));6369});63706371model.dispose();6372});63736374test('can undo typing and EOL change in one undo stop', () => {6375const model = createTextModel(6376[6377'A line',6378'Another line',6379].join('\n')6380);63816382withTestCodeEditor(model, {}, (editor, viewModel) => {6383viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6384viewModel.type('first', 'keyboard');6385assert.strictEqual(model.getValue(), 'A first line\nAnother line');6386assertCursor(viewModel, new Selection(1, 8, 1, 8));63876388model.pushEOL(EndOfLineSequence.CRLF);6389assert.strictEqual(model.getValue(), 'A first line\r\nAnother line');6390assertCursor(viewModel, new Selection(1, 8, 1, 8));63916392editor.runCommand(CoreEditingCommands.Undo, null);6393assert.strictEqual(model.getValue(), 'A line\nAnother line');6394assertCursor(viewModel, new Selection(1, 3, 1, 3));6395});63966397model.dispose();6398});63996400test('issue #93585: Undo multi cursor edit corrupts document', () => {6401const model = createTextModel(6402[6403'hello world',6404'hello world',6405].join('\n')6406);64076408withTestCodeEditor(model, {}, (editor, viewModel) => {6409viewModel.setSelections('test', [6410new Selection(2, 7, 2, 12),6411new Selection(1, 7, 1, 12),6412]);6413viewModel.type('no', 'keyboard');6414assert.strictEqual(model.getValue(), 'hello no\nhello no');64156416editor.runCommand(CoreEditingCommands.Undo, null);6417assert.strictEqual(model.getValue(), 'hello world\nhello world');6418});64196420model.dispose();6421});64226423test('there is a single undo stop for consecutive whitespaces', () => {6424const model = createTextModel(6425[6426''6427].join('\n'),6428undefined,6429{6430insertSpaces: false,6431}6432);64336434withTestCodeEditor(model, {}, (editor, viewModel) => {6435viewModel.type('a', 'keyboard');6436viewModel.type('b', 'keyboard');6437viewModel.type(' ', 'keyboard');6438viewModel.type(' ', 'keyboard');6439viewModel.type('c', 'keyboard');6440viewModel.type('d', 'keyboard');64416442assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');64436444editor.runCommand(CoreEditingCommands.Undo, null);6445assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab ', 'assert2');64466447editor.runCommand(CoreEditingCommands.Undo, null);6448assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');64496450editor.runCommand(CoreEditingCommands.Undo, null);6451assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');6452});64536454model.dispose();6455});64566457test('there is no undo stop after a single whitespace', () => {6458const model = createTextModel(6459[6460''6461].join('\n'),6462undefined,6463{6464insertSpaces: false,6465}6466);64676468withTestCodeEditor(model, {}, (editor, viewModel) => {6469viewModel.type('a', 'keyboard');6470viewModel.type('b', 'keyboard');6471viewModel.type(' ', 'keyboard');6472viewModel.type('c', 'keyboard');6473viewModel.type('d', 'keyboard');64746475assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');64766477editor.runCommand(CoreEditingCommands.Undo, null);6478assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');64796480editor.runCommand(CoreEditingCommands.Undo, null);6481assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');6482});64836484model.dispose();6485});6486});64876488suite('Overtype Mode', () => {64896490setup(() => {6491InputMode.setInputMode('overtype');6492});64936494teardown(() => {6495InputMode.setInputMode('insert');6496});64976498ensureNoDisposablesAreLeakedInTestSuite();64996500test('simple type', () => {6501const model = createTextModel(6502[6503'123456789',6504'123456789',6505].join('\n'),6506undefined,6507{6508insertSpaces: false,6509}6510);65116512withTestCodeEditor(model, {}, (editor, viewModel) => {6513viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6514viewModel.type('a', 'keyboard');6515assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6516'12a456789',6517'123456789',6518].join('\n'), 'assert1');65196520viewModel.setSelections('test', [new Selection(1, 9, 1, 9)]);6521viewModel.type('bbb', 'keyboard');6522assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6523'12a45678bbb',6524'123456789',6525].join('\n'), 'assert2');6526});65276528model.dispose();6529});65306531test('multi-line selection type', () => {6532const model = createTextModel(6533[6534'123456789',6535'123456789',6536].join('\n'),6537undefined,6538{6539insertSpaces: false,6540}6541);65426543withTestCodeEditor(model, {}, (editor, viewModel) => {6544viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);6545viewModel.type('cc', 'keyboard');6546assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6547'1234cc456789',6548].join('\n'), 'assert1');6549});65506551model.dispose();6552});65536554test('simple paste', () => {6555const model = createTextModel(6556[6557'123456789',6558'123456789',6559].join('\n'),6560undefined,6561{6562insertSpaces: false,6563}6564);65656566withTestCodeEditor(model, {}, (editor, viewModel) => {6567viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6568viewModel.paste('cc', false);6569assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6570'1234cc789',6571'123456789',6572].join('\n'), 'assert1');65736574viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6575viewModel.paste('dddddddd', false);6576assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6577'1234dddddddd',6578'123456789',6579].join('\n'), 'assert2');6580});65816582model.dispose();6583});65846585test('multi-line selection paste', () => {6586const model = createTextModel(6587[6588'123456789',6589'123456789',6590].join('\n'),6591undefined,6592{6593insertSpaces: false,6594}6595);65966597withTestCodeEditor(model, {}, (editor, viewModel) => {6598viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);6599viewModel.paste('cc', false);6600assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6601'1234cc456789',6602].join('\n'), 'assert1');6603});66046605model.dispose();6606});66076608test('paste multi-line text', () => {6609const model = createTextModel(6610[6611'123456789',6612'123456789',6613].join('\n'),6614undefined,6615{6616insertSpaces: false,6617}6618);66196620withTestCodeEditor(model, {}, (editor, viewModel) => {6621viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6622viewModel.paste([6623'aaaaaaa',6624'bbbbbbb'6625].join('\n'), false);6626assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6627'1234aaaaaaa',6628'bbbbbbb',6629'123456789',6630].join('\n'), 'assert1');6631});66326633model.dispose();6634});66356636test('composition type', () => {6637const model = createTextModel(6638[6639'123456789',6640'123456789',6641].join('\n'),6642undefined,6643{6644insertSpaces: false,6645}6646);66476648withTestCodeEditor(model, {}, (editor, viewModel) => {6649viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6650viewModel.startComposition();6651viewModel.compositionType('セ', 0, 0, 0, 'keyboard');6652assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6653'1234セ56789',6654'123456789',6655].join('\n'), 'assert1');66566657viewModel.endComposition('keyboard');6658assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6659'1234セ6789',6660'123456789',6661].join('\n'), 'assert1');6662});66636664model.dispose();6665});6666});666766686669