Skip to content

Commit

Permalink
Implement increment and decrement operators
Browse files Browse the repository at this point in the history
This adds support for single or numeric-prefix control+x and control+a.
  • Loading branch information
ascandella committed Jul 26, 2016
1 parent cc59ae0 commit faa43d3
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 4 deletions.
2 changes: 1 addition & 1 deletion extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export async function activate(context: vscode.ExtensionContext) {
showCmdLine("", modeHandlerToEditorIdentity[new EditorIdentity(vscode.window.activeTextEditor).toString()]);
});

'rfbducw['.split('').forEach(key => {
'rfbducw[ax'.split('').forEach(key => {
registerCommand(context, `extension.vim_ctrl+${key}`, () => handleKeyEvent(`ctrl+${key}`));
});

Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@
"command": "extension.vim_ctrl+c",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+a",
"command": "extension.vim_ctrl+a",
"when": "editorTextFocus"
},
{
"key": "ctrl+x",
"command": "extension.vim_ctrl+x",
"when": "editorTextFocus"
},
{
"key": "left",
"command": "extension.vim_left",
Expand Down
72 changes: 72 additions & 0 deletions src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2694,3 +2694,75 @@ class ToggleCaseAndMoveForward extends BaseMovement {
return position.getRight();
}
}

abstract class IncrementDecrementNumberAction extends BaseMovement {
modes = [ModeName.Normal];
canBePrefixedWithCount = true;

offset: number;

public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise<Position> {
count = count || 1;
const text = TextEditor.getLineAt(position).text;

// They may start on a number, so check the current word boundaries first.
let wordEnd = position.getCurrentWordEnd(true);
let wordStart = position.getWordLeft(true);
do {
// '-' doesn't count as a word, but is important to include in parsing the number
if (text[wordStart.character - 1] === '-') {
wordStart = wordStart.getLeft();
}
const word = text.substring(wordStart.character, wordEnd.character + 1);
// Strict number parsing so "1a" doesn't silently get converted to "1"
const num = Number(word);

if (!isNaN(num)) {
return this.replaceNum(num, this.offset * count, wordStart, wordEnd);
}

if (wordEnd.isLineEnd()) {
// We got to the end of the line and didn't find anything that looks like a number
break;
}

// Move forward to the next word
wordStart = wordStart.getWordRight();
wordEnd = wordStart.getCurrentWordEnd();
} while (true);

// No usable numbers, return the original position
return position;
}

public async replaceNum(start: number, offset: number, startPos: Position, endPos: Position): Promise<Position> {
const oldWidth = start.toString().length;
start += offset;
const newNum = start.toString();

const range = new vscode.Range(startPos, endPos.getRight());

if (oldWidth !== newNum.length) {
// Can't use replace, since new number may be different width than old
await TextEditor.delete(range);
await TextEditor.insertAt(newNum, startPos);
} else {
await TextEditor.replace(range, newNum);
}

// Advance to the end of the word in case it's longer
return Position.FromVSCodePosition(endPos.translate(0, newNum.length - oldWidth));
}
}

@RegisterAction
class IncrementNumberAction extends IncrementDecrementNumberAction {
keys = ["ctrl+a"];
offset = +1;
}

@RegisterAction
class DecrementNumberAction extends IncrementDecrementNumberAction {
keys = ["ctrl+x"];
offset = -1;
}
2 changes: 0 additions & 2 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,6 @@ export class ModeHandler implements vscode.Disposable {
}

async handleKeyEvent(key: string): Promise<Boolean> {
if (key === "<c-r>") { key = "ctrl+r"; } // TODO - temporary hack for tests only!

// Due to a limitation in Electron, en-US QWERTY char codes are used in international keyboards.
// We'll try to mitigate this problem until it's fixed upstream.
// https://github.com/Microsoft/vscode/issues/713
Expand Down
42 changes: 42 additions & 0 deletions test/mode/modeNormal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -963,4 +963,46 @@ suite("Mode Normal", () => {
keysPressed: "dE",
end: ["one two| "]
});

newTest({
title: "can ctrl-a correctly behind a word",
start: ["|one 9"],
keysPressed: "<c-a>",
end: ["one 1|0"]
});

newTest({
title: "can ctrl-a on word",
start: ["one -|11"],
keysPressed: "<c-a>",
end: ["one -1|0"]
});

newTest({
title: "can ctrl-a on decimal",
start: ["1|1.123"],
keysPressed: "<c-a>",
end: ["1|2.123"]
});

newTest({
title: "can ctrl-a with numeric prefix",
start: ["|-10"],
keysPressed: "15<c-a>",
end: ["|5"]
});

newTest({
title: "can ctrl-a on a decimal",
start: ["-10.|1"],
keysPressed: "10<c-a>",
end: ["-10.1|1"]
});

newTest({
title: "can ctrl-x correctly behind a word",
start: ["|one 10"],
keysPressed: "<c-x>",
end: ["one |9"]
});
});
5 changes: 4 additions & 1 deletion test/testSimplifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class TestObjectHelper {
}

/**
* Tokenize a string like "abc<esc>d<c-c>" into ["a", "b", "c", "<esc>", "d", "<c-c>"]
* Tokenize a string like "abc<esc>d<c-c>" into ["a", "b", "c", "<esc>", "d", "ctrl+c"]
*/
function tokenizeKeySequence(sequence: string): string[] {
let isBracketedKey = false;
Expand All @@ -162,6 +162,9 @@ function tokenizeKeySequence(sequence: string): string[] {

if (char === '>') {
isBracketedKey = false;
if (key.startsWith("<c-")) {
key = "ctrl+" + key.substring(3, key.length - 1);
}
}

if (isBracketedKey) {
Expand Down

0 comments on commit faa43d3

Please sign in to comment.