Path: blob/main/src/vs/editor/common/cursor/cursorMoveCommands.ts
3294 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 * as types from '../../../base/common/types.js';6import { CursorState, ICursorSimpleModel, PartialCursorState, SelectionStartKind, SingleCursorState } from '../cursorCommon.js';7import { MoveOperations } from './cursorMoveOperations.js';8import { WordOperations } from './cursorWordOperations.js';9import { IPosition, Position } from '../core/position.js';10import { Range } from '../core/range.js';11import { ICommandMetadata } from '../../../platform/commands/common/commands.js';12import { IViewModel } from '../viewModel.js';13import { TextDirection } from '../model.js';1415export class CursorMoveCommands {1617public static addCursorDown(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {18const result: PartialCursorState[] = [];19let resultLen = 0;20for (let i = 0, len = cursors.length; i < len; i++) {21const cursor = cursors[i];22result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);23if (useLogicalLine) {24result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel.model, cursor.modelState));25} else {26result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel, cursor.viewState));27}28}29return result;30}3132public static addCursorUp(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {33const result: PartialCursorState[] = [];34let resultLen = 0;35for (let i = 0, len = cursors.length; i < len; i++) {36const cursor = cursors[i];37result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);38if (useLogicalLine) {39result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel.model, cursor.modelState));40} else {41result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel, cursor.viewState));42}43}44return result;45}4647public static moveToBeginningOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {48const result: PartialCursorState[] = [];49for (let i = 0, len = cursors.length; i < len; i++) {50const cursor = cursors[i];51result[i] = this._moveToLineStart(viewModel, cursor, inSelectionMode);52}5354return result;55}5657private static _moveToLineStart(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {58const currentViewStateColumn = cursor.viewState.position.column;59const currentModelStateColumn = cursor.modelState.position.column;60const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn;6162const currentViewStatelineNumber = cursor.viewState.position.lineNumber;63const firstNonBlankColumn = viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);64const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn;6566if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) {67return this._moveToLineStartByView(viewModel, cursor, inSelectionMode);68} else {69return this._moveToLineStartByModel(viewModel, cursor, inSelectionMode);70}71}7273private static _moveToLineStartByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {74return CursorState.fromViewState(75MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)76);77}7879private static _moveToLineStartByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {80return CursorState.fromModelState(81MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)82);83}8485public static moveToEndOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, sticky: boolean): PartialCursorState[] {86const result: PartialCursorState[] = [];87for (let i = 0, len = cursors.length; i < len; i++) {88const cursor = cursors[i];89result[i] = this._moveToLineEnd(viewModel, cursor, inSelectionMode, sticky);90}9192return result;93}9495private static _moveToLineEnd(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {96const viewStatePosition = cursor.viewState.position;97const viewModelMaxColumn = viewModel.getLineMaxColumn(viewStatePosition.lineNumber);98const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn;99100const modelStatePosition = cursor.modelState.position;101const modelMaxColumn = viewModel.model.getLineMaxColumn(modelStatePosition.lineNumber);102const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column;103104if (isEndOfViewLine || isEndLineOfWrappedLine) {105return this._moveToLineEndByModel(viewModel, cursor, inSelectionMode, sticky);106} else {107return this._moveToLineEndByView(viewModel, cursor, inSelectionMode, sticky);108}109}110111private static _moveToLineEndByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {112return CursorState.fromViewState(113MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, sticky)114);115}116117private static _moveToLineEndByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {118return CursorState.fromModelState(119MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, sticky)120);121}122123public static expandLineSelection(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {124const result: PartialCursorState[] = [];125for (let i = 0, len = cursors.length; i < len; i++) {126const cursor = cursors[i];127128const startLineNumber = cursor.modelState.selection.startLineNumber;129const lineCount = viewModel.model.getLineCount();130131let endLineNumber = cursor.modelState.selection.endLineNumber;132let endColumn: number;133if (endLineNumber === lineCount) {134endColumn = viewModel.model.getLineMaxColumn(lineCount);135} else {136endLineNumber++;137endColumn = 1;138}139140result[i] = CursorState.fromModelState(new SingleCursorState(141new Range(startLineNumber, 1, startLineNumber, 1), SelectionStartKind.Simple, 0,142new Position(endLineNumber, endColumn), 0143));144}145return result;146}147148public static moveToBeginningOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {149const result: PartialCursorState[] = [];150for (let i = 0, len = cursors.length; i < len; i++) {151const cursor = cursors[i];152result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));153}154return result;155}156157public static moveToEndOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {158const result: PartialCursorState[] = [];159for (let i = 0, len = cursors.length; i < len; i++) {160const cursor = cursors[i];161result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));162}163return result;164}165166public static selectAll(viewModel: IViewModel, cursor: CursorState): PartialCursorState {167const lineCount = viewModel.model.getLineCount();168const maxColumn = viewModel.model.getLineMaxColumn(lineCount);169170return CursorState.fromModelState(new SingleCursorState(171new Range(1, 1, 1, 1), SelectionStartKind.Simple, 0,172new Position(lineCount, maxColumn), 0173));174}175176public static line(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined): PartialCursorState {177const position = viewModel.model.validatePosition(_position);178const viewPosition = (179_viewPosition180? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)181: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)182);183184if (!inSelectionMode) {185// Entering line selection for the first time186const lineCount = viewModel.model.getLineCount();187188let selectToLineNumber = position.lineNumber + 1;189let selectToColumn = 1;190if (selectToLineNumber > lineCount) {191selectToLineNumber = lineCount;192selectToColumn = viewModel.model.getLineMaxColumn(selectToLineNumber);193}194195return CursorState.fromModelState(new SingleCursorState(196new Range(position.lineNumber, 1, selectToLineNumber, selectToColumn), SelectionStartKind.Line, 0,197new Position(selectToLineNumber, selectToColumn), 0198));199}200201// Continuing line selection202const enteringLineNumber = cursor.modelState.selectionStart.getStartPosition().lineNumber;203204if (position.lineNumber < enteringLineNumber) {205206return CursorState.fromViewState(cursor.viewState.move(207true, viewPosition.lineNumber, 1, 0208));209210} else if (position.lineNumber > enteringLineNumber) {211212const lineCount = viewModel.getLineCount();213214let selectToViewLineNumber = viewPosition.lineNumber + 1;215let selectToViewColumn = 1;216if (selectToViewLineNumber > lineCount) {217selectToViewLineNumber = lineCount;218selectToViewColumn = viewModel.getLineMaxColumn(selectToViewLineNumber);219}220221return CursorState.fromViewState(cursor.viewState.move(222true, selectToViewLineNumber, selectToViewColumn, 0223));224225} else {226227const endPositionOfSelectionStart = cursor.modelState.selectionStart.getEndPosition();228return CursorState.fromModelState(cursor.modelState.move(229true, endPositionOfSelectionStart.lineNumber, endPositionOfSelectionStart.column, 0230));231232}233}234235public static word(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {236const position = viewModel.model.validatePosition(_position);237return CursorState.fromModelState(WordOperations.word(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, position));238}239240public static cancelSelection(viewModel: IViewModel, cursor: CursorState): PartialCursorState {241if (!cursor.modelState.hasSelection()) {242return new CursorState(cursor.modelState, cursor.viewState);243}244245const lineNumber = cursor.viewState.position.lineNumber;246const column = cursor.viewState.position.column;247248return CursorState.fromViewState(new SingleCursorState(249new Range(lineNumber, column, lineNumber, column), SelectionStartKind.Simple, 0,250new Position(lineNumber, column), 0251));252}253254public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined): PartialCursorState {255if (inSelectionMode) {256if (cursor.modelState.selectionStartKind === SelectionStartKind.Word) {257return this.word(viewModel, cursor, inSelectionMode, _position);258}259if (cursor.modelState.selectionStartKind === SelectionStartKind.Line) {260return this.line(viewModel, cursor, inSelectionMode, _position, _viewPosition);261}262}263const position = viewModel.model.validatePosition(_position);264const viewPosition = (265_viewPosition266? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)267: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)268);269return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0));270}271272public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {273switch (direction) {274case CursorMove.Direction.Left: {275if (unit === CursorMove.Unit.HalfLine) {276// Move left by half the current line length277return this._moveHalfLineLeft(viewModel, cursors, inSelectionMode);278} else {279// Move left by `moveParams.value` columns280return this._moveLeft(viewModel, cursors, inSelectionMode, value);281}282}283case CursorMove.Direction.Right: {284if (unit === CursorMove.Unit.HalfLine) {285// Move right by half the current line length286return this._moveHalfLineRight(viewModel, cursors, inSelectionMode);287} else {288// Move right by `moveParams.value` columns289return this._moveRight(viewModel, cursors, inSelectionMode, value);290}291}292case CursorMove.Direction.Up: {293if (unit === CursorMove.Unit.WrappedLine) {294// Move up by view lines295return this._moveUpByViewLines(viewModel, cursors, inSelectionMode, value);296} else {297// Move up by model lines298return this._moveUpByModelLines(viewModel, cursors, inSelectionMode, value);299}300}301case CursorMove.Direction.Down: {302if (unit === CursorMove.Unit.WrappedLine) {303// Move down by view lines304return this._moveDownByViewLines(viewModel, cursors, inSelectionMode, value);305} else {306// Move down by model lines307return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value);308}309}310case CursorMove.Direction.PrevBlankLine: {311if (unit === CursorMove.Unit.WrappedLine) {312return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));313} else {314return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));315}316}317case CursorMove.Direction.NextBlankLine: {318if (unit === CursorMove.Unit.WrappedLine) {319return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));320} else {321return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));322}323}324case CursorMove.Direction.WrappedLineStart: {325// Move to the beginning of the current view line326return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode);327}328case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: {329// Move to the first non-whitespace column of the current view line330return this._moveToViewFirstNonWhitespaceColumn(viewModel, cursors, inSelectionMode);331}332case CursorMove.Direction.WrappedLineColumnCenter: {333// Move to the "center" of the current view line334return this._moveToViewCenterColumn(viewModel, cursors, inSelectionMode);335}336case CursorMove.Direction.WrappedLineEnd: {337// Move to the end of the current view line338return this._moveToViewMaxColumn(viewModel, cursors, inSelectionMode);339}340case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: {341// Move to the last non-whitespace column of the current view line342return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode);343}344default:345return null;346}347348}349350public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {351const visibleViewRange = viewModel.getCompletelyVisibleViewRange();352const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);353switch (direction) {354case CursorMove.Direction.ViewPortTop: {355// Move to the nth line start in the viewport (from the top)356const modelLineNumber = this._firstLineNumberInRange(viewModel.model, visibleModelRange, value);357const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);358return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];359}360case CursorMove.Direction.ViewPortBottom: {361// Move to the nth line start in the viewport (from the bottom)362const modelLineNumber = this._lastLineNumberInRange(viewModel.model, visibleModelRange, value);363const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);364return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];365}366case CursorMove.Direction.ViewPortCenter: {367// Move to the line start in the viewport center368const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2);369const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);370return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];371}372case CursorMove.Direction.ViewPortIfOutside: {373// Move to a position inside the viewport374const result: PartialCursorState[] = [];375for (let i = 0, len = cursors.length; i < len; i++) {376const cursor = cursors[i];377result[i] = this.findPositionInViewportIfOutside(viewModel, cursor, visibleViewRange, inSelectionMode);378}379return result;380}381default:382return null;383}384}385386public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {387const viewLineNumber = cursor.viewState.position.lineNumber;388389if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) {390// Nothing to do, cursor is in viewport391return new CursorState(cursor.modelState, cursor.viewState);392393} else {394let newViewLineNumber: number;395if (viewLineNumber > visibleViewRange.endLineNumber - 1) {396newViewLineNumber = visibleViewRange.endLineNumber - 1;397} else if (viewLineNumber < visibleViewRange.startLineNumber) {398newViewLineNumber = visibleViewRange.startLineNumber;399} else {400newViewLineNumber = viewLineNumber;401}402const position = MoveOperations.vertical(viewModel.cursorConfig, viewModel, viewLineNumber, cursor.viewState.position.column, cursor.viewState.leftoverVisibleColumns, newViewLineNumber, false);403return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, position.lineNumber, position.column, position.leftoverVisibleColumns));404}405}406407/**408* Find the nth line start included in the range (from the start).409*/410private static _firstLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number {411let startLineNumber = range.startLineNumber;412if (range.startColumn !== model.getLineMinColumn(startLineNumber)) {413// Move on to the second line if the first line start is not included in the range414startLineNumber++;415}416417return Math.min(range.endLineNumber, startLineNumber + count - 1);418}419420/**421* Find the nth line start included in the range (from the end).422*/423private static _lastLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number {424let startLineNumber = range.startLineNumber;425if (range.startColumn !== model.getLineMinColumn(startLineNumber)) {426// Move on to the second line if the first line start is not included in the range427startLineNumber++;428}429430return Math.max(startLineNumber, range.endLineNumber - count + 1);431}432433private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {434return cursors.map(cursor => {435const direction = viewModel.getTextDirection(cursor.viewState.position.lineNumber);436const isRtl = direction === TextDirection.RTL;437438return CursorState.fromViewState(439isRtl440? MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)441: MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)442);443});444}445446private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {447const result: PartialCursorState[] = [];448for (let i = 0, len = cursors.length; i < len; i++) {449const cursor = cursors[i];450const viewLineNumber = cursor.viewState.position.lineNumber;451const halfLine = Math.round(viewModel.getLineLength(viewLineNumber) / 2);452result[i] = CursorState.fromViewState(MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));453}454return result;455}456457private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {458return cursors.map(cursor => {459const direction = viewModel.getTextDirection(cursor.viewState.position.lineNumber);460const isRtl = direction === TextDirection.RTL;461462return CursorState.fromViewState(463isRtl464? MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)465: MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)466);467});468}469470private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {471const result: PartialCursorState[] = [];472for (let i = 0, len = cursors.length; i < len; i++) {473const cursor = cursors[i];474const viewLineNumber = cursor.viewState.position.lineNumber;475const halfLine = Math.round(viewModel.getLineLength(viewLineNumber) / 2);476result[i] = CursorState.fromViewState(MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));477}478return result;479}480481private static _moveDownByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {482const result: PartialCursorState[] = [];483for (let i = 0, len = cursors.length; i < len; i++) {484const cursor = cursors[i];485result[i] = CursorState.fromViewState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));486}487return result;488}489490private static _moveDownByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {491const result: PartialCursorState[] = [];492for (let i = 0, len = cursors.length; i < len; i++) {493const cursor = cursors[i];494result[i] = CursorState.fromModelState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));495}496return result;497}498499private static _moveUpByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {500const result: PartialCursorState[] = [];501for (let i = 0, len = cursors.length; i < len; i++) {502const cursor = cursors[i];503result[i] = CursorState.fromViewState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));504}505return result;506}507508private static _moveUpByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {509const result: PartialCursorState[] = [];510for (let i = 0, len = cursors.length; i < len; i++) {511const cursor = cursors[i];512result[i] = CursorState.fromModelState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));513}514return result;515}516517private static _moveToViewPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {518return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0));519}520521private static _moveToModelPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {522return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0));523}524525private static _moveToViewMinColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {526const result: PartialCursorState[] = [];527for (let i = 0, len = cursors.length; i < len; i++) {528const cursor = cursors[i];529const viewLineNumber = cursor.viewState.position.lineNumber;530const viewColumn = viewModel.getLineMinColumn(viewLineNumber);531result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);532}533return result;534}535536private static _moveToViewFirstNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {537const result: PartialCursorState[] = [];538for (let i = 0, len = cursors.length; i < len; i++) {539const cursor = cursors[i];540const viewLineNumber = cursor.viewState.position.lineNumber;541const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);542result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);543}544return result;545}546547private static _moveToViewCenterColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {548const result: PartialCursorState[] = [];549for (let i = 0, len = cursors.length; i < len; i++) {550const cursor = cursors[i];551const viewLineNumber = cursor.viewState.position.lineNumber;552const viewColumn = Math.round((viewModel.getLineMaxColumn(viewLineNumber) + viewModel.getLineMinColumn(viewLineNumber)) / 2);553result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);554}555return result;556}557558private static _moveToViewMaxColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {559const result: PartialCursorState[] = [];560for (let i = 0, len = cursors.length; i < len; i++) {561const cursor = cursors[i];562const viewLineNumber = cursor.viewState.position.lineNumber;563const viewColumn = viewModel.getLineMaxColumn(viewLineNumber);564result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);565}566return result;567}568569private static _moveToViewLastNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {570const result: PartialCursorState[] = [];571for (let i = 0, len = cursors.length; i < len; i++) {572const cursor = cursors[i];573const viewLineNumber = cursor.viewState.position.lineNumber;574const viewColumn = viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);575result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);576}577return result;578}579}580581export namespace CursorMove {582583const isCursorMoveArgs = function (arg: any): boolean {584if (!types.isObject(arg)) {585return false;586}587588const cursorMoveArg: RawArguments = arg;589590if (!types.isString(cursorMoveArg.to)) {591return false;592}593594if (!types.isUndefined(cursorMoveArg.select) && !types.isBoolean(cursorMoveArg.select)) {595return false;596}597598if (!types.isUndefined(cursorMoveArg.by) && !types.isString(cursorMoveArg.by)) {599return false;600}601602if (!types.isUndefined(cursorMoveArg.value) && !types.isNumber(cursorMoveArg.value)) {603return false;604}605606return true;607};608609export const metadata: ICommandMetadata = {610description: 'Move cursor to a logical position in the view',611args: [612{613name: 'Cursor move argument object',614description: `Property-value pairs that can be passed through this argument:615* 'to': A mandatory logical position value providing where to move the cursor.616\`\`\`617'left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine',618'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'619'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'620'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'621\`\`\`622* 'by': Unit to move. Default is computed based on 'to' value.623\`\`\`624'line', 'wrappedLine', 'character', 'halfLine'625\`\`\`626* 'value': Number of units to move. Default is '1'.627* 'select': If 'true' makes the selection. Default is 'false'.628`,629constraint: isCursorMoveArgs,630schema: {631'type': 'object',632'required': ['to'],633'properties': {634'to': {635'type': 'string',636'enum': ['left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside']637},638'by': {639'type': 'string',640'enum': ['line', 'wrappedLine', 'character', 'halfLine']641},642'value': {643'type': 'number',644'default': 1645},646'select': {647'type': 'boolean',648'default': false649}650}651}652}653]654};655656/**657* Positions in the view for cursor move command.658*/659export const RawDirection = {660Left: 'left',661Right: 'right',662Up: 'up',663Down: 'down',664665PrevBlankLine: 'prevBlankLine',666NextBlankLine: 'nextBlankLine',667668WrappedLineStart: 'wrappedLineStart',669WrappedLineFirstNonWhitespaceCharacter: 'wrappedLineFirstNonWhitespaceCharacter',670WrappedLineColumnCenter: 'wrappedLineColumnCenter',671WrappedLineEnd: 'wrappedLineEnd',672WrappedLineLastNonWhitespaceCharacter: 'wrappedLineLastNonWhitespaceCharacter',673674ViewPortTop: 'viewPortTop',675ViewPortCenter: 'viewPortCenter',676ViewPortBottom: 'viewPortBottom',677678ViewPortIfOutside: 'viewPortIfOutside'679};680681/**682* Units for Cursor move 'by' argument683*/684export const RawUnit = {685Line: 'line',686WrappedLine: 'wrappedLine',687Character: 'character',688HalfLine: 'halfLine'689};690691/**692* Arguments for Cursor move command693*/694export interface RawArguments {695to: string;696select?: boolean;697by?: string;698value?: number;699}700701export function parse(args: Partial<RawArguments>): ParsedArguments | null {702if (!args.to) {703// illegal arguments704return null;705}706707let direction: Direction;708switch (args.to) {709case RawDirection.Left:710direction = Direction.Left;711break;712case RawDirection.Right:713direction = Direction.Right;714break;715case RawDirection.Up:716direction = Direction.Up;717break;718case RawDirection.Down:719direction = Direction.Down;720break;721case RawDirection.PrevBlankLine:722direction = Direction.PrevBlankLine;723break;724case RawDirection.NextBlankLine:725direction = Direction.NextBlankLine;726break;727case RawDirection.WrappedLineStart:728direction = Direction.WrappedLineStart;729break;730case RawDirection.WrappedLineFirstNonWhitespaceCharacter:731direction = Direction.WrappedLineFirstNonWhitespaceCharacter;732break;733case RawDirection.WrappedLineColumnCenter:734direction = Direction.WrappedLineColumnCenter;735break;736case RawDirection.WrappedLineEnd:737direction = Direction.WrappedLineEnd;738break;739case RawDirection.WrappedLineLastNonWhitespaceCharacter:740direction = Direction.WrappedLineLastNonWhitespaceCharacter;741break;742case RawDirection.ViewPortTop:743direction = Direction.ViewPortTop;744break;745case RawDirection.ViewPortBottom:746direction = Direction.ViewPortBottom;747break;748case RawDirection.ViewPortCenter:749direction = Direction.ViewPortCenter;750break;751case RawDirection.ViewPortIfOutside:752direction = Direction.ViewPortIfOutside;753break;754default:755// illegal arguments756return null;757}758759let unit = Unit.None;760switch (args.by) {761case RawUnit.Line:762unit = Unit.Line;763break;764case RawUnit.WrappedLine:765unit = Unit.WrappedLine;766break;767case RawUnit.Character:768unit = Unit.Character;769break;770case RawUnit.HalfLine:771unit = Unit.HalfLine;772break;773}774775return {776direction: direction,777unit: unit,778select: (!!args.select),779value: (args.value || 1)780};781}782783export interface ParsedArguments {784direction: Direction;785unit: Unit;786select: boolean;787value: number;788}789790export interface SimpleMoveArguments {791direction: SimpleMoveDirection;792unit: Unit;793select: boolean;794value: number;795}796797export const enum Direction {798Left,799Right,800Up,801Down,802PrevBlankLine,803NextBlankLine,804805WrappedLineStart,806WrappedLineFirstNonWhitespaceCharacter,807WrappedLineColumnCenter,808WrappedLineEnd,809WrappedLineLastNonWhitespaceCharacter,810811ViewPortTop,812ViewPortCenter,813ViewPortBottom,814815ViewPortIfOutside,816}817818export type SimpleMoveDirection = (819Direction.Left820| Direction.Right821| Direction.Up822| Direction.Down823| Direction.PrevBlankLine824| Direction.NextBlankLine825| Direction.WrappedLineStart826| Direction.WrappedLineFirstNonWhitespaceCharacter827| Direction.WrappedLineColumnCenter828| Direction.WrappedLineEnd829| Direction.WrappedLineLastNonWhitespaceCharacter830);831832export type ViewportDirection = (833Direction.ViewPortTop834| Direction.ViewPortCenter835| Direction.ViewPortBottom836| Direction.ViewPortIfOutside837);838839export const enum Unit {840None,841Line,842WrappedLine,843Character,844HalfLine,845}846847}848849850