mirror of
https://github.com/VSCodeVim/Vim.git
synced 2024-09-21 09:07:56 +03:00
motion and insert commands.
This commit is contained in:
parent
e9fc7c5dab
commit
112fd58424
@ -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
439
src/actions/insertCommands.ts
Normal file
439
src/actions/insertCommands.ts
Normal 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
1744
src/actions/motion.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -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';
|
||||
|
@ -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];
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user