initial version

This commit is contained in:
rebornix 2016-10-11 16:55:11 -07:00 committed by Peng Lyu
parent 7fcd0195d8
commit 019cce67f1
5 changed files with 155 additions and 13 deletions

View File

@ -1,4 +1,4 @@
import { VimState } from './../mode/modeHandler';
import { VimState, RecordedState } from './../mode/modeHandler';
import { SearchState, SearchDirection } from './../state/searchState';
import { ReplaceState } from './../state/replaceState';
import { VisualBlockMode } from './../mode/modeVisualBlock';
@ -614,6 +614,9 @@ class CommandInsertRegisterContent extends BaseCommand {
if (register.text instanceof Array) {
text = (register.text as string []).join("\n");
} else if (register.text instanceof RecordedState) {
// TODO: Play macro
return vimState;
} else {
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
class CommandEsc extends BaseCommand {
modes = [
@ -812,7 +889,8 @@ export class CommandInsertPreviousText extends BaseCommand {
keys = ["<C-a>"];
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
actions.shift();
// 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 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);
}
@ -1883,6 +1964,10 @@ export class GPutCommand extends BaseCommand {
const register = await Register.get(vimState);
let addedLinesCount: number;
if (register.text instanceof RecordedState) {
// TODO: run register recordedState
return vimState;
}
if (typeof register.text === "object") { // visual block mode
addedLinesCount = register.text.length * vimState.recordedState.count;
} else {
@ -2027,7 +2112,10 @@ export class GPutBeforeCommand extends BaseCommand {
const register = await Register.get(vimState);
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;
} else {
addedLinesCount = register.text.split('\n').length;

View File

@ -2,7 +2,7 @@
import * as vscode from "vscode";
import * as node from "../node";
import {ModeHandler} from "../../mode/modeHandler";
import {ModeHandler, RecordedState} from "../../mode/modeHandler";
import { Register} from '../../register/register';
export interface IRegisterCommandArguments extends node.ICommandArgs {
@ -26,6 +26,8 @@ export class RegisterCommand extends node.CommandBase {
let result = (await Register.getByKey(register)).text;
if (result instanceof Array) {
result = result.join("\n").substr(0, 100);
} else if (result instanceof RecordedState) {
// TODO
}
return result;

View File

@ -25,7 +25,7 @@ import { VisualLineMode } from './modeVisualLine';
import { HistoryTracker } from './../history/historyTracker';
import {
BaseMovement, BaseCommand, Actions, BaseAction,
BaseOperator, DocumentContentChangeAction, CommandInsertInInsertMode, CommandInsertPreviousText,
BaseOperator, DocumentContentChangeAction, CommandInsertInInsertMode, CommandInsertPreviousText, CommandQuitRecordMacro,
isIMovement, KeypressState } from './../actions/actions';
import { Position, PositionDiff } from './../motion/position';
import { Range } from './../motion/range';
@ -158,6 +158,8 @@ export class VimState {
public searchStatePrevious: SearchState | undefined = undefined;
public isRecordingMacro: boolean = false;
public replaceState: ReplaceState | undefined = undefined;
/**
@ -213,6 +215,8 @@ export class VimState {
public recordedState = new RecordedState();
public recordedMacro = new RecordedState();
/**
* Programmatically triggering an edit will unfortunately ALSO trigger our mouse update
* 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 actionToRecord: BaseAction | undefined = action;
if (recordedState.actionsRun.length === 0) {
recordedState.actionsRun.push(action);
@ -670,7 +675,8 @@ export class ModeHandler implements vscode.Disposable {
if (lastAction instanceof DocumentContentChangeAction) {
if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) {
// does nothing
// delay the macro recording
actionToRecord = undefined;
} else {
// Push real document content change to the stack
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) {
// This means we are already in Insert Mode but there is still not DocumentContentChangeAction in stack
vimState.historyTracker.currentContentChanges = [];
recordedState.actionsRun.push(new DocumentContentChangeAction());
let newContentChange = new DocumentContentChangeAction();
recordedState.actionsRun.push(newContentChange);
actionToRecord = newContentChange;
} else {
recordedState.actionsRun.push(action);
}
}
}
if (vimState.isRecordingMacro && actionToRecord && !(actionToRecord instanceof CommandQuitRecordMacro)) {
vimState.recordedMacro.actionsRun.push(actionToRecord);
}
vimState = await this.runAction(vimState, recordedState, action);
if (vimState.currentMode === ModeName.Insert) {
@ -785,7 +797,8 @@ export class ModeHandler implements vscode.Disposable {
vimState.previousFullAction = vimState.recordedState;
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;
break;
case "macro":
vimState = await this.runMacro(vimState, vimState.recordedMacro);
break;
}
}
@ -1263,6 +1279,23 @@ export class ModeHandler implements vscode.Disposable {
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> {
// Draw selection (or cursor)
@ -1426,7 +1459,9 @@ export class ModeHandler implements vscode.Disposable {
if (this.currentMode.name === ModeName.SearchInProgressMode) {
this.setupStatusBarItem(`Searching for: ${ this.vimState.searchState!.searchString }`);
} 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);

View File

@ -15,7 +15,7 @@ export enum RegisterMode {
};
export interface IRegisterContent {
text : string | string[];
text : string | string[] | RecordedState;
registerMode : RegisterMode;
isClipboardRegister: boolean;
}
@ -42,10 +42,17 @@ export class Register {
return register && register.isClipboardRegister;
}
/**
* ". readonly register: last content change.
*/
public static lastContentChange: RecordedState;
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)) {
throw new Error(`Invalid register ${register}`);
}

View File

@ -187,6 +187,14 @@ export interface Dot {
type: "dot";
}
/**
* Represents macro
*/
export interface Macro {
type: "macro";
register: string;
}
export type Transformation
= InsertTextTransformation
| InsertTextVSCodeTransformation
@ -196,6 +204,7 @@ export type Transformation
| MoveCursorTransformation
| ShowCommandLine
| Dot
| Macro
| DeleteTextTransformation;
/**