Skip to content

Commit

Permalink
Merge pull request #321 from VSCodeVim/repeated-motions
Browse files Browse the repository at this point in the history
Repeated motions
  • Loading branch information
johnfn authored Jun 20, 2016
2 parents d9a54c5 + be3034e commit e994e9c
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 46 deletions.
125 changes: 96 additions & 29 deletions src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@ const controlKeys: string[] = [
"delete"
];

const containsControlKey = function(s: string): boolean {
for (const controlKey of controlKeys) {
if (s.indexOf(controlKey) !== -1) {
return true;
const compareKeypressSequence = function (one: string[], two: string[]): boolean {
const containsControlKey = (s: string): boolean => {
for (const controlKey of controlKeys) {
if (s.indexOf(controlKey) !== -1) {
return true;
}
}
}

return false;
};
return false;
};

const isSingleNumber = (s: string): boolean => {
return s.length === 1 && "1234567890".indexOf(s) > -1;
};

const compareKeypressSequence = function (one: string[], two: string[]): boolean {
if (one.length !== two.length) {
return false;
}
Expand All @@ -34,6 +38,9 @@ const compareKeypressSequence = function (one: string[], two: string[]): boolean
if (left === "<any>") { continue; }
if (right === "<any>") { continue; }

if (left === "<number>" && isSingleNumber(right)) { continue; }
if (right === "<number>" && isSingleNumber(left) ) { continue; }

if (left === "<character>" && !containsControlKey(right)) { continue; }
if (right === "<character>" && !containsControlKey(left)) { continue; }

Expand Down Expand Up @@ -124,25 +131,55 @@ export abstract class BaseMovement extends BaseAction {
public setsDesiredColumnToEOL = false;

/**
* Run the movement.
* Run the movement a single time.
*
* Generally returns a new Position. If necessary, it can return an IMovement instead.
*/
public abstract async execAction(position: Position, vimState: VimState): Promise<Position | IMovement>;
public async execAction(position: Position, vimState: VimState): Promise<Position | IMovement> {
throw new Error("Not implemented!");
}

/**
* Run the movement in an operator context. 99% of the time, this function can be
* ignored, as it is exactly the same as the above function.
* Run the movement in an operator context a single time.
*
* Some movements operate over different ranges when used for operators.
*/
public async execActionForOperator(position: Position, vimState: VimState): Promise<Position | IMovement> {
return await this.execAction(position, vimState);
}

/**
* Run a movement count times.
*/
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
let recordedState = vimState.recordedState;
let result: Position | IMovement;

for (let i = 0; i < count; i++) {
const lastIteration = (i === count - 1);
const temporaryResult = (recordedState.operator && lastIteration) ?
await this.execActionForOperator(position, vimState) :
await this.execAction (position, vimState);

result = temporaryResult;

if (result instanceof Position) {
position = result;
} else if (isIMovement(result)) {
position = result.stop;
}
}

return result;
}
}

/**
* A command is something like <esc>, :, v, i, etc.
*/
export abstract class BaseCommand extends BaseAction {
isCompleteAction = true;

/**
* Run the command.
*/
Expand Down Expand Up @@ -219,6 +256,21 @@ export function RegisterAction(action: typeof BaseAction): void {



@RegisterAction
class CommandNumber extends BaseCommand {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["<number>"];
isCompleteAction = false;

public async exec(position: Position, vimState: VimState): Promise<VimState> {
const number = parseInt(this.keysPressed[0], 10);

vimState.recordedState.count = vimState.recordedState.count * 10 + number;

return vimState;
}
}

@RegisterAction
class CommandEsc extends BaseCommand {
modes = [ModeName.Insert, ModeName.Visual, ModeName.VisualLine];
Expand Down Expand Up @@ -1025,15 +1077,15 @@ class MoveFindForward extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["f", "<character>"];

public async execAction(position: Position, vimState: VimState): Promise<Position> {
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
const toFind = this.keysPressed[1];
let result = position.findForwards(toFind, count);

return position.findForwards(toFind);
}
if (vimState.recordedState.operator) {
result = result.getRight();
}

public async execActionForOperator(position: Position, vimState: VimState): Promise<Position> {
const pos = await this.execAction(position, vimState);
return pos.getRight();
return result;
}
}

Expand All @@ -1042,10 +1094,15 @@ class MoveFindBackward extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["F", "<character>"];

public async execAction(position: Position, vimState: VimState): Promise<Position> {
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
const toFind = this.keysPressed[1];
let result = position.findBackwards(toFind, count);

return position.findBackwards(toFind);
if (vimState.recordedState.operator) {
result = result.getLeft();
}

return result;
}
}

Expand All @@ -1055,14 +1112,15 @@ class MoveTilForward extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["t", "<character>"];

public async execAction(position: Position, vimState: VimState): Promise<Position> {
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
const toFind = this.keysPressed[1];
let result = position.tilForwards(toFind, count);

return position.tilForwards(toFind);
}
if (vimState.recordedState.operator) {
result = result.getRight();
}

public async execActionForOperator(position: Position, vimState: VimState): Promise<Position> {
return (await this.execAction(position, vimState)).getRight();
return result;
}
}

Expand All @@ -1071,10 +1129,15 @@ class MoveTilBackward extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["T", "<character>"];

public async execAction(position: Position, vimState: VimState): Promise<Position> {
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
const toFind = this.keysPressed[1];
let result = position.tilBackwards(toFind, count);

return position.tilBackwards(toFind);
if (vimState.recordedState.operator) {
result = result.getLeft();
}

return result;
}
}

Expand Down Expand Up @@ -1124,8 +1187,12 @@ class MoveNonBlankLast extends BaseMovement {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = ["G"];

public async execAction(position: Position, vimState: VimState): Promise<Position> {
return position.getDocumentEnd();
public async execActionWithCount(position: Position, vimState: VimState, count = 1): Promise<Position | IMovement> {
if (count === 1) {
return position.getDocumentEnd();
}

return new Position(count, 0);
}
}

Expand Down
43 changes: 26 additions & 17 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class VimState {
* 1 === forward
* -1 === backward
*/
public searchDirection: number = 1;
public searchDirection = 1;

/**
* The mode Vim will be in once this action finishes.
Expand Down Expand Up @@ -190,7 +190,7 @@ export class RecordedState {
/**
* The number of times the user wants to repeat this action.
*/
public count: number = 1;
public count: number = 0;

public clone(): RecordedState {
const res = new RecordedState();
Expand Down Expand Up @@ -353,7 +353,7 @@ export class ModeHandler implements vscode.Disposable {
}
}

await this.updateView(this._vimState);
await this.updateView(this._vimState, false);
}
});
}
Expand Down Expand Up @@ -459,7 +459,9 @@ export class ModeHandler implements vscode.Disposable {
vimState = await this.handleCommand(vimState);
}

ranAction = true;
if (action.isCompleteAction) {
ranAction = true;
}
}

// Update mode (note the ordering allows you to go into search mode,
Expand Down Expand Up @@ -515,9 +517,13 @@ export class ModeHandler implements vscode.Disposable {
private async executeMovement(vimState: VimState, movement: BaseMovement): Promise<VimState> {
let recordedState = vimState.recordedState;

const result = recordedState.operator ?
await movement.execActionForOperator(vimState.cursorPosition, vimState) :
await movement.execAction (vimState.cursorPosition, vimState);
if (recordedState.count < 1) {
recordedState.count = 1;
} else if (recordedState.count > 99999) {
recordedState.count = 99999;
}

const result = await movement.execActionWithCount(vimState.cursorPosition, vimState, recordedState.count);

if (result instanceof Position) {
vimState.cursorPosition = result;
Expand All @@ -527,6 +533,8 @@ export class ModeHandler implements vscode.Disposable {
vimState.currentRegisterMode = result.registerMode;
}

vimState.recordedState.count = 0;

let stop = vimState.cursorPosition;

// Keep the cursor within bounds
Expand Down Expand Up @@ -634,8 +642,7 @@ export class ModeHandler implements vscode.Disposable {
return vimState;
}

// TODO: this method signature is totally nonsensical!!!!
private async updateView(vimState: VimState): Promise<void> {
private async updateView(vimState: VimState, drawSelection = true): Promise<void> {
// Update cursor position

let start = vimState.cursorStartPosition;
Expand All @@ -662,14 +669,16 @@ export class ModeHandler implements vscode.Disposable {

// Draw selection (or cursor)

if (vimState.currentMode === ModeName.Visual) {
vscode.window.activeTextEditor.selection = new vscode.Selection(start, stop);
} else if (vimState.currentMode === ModeName.VisualLine) {
vscode.window.activeTextEditor.selection = new vscode.Selection(
Position.EarlierOf(start, stop).getLineBegin(),
Position.LaterOf(start, stop).getLineEnd());
} else {
vscode.window.activeTextEditor.selection = new vscode.Selection(stop, stop);
if (drawSelection) {
if (vimState.currentMode === ModeName.Visual) {
vscode.window.activeTextEditor.selection = new vscode.Selection(start, stop);
} else if (vimState.currentMode === ModeName.VisualLine) {
vscode.window.activeTextEditor.selection = new vscode.Selection(
Position.EarlierOf(start, stop).getLineBegin(),
Position.LaterOf(start, stop).getLineEnd());
} else {
vscode.window.activeTextEditor.selection = new vscode.Selection(stop, stop);
}
}

// Scroll to position of cursor
Expand Down

0 comments on commit e994e9c

Please sign in to comment.