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

View File

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

View File

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

View File

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

View File

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