Skip to content

Commit

Permalink
g? (rot13) support
Browse files Browse the repository at this point in the history
Fixes #4363
  • Loading branch information
J-Fields committed Dec 26, 2019
1 parent 7a148f5 commit 10a41e0
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 12 deletions.
7 changes: 7 additions & 0 deletions src/actions/commands/actions.ts
Expand Up @@ -1397,6 +1397,13 @@ export class CommandSearchBackwards extends BaseCommand {
isMotion = true;
isJump = true;

public doesActionApply(vimState: VimState, keysPressed: string[]): boolean {
// Prevent collision with `g?` (rot13 operator)
return (
super.doesActionApply(vimState, keysPressed) && vimState.recordedState.operator === undefined
);
}

public async exec(position: Position, vimState: VimState): Promise<VimState> {
globalState.searchState = new SearchState(
SearchDirection.Backward,
Expand Down
48 changes: 48 additions & 0 deletions src/actions/operator.ts
Expand Up @@ -783,6 +783,54 @@ export class CommentOperator extends BaseOperator {
}
}

@RegisterAction
export class ROT13Operator extends BaseOperator {
public keys = ['g', '?'];
public modes = [Mode.Normal, Mode.Visual, Mode.VisualLine, Mode.VisualBlock];

public async run(vimState: VimState, start: Position, end: Position): Promise<VimState> {
const selections =
vimState.currentMode === Mode.Normal
? [new vscode.Selection(start, end.getRight())]
: vimState.editor.selections;
for (const range of selections) {
const original = TextEditor.getText(range);
vimState.recordedState.transformations.push({
type: 'replaceText',
text: ROT13Operator.rot13(original),
start: Position.FromVSCodePosition(range.start),
end: Position.FromVSCodePosition(range.end),
});
}

return vimState;
}

/**
* https://en.wikipedia.org/wiki/ROT13
*/
public static rot13(str: string) {
return str
.split('')
.map((char: string) => {
let charCode = char.charCodeAt(0);

if (char >= 'a' && char <= 'z') {
const a = 'a'.charCodeAt(0);
charCode = ((charCode - a + 13) % 26) + a;
}

if (char >= 'A' && char <= 'Z') {
const A = 'A'.charCodeAt(0);
charCode = ((charCode - A + 13) % 26) + A;
}

return String.fromCharCode(charCode);
})
.join('');
}
}

@RegisterAction
export class CommentBlockOperator extends BaseOperator {
public keys = ['g', 'C'];
Expand Down
6 changes: 4 additions & 2 deletions src/actions/plugins/sneak.ts
Expand Up @@ -49,7 +49,8 @@ export class SneakForward extends BaseMovement {

const ignorecase =
configuration.sneakUseIgnorecaseAndSmartcase &&
configuration.ignorecase && !(configuration.smartcase && /[A-Z]/.test(searchString));
configuration.ignorecase &&
!(configuration.smartcase && /[A-Z]/.test(searchString));

// Check for matches
if (ignorecase) {
Expand Down Expand Up @@ -112,7 +113,8 @@ export class SneakBackward extends BaseMovement {

const ignorecase =
configuration.sneakUseIgnorecaseAndSmartcase &&
configuration.ignorecase && !(configuration.smartcase && /[A-Z]/.test(searchString));
configuration.ignorecase &&
!(configuration.smartcase && /[A-Z]/.test(searchString));

// Check for matches
if (ignorecase) {
Expand Down
5 changes: 1 addition & 4 deletions src/configuration/vimrc.ts
Expand Up @@ -45,10 +45,7 @@ class VimrcImpl {
const mappings = (() => {
switch (remap.keyRemappingType) {
case 'map':
return [
config.normalModeKeyBindings,
config.visualModeKeyBindings,
];
return [config.normalModeKeyBindings, config.visualModeKeyBindings];
case 'nmap':
return [config.normalModeKeyBindings];
case 'vmap':
Expand Down
9 changes: 5 additions & 4 deletions src/state/recordedState.ts
Expand Up @@ -113,11 +113,11 @@ export class RecordedState {
*/
public get operator(): BaseOperator {
let list = this.actionsRun.filter(a => a instanceof BaseOperator).reverse();
return list[0] as any;
return list[0] as BaseOperator;
}

public get operators(): BaseOperator[] {
return this.actionsRun.filter(a => a instanceof BaseOperator).reverse() as any;
return this.actionsRun.filter(a => a instanceof BaseOperator).reverse() as BaseOperator[];
}

/**
Expand All @@ -128,7 +128,7 @@ export class RecordedState {

// TODO - disregard <Esc>, then assert this is of length 1.

return list[0] as any;
return list[0] as BaseCommand;
}

public get hasRunAMovement(): boolean {
Expand Down Expand Up @@ -167,7 +167,8 @@ export class RecordedState {
mode !== Mode.SearchInProgressMode &&
mode !== Mode.CommandlineInProgress &&
(this.hasRunAMovement ||
mode === Mode.Visual || mode === Mode.VisualLine ||
mode === Mode.Visual ||
mode === Mode.VisualLine ||
(this.operators.length > 1 &&
this.operators.reverse()[0].constructor === this.operators.reverse()[1].constructor))
);
Expand Down
6 changes: 5 additions & 1 deletion test/cmd_line/split.test.ts
Expand Up @@ -21,7 +21,11 @@ suite('Horizontal split', () => {
await commandLine.Run(cmd, modeHandler.vimState);
await WaitForEditorsToClose(2);

assert.strictEqual(vscode.window.visibleTextEditors.length, 2, 'Editor did not split in 1 sec');
assert.strictEqual(
vscode.window.visibleTextEditors.length,
2,
'Editor did not split in 1 sec'
);
});
}
});
6 changes: 5 additions & 1 deletion test/cmd_line/vsplit.test.ts
Expand Up @@ -21,7 +21,11 @@ suite('Vertical split', () => {
await commandLine.Run(cmd, modeHandler.vimState);
await WaitForEditorsToClose(2);

assert.strictEqual(vscode.window.visibleTextEditors.length, 2, 'Editor did not split in 1 sec');
assert.strictEqual(
vscode.window.visibleTextEditors.length,
2,
'Editor did not split in 1 sec'
);
});
}
});
56 changes: 56 additions & 0 deletions test/operator/rot13.test.ts
@@ -0,0 +1,56 @@
import * as assert from 'assert';

import { getTestingFunctions } from '../testSimplifier';

import { setupWorkspace, cleanUpWorkspace } from '../testUtils';
import { ROT13Operator } from '../../src/actions/operator';

suite('rot13 operator', () => {
const { newTest, newTestOnly, newTestSkip } = getTestingFunctions();

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

teardown(cleanUpWorkspace);

test('rot13() unit test', () => {
const testCases = [
['abcdefghijklmnopqrstuvwxyz', 'nopqrstuvwxyzabcdefghijklm'],
['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'NOPQRSTUVWXYZABCDEFGHIJKLM'],
['!@#$%^&*()', '!@#$%^&*()'],
['âéü', 'âéü'],
];
for (const [input, output] of testCases) {
assert.strictEqual(ROT13Operator.rot13(input), output);
}
});

newTest({
title: 'g?j works',
start: ['a|bc', 'def', 'ghi'],
keysPressed: 'g?j',
end: ['n|op', 'qrs', 'ghi'],
});

newTest({
title: 'g? in visual mode works',
start: ['a|bc', 'def', 'ghi'],
keysPressed: 'vj$g?',
end: ['a|op', 'qrs', 'ghi'],
});

newTest({
title: 'g? in visual line mode works',
start: ['a|bc', 'def', 'ghi'],
keysPressed: 'Vj$g?',
end: ['|nop', 'qrs', 'ghi'],
});

newTest({
title: 'g? in visual block mode works',
start: ['a|bc', 'def', 'ghi'],
keysPressed: '<C-v>j$g?',
end: ['a|op', 'drs', 'ghi'],
});
});

0 comments on commit 10a41e0

Please sign in to comment.