mirror of
https://github.com/VSCodeVim/Vim.git
synced 2024-11-09 13:34:29 +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 { 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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user