Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ; and , #674

Merged
merged 10 commits into from
Sep 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'],
});

});