Merge pull request #9292 from Microsoft/sandy081/editorCommands
Introduce a move command for the editor
This commit is contained in:
commit
f39db001af
7 changed files with 249 additions and 11 deletions
|
@ -433,6 +433,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
|
|||
|
||||
// vars
|
||||
EditorType: editorCommon.EditorType,
|
||||
CursorMoveViewPosition: editorCommon.CursorMoveViewPosition,
|
||||
Handler: editorCommon.Handler,
|
||||
|
||||
// consts
|
||||
|
|
|
@ -9,11 +9,13 @@ import {IEditorService} from 'vs/platform/editor/common/editor';
|
|||
import {ServicesAccessor} from 'vs/platform/instantiation/common/instantiation';
|
||||
import {IKeybindings, KbExpr} from 'vs/platform/keybinding/common/keybinding';
|
||||
import {ICommandDescriptor, KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import {ICommandHandlerDescription} from 'vs/platform/commands/common/commands';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
import {ICommandHandler} from 'vs/platform/commands/common/commands';
|
||||
|
||||
const H = editorCommon.Handler;
|
||||
const D = editorCommon.CommandDescription;
|
||||
|
||||
export function findFocusedEditor(commandId: string, accessor: ServicesAccessor, complain: boolean): editorCommon.ICommonCodeEditor {
|
||||
let editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
|
||||
|
@ -55,17 +57,18 @@ function triggerEditorHandler(handlerId: string, accessor: ServicesAccessor, arg
|
|||
});
|
||||
}
|
||||
|
||||
function registerCoreCommand(handlerId: string, kb: IKeybindings, weight: number = KeybindingsRegistry.WEIGHT.editorCore(), when?: KbExpr): void {
|
||||
function registerCoreCommand(handlerId: string, kb: IKeybindings, weight?: number, when?: KbExpr, description?: ICommandHandlerDescription): void {
|
||||
let desc: ICommandDescriptor = {
|
||||
id: handlerId,
|
||||
handler: triggerEditorHandler.bind(null, handlerId),
|
||||
weight: weight,
|
||||
weight: weight ? weight : KeybindingsRegistry.WEIGHT.editorCore(),
|
||||
when: (when ? when : KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS)),
|
||||
primary: kb.primary,
|
||||
secondary: kb.secondary,
|
||||
win: kb.win,
|
||||
mac: kb.mac,
|
||||
linux: kb.linux
|
||||
linux: kb.linux,
|
||||
description: description
|
||||
};
|
||||
KeybindingsRegistry.registerCommandDesc(desc);
|
||||
}
|
||||
|
@ -158,6 +161,9 @@ function getWordNavigationKB(shift:boolean, key:KeyCode): number {
|
|||
// Control+Command+d => noop
|
||||
// Control+Command+shift+d => noop
|
||||
|
||||
// Register cursor commands
|
||||
registerCoreCommand(H.CursorMove, { primary: null }, null, null, D.CursorMove);
|
||||
|
||||
registerCoreCommand(H.CursorLeft, {
|
||||
primary: KeyCode.LeftArrow,
|
||||
mac: { primary: KeyCode.LeftArrow, secondary: [KeyMod.WinCtrl | KeyCode.KEY_B] }
|
||||
|
|
|
@ -944,6 +944,7 @@ export class Cursor extends EventEmitter {
|
|||
|
||||
this._handlers[H.JumpToBracket] = (ctx) => this._jumpToBracket(ctx);
|
||||
|
||||
this._handlers[H.CursorMove] = (ctx) => this._cursorMove(ctx);
|
||||
this._handlers[H.MoveTo] = (ctx) => this._moveTo(false, ctx);
|
||||
this._handlers[H.MoveToSelect] = (ctx) => this._moveTo(true, ctx);
|
||||
this._handlers[H.ColumnSelect] = (ctx) => this._columnSelectMouse(ctx);
|
||||
|
@ -1126,6 +1127,10 @@ export class Cursor extends EventEmitter {
|
|||
return this._invokeForAll(ctx, (cursorIndex: number, oneCursor: OneCursor, oneCtx: IOneCursorOperationContext) => OneCursorOp.moveTo(oneCursor, inSelectionMode, ctx.eventData.position, ctx.eventData.viewPosition, ctx.eventSource, oneCtx));
|
||||
}
|
||||
|
||||
private _cursorMove(ctx: IMultipleCursorOperationContext): boolean {
|
||||
return this._invokeForAll(ctx, (cursorIndex: number, oneCursor: OneCursor, oneCtx: IOneCursorOperationContext) => OneCursorOp.move(oneCursor, !!ctx.eventData.inSelectionMode, ctx.eventData.to, ctx.eventSource, oneCtx));
|
||||
}
|
||||
|
||||
private _columnSelectToLineNumber: number = 0;
|
||||
private _getColumnSelectToLineNumber(): number {
|
||||
if (!this._columnSelectToLineNumber) {
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {onUnexpectedError} from 'vs/base/common/errors';
|
||||
import {onUnexpectedError, illegalArgument} from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import {ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition} from 'vs/editor/common/commands/replaceCommand';
|
||||
import {ShiftCommand} from 'vs/editor/common/commands/shiftCommand';
|
||||
import {SurroundSelectionCommand} from 'vs/editor/common/commands/surroundSelectionCommand';
|
||||
|
@ -530,9 +531,6 @@ export class OneCursor {
|
|||
public getPositionDown(lineNumber:number, column:number, leftoverVisibleColumns:number, count:number, allowMoveOnLastLine:boolean): IMoveResult {
|
||||
return this.helper.getPositionDown(this.model, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnLastLine);
|
||||
}
|
||||
public getColumnAtBeginningOfLine(lineNumber:number, column:number): number {
|
||||
return this.helper.getColumnAtBeginningOfLine(this.model, lineNumber, column);
|
||||
}
|
||||
public getColumnAtEndOfLine(lineNumber:number, column:number): number {
|
||||
return this.helper.getColumnAtEndOfLine(this.model, lineNumber, column);
|
||||
}
|
||||
|
@ -553,6 +551,18 @@ export class OneCursor {
|
|||
public getViewLineMaxColumn(lineNumber:number): number {
|
||||
return this.viewModelHelper.viewModel.getLineMaxColumn(lineNumber);
|
||||
}
|
||||
public getViewLineMinColumn(lineNumber:number): number {
|
||||
return this.viewModelHelper.viewModel.getLineMinColumn(lineNumber);
|
||||
}
|
||||
public getViewLineCenterColumn(lineNumber:number): number {
|
||||
return Math.round((this.getViewLineMaxColumn(lineNumber) + this.getViewLineMinColumn(lineNumber)) / 2);
|
||||
}
|
||||
public getViewLineFirstNonWhiteSpaceColumn(lineNumber:number): number {
|
||||
return this.viewModelHelper.viewModel.getLineFirstNonWhitespaceColumn(lineNumber);
|
||||
}
|
||||
public getViewLineLastNonWhiteSpaceColumn(lineNumber:number): number {
|
||||
return this.viewModelHelper.viewModel.getLineLastNonWhitespaceColumn(lineNumber);
|
||||
}
|
||||
public getLeftOfViewPosition(lineNumber:number, column:number): editorCommon.IPosition {
|
||||
return this.helper.getLeftOfPosition(this.viewModelHelper.viewModel, lineNumber, column);
|
||||
}
|
||||
|
@ -613,7 +623,6 @@ export class OneCursorOp {
|
|||
}
|
||||
|
||||
public static moveTo(cursor:OneCursor, inSelectionMode: boolean, position: editorCommon.IPosition, viewPosition:editorCommon.IPosition, eventSource: string, ctx: IOneCursorOperationContext): boolean {
|
||||
|
||||
let validatedPosition = cursor.model.validatePosition(position);
|
||||
let validatedViewPosition: editorCommon.IPosition;
|
||||
if (viewPosition) {
|
||||
|
@ -622,15 +631,55 @@ export class OneCursorOp {
|
|||
validatedViewPosition = cursor.convertModelPositionToViewPosition(validatedPosition.lineNumber, validatedPosition.column);
|
||||
}
|
||||
|
||||
return this.move(cursor, inSelectionMode, validatedViewPosition, eventSource, ctx);
|
||||
}
|
||||
|
||||
public static move(cursor: OneCursor, inSelectionMode: boolean, to: editorCommon.IPosition | string, eventSource: string, ctx: IOneCursorOperationContext): boolean {
|
||||
if (!to) {
|
||||
illegalArgument('to');
|
||||
}
|
||||
|
||||
if (types.isString(to)) {
|
||||
return this.moveToLogicalViewPosition(cursor, inSelectionMode, to, ctx);
|
||||
}
|
||||
|
||||
let viewPosition: editorCommon.IPosition = <editorCommon.IPosition>to;
|
||||
let reason = (eventSource === 'mouse' ? editorCommon.CursorChangeReason.Explicit : editorCommon.CursorChangeReason.NotSet);
|
||||
if (eventSource === 'api') {
|
||||
ctx.shouldRevealVerticalInCenter = true;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
ctx.cursorPositionChangeReason = reason;
|
||||
}
|
||||
cursor.moveViewPosition(inSelectionMode, validatedViewPosition.lineNumber, validatedViewPosition.column, 0, false);
|
||||
cursor.moveViewPosition(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static moveToLogicalViewPosition(cursor: OneCursor, inSelectionMode: boolean, cursorMoveViewPosition: string, ctx: IOneCursorOperationContext): boolean {
|
||||
let validatedViewPosition = cursor.getValidViewPosition();
|
||||
let viewLineNumber = validatedViewPosition.lineNumber;
|
||||
let viewColumn;
|
||||
switch (cursorMoveViewPosition) {
|
||||
case editorCommon.CursorMoveViewPosition.LineStart:
|
||||
viewColumn = cursor.getViewLineMinColumn(viewLineNumber);
|
||||
break;
|
||||
case editorCommon.CursorMoveViewPosition.LineFirstNonWhitespaceCharacter:
|
||||
viewColumn = cursor.getViewLineFirstNonWhiteSpaceColumn(viewLineNumber);
|
||||
break;
|
||||
case editorCommon.CursorMoveViewPosition.LineColumnCenter:
|
||||
viewColumn = cursor.getViewLineCenterColumn(viewLineNumber);
|
||||
break;
|
||||
case editorCommon.CursorMoveViewPosition.LineEnd:
|
||||
viewColumn = cursor.getViewLineMaxColumn(viewLineNumber);
|
||||
break;
|
||||
case editorCommon.CursorMoveViewPosition.LineLastNonWhitespaceCharacter:
|
||||
viewColumn = cursor.getViewLineLastNonWhiteSpaceColumn(viewLineNumber);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit;
|
||||
cursor.moveViewPosition(inSelectionMode, viewLineNumber, viewColumn, 0, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import {IAction} from 'vs/base/common/actions';
|
||||
import {IEventEmitter, BulkListenerCallback} from 'vs/base/common/eventEmitter';
|
||||
import {MarkedString} from 'vs/base/common/htmlContent';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IInstantiationService, IConstructorSignature1, IConstructorSignature2} from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -19,6 +21,7 @@ import {Range} from 'vs/editor/common/core/range';
|
|||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {ModeTransition} from 'vs/editor/common/core/modeTransition';
|
||||
import {IndentRange} from 'vs/editor/common/model/indentRanges';
|
||||
import {ICommandHandlerDescription} from 'vs/platform/commands/common/commands';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -4171,6 +4174,33 @@ export var EventType = {
|
|||
DiffUpdated: 'diffUpdated'
|
||||
};
|
||||
|
||||
/**
|
||||
* Logical positions in the view for cursor move command.
|
||||
*/
|
||||
export const CursorMoveViewPosition = {
|
||||
LineStart: 'lineStart',
|
||||
LineFirstNonWhitespaceCharacter: 'lineFirstNonWhitespaceCharacter',
|
||||
LineColumnCenter: 'lineColumnCenter',
|
||||
LineEnd: 'lineEnd',
|
||||
LineLastNonWhitespaceCharacter: 'lineLastNonWhitespaceCharacter'
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export var CommandDescription= {
|
||||
CursorMove: <ICommandHandlerDescription>{
|
||||
description: nls.localize('editorCommand.cursorMove.description', "Move cursor to a logical position in the view"),
|
||||
args: [
|
||||
{
|
||||
name: nls.localize('editorCommand.cursorMove.arg.name', "Cursor move argument"),
|
||||
description: nls.localize('editorCommand.cursorMove.arg.description', "Argument containing mandatory 'to' value and an optional 'inSelectionMode' value. Value of 'to' has to be a defined value in `CursorMoveViewPosition`."),
|
||||
constraint: (arg) => types.isObject(arg) && types.isString(arg.to) && (types.isUndefined(arg.inSelectionMode) || types.isBoolean(arg.inSelectionMode))
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Built-in commands.
|
||||
*/
|
||||
|
@ -4230,6 +4260,8 @@ export var Handler = {
|
|||
CursorColumnSelectDown: 'cursorColumnSelectDown',
|
||||
CursorColumnSelectPageDown: 'cursorColumnSelectPageDown',
|
||||
|
||||
CursorMove: 'cursorMove',
|
||||
|
||||
AddCursorDown: 'addCursorDown',
|
||||
AddCursorUp: 'addCursorUp',
|
||||
CursorUndo: 'cursorUndo',
|
||||
|
|
|
@ -13,7 +13,7 @@ import {Selection} from 'vs/editor/common/core/selection';
|
|||
import {
|
||||
EndOfLinePreference, EventType, Handler, IPosition, ISelection, IEditorOptions,
|
||||
DefaultEndOfLine, ITextModelCreationOptions, ICommand,
|
||||
ITokenizedModel, IEditOperationBuilder, ICursorStateComputerData
|
||||
ITokenizedModel, IEditOperationBuilder, ICursorStateComputerData, CursorMoveViewPosition
|
||||
} from 'vs/editor/common/editorCommon';
|
||||
import {Model} from 'vs/editor/common/model/model';
|
||||
import {IMode, IndentAction} from 'vs/editor/common/modes';
|
||||
|
@ -30,6 +30,36 @@ function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwri
|
|||
cursor.trigger(overwriteSource || 'tests', command, extraData);
|
||||
}
|
||||
|
||||
// Move command
|
||||
|
||||
function move(cursor: Cursor, args: any) {
|
||||
cursorCommand(cursor, H.CursorMove, args);
|
||||
}
|
||||
|
||||
function moveToLineStart(cursor: Cursor) {
|
||||
move(cursor, {to: CursorMoveViewPosition.LineStart});
|
||||
}
|
||||
|
||||
function moveToLineFirstNonWhiteSpaceCharacter(cursor: Cursor) {
|
||||
move(cursor, {to: CursorMoveViewPosition.LineFirstNonWhitespaceCharacter});
|
||||
}
|
||||
|
||||
function moveToLineCenter(cursor: Cursor) {
|
||||
move(cursor, {to: CursorMoveViewPosition.LineColumnCenter});
|
||||
}
|
||||
|
||||
function moveToLineEnd(cursor: Cursor) {
|
||||
move(cursor, {to: CursorMoveViewPosition.LineEnd});
|
||||
}
|
||||
|
||||
function moveToLineLastNonWhiteSpaceCharacter(cursor: Cursor) {
|
||||
move(cursor, {to: CursorMoveViewPosition.LineLastNonWhitespaceCharacter});
|
||||
}
|
||||
|
||||
function moveToPosition(cursor: Cursor, lineNumber: number, column: number) {
|
||||
move(cursor, {to: new Position(lineNumber, column)});
|
||||
}
|
||||
|
||||
function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) {
|
||||
cursorCommand(cursor, inSelectionMode ? H.MoveToSelect : H.MoveTo, { position: new Position(lineNumber, column) });
|
||||
}
|
||||
|
@ -191,6 +221,109 @@ suite('Editor Controller - Cursor', () => {
|
|||
cursorEqual(thisCursor, 1, 1);
|
||||
});
|
||||
|
||||
// --------- move to first character of line
|
||||
|
||||
test('move to first character of line from middle', () => {
|
||||
moveTo(thisCursor, 1, 8);
|
||||
moveToLineStart(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 1);
|
||||
});
|
||||
|
||||
test('move to first character of line from first non white space character', () => {
|
||||
moveTo(thisCursor, 1, 6);
|
||||
moveToLineStart(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 1);
|
||||
});
|
||||
|
||||
test('move to first character of line from first character', () => {
|
||||
moveTo(thisCursor, 1, 1);
|
||||
moveToLineStart(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 1);
|
||||
});
|
||||
|
||||
test('move to first non white space character of line from middle', () => {
|
||||
moveTo(thisCursor, 1, 8);
|
||||
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 6);
|
||||
});
|
||||
|
||||
test('move to first non white space character of line from first non white space character', () => {
|
||||
moveTo(thisCursor, 1, 6);
|
||||
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 6);
|
||||
});
|
||||
|
||||
test('move to first non white space character of line from first character', () => {
|
||||
moveTo(thisCursor, 1, 1);
|
||||
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 6);
|
||||
});
|
||||
|
||||
test('move to end of line from middle', () => {
|
||||
moveTo(thisCursor, 1, 8);
|
||||
moveToLineEnd(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length + 1);
|
||||
});
|
||||
|
||||
test('move to end of line from last non white space character', () => {
|
||||
moveTo(thisCursor, 1, LINE1.length - 1);
|
||||
moveToLineEnd(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length + 1);
|
||||
});
|
||||
|
||||
test('move to end of line from line end', () => {
|
||||
moveTo(thisCursor, 1, LINE1.length + 1);
|
||||
moveToLineEnd(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length + 1);
|
||||
});
|
||||
|
||||
test('move to last non white space character from middle', () => {
|
||||
moveTo(thisCursor, 1, 8);
|
||||
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length - 1);
|
||||
});
|
||||
|
||||
test('move to last non white space character from last non white space character', () => {
|
||||
moveTo(thisCursor, 1, LINE1.length - 1);
|
||||
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length - 1);
|
||||
});
|
||||
|
||||
test('move to last non white space character from line end', () => {
|
||||
moveTo(thisCursor, 1, LINE1.length + 1);
|
||||
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, LINE1.length - 1);
|
||||
});
|
||||
|
||||
test('move to center of line not from center', () => {
|
||||
moveTo(thisCursor, 1, 8);
|
||||
moveToLineCenter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 11);
|
||||
});
|
||||
|
||||
test('move to center of line from center', () => {
|
||||
moveTo(thisCursor, 1, 11);
|
||||
moveToLineCenter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 11);
|
||||
});
|
||||
|
||||
test('move to center of line from start', () => {
|
||||
moveToLineStart(thisCursor);
|
||||
moveToLineCenter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 11);
|
||||
});
|
||||
|
||||
test('move to center of line from end', () => {
|
||||
moveToLineEnd(thisCursor);
|
||||
moveToLineCenter(thisCursor);
|
||||
cursorEqual(thisCursor, 1, 11);
|
||||
});
|
||||
|
||||
test('move to position', () => {
|
||||
moveToPosition(thisCursor, 3, 10);
|
||||
cursorEqual(thisCursor, 3, 10);
|
||||
});
|
||||
|
||||
test('move', () => {
|
||||
moveTo(thisCursor, 1, 2);
|
||||
cursorEqual(thisCursor, 1, 2);
|
||||
|
|
12
src/vs/monaco.d.ts
vendored
12
src/vs/monaco.d.ts
vendored
|
@ -3189,6 +3189,17 @@ declare module monaco.editor {
|
|||
IDiffEditor: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Logical positions in the view for cursor move command.
|
||||
*/
|
||||
export const CursorMoveViewPosition: {
|
||||
LineStart: string;
|
||||
LineFirstNonWhitespaceCharacter: string;
|
||||
LineColumnCenter: string;
|
||||
LineEnd: string;
|
||||
LineLastNonWhitespaceCharacter: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Built-in commands.
|
||||
*/
|
||||
|
@ -3234,6 +3245,7 @@ declare module monaco.editor {
|
|||
CursorColumnSelectPageUp: string;
|
||||
CursorColumnSelectDown: string;
|
||||
CursorColumnSelectPageDown: string;
|
||||
CursorMove: string;
|
||||
AddCursorDown: string;
|
||||
AddCursorUp: string;
|
||||
CursorUndo: string;
|
||||
|
|
Loading…
Reference in a new issue