motion and insert commands.

This commit is contained in:
rebornix 2017-05-04 11:24:00 -07:00 committed by Peng Lyu
parent e9fc7c5dab
commit 112fd58424
8 changed files with 2208 additions and 2171 deletions

View File

@ -149,11 +149,12 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidChangeConfiguration((e: void) => {
Configuration.updateConfiguration();
/* tslint:disable:forin */
// Update the remappers foreach modehandler
for (let mh in modeHandlerToEditorIdentity) {
modeHandlerToEditorIdentity[mh].createRemappers();
}
})
});
vscode.window.onDidChangeActiveTextEditor(handleActiveEditorChange, this);
@ -177,7 +178,7 @@ export async function activate(context: vscode.ExtensionContext) {
modeHandler.vimState.historyTracker.currentContentChanges =
modeHandler.vimState.historyTracker.currentContentChanges.concat(event.contentChanges);
}
}
};
if (Globals.isTesting) {
contentChangeHandler(Globals.modeHandlerForTesting as ModeHandler);
@ -216,7 +217,8 @@ export async function activate(context: vscode.ExtensionContext) {
const mh = await getAndUpdateModeHandler();
if (compositionState.isInComposition) {
compositionState.composingText = compositionState.composingText.substr(0, compositionState.composingText.length - args.replaceCharCnt) + args.text;
compositionState.composingText = compositionState.composingText
.substr(0, compositionState.composingText.length - args.replaceCharCnt) + args.text;
} else {
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: args.text,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,439 @@
import * as vscode from 'vscode';
import { RecordedState, VimState } from './../mode/modeHandler';
import { Register, RegisterMode } from './../register/register';
import { Position, PositionDiff } from './../motion/position';
import { Range } from './../motion/range';
import { ModeName } from './../mode/mode';
import { Configuration } from './../configuration/configuration';
import { TextEditor } from './../textEditor';
import {
ArrowsInInsertMode
} from './motion';
import {
RegisterAction, BaseCommand, DocumentContentChangeAction, CommandInsertAtCursor, CommandInsertAfterCursor
} from './actions';
@RegisterAction
class CommandEscInsertMode extends BaseCommand {
modes = [
ModeName.Insert
];
keys = [
["<Esc>"],
["<C-c>"],
["<C-[>"],
];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.cursorPosition = position.getLeft();
// only remove leading spaces inserted by vscode.
// vscode only inserts them when user enter a new line,
// ie, o/O in Normal mode or \n in Insert mode.
const lastActionBeforeEsc = vimState.keyHistory[vimState.keyHistory.length - 2];
if (['o', 'O', '\n'].indexOf(lastActionBeforeEsc) > -1 &&
vimState.editor.document.languageId !== 'plaintext' &&
/^\s+$/.test(TextEditor.getLineAt(position).text)) {
vimState.recordedState.transformations.push({
type: "deleteRange",
range: new Range(position.getLineBegin(), position.getLineEnd())
});
vimState.cursorPosition = position.getLineBegin();
}
vimState.currentMode = ModeName.Normal;
// If we wanted to repeat this insert (only for i and a), now is the time to do it. Insert
// count amount of these strings before returning back to normal mode
const typeOfInsert = vimState.recordedState.actionsRun[vimState.recordedState.actionsRun.length - 3];
if (vimState.recordedState.count > 1 &&
(typeOfInsert instanceof CommandInsertAtCursor || typeOfInsert instanceof CommandInsertAfterCursor)) {
const changeAction = vimState.recordedState.actionsRun[vimState.recordedState.actionsRun.length - 2] as DocumentContentChangeAction;
const changesArray = changeAction.contentChanges;
let docChanges: vscode.TextDocumentContentChangeEvent[] = [];
for (let i = 0; i < changesArray.length; i++) {
docChanges.push(changesArray[i].textDiff);
}
let positionDiff = new PositionDiff(0, 0);
// Add count amount of inserts in the case of 4i=<esc>
for (let i = 0; i < (vimState.recordedState.count - 1); i++) {
// If this is the last transform, move cursor back one character
if (i === (vimState.recordedState.count - 2)) {
positionDiff = new PositionDiff(0, -1);
}
// Add a transform containing the change
vimState.recordedState.transformations.push({
type: "contentChange",
changes: docChanges,
diff: positionDiff
});
}
}
if (vimState.historyTracker.currentContentChanges.length > 0) {
vimState.historyTracker.lastContentChanges = vimState.historyTracker.currentContentChanges;
vimState.historyTracker.currentContentChanges = [];
}
return vimState;
}
}
@RegisterAction
export class CommandInsertPreviousText extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-a>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
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
// actions.pop();
if (actions.length > 0 && actions[0] instanceof ArrowsInInsertMode) {
// Note, arrow keys are the only Insert action command that can't be repeated here as far as @rebornix knows.
actions.shift();
}
for (let action of actions) {
if (action instanceof BaseCommand) {
vimState = await action.execCount(vimState.cursorPosition, vimState);
}
if (action instanceof DocumentContentChangeAction) {
vimState = await action.exec(vimState.cursorPosition, vimState);
}
}
vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.end);
vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
vimState.currentMode = ModeName.Insert;
return vimState;
}
}
@RegisterAction
class CommandInsertPreviousTextAndQuit extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-shift+2>"]; // <C-@>
public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState = await new CommandInsertPreviousText().exec(position, vimState);
vimState.currentMode = ModeName.Normal;
return vimState;
}
}
@RegisterAction
class CommandInsertBelowChar extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-e>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
if (TextEditor.isLastLine(position)) {
return vimState;
}
const charBelowCursorPosition = position.getDownByCount(1);
if (charBelowCursorPosition.isLineEnd()) {
return vimState;
}
const char = TextEditor.getText(new vscode.Range(charBelowCursorPosition, charBelowCursorPosition.getRight()));
await TextEditor.insert(char, position);
vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
return vimState;
}
}
@RegisterAction
class CommandInsertIndentInCurrentLine extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-t>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
const originalText = TextEditor.getLineAt(position).text;
const indentationWidth = TextEditor.getIndentationLevel(originalText);
const tabSize = Configuration.tabstop || Number(vimState.editor.options.tabSize);
const newIndentationWidth = (indentationWidth / tabSize + 1) * tabSize;
TextEditor.replaceText(
vimState, TextEditor.setIndentationLevel(originalText, newIndentationWidth),
position.getLineBegin(), position.getLineEnd(),
new PositionDiff(0, newIndentationWidth - indentationWidth)
);
return vimState;
}
}
@RegisterAction
export class CommandInsertInInsertMode extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<character>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
const char = this.keysPressed[this.keysPressed.length - 1];
const line = TextEditor.getLineAt(position).text;
if (char === "<BS>") {
const selection = TextEditor.getSelection();
// Check if a selection is active
if (!selection.isEmpty) {
vimState.recordedState.transformations.push({
type: "deleteRange",
range: new Range(selection.start as Position, selection.end as Position),
});
} else {
if (line.length > 0 && line.match(/^\s+$/) && Configuration.expandtab) {
// If the line is empty except whitespace, backspace should return to
// the next lowest level of indentation.
const tabSize = vimState.editor.options.tabSize as number;
const desiredLineLength = Math.floor((position.character - 1) / tabSize) * tabSize;
vimState.recordedState.transformations.push({
type: "deleteRange",
range: new Range(new Position(position.line, desiredLineLength), new Position(position.line, line.length))
});
} else {
if (position.line !== 0 || position.character !== 0) {
vimState.recordedState.transformations.push({
type: "deleteText",
position: position,
});
}
}
}
vimState.cursorPosition = vimState.cursorPosition.getLeft();
vimState.cursorStartPosition = vimState.cursorStartPosition.getLeft();
} else {
if (vimState.isMultiCursor) {
vimState.recordedState.transformations.push({
type: "insertText",
text: char,
position: vimState.cursorPosition,
});
} else {
vimState.recordedState.transformations.push({
type: "insertTextVSCode",
text: char,
});
}
}
return vimState;
}
public toString(): string {
return this.keysPressed[this.keysPressed.length - 1];
}
}
@RegisterAction
class CommandInsertRegisterContent extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-r>", "<character>"];
isCompleteAction = false;
public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.recordedState.registerName = this.keysPressed[1];
const register = await Register.get(vimState);
let text: string;
if (register.text instanceof Array) {
text = (register.text as string[]).join("\n");
} else if (register.text instanceof RecordedState) {
vimState.recordedState.transformations.push({
type: "macro",
register: vimState.recordedState.registerName,
replay: "keystrokes"
});
return vimState;
} else {
text = register.text;
}
if (register.registerMode === RegisterMode.LineWise) {
text += "\n";
}
await TextEditor.insertAt(text, position);
vimState.currentMode = ModeName.Insert;
vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
return vimState;
}
public doesActionApply(vimState: VimState, keysPressed: string[]): boolean {
const register = keysPressed[1];
return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register);
}
public couldActionApply(vimState: VimState, keysPressed: string[]): boolean {
const register = keysPressed[1];
return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register);
}
}
@RegisterAction
export class CommandOneNormalCommandInInsertMode extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-o>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.returnToInsertAfterCommand = true;
return await new CommandEscInsertMode().exec(
position.character === 0 ? position : position.getRight(),
vimState);
}
}
@RegisterAction
class CommandCtrlW extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-w>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
let wordBegin;
if (position.isInLeadingWhitespace()) {
wordBegin = position.getLineBegin();
} else if (position.isLineBeginning()) {
wordBegin = position.getPreviousLineBegin().getLineEnd();
} else {
wordBegin = position.getWordLeft();
}
await TextEditor.delete(new vscode.Range(wordBegin, position));
vimState.cursorPosition = wordBegin;
return vimState;
}
}
@RegisterAction
class CommandDeleteIndentInCurrentLine extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-d>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
const originalText = TextEditor.getLineAt(position).text;
const indentationWidth = TextEditor.getIndentationLevel(originalText);
if (indentationWidth === 0) {
return vimState;
}
const tabSize = Configuration.tabstop;
const newIndentationWidth = (indentationWidth / tabSize - 1) * tabSize;
await TextEditor.replace(new vscode.Range(position.getLineBegin(), position.getLineEnd()),
TextEditor.setIndentationLevel(originalText, newIndentationWidth < 0 ? 0 : newIndentationWidth));
const cursorPosition = Position.FromVSCodePosition(position.with(position.line,
position.character + (newIndentationWidth - indentationWidth) / tabSize));
vimState.cursorPosition = cursorPosition;
vimState.cursorStartPosition = cursorPosition;
vimState.currentMode = ModeName.Insert;
return vimState;
}
}
@RegisterAction
class CommandInsertAboveChar extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-y>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
if (TextEditor.isFirstLine(position)) {
return vimState;
}
const charAboveCursorPosition = position.getUpByCount(1);
if (charAboveCursorPosition.isLineEnd()) {
return vimState;
}
const char = TextEditor.getText(new vscode.Range(charAboveCursorPosition, charAboveCursorPosition.getRight()));
await TextEditor.insert(char, position);
vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start);
return vimState;
}
}
@RegisterAction
class CommandCtrlHInInsertMode extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-h>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.recordedState.transformations.push({
type: "deleteText",
position: position,
});
return vimState;
}
}
@RegisterAction
class CommandCtrlUInInsertMode extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-u>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
const start = position.getLineBegin();
const stop = position.getLineEnd();
await TextEditor.delete(new vscode.Range(start, stop));
vimState.cursorPosition = start;
vimState.cursorStartPosition = start;
return vimState;
}
}
@RegisterAction
class CommandCtrlN extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-n>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
await vscode.commands.executeCommand("selectNextSuggestion");
return vimState;
}
}
@RegisterAction
class CommandCtrlP extends BaseCommand {
modes = [ModeName.Insert];
keys = ["<C-p>"];
public async exec(position: Position, vimState: VimState): Promise<VimState> {
await vscode.commands.executeCommand("selectPrevSuggestion");
return vimState;
}
}

1744
src/actions/motion.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,10 +7,13 @@ import { RecordedState, VimState } from './../mode/modeHandler';
import { PairMatcher } from './../matching/matcher';
import { Configuration } from './../configuration/configuration';
import {
BaseCommand, BaseMovement, RegisterAction, IMovement, ChangeOperator, DeleteOperator, YankOperator,
BaseCommand, RegisterAction, ChangeOperator, DeleteOperator, YankOperator,
} from './actions';
import {
BaseMovement, IMovement,
MoveQuoteMatch, MoveASingleQuotes, MoveADoubleQuotes, MoveABacktick, MoveInsideCharacter, MoveACurlyBrace, MoveInsideTag,
MoveAParentheses, MoveASquareBracket, MoveACaret, MoveAroundTag
} from './actions';
} from './motion';
import {
TextObjectMovement, SelectInnerWord, SelectInnerBigWord, SelectInnerSentence, SelectInnerParagraph
} from './textobject';

View File

@ -5,9 +5,11 @@ import { Range } from './../motion/range';
import { TextEditor } from './../textEditor';
import { RecordedState, VimState } from './../mode/modeHandler';
import { BaseMovement, RegisterAction, IMovement, ChangeOperator,
import { RegisterAction, ChangeOperator } from './actions';
import {
BaseMovement, IMovement,
MoveASingleQuotes, MoveADoubleQuotes, MoveAClosingCurlyBrace, MoveAParentheses, MoveASquareBracket
} from './actions';
} from './motion';
export abstract class TextObjectMovement extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock];

View File

@ -27,9 +27,14 @@ import { VisualLineMode } from './modeVisualLine';
import { HistoryTracker } from './../history/historyTracker';
import { EasyMotion } from './../easymotion/easymotion';
import {
BaseMovement, BaseCommand, Actions, BaseAction,
BaseOperator, DocumentContentChangeAction, CommandInsertInInsertMode, CommandInsertPreviousText, CommandQuitRecordMacro,
isIMovement, KeypressState } from './../actions/actions';
BaseCommand, Actions, BaseAction,
BaseOperator, DocumentContentChangeAction, CommandQuitRecordMacro, KeypressState } from './../actions/actions';
import {
BaseMovement, isIMovement
} from './../actions/motion';
import {
CommandInsertInInsertMode, CommandInsertPreviousText
} from './../actions/insertCommands';
import { Position, PositionDiff } from './../motion/position';
import { Range } from './../motion/range';
import { RegisterMode, Register } from './../register/register';

View File

@ -2,7 +2,7 @@
import * as vscode from "vscode";
import { Position, PositionDiff } from "./position";
import { IMovement } from './../actions/actions';
import { IMovement } from './../actions/motion';
export class Range {
private _start: Position;