Merge pull request #9292 from Microsoft/sandy081/editorCommands

Introduce a move command for the editor
This commit is contained in:
Sandeep Somavarapu 2016-07-18 09:36:30 +02:00 committed by GitHub
commit f39db001af
7 changed files with 249 additions and 11 deletions

View file

@ -433,6 +433,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
// vars
EditorType: editorCommon.EditorType,
CursorMoveViewPosition: editorCommon.CursorMoveViewPosition,
Handler: editorCommon.Handler,
// consts

View file

@ -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] }

View file

@ -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) {

View file

@ -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;
}

View file

@ -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',

View file

@ -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
View file

@ -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;