import * as nls from '../../nls.js';
import { isFirefox } from '../../base/browser/browser.js';
import { KeyCode, KeyMod } from '../../base/common/keyCodes.js';
import * as types from '../../base/common/types.js';
import { status } from '../../base/browser/ui/aria/aria.js';
import { ICodeEditor } from './editorBrowser.js';
import { Command, EditorCommand, ICommandOptions, registerEditorCommand, MultiCommand, UndoCommand, RedoCommand, SelectAllCommand } from './editorExtensions.js';
import { ICodeEditorService } from './services/codeEditorService.js';
import { ColumnSelection, IColumnSelectResult } from '../common/cursor/cursorColumnSelection.js';
import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from '../common/cursorCommon.js';
import { DeleteOperations } from '../common/cursor/cursorDeleteOperations.js';
import { CursorChangeReason } from '../common/cursorEvents.js';
import { CursorMove as CursorMove_, CursorMoveCommands } from '../common/cursor/cursorMoveCommands.js';
import { TypeOperations } from '../common/cursor/cursorTypeOperations.js';
import { IPosition, Position } from '../common/core/position.js';
import { Range } from '../common/core/range.js';
import { Handler, ScrollType } from '../common/editorCommon.js';
import { EditorContextKeys } from '../common/editorContextKeys.js';
import { VerticalRevealType } from '../common/viewEvents.js';
import { ICommandMetadata } from '../../platform/commands/common/commands.js';
import { ContextKeyExpr } from '../../platform/contextkey/common/contextkey.js';
import { ServicesAccessor } from '../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight, KeybindingsRegistry } from '../../platform/keybinding/common/keybindingsRegistry.js';
import { EditorOption } from '../common/config/editorOptions.js';
import { IViewModel } from '../common/viewModel.js';
import { ISelection } from '../common/core/selection.js';
import { getActiveElement, isEditableElement } from '../../base/browser/dom.js';
import { EnterOperation } from '../common/cursor/cursorTypeEditOperations.js';
const CORE_WEIGHT = KeybindingWeight.EditorCore;
export abstract class CoreEditorCommand<T> extends EditorCommand {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args?: Partial<T> | null): void {
const viewModel = editor._getViewModel();
if (!viewModel) {
return;
}
this.runCoreEditorCommand(viewModel, args || {});
}
public abstract runCoreEditorCommand(viewModel: IViewModel, args: Partial<T>): void;
}
export namespace EditorScroll_ {
const isEditorScrollArgs = function (arg: any): boolean {
if (!types.isObject(arg)) {
return false;
}
const scrollArg: RawArguments = arg;
if (!types.isString(scrollArg.to)) {
return false;
}
if (!types.isUndefined(scrollArg.by) && !types.isString(scrollArg.by)) {
return false;
}
if (!types.isUndefined(scrollArg.value) && !types.isNumber(scrollArg.value)) {
return false;
}
if (!types.isUndefined(scrollArg.revealCursor) && !types.isBoolean(scrollArg.revealCursor)) {
return false;
}
return true;
};
export const metadata: ICommandMetadata = {
description: 'Scroll editor in the given direction',
args: [
{
name: 'Editor scroll argument object',
description: `Property-value pairs that can be passed through this argument:
* 'to': A mandatory direction value.
\`\`\`
'up', 'down'
\`\`\`
* 'by': Unit to move. Default is computed based on 'to' value.
\`\`\`
'line', 'wrappedLine', 'page', 'halfPage', 'editor'
\`\`\`
* 'value': Number of units to move. Default is '1'.
* 'revealCursor': If 'true' reveals the cursor if it is outside view port.
`,
constraint: isEditorScrollArgs,
schema: {
'type': 'object',
'required': ['to'],
'properties': {
'to': {
'type': 'string',
'enum': ['up', 'down']
},
'by': {
'type': 'string',
'enum': ['line', 'wrappedLine', 'page', 'halfPage', 'editor']
},
'value': {
'type': 'number',
'default': 1
},
'revealCursor': {
'type': 'boolean',
}
}
}
}
]
};
export const RawDirection = {
Up: 'up',
Right: 'right',
Down: 'down',
Left: 'left'
};
export const RawUnit = {
Line: 'line',
WrappedLine: 'wrappedLine',
Page: 'page',
HalfPage: 'halfPage',
Editor: 'editor',
Column: 'column'
};
export interface RawArguments {
to: string;
by?: string;
value?: number;
revealCursor?: boolean;
select?: boolean;
}
export function parse(args: Partial<RawArguments>): ParsedArguments | null {
let direction: Direction;
switch (args.to) {
case RawDirection.Up:
direction = Direction.Up;
break;
case RawDirection.Right:
direction = Direction.Right;
break;
case RawDirection.Down:
direction = Direction.Down;
break;
case RawDirection.Left:
direction = Direction.Left;
break;
default:
return null;
}
let unit: Unit;
switch (args.by) {
case RawUnit.Line:
unit = Unit.Line;
break;
case RawUnit.WrappedLine:
unit = Unit.WrappedLine;
break;
case RawUnit.Page:
unit = Unit.Page;
break;
case RawUnit.HalfPage:
unit = Unit.HalfPage;
break;
case RawUnit.Editor:
unit = Unit.Editor;
break;
case RawUnit.Column:
unit = Unit.Column;
break;
default:
unit = Unit.WrappedLine;
}
const value = Math.floor(args.value || 1);
const revealCursor = !!args.revealCursor;
return {
direction: direction,
unit: unit,
value: value,
revealCursor: revealCursor,
select: (!!args.select)
};
}
export interface ParsedArguments {
direction: Direction;
unit: Unit;
value: number;
revealCursor: boolean;
select: boolean;
}
export const enum Direction {
Up = 1,
Right = 2,
Down = 3,
Left = 4
}
export const enum Unit {
Line = 1,
WrappedLine = 2,
Page = 3,
HalfPage = 4,
Editor = 5,
Column = 6
}
}
export namespace RevealLine_ {
const isRevealLineArgs = function (arg: any): boolean {
if (!types.isObject(arg)) {
return false;
}
const reveaLineArg: RawArguments = arg;
if (!types.isNumber(reveaLineArg.lineNumber) && !types.isString(reveaLineArg.lineNumber)) {
return false;
}
if (!types.isUndefined(reveaLineArg.at) && !types.isString(reveaLineArg.at)) {
return false;
}
return true;
};
export const metadata: ICommandMetadata = {
description: 'Reveal the given line at the given logical position',
args: [
{
name: 'Reveal line argument object',
description: `Property-value pairs that can be passed through this argument:
* 'lineNumber': A mandatory line number value.
* 'at': Logical position at which line has to be revealed.
\`\`\`
'top', 'center', 'bottom'
\`\`\`
`,
constraint: isRevealLineArgs,
schema: {
'type': 'object',
'required': ['lineNumber'],
'properties': {
'lineNumber': {
'type': ['number', 'string'],
},
'at': {
'type': 'string',
'enum': ['top', 'center', 'bottom']
}
}
}
}
]
};
export interface RawArguments {
lineNumber?: number | string;
at?: string;
}
export const RawAtArgument = {
Top: 'top',
Center: 'center',
Bottom: 'bottom'
};
}
abstract class EditorOrNativeTextInputCommand {
constructor(target: MultiCommand) {
target.addImplementation(10000, 'code-editor', (accessor: ServicesAccessor, args: unknown) => {
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
return this._runEditorCommand(accessor, focusedEditor, args);
}
return false;
});
target.addImplementation(1000, 'generic-dom-input-textarea', (accessor: ServicesAccessor, args: unknown) => {
const activeElement = getActiveElement();
if (activeElement && isEditableElement(activeElement)) {
this.runDOMCommand(activeElement);
return true;
}
return false;
});
target.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: unknown) => {
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
if (activeEditor) {
activeEditor.focus();
return this._runEditorCommand(accessor, activeEditor, args);
}
return false;
});
}
public _runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: unknown): boolean | Promise<void> {
const result = this.runEditorCommand(accessor, editor, args);
if (result) {
return result;
}
return true;
}
public abstract runDOMCommand(activeElement: Element): void;
public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: unknown): void | Promise<void>;
}
export const enum NavigationCommandRevealType {
Regular = 0,
Minimal = 1,
None = 2
}
export namespace CoreNavigationCommands {
export interface BaseCommandOptions {
source?: 'mouse' | 'keyboard' | string;
}
export interface MoveCommandOptions extends BaseCommandOptions {
position: IPosition;
viewPosition?: IPosition;
revealType: NavigationCommandRevealType;
}
class BaseMoveToCommand extends CoreEditorCommand<MoveCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
viewModel.model.pushStackElement();
const cursorStateChanged = viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition)
]
);
if (cursorStateChanged && args.revealType !== NavigationCommandRevealType.None) {
viewModel.revealAllCursors(args.source, true, true);
}
}
}
export const MoveTo: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new BaseMoveToCommand({
id: '_moveTo',
inSelectionMode: false,
precondition: undefined
}));
export const MoveToSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new BaseMoveToCommand({
id: '_moveToSelect',
inSelectionMode: true,
precondition: undefined
}));
abstract class ColumnSelectCommand<T extends BaseCommandOptions = BaseCommandOptions> extends CoreEditorCommand<T> {
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<T>): void {
viewModel.model.pushStackElement();
const result = this._getColumnSelectResult(viewModel, viewModel.getPrimaryCursorState(), viewModel.getCursorColumnSelectData(), args);
if (result === null) {
return;
}
viewModel.setCursorStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState)));
viewModel.setCursorColumnSelectData({
isReal: true,
fromViewLineNumber: result.fromLineNumber,
fromViewVisualColumn: result.fromVisualColumn,
toViewLineNumber: result.toLineNumber,
toViewVisualColumn: result.toVisualColumn
});
if (result.reversed) {
viewModel.revealTopMostCursor(args.source);
} else {
viewModel.revealBottomMostCursor(args.source);
}
}
protected abstract _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<T>): IColumnSelectResult | null;
}
export interface ColumnSelectCommandOptions extends BaseCommandOptions {
position: IPosition;
viewPosition: IPosition;
mouseColumn: number;
doColumnSelect: boolean;
}
export const ColumnSelect: CoreEditorCommand<ColumnSelectCommandOptions> = registerEditorCommand(new class extends ColumnSelectCommand<ColumnSelectCommandOptions> {
constructor() {
super({
id: 'columnSelect',
precondition: undefined
});
}
protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<ColumnSelectCommandOptions>): IColumnSelectResult | null {
if (typeof args.position === 'undefined' || typeof args.viewPosition === 'undefined' || typeof args.mouseColumn === 'undefined') {
return null;
}
const validatedPosition = viewModel.model.validatePosition(args.position);
const validatedViewPosition = viewModel.coordinatesConverter.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition);
const fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber;
const fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1;
return ColumnSelection.columnSelect(viewModel.cursorConfig, viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1);
}
});
export const CursorColumnSelectLeft: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends ColumnSelectCommand {
constructor() {
super({
id: 'cursorColumnSelectLeft',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.LeftArrow,
linux: { primary: 0 }
}
});
}
protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<BaseCommandOptions>): IColumnSelectResult {
return ColumnSelection.columnSelectLeft(viewModel.cursorConfig, viewModel, prevColumnSelectData);
}
});
export const CursorColumnSelectRight: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends ColumnSelectCommand {
constructor() {
super({
id: 'cursorColumnSelectRight',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.RightArrow,
linux: { primary: 0 }
}
});
}
protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<BaseCommandOptions>): IColumnSelectResult {
return ColumnSelection.columnSelectRight(viewModel.cursorConfig, viewModel, prevColumnSelectData);
}
});
class ColumnSelectUpCommand extends ColumnSelectCommand {
private readonly _isPaged: boolean;
constructor(opts: ICommandOptions & { isPaged: boolean }) {
super(opts);
this._isPaged = opts.isPaged;
}
protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<BaseCommandOptions>): IColumnSelectResult {
return ColumnSelection.columnSelectUp(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged);
}
}
export const CursorColumnSelectUp: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new ColumnSelectUpCommand({
isPaged: false,
id: 'cursorColumnSelectUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.UpArrow,
linux: { primary: 0 }
}
}));
export const CursorColumnSelectPageUp: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new ColumnSelectUpCommand({
isPaged: true,
id: 'cursorColumnSelectPageUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.PageUp,
linux: { primary: 0 }
}
}));
class ColumnSelectDownCommand extends ColumnSelectCommand {
private readonly _isPaged: boolean;
constructor(opts: ICommandOptions & { isPaged: boolean }) {
super(opts);
this._isPaged = opts.isPaged;
}
protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: Partial<BaseCommandOptions>): IColumnSelectResult {
return ColumnSelection.columnSelectDown(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged);
}
}
export const CursorColumnSelectDown: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new ColumnSelectDownCommand({
isPaged: false,
id: 'cursorColumnSelectDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.DownArrow,
linux: { primary: 0 }
}
}));
export const CursorColumnSelectPageDown: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new ColumnSelectDownCommand({
isPaged: true,
id: 'cursorColumnSelectPageDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.PageDown,
linux: { primary: 0 }
}
}));
export class CursorMoveImpl extends CoreEditorCommand<CursorMove_.RawArguments> {
constructor() {
super({
id: 'cursorMove',
precondition: undefined,
metadata: CursorMove_.metadata
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions & CursorMove_.RawArguments>): void {
const parsed = CursorMove_.parse(args);
if (!parsed) {
return;
}
this._runCursorMove(viewModel, args.source, parsed);
}
private _runCursorMove(viewModel: IViewModel, source: string | null | undefined, args: CursorMove_.ParsedArguments): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
source,
CursorChangeReason.Explicit,
CursorMoveImpl._move(viewModel, viewModel.getCursorStates(), args)
);
viewModel.revealAllCursors(source, true);
}
private static _move(viewModel: IViewModel, cursors: CursorState[], args: CursorMove_.ParsedArguments): PartialCursorState[] | null {
const inSelectionMode = args.select;
const value = args.value;
switch (args.direction) {
case CursorMove_.Direction.Left:
case CursorMove_.Direction.Right:
case CursorMove_.Direction.Up:
case CursorMove_.Direction.Down:
case CursorMove_.Direction.PrevBlankLine:
case CursorMove_.Direction.NextBlankLine:
case CursorMove_.Direction.WrappedLineStart:
case CursorMove_.Direction.WrappedLineFirstNonWhitespaceCharacter:
case CursorMove_.Direction.WrappedLineColumnCenter:
case CursorMove_.Direction.WrappedLineEnd:
case CursorMove_.Direction.WrappedLineLastNonWhitespaceCharacter:
return CursorMoveCommands.simpleMove(viewModel, cursors, args.direction, inSelectionMode, value, args.unit);
case CursorMove_.Direction.ViewPortTop:
case CursorMove_.Direction.ViewPortBottom:
case CursorMove_.Direction.ViewPortCenter:
case CursorMove_.Direction.ViewPortIfOutside:
return CursorMoveCommands.viewportMove(viewModel, cursors, args.direction, inSelectionMode, value);
default:
return null;
}
}
}
export const CursorMove: CursorMoveImpl = registerEditorCommand(new CursorMoveImpl());
const enum Constants {
PAGE_SIZE_MARKER = -1
}
export interface CursorMoveCommandOptions extends BaseCommandOptions {
pageSize?: number;
}
class CursorMoveBasedCommand extends CoreEditorCommand<CursorMoveCommandOptions> {
private readonly _staticArgs: CursorMove_.SimpleMoveArguments;
constructor(opts: ICommandOptions & { args: CursorMove_.SimpleMoveArguments }) {
super(opts);
this._staticArgs = opts.args;
}
public runCoreEditorCommand(viewModel: IViewModel, dynamicArgs: Partial<CursorMoveCommandOptions>): void {
let args = this._staticArgs;
if (this._staticArgs.value === Constants.PAGE_SIZE_MARKER) {
args = {
direction: this._staticArgs.direction,
unit: this._staticArgs.unit,
select: this._staticArgs.select,
value: dynamicArgs.pageSize || viewModel.cursorConfig.pageSize
};
}
viewModel.model.pushStackElement();
viewModel.setCursorStates(
dynamicArgs.source,
CursorChangeReason.Explicit,
CursorMoveCommands.simpleMove(viewModel, viewModel.getCursorStates(), args.direction, args.select, args.value, args.unit)
);
viewModel.revealAllCursors(dynamicArgs.source, true);
}
}
export const CursorLeft: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Left,
unit: CursorMove_.Unit.None,
select: false,
value: 1
},
id: 'cursorLeft',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.LeftArrow,
mac: { primary: KeyCode.LeftArrow, secondary: [KeyMod.WinCtrl | KeyCode.KeyB] }
}
}));
export const CursorLeftSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Left,
unit: CursorMove_.Unit.None,
select: true,
value: 1
},
id: 'cursorLeftSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.LeftArrow
}
}));
export const CursorRight: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Right,
unit: CursorMove_.Unit.None,
select: false,
value: 1
},
id: 'cursorRight',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.RightArrow,
mac: { primary: KeyCode.RightArrow, secondary: [KeyMod.WinCtrl | KeyCode.KeyF] }
}
}));
export const CursorRightSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Right,
unit: CursorMove_.Unit.None,
select: true,
value: 1
},
id: 'cursorRightSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.RightArrow
}
}));
export const CursorUp: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Up,
unit: CursorMove_.Unit.WrappedLine,
select: false,
value: 1
},
id: 'cursorUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.UpArrow,
mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.WinCtrl | KeyCode.KeyP] }
}
}));
export const CursorUpSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Up,
unit: CursorMove_.Unit.WrappedLine,
select: true,
value: 1
},
id: 'cursorUpSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.UpArrow,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow],
mac: { primary: KeyMod.Shift | KeyCode.UpArrow },
linux: { primary: KeyMod.Shift | KeyCode.UpArrow }
}
}));
export const CursorPageUp: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Up,
unit: CursorMove_.Unit.WrappedLine,
select: false,
value: Constants.PAGE_SIZE_MARKER
},
id: 'cursorPageUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.PageUp
}
}));
export const CursorPageUpSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Up,
unit: CursorMove_.Unit.WrappedLine,
select: true,
value: Constants.PAGE_SIZE_MARKER
},
id: 'cursorPageUpSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.PageUp
}
}));
export const CursorDown: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Down,
unit: CursorMove_.Unit.WrappedLine,
select: false,
value: 1
},
id: 'cursorDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.DownArrow,
mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.WinCtrl | KeyCode.KeyN] }
}
}));
export const CursorDownSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Down,
unit: CursorMove_.Unit.WrappedLine,
select: true,
value: 1
},
id: 'cursorDownSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.DownArrow,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow],
mac: { primary: KeyMod.Shift | KeyCode.DownArrow },
linux: { primary: KeyMod.Shift | KeyCode.DownArrow }
}
}));
export const CursorPageDown: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Down,
unit: CursorMove_.Unit.WrappedLine,
select: false,
value: Constants.PAGE_SIZE_MARKER
},
id: 'cursorPageDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.PageDown
}
}));
export const CursorPageDownSelect: CoreEditorCommand<CursorMoveCommandOptions> = registerEditorCommand(new CursorMoveBasedCommand({
args: {
direction: CursorMove_.Direction.Down,
unit: CursorMove_.Unit.WrappedLine,
select: true,
value: Constants.PAGE_SIZE_MARKER
},
id: 'cursorPageDownSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.PageDown
}
}));
export interface CreateCursorCommandOptions extends MoveCommandOptions {
wholeLine?: boolean;
}
export const CreateCursor: CoreEditorCommand<CreateCursorCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<CreateCursorCommandOptions> {
constructor() {
super({
id: 'createCursor',
precondition: undefined
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<CreateCursorCommandOptions>): void {
if (!args.position) {
return;
}
let newState: PartialCursorState;
if (args.wholeLine) {
newState = CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition);
} else {
newState = CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition);
}
const states: PartialCursorState[] = viewModel.getCursorStates();
if (states.length > 1) {
const newModelPosition = (newState.modelState ? newState.modelState.position : null);
const newViewPosition = (newState.viewState ? newState.viewState.position : null);
for (let i = 0, len = states.length; i < len; i++) {
const state = states[i];
if (newModelPosition && !state.modelState!.selection.containsPosition(newModelPosition)) {
continue;
}
if (newViewPosition && !state.viewState!.selection.containsPosition(newViewPosition)) {
continue;
}
states.splice(i, 1);
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
states
);
return;
}
}
states.push(newState);
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
states
);
}
});
export const LastCursorMoveToSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<MoveCommandOptions> {
constructor() {
super({
id: '_lastCursorMoveToSelect',
precondition: undefined
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(viewModel, states[lastAddedCursorIndex], true, args.position, args.viewPosition);
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
);
}
});
class HomeCommand extends CoreEditorCommand<BaseCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
CursorMoveCommands.moveToBeginningOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
viewModel.revealAllCursors(args.source, true);
}
}
export const CursorHome: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new HomeCommand({
inSelectionMode: false,
id: 'cursorHome',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Home,
mac: { primary: KeyCode.Home, secondary: [KeyMod.CtrlCmd | KeyCode.LeftArrow] }
}
}));
export const CursorHomeSelect: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new HomeCommand({
inSelectionMode: true,
id: 'cursorHomeSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.Home,
mac: { primary: KeyMod.Shift | KeyCode.Home, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow] }
}
}));
class LineStartCommand extends CoreEditorCommand<BaseCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
this._exec(viewModel.getCursorStates())
);
viewModel.revealAllCursors(args.source, true);
}
private _exec(cursors: CursorState[]): PartialCursorState[] {
const result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const lineNumber = cursor.modelState.position.lineNumber;
result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, 1, 0));
}
return result;
}
}
export const CursorLineStart: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new LineStartCommand({
inSelectionMode: false,
id: 'cursorLineStart',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyCode.KeyA }
}
}));
export const CursorLineStartSelect: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new LineStartCommand({
inSelectionMode: true,
id: 'cursorLineStartSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyA }
}
}));
export interface EndCommandOptions extends BaseCommandOptions {
sticky?: boolean;
}
class EndCommand extends CoreEditorCommand<EndCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<EndCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
CursorMoveCommands.moveToEndOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode, args.sticky || false)
);
viewModel.revealAllCursors(args.source, true);
}
}
export const CursorEnd: CoreEditorCommand<EndCommandOptions> = registerEditorCommand(new EndCommand({
inSelectionMode: false,
id: 'cursorEnd',
precondition: undefined,
kbOpts: {
args: { sticky: false },
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.End,
mac: { primary: KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow] }
},
metadata: {
description: `Go to End`,
args: [{
name: 'args',
schema: {
type: 'object',
properties: {
'sticky': {
description: nls.localize('stickydesc', "Stick to the end even when going to longer lines"),
type: 'boolean',
default: false
}
}
}
}]
}
}));
export const CursorEndSelect: CoreEditorCommand<EndCommandOptions> = registerEditorCommand(new EndCommand({
inSelectionMode: true,
id: 'cursorEndSelect',
precondition: undefined,
kbOpts: {
args: { sticky: false },
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.End,
mac: { primary: KeyMod.Shift | KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow] }
},
metadata: {
description: `Select to End`,
args: [{
name: 'args',
schema: {
type: 'object',
properties: {
'sticky': {
description: nls.localize('stickydesc', "Stick to the end even when going to longer lines"),
type: 'boolean',
default: false
}
}
}
}]
}
}));
class LineEndCommand extends CoreEditorCommand<BaseCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
this._exec(viewModel, viewModel.getCursorStates())
);
viewModel.revealAllCursors(args.source, true);
}
private _exec(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {
const result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const lineNumber = cursor.modelState.position.lineNumber;
const maxColumn = viewModel.model.getLineMaxColumn(lineNumber);
result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, maxColumn, 0));
}
return result;
}
}
export const CursorLineEnd: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new LineEndCommand({
inSelectionMode: false,
id: 'cursorLineEnd',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyCode.KeyE }
}
}));
export const CursorLineEndSelect: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new LineEndCommand({
inSelectionMode: true,
id: 'cursorLineEndSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyE }
}
}));
class TopCommand extends CoreEditorCommand<BaseCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
CursorMoveCommands.moveToBeginningOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
viewModel.revealAllCursors(args.source, true);
}
}
export const CursorTop: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new TopCommand({
inSelectionMode: false,
id: 'cursorTop',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.Home,
mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }
}
}));
export const CursorTopSelect: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new TopCommand({
inSelectionMode: true,
id: 'cursorTopSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Home,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }
}
}));
class BottomCommand extends CoreEditorCommand<BaseCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
CursorMoveCommands.moveToEndOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
viewModel.revealAllCursors(args.source, true);
}
}
export const CursorBottom: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new BottomCommand({
inSelectionMode: false,
id: 'cursorBottom',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.End,
mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }
}
}));
export const CursorBottomSelect: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new BottomCommand({
inSelectionMode: true,
id: 'cursorBottomSelect',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.End,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }
}
}));
export type EditorScrollCommandOptions = EditorScroll_.RawArguments & BaseCommandOptions;
export class EditorScrollImpl extends CoreEditorCommand<EditorScrollCommandOptions> {
constructor() {
super({
id: 'editorScroll',
precondition: undefined,
metadata: EditorScroll_.metadata
});
}
determineScrollMethod(args: EditorScroll_.ParsedArguments) {
const horizontalUnits = [EditorScroll_.Unit.Column];
const verticalUnits = [
EditorScroll_.Unit.Line,
EditorScroll_.Unit.WrappedLine,
EditorScroll_.Unit.Page,
EditorScroll_.Unit.HalfPage,
EditorScroll_.Unit.Editor,
EditorScroll_.Unit.Column
];
const horizontalDirections = [EditorScroll_.Direction.Left, EditorScroll_.Direction.Right];
const verticalDirections = [EditorScroll_.Direction.Up, EditorScroll_.Direction.Down];
if (horizontalUnits.includes(args.unit) && horizontalDirections.includes(args.direction)) {
return this._runHorizontalEditorScroll.bind(this);
}
if (verticalUnits.includes(args.unit) && verticalDirections.includes(args.direction)) {
return this._runVerticalEditorScroll.bind(this);
}
return null;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<EditorScrollCommandOptions>): void {
const parsed = EditorScroll_.parse(args);
if (!parsed) {
return;
}
const runEditorScroll = this.determineScrollMethod(parsed);
if (!runEditorScroll) {
return;
}
runEditorScroll(viewModel, args.source, parsed);
}
_runVerticalEditorScroll(viewModel: IViewModel, source: string | null | undefined, args: EditorScroll_.ParsedArguments): void {
const desiredScrollTop = this._computeDesiredScrollTop(viewModel, args);
if (args.revealCursor) {
const desiredVisibleViewRange = viewModel.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop);
viewModel.setCursorStates(
source,
CursorChangeReason.Explicit,
[
CursorMoveCommands.findPositionInViewportIfOutside(viewModel, viewModel.getPrimaryCursorState(), desiredVisibleViewRange, args.select)
]
);
}
viewModel.viewLayout.setScrollPosition({ scrollTop: desiredScrollTop }, ScrollType.Smooth);
}
private _computeDesiredScrollTop(viewModel: IViewModel, args: EditorScroll_.ParsedArguments): number {
if (args.unit === EditorScroll_.Unit.Line) {
const futureViewport = viewModel.viewLayout.getFutureViewport();
const visibleViewRange = viewModel.getCompletelyVisibleViewRangeAtScrollTop(futureViewport.top);
const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
let desiredTopModelLineNumber: number;
if (args.direction === EditorScroll_.Direction.Up) {
desiredTopModelLineNumber = Math.max(1, visibleModelRange.startLineNumber - args.value);
} else {
desiredTopModelLineNumber = Math.min(viewModel.model.getLineCount(), visibleModelRange.startLineNumber + args.value);
}
const viewPosition = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1));
return viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
}
if (args.unit === EditorScroll_.Unit.Editor) {
let desiredTopModelLineNumber = 0;
if (args.direction === EditorScroll_.Direction.Down) {
desiredTopModelLineNumber = viewModel.model.getLineCount() - viewModel.cursorConfig.pageSize;
}
return viewModel.viewLayout.getVerticalOffsetForLineNumber(desiredTopModelLineNumber);
}
let noOfLines: number;
if (args.unit === EditorScroll_.Unit.Page) {
noOfLines = viewModel.cursorConfig.pageSize * args.value;
} else if (args.unit === EditorScroll_.Unit.HalfPage) {
noOfLines = Math.round(viewModel.cursorConfig.pageSize / 2) * args.value;
} else {
noOfLines = args.value;
}
const deltaLines = (args.direction === EditorScroll_.Direction.Up ? -1 : 1) * noOfLines;
return viewModel.viewLayout.getCurrentScrollTop() + deltaLines * viewModel.cursorConfig.lineHeight;
}
_runHorizontalEditorScroll(viewModel: IViewModel, source: string | null | undefined, args: EditorScroll_.ParsedArguments): void {
const desiredScrollLeft = this._computeDesiredScrollLeft(viewModel, args);
viewModel.viewLayout.setScrollPosition({ scrollLeft: desiredScrollLeft }, ScrollType.Smooth);
}
_computeDesiredScrollLeft(viewModel: IViewModel, args: EditorScroll_.ParsedArguments) {
const deltaColumns = (args.direction === EditorScroll_.Direction.Left ? -1 : 1) * args.value;
return viewModel.viewLayout.getCurrentScrollLeft() + deltaColumns * viewModel.cursorConfig.typicalHalfwidthCharacterWidth;
}
}
export const EditorScroll: EditorScrollImpl = registerEditorCommand(new EditorScrollImpl());
export const ScrollLineUp: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollLineUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.UpArrow,
mac: { primary: KeyMod.WinCtrl | KeyCode.PageUp }
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Up,
by: EditorScroll_.RawUnit.WrappedLine,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollPageUp: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollPageUp',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.PageUp,
win: { primary: KeyMod.Alt | KeyCode.PageUp },
linux: { primary: KeyMod.Alt | KeyCode.PageUp }
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Up,
by: EditorScroll_.RawUnit.Page,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollEditorTop: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollEditorTop',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Up,
by: EditorScroll_.RawUnit.Editor,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollLineDown: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollLineDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
mac: { primary: KeyMod.WinCtrl | KeyCode.PageDown }
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Down,
by: EditorScroll_.RawUnit.WrappedLine,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollPageDown: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollPageDown',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.PageDown,
win: { primary: KeyMod.Alt | KeyCode.PageDown },
linux: { primary: KeyMod.Alt | KeyCode.PageDown }
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Down,
by: EditorScroll_.RawUnit.Page,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollEditorBottom: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollEditorBottom',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Down,
by: EditorScroll_.RawUnit.Editor,
value: 1,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollLeft: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollLeft',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Left,
by: EditorScroll_.RawUnit.Column,
value: 2,
revealCursor: false,
select: false,
source: args.source
});
}
});
export const ScrollRight: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'scrollRight',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
}
});
}
runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
EditorScroll.runCoreEditorCommand(viewModel, {
to: EditorScroll_.RawDirection.Right,
by: EditorScroll_.RawUnit.Column,
value: 2,
revealCursor: false,
select: false,
source: args.source
});
}
});
class WordCommand extends CoreEditorCommand<MoveCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
CursorMoveCommands.word(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position)
]
);
if (args.revealType !== NavigationCommandRevealType.None) {
viewModel.revealAllCursors(args.source, true, true);
}
}
}
export const WordSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new WordCommand({
inSelectionMode: false,
id: '_wordSelect',
precondition: undefined
}));
export const WordSelectDrag: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new WordCommand({
inSelectionMode: true,
id: '_wordSelectDrag',
precondition: undefined
}));
export const LastCursorWordSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<MoveCommandOptions> {
constructor() {
super({
id: 'lastCursorWordSelect',
precondition: undefined
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
const lastAddedState = states[lastAddedCursorIndex];
newStates[lastAddedCursorIndex] = CursorMoveCommands.word(viewModel, lastAddedState, lastAddedState.modelState.hasSelection(), args.position);
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
);
}
});
class LineCommand extends CoreEditorCommand<MoveCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition)
]
);
if (args.revealType !== NavigationCommandRevealType.None) {
viewModel.revealAllCursors(args.source, false, true);
}
}
}
export const LineSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new LineCommand({
inSelectionMode: false,
id: '_lineSelect',
precondition: undefined
}));
export const LineSelectDrag: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new LineCommand({
inSelectionMode: true,
id: '_lineSelectDrag',
precondition: undefined
}));
class LastCursorLineCommand extends CoreEditorCommand<MoveCommandOptions> {
private readonly _inSelectionMode: boolean;
constructor(opts: ICommandOptions & { inSelectionMode: boolean }) {
super(opts);
this._inSelectionMode = opts.inSelectionMode;
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<MoveCommandOptions>): void {
if (!args.position) {
return;
}
const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
newStates[lastAddedCursorIndex] = CursorMoveCommands.line(viewModel, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition);
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
);
}
}
export const LastCursorLineSelect: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new LastCursorLineCommand({
inSelectionMode: false,
id: 'lastCursorLineSelect',
precondition: undefined
}));
export const LastCursorLineSelectDrag: CoreEditorCommand<MoveCommandOptions> = registerEditorCommand(new LastCursorLineCommand({
inSelectionMode: true,
id: 'lastCursorLineSelectDrag',
precondition: undefined
}));
export const CancelSelection: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'cancelSelection',
precondition: EditorContextKeys.hasNonEmptySelection,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
}
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
CursorMoveCommands.cancelSelection(viewModel, viewModel.getPrimaryCursorState())
]
);
viewModel.revealAllCursors(args.source, true);
}
});
export const RemoveSecondaryCursors: CoreEditorCommand<BaseCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<BaseCommandOptions> {
constructor() {
super({
id: 'removeSecondaryCursors',
precondition: EditorContextKeys.hasMultipleSelections,
kbOpts: {
weight: CORE_WEIGHT + 1,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
}
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<BaseCommandOptions>): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
viewModel.getPrimaryCursorState()
]
);
viewModel.revealAllCursors(args.source, true);
status(nls.localize('removedCursor', "Removed secondary cursors"));
}
});
export type RevealLineCommandOptions = RevealLine_.RawArguments & BaseCommandOptions;
export const RevealLine: CoreEditorCommand<RevealLineCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<RevealLineCommandOptions> {
constructor() {
super({
id: 'revealLine',
precondition: undefined,
metadata: RevealLine_.metadata
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<RevealLineCommandOptions>): void {
const revealLineArg = args;
const lineNumberArg = revealLineArg.lineNumber || 0;
let lineNumber = typeof lineNumberArg === 'number' ? (lineNumberArg + 1) : (parseInt(lineNumberArg) + 1);
if (lineNumber < 1) {
lineNumber = 1;
}
const lineCount = viewModel.model.getLineCount();
if (lineNumber > lineCount) {
lineNumber = lineCount;
}
const range = new Range(
lineNumber, 1,
lineNumber, viewModel.model.getLineMaxColumn(lineNumber)
);
let revealAt = VerticalRevealType.Simple;
if (revealLineArg.at) {
switch (revealLineArg.at) {
case RevealLine_.RawAtArgument.Top:
revealAt = VerticalRevealType.Top;
break;
case RevealLine_.RawAtArgument.Center:
revealAt = VerticalRevealType.Center;
break;
case RevealLine_.RawAtArgument.Bottom:
revealAt = VerticalRevealType.Bottom;
break;
default:
break;
}
}
const viewRange = viewModel.coordinatesConverter.convertModelRangeToViewRange(range);
viewModel.revealRange(args.source, false, viewRange, revealAt, ScrollType.Smooth);
}
});
export const SelectAll = new class extends EditorOrNativeTextInputCommand {
constructor() {
super(SelectAllCommand);
}
public runDOMCommand(activeElement: Element): void {
if (isFirefox) {
(<HTMLInputElement>activeElement).focus();
(<HTMLInputElement>activeElement).select();
}
activeElement.ownerDocument.execCommand('selectAll');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void {
const viewModel = editor._getViewModel();
if (!viewModel) {
return;
}
this.runCoreEditorCommand(viewModel, args);
}
public runCoreEditorCommand(viewModel: IViewModel, args: unknown): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
'keyboard',
CursorChangeReason.Explicit,
[
CursorMoveCommands.selectAll(viewModel, viewModel.getPrimaryCursorState())
]
);
}
}();
export interface SetSelectionCommandOptions extends BaseCommandOptions {
selection: ISelection;
}
export const SetSelection: CoreEditorCommand<SetSelectionCommandOptions> = registerEditorCommand(new class extends CoreEditorCommand<SetSelectionCommandOptions> {
constructor() {
super({
id: 'setSelection',
precondition: undefined
});
}
public runCoreEditorCommand(viewModel: IViewModel, args: Partial<SetSelectionCommandOptions>): void {
if (!args.selection) {
return;
}
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
CursorState.fromModelSelection(args.selection)
]
);
}
});
}
const columnSelectionCondition = ContextKeyExpr.and(
EditorContextKeys.textInputFocus,
EditorContextKeys.columnSelection
);
function registerColumnSelection(id: string, keybinding: number): void {
KeybindingsRegistry.registerKeybindingRule({
id: id,
primary: keybinding,
when: columnSelectionCondition,
weight: CORE_WEIGHT + 1
});
}
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectLeft.id, KeyMod.Shift | KeyCode.LeftArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectRight.id, KeyMod.Shift | KeyCode.RightArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectUp.id, KeyMod.Shift | KeyCode.UpArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyMod.Shift | KeyCode.PageUp);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown);
function registerCommand<T extends Command>(command: T): T {
command.register();
return command;
}
export namespace CoreEditingCommands {
export abstract class CoreEditingCommand extends EditorCommand {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void {
const viewModel = editor._getViewModel();
if (!viewModel) {
return;
}
this.runCoreEditingCommand(editor, viewModel, args || {});
}
public abstract runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void;
}
export const LineBreakInsert: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
constructor() {
super({
id: 'lineBreakInsert',
precondition: EditorContextKeys.writable,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyCode.KeyO }
}
});
}
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void {
editor.pushUndoStop();
editor.executeCommands(this.id, EnterOperation.lineBreakInsert(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
}
});
export const Outdent: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
constructor() {
super({
id: 'outdent',
precondition: EditorContextKeys.writable,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: ContextKeyExpr.and(
EditorContextKeys.editorTextFocus,
EditorContextKeys.tabDoesNotMoveFocus
),
primary: KeyMod.Shift | KeyCode.Tab
}
});
}
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void {
editor.pushUndoStop();
editor.executeCommands(this.id, TypeOperations.outdent(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
editor.pushUndoStop();
}
});
export const Tab: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
constructor() {
super({
id: 'tab',
precondition: EditorContextKeys.writable,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: ContextKeyExpr.and(
EditorContextKeys.editorTextFocus,
EditorContextKeys.tabDoesNotMoveFocus
),
primary: KeyCode.Tab
}
});
}
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void {
editor.pushUndoStop();
editor.executeCommands(this.id, TypeOperations.tab(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
editor.pushUndoStop();
}
});
export const DeleteLeft: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
constructor() {
super({
id: 'deleteLeft',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Backspace,
secondary: [KeyMod.Shift | KeyCode.Backspace],
mac: { primary: KeyCode.Backspace, secondary: [KeyMod.Shift | KeyCode.Backspace, KeyMod.WinCtrl | KeyCode.KeyH, KeyMod.WinCtrl | KeyCode.Backspace] }
}
});
}
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void {
const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection), viewModel.getCursorAutoClosedCharacters());
if (shouldPushStackElementBefore) {
editor.pushUndoStop();
}
editor.executeCommands(this.id, commands);
viewModel.setPrevEditOperationType(EditOperationType.DeletingLeft);
}
});
export const DeleteRight: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
constructor() {
super({
id: 'deleteRight',
precondition: undefined,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Delete,
mac: { primary: KeyCode.Delete, secondary: [KeyMod.WinCtrl | KeyCode.KeyD, KeyMod.WinCtrl | KeyCode.Delete] }
}
});
}
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: unknown): void {
const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection));
if (shouldPushStackElementBefore) {
editor.pushUndoStop();
}
editor.executeCommands(this.id, commands);
viewModel.setPrevEditOperationType(EditOperationType.DeletingRight);
}
});
export const Undo = new class extends EditorOrNativeTextInputCommand {
constructor() {
super(UndoCommand);
}
public runDOMCommand(activeElement: Element): void {
activeElement.ownerDocument.execCommand('undo');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void | Promise<void> {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
return editor.getModel().undo();
}
}();
export const Redo = new class extends EditorOrNativeTextInputCommand {
constructor() {
super(RedoCommand);
}
public runDOMCommand(activeElement: Element): void {
activeElement.ownerDocument.execCommand('redo');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void | Promise<void> {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
return editor.getModel().redo();
}
}();
}
class EditorHandlerCommand extends Command {
private readonly _handlerId: string;
constructor(id: string, handlerId: string, metadata?: ICommandMetadata) {
super({
id: id,
precondition: undefined,
metadata
});
this._handlerId = handlerId;
}
public runCommand(accessor: ServicesAccessor, args: unknown): void {
const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (!editor) {
return;
}
editor.trigger('keyboard', this._handlerId, args);
}
}
function registerOverwritableCommand(handlerId: string, metadata?: ICommandMetadata): void {
registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId));
registerCommand(new EditorHandlerCommand(handlerId, handlerId, metadata));
}
registerOverwritableCommand(Handler.Type, {
description: `Type`,
args: [{
name: 'args',
schema: {
'type': 'object',
'required': ['text'],
'properties': {
'text': {
'type': 'string'
}
},
}
}]
});
registerOverwritableCommand(Handler.ReplacePreviousChar);
registerOverwritableCommand(Handler.CompositionType);
registerOverwritableCommand(Handler.CompositionStart);
registerOverwritableCommand(Handler.CompositionEnd);
registerOverwritableCommand(Handler.Paste);
registerOverwritableCommand(Handler.Cut);