Path: blob/main/src/vs/editor/test/browser/controller/cursor.test.ts
5236 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 #256039: paste from multiple cursors with empty selections and multiCursorPaste full', () => {2242usingCursor({2243text: [2244'line1',2245'line2',2246'line3'2247],2248editorOpts: {2249multiCursorPaste: 'full'2250}2251}, (editor, model, viewModel) => {2252// 2 cursors on lines 1 and 22253viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]);22542255viewModel.paste(2256'line1\nline2\n',2257true,2258['line1\n', 'line2\n']2259);22602261// Each cursor gets its respective line2262assert.strictEqual(model.getValue(), [2263'line1',2264'line1',2265'line2',2266'line2',2267'line3'2268].join('\n'));2269});2270});22712272test('issue #3071: Investigate why undo stack gets corrupted', () => {2273const model = createTextModel(2274[2275'some lines',2276'and more lines',2277'just some text',2278].join('\n')2279);22802281withTestCodeEditor(model, {}, (editor, viewModel) => {2282moveTo(editor, viewModel, 1, 1, false);2283moveTo(editor, viewModel, 3, 4, true);22842285let isFirst = true;2286const disposable = model.onDidChangeContent(() => {2287if (isFirst) {2288isFirst = false;2289viewModel.type('\t', 'keyboard');2290}2291});22922293editor.runCommand(CoreEditingCommands.Tab, null);2294assert.strictEqual(model.getValue(), [2295'\t just some text'2296].join('\n'), '001');22972298editor.runCommand(CoreEditingCommands.Undo, null);2299assert.strictEqual(model.getValue(), [2300' some lines',2301' and more lines',2302' just some text',2303].join('\n'), '002');23042305editor.runCommand(CoreEditingCommands.Undo, null);2306assert.strictEqual(model.getValue(), [2307'some lines',2308'and more lines',2309'just some text',2310].join('\n'), '003');23112312editor.runCommand(CoreEditingCommands.Undo, null);2313assert.strictEqual(model.getValue(), [2314'some lines',2315'and more lines',2316'just some text',2317].join('\n'), '004');23182319disposable.dispose();2320});2321});23222323test('issue #12950: Cannot Double Click To Insert Emoji Using OSX Emoji Panel', () => {2324usingCursor({2325text: [2326'some lines',2327'and more lines',2328'just some text',2329],2330languageId: null2331}, (editor, model, viewModel) => {2332moveTo(editor, viewModel, 3, 1, false);23332334viewModel.type('😍', 'keyboard');23352336assert.strictEqual(model.getValue(), [2337'some lines',2338'and more lines',2339'😍just some text',2340].join('\n'));2341});2342});23432344test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => {2345const model = createTextModel(2346[2347'function a() {',2348'\tvar a = {',2349'\t\tx: 3',2350'\t};',2351'}',2352].join('\n')2353);23542355withTestCodeEditor(model, {}, (editor, viewModel) => {2356moveTo(editor, viewModel, 3, 2, false);2357editor.runCommand(CoreEditingCommands.Tab, null);2358assert.strictEqual(model.getLineContent(3), '\t \tx: 3');2359});2360});23612362test('issue #4312: trying to type a tab character over a sequence of spaces results in unexpected behaviour', () => {2363const model = createTextModel(2364[2365'var foo = 123; // this is a comment',2366'var bar = 4; // another comment'2367].join('\n'),2368undefined,2369{2370insertSpaces: false,2371}2372);23732374withTestCodeEditor(model, {}, (editor, viewModel) => {2375moveTo(editor, viewModel, 1, 15, false);2376moveTo(editor, viewModel, 1, 22, true);2377editor.runCommand(CoreEditingCommands.Tab, null);2378assert.strictEqual(model.getLineContent(1), 'var foo = 123;\t// this is a comment');2379});2380});23812382test('issue #832: word right', () => {23832384usingCursor({2385text: [2386' /* Just some more text a+= 3 +5-3 + 7 */ '2387],2388}, (editor, model, viewModel) => {2389moveTo(editor, viewModel, 1, 1, false);23902391function assertWordRight(col: number, expectedCol: number) {2392const args = {2393position: {2394lineNumber: 1,2395column: col2396}2397};2398if (col === 1) {2399CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, args);2400} else {2401CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, args);2402}24032404assert.strictEqual(viewModel.getSelection().startColumn, 1, 'TEST FOR ' + col);2405assert.strictEqual(viewModel.getSelection().endColumn, expectedCol, 'TEST FOR ' + col);2406}24072408assertWordRight(1, ' '.length + 1);2409assertWordRight(2, ' '.length + 1);2410assertWordRight(3, ' '.length + 1);2411assertWordRight(4, ' '.length + 1);2412assertWordRight(5, ' /'.length + 1);2413assertWordRight(6, ' /*'.length + 1);2414assertWordRight(7, ' /* '.length + 1);2415assertWordRight(8, ' /* Just'.length + 1);2416assertWordRight(9, ' /* Just'.length + 1);2417assertWordRight(10, ' /* Just'.length + 1);2418assertWordRight(11, ' /* Just'.length + 1);2419assertWordRight(12, ' /* Just '.length + 1);2420assertWordRight(13, ' /* Just some'.length + 1);2421assertWordRight(14, ' /* Just some'.length + 1);2422assertWordRight(15, ' /* Just some'.length + 1);2423assertWordRight(16, ' /* Just some'.length + 1);2424assertWordRight(17, ' /* Just some '.length + 1);2425assertWordRight(18, ' /* Just some '.length + 1);2426assertWordRight(19, ' /* Just some '.length + 1);2427assertWordRight(20, ' /* Just some more'.length + 1);2428assertWordRight(21, ' /* Just some more'.length + 1);2429assertWordRight(22, ' /* Just some more'.length + 1);2430assertWordRight(23, ' /* Just some more'.length + 1);2431assertWordRight(24, ' /* Just some more '.length + 1);2432assertWordRight(25, ' /* Just some more '.length + 1);2433assertWordRight(26, ' /* Just some more '.length + 1);2434assertWordRight(27, ' /* Just some more text'.length + 1);2435assertWordRight(28, ' /* Just some more text'.length + 1);2436assertWordRight(29, ' /* Just some more text'.length + 1);2437assertWordRight(30, ' /* Just some more text'.length + 1);2438assertWordRight(31, ' /* Just some more text '.length + 1);2439assertWordRight(32, ' /* Just some more text a'.length + 1);2440assertWordRight(33, ' /* Just some more text a+'.length + 1);2441assertWordRight(34, ' /* Just some more text a+='.length + 1);2442assertWordRight(35, ' /* Just some more text a+= '.length + 1);2443assertWordRight(36, ' /* Just some more text a+= 3'.length + 1);2444assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1);2445assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1);2446assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1);2447assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1);2448assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1);2449assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1);2450assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1);2451assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1);2452assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1);2453assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1);2454assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1);2455assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1);2456assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);2457assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);2458});2459});24602461test('issue #33788: Wrong cursor position when double click to select a word', () => {2462const model = createTextModel(2463[2464'Just some text'2465].join('\n')2466);24672468withTestCodeEditor(model, {}, (editor, viewModel) => {2469CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });2470assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));24712472CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });2473assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));2474});2475});24762477test('issue #12887: Double-click highlighting separating white space', () => {2478const model = createTextModel(2479[2480'abc def'2481].join('\n')2482);24832484withTestCodeEditor(model, {}, (editor, viewModel) => {2485CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 5) });2486assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 5, 1, 8));2487});2488});24892490test('Double-click on punctuation should select the character, not adjacent space', () => {2491const model = createTextModel(2492[2493'// a b c 1 2 3 ~ ! @ # $ % ^ & * ( ) _ + \\ /'2494].join('\n')2495);24962497withTestCodeEditor(model, {}, (editor, viewModel) => {2498// Test double-click on '@' at position 202499CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 20) });2500assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 20, 1, 21), 'Should select @ character');25012502// Test double-click on '#' at position 222503CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 22) });2504assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 22, 1, 23), 'Should select # character');25052506// Test double-click on '!' at position 182507CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 18) });2508assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 18, 1, 19), 'Should select ! character');25092510// Test double-click on first '/' in '//' at position 12511CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 1) });2512assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 3), 'Should select // token');25132514// Test double-click on second '/' in '//' at position 22515CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 2) });2516assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 3), 'Should select // token');25172518// Test double-click on '\' at position 422519CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 42) });2520assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 42, 1, 43), 'Should select \\ character');2521});2522});25232524test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => {2525withTestCodeEditor([], {}, (editor, viewModel) => {2526const model = editor.getModel()!;2527assertCursor(viewModel, new Position(1, 1));25282529// Typing sennsei in Japanese - Hiragana2530viewModel.type('s', 'keyboard');2531viewModel.compositionType('せ', 1, 0, 0);2532viewModel.compositionType('せn', 1, 0, 0);2533viewModel.compositionType('せん', 2, 0, 0);2534viewModel.compositionType('せんs', 2, 0, 0);2535viewModel.compositionType('せんせ', 3, 0, 0);2536viewModel.compositionType('せんせ', 3, 0, 0);2537viewModel.compositionType('せんせい', 3, 0, 0);2538viewModel.compositionType('せんせい', 4, 0, 0);2539viewModel.compositionType('せんせい', 4, 0, 0);2540viewModel.compositionType('せんせい', 4, 0, 0);25412542assert.strictEqual(model.getLineContent(1), 'せんせい');2543assertCursor(viewModel, new Position(1, 5));25442545editor.runCommand(CoreEditingCommands.Undo, null);2546assert.strictEqual(model.getLineContent(1), '');2547assertCursor(viewModel, new Position(1, 1));2548});2549});25502551test('issue #23983: Calling model.setEOL does not reset cursor position', () => {2552usingCursor({2553text: [2554'first line',2555'second line'2556]2557}, (editor, model, viewModel) => {2558model.setEOL(EndOfLineSequence.CRLF);25592560viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);2561model.setEOL(EndOfLineSequence.LF);25622563assertCursor(viewModel, new Selection(2, 2, 2, 2));2564});2565});25662567test('issue #23983: Calling model.setValue() resets cursor position', () => {2568usingCursor({2569text: [2570'first line',2571'second line'2572]2573}, (editor, model, viewModel) => {2574model.setEOL(EndOfLineSequence.CRLF);25752576viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);2577model.setValue([2578'different first line',2579'different second line',2580'new third line'2581].join('\n'));25822583assertCursor(viewModel, new Selection(1, 1, 1, 1));2584});2585});25862587test('issue #36740: wordwrap creates an extra step / character at the wrapping point', () => {2588// a single model line => 4 view lines2589withTestCodeEditor([2590[2591'Lorem ipsum ',2592'dolor sit amet ',2593'consectetur ',2594'adipiscing elit',2595].join('')2596], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {2597viewModel.setSelections('test', [new Selection(1, 7, 1, 7)]);25982599moveRight(editor, viewModel);2600assertCursor(viewModel, new Selection(1, 8, 1, 8));26012602moveRight(editor, viewModel);2603assertCursor(viewModel, new Selection(1, 9, 1, 9));26042605moveRight(editor, viewModel);2606assertCursor(viewModel, new Selection(1, 10, 1, 10));26072608moveRight(editor, viewModel);2609assertCursor(viewModel, new Selection(1, 11, 1, 11));26102611moveRight(editor, viewModel);2612assertCursor(viewModel, new Selection(1, 12, 1, 12));26132614moveRight(editor, viewModel);2615assertCursor(viewModel, new Selection(1, 13, 1, 13));26162617// moving to view line 22618moveRight(editor, viewModel);2619assertCursor(viewModel, new Selection(1, 14, 1, 14));26202621moveLeft(editor, viewModel);2622assertCursor(viewModel, new Selection(1, 13, 1, 13));26232624// moving back to view line 12625moveLeft(editor, viewModel);2626assertCursor(viewModel, new Selection(1, 12, 1, 12));2627});2628});26292630test('issue #110376: multiple selections with wordwrap behave differently', () => {2631// a single model line => 4 view lines2632withTestCodeEditor([2633[2634'just a sentence. just a ',2635'sentence. just a sentence.',2636].join('')2637], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => {2638viewModel.setSelections('test', [2639new Selection(1, 1, 1, 16),2640new Selection(1, 18, 1, 33),2641new Selection(1, 35, 1, 50),2642]);26432644moveLeft(editor, viewModel);2645assertCursor(viewModel, [2646new Selection(1, 1, 1, 1),2647new Selection(1, 18, 1, 18),2648new Selection(1, 35, 1, 35),2649]);26502651viewModel.setSelections('test', [2652new Selection(1, 1, 1, 16),2653new Selection(1, 18, 1, 33),2654new Selection(1, 35, 1, 50),2655]);26562657moveRight(editor, viewModel);2658assertCursor(viewModel, [2659new Selection(1, 16, 1, 16),2660new Selection(1, 33, 1, 33),2661new Selection(1, 50, 1, 50),2662]);2663});2664});26652666test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => {2667// a single model line => 4 view lines2668withTestCodeEditor([2669[2670'lorem_ipsum-1993x11x13',2671'dolor_sit_amet-1998x04x27',2672'consectetur-2007x10x08',2673'adipiscing-2012x07x27',2674'elit-2015x02x27',2675].join('\n')2676], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {2677viewModel.setSelections('test', [2678new Selection(1, 13, 1, 13),2679new Selection(2, 16, 2, 16),2680new Selection(3, 13, 3, 13),2681new Selection(4, 12, 4, 12),2682new Selection(5, 6, 5, 6),2683]);2684assertCursor(viewModel, [2685new Selection(1, 13, 1, 13),2686new Selection(2, 16, 2, 16),2687new Selection(3, 13, 3, 13),2688new Selection(4, 12, 4, 12),2689new Selection(5, 6, 5, 6),2690]);26912692moveRight(editor, viewModel, true);2693assertCursor(viewModel, [2694new Selection(1, 13, 1, 14),2695new Selection(2, 16, 2, 17),2696new Selection(3, 13, 3, 14),2697new Selection(4, 12, 4, 13),2698new Selection(5, 6, 5, 7),2699]);27002701moveRight(editor, viewModel, true);2702assertCursor(viewModel, [2703new Selection(1, 13, 1, 15),2704new Selection(2, 16, 2, 18),2705new Selection(3, 13, 3, 15),2706new Selection(4, 12, 4, 14),2707new Selection(5, 6, 5, 8),2708]);27092710moveRight(editor, viewModel, true);2711assertCursor(viewModel, [2712new Selection(1, 13, 1, 16),2713new Selection(2, 16, 2, 19),2714new Selection(3, 13, 3, 16),2715new Selection(4, 12, 4, 15),2716new Selection(5, 6, 5, 9),2717]);27182719moveRight(editor, viewModel, true);2720assertCursor(viewModel, [2721new Selection(1, 13, 1, 17),2722new Selection(2, 16, 2, 20),2723new Selection(3, 13, 3, 17),2724new Selection(4, 12, 4, 16),2725new Selection(5, 6, 5, 10),2726]);2727});2728});27292730test('issue #41573 - delete across multiple lines does not shrink the selection when word wraps', () => {2731withTestCodeEditor([2732'Authorization: \'Bearer pHKRfCTFSnGxs6akKlb9ddIXcca0sIUSZJutPHYqz7vEeHdMTMh0SGN0IGU3a0n59DXjTLRsj5EJ2u33qLNIFi9fk5XF8pK39PndLYUZhPt4QvHGLScgSkK0L4gwzkzMloTQPpKhqiikiIOvyNNSpd2o8j29NnOmdTUOKi9DVt74PD2ohKxyOrWZ6oZprTkb3eKajcpnS0LABKfaw2rmv4\','2733].join('\n'), { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }, (editor, viewModel) => {2734moveTo(editor, viewModel, 1, 43, false);2735moveTo(editor, viewModel, 1, 147, true);2736assertCursor(viewModel, new Selection(1, 43, 1, 147));27372738editor.getModel().applyEdits([{2739range: new Range(1, 1, 1, 43),2740text: ''2741}]);27422743assertCursor(viewModel, new Selection(1, 1, 1, 105));2744});2745});27462747test('issue #22717: Moving text cursor cause an incorrect position in Chinese', () => {2748// a single model line => 4 view lines2749withTestCodeEditor([2750[2751'一二三四五六七八九十',2752'12345678901234567890',2753].join('\n')2754], {}, (editor, viewModel) => {2755viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);27562757moveDown(editor, viewModel);2758assertCursor(viewModel, new Selection(2, 9, 2, 9));27592760moveRight(editor, viewModel);2761assertCursor(viewModel, new Selection(2, 10, 2, 10));27622763moveRight(editor, viewModel);2764assertCursor(viewModel, new Selection(2, 11, 2, 11));27652766moveUp(editor, viewModel);2767assertCursor(viewModel, new Selection(1, 6, 1, 6));2768});2769});27702771test('issue #112301: new stickyTabStops feature interferes with word wrap', () => {2772withTestCodeEditor([2773[2774'function hello() {',2775' console.log(`this is a long console message`)',2776'}',2777].join('\n')2778], { wordWrap: 'wordWrapColumn', wordWrapColumn: 32, stickyTabStops: true }, (editor, viewModel) => {2779viewModel.setSelections('test', [2780new Selection(2, 31, 2, 31)2781]);2782moveRight(editor, viewModel, false);2783assertCursor(viewModel, new Position(2, 32));27842785moveRight(editor, viewModel, false);2786assertCursor(viewModel, new Position(2, 33));27872788moveRight(editor, viewModel, false);2789assertCursor(viewModel, new Position(2, 34));27902791moveLeft(editor, viewModel, false);2792assertCursor(viewModel, new Position(2, 33));27932794moveLeft(editor, viewModel, false);2795assertCursor(viewModel, new Position(2, 32));27962797moveLeft(editor, viewModel, false);2798assertCursor(viewModel, new Position(2, 31));2799});2800});28012802test('issue #44805: Should not be able to undo in readonly editor', () => {2803const model = createTextModel(2804[2805''2806].join('\n')2807);28082809withTestCodeEditor(model, { readOnly: true }, (editor, viewModel) => {2810model.pushEditOperations([new Selection(1, 1, 1, 1)], [{2811range: new Range(1, 1, 1, 1),2812text: 'Hello world!'2813}], () => [new Selection(1, 1, 1, 1)]);2814assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');28152816editor.runCommand(CoreEditingCommands.Undo, null);2817assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');2818});2819});28202821test('issue #46314: ViewModel is out of sync with Model!', () => {28222823const tokenizationSupport: ITokenizationSupport = {2824getInitialState: () => NullState,2825tokenize: undefined!,2826tokenizeEncoded: (line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult => {2827return new EncodedTokenizationResult(new Uint32Array(0), [], state);2828}2829};28302831const LANGUAGE_ID = 'modelModeTest1';2832const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);2833const model = createTextModel('Just text', LANGUAGE_ID);28342835withTestCodeEditor(model, {}, (editor1, cursor1) => {2836withTestCodeEditor(model, {}, (editor2, cursor2) => {28372838const disposable = editor1.onDidChangeCursorPosition(() => {2839model.tokenization.tokenizeIfCheap(1);2840});28412842model.applyEdits([{ range: new Range(1, 1, 1, 1), text: '-' }]);28432844disposable.dispose();2845});2846});28472848languageRegistration.dispose();2849model.dispose();2850});28512852test('issue #37967: problem replacing consecutive characters', () => {2853const model = createTextModel(2854[2855'const a = "foo";',2856'const b = ""'2857].join('\n')2858);28592860withTestCodeEditor(model, { multiCursorMergeOverlapping: false }, (editor, viewModel) => {2861editor.setSelections([2862new Selection(1, 12, 1, 12),2863new Selection(1, 16, 1, 16),2864new Selection(2, 12, 2, 12),2865new Selection(2, 13, 2, 13),2866]);28672868editor.runCommand(CoreEditingCommands.DeleteLeft, null);28692870assertCursor(viewModel, [2871new Selection(1, 11, 1, 11),2872new Selection(1, 14, 1, 14),2873new Selection(2, 11, 2, 11),2874new Selection(2, 11, 2, 11),2875]);28762877viewModel.type('\'', 'keyboard');28782879assert.strictEqual(model.getLineContent(1), 'const a = \'foo\';');2880assert.strictEqual(model.getLineContent(2), 'const b = \'\'');2881});2882});28832884test('issue #15761: Cursor doesn\'t move in a redo operation', () => {2885const model = createTextModel(2886[2887'hello'2888].join('\n')2889);28902891withTestCodeEditor(model, {}, (editor, viewModel) => {2892editor.setSelections([2893new Selection(1, 4, 1, 4)2894]);28952896editor.executeEdits('test', [{2897range: new Range(1, 1, 1, 1),2898text: '*',2899forceMoveMarkers: true2900}]);2901assertCursor(viewModel, [2902new Selection(1, 5, 1, 5),2903]);29042905editor.runCommand(CoreEditingCommands.Undo, null);2906assertCursor(viewModel, [2907new Selection(1, 4, 1, 4),2908]);29092910editor.runCommand(CoreEditingCommands.Redo, null);2911assertCursor(viewModel, [2912new Selection(1, 5, 1, 5),2913]);2914});2915});29162917test('issue #42783: API Calls with Undo Leave Cursor in Wrong Position', () => {2918const model = createTextModel(2919[2920'ab'2921].join('\n')2922);29232924withTestCodeEditor(model, {}, (editor, viewModel) => {2925editor.setSelections([2926new Selection(1, 1, 1, 1)2927]);29282929editor.executeEdits('test', [{2930range: new Range(1, 1, 1, 3),2931text: ''2932}]);2933assertCursor(viewModel, [2934new Selection(1, 1, 1, 1),2935]);29362937editor.runCommand(CoreEditingCommands.Undo, null);2938assertCursor(viewModel, [2939new Selection(1, 1, 1, 1),2940]);29412942editor.executeEdits('test', [{2943range: new Range(1, 1, 1, 2),2944text: ''2945}]);2946assertCursor(viewModel, [2947new Selection(1, 1, 1, 1),2948]);2949});2950});29512952test('issue #85712: Paste line moves cursor to start of current line rather than start of next line', () => {2953const model = createTextModel(2954[2955'abc123',2956''2957].join('\n')2958);29592960withTestCodeEditor(model, {}, (editor, viewModel) => {2961editor.setSelections([2962new Selection(2, 1, 2, 1)2963]);2964viewModel.paste('something\n', true);2965assert.strictEqual(model.getValue(), [2966'abc123',2967'something',2968''2969].join('\n'));2970assertCursor(viewModel, new Position(3, 1));2971});2972});29732974test('issue #84897: Left delete behavior in some languages is changed', () => {2975const model = createTextModel(2976[2977'สวัสดี'2978].join('\n')2979);29802981withTestCodeEditor(model, {}, (editor, viewModel) => {2982editor.setSelections([2983new Selection(1, 7, 1, 7)2984]);29852986editor.runCommand(CoreEditingCommands.DeleteLeft, null);2987assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');29882989editor.runCommand(CoreEditingCommands.DeleteLeft, null);2990assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');29912992editor.runCommand(CoreEditingCommands.DeleteLeft, null);2993assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');29942995editor.runCommand(CoreEditingCommands.DeleteLeft, null);2996assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');29972998editor.runCommand(CoreEditingCommands.DeleteLeft, null);2999assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');30003001editor.runCommand(CoreEditingCommands.DeleteLeft, null);3002assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');3003});3004});30053006test('issue #122914: Left delete behavior in some languages is changed (useTabStops: false)', () => {3007const model = createTextModel(3008[3009'สวัสดี'3010].join('\n')3011);30123013withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {3014editor.setSelections([3015new Selection(1, 7, 1, 7)3016]);30173018editor.runCommand(CoreEditingCommands.DeleteLeft, null);3019assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');30203021editor.runCommand(CoreEditingCommands.DeleteLeft, null);3022assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');30233024editor.runCommand(CoreEditingCommands.DeleteLeft, null);3025assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');30263027editor.runCommand(CoreEditingCommands.DeleteLeft, null);3028assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');30293030editor.runCommand(CoreEditingCommands.DeleteLeft, null);3031assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');30323033editor.runCommand(CoreEditingCommands.DeleteLeft, null);3034assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');3035});3036});30373038test('issue #99629: Emoji modifiers in text treated separately when using backspace', () => {3039const model = createTextModel(3040[3041'👶🏾'3042].join('\n')3043);30443045withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {3046const len = model.getValueLength();3047editor.setSelections([3048new Selection(1, 1 + len, 1, 1 + len)3049]);30503051editor.runCommand(CoreEditingCommands.DeleteLeft, null);3052assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');3053});3054});30553056test('issue #99629: Emoji modifiers in text treated separately when using backspace (ZWJ sequence)', () => {3057const model = createTextModel(3058[3059'👨👩🏽👧👦'3060].join('\n')3061);30623063withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {3064const len = model.getValueLength();3065editor.setSelections([3066new Selection(1, 1 + len, 1, 1 + len)3067]);30683069editor.runCommand(CoreEditingCommands.DeleteLeft, null);3070assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨👩🏽👧');30713072editor.runCommand(CoreEditingCommands.DeleteLeft, null);3073assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨👩🏽');30743075editor.runCommand(CoreEditingCommands.DeleteLeft, null);3076assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨');30773078editor.runCommand(CoreEditingCommands.DeleteLeft, null);3079assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');3080});3081});30823083test('issue #105730: move left behaves differently for multiple cursors', () => {3084const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, ');30853086withTestCodeEditor(3087model,3088{3089wordWrap: 'wordWrapColumn',3090wordWrapColumn: 243091},3092(editor, viewModel) => {3093viewModel.setSelections('test', [3094new Selection(1, 10, 1, 12),3095new Selection(1, 21, 1, 23),3096new Selection(1, 32, 1, 34)3097]);3098moveLeft(editor, viewModel, false);3099assertCursor(viewModel, [3100new Selection(1, 10, 1, 10),3101new Selection(1, 21, 1, 21),3102new Selection(1, 32, 1, 32)3103]);31043105viewModel.setSelections('test', [3106new Selection(1, 10, 1, 12),3107new Selection(1, 21, 1, 23),3108new Selection(1, 32, 1, 34)3109]);3110moveLeft(editor, viewModel, true);3111assertCursor(viewModel, [3112new Selection(1, 10, 1, 11),3113new Selection(1, 21, 1, 22),3114new Selection(1, 32, 1, 33)3115]);3116});3117});31183119test('issue #105730: move right should always skip wrap point', () => {3120const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, \nasdfghjkl,');31213122withTestCodeEditor(3123model,3124{3125wordWrap: 'wordWrapColumn',3126wordWrapColumn: 243127},3128(editor, viewModel) => {3129viewModel.setSelections('test', [3130new Selection(1, 22, 1, 22)3131]);3132moveRight(editor, viewModel, false);3133moveRight(editor, viewModel, false);3134assertCursor(viewModel, [3135new Selection(1, 24, 1, 24),3136]);31373138viewModel.setSelections('test', [3139new Selection(1, 22, 1, 22)3140]);3141moveRight(editor, viewModel, true);3142moveRight(editor, viewModel, true);3143assertCursor(viewModel, [3144new Selection(1, 22, 1, 24),3145]);3146}3147);3148});31493150test('issue #123178: sticky tab in consecutive wrapped lines', () => {3151const model = createTextModel(' aaaa aaaa', undefined, { tabSize: 4 });31523153withTestCodeEditor(3154model,3155{3156wordWrap: 'wordWrapColumn',3157wordWrapColumn: 8,3158stickyTabStops: true,3159},3160(editor, viewModel) => {3161viewModel.setSelections('test', [3162new Selection(1, 9, 1, 9)3163]);3164moveRight(editor, viewModel, false);3165assertCursor(viewModel, [3166new Selection(1, 10, 1, 10),3167]);31683169moveLeft(editor, viewModel, false);3170assertCursor(viewModel, [3171new Selection(1, 9, 1, 9),3172]);3173}3174);3175});31763177test('Cursor honors insertSpaces configuration on new line', () => {3178usingCursor({3179text: [3180' \tMy First Line\t ',3181'\tMy Second Line',3182' Third Line',3183'',3184'1'3185]3186}, (editor, model, viewModel) => {3187CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(1, 21), source: 'keyboard' });3188viewModel.type('\n', 'keyboard');3189assert.strictEqual(model.getLineContent(1), ' \tMy First Line\t ');3190assert.strictEqual(model.getLineContent(2), ' ');3191});3192});31933194test('Cursor honors insertSpaces configuration on tab', () => {3195const model = createTextModel(3196[3197' \tMy First Line\t ',3198'My Second Line123',3199' Third Line',3200'',3201'1'3202].join('\n'),3203undefined,3204{3205tabSize: 13,3206indentSize: 13,3207}3208);32093210withTestCodeEditor(model, {}, (editor, viewModel) => {3211// Tab on column 13212CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 1) });3213editor.runCommand(CoreEditingCommands.Tab, null);3214assert.strictEqual(model.getLineContent(2), ' My Second Line123');3215editor.runCommand(CoreEditingCommands.Undo, null);32163217// Tab on column 23218assert.strictEqual(model.getLineContent(2), 'My Second Line123');3219CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 2) });3220editor.runCommand(CoreEditingCommands.Tab, null);3221assert.strictEqual(model.getLineContent(2), 'M y Second Line123');3222editor.runCommand(CoreEditingCommands.Undo, null);32233224// Tab on column 33225assert.strictEqual(model.getLineContent(2), 'My Second Line123');3226CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 3) });3227editor.runCommand(CoreEditingCommands.Tab, null);3228assert.strictEqual(model.getLineContent(2), 'My Second Line123');3229editor.runCommand(CoreEditingCommands.Undo, null);32303231// Tab on column 43232assert.strictEqual(model.getLineContent(2), 'My Second Line123');3233CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 4) });3234editor.runCommand(CoreEditingCommands.Tab, null);3235assert.strictEqual(model.getLineContent(2), 'My Second Line123');3236editor.runCommand(CoreEditingCommands.Undo, null);32373238// Tab on column 53239assert.strictEqual(model.getLineContent(2), 'My Second Line123');3240CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });3241editor.runCommand(CoreEditingCommands.Tab, null);3242assert.strictEqual(model.getLineContent(2), 'My S econd Line123');3243editor.runCommand(CoreEditingCommands.Undo, null);32443245// Tab on column 53246assert.strictEqual(model.getLineContent(2), 'My Second Line123');3247CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });3248editor.runCommand(CoreEditingCommands.Tab, null);3249assert.strictEqual(model.getLineContent(2), 'My S econd Line123');3250editor.runCommand(CoreEditingCommands.Undo, null);32513252// Tab on column 133253assert.strictEqual(model.getLineContent(2), 'My Second Line123');3254CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 13) });3255editor.runCommand(CoreEditingCommands.Tab, null);3256assert.strictEqual(model.getLineContent(2), 'My Second Li ne123');3257editor.runCommand(CoreEditingCommands.Undo, null);32583259// Tab on column 143260assert.strictEqual(model.getLineContent(2), 'My Second Line123');3261CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 14) });3262editor.runCommand(CoreEditingCommands.Tab, null);3263assert.strictEqual(model.getLineContent(2), 'My Second Lin e123');3264});3265});32663267test('Enter auto-indents with insertSpaces setting 1', () => {3268const languageId = setupOnEnterLanguage(IndentAction.Indent);3269usingCursor({3270text: [3271'\thello'3272],3273languageId: languageId3274}, (editor, model, viewModel) => {3275moveTo(editor, viewModel, 1, 7, false);3276assertCursor(viewModel, new Selection(1, 7, 1, 7));32773278viewModel.type('\n', 'keyboard');3279assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');3280});3281});32823283test('Enter auto-indents with insertSpaces setting 2', () => {3284const languageId = setupOnEnterLanguage(IndentAction.None);3285usingCursor({3286text: [3287'\thello'3288],3289languageId: languageId3290}, (editor, model, viewModel) => {3291moveTo(editor, viewModel, 1, 7, false);3292assertCursor(viewModel, new Selection(1, 7, 1, 7));32933294viewModel.type('\n', 'keyboard');3295assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');3296});3297});32983299test('Enter auto-indents with insertSpaces setting 3', () => {3300const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);3301usingCursor({3302text: [3303'\thell()'3304],3305languageId: languageId3306}, (editor, model, viewModel) => {3307moveTo(editor, viewModel, 1, 7, false);3308assertCursor(viewModel, new Selection(1, 7, 1, 7));33093310viewModel.type('\n', 'keyboard');3311assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )');3312});3313});33143315test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: true', () => {3316usingCursor({3317text: [3318' \t'3319],3320}, (editor, model, viewModel) => {3321moveTo(editor, viewModel, 1, 4, false);3322viewModel.type('\n', 'keyboard');3323assert.strictEqual(model.getValue(), ' \t\n ');3324});3325});33263327test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: false', () => {3328usingCursor({3329text: [3330' \t'3331]3332}, (editor, model, viewModel) => {3333model.updateOptions({3334insertSpaces: false3335});3336moveTo(editor, viewModel, 1, 4, false);3337viewModel.type('\n', 'keyboard');3338assert.strictEqual(model.getValue(), ' \t\n\t');3339});3340});33413342test('removeAutoWhitespace off', () => {3343usingCursor({3344text: [3345' some line abc '3346],3347modelOpts: {3348trimAutoWhitespace: false3349}3350}, (editor, model, viewModel) => {33513352// Move cursor to the end, verify that we do not trim whitespaces if line has values3353moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3354viewModel.type('\n', 'keyboard');3355assert.strictEqual(model.getLineContent(1), ' some line abc ');3356assert.strictEqual(model.getLineContent(2), ' ');33573358// Try to enter again, we should trimmed previous line3359viewModel.type('\n', 'keyboard');3360assert.strictEqual(model.getLineContent(1), ' some line abc ');3361assert.strictEqual(model.getLineContent(2), ' ');3362assert.strictEqual(model.getLineContent(3), ' ');3363});3364});33653366test('removeAutoWhitespace on: removes only whitespace the cursor added 1', () => {3367usingCursor({3368text: [3369' '3370]3371}, (editor, model, viewModel) => {3372moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3373viewModel.type('\n', 'keyboard');3374assert.strictEqual(model.getLineContent(1), ' ');3375assert.strictEqual(model.getLineContent(2), ' ');33763377viewModel.type('\n', 'keyboard');3378assert.strictEqual(model.getLineContent(1), ' ');3379assert.strictEqual(model.getLineContent(2), '');3380assert.strictEqual(model.getLineContent(3), ' ');3381});3382});33833384test('issue #115033: indent and appendText', () => {3385const languageId = 'onEnterMode';33863387disposables.add(languageService.registerLanguage({ id: languageId }));3388disposables.add(languageConfigurationService.register(languageId, {3389onEnterRules: [{3390beforeText: /.*/,3391action: {3392indentAction: IndentAction.Indent,3393appendText: 'x'3394}3395}]3396}));3397usingCursor({3398text: [3399'text'3400],3401languageId: languageId,3402}, (editor, model, viewModel) => {34033404moveTo(editor, viewModel, 1, 5);3405viewModel.type('\n', 'keyboard');3406assert.strictEqual(model.getLineContent(1), 'text');3407assert.strictEqual(model.getLineContent(2), ' x');3408assertCursor(viewModel, new Position(2, 6));3409});3410});34113412test('issue #6862: Editor removes auto inserted indentation when formatting on type', () => {3413const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);3414usingCursor({3415text: [3416'function foo (params: string) {}'3417],3418languageId: languageId,3419}, (editor, model, viewModel) => {34203421moveTo(editor, viewModel, 1, 32);3422viewModel.type('\n', 'keyboard');3423assert.strictEqual(model.getLineContent(1), 'function foo (params: string) {');3424assert.strictEqual(model.getLineContent(2), ' ');3425assert.strictEqual(model.getLineContent(3), '}');34263427class TestCommand implements ICommand {34283429private _selectionId: string | null = null;34303431public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {3432builder.addEditOperation(new Range(1, 13, 1, 14), '');3433this._selectionId = builder.trackSelection(viewModel.getSelection());3434}34353436public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {3437return helper.getTrackedSelection(this._selectionId!);3438}34393440}34413442viewModel.executeCommand(new TestCommand(), 'autoFormat');3443assert.strictEqual(model.getLineContent(1), 'function foo(params: string) {');3444assert.strictEqual(model.getLineContent(2), ' ');3445assert.strictEqual(model.getLineContent(3), '}');3446});3447});34483449test('removeAutoWhitespace on: removes only whitespace the cursor added 2', () => {3450const languageId = 'testLang';3451const registration = languageService.registerLanguage({ id: languageId });3452const model = createTextModel(3453[3454' if (a) {',3455' ',3456'',3457'',3458' }'3459].join('\n'),3460languageId3461);34623463withTestCodeEditor(model, {}, (editor, viewModel) => {34643465moveTo(editor, viewModel, 3, 1);3466editor.runCommand(CoreEditingCommands.Tab, null);3467assert.strictEqual(model.getLineContent(1), ' if (a) {');3468assert.strictEqual(model.getLineContent(2), ' ');3469assert.strictEqual(model.getLineContent(3), ' ');3470assert.strictEqual(model.getLineContent(4), '');3471assert.strictEqual(model.getLineContent(5), ' }');34723473moveTo(editor, viewModel, 4, 1);3474editor.runCommand(CoreEditingCommands.Tab, null);3475assert.strictEqual(model.getLineContent(1), ' if (a) {');3476assert.strictEqual(model.getLineContent(2), ' ');3477assert.strictEqual(model.getLineContent(3), '');3478assert.strictEqual(model.getLineContent(4), ' ');3479assert.strictEqual(model.getLineContent(5), ' }');34803481moveTo(editor, viewModel, 5, model.getLineMaxColumn(5));3482viewModel.type('something', 'keyboard');3483assert.strictEqual(model.getLineContent(1), ' if (a) {');3484assert.strictEqual(model.getLineContent(2), ' ');3485assert.strictEqual(model.getLineContent(3), '');3486assert.strictEqual(model.getLineContent(4), '');3487assert.strictEqual(model.getLineContent(5), ' }something');3488});34893490registration.dispose();3491});34923493test('removeAutoWhitespace on: test 1', () => {3494const model = createTextModel(3495[3496' some line abc '3497].join('\n')3498);34993500withTestCodeEditor(model, {}, (editor, viewModel) => {35013502// Move cursor to the end, verify that we do not trim whitespaces if line has values3503moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);3504viewModel.type('\n', 'keyboard');3505assert.strictEqual(model.getLineContent(1), ' some line abc ');3506assert.strictEqual(model.getLineContent(2), ' ');35073508// Try to enter again, we should trimmed previous line3509viewModel.type('\n', 'keyboard');3510assert.strictEqual(model.getLineContent(1), ' some line abc ');3511assert.strictEqual(model.getLineContent(2), '');3512assert.strictEqual(model.getLineContent(3), ' ');35133514// More whitespaces3515editor.runCommand(CoreEditingCommands.Tab, null);3516assert.strictEqual(model.getLineContent(1), ' some line abc ');3517assert.strictEqual(model.getLineContent(2), '');3518assert.strictEqual(model.getLineContent(3), ' ');35193520// Enter and verify that trimmed again3521viewModel.type('\n', 'keyboard');3522assert.strictEqual(model.getLineContent(1), ' some line abc ');3523assert.strictEqual(model.getLineContent(2), '');3524assert.strictEqual(model.getLineContent(3), '');3525assert.strictEqual(model.getLineContent(4), ' ');35263527// Trimmed if we will keep only text3528moveTo(editor, viewModel, 1, 5);3529viewModel.type('\n', 'keyboard');3530assert.strictEqual(model.getLineContent(1), ' ');3531assert.strictEqual(model.getLineContent(2), ' some line abc ');3532assert.strictEqual(model.getLineContent(3), '');3533assert.strictEqual(model.getLineContent(4), '');3534assert.strictEqual(model.getLineContent(5), '');35353536// Trimmed if we will keep only text by selection3537moveTo(editor, viewModel, 2, 5);3538moveTo(editor, viewModel, 3, 1, true);3539viewModel.type('\n', 'keyboard');3540assert.strictEqual(model.getLineContent(1), ' ');3541assert.strictEqual(model.getLineContent(2), ' ');3542assert.strictEqual(model.getLineContent(3), ' ');3543assert.strictEqual(model.getLineContent(4), '');3544assert.strictEqual(model.getLineContent(5), '');3545});3546});35473548test('issue #15118: remove auto whitespace when pasting entire line', () => {3549const model = createTextModel(3550[3551' function f() {',3552' // I\'m gonna copy this line',3553' return 3;',3554' }',3555].join('\n')3556);35573558withTestCodeEditor(model, {}, (editor, viewModel) => {35593560moveTo(editor, viewModel, 3, model.getLineMaxColumn(3));3561viewModel.type('\n', 'keyboard');35623563assert.strictEqual(model.getValue(), [3564' function f() {',3565' // I\'m gonna copy this line',3566' return 3;',3567' ',3568' }',3569].join('\n'));3570assertCursor(viewModel, new Position(4, model.getLineMaxColumn(4)));35713572viewModel.paste(' // I\'m gonna copy this line\n', true);3573assert.strictEqual(model.getValue(), [3574' function f() {',3575' // I\'m gonna copy this line',3576' return 3;',3577' // I\'m gonna copy this line',3578'',3579' }',3580].join('\n'));3581assertCursor(viewModel, new Position(5, 1));3582});3583});35843585test('issue #40695: maintain cursor position when copying lines using ctrl+c, ctrl+v', () => {3586const model = createTextModel(3587[3588' function f() {',3589' // I\'m gonna copy this line',3590' // Another line',3591' return 3;',3592' }',3593].join('\n')3594);35953596withTestCodeEditor(model, {}, (editor, viewModel) => {35973598editor.setSelections([new Selection(4, 10, 4, 10)]);3599viewModel.paste(' // I\'m gonna copy this line\n', true);36003601assert.strictEqual(model.getValue(), [3602' function f() {',3603' // I\'m gonna copy this line',3604' // Another line',3605' // I\'m gonna copy this line',3606' return 3;',3607' }',3608].join('\n'));3609assertCursor(viewModel, new Position(5, 10));3610});3611});36123613test('UseTabStops is off', () => {3614const model = createTextModel(3615[3616' x',3617' a ',3618' '3619].join('\n')3620);36213622withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {3623// DeleteLeft removes just one whitespace3624moveTo(editor, viewModel, 2, 9);3625editor.runCommand(CoreEditingCommands.DeleteLeft, null);3626assert.strictEqual(model.getLineContent(2), ' a ');3627});3628});36293630test('Backspace removes whitespaces with tab size', () => {3631const model = createTextModel(3632[3633' \t \t x',3634' a ',3635' '3636].join('\n')3637);36383639withTestCodeEditor(model, { useTabStops: true }, (editor, viewModel) => {3640// DeleteLeft does not remove tab size, because some text exists before3641moveTo(editor, viewModel, 2, model.getLineContent(2).length + 1);3642editor.runCommand(CoreEditingCommands.DeleteLeft, null);3643assert.strictEqual(model.getLineContent(2), ' a ');36443645// DeleteLeft removes tab size = 43646moveTo(editor, viewModel, 2, 9);3647editor.runCommand(CoreEditingCommands.DeleteLeft, null);3648assert.strictEqual(model.getLineContent(2), ' a ');36493650// DeleteLeft removes tab size = 43651editor.runCommand(CoreEditingCommands.DeleteLeft, null);3652assert.strictEqual(model.getLineContent(2), 'a ');36533654// Undo DeleteLeft - get us back to original indentation3655editor.runCommand(CoreEditingCommands.Undo, null);3656assert.strictEqual(model.getLineContent(2), ' a ');36573658// Nothing is broken when cursor is in (1,1)3659moveTo(editor, viewModel, 1, 1);3660editor.runCommand(CoreEditingCommands.DeleteLeft, null);3661assert.strictEqual(model.getLineContent(1), ' \t \t x');36623663// DeleteLeft stops at tab stops even in mixed whitespace case3664moveTo(editor, viewModel, 1, 10);3665editor.runCommand(CoreEditingCommands.DeleteLeft, null);3666assert.strictEqual(model.getLineContent(1), ' \t \t x');36673668editor.runCommand(CoreEditingCommands.DeleteLeft, null);3669assert.strictEqual(model.getLineContent(1), ' \t \tx');36703671editor.runCommand(CoreEditingCommands.DeleteLeft, null);3672assert.strictEqual(model.getLineContent(1), ' \tx');36733674editor.runCommand(CoreEditingCommands.DeleteLeft, null);3675assert.strictEqual(model.getLineContent(1), 'x');36763677// DeleteLeft on last line3678moveTo(editor, viewModel, 3, model.getLineContent(3).length + 1);3679editor.runCommand(CoreEditingCommands.DeleteLeft, null);3680assert.strictEqual(model.getLineContent(3), '');36813682// DeleteLeft with removing new line symbol3683editor.runCommand(CoreEditingCommands.DeleteLeft, null);3684assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x\n a ');36853686// In case of selection DeleteLeft only deletes selected text3687moveTo(editor, viewModel, 2, 3);3688moveTo(editor, viewModel, 2, 4, true);3689editor.runCommand(CoreEditingCommands.DeleteLeft, null);3690assert.strictEqual(model.getLineContent(2), ' a ');3691});3692});36933694test('PR #5423: Auto indent + undo + redo is funky', () => {3695const model = createTextModel(3696[3697''3698].join('\n'),3699undefined,3700{3701insertSpaces: false,3702}3703);37043705withTestCodeEditor(model, {}, (editor, viewModel) => {3706viewModel.type('\n', 'keyboard');3707assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');37083709editor.runCommand(CoreEditingCommands.Tab, null);3710assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');37113712viewModel.type('y', 'keyboard');3713assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2');37143715viewModel.type('\n', 'keyboard');3716assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3');37173718viewModel.type('x');3719assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4');37203721CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});3722assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5');37233724editor.runCommand(CoreEditingCommands.DeleteLeft, null);3725assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert6');37263727editor.runCommand(CoreEditingCommands.DeleteLeft, null);3728assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tyx', 'assert7');37293730editor.runCommand(CoreEditingCommands.DeleteLeft, null);3731assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert8');37323733editor.runCommand(CoreEditingCommands.DeleteLeft, null);3734assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert9');37353736editor.runCommand(CoreEditingCommands.DeleteLeft, null);3737assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert10');37383739editor.runCommand(CoreEditingCommands.Undo, null);3740assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert11');37413742editor.runCommand(CoreEditingCommands.Undo, null);3743assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert12');37443745editor.runCommand(CoreEditingCommands.Undo, null);3746assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert13');37473748editor.runCommand(CoreEditingCommands.Redo, null);3749assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert14');37503751editor.runCommand(CoreEditingCommands.Redo, null);3752assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert15');37533754editor.runCommand(CoreEditingCommands.Redo, null);3755assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert16');3756});3757});37583759test('issue #90973: Undo brings back model alternative version', () => {3760const model = createTextModel(3761[3762''3763].join('\n'),3764undefined,3765{3766insertSpaces: false,3767}3768);37693770withTestCodeEditor(model, {}, (editor, viewModel) => {3771const beforeVersion = model.getVersionId();3772const beforeAltVersion = model.getAlternativeVersionId();3773viewModel.type('Hello', 'keyboard');3774editor.runCommand(CoreEditingCommands.Undo, null);3775const afterVersion = model.getVersionId();3776const afterAltVersion = model.getAlternativeVersionId();37773778assert.notStrictEqual(beforeVersion, afterVersion);3779assert.strictEqual(beforeAltVersion, afterAltVersion);3780});3781});37823783test('Enter honors increaseIndentPattern', () => {3784usingCursor({3785text: [3786'if (true) {',3787'\tif (true) {'3788],3789languageId: indentRulesLanguageId,3790modelOpts: { insertSpaces: false },3791editorOpts: { autoIndent: 'full' }3792}, (editor, model, viewModel) => {3793moveTo(editor, viewModel, 1, 12, false);3794assertCursor(viewModel, new Selection(1, 12, 1, 12));37953796viewModel.type('\n', 'keyboard');3797model.tokenization.forceTokenization(model.getLineCount());3798assertCursor(viewModel, new Selection(2, 2, 2, 2));37993800moveTo(editor, viewModel, 3, 13, false);3801assertCursor(viewModel, new Selection(3, 13, 3, 13));38023803viewModel.type('\n', 'keyboard');3804assertCursor(viewModel, new Selection(4, 3, 4, 3));3805});3806});38073808test('Type honors decreaseIndentPattern', () => {3809usingCursor({3810text: [3811'if (true) {',3812'\t'3813],3814languageId: indentRulesLanguageId,3815editorOpts: { autoIndent: 'full' }3816}, (editor, model, viewModel) => {3817moveTo(editor, viewModel, 2, 2, false);3818assertCursor(viewModel, new Selection(2, 2, 2, 2));38193820viewModel.type('}', 'keyboard');3821assertCursor(viewModel, new Selection(2, 2, 2, 2));3822assert.strictEqual(model.getLineContent(2), '}', '001');3823});3824});38253826test('Enter honors unIndentedLinePattern', () => {3827usingCursor({3828text: [3829'if (true) {',3830'\t\t\treturn true'3831],3832languageId: indentRulesLanguageId,3833modelOpts: { insertSpaces: false },3834editorOpts: { autoIndent: 'full' }3835}, (editor, model, viewModel) => {3836moveTo(editor, viewModel, 2, 15, false);3837assertCursor(viewModel, new Selection(2, 15, 2, 15));38383839viewModel.type('\n', 'keyboard');3840assertCursor(viewModel, new Selection(3, 2, 3, 2));3841});3842});38433844test('Enter honors indentNextLinePattern', () => {3845usingCursor({3846text: [3847'if (true)',3848'\treturn true;',3849'if (true)',3850'\t\t\t\treturn true'3851],3852languageId: indentRulesLanguageId,3853modelOpts: { insertSpaces: false },3854editorOpts: { autoIndent: 'full' }3855}, (editor, model, viewModel) => {3856moveTo(editor, viewModel, 2, 14, false);3857assertCursor(viewModel, new Selection(2, 14, 2, 14));38583859viewModel.type('\n', 'keyboard');3860model.tokenization.forceTokenization(model.getLineCount());3861assertCursor(viewModel, new Selection(3, 1, 3, 1));38623863moveTo(editor, viewModel, 5, 16, false);3864assertCursor(viewModel, new Selection(5, 16, 5, 16));38653866viewModel.type('\n', 'keyboard');3867assertCursor(viewModel, new Selection(6, 2, 6, 2));3868});3869});38703871test('Enter honors indentNextLinePattern 2', () => {3872const model = createTextModel(3873[3874'if (true)',3875'\tif (true)'3876].join('\n'),3877indentRulesLanguageId,3878{3879insertSpaces: false,3880}3881);38823883withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {3884moveTo(editor, viewModel, 2, 11, false);3885assertCursor(viewModel, new Selection(2, 11, 2, 11));38863887viewModel.type('\n', 'keyboard');3888model.tokenization.forceTokenization(model.getLineCount());3889assertCursor(viewModel, new Selection(3, 3, 3, 3));38903891viewModel.type('console.log();', 'keyboard');3892viewModel.type('\n', 'keyboard');3893assertCursor(viewModel, new Selection(4, 1, 4, 1));3894});3895});38963897test('Enter honors intential indent', () => {3898usingCursor({3899text: [3900'if (true) {',3901'\tif (true) {',3902'return true;',3903'}}'3904],3905languageId: indentRulesLanguageId,3906editorOpts: { autoIndent: 'full' }3907}, (editor, model, viewModel) => {3908moveTo(editor, viewModel, 3, 13, false);3909assertCursor(viewModel, new Selection(3, 13, 3, 13));39103911viewModel.type('\n', 'keyboard');3912assertCursor(viewModel, new Selection(4, 1, 4, 1));3913assert.strictEqual(model.getLineContent(3), 'return true;', '001');3914});3915});39163917test('Enter supports selection 1', () => {3918usingCursor({3919text: [3920'if (true) {',3921'\tif (true) {',3922'\t\treturn true;',3923'\t}a}'3924],3925languageId: indentRulesLanguageId,3926modelOpts: { insertSpaces: false }3927}, (editor, model, viewModel) => {3928moveTo(editor, viewModel, 4, 3, false);3929moveTo(editor, viewModel, 4, 4, true);3930assertCursor(viewModel, new Selection(4, 3, 4, 4));39313932viewModel.type('\n', 'keyboard');3933assertCursor(viewModel, new Selection(5, 1, 5, 1));3934assert.strictEqual(model.getLineContent(4), '\t}', '001');3935});3936});39373938test('Enter supports selection 2', () => {3939usingCursor({3940text: [3941'if (true) {',3942'\tif (true) {'3943],3944languageId: indentRulesLanguageId,3945modelOpts: { insertSpaces: false }3946}, (editor, model, viewModel) => {3947moveTo(editor, viewModel, 2, 12, false);3948moveTo(editor, viewModel, 2, 13, true);3949assertCursor(viewModel, new Selection(2, 12, 2, 13));39503951viewModel.type('\n', 'keyboard');3952assertCursor(viewModel, new Selection(3, 3, 3, 3));39533954viewModel.type('\n', 'keyboard');3955assertCursor(viewModel, new Selection(4, 3, 4, 3));3956});3957});39583959test('Enter honors tabSize and insertSpaces 1', () => {3960usingCursor({3961text: [3962'if (true) {',3963'\tif (true) {'3964],3965languageId: indentRulesLanguageId,3966}, (editor, model, viewModel) => {3967moveTo(editor, viewModel, 1, 12, false);3968assertCursor(viewModel, new Selection(1, 12, 1, 12));39693970viewModel.type('\n', 'keyboard');3971assertCursor(viewModel, new Selection(2, 5, 2, 5));39723973model.tokenization.forceTokenization(model.getLineCount());39743975moveTo(editor, viewModel, 3, 13, false);3976assertCursor(viewModel, new Selection(3, 13, 3, 13));39773978viewModel.type('\n', 'keyboard');3979assertCursor(viewModel, new Selection(4, 9, 4, 9));3980});3981});39823983test('Enter honors tabSize and insertSpaces 2', () => {3984usingCursor({3985text: [3986'if (true) {',3987' if (true) {'3988],3989languageId: indentRulesLanguageId,3990}, (editor, model, viewModel) => {3991moveTo(editor, viewModel, 1, 12, false);3992assertCursor(viewModel, new Selection(1, 12, 1, 12));39933994viewModel.type('\n', 'keyboard');3995model.tokenization.forceTokenization(model.getLineCount());3996assertCursor(viewModel, new Selection(2, 5, 2, 5));39973998moveTo(editor, viewModel, 3, 16, false);3999assertCursor(viewModel, new Selection(3, 16, 3, 16));40004001viewModel.type('\n', 'keyboard');4002assert.strictEqual(model.getLineContent(3), ' if (true) {');4003assertCursor(viewModel, new Selection(4, 9, 4, 9));4004});4005});40064007test('Enter honors tabSize and insertSpaces 3', () => {4008usingCursor({4009text: [4010'if (true) {',4011' if (true) {'4012],4013languageId: indentRulesLanguageId,4014modelOpts: { insertSpaces: false }4015}, (editor, model, viewModel) => {4016moveTo(editor, viewModel, 1, 12, false);4017assertCursor(viewModel, new Selection(1, 12, 1, 12));40184019viewModel.type('\n', 'keyboard');4020model.tokenization.forceTokenization(model.getLineCount());4021assertCursor(viewModel, new Selection(2, 2, 2, 2));40224023moveTo(editor, viewModel, 3, 16, false);4024assertCursor(viewModel, new Selection(3, 16, 3, 16));40254026viewModel.type('\n', 'keyboard');4027assert.strictEqual(model.getLineContent(3), ' if (true) {');4028assertCursor(viewModel, new Selection(4, 3, 4, 3));4029});4030});40314032test('Enter supports intentional indentation', () => {4033usingCursor({4034text: [4035'\tif (true) {',4036'\t\tswitch(true) {',4037'\t\t\tcase true:',4038'\t\t\t\tbreak;',4039'\t\t}',4040'\t}'4041],4042languageId: indentRulesLanguageId,4043modelOpts: { insertSpaces: false },4044editorOpts: { autoIndent: 'full' }4045}, (editor, model, viewModel) => {4046moveTo(editor, viewModel, 5, 4, false);4047assertCursor(viewModel, new Selection(5, 4, 5, 4));40484049viewModel.type('\n', 'keyboard');4050assert.strictEqual(model.getLineContent(5), '\t\t}');4051assertCursor(viewModel, new Selection(6, 3, 6, 3));4052});4053});40544055test('Enter should not adjust cursor position when press enter in the middle of a line 1', () => {4056usingCursor({4057text: [4058'if (true) {',4059'\tif (true) {',4060'\t\treturn true;',4061'\t}a}'4062],4063languageId: indentRulesLanguageId,4064modelOpts: { insertSpaces: false }4065}, (editor, model, viewModel) => {4066moveTo(editor, viewModel, 3, 9, false);4067assertCursor(viewModel, new Selection(3, 9, 3, 9));40684069viewModel.type('\n', 'keyboard');4070assertCursor(viewModel, new Selection(4, 3, 4, 3));4071assert.strictEqual(model.getLineContent(4), '\t\t true;', '001');4072});4073});40744075test('Enter should not adjust cursor position when press enter in the middle of a line 2', () => {4076usingCursor({4077text: [4078'if (true) {',4079'\tif (true) {',4080'\t\treturn true;',4081'\t}a}'4082],4083languageId: indentRulesLanguageId,4084modelOpts: { insertSpaces: false }4085}, (editor, model, viewModel) => {4086moveTo(editor, viewModel, 3, 3, false);4087assertCursor(viewModel, new Selection(3, 3, 3, 3));40884089viewModel.type('\n', 'keyboard');4090assertCursor(viewModel, new Selection(4, 3, 4, 3));4091assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');4092});4093});40944095test('Enter should not adjust cursor position when press enter in the middle of a line 3', () => {4096usingCursor({4097text: [4098'if (true) {',4099' if (true) {',4100' return true;',4101' }a}'4102],4103languageId: indentRulesLanguageId4104}, (editor, model, viewModel) => {4105moveTo(editor, viewModel, 3, 11, false);4106assertCursor(viewModel, new Selection(3, 11, 3, 11));41074108viewModel.type('\n', 'keyboard');4109assertCursor(viewModel, new Selection(4, 5, 4, 5));4110assert.strictEqual(model.getLineContent(4), ' true;', '001');4111});4112});41134114test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 1', () => {4115usingCursor({4116text: [4117'if (true) {',4118'\tif (true) {',4119'\t\treturn true;',4120'\t}a}'4121],4122languageId: indentRulesLanguageId,4123modelOpts: { insertSpaces: false }4124}, (editor, model, viewModel) => {4125moveTo(editor, viewModel, 3, 2, false);4126assertCursor(viewModel, new Selection(3, 2, 3, 2));41274128viewModel.type('\n', 'keyboard');4129assertCursor(viewModel, new Selection(4, 2, 4, 2));4130assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');41314132moveTo(editor, viewModel, 4, 1, false);4133assertCursor(viewModel, new Selection(4, 1, 4, 1));41344135viewModel.type('\n', 'keyboard');4136assertCursor(viewModel, new Selection(5, 1, 5, 1));4137assert.strictEqual(model.getLineContent(5), '\t\treturn true;', '002');4138});4139});41404141test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 2', () => {4142usingCursor({4143text: [4144'\tif (true) {',4145'\t\tif (true) {',4146'\t \treturn true;',4147'\t\t}a}'4148],4149languageId: indentRulesLanguageId,4150modelOpts: { insertSpaces: false }4151}, (editor, model, viewModel) => {4152moveTo(editor, viewModel, 3, 4, false);4153assertCursor(viewModel, new Selection(3, 4, 3, 4));41544155viewModel.type('\n', 'keyboard');4156assertCursor(viewModel, new Selection(4, 3, 4, 3));4157assert.strictEqual(model.getLineContent(4), '\t\t\treturn true;', '001');41584159moveTo(editor, viewModel, 4, 1, false);4160assertCursor(viewModel, new Selection(4, 1, 4, 1));41614162viewModel.type('\n', 'keyboard');4163assertCursor(viewModel, new Selection(5, 1, 5, 1));4164assert.strictEqual(model.getLineContent(5), '\t\t\treturn true;', '002');4165});4166});41674168test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 3', () => {4169usingCursor({4170text: [4171'if (true) {',4172' if (true) {',4173' return true;',4174'}a}'4175],4176languageId: indentRulesLanguageId4177}, (editor, model, viewModel) => {4178moveTo(editor, viewModel, 3, 2, false);4179assertCursor(viewModel, new Selection(3, 2, 3, 2));41804181viewModel.type('\n', 'keyboard');4182assertCursor(viewModel, new Selection(4, 2, 4, 2));4183assert.strictEqual(model.getLineContent(4), ' return true;', '001');41844185moveTo(editor, viewModel, 4, 3, false);4186viewModel.type('\n', 'keyboard');4187assertCursor(viewModel, new Selection(5, 3, 5, 3));4188assert.strictEqual(model.getLineContent(5), ' return true;', '002');4189});4190});41914192test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 4', () => {4193usingCursor({4194text: [4195'if (true) {',4196' if (true) {',4197'\t return true;',4198'}a}',4199'',4200'if (true) {',4201' if (true) {',4202'\t return true;',4203'}a}'4204],4205languageId: indentRulesLanguageId,4206modelOpts: {4207tabSize: 2,4208indentSize: 24209}4210}, (editor, model, viewModel) => {4211moveTo(editor, viewModel, 3, 3, false);4212assertCursor(viewModel, new Selection(3, 3, 3, 3));42134214viewModel.type('\n', 'keyboard');4215assertCursor(viewModel, new Selection(4, 4, 4, 4));4216assert.strictEqual(model.getLineContent(4), ' return true;', '001');42174218moveTo(editor, viewModel, 9, 4, false);4219viewModel.type('\n', 'keyboard');4220assertCursor(viewModel, new Selection(10, 5, 10, 5));4221assert.strictEqual(model.getLineContent(10), ' return true;', '001');4222});4223});42244225test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 5', () => {4226usingCursor({4227text: [4228'if (true) {',4229' if (true) {',4230' return true;',4231' return true;',4232''4233],4234languageId: indentRulesLanguageId,4235modelOpts: { tabSize: 2 }4236}, (editor, model, viewModel) => {4237moveTo(editor, viewModel, 3, 5, false);4238moveTo(editor, viewModel, 4, 3, true);4239assertCursor(viewModel, new Selection(3, 5, 4, 3));42404241viewModel.type('\n', 'keyboard');4242assertCursor(viewModel, new Selection(4, 3, 4, 3));4243assert.strictEqual(model.getLineContent(4), ' return true;', '001');4244});4245});42464247test('issue microsoft/monaco-editor#108 part 1/2: Auto indentation on Enter with selection is half broken', () => {4248usingCursor({4249text: [4250'function baz() {',4251'\tvar x = 1;',4252'\t\t\t\t\t\t\treturn x;',4253'}'4254],4255modelOpts: {4256insertSpaces: false,4257},4258languageId: indentRulesLanguageId,4259}, (editor, model, viewModel) => {4260moveTo(editor, viewModel, 3, 8, false);4261moveTo(editor, viewModel, 2, 12, true);4262assertCursor(viewModel, new Selection(3, 8, 2, 12));42634264viewModel.type('\n', 'keyboard');4265assert.strictEqual(model.getLineContent(3), '\treturn x;');4266assertCursor(viewModel, new Position(3, 2));4267});4268});42694270test('issue microsoft/monaco-editor#108 part 2/2: Auto indentation on Enter with selection is half broken', () => {4271usingCursor({4272text: [4273'function baz() {',4274'\tvar x = 1;',4275'\t\t\t\t\t\t\treturn x;',4276'}'4277],4278modelOpts: {4279insertSpaces: false,4280},4281languageId: indentRulesLanguageId,4282}, (editor, model, viewModel) => {4283moveTo(editor, viewModel, 2, 12, false);4284moveTo(editor, viewModel, 3, 8, true);4285assertCursor(viewModel, new Selection(2, 12, 3, 8));42864287viewModel.type('\n', 'keyboard');4288assert.strictEqual(model.getLineContent(3), '\treturn x;');4289assertCursor(viewModel, new Position(3, 2));4290});4291});42924293test('onEnter works if there are no indentation rules', () => {4294usingCursor({4295text: [4296'<?',4297'\tif (true) {',4298'\t\techo $hi;',4299'\t\techo $bye;',4300'\t}',4301'?>'4302],4303modelOpts: { insertSpaces: false }4304}, (editor, model, viewModel) => {4305moveTo(editor, viewModel, 5, 3, false);4306assertCursor(viewModel, new Selection(5, 3, 5, 3));43074308viewModel.type('\n', 'keyboard');4309assert.strictEqual(model.getLineContent(6), '\t');4310assertCursor(viewModel, new Selection(6, 2, 6, 2));4311assert.strictEqual(model.getLineContent(5), '\t}');4312});4313});43144315test('onEnter works if there are no indentation rules 2', () => {4316usingCursor({4317text: [4318' if (5)',4319' return 5;',4320' '4321],4322modelOpts: { insertSpaces: false }4323}, (editor, model, viewModel) => {4324moveTo(editor, viewModel, 3, 2, false);4325assertCursor(viewModel, new Selection(3, 2, 3, 2));43264327viewModel.type('\n', 'keyboard');4328assertCursor(viewModel, new Selection(4, 2, 4, 2));4329assert.strictEqual(model.getLineContent(4), '\t');4330});4331});43324333test('bug #16543: Tab should indent to correct indentation spot immediately', () => {4334const model = createTextModel(4335[4336'function baz() {',4337'\tfunction hello() { // something here',4338'\t',4339'',4340'\t}',4341'}'4342].join('\n'),4343indentRulesLanguageId,4344{4345insertSpaces: false,4346}4347);43484349withTestCodeEditor(model, {}, (editor, viewModel) => {4350moveTo(editor, viewModel, 4, 1, false);4351assertCursor(viewModel, new Selection(4, 1, 4, 1));43524353editor.runCommand(CoreEditingCommands.Tab, null);4354assert.strictEqual(model.getLineContent(4), '\t\t');4355});4356});435743584359test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4360const model = createTextModel(4361[4362'\tfunction baz() {',4363'\t\tfunction hello() { // something here',4364'\t\t',4365'\t',4366'\t\t}',4367'\t}'4368].join('\n'),4369indentRulesLanguageId,4370{4371insertSpaces: false,4372}4373);43744375withTestCodeEditor(model, {}, (editor, viewModel) => {4376moveTo(editor, viewModel, 4, 2, false);4377assertCursor(viewModel, new Selection(4, 2, 4, 2));43784379editor.runCommand(CoreEditingCommands.Tab, null);4380assert.strictEqual(model.getLineContent(4), '\t\t\t');4381});4382});438343844385test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4386const model = createTextModel(4387[4388'\tfunction baz() {',4389'\t\tfunction hello() { // something here',4390'\t\t',4391' ',4392'\t\t}',4393'\t}'4394].join('\n'),4395indentRulesLanguageId,4396{4397insertSpaces: false,4398}4399);44004401withTestCodeEditor(model, {}, (editor, viewModel) => {4402moveTo(editor, viewModel, 4, 1, false);4403assertCursor(viewModel, new Selection(4, 1, 4, 1));44044405editor.runCommand(CoreEditingCommands.Tab, null);4406assert.strictEqual(model.getLineContent(4), '\t\t\t');4407});4408});44094410test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4411const model = createTextModel(4412[4413'\tfunction baz() {',4414'\t\tfunction hello() { // something here',4415'\t\t',4416'\t\t\t',4417'\t\t}',4418'\t}'4419].join('\n'),4420indentRulesLanguageId,4421{4422insertSpaces: false,4423}4424);44254426withTestCodeEditor(model, {}, (editor, viewModel) => {4427moveTo(editor, viewModel, 4, 3, false);4428assertCursor(viewModel, new Selection(4, 3, 4, 3));44294430editor.runCommand(CoreEditingCommands.Tab, null);4431assert.strictEqual(model.getLineContent(4), '\t\t\t\t');4432});4433});44344435test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {4436const model = createTextModel(4437[4438'\tfunction baz() {',4439'\t\tfunction hello() { // something here',4440'\t\t',4441'\t\t\t\t',4442'\t\t}',4443'\t}'4444].join('\n'),4445indentRulesLanguageId,4446{4447insertSpaces: false,4448}4449);44504451withTestCodeEditor(model, {}, (editor, viewModel) => {4452moveTo(editor, viewModel, 4, 4, false);4453assertCursor(viewModel, new Selection(4, 4, 4, 4));44544455editor.runCommand(CoreEditingCommands.Tab, null);4456assert.strictEqual(model.getLineContent(4), '\t\t\t\t\t');4457});4458});44594460test('bug #31015: When pressing Tab on lines and Enter rules are avail, indent straight to the right spotTab', () => {4461const onEnterLanguageId = setupOnEnterLanguage(IndentAction.Indent);4462const model = createTextModel(4463[4464' if (a) {',4465' ',4466'',4467'',4468' }'4469].join('\n'),4470onEnterLanguageId4471);44724473withTestCodeEditor(model, {}, (editor, viewModel) => {44744475moveTo(editor, viewModel, 3, 1);4476editor.runCommand(CoreEditingCommands.Tab, null);4477assert.strictEqual(model.getLineContent(1), ' if (a) {');4478assert.strictEqual(model.getLineContent(2), ' ');4479assert.strictEqual(model.getLineContent(3), ' ');4480assert.strictEqual(model.getLineContent(4), '');4481assert.strictEqual(model.getLineContent(5), ' }');4482});4483});44844485test('type honors indentation rules: ruby keywords', () => {4486const rubyLanguageId = setupIndentRulesLanguage('ruby', {4487increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/,4488decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/4489});4490const model = createTextModel(4491[4492'class Greeter',4493' def initialize(name)',4494' @name = name',4495' en'4496].join('\n'),4497rubyLanguageId4498);44994500withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4501moveTo(editor, viewModel, 4, 7, false);4502assertCursor(viewModel, new Selection(4, 7, 4, 7));45034504viewModel.type('d', 'keyboard');4505assert.strictEqual(model.getLineContent(4), ' end');4506});4507});45084509test('Auto indent on type: increaseIndentPattern has higher priority than decreaseIndent when inheriting', () => {4510usingCursor({4511text: [4512'\tif (true) {',4513'\t\tconsole.log();',4514'\t} else if {',4515'\t\tconsole.log()',4516'\t}'4517],4518languageId: indentRulesLanguageId4519}, (editor, model, viewModel) => {4520moveTo(editor, viewModel, 5, 3, false);4521assertCursor(viewModel, new Selection(5, 3, 5, 3));45224523viewModel.type('e', 'keyboard');4524assertCursor(viewModel, new Selection(5, 4, 5, 4));4525assert.strictEqual(model.getLineContent(5), '\t}e', 'This line should not decrease indent');4526});4527});45284529test('type honors users indentation adjustment', () => {4530usingCursor({4531text: [4532'\tif (true ||',4533'\t ) {',4534'\t}',4535'if (true ||',4536') {',4537'}'4538],4539languageId: indentRulesLanguageId4540}, (editor, model, viewModel) => {4541moveTo(editor, viewModel, 2, 3, false);4542assertCursor(viewModel, new Selection(2, 3, 2, 3));45434544viewModel.type(' ', 'keyboard');4545assertCursor(viewModel, new Selection(2, 4, 2, 4));4546assert.strictEqual(model.getLineContent(2), '\t ) {', 'This line should not decrease indent');4547});4548});45494550test('bug 29972: if a line is line comment, open bracket should not indent next line', () => {4551usingCursor({4552text: [4553'if (true) {',4554'\t// {',4555'\t\t'4556],4557languageId: indentRulesLanguageId,4558editorOpts: { autoIndent: 'full' }4559}, (editor, model, viewModel) => {4560moveTo(editor, viewModel, 3, 3, false);4561assertCursor(viewModel, new Selection(3, 3, 3, 3));45624563viewModel.type('}', 'keyboard');4564assertCursor(viewModel, new Selection(3, 2, 3, 2));4565assert.strictEqual(model.getLineContent(3), '}');4566});4567});456845694570test('issue #38261: TAB key results in bizarre indentation in C++ mode ', () => {4571const languageId = 'indentRulesMode';45724573disposables.add(languageService.registerLanguage({ id: languageId }));4574disposables.add(languageConfigurationService.register(languageId, {4575brackets: [4576['{', '}'],4577['[', ']'],4578['(', ')']4579],4580indentationRules: {4581increaseIndentPattern: new RegExp('(^.*\\{[^}]*$)'),4582decreaseIndentPattern: new RegExp('^\\s*\\}')4583}4584}));45854586const model = createTextModel(4587[4588'int main() {',4589' return 0;',4590'}',4591'',4592'bool Foo::bar(const string &a,',4593' const string &b) {',4594' foo();',4595'',4596')',4597].join('\n'),4598languageId,4599{4600tabSize: 2,4601indentSize: 24602}4603);46044605withTestCodeEditor(model, { autoIndent: 'advanced' }, (editor, viewModel) => {4606moveTo(editor, viewModel, 8, 1, false);4607assertCursor(viewModel, new Selection(8, 1, 8, 1));46084609editor.runCommand(CoreEditingCommands.Tab, null);4610assert.strictEqual(model.getValue(),4611[4612'int main() {',4613' return 0;',4614'}',4615'',4616'bool Foo::bar(const string &a,',4617' const string &b) {',4618' foo();',4619' ',4620')',4621].join('\n')4622);4623assert.deepStrictEqual(viewModel.getSelection(), new Selection(8, 3, 8, 3));4624});4625});46264627test('issue #57197: indent rules regex should be stateless', () => {4628const languageId = setupIndentRulesLanguage('lang', {4629decreaseIndentPattern: /^\s*}$/gm,4630increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■⬜□☐▪▫–—≡→›✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm,4631});4632usingCursor({4633text: [4634'Project:',4635],4636languageId: languageId,4637modelOpts: { insertSpaces: false },4638editorOpts: { autoIndent: 'full' }4639}, (editor, model, viewModel) => {4640moveTo(editor, viewModel, 1, 9, false);4641assertCursor(viewModel, new Selection(1, 9, 1, 9));46424643viewModel.type('\n', 'keyboard');4644model.tokenization.forceTokenization(model.getLineCount());4645assertCursor(viewModel, new Selection(2, 2, 2, 2));46464647moveTo(editor, viewModel, 1, 9, false);4648assertCursor(viewModel, new Selection(1, 9, 1, 9));4649viewModel.type('\n', 'keyboard');4650model.tokenization.forceTokenization(model.getLineCount());4651assertCursor(viewModel, new Selection(2, 2, 2, 2));4652});4653});46544655test('typing in json', () => {4656const languageId = 'indentRulesMode';46574658disposables.add(languageService.registerLanguage({ id: languageId }));4659disposables.add(languageConfigurationService.register(languageId, {4660brackets: [4661['{', '}'],4662['[', ']'],4663['(', ')']4664],4665indentationRules: {4666increaseIndentPattern: new RegExp('({+(?=([^"]*"[^"]*")*[^"}]*$))|(\\[+(?=([^"]*"[^"]*")*[^"\\]]*$))'),4667decreaseIndentPattern: new RegExp('^\\s*[}\\]],?\\s*$')4668}4669}));46704671const model = createTextModel(4672[4673'{',4674' "scripts: {"',4675' "watch": "a {"',4676' "build{": "b"',4677' "tasks": []',4678' "tasks": ["a"]',4679' "}"',4680'"}"'4681].join('\n'),4682languageId,4683{4684tabSize: 2,4685indentSize: 24686}4687);46884689withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4690moveTo(editor, viewModel, 3, 19, false);4691assertCursor(viewModel, new Selection(3, 19, 3, 19));46924693viewModel.type('\n', 'keyboard');4694assert.deepStrictEqual(model.getLineContent(4), ' ');46954696moveTo(editor, viewModel, 5, 18, false);4697assertCursor(viewModel, new Selection(5, 18, 5, 18));46984699viewModel.type('\n', 'keyboard');4700assert.deepStrictEqual(model.getLineContent(6), ' ');47014702moveTo(editor, viewModel, 7, 15, false);4703assertCursor(viewModel, new Selection(7, 15, 7, 15));47044705viewModel.type('\n', 'keyboard');4706assert.deepStrictEqual(model.getLineContent(8), ' ');4707assert.deepStrictEqual(model.getLineContent(9), ' ]');47084709moveTo(editor, viewModel, 10, 18, false);4710assertCursor(viewModel, new Selection(10, 18, 10, 18));47114712viewModel.type('\n', 'keyboard');4713assert.deepStrictEqual(model.getLineContent(11), ' ]');4714});4715});47164717test('issue #111128: Multicursor `Enter` issue with indentation', () => {4718const model = createTextModel(' let a, b, c;', indentRulesLanguageId, { detectIndentation: false, insertSpaces: false, tabSize: 4 });4719withTestCodeEditor(model, {}, (editor, viewModel) => {4720editor.setSelections([4721new Selection(1, 11, 1, 11),4722new Selection(1, 14, 1, 14),4723]);4724viewModel.type('\n', 'keyboard');4725assert.strictEqual(model.getValue(), ' let a,\n\t b,\n\t c;');4726});4727});47284729test('issue #122714: tabSize=1 prevent typing a string matching decreaseIndentPattern in an empty file', () => {4730const latextLanguageId = setupIndentRulesLanguage('latex', {4731increaseIndentPattern: new RegExp('\\\\begin{(?!document)([^}]*)}(?!.*\\\\end{\\1})'),4732decreaseIndentPattern: new RegExp('^\\s*\\\\end{(?!document)')4733});4734const model = createTextModel(4735'\\end',4736latextLanguageId,4737{ tabSize: 1 }4738);47394740withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {4741moveTo(editor, viewModel, 1, 5, false);4742assertCursor(viewModel, new Selection(1, 5, 1, 5));47434744viewModel.type('{', 'keyboard');4745assert.strictEqual(model.getLineContent(1), '\\end{}');4746});4747});47484749test('ElectricCharacter - does nothing if no electric char', () => {4750usingCursor({4751text: [4752' if (a) {',4753''4754],4755languageId: electricCharLanguageId4756}, (editor, model, viewModel) => {4757moveTo(editor, viewModel, 2, 1);4758viewModel.type('*', 'keyboard');4759assert.deepStrictEqual(model.getLineContent(2), '*');4760});4761});47624763test('ElectricCharacter - indents in order to match bracket', () => {4764usingCursor({4765text: [4766' if (a) {',4767''4768],4769languageId: electricCharLanguageId4770}, (editor, model, viewModel) => {4771moveTo(editor, viewModel, 2, 1);4772viewModel.type('}', 'keyboard');4773assert.deepStrictEqual(model.getLineContent(2), ' }');4774});4775});47764777test('ElectricCharacter - unindents in order to match bracket', () => {4778usingCursor({4779text: [4780' if (a) {',4781' '4782],4783languageId: electricCharLanguageId4784}, (editor, model, viewModel) => {4785moveTo(editor, viewModel, 2, 5);4786viewModel.type('}', 'keyboard');4787assert.deepStrictEqual(model.getLineContent(2), ' }');4788});4789});47904791test('ElectricCharacter - matches with correct bracket', () => {4792usingCursor({4793text: [4794' if (a) {',4795' if (b) {',4796' }',4797' '4798],4799languageId: electricCharLanguageId4800}, (editor, model, viewModel) => {4801moveTo(editor, viewModel, 4, 1);4802viewModel.type('}', 'keyboard');4803assert.deepStrictEqual(model.getLineContent(4), ' } ');4804});4805});48064807test('ElectricCharacter - does nothing if bracket does not match', () => {4808usingCursor({4809text: [4810' if (a) {',4811' if (b) {',4812' }',4813' } '4814],4815languageId: electricCharLanguageId4816}, (editor, model, viewModel) => {4817moveTo(editor, viewModel, 4, 6);4818viewModel.type('}', 'keyboard');4819assert.deepStrictEqual(model.getLineContent(4), ' } }');4820});4821});48224823test('ElectricCharacter - matches bracket even in line with content', () => {4824usingCursor({4825text: [4826' if (a) {',4827'// hello'4828],4829languageId: electricCharLanguageId4830}, (editor, model, viewModel) => {4831moveTo(editor, viewModel, 2, 1);4832viewModel.type('}', 'keyboard');4833assert.deepStrictEqual(model.getLineContent(2), ' }// hello');4834});4835});48364837test('ElectricCharacter - is no-op if bracket is lined up', () => {4838usingCursor({4839text: [4840' if (a) {',4841' '4842],4843languageId: electricCharLanguageId4844}, (editor, model, viewModel) => {4845moveTo(editor, viewModel, 2, 3);4846viewModel.type('}', 'keyboard');4847assert.deepStrictEqual(model.getLineContent(2), ' }');4848});4849});48504851test('ElectricCharacter - is no-op if there is non-whitespace text before', () => {4852usingCursor({4853text: [4854' if (a) {',4855'a'4856],4857languageId: electricCharLanguageId4858}, (editor, model, viewModel) => {4859moveTo(editor, viewModel, 2, 2);4860viewModel.type('}', 'keyboard');4861assert.deepStrictEqual(model.getLineContent(2), 'a}');4862});4863});48644865test('ElectricCharacter - is no-op if pairs are all matched before', () => {4866usingCursor({4867text: [4868'foo(() => {',4869' ( 1 + 2 ) ',4870'})'4871],4872languageId: electricCharLanguageId4873}, (editor, model, viewModel) => {4874moveTo(editor, viewModel, 2, 13);4875viewModel.type('*', 'keyboard');4876assert.deepStrictEqual(model.getLineContent(2), ' ( 1 + 2 ) *');4877});4878});48794880test('ElectricCharacter - is no-op if matching bracket is on the same line', () => {4881usingCursor({4882text: [4883'(div',4884],4885languageId: electricCharLanguageId4886}, (editor, model, viewModel) => {4887moveTo(editor, viewModel, 1, 5);4888let changeText: string | null = null;4889const disposable = model.onDidChangeContent(e => {4890changeText = e.changes[0].text;4891});4892viewModel.type(')', 'keyboard');4893assert.deepStrictEqual(model.getLineContent(1), '(div)');4894assert.deepStrictEqual(changeText, ')');4895disposable.dispose();4896});4897});48984899test('ElectricCharacter - is no-op if the line has other content', () => {4900usingCursor({4901text: [4902'Math.max(',4903'\t2',4904'\t3'4905],4906languageId: electricCharLanguageId4907}, (editor, model, viewModel) => {4908moveTo(editor, viewModel, 3, 3);4909viewModel.type(')', 'keyboard');4910assert.deepStrictEqual(model.getLineContent(3), '\t3)');4911});4912});49134914test('ElectricCharacter - appends text', () => {4915usingCursor({4916text: [4917' if (a) {',4918'/*'4919],4920languageId: electricCharLanguageId4921}, (editor, model, viewModel) => {4922moveTo(editor, viewModel, 2, 3);4923viewModel.type('*', 'keyboard');4924assert.deepStrictEqual(model.getLineContent(2), '/** */');4925});4926});49274928test('ElectricCharacter - appends text 2', () => {4929usingCursor({4930text: [4931' if (a) {',4932' /*'4933],4934languageId: electricCharLanguageId4935}, (editor, model, viewModel) => {4936moveTo(editor, viewModel, 2, 5);4937viewModel.type('*', 'keyboard');4938assert.deepStrictEqual(model.getLineContent(2), ' /** */');4939});4940});49414942test('ElectricCharacter - issue #23711: Replacing selected text with )]} fails to delete old text with backwards-dragged selection', () => {4943usingCursor({4944text: [4945'{',4946'word'4947],4948languageId: electricCharLanguageId4949}, (editor, model, viewModel) => {4950moveTo(editor, viewModel, 2, 5);4951moveTo(editor, viewModel, 2, 1, true);4952viewModel.type('}', 'keyboard');4953assert.deepStrictEqual(model.getLineContent(2), '}');4954});4955});49564957test('issue #61070: backtick (`) should auto-close after a word character', () => {4958usingCursor({4959text: ['const markup = highlight'],4960languageId: autoClosingLanguageId4961}, (editor, model, viewModel) => {4962model.tokenization.forceTokenization(1);4963assertType(editor, model, viewModel, 1, 25, '`', '``', `auto closes \` @ (1, 25)`);4964});4965});49664967test('issue #132912: quotes should not auto-close if they are closing a string', () => {4968setupAutoClosingLanguageTokenization();4969const model = createTextModel('const t2 = `something ${t1}', autoClosingLanguageId);4970withTestCodeEditor(4971model,4972{},4973(editor, viewModel) => {4974const model = viewModel.model;4975model.tokenization.forceTokenization(1);4976assertType(editor, model, viewModel, 1, 28, '`', '`', `does not auto close \` @ (1, 28)`);4977}4978);4979});49804981test('autoClosingPairs - open parens: default', () => {4982usingCursor({4983text: [4984'var a = [];',4985'var b = `asd`;',4986'var c = \'asd\';',4987'var d = "asd";',4988'var e = /*3*/ 3;',4989'var f = /** 3 */3;',4990'var g = (3+5);',4991'var h = { a: \'value\' };',4992],4993languageId: autoClosingLanguageId4994}, (editor, model, viewModel) => {49954996const autoClosePositions = [4997'var| a| |=| [|]|;|',4998'var| b| |=| |`asd|`|;|',4999'var| c| |=| |\'asd|\'|;|',5000'var| d| |=| |"asd|"|;|',5001'var| e| |=| /*3*/| 3|;|',5002'var| f| |=| /**| 3| */3|;|',5003'var| g| |=| (3+5|)|;|',5004'var| h| |=| {| a|:| |\'value|\'| |}|;|',5005];5006for (let i = 0, len = autoClosePositions.length; i < len; i++) {5007const lineNumber = i + 1;5008const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50095010for (let column = 1; column < autoCloseColumns.length; column++) {5011model.tokenization.forceTokenization(lineNumber);5012if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5013assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5014} else {5015assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5016}5017}5018}5019});5020});50215022test('autoClosingPairs - open parens: whitespace', () => {5023usingCursor({5024text: [5025'var a = [];',5026'var b = `asd`;',5027'var c = \'asd\';',5028'var d = "asd";',5029'var e = /*3*/ 3;',5030'var f = /** 3 */3;',5031'var g = (3+5);',5032'var h = { a: \'value\' };',5033],5034languageId: autoClosingLanguageId,5035editorOpts: {5036autoClosingBrackets: 'beforeWhitespace'5037}5038}, (editor, model, viewModel) => {50395040const autoClosePositions = [5041'var| a| =| [|];|',5042'var| b| =| `asd`;|',5043'var| c| =| \'asd\';|',5044'var| d| =| "asd";|',5045'var| e| =| /*3*/| 3;|',5046'var| f| =| /**| 3| */3;|',5047'var| g| =| (3+5|);|',5048'var| h| =| {| a:| \'value\'| |};|',5049];5050for (let i = 0, len = autoClosePositions.length; i < len; i++) {5051const lineNumber = i + 1;5052const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50535054for (let column = 1; column < autoCloseColumns.length; column++) {5055model.tokenization.forceTokenization(lineNumber);5056if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5057assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5058} else {5059assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5060}5061}5062}5063});5064});50655066test('autoClosingPairs - open parens disabled/enabled open quotes enabled/disabled', () => {5067usingCursor({5068text: [5069'var a = [];',5070],5071languageId: autoClosingLanguageId,5072editorOpts: {5073autoClosingBrackets: 'beforeWhitespace',5074autoClosingQuotes: 'never'5075}5076}, (editor, model, viewModel) => {50775078const autoClosePositions = [5079'var| a| =| [|];|',5080];5081for (let i = 0, len = autoClosePositions.length; i < len; i++) {5082const lineNumber = i + 1;5083const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);50845085for (let column = 1; column < autoCloseColumns.length; column++) {5086model.tokenization.forceTokenization(lineNumber);5087if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5088assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5089} else {5090assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5091}5092assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5093}5094}5095});50965097usingCursor({5098text: [5099'var b = [];',5100],5101languageId: autoClosingLanguageId,5102editorOpts: {5103autoClosingBrackets: 'never',5104autoClosingQuotes: 'beforeWhitespace'5105}5106}, (editor, model, viewModel) => {51075108const autoClosePositions = [5109'var b =| [|];|',5110];5111for (let i = 0, len = autoClosePositions.length; i < len; i++) {5112const lineNumber = i + 1;5113const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);51145115for (let column = 1; column < autoCloseColumns.length; column++) {5116model.tokenization.forceTokenization(lineNumber);5117if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5118assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);5119} else {5120assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5121}5122assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5123}5124}5125});5126});51275128test('autoClosingPairs - configurable open parens', () => {5129setAutoClosingLanguageEnabledSet('abc');5130usingCursor({5131text: [5132'var a = [];',5133'var b = `asd`;',5134'var c = \'asd\';',5135'var d = "asd";',5136'var e = /*3*/ 3;',5137'var f = /** 3 */3;',5138'var g = (3+5);',5139'var h = { a: \'value\' };',5140],5141languageId: autoClosingLanguageId,5142editorOpts: {5143autoClosingBrackets: 'languageDefined'5144}5145}, (editor, model, viewModel) => {51465147const autoClosePositions = [5148'v|ar |a = [|];|',5149'v|ar |b = `|asd`;|',5150'v|ar |c = \'|asd\';|',5151'v|ar d = "|asd";|',5152'v|ar e = /*3*/ 3;|',5153'v|ar f = /** 3| */3;|',5154'v|ar g = (3+5|);|',5155'v|ar h = { |a: \'v|alue\' |};|',5156];5157for (let i = 0, len = autoClosePositions.length; i < len; i++) {5158const lineNumber = i + 1;5159const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);51605161for (let column = 1; column < autoCloseColumns.length; column++) {5162model.tokenization.forceTokenization(lineNumber);5163if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5164assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5165} else {5166assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5167}5168}5169}5170});5171});51725173test('autoClosingPairs - auto-pairing can be disabled', () => {5174usingCursor({5175text: [5176'var a = [];',5177'var b = `asd`;',5178'var c = \'asd\';',5179'var d = "asd";',5180'var e = /*3*/ 3;',5181'var f = /** 3 */3;',5182'var g = (3+5);',5183'var h = { a: \'value\' };',5184],5185languageId: autoClosingLanguageId,5186editorOpts: {5187autoClosingBrackets: 'never',5188autoClosingQuotes: 'never'5189}5190}, (editor, model, viewModel) => {51915192const autoClosePositions = [5193'var a = [];',5194'var b = `asd`;',5195'var c = \'asd\';',5196'var d = "asd";',5197'var e = /*3*/ 3;',5198'var f = /** 3 */3;',5199'var g = (3+5);',5200'var h = { a: \'value\' };',5201];5202for (let i = 0, len = autoClosePositions.length; i < len; i++) {5203const lineNumber = i + 1;5204const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);52055206for (let column = 1; column < autoCloseColumns.length; column++) {5207model.tokenization.forceTokenization(lineNumber);5208if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5209assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);5210assertType(editor, model, viewModel, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`);5211} else {5212assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);5213assertType(editor, model, viewModel, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`);5214}5215}5216}5217});5218});52195220test('autoClosingPairs - auto wrapping is configurable', () => {5221usingCursor({5222text: [5223'var a = asd'5224],5225languageId: autoClosingLanguageId5226}, (editor, model, viewModel) => {52275228viewModel.setSelections('test', [5229new Selection(1, 1, 1, 4),5230new Selection(1, 9, 1, 12),5231]);52325233// type a `5234viewModel.type('`', 'keyboard');52355236assert.strictEqual(model.getValue(), '`var` a = `asd`');52375238// type a (5239viewModel.type('(', 'keyboard');52405241assert.strictEqual(model.getValue(), '`(var)` a = `(asd)`');5242});52435244usingCursor({5245text: [5246'var a = asd'5247],5248languageId: autoClosingLanguageId,5249editorOpts: {5250autoSurround: 'never'5251}5252}, (editor, model, viewModel) => {52535254viewModel.setSelections('test', [5255new Selection(1, 1, 1, 4),5256]);52575258// type a `5259viewModel.type('`', 'keyboard');52605261assert.strictEqual(model.getValue(), '` a = asd');5262});52635264usingCursor({5265text: [5266'var a = asd'5267],5268languageId: autoClosingLanguageId,5269editorOpts: {5270autoSurround: 'quotes'5271}5272}, (editor, model, viewModel) => {52735274viewModel.setSelections('test', [5275new Selection(1, 1, 1, 4),5276]);52775278// type a `5279viewModel.type('`', 'keyboard');5280assert.strictEqual(model.getValue(), '`var` a = asd');52815282// type a (5283viewModel.type('(', 'keyboard');5284assert.strictEqual(model.getValue(), '`(` a = asd');5285});52865287usingCursor({5288text: [5289'var a = asd'5290],5291languageId: autoClosingLanguageId,5292editorOpts: {5293autoSurround: 'brackets'5294}5295}, (editor, model, viewModel) => {52965297viewModel.setSelections('test', [5298new Selection(1, 1, 1, 4),5299]);53005301// type a (5302viewModel.type('(', 'keyboard');5303assert.strictEqual(model.getValue(), '(var) a = asd');53045305// type a `5306viewModel.type('`', 'keyboard');5307assert.strictEqual(model.getValue(), '(`) a = asd');5308});5309});53105311test('autoClosingPairs - quote', () => {5312usingCursor({5313text: [5314'var a = [];',5315'var b = `asd`;',5316'var c = \'asd\';',5317'var d = "asd";',5318'var e = /*3*/ 3;',5319'var f = /** 3 */3;',5320'var g = (3+5);',5321'var h = { a: \'value\' };',5322],5323languageId: autoClosingLanguageId5324}, (editor, model, viewModel) => {53255326const autoClosePositions = [5327'var a |=| [|]|;|',5328'var b |=| `asd`|;|',5329'var c |=| \'asd\'|;|',5330'var d |=| "asd"|;|',5331'var e |=| /*3*/| 3;|',5332'var f |=| /**| 3 */3;|',5333'var g |=| (3+5)|;|',5334'var h |=| {| a:| \'value\'| |}|;|',5335];5336for (let i = 0, len = autoClosePositions.length; i < len; i++) {5337const lineNumber = i + 1;5338const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);53395340for (let column = 1; column < autoCloseColumns.length; column++) {5341model.tokenization.forceTokenization(lineNumber);5342if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {5343assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);5344} else if (autoCloseColumns[column] === AutoClosingColumnType.Special2) {5345assertType(editor, model, viewModel, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`);5346} else {5347assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);5348}5349}5350}5351});5352});53535354test('autoClosingPairs - multi-character autoclose', () => {5355usingCursor({5356text: [5357'',5358],5359languageId: autoClosingLanguageId5360}, (editor, model, viewModel) => {53615362model.setValue('begi');5363viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);5364viewModel.type('n', 'keyboard');5365assert.strictEqual(model.getLineContent(1), 'beginend');53665367model.setValue('/*');5368viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);5369viewModel.type('*', 'keyboard');5370assert.strictEqual(model.getLineContent(1), '/** */');5371});5372});53735374test('autoClosingPairs - doc comments can be turned off', () => {5375usingCursor({5376text: [5377'',5378],5379languageId: autoClosingLanguageId,5380editorOpts: {5381autoClosingComments: 'never'5382}5383}, (editor, model, viewModel) => {53845385model.setValue('/*');5386viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);5387viewModel.type('*', 'keyboard');5388assert.strictEqual(model.getLineContent(1), '/**');5389});5390});53915392test('issue #72177: multi-character autoclose with conflicting patterns', () => {5393const languageId = 'autoClosingModeMultiChar';53945395disposables.add(languageService.registerLanguage({ id: languageId }));5396disposables.add(languageConfigurationService.register(languageId, {5397autoClosingPairs: [5398{ open: '(', close: ')' },5399{ open: '(*', close: '*)' },5400{ open: '<@', close: '@>' },5401{ open: '<@@', close: '@@>' },5402],5403}));54045405usingCursor({5406text: [5407'',5408],5409languageId: languageId5410}, (editor, model, viewModel) => {5411viewModel.type('(', 'keyboard');5412assert.strictEqual(model.getLineContent(1), '()');5413viewModel.type('*', 'keyboard');5414assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`);54155416model.setValue('(');5417viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);5418viewModel.type('*', 'keyboard');5419assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`);54205421model.setValue('');5422viewModel.type('<@', 'keyboard');5423assert.strictEqual(model.getLineContent(1), '<@@>');5424viewModel.type('@', 'keyboard');5425assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`);5426viewModel.type('(', 'keyboard');5427assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`);5428});5429});54305431test('issue #55314: Do not auto-close when ending with open', () => {5432const languageId = 'myElectricMode';54335434disposables.add(languageService.registerLanguage({ id: languageId }));5435disposables.add(languageConfigurationService.register(languageId, {5436autoClosingPairs: [5437{ open: '{', close: '}' },5438{ open: '[', close: ']' },5439{ open: '(', close: ')' },5440{ open: '\'', close: '\'', notIn: ['string', 'comment'] },5441{ open: '\"', close: '\"', notIn: ['string'] },5442{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },5443{ open: '`', close: '`', notIn: ['string', 'comment'] },5444{ open: '/**', close: ' */', notIn: ['string'] }5445],5446}));54475448usingCursor({5449text: [5450'little goat',5451'little LAMB',5452'little sheep',5453'Big LAMB'5454],5455languageId: languageId5456}, (editor, model, viewModel) => {5457model.tokenization.forceTokenization(model.getLineCount());5458assertType(editor, model, viewModel, 1, 4, '"', '"', `does not double quote when ending with open`);5459model.tokenization.forceTokenization(model.getLineCount());5460assertType(editor, model, viewModel, 2, 4, '"', '"', `does not double quote when ending with open`);5461model.tokenization.forceTokenization(model.getLineCount());5462assertType(editor, model, viewModel, 3, 4, '"', '"', `does not double quote when ending with open`);5463model.tokenization.forceTokenization(model.getLineCount());5464assertType(editor, model, viewModel, 4, 2, '"', '"', `does not double quote when ending with open`);5465model.tokenization.forceTokenization(model.getLineCount());5466assertType(editor, model, viewModel, 4, 3, '"', '"', `does not double quote when ending with open`);5467});5468});54695470test('issue #27937: Trying to add an item to the front of a list is cumbersome', () => {5471usingCursor({5472text: [5473'var arr = ["b", "c"];'5474],5475languageId: autoClosingLanguageId5476}, (editor, model, viewModel) => {5477assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`);5478});5479});54805481test('issue #25658 - Do not auto-close single/double quotes after word characters', () => {5482usingCursor({5483text: [5484'',5485],5486languageId: autoClosingLanguageId5487}, (editor, model, viewModel) => {54885489function typeCharacters(viewModel: ViewModel, chars: string): void {5490for (let i = 0, len = chars.length; i < len; i++) {5491viewModel.type(chars[i], 'keyboard');5492}5493}54945495// First gif5496model.tokenization.forceTokenization(model.getLineCount());5497typeCharacters(viewModel, 'teste1 = teste\' ok');5498assert.strictEqual(model.getLineContent(1), 'teste1 = teste\' ok');54995500viewModel.setSelections('test', [new Selection(1, 1000, 1, 1000)]);5501typeCharacters(viewModel, '\n');5502model.tokenization.forceTokenization(model.getLineCount());5503typeCharacters(viewModel, 'teste2 = teste \'ok');5504assert.strictEqual(model.getLineContent(2), 'teste2 = teste \'ok\'');55055506viewModel.setSelections('test', [new Selection(2, 1000, 2, 1000)]);5507typeCharacters(viewModel, '\n');5508model.tokenization.forceTokenization(model.getLineCount());5509typeCharacters(viewModel, 'teste3 = teste" ok');5510assert.strictEqual(model.getLineContent(3), 'teste3 = teste" ok');55115512viewModel.setSelections('test', [new Selection(3, 1000, 3, 1000)]);5513typeCharacters(viewModel, '\n');5514model.tokenization.forceTokenization(model.getLineCount());5515typeCharacters(viewModel, 'teste4 = teste "ok');5516assert.strictEqual(model.getLineContent(4), 'teste4 = teste "ok"');55175518// Second gif5519viewModel.setSelections('test', [new Selection(4, 1000, 4, 1000)]);5520typeCharacters(viewModel, '\n');5521model.tokenization.forceTokenization(model.getLineCount());5522typeCharacters(viewModel, 'teste \'');5523assert.strictEqual(model.getLineContent(5), 'teste \'\'');55245525viewModel.setSelections('test', [new Selection(5, 1000, 5, 1000)]);5526typeCharacters(viewModel, '\n');5527model.tokenization.forceTokenization(model.getLineCount());5528typeCharacters(viewModel, 'teste "');5529assert.strictEqual(model.getLineContent(6), 'teste ""');55305531viewModel.setSelections('test', [new Selection(6, 1000, 6, 1000)]);5532typeCharacters(viewModel, '\n');5533model.tokenization.forceTokenization(model.getLineCount());5534typeCharacters(viewModel, 'teste\'');5535assert.strictEqual(model.getLineContent(7), 'teste\'');55365537viewModel.setSelections('test', [new Selection(7, 1000, 7, 1000)]);5538typeCharacters(viewModel, '\n');5539model.tokenization.forceTokenization(model.getLineCount());5540typeCharacters(viewModel, 'teste"');5541assert.strictEqual(model.getLineContent(8), 'teste"');5542});5543});55445545test('issue #37315 - overtypes only those characters that it inserted', () => {5546usingCursor({5547text: [5548'',5549'y=();'5550],5551languageId: autoClosingLanguageId5552}, (editor, model, viewModel) => {5553assertCursor(viewModel, new Position(1, 1));55545555viewModel.type('x=(', 'keyboard');5556assert.strictEqual(model.getLineContent(1), 'x=()');55575558viewModel.type('asd', 'keyboard');5559assert.strictEqual(model.getLineContent(1), 'x=(asd)');55605561// overtype!5562viewModel.type(')', 'keyboard');5563assert.strictEqual(model.getLineContent(1), 'x=(asd)');55645565// do not overtype!5566viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5567viewModel.type(')', 'keyboard');5568assert.strictEqual(model.getLineContent(2), 'y=());');55695570});5571});55725573test('issue #37315 - stops overtyping once cursor leaves area', () => {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.setSelections('test', [new Selection(1, 5, 1, 5)]);5587viewModel.type(')', 'keyboard');5588assert.strictEqual(model.getLineContent(1), 'x=())');5589});5590});55915592test('issue #37315 - it overtypes only once', () => {5593usingCursor({5594text: [5595'',5596'y=();'5597],5598languageId: autoClosingLanguageId5599}, (editor, model, viewModel) => {5600assertCursor(viewModel, new Position(1, 1));56015602viewModel.type('x=(', 'keyboard');5603assert.strictEqual(model.getLineContent(1), 'x=()');56045605viewModel.type(')', 'keyboard');5606assert.strictEqual(model.getLineContent(1), 'x=()');56075608viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5609viewModel.type(')', 'keyboard');5610assert.strictEqual(model.getLineContent(1), 'x=())');5611});5612});56135614test('issue #37315 - it can remember multiple auto-closed instances', () => {5615usingCursor({5616text: [5617'',5618'y=();'5619],5620languageId: autoClosingLanguageId5621}, (editor, model, viewModel) => {5622assertCursor(viewModel, new Position(1, 1));56235624viewModel.type('x=(', 'keyboard');5625assert.strictEqual(model.getLineContent(1), 'x=()');56265627viewModel.type('(', 'keyboard');5628assert.strictEqual(model.getLineContent(1), 'x=(())');56295630viewModel.type(')', 'keyboard');5631assert.strictEqual(model.getLineContent(1), 'x=(())');56325633viewModel.type(')', 'keyboard');5634assert.strictEqual(model.getLineContent(1), 'x=(())');5635});5636});56375638test('issue #118270 - auto closing deletes only those characters that it inserted', () => {5639usingCursor({5640text: [5641'',5642'y=();'5643],5644languageId: autoClosingLanguageId5645}, (editor, model, viewModel) => {5646assertCursor(viewModel, new Position(1, 1));56475648viewModel.type('x=(', 'keyboard');5649assert.strictEqual(model.getLineContent(1), 'x=()');56505651viewModel.type('asd', 'keyboard');5652assert.strictEqual(model.getLineContent(1), 'x=(asd)');56535654editor.runCommand(CoreEditingCommands.DeleteLeft, null);5655editor.runCommand(CoreEditingCommands.DeleteLeft, null);5656editor.runCommand(CoreEditingCommands.DeleteLeft, null);5657assert.strictEqual(model.getLineContent(1), 'x=()');56585659// delete closing char!5660editor.runCommand(CoreEditingCommands.DeleteLeft, null);5661assert.strictEqual(model.getLineContent(1), 'x=');56625663// do not delete closing char!5664viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5665editor.runCommand(CoreEditingCommands.DeleteLeft, null);5666assert.strictEqual(model.getLineContent(2), 'y=);');56675668});5669});56705671test('issue #78527 - does not close quote on odd count', () => {5672usingCursor({5673text: [5674'std::cout << \'"\' << entryMap'5675],5676languageId: autoClosingLanguageId5677}, (editor, model, viewModel) => {5678viewModel.setSelections('test', [new Selection(1, 29, 1, 29)]);56795680viewModel.type('[', 'keyboard');5681assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[]');56825683viewModel.type('"', 'keyboard');5684assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[""]');56855686viewModel.type('a', 'keyboard');5687assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');56885689viewModel.type('"', 'keyboard');5690assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');56915692viewModel.type(']', 'keyboard');5693assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');5694});5695});56965697test('issue #85983 - editor.autoClosingBrackets: beforeWhitespace is incorrect for Python', () => {5698const languageId = 'pythonMode';56995700disposables.add(languageService.registerLanguage({ id: languageId }));5701disposables.add(languageConfigurationService.register(languageId, {5702autoClosingPairs: [5703{ open: '{', close: '}' },5704{ open: '[', close: ']' },5705{ open: '(', close: ')' },5706{ open: '\"', close: '\"', notIn: ['string'] },5707{ open: 'r\"', close: '\"', notIn: ['string', 'comment'] },5708{ open: 'R\"', close: '\"', notIn: ['string', 'comment'] },5709{ open: 'u\"', close: '\"', notIn: ['string', 'comment'] },5710{ open: 'U\"', close: '\"', notIn: ['string', 'comment'] },5711{ open: 'f\"', close: '\"', notIn: ['string', 'comment'] },5712{ open: 'F\"', close: '\"', notIn: ['string', 'comment'] },5713{ open: 'b\"', close: '\"', notIn: ['string', 'comment'] },5714{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },5715{ open: '\'', close: '\'', notIn: ['string', 'comment'] },5716{ open: 'r\'', close: '\'', notIn: ['string', 'comment'] },5717{ open: 'R\'', close: '\'', notIn: ['string', 'comment'] },5718{ open: 'u\'', close: '\'', notIn: ['string', 'comment'] },5719{ open: 'U\'', close: '\'', notIn: ['string', 'comment'] },5720{ open: 'f\'', close: '\'', notIn: ['string', 'comment'] },5721{ open: 'F\'', close: '\'', notIn: ['string', 'comment'] },5722{ open: 'b\'', close: '\'', notIn: ['string', 'comment'] },5723{ open: 'B\'', close: '\'', notIn: ['string', 'comment'] },5724{ open: '`', close: '`', notIn: ['string'] }5725],5726}));57275728usingCursor({5729text: [5730'foo\'hello\''5731],5732editorOpts: {5733autoClosingBrackets: 'beforeWhitespace'5734},5735languageId: languageId5736}, (editor, model, viewModel) => {5737assertType(editor, model, viewModel, 1, 4, '(', '(', `does not auto close @ (1, 4)`);5738});5739});57405741test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => {5742usingCursor({5743text: [5744'<div id'5745],5746languageId: autoClosingLanguageId5747}, (editor, model, viewModel) => {5748viewModel.setSelections('test', [new Selection(1, 8, 1, 8)]);57495750viewModel.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)], EditSources.unknown({}));5751assert.strictEqual(model.getLineContent(1), '<div id=""');57525753viewModel.type('a', 'keyboard');5754assert.strictEqual(model.getLineContent(1), '<div id="a"');57555756viewModel.type('"', 'keyboard');5757assert.strictEqual(model.getLineContent(1), '<div id="a"');5758});5759});57605761test('issue #78833 - Add config to use old brackets/quotes overtyping', () => {5762usingCursor({5763text: [5764'',5765'y=();'5766],5767languageId: autoClosingLanguageId,5768editorOpts: {5769autoClosingOvertype: 'always'5770}5771}, (editor, model, viewModel) => {5772assertCursor(viewModel, new Position(1, 1));57735774viewModel.type('x=(', 'keyboard');5775assert.strictEqual(model.getLineContent(1), 'x=()');57765777viewModel.type(')', 'keyboard');5778assert.strictEqual(model.getLineContent(1), 'x=()');57795780viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5781viewModel.type(')', 'keyboard');5782assert.strictEqual(model.getLineContent(1), 'x=()');57835784viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);5785viewModel.type(')', 'keyboard');5786assert.strictEqual(model.getLineContent(2), 'y=();');5787});5788});57895790test('issue #15825: accents on mac US intl keyboard', () => {5791usingCursor({5792text: [5793],5794languageId: autoClosingLanguageId5795}, (editor, model, viewModel) => {5796assertCursor(viewModel, new Position(1, 1));57975798// Typing ` + e on the mac US intl kb layout5799viewModel.startComposition();5800viewModel.type('`', 'keyboard');5801viewModel.compositionType('è', 1, 0, 0, 'keyboard');5802viewModel.endComposition('keyboard');58035804assert.strictEqual(model.getValue(), 'è');5805});5806});58075808test('issue #90016: allow accents on mac US intl keyboard to surround selection', () => {5809usingCursor({5810text: [5811'test'5812],5813languageId: autoClosingLanguageId5814}, (editor, model, viewModel) => {5815viewModel.setSelections('test', [new Selection(1, 1, 1, 5)]);58165817// Typing ` + e on the mac US intl kb layout5818viewModel.startComposition();5819viewModel.type('\'', 'keyboard');5820viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5821viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5822viewModel.endComposition('keyboard');58235824assert.strictEqual(model.getValue(), '\'test\'');5825});5826});58275828test('issue #53357: Over typing ignores characters after backslash', () => {5829usingCursor({5830text: [5831'console.log();'5832],5833languageId: autoClosingLanguageId5834}, (editor, model, viewModel) => {58355836viewModel.setSelections('test', [new Selection(1, 13, 1, 13)]);58375838viewModel.type('\'', 'keyboard');5839assert.strictEqual(model.getValue(), 'console.log(\'\');');58405841viewModel.type('it', 'keyboard');5842assert.strictEqual(model.getValue(), 'console.log(\'it\');');58435844viewModel.type('\\', 'keyboard');5845assert.strictEqual(model.getValue(), 'console.log(\'it\\\');');58465847viewModel.type('\'', 'keyboard');5848assert.strictEqual(model.getValue(), 'console.log(\'it\\\'\');');5849});5850});58515852test('issue #84998: Overtyping Brackets doesn\'t work after backslash', () => {5853usingCursor({5854text: [5855''5856],5857languageId: autoClosingLanguageId5858}, (editor, model, viewModel) => {58595860viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);58615862viewModel.type('\\', 'keyboard');5863assert.strictEqual(model.getValue(), '\\');58645865viewModel.type('(', 'keyboard');5866assert.strictEqual(model.getValue(), '\\()');58675868viewModel.type('abc', 'keyboard');5869assert.strictEqual(model.getValue(), '\\(abc)');58705871viewModel.type('\\', 'keyboard');5872assert.strictEqual(model.getValue(), '\\(abc\\)');58735874viewModel.type(')', 'keyboard');5875assert.strictEqual(model.getValue(), '\\(abc\\)');5876});5877});58785879test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => {5880usingCursor({5881text: [5882'hello',5883'world'5884],5885languageId: autoClosingLanguageId5886}, (editor, model, viewModel) => {5887assertCursor(viewModel, new Position(1, 1));58885889// Typing ` and pressing shift+down on the mac US intl kb layout5890// Here we're just replaying what the cursor gets5891viewModel.startComposition();5892viewModel.type('`', 'keyboard');5893moveDown(editor, viewModel, true);5894viewModel.compositionType('`', 1, 0, 0, 'keyboard');5895viewModel.compositionType('`', 1, 0, 0, 'keyboard');5896viewModel.endComposition('keyboard');58975898assert.strictEqual(model.getValue(), '`hello\nworld');5899assertCursor(viewModel, new Selection(1, 2, 2, 2));5900});5901});59025903test('issue #26820: auto close quotes when not used as accents', () => {5904usingCursor({5905text: [5906''5907],5908languageId: autoClosingLanguageId5909}, (editor, model, viewModel) => {5910assertCursor(viewModel, new Position(1, 1));59115912// on the mac US intl kb layout59135914// Typing ' + space5915viewModel.startComposition();5916viewModel.type('\'', 'keyboard');5917viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5918viewModel.endComposition('keyboard');5919assert.strictEqual(model.getValue(), '\'\'');59205921// Typing one more ' + space5922viewModel.startComposition();5923viewModel.type('\'', 'keyboard');5924viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5925viewModel.endComposition('keyboard');5926assert.strictEqual(model.getValue(), '\'\'');59275928// Typing ' as a closing tag5929model.setValue('\'abc');5930viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);5931viewModel.startComposition();5932viewModel.type('\'', 'keyboard');5933viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5934viewModel.endComposition('keyboard');59355936assert.strictEqual(model.getValue(), '\'abc\'');59375938// quotes before the newly added character are all paired.5939model.setValue('\'abc\'def ');5940viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);5941viewModel.startComposition();5942viewModel.type('\'', 'keyboard');5943viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5944viewModel.endComposition('keyboard');59455946assert.strictEqual(model.getValue(), '\'abc\'def \'\'');59475948// No auto closing if there is non-whitespace character after the cursor5949model.setValue('abc');5950viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);5951viewModel.startComposition();5952viewModel.type('\'', 'keyboard');5953viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5954viewModel.endComposition('keyboard');59555956// No auto closing if it's after a word.5957model.setValue('abc');5958viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);5959viewModel.startComposition();5960viewModel.type('\'', 'keyboard');5961viewModel.compositionType('\'', 1, 0, 0, 'keyboard');5962viewModel.endComposition('keyboard');59635964assert.strictEqual(model.getValue(), 'abc\'');5965});5966});59675968test('issue #144690: Quotes do not overtype when using US Intl PC keyboard layout', () => {5969usingCursor({5970text: [5971''5972],5973languageId: autoClosingLanguageId5974}, (editor, model, viewModel) => {5975assertCursor(viewModel, new Position(1, 1));59765977// Pressing ' + ' + ;59785979viewModel.startComposition();5980viewModel.type(`'`, 'keyboard');5981viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');5982viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');5983viewModel.endComposition('keyboard');5984viewModel.startComposition();5985viewModel.type(`'`, 'keyboard');5986viewModel.compositionType(`';`, 1, 0, 0, 'keyboard');5987viewModel.compositionType(`';`, 2, 0, 0, 'keyboard');5988viewModel.endComposition('keyboard');59895990assert.strictEqual(model.getValue(), `'';`);5991});5992});59935994test('issue #144693: Typing a quote using US Intl PC keyboard layout always surrounds words', () => {5995usingCursor({5996text: [5997'const hello = 3;'5998],5999languageId: autoClosingLanguageId6000}, (editor, model, viewModel) => {6001viewModel.setSelections('test', [new Selection(1, 7, 1, 12)]);60026003// Pressing ' + e60046005viewModel.startComposition();6006viewModel.type(`'`, 'keyboard');6007viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');6008viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');6009viewModel.endComposition('keyboard');60106011assert.strictEqual(model.getValue(), `const é = 3;`);6012});6013});60146015test('issue #82701: auto close does not execute when IME is canceled via backspace', () => {6016usingCursor({6017text: [6018'{}'6019],6020languageId: autoClosingLanguageId6021}, (editor, model, viewModel) => {6022viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);60236024// Typing a + backspace6025viewModel.startComposition();6026viewModel.type('a', 'keyboard');6027viewModel.compositionType('', 1, 0, 0, 'keyboard');6028viewModel.endComposition('keyboard');6029assert.strictEqual(model.getValue(), '{}');6030});6031});60326033test('issue #20891: All cursors should do the same thing', () => {6034usingCursor({6035text: [6036'var a = asd'6037],6038languageId: autoClosingLanguageId6039}, (editor, model, viewModel) => {60406041viewModel.setSelections('test', [6042new Selection(1, 9, 1, 9),6043new Selection(1, 12, 1, 12),6044]);60456046// type a `6047viewModel.type('`', 'keyboard');60486049assert.strictEqual(model.getValue(), 'var a = `asd`');6050});6051});60526053test('issue #41825: Special handling of quotes in surrounding pairs', () => {6054const languageId = 'myMode';60556056disposables.add(languageService.registerLanguage({ id: languageId }));6057disposables.add(languageConfigurationService.register(languageId, {6058surroundingPairs: [6059{ open: '"', close: '"' },6060{ open: '\'', close: '\'' },6061]6062}));60636064const model = createTextModel('var x = \'hi\';', languageId);60656066withTestCodeEditor(model, {}, (editor, viewModel) => {6067editor.setSelections([6068new Selection(1, 9, 1, 10),6069new Selection(1, 12, 1, 13)6070]);6071viewModel.type('"', 'keyboard');6072assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = "hi";', 'assert1');60736074editor.setSelections([6075new Selection(1, 9, 1, 10),6076new Selection(1, 12, 1, 13)6077]);6078viewModel.type('\'', 'keyboard');6079assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = \'hi\';', 'assert2');6080});6081});60826083test('All cursors should do the same thing when deleting left', () => {6084const model = createTextModel(6085[6086'var a = ()'6087].join('\n'),6088autoClosingLanguageId6089);60906091withTestCodeEditor(model, {}, (editor, viewModel) => {6092viewModel.setSelections('test', [6093new Selection(1, 4, 1, 4),6094new Selection(1, 10, 1, 10),6095]);60966097// delete left6098editor.runCommand(CoreEditingCommands.DeleteLeft, null);60996100assert.strictEqual(model.getValue(), 'va a = )');6101});6102});61036104test('issue #7100: Mouse word selection is strange when non-word character is at the end of line', () => {6105const model = createTextModel(6106[6107'before.a',6108'before',6109'hello:',6110'there:',6111'this is strange:',6112'here',6113'it',6114'is',6115].join('\n')6116);61176118withTestCodeEditor(model, {}, (editor, viewModel) => {6119editor.runCommand(CoreNavigationCommands.WordSelect, {6120position: new Position(3, 7)6121});6122assertCursor(viewModel, new Selection(3, 7, 3, 7));61236124editor.runCommand(CoreNavigationCommands.WordSelectDrag, {6125position: new Position(4, 7)6126});6127assertCursor(viewModel, new Selection(3, 7, 4, 7));6128});6129});61306131test('issue #112039: shift-continuing a double/triple-click and drag selection does not remember its starting mode', () => {6132const model = createTextModel(6133[6134'just some text',6135'and another line',6136'and another one',6137].join('\n')6138);61396140withTestCodeEditor(model, {}, (editor, viewModel) => {6141editor.runCommand(CoreNavigationCommands.WordSelect, {6142position: new Position(2, 6)6143});6144editor.runCommand(CoreNavigationCommands.MoveToSelect, {6145position: new Position(1, 8),6146});6147assertCursor(viewModel, new Selection(2, 12, 1, 6));6148});6149});61506151test('issue #158236: Shift click selection does not work on line number indicator', () => {6152const model = createTextModel(6153[6154'just some text',6155'and another line',6156'and another one',6157].join('\n')6158);61596160withTestCodeEditor(model, {}, (editor, viewModel) => {6161editor.runCommand(CoreNavigationCommands.MoveTo, {6162position: new Position(3, 5)6163});6164editor.runCommand(CoreNavigationCommands.LineSelectDrag, {6165position: new Position(2, 1)6166});6167assertCursor(viewModel, new Selection(3, 5, 2, 1));6168});6169});61706171test('issue #111513: Text gets automatically selected when typing at the same location in another editor', () => {6172const model = createTextModel(6173[6174'just',6175'',6176'some text',6177].join('\n')6178);61796180withTestCodeEditor(model, {}, (editor1, viewModel1) => {6181editor1.setSelections([6182new Selection(2, 1, 2, 1)6183]);6184withTestCodeEditor(model, {}, (editor2, viewModel2) => {6185editor2.setSelections([6186new Selection(2, 1, 2, 1)6187]);6188viewModel2.type('e', 'keyboard');6189assertCursor(viewModel2, new Position(2, 2));6190assertCursor(viewModel1, new Position(2, 2));6191});6192});6193});6194});61956196suite('Undo stops', () => {61976198ensureNoDisposablesAreLeakedInTestSuite();61996200test('there is an undo stop between typing and deleting left', () => {6201const model = createTextModel(6202[6203'A line',6204'Another line',6205].join('\n')6206);62076208withTestCodeEditor(model, {}, (editor, viewModel) => {6209viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6210viewModel.type('first', 'keyboard');6211assert.strictEqual(model.getLineContent(1), 'A first line');6212assertCursor(viewModel, new Selection(1, 8, 1, 8));62136214editor.runCommand(CoreEditingCommands.DeleteLeft, null);6215editor.runCommand(CoreEditingCommands.DeleteLeft, null);6216assert.strictEqual(model.getLineContent(1), 'A fir line');6217assertCursor(viewModel, new Selection(1, 6, 1, 6));62186219editor.runCommand(CoreEditingCommands.Undo, null);6220assert.strictEqual(model.getLineContent(1), 'A first line');6221assertCursor(viewModel, new Selection(1, 8, 1, 8));62226223editor.runCommand(CoreEditingCommands.Undo, null);6224assert.strictEqual(model.getLineContent(1), 'A line');6225assertCursor(viewModel, new Selection(1, 3, 1, 3));6226});62276228model.dispose();6229});62306231test('there is an undo stop between typing and deleting right', () => {6232const model = createTextModel(6233[6234'A line',6235'Another line',6236].join('\n')6237);62386239withTestCodeEditor(model, {}, (editor, viewModel) => {6240viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6241viewModel.type('first', 'keyboard');6242assert.strictEqual(model.getLineContent(1), 'A first line');6243assertCursor(viewModel, new Selection(1, 8, 1, 8));62446245editor.runCommand(CoreEditingCommands.DeleteRight, null);6246editor.runCommand(CoreEditingCommands.DeleteRight, null);6247assert.strictEqual(model.getLineContent(1), 'A firstine');6248assertCursor(viewModel, new Selection(1, 8, 1, 8));62496250editor.runCommand(CoreEditingCommands.Undo, null);6251assert.strictEqual(model.getLineContent(1), 'A first line');6252assertCursor(viewModel, new Selection(1, 8, 1, 8));62536254editor.runCommand(CoreEditingCommands.Undo, null);6255assert.strictEqual(model.getLineContent(1), 'A line');6256assertCursor(viewModel, new Selection(1, 3, 1, 3));6257});62586259model.dispose();6260});62616262test('there is an undo stop between deleting left and typing', () => {6263const model = createTextModel(6264[6265'A line',6266'Another line',6267].join('\n')6268);62696270withTestCodeEditor(model, {}, (editor, viewModel) => {6271viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);6272editor.runCommand(CoreEditingCommands.DeleteLeft, null);6273editor.runCommand(CoreEditingCommands.DeleteLeft, null);6274editor.runCommand(CoreEditingCommands.DeleteLeft, null);6275editor.runCommand(CoreEditingCommands.DeleteLeft, null);6276editor.runCommand(CoreEditingCommands.DeleteLeft, null);6277editor.runCommand(CoreEditingCommands.DeleteLeft, null);6278editor.runCommand(CoreEditingCommands.DeleteLeft, null);6279assert.strictEqual(model.getLineContent(2), ' line');6280assertCursor(viewModel, new Selection(2, 1, 2, 1));62816282viewModel.type('Second', 'keyboard');6283assert.strictEqual(model.getLineContent(2), 'Second line');6284assertCursor(viewModel, new Selection(2, 7, 2, 7));62856286editor.runCommand(CoreEditingCommands.Undo, null);6287assert.strictEqual(model.getLineContent(2), ' line');6288assertCursor(viewModel, new Selection(2, 1, 2, 1));62896290editor.runCommand(CoreEditingCommands.Undo, null);6291assert.strictEqual(model.getLineContent(2), 'Another line');6292assertCursor(viewModel, new Selection(2, 8, 2, 8));6293});62946295model.dispose();6296});62976298test('there is an undo stop between deleting left and deleting right', () => {6299const model = createTextModel(6300[6301'A line',6302'Another line',6303].join('\n')6304);63056306withTestCodeEditor(model, {}, (editor, viewModel) => {6307viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);6308editor.runCommand(CoreEditingCommands.DeleteLeft, null);6309editor.runCommand(CoreEditingCommands.DeleteLeft, null);6310editor.runCommand(CoreEditingCommands.DeleteLeft, null);6311editor.runCommand(CoreEditingCommands.DeleteLeft, null);6312editor.runCommand(CoreEditingCommands.DeleteLeft, null);6313editor.runCommand(CoreEditingCommands.DeleteLeft, null);6314editor.runCommand(CoreEditingCommands.DeleteLeft, null);6315assert.strictEqual(model.getLineContent(2), ' line');6316assertCursor(viewModel, new Selection(2, 1, 2, 1));63176318editor.runCommand(CoreEditingCommands.DeleteRight, null);6319editor.runCommand(CoreEditingCommands.DeleteRight, null);6320editor.runCommand(CoreEditingCommands.DeleteRight, null);6321editor.runCommand(CoreEditingCommands.DeleteRight, null);6322editor.runCommand(CoreEditingCommands.DeleteRight, null);6323assert.strictEqual(model.getLineContent(2), '');6324assertCursor(viewModel, new Selection(2, 1, 2, 1));63256326editor.runCommand(CoreEditingCommands.Undo, null);6327assert.strictEqual(model.getLineContent(2), ' line');6328assertCursor(viewModel, new Selection(2, 1, 2, 1));63296330editor.runCommand(CoreEditingCommands.Undo, null);6331assert.strictEqual(model.getLineContent(2), 'Another line');6332assertCursor(viewModel, new Selection(2, 8, 2, 8));6333});63346335model.dispose();6336});63376338test('there is an undo stop between deleting right and typing', () => {6339const model = createTextModel(6340[6341'A line',6342'Another line',6343].join('\n')6344);63456346withTestCodeEditor(model, {}, (editor, viewModel) => {6347viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);6348editor.runCommand(CoreEditingCommands.DeleteRight, null);6349editor.runCommand(CoreEditingCommands.DeleteRight, null);6350editor.runCommand(CoreEditingCommands.DeleteRight, null);6351editor.runCommand(CoreEditingCommands.DeleteRight, null);6352assert.strictEqual(model.getLineContent(2), 'Another ');6353assertCursor(viewModel, new Selection(2, 9, 2, 9));63546355viewModel.type('text', 'keyboard');6356assert.strictEqual(model.getLineContent(2), 'Another text');6357assertCursor(viewModel, new Selection(2, 13, 2, 13));63586359editor.runCommand(CoreEditingCommands.Undo, null);6360assert.strictEqual(model.getLineContent(2), 'Another ');6361assertCursor(viewModel, new Selection(2, 9, 2, 9));63626363editor.runCommand(CoreEditingCommands.Undo, null);6364assert.strictEqual(model.getLineContent(2), 'Another line');6365assertCursor(viewModel, new Selection(2, 9, 2, 9));6366});63676368model.dispose();6369});63706371test('there is an undo stop between deleting right and deleting left', () => {6372const model = createTextModel(6373[6374'A line',6375'Another line',6376].join('\n')6377);63786379withTestCodeEditor(model, {}, (editor, viewModel) => {6380viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);6381editor.runCommand(CoreEditingCommands.DeleteRight, null);6382editor.runCommand(CoreEditingCommands.DeleteRight, null);6383editor.runCommand(CoreEditingCommands.DeleteRight, null);6384editor.runCommand(CoreEditingCommands.DeleteRight, null);6385assert.strictEqual(model.getLineContent(2), 'Another ');6386assertCursor(viewModel, new Selection(2, 9, 2, 9));63876388editor.runCommand(CoreEditingCommands.DeleteLeft, null);6389editor.runCommand(CoreEditingCommands.DeleteLeft, null);6390editor.runCommand(CoreEditingCommands.DeleteLeft, null);6391editor.runCommand(CoreEditingCommands.DeleteLeft, null);6392editor.runCommand(CoreEditingCommands.DeleteLeft, null);6393editor.runCommand(CoreEditingCommands.DeleteLeft, null);6394assert.strictEqual(model.getLineContent(2), 'An');6395assertCursor(viewModel, new Selection(2, 3, 2, 3));63966397editor.runCommand(CoreEditingCommands.Undo, null);6398assert.strictEqual(model.getLineContent(2), 'Another ');6399assertCursor(viewModel, new Selection(2, 9, 2, 9));64006401editor.runCommand(CoreEditingCommands.Undo, null);6402assert.strictEqual(model.getLineContent(2), 'Another line');6403assertCursor(viewModel, new Selection(2, 9, 2, 9));6404});64056406model.dispose();6407});64086409test('inserts undo stop when typing space', () => {6410const model = createTextModel(6411[6412'A line',6413'Another line',6414].join('\n')6415);64166417withTestCodeEditor(model, {}, (editor, viewModel) => {6418viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6419viewModel.type('first and interesting', 'keyboard');6420assert.strictEqual(model.getLineContent(1), 'A first and interesting line');6421assertCursor(viewModel, new Selection(1, 24, 1, 24));64226423editor.runCommand(CoreEditingCommands.Undo, null);6424assert.strictEqual(model.getLineContent(1), 'A first and line');6425assertCursor(viewModel, new Selection(1, 12, 1, 12));64266427editor.runCommand(CoreEditingCommands.Undo, null);6428assert.strictEqual(model.getLineContent(1), 'A first line');6429assertCursor(viewModel, new Selection(1, 8, 1, 8));64306431editor.runCommand(CoreEditingCommands.Undo, null);6432assert.strictEqual(model.getLineContent(1), 'A line');6433assertCursor(viewModel, new Selection(1, 3, 1, 3));6434});64356436model.dispose();6437});64386439test('can undo typing and EOL change in one undo stop', () => {6440const model = createTextModel(6441[6442'A line',6443'Another line',6444].join('\n')6445);64466447withTestCodeEditor(model, {}, (editor, viewModel) => {6448viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6449viewModel.type('first', 'keyboard');6450assert.strictEqual(model.getValue(), 'A first line\nAnother line');6451assertCursor(viewModel, new Selection(1, 8, 1, 8));64526453model.pushEOL(EndOfLineSequence.CRLF);6454assert.strictEqual(model.getValue(), 'A first line\r\nAnother line');6455assertCursor(viewModel, new Selection(1, 8, 1, 8));64566457editor.runCommand(CoreEditingCommands.Undo, null);6458assert.strictEqual(model.getValue(), 'A line\nAnother line');6459assertCursor(viewModel, new Selection(1, 3, 1, 3));6460});64616462model.dispose();6463});64646465test('issue #93585: Undo multi cursor edit corrupts document', () => {6466const model = createTextModel(6467[6468'hello world',6469'hello world',6470].join('\n')6471);64726473withTestCodeEditor(model, {}, (editor, viewModel) => {6474viewModel.setSelections('test', [6475new Selection(2, 7, 2, 12),6476new Selection(1, 7, 1, 12),6477]);6478viewModel.type('no', 'keyboard');6479assert.strictEqual(model.getValue(), 'hello no\nhello no');64806481editor.runCommand(CoreEditingCommands.Undo, null);6482assert.strictEqual(model.getValue(), 'hello world\nhello world');6483});64846485model.dispose();6486});64876488test('there is a single undo stop for consecutive whitespaces', () => {6489const model = createTextModel(6490[6491''6492].join('\n'),6493undefined,6494{6495insertSpaces: false,6496}6497);64986499withTestCodeEditor(model, {}, (editor, viewModel) => {6500viewModel.type('a', 'keyboard');6501viewModel.type('b', 'keyboard');6502viewModel.type(' ', 'keyboard');6503viewModel.type(' ', 'keyboard');6504viewModel.type('c', 'keyboard');6505viewModel.type('d', 'keyboard');65066507assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');65086509editor.runCommand(CoreEditingCommands.Undo, null);6510assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab ', 'assert2');65116512editor.runCommand(CoreEditingCommands.Undo, null);6513assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');65146515editor.runCommand(CoreEditingCommands.Undo, null);6516assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');6517});65186519model.dispose();6520});65216522test('there is no undo stop after a single whitespace', () => {6523const model = createTextModel(6524[6525''6526].join('\n'),6527undefined,6528{6529insertSpaces: false,6530}6531);65326533withTestCodeEditor(model, {}, (editor, viewModel) => {6534viewModel.type('a', 'keyboard');6535viewModel.type('b', 'keyboard');6536viewModel.type(' ', 'keyboard');6537viewModel.type('c', 'keyboard');6538viewModel.type('d', 'keyboard');65396540assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');65416542editor.runCommand(CoreEditingCommands.Undo, null);6543assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');65446545editor.runCommand(CoreEditingCommands.Undo, null);6546assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');6547});65486549model.dispose();6550});6551});65526553suite('Overtype Mode', () => {65546555setup(() => {6556InputMode.setInputMode('overtype');6557});65586559teardown(() => {6560InputMode.setInputMode('insert');6561});65626563ensureNoDisposablesAreLeakedInTestSuite();65646565test('simple type', () => {6566const model = createTextModel(6567[6568'123456789',6569'123456789',6570].join('\n'),6571undefined,6572{6573insertSpaces: false,6574}6575);65766577withTestCodeEditor(model, {}, (editor, viewModel) => {6578viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);6579viewModel.type('a', 'keyboard');6580assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6581'12a456789',6582'123456789',6583].join('\n'), 'assert1');65846585viewModel.setSelections('test', [new Selection(1, 9, 1, 9)]);6586viewModel.type('bbb', 'keyboard');6587assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6588'12a45678bbb',6589'123456789',6590].join('\n'), 'assert2');6591});65926593model.dispose();6594});65956596test('multi-line selection type', () => {6597const model = createTextModel(6598[6599'123456789',6600'123456789',6601].join('\n'),6602undefined,6603{6604insertSpaces: false,6605}6606);66076608withTestCodeEditor(model, {}, (editor, viewModel) => {6609viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);6610viewModel.type('cc', 'keyboard');6611assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6612'1234cc456789',6613].join('\n'), 'assert1');6614});66156616model.dispose();6617});66186619test('simple paste', () => {6620const model = createTextModel(6621[6622'123456789',6623'123456789',6624].join('\n'),6625undefined,6626{6627insertSpaces: false,6628}6629);66306631withTestCodeEditor(model, {}, (editor, viewModel) => {6632viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6633viewModel.paste('cc', false);6634assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6635'1234cc789',6636'123456789',6637].join('\n'), 'assert1');66386639viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6640viewModel.paste('dddddddd', false);6641assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6642'1234dddddddd',6643'123456789',6644].join('\n'), 'assert2');6645});66466647model.dispose();6648});66496650test('multi-line selection paste', () => {6651const model = createTextModel(6652[6653'123456789',6654'123456789',6655].join('\n'),6656undefined,6657{6658insertSpaces: false,6659}6660);66616662withTestCodeEditor(model, {}, (editor, viewModel) => {6663viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);6664viewModel.paste('cc', false);6665assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6666'1234cc456789',6667].join('\n'), 'assert1');6668});66696670model.dispose();6671});66726673test('paste multi-line text', () => {6674const model = createTextModel(6675[6676'123456789',6677'123456789',6678].join('\n'),6679undefined,6680{6681insertSpaces: false,6682}6683);66846685withTestCodeEditor(model, {}, (editor, viewModel) => {6686viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6687viewModel.paste([6688'aaaaaaa',6689'bbbbbbb'6690].join('\n'), false);6691assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6692'1234aaaaaaa',6693'bbbbbbb',6694'123456789',6695].join('\n'), 'assert1');6696});66976698model.dispose();6699});67006701test('composition type', () => {6702const model = createTextModel(6703[6704'123456789',6705'123456789',6706].join('\n'),6707undefined,6708{6709insertSpaces: false,6710}6711);67126713withTestCodeEditor(model, {}, (editor, viewModel) => {6714viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);6715viewModel.startComposition();6716viewModel.compositionType('セ', 0, 0, 0, 'keyboard');6717assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6718'1234セ56789',6719'123456789',6720].join('\n'), 'assert1');67216722viewModel.endComposition('keyboard');6723assert.strictEqual(model.getValue(EndOfLinePreference.LF), [6724'1234セ6789',6725'123456789',6726].join('\n'), 'assert1');6727});67286729model.dispose();6730});6731});673267336734