mirror of
https://github.com/VSCodeVim/Vim.git
synced 2024-09-19 16:18:45 +03:00
initial version
This commit is contained in:
parent
7fcd0195d8
commit
019cce67f1
@ -1,4 +1,4 @@
|
|||||||
import { VimState } from './../mode/modeHandler';
|
import { VimState, RecordedState } from './../mode/modeHandler';
|
||||||
import { SearchState, SearchDirection } from './../state/searchState';
|
import { SearchState, SearchDirection } from './../state/searchState';
|
||||||
import { ReplaceState } from './../state/replaceState';
|
import { ReplaceState } from './../state/replaceState';
|
||||||
import { VisualBlockMode } from './../mode/modeVisualBlock';
|
import { VisualBlockMode } from './../mode/modeVisualBlock';
|
||||||
@ -614,6 +614,9 @@ class CommandInsertRegisterContent extends BaseCommand {
|
|||||||
|
|
||||||
if (register.text instanceof Array) {
|
if (register.text instanceof Array) {
|
||||||
text = (register.text as string []).join("\n");
|
text = (register.text as string []).join("\n");
|
||||||
|
} else if (register.text instanceof RecordedState) {
|
||||||
|
// TODO: Play macro
|
||||||
|
return vimState;
|
||||||
} else {
|
} else {
|
||||||
text = register.text;
|
text = register.text;
|
||||||
}
|
}
|
||||||
@ -644,6 +647,80 @@ class CommandInsertRegisterContent extends BaseCommand {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RegisterAction
|
||||||
|
class CommandRecordMacro extends BaseCommand {
|
||||||
|
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
|
||||||
|
keys = ["q", "<character>"];
|
||||||
|
|
||||||
|
public async exec(position: Position, vimState: VimState): Promise<VimState> {
|
||||||
|
const register = this.keysPressed[1];
|
||||||
|
vimState.recordedMacro.registerName = register;
|
||||||
|
Register.putByKey(new RecordedState(), register);
|
||||||
|
vimState.isRecordingMacro = true;
|
||||||
|
return vimState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public doesActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
const register = this.keysPressed[1];
|
||||||
|
|
||||||
|
return super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
public couldActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
const register = this.keysPressed[1];
|
||||||
|
|
||||||
|
return super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegisterAction
|
||||||
|
export class CommandQuitRecordMacro extends BaseCommand {
|
||||||
|
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
|
||||||
|
keys = ["q"];
|
||||||
|
|
||||||
|
public async exec(position: Position, vimState: VimState): Promise<VimState> {
|
||||||
|
Register.putByKey(vimState.recordedMacro, vimState.recordedMacro.registerName);
|
||||||
|
vimState.isRecordingMacro = false;
|
||||||
|
return vimState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public doesActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
return super.doesActionApply(vimState, keysPressed) && vimState.isRecordingMacro;
|
||||||
|
}
|
||||||
|
|
||||||
|
public couldActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
return super.couldActionApply(vimState, keysPressed) && vimState.isRecordingMacro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegisterAction
|
||||||
|
class CommandExecuteMacro extends BaseCommand {
|
||||||
|
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
|
||||||
|
keys = ["@", "<character>"];
|
||||||
|
|
||||||
|
public async exec(position: Position, vimState: VimState): Promise<VimState> {
|
||||||
|
const register = this.keysPressed[1];
|
||||||
|
vimState.recordedState.transformations.push({
|
||||||
|
type: "macro",
|
||||||
|
register: register
|
||||||
|
});
|
||||||
|
|
||||||
|
return vimState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public doesActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
const register = keysPressed[1];
|
||||||
|
|
||||||
|
return super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
public couldActionApply(vimState: VimState, keysPressed: string[]): boolean {
|
||||||
|
const register = keysPressed[1];
|
||||||
|
|
||||||
|
return super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RegisterAction
|
@RegisterAction
|
||||||
class CommandEsc extends BaseCommand {
|
class CommandEsc extends BaseCommand {
|
||||||
modes = [
|
modes = [
|
||||||
@ -812,7 +889,8 @@ export class CommandInsertPreviousText extends BaseCommand {
|
|||||||
keys = ["<C-a>"];
|
keys = ["<C-a>"];
|
||||||
|
|
||||||
public async exec(position: Position, vimState: VimState): Promise<VimState> {
|
public async exec(position: Position, vimState: VimState): Promise<VimState> {
|
||||||
let actions = Register.lastContentChange.actionsRun.slice(0);
|
let actions = ((await Register.getByKey('.')).text as RecordedState).actionsRun.slice(0);
|
||||||
|
// let actions = Register.lastContentChange.actionsRun.slice(0);
|
||||||
// The first action is entering Insert Mode, which is not necessary in this case
|
// The first action is entering Insert Mode, which is not necessary in this case
|
||||||
actions.shift();
|
actions.shift();
|
||||||
// The last action is leaving Insert Mode, which is not necessary in this case
|
// The last action is leaving Insert Mode, which is not necessary in this case
|
||||||
@ -1733,7 +1811,10 @@ export class PutCommand extends BaseCommand {
|
|||||||
const register = await Register.get(vimState);
|
const register = await Register.get(vimState);
|
||||||
const dest = after ? position : position.getRight();
|
const dest = after ? position : position.getRight();
|
||||||
|
|
||||||
if (typeof register.text === "object") {
|
if (register.text instanceof RecordedState) {
|
||||||
|
// TODO:w
|
||||||
|
return vimState;
|
||||||
|
} else if (typeof register.text === "object") {
|
||||||
return await this.execVisualBlockPaste(register.text, position, vimState, after);
|
return await this.execVisualBlockPaste(register.text, position, vimState, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1883,6 +1964,10 @@ export class GPutCommand extends BaseCommand {
|
|||||||
const register = await Register.get(vimState);
|
const register = await Register.get(vimState);
|
||||||
let addedLinesCount: number;
|
let addedLinesCount: number;
|
||||||
|
|
||||||
|
if (register.text instanceof RecordedState) {
|
||||||
|
// TODO: run register recordedState
|
||||||
|
return vimState;
|
||||||
|
}
|
||||||
if (typeof register.text === "object") { // visual block mode
|
if (typeof register.text === "object") { // visual block mode
|
||||||
addedLinesCount = register.text.length * vimState.recordedState.count;
|
addedLinesCount = register.text.length * vimState.recordedState.count;
|
||||||
} else {
|
} else {
|
||||||
@ -2027,7 +2112,10 @@ export class GPutBeforeCommand extends BaseCommand {
|
|||||||
const register = await Register.get(vimState);
|
const register = await Register.get(vimState);
|
||||||
let addedLinesCount: number;
|
let addedLinesCount: number;
|
||||||
|
|
||||||
if (typeof register.text === "object") { // visual block mode
|
if (register.text instanceof RecordedState) {
|
||||||
|
// TODO;
|
||||||
|
return vimState;
|
||||||
|
} else if (typeof register.text === "object") { // visual block mode
|
||||||
addedLinesCount = register.text.length * vimState.recordedState.count;
|
addedLinesCount = register.text.length * vimState.recordedState.count;
|
||||||
} else {
|
} else {
|
||||||
addedLinesCount = register.text.split('\n').length;
|
addedLinesCount = register.text.split('\n').length;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import * as node from "../node";
|
import * as node from "../node";
|
||||||
import {ModeHandler} from "../../mode/modeHandler";
|
import {ModeHandler, RecordedState} from "../../mode/modeHandler";
|
||||||
import { Register} from '../../register/register';
|
import { Register} from '../../register/register';
|
||||||
|
|
||||||
export interface IRegisterCommandArguments extends node.ICommandArgs {
|
export interface IRegisterCommandArguments extends node.ICommandArgs {
|
||||||
@ -26,6 +26,8 @@ export class RegisterCommand extends node.CommandBase {
|
|||||||
let result = (await Register.getByKey(register)).text;
|
let result = (await Register.getByKey(register)).text;
|
||||||
if (result instanceof Array) {
|
if (result instanceof Array) {
|
||||||
result = result.join("\n").substr(0, 100);
|
result = result.join("\n").substr(0, 100);
|
||||||
|
} else if (result instanceof RecordedState) {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -25,7 +25,7 @@ import { VisualLineMode } from './modeVisualLine';
|
|||||||
import { HistoryTracker } from './../history/historyTracker';
|
import { HistoryTracker } from './../history/historyTracker';
|
||||||
import {
|
import {
|
||||||
BaseMovement, BaseCommand, Actions, BaseAction,
|
BaseMovement, BaseCommand, Actions, BaseAction,
|
||||||
BaseOperator, DocumentContentChangeAction, CommandInsertInInsertMode, CommandInsertPreviousText,
|
BaseOperator, DocumentContentChangeAction, CommandInsertInInsertMode, CommandInsertPreviousText, CommandQuitRecordMacro,
|
||||||
isIMovement, KeypressState } from './../actions/actions';
|
isIMovement, KeypressState } from './../actions/actions';
|
||||||
import { Position, PositionDiff } from './../motion/position';
|
import { Position, PositionDiff } from './../motion/position';
|
||||||
import { Range } from './../motion/range';
|
import { Range } from './../motion/range';
|
||||||
@ -158,6 +158,8 @@ export class VimState {
|
|||||||
|
|
||||||
public searchStatePrevious: SearchState | undefined = undefined;
|
public searchStatePrevious: SearchState | undefined = undefined;
|
||||||
|
|
||||||
|
public isRecordingMacro: boolean = false;
|
||||||
|
|
||||||
public replaceState: ReplaceState | undefined = undefined;
|
public replaceState: ReplaceState | undefined = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,6 +215,8 @@ export class VimState {
|
|||||||
|
|
||||||
public recordedState = new RecordedState();
|
public recordedState = new RecordedState();
|
||||||
|
|
||||||
|
public recordedMacro = new RecordedState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Programmatically triggering an edit will unfortunately ALSO trigger our mouse update
|
* Programmatically triggering an edit will unfortunately ALSO trigger our mouse update
|
||||||
* function. We use this variable to determine if the update function was triggered
|
* function. We use this variable to determine if the update function was triggered
|
||||||
@ -662,6 +666,7 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let action = result as BaseAction;
|
let action = result as BaseAction;
|
||||||
|
let actionToRecord: BaseAction | undefined = action;
|
||||||
|
|
||||||
if (recordedState.actionsRun.length === 0) {
|
if (recordedState.actionsRun.length === 0) {
|
||||||
recordedState.actionsRun.push(action);
|
recordedState.actionsRun.push(action);
|
||||||
@ -670,7 +675,8 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
|
|
||||||
if (lastAction instanceof DocumentContentChangeAction) {
|
if (lastAction instanceof DocumentContentChangeAction) {
|
||||||
if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) {
|
if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) {
|
||||||
// does nothing
|
// delay the macro recording
|
||||||
|
actionToRecord = undefined;
|
||||||
} else {
|
} else {
|
||||||
// Push real document content change to the stack
|
// Push real document content change to the stack
|
||||||
lastAction.contentChanges = lastAction.contentChanges.concat(vimState.historyTracker.currentContentChanges);
|
lastAction.contentChanges = lastAction.contentChanges.concat(vimState.historyTracker.currentContentChanges);
|
||||||
@ -681,13 +687,19 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) {
|
if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) {
|
||||||
// This means we are already in Insert Mode but there is still not DocumentContentChangeAction in stack
|
// This means we are already in Insert Mode but there is still not DocumentContentChangeAction in stack
|
||||||
vimState.historyTracker.currentContentChanges = [];
|
vimState.historyTracker.currentContentChanges = [];
|
||||||
recordedState.actionsRun.push(new DocumentContentChangeAction());
|
let newContentChange = new DocumentContentChangeAction();
|
||||||
|
recordedState.actionsRun.push(newContentChange);
|
||||||
|
actionToRecord = newContentChange;
|
||||||
} else {
|
} else {
|
||||||
recordedState.actionsRun.push(action);
|
recordedState.actionsRun.push(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vimState.isRecordingMacro && actionToRecord && !(actionToRecord instanceof CommandQuitRecordMacro)) {
|
||||||
|
vimState.recordedMacro.actionsRun.push(actionToRecord);
|
||||||
|
}
|
||||||
|
|
||||||
vimState = await this.runAction(vimState, recordedState, action);
|
vimState = await this.runAction(vimState, recordedState, action);
|
||||||
|
|
||||||
if (vimState.currentMode === ModeName.Insert) {
|
if (vimState.currentMode === ModeName.Insert) {
|
||||||
@ -785,7 +797,8 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
vimState.previousFullAction = vimState.recordedState;
|
vimState.previousFullAction = vimState.recordedState;
|
||||||
|
|
||||||
if (recordedState.isInsertion) {
|
if (recordedState.isInsertion) {
|
||||||
Register.lastContentChange = recordedState;
|
Register.putByKey(recordedState, ".");
|
||||||
|
// Register.lastContentChange = recordedState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1164,6 +1177,9 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
|
|
||||||
vimState.previousFullAction = clonedAction;
|
vimState.previousFullAction = clonedAction;
|
||||||
break;
|
break;
|
||||||
|
case "macro":
|
||||||
|
vimState = await this.runMacro(vimState, vimState.recordedMacro);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1263,6 +1279,23 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
return vimState;
|
return vimState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runMacro(vimState: VimState, recordedMacro: RecordedState): Promise<VimState> {
|
||||||
|
const actions = recordedMacro.actionsRun.slice(0);
|
||||||
|
let recordedState = new RecordedState();
|
||||||
|
vimState.recordedState = recordedState;
|
||||||
|
vimState.isRunningDotCommand = true;
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
for (let action of actions) {
|
||||||
|
recordedState.actionsRun = actions.slice(0, ++i);
|
||||||
|
vimState = await this.runAction(vimState, recordedState, action);
|
||||||
|
|
||||||
|
await this.updateView(vimState, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
vimState.isRunningDotCommand = false;
|
||||||
|
return vimState;
|
||||||
|
}
|
||||||
public async updateView(vimState: VimState, drawSelection = true): Promise<void> {
|
public async updateView(vimState: VimState, drawSelection = true): Promise<void> {
|
||||||
// Draw selection (or cursor)
|
// Draw selection (or cursor)
|
||||||
|
|
||||||
@ -1426,7 +1459,9 @@ export class ModeHandler implements vscode.Disposable {
|
|||||||
if (this.currentMode.name === ModeName.SearchInProgressMode) {
|
if (this.currentMode.name === ModeName.SearchInProgressMode) {
|
||||||
this.setupStatusBarItem(`Searching for: ${ this.vimState.searchState!.searchString }`);
|
this.setupStatusBarItem(`Searching for: ${ this.vimState.searchState!.searchString }`);
|
||||||
} else {
|
} else {
|
||||||
this.setupStatusBarItem(`-- ${ this.currentMode.text.toUpperCase() } ${ this._vimState.isMultiCursor ? 'MULTI CURSOR' : '' } --`);
|
this.setupStatusBarItem(
|
||||||
|
`-- ${ this.currentMode.text.toUpperCase() } ${ this._vimState.isMultiCursor ? 'MULTI CURSOR' : '' } -- ` +
|
||||||
|
`${this._vimState.isRecordingMacro ? 'Recording' : ''}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
vscode.commands.executeCommand('setContext', 'vim.mode', this.currentMode.text);
|
vscode.commands.executeCommand('setContext', 'vim.mode', this.currentMode.text);
|
||||||
|
@ -15,7 +15,7 @@ export enum RegisterMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface IRegisterContent {
|
export interface IRegisterContent {
|
||||||
text : string | string[];
|
text : string | string[] | RecordedState;
|
||||||
registerMode : RegisterMode;
|
registerMode : RegisterMode;
|
||||||
isClipboardRegister: boolean;
|
isClipboardRegister: boolean;
|
||||||
}
|
}
|
||||||
@ -42,10 +42,17 @@ export class Register {
|
|||||||
return register && register.isClipboardRegister;
|
return register && register.isClipboardRegister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ". readonly register: last content change.
|
||||||
|
*/
|
||||||
public static lastContentChange: RecordedState;
|
public static lastContentChange: RecordedState;
|
||||||
|
|
||||||
public static isValidRegister(register: string): boolean {
|
public static isValidRegister(register: string): boolean {
|
||||||
return register in Register.registers || /^[a-z0-9]+$/i.test(register);
|
return register in Register.registers || /^[a-z0-9]+$/i.test(register) || /\./.test(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isValidRegisterForMacro(register: string): boolean {
|
||||||
|
return /^[a-z]+$/i.test(register);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,7 +77,8 @@ export class Register {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static putByKey(content: string | string[], register = '"', registerMode = RegisterMode.FigureItOutFromCurrentMode): void {
|
public static putByKey(content: string | string[] | RecordedState, register = '"',
|
||||||
|
registerMode = RegisterMode.FigureItOutFromCurrentMode): void {
|
||||||
if (!Register.isValidRegister(register)) {
|
if (!Register.isValidRegister(register)) {
|
||||||
throw new Error(`Invalid register ${register}`);
|
throw new Error(`Invalid register ${register}`);
|
||||||
}
|
}
|
||||||
|
@ -187,6 +187,14 @@ export interface Dot {
|
|||||||
type: "dot";
|
type: "dot";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents macro
|
||||||
|
*/
|
||||||
|
export interface Macro {
|
||||||
|
type: "macro";
|
||||||
|
register: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type Transformation
|
export type Transformation
|
||||||
= InsertTextTransformation
|
= InsertTextTransformation
|
||||||
| InsertTextVSCodeTransformation
|
| InsertTextVSCodeTransformation
|
||||||
@ -196,6 +204,7 @@ export type Transformation
|
|||||||
| MoveCursorTransformation
|
| MoveCursorTransformation
|
||||||
| ShowCommandLine
|
| ShowCommandLine
|
||||||
| Dot
|
| Dot
|
||||||
|
| Macro
|
||||||
| DeleteTextTransformation;
|
| DeleteTextTransformation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user