Path: blob/main/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';7import { CoreNavigationCommands } from '../../../browser/coreCommands.js';8import { Position } from '../../../common/core/position.js';9import { Range } from '../../../common/core/range.js';10import { Selection } from '../../../common/core/selection.js';11import { CursorMove } from '../../../common/cursor/cursorMoveCommands.js';12import { ViewModel } from '../../../common/viewModel/viewModelImpl.js';13import { ITestCodeEditor, withTestCodeEditor } from '../testCodeEditor.js';1415suite('Cursor move command test', () => {1617ensureNoDisposablesAreLeakedInTestSuite();1819const TEXT = [20' \tMy First Line\t ',21'\tMy Second Line',22' Third Line🐶',23'',24'1'25].join('\n');2627function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {28withTestCodeEditor(TEXT, {}, (editor, viewModel) => {29callback(editor, viewModel);30});31}3233test('move left should move to left character', () => {34executeTest((editor, viewModel) => {35moveTo(viewModel, 1, 8);36moveLeft(viewModel);37cursorEqual(viewModel, 1, 7);38});39});4041test('move left should move to left by n characters', () => {42executeTest((editor, viewModel) => {43moveTo(viewModel, 1, 8);44moveLeft(viewModel, 3);45cursorEqual(viewModel, 1, 5);46});47});4849test('move left should move to left by half line', () => {50executeTest((editor, viewModel) => {51moveTo(viewModel, 1, 8);52moveLeft(viewModel, 1, CursorMove.RawUnit.HalfLine);53cursorEqual(viewModel, 1, 1);54});55});5657test('move left moves to previous line', () => {58executeTest((editor, viewModel) => {59moveTo(viewModel, 2, 3);60moveLeft(viewModel, 10);61cursorEqual(viewModel, 1, 21);62});63});6465test('move right should move to right character', () => {66executeTest((editor, viewModel) => {67moveTo(viewModel, 1, 5);68moveRight(viewModel);69cursorEqual(viewModel, 1, 6);70});71});7273test('move right should move to right by n characters', () => {74executeTest((editor, viewModel) => {75moveTo(viewModel, 1, 2);76moveRight(viewModel, 6);77cursorEqual(viewModel, 1, 8);78});79});8081test('move right should move to right by half line', () => {82executeTest((editor, viewModel) => {83moveTo(viewModel, 1, 4);84moveRight(viewModel, 1, CursorMove.RawUnit.HalfLine);85cursorEqual(viewModel, 1, 14);86});87});8889test('move right moves to next line', () => {90executeTest((editor, viewModel) => {91moveTo(viewModel, 1, 8);92moveRight(viewModel, 100);93cursorEqual(viewModel, 2, 1);94});95});9697test('move to first character of line from middle', () => {98executeTest((editor, viewModel) => {99moveTo(viewModel, 1, 8);100moveToLineStart(viewModel);101cursorEqual(viewModel, 1, 1);102});103});104105test('move to first character of line from first non white space character', () => {106executeTest((editor, viewModel) => {107moveTo(viewModel, 1, 6);108moveToLineStart(viewModel);109cursorEqual(viewModel, 1, 1);110});111});112113test('move to first character of line from first character', () => {114executeTest((editor, viewModel) => {115moveTo(viewModel, 1, 1);116moveToLineStart(viewModel);117cursorEqual(viewModel, 1, 1);118});119});120121test('move to first non white space character of line from middle', () => {122executeTest((editor, viewModel) => {123moveTo(viewModel, 1, 8);124moveToLineFirstNonWhitespaceCharacter(viewModel);125cursorEqual(viewModel, 1, 6);126});127});128129test('move to first non white space character of line from first non white space character', () => {130executeTest((editor, viewModel) => {131moveTo(viewModel, 1, 6);132moveToLineFirstNonWhitespaceCharacter(viewModel);133cursorEqual(viewModel, 1, 6);134});135});136137test('move to first non white space character of line from first character', () => {138executeTest((editor, viewModel) => {139moveTo(viewModel, 1, 1);140moveToLineFirstNonWhitespaceCharacter(viewModel);141cursorEqual(viewModel, 1, 6);142});143});144145test('move to end of line from middle', () => {146executeTest((editor, viewModel) => {147moveTo(viewModel, 1, 8);148moveToLineEnd(viewModel);149cursorEqual(viewModel, 1, 21);150});151});152153test('move to end of line from last non white space character', () => {154executeTest((editor, viewModel) => {155moveTo(viewModel, 1, 19);156moveToLineEnd(viewModel);157cursorEqual(viewModel, 1, 21);158});159});160161test('move to end of line from line end', () => {162executeTest((editor, viewModel) => {163moveTo(viewModel, 1, 21);164moveToLineEnd(viewModel);165cursorEqual(viewModel, 1, 21);166});167});168169test('move to last non white space character from middle', () => {170executeTest((editor, viewModel) => {171moveTo(viewModel, 1, 8);172moveToLineLastNonWhitespaceCharacter(viewModel);173cursorEqual(viewModel, 1, 19);174});175});176177test('move to last non white space character from last non white space character', () => {178executeTest((editor, viewModel) => {179moveTo(viewModel, 1, 19);180moveToLineLastNonWhitespaceCharacter(viewModel);181cursorEqual(viewModel, 1, 19);182});183});184185test('move to last non white space character from line end', () => {186executeTest((editor, viewModel) => {187moveTo(viewModel, 1, 21);188moveToLineLastNonWhitespaceCharacter(viewModel);189cursorEqual(viewModel, 1, 19);190});191});192193test('move to center of line not from center', () => {194executeTest((editor, viewModel) => {195moveTo(viewModel, 1, 8);196moveToLineCenter(viewModel);197cursorEqual(viewModel, 1, 11);198});199});200201test('move to center of line from center', () => {202executeTest((editor, viewModel) => {203moveTo(viewModel, 1, 11);204moveToLineCenter(viewModel);205cursorEqual(viewModel, 1, 11);206});207});208209test('move to center of line from start', () => {210executeTest((editor, viewModel) => {211moveToLineStart(viewModel);212moveToLineCenter(viewModel);213cursorEqual(viewModel, 1, 11);214});215});216217test('move to center of line from end', () => {218executeTest((editor, viewModel) => {219moveToLineEnd(viewModel);220moveToLineCenter(viewModel);221cursorEqual(viewModel, 1, 11);222});223});224225test('move up by cursor move command', () => {226executeTest((editor, viewModel) => {227moveTo(viewModel, 3, 5);228cursorEqual(viewModel, 3, 5);229230moveUp(viewModel, 2);231cursorEqual(viewModel, 1, 5);232233moveUp(viewModel, 1);234cursorEqual(viewModel, 1, 1);235});236});237238test('move up by model line cursor move command', () => {239executeTest((editor, viewModel) => {240moveTo(viewModel, 3, 5);241cursorEqual(viewModel, 3, 5);242243moveUpByModelLine(viewModel, 2);244cursorEqual(viewModel, 1, 5);245246moveUpByModelLine(viewModel, 1);247cursorEqual(viewModel, 1, 1);248});249});250251test('move down by model line cursor move command', () => {252executeTest((editor, viewModel) => {253moveTo(viewModel, 3, 5);254cursorEqual(viewModel, 3, 5);255256moveDownByModelLine(viewModel, 2);257cursorEqual(viewModel, 5, 2);258259moveDownByModelLine(viewModel, 1);260cursorEqual(viewModel, 5, 2);261});262});263264test('move up with selection by cursor move command', () => {265executeTest((editor, viewModel) => {266moveTo(viewModel, 3, 5);267cursorEqual(viewModel, 3, 5);268269moveUp(viewModel, 1, true);270cursorEqual(viewModel, 2, 2, 3, 5);271272moveUp(viewModel, 1, true);273cursorEqual(viewModel, 1, 5, 3, 5);274});275});276277test('move up and down with tabs by cursor move command', () => {278executeTest((editor, viewModel) => {279moveTo(viewModel, 1, 5);280cursorEqual(viewModel, 1, 5);281282moveDown(viewModel, 4);283cursorEqual(viewModel, 5, 2);284285moveUp(viewModel, 1);286cursorEqual(viewModel, 4, 1);287288moveUp(viewModel, 1);289cursorEqual(viewModel, 3, 5);290291moveUp(viewModel, 1);292cursorEqual(viewModel, 2, 2);293294moveUp(viewModel, 1);295cursorEqual(viewModel, 1, 5);296});297});298299test('move up and down with end of lines starting from a long one by cursor move command', () => {300executeTest((editor, viewModel) => {301moveToEndOfLine(viewModel);302cursorEqual(viewModel, 1, 21);303304moveToEndOfLine(viewModel);305cursorEqual(viewModel, 1, 21);306307moveDown(viewModel, 2);308cursorEqual(viewModel, 3, 17);309310moveDown(viewModel, 1);311cursorEqual(viewModel, 4, 1);312313moveDown(viewModel, 1);314cursorEqual(viewModel, 5, 2);315316moveUp(viewModel, 4);317cursorEqual(viewModel, 1, 21);318});319});320321test('move to view top line moves to first visible line if it is first line', () => {322executeTest((editor, viewModel) => {323viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);324325moveTo(viewModel, 2, 2);326moveToTop(viewModel);327328cursorEqual(viewModel, 1, 6);329});330});331332test('move to view top line moves to top visible line when first line is not visible', () => {333executeTest((editor, viewModel) => {334viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1);335336moveTo(viewModel, 4, 1);337moveToTop(viewModel);338339cursorEqual(viewModel, 2, 2);340});341});342343test('move to view top line moves to nth line from top', () => {344executeTest((editor, viewModel) => {345viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);346347moveTo(viewModel, 4, 1);348moveToTop(viewModel, 3);349350cursorEqual(viewModel, 3, 5);351});352});353354test('move to view top line moves to last line if n is greater than last visible line number', () => {355executeTest((editor, viewModel) => {356viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1);357358moveTo(viewModel, 2, 2);359moveToTop(viewModel, 4);360361cursorEqual(viewModel, 3, 5);362});363});364365test('move to view center line moves to the center line', () => {366executeTest((editor, viewModel) => {367viewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1);368369moveTo(viewModel, 2, 2);370moveToCenter(viewModel);371372cursorEqual(viewModel, 3, 5);373});374});375376test('move to view bottom line moves to last visible line if it is last line', () => {377executeTest((editor, viewModel) => {378viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);379380moveTo(viewModel, 2, 2);381moveToBottom(viewModel);382383cursorEqual(viewModel, 5, 1);384});385});386387test('move to view bottom line moves to last visible line when last line is not visible', () => {388executeTest((editor, viewModel) => {389viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1);390391moveTo(viewModel, 2, 2);392moveToBottom(viewModel);393394cursorEqual(viewModel, 3, 5);395});396});397398test('move to view bottom line moves to nth line from bottom', () => {399executeTest((editor, viewModel) => {400viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);401402moveTo(viewModel, 4, 1);403moveToBottom(viewModel, 3);404405cursorEqual(viewModel, 3, 5);406});407});408409test('move to view bottom line moves to first line if n is lesser than first visible line number', () => {410executeTest((editor, viewModel) => {411viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1);412413moveTo(viewModel, 4, 1);414moveToBottom(viewModel, 5);415416cursorEqual(viewModel, 2, 2);417});418});419});420421suite('Cursor move by blankline test', () => {422423ensureNoDisposablesAreLeakedInTestSuite();424425const TEXT = [426' \tMy First Line\t ',427'\tMy Second Line',428' Third Line🐶',429'',430'1',431'2',432'3',433'',434' ',435'a',436'b',437].join('\n');438439function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {440withTestCodeEditor(TEXT, {}, (editor, viewModel) => {441callback(editor, viewModel);442});443}444445test('move down should move to start of next blank line', () => {446executeTest((editor, viewModel) => {447moveDownByBlankLine(viewModel, false);448cursorEqual(viewModel, 4, 1);449});450});451452test('move up should move to start of previous blank line', () => {453executeTest((editor, viewModel) => {454moveTo(viewModel, 7, 1);455moveUpByBlankLine(viewModel, false);456cursorEqual(viewModel, 4, 1);457});458});459460test('move down should skip over whitespace if already on blank line', () => {461executeTest((editor, viewModel) => {462moveTo(viewModel, 8, 1);463moveDownByBlankLine(viewModel, false);464cursorEqual(viewModel, 11, 1);465});466});467468test('move up should skip over whitespace if already on blank line', () => {469executeTest((editor, viewModel) => {470moveTo(viewModel, 9, 1);471moveUpByBlankLine(viewModel, false);472cursorEqual(viewModel, 4, 1);473});474});475476test('move up should go to first column of first line if not empty', () => {477executeTest((editor, viewModel) => {478moveTo(viewModel, 2, 1);479moveUpByBlankLine(viewModel, false);480cursorEqual(viewModel, 1, 1);481});482});483484test('move down should go to first column of last line if not empty', () => {485executeTest((editor, viewModel) => {486moveTo(viewModel, 10, 1);487moveDownByBlankLine(viewModel, false);488cursorEqual(viewModel, 11, 1);489});490});491492test('select down should select to start of next blank line', () => {493executeTest((editor, viewModel) => {494moveDownByBlankLine(viewModel, true);495selectionEqual(viewModel.getSelection(), 4, 1, 1, 1);496});497});498499test('select up should select to start of previous blank line', () => {500executeTest((editor, viewModel) => {501moveTo(viewModel, 7, 1);502moveUpByBlankLine(viewModel, true);503selectionEqual(viewModel.getSelection(), 4, 1, 7, 1);504});505});506});507508// Move command509510function move(viewModel: ViewModel, args: any) {511CoreNavigationCommands.CursorMove.runCoreEditorCommand(viewModel, args);512}513514function moveToLineStart(viewModel: ViewModel) {515move(viewModel, { to: CursorMove.RawDirection.WrappedLineStart });516}517518function moveToLineFirstNonWhitespaceCharacter(viewModel: ViewModel) {519move(viewModel, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter });520}521522function moveToLineCenter(viewModel: ViewModel) {523move(viewModel, { to: CursorMove.RawDirection.WrappedLineColumnCenter });524}525526function moveToLineEnd(viewModel: ViewModel) {527move(viewModel, { to: CursorMove.RawDirection.WrappedLineEnd });528}529530function moveToLineLastNonWhitespaceCharacter(viewModel: ViewModel) {531move(viewModel, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter });532}533534function moveLeft(viewModel: ViewModel, value?: number, by?: string, select?: boolean) {535move(viewModel, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select });536}537538function moveRight(viewModel: ViewModel, value?: number, by?: string, select?: boolean) {539move(viewModel, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select });540}541542function moveUp(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {543move(viewModel, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });544}545546function moveUpByBlankLine(viewModel: ViewModel, select?: boolean) {547move(viewModel, { to: CursorMove.RawDirection.PrevBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });548}549550function moveUpByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {551move(viewModel, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select });552}553554function moveDown(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {555move(viewModel, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });556}557558function moveDownByBlankLine(viewModel: ViewModel, select?: boolean) {559move(viewModel, { to: CursorMove.RawDirection.NextBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });560}561562function moveDownByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {563move(viewModel, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select });564}565566function moveToTop(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {567move(viewModel, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select });568}569570function moveToCenter(viewModel: ViewModel, select?: boolean) {571move(viewModel, { to: CursorMove.RawDirection.ViewPortCenter, select: select });572}573574function moveToBottom(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {575move(viewModel, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select });576}577578function cursorEqual(viewModel: ViewModel, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) {579positionEqual(viewModel.getPosition(), posLineNumber, posColumn);580selectionEqual(viewModel.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn);581}582583function positionEqual(position: Position, lineNumber: number, column: number) {584assert.deepStrictEqual(position, new Position(lineNumber, column), 'position equal');585}586587function selectionEqual(selection: Selection, posLineNumber: number, posColumn: number, selLineNumber: number, selColumn: number) {588assert.deepStrictEqual({589selectionStartLineNumber: selection.selectionStartLineNumber,590selectionStartColumn: selection.selectionStartColumn,591positionLineNumber: selection.positionLineNumber,592positionColumn: selection.positionColumn593}, {594selectionStartLineNumber: selLineNumber,595selectionStartColumn: selColumn,596positionLineNumber: posLineNumber,597positionColumn: posColumn598}, 'selection equal');599}600601function moveTo(viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) {602if (inSelectionMode) {603CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, {604position: new Position(lineNumber, column)605});606} else {607CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, {608position: new Position(lineNumber, column)609});610}611}612613function moveToEndOfLine(viewModel: ViewModel, inSelectionMode: boolean = false) {614if (inSelectionMode) {615CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {});616} else {617CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {});618}619}620621622