Skip to content

Commit

Permalink
Implement ; and , (#674)
Browse files Browse the repository at this point in the history
* Implement ; and ,

* Trying to fix travis ci agent (which does not handle clipboard tests well)

* fix tslint errors

* cleanup

* more cleanup

* even more cleanup

* and more cleanup
  • Loading branch information
aminroosta authored and johnfn committed Sep 3, 2016
1 parent 660df9a commit 69ff80c
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 4 deletions.
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ Status | Command | Description
:white_check_mark: |:1234: F{char} | to the Nth occurrence of {char} to the left
:white_check_mark: |:1234: t{char} | till before the Nth occurrence of {char} to the right
:white_check_mark: |:1234: T{char} | till before the Nth occurrence of {char} to the left
|:1234: ; | repeat the last "f", "F", "t", or "T" N times
|:1234: , | repeat the last "f", "F", "t", or "T" N times in opposite direction
:white_check_mark: |:1234: ; | repeat the last "f", "F", "t", or "T" N times
:white_check_mark: |:1234: , | repeat the last "f", "F", "t", or "T" N times in opposite direction

## Up-down motions

Expand Down
72 changes: 72 additions & 0 deletions src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ export abstract class BaseMovement extends BaseAction {

canBePrefixedWithCount = false;

/**
* Whether we should change lastRepeatableMovement in VimState.
*/
public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) {
return false;
}

/**
* Whether we should change desiredColumn in VimState.
*/
Expand Down Expand Up @@ -1992,6 +1999,10 @@ class MoveFindForward extends BaseMovement {

return result;
}

public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) {
return !vimState.recordedState.operator || !(isIMovement(result) && result.failed);
}
}

@RegisterAction
Expand All @@ -2009,6 +2020,10 @@ class MoveFindBackward extends BaseMovement {

return result;
}

public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) {
return !vimState.recordedState.operator || !(isIMovement(result) && result.failed);
}
}


Expand All @@ -2031,6 +2046,10 @@ class MoveTilForward extends BaseMovement {

return result;
}

public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) {
return !vimState.recordedState.operator || !(isIMovement(result) && result.failed);
}
}

@RegisterAction
Expand All @@ -2048,6 +2067,59 @@ class MoveTilBackward extends BaseMovement {

return result;
}

public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) {
return !vimState.recordedState.operator || !(isIMovement(result) && result.failed);
}
}

@RegisterAction
class MoveRepeat extends BaseMovement {
keys = [";"];

public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise<Position | IMovement> {
const movement = VimState.lastRepeatableMovement;
if (movement) {
const result = await movement.execActionWithCount(position, vimState, count);
/**
* For t<character> and T<character> commands vim executes ; as 2;
* This way the cursor will get to the next instance of <character>
*/
if (result instanceof Position && position.isEqual(result) && count <= 1) {
return await movement.execActionWithCount(position, vimState, 2);
}
return result;
}
return position;
}
}


@RegisterAction
class MoveRepeatReversed extends BaseMovement {
keys = [","];
static reverseMotionMapping : Map<Function, () => BaseMovement> = new Map([
[MoveFindForward, () => new MoveFindBackward()],
[MoveFindBackward, () => new MoveFindForward()],
[MoveTilForward, () => new MoveTilBackward()],
[MoveTilBackward, () => new MoveTilForward()]
]);

public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise<Position | IMovement> {
const movement = VimState.lastRepeatableMovement;
if (movement) {
const reverse = MoveRepeatReversed.reverseMotionMapping.get(movement.constructor)();
reverse.keysPressed = [reverse.keys[0], movement.keysPressed[1]];

let result = await reverse.execActionWithCount(position, vimState, count);
// For t<character> and T<character> commands vim executes ; as 2;
if (result instanceof Position && position.isEqual(result) && count <= 1) {
result = await reverse.execActionWithCount(position, vimState, 2);
}
return result;
}
return position;
}
}

@RegisterAction
Expand Down
6 changes: 6 additions & 0 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export class VimState {

public historyTracker: HistoryTracker;

public static lastRepeatableMovement : BaseMovement | undefined = undefined;

/**
* The keystroke sequence that made up our last complete action (that can be
* repeated with '.').
Expand Down Expand Up @@ -850,6 +852,10 @@ export class ModeHandler implements vscode.Disposable {
}
}

if (movement.canBeRepeatedWithSemicolon(vimState, result)) {
VimState.lastRepeatableMovement = movement;
}

vimState.recordedState.count = 0;

let stop = vimState.cursorPosition;
Expand Down
2 changes: 1 addition & 1 deletion test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Globals.isTesting = true;
testRunner.configure({
ui: 'tdd',
useColors: true,
timeout: 2500,
timeout: 4000,
});

module.exports = testRunner;
Expand Down
2 changes: 1 addition & 1 deletion test/register/register.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ suite("register", () => {
newTest({
title: "Can use two registers together",
start: ['|one', "two"],
keysPressed: '"*yyjyy"*pp',
keysPressed: '"ayyj"byy"ap"bp',
end: ["one", "two", "one", "|two"],
});

Expand Down
49 changes: 49 additions & 0 deletions test/register/repeatableMovement.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use strict";

import { ModeHandler } from "../../src/mode/modeHandler";
import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from '../testUtils';
import { getTestingFunctions } from '../testSimplifier';

suite("register", () => {
let modeHandler: ModeHandler = new ModeHandler();

let {
newTest,
newTestOnly,
} = getTestingFunctions(modeHandler);

setup(async () => {
await setupWorkspace();
});

suiteTeardown(cleanUpWorkspace);

newTest({
title: "Can repeat f<character>",
start: ['|abc abc abc'],
keysPressed: 'fa;',
end: ['abc abc |abc'],
});

newTest({
title: "Can repeat reversed F<character>",
start: ['|abc abc abc'],
keysPressed: 'fa$,',
end: ['abc abc |abc'],
});

newTest({
title: "Can repeat t<character>",
start: ['|abc abc abc'],
keysPressed: 'tc;',
end: ['abc a|bc abc'],
});

newTest({
title: "Can repeat N times reversed t<character>",
start: ['|abc abc abc abc'],
keysPressed: 'tc$3,',
end: ['abc| abc abc abc'],
});

});

0 comments on commit 69ff80c

Please sign in to comment.