Skip to content

Commit

Permalink
Rewrite Normal Mode tests to use the ModeHandler interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnfn committed May 28, 2016
1 parent 988afcf commit 9e507fc
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 65 deletions.
8 changes: 4 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
// "runtimeArgs": ["--harmony-default-parameters", "--harmony-rest-parameters"],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/src",
"preLaunchTask": "npm"
"outDir": "${workspaceRoot}/out/src"
// "preLaunchTask": "npm"
},
{
"name": "Launch Tests",
Expand All @@ -23,8 +23,8 @@
// "runtimeArgs": ["--js-flags=\"--harmony --harmony-default-parameters\""],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/test",
"preLaunchTask": "npm"
"outDir": "${workspaceRoot}/out/test"
// "preLaunchTask": "npm"
}
]
}
28 changes: 16 additions & 12 deletions extension.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
"use strict"
"use strict";

/**
* Extension.ts is a lightweight wrapper around ModeHandler. It converts key
* events to their string names and passes them on to ModeHandler via
* handleKeyEvent().
*/

import * as vscode from 'vscode';
import {showCmdLine} from './src/cmd_line/main';
import * as cc from './src/cmd_line/lexer';
import {ModeHandler} from "./src/mode/modeHandler";
import {ModeName} from "./src/mode/mode";

var modeHandler : ModeHandler;
var extensionContext : vscode.ExtensionContext;
var extensionContext: vscode.ExtensionContext;
var modeHandler: ModeHandler;

export function activate(context: vscode.ExtensionContext) {
extensionContext = context;

registerCommand(context, 'type', async (args) => {
if (!vscode.window.activeTextEditor) {
return;
}
if (!vscode.window.activeTextEditor) {
return;
}

console.log(args.text);

var isHandled = await handleKeyEvent(args.text);

if (!isHandled) {
if (!isHandled) {
vscode.commands.executeCommand('default:type', {
text: args.text
});
}
});

registerCommand(context, 'extension.vim_esc', () => handleKeyEvent("esc"));
registerCommand(context, 'extension.showCmdLine', () => {
if (!modeHandler) {
Expand Down
18 changes: 15 additions & 3 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ export class ModeHandler implements vscode.Disposable {
this.setCurrentModeByName(ModeName.Normal);
}

/**
* The active mode.
*/
get currentMode() : Mode {
return this._modes.find(mode => mode.isActive);
}

setNormal() {
this.setCurrentModeByName(ModeName.Normal);
}
Expand All @@ -56,7 +59,7 @@ export class ModeHandler implements vscode.Disposable {
this.setupStatusBarItem(statusBarText ? `-- ${statusBarText.toUpperCase()} --` : '');
}

handleKeyEvent(key : string) : Promise<Boolean> {
async handleKeyEvent(key : string) : Promise<Boolean> {
// 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 All @@ -79,12 +82,21 @@ export class ModeHandler implements vscode.Disposable {
if (nextMode) {
this.currentMode.handleDeactivation();
this.setCurrentModeByName(nextMode.name);
return nextMode.handleActivation(key).then(() => { return true; });

await nextMode.handleActivation(key);

return true;
} else {
return this.currentMode.handleKeyEvent(key);
}
}

async handleMultipleKeyEvents(keys: string[]): Promise<void> {
for (const key of keys) {
await this.handleKeyEvent(key);
}
}

setupStatusBarItem(text : string) : void {
if (!this._statusBarItem) {
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
Expand Down
7 changes: 2 additions & 5 deletions src/mode/modeNormal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,10 @@ export class NormalMode extends Mode {
}

shouldBeActivated(key : string, currentMode : ModeName) : boolean {
// TODO: Have these keybinds configurable
return (key === 'esc' || key === 'ctrl+[' || (key === "v" && currentMode === ModeName.Visual));
return (key === '<esc>' || key === '<c-[>' || key === '<c-c>' || (key === "v" && currentMode === ModeName.Visual));
}

async handleActivation(key : string): Promise<void> {
this.motion.left().move();
}
async handleActivation(key : string): Promise<void> { ; }

async handleKeyEvent(key : string): Promise<Boolean> {
this._keyHistory.push(key);
Expand Down
2 changes: 1 addition & 1 deletion src/textEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class TextEditor {
}

/**
* Delete the entire document.
* Removes all text in the entire document.
*/
static async deleteDocument(): Promise<boolean> {
const start = new vscode.Position(0, 0);
Expand Down
105 changes: 65 additions & 40 deletions test/mode/modeInsert.test.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,118 @@
"use strict";

import * as assert from 'assert';
import {CommandKeyMap} from '../../src/configuration/commandKeyMap';
import {setupWorkspace, cleanUpWorkspace, assertEqualLines} from './../testUtils';
import {InsertMode} from '../../src/mode/modeInsert';
import {setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual} from './../testUtils';
import {ModeName} from '../../src/mode/mode';
import {Motion, MotionMode} from '../../src/motion/motion';
import {TextEditor} from '../../src/textEditor';
import {ModeHandler} from "../../src/mode/modeHandler";

suite("Mode Insert", () => {

let motion: Motion;
let modeInsert: InsertMode;
let modeHandler: ModeHandler;

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

motion = new Motion(MotionMode.Cursor);
modeInsert = new InsertMode(motion, CommandKeyMap.DefaultInsertKeyMap());
modeHandler = new ModeHandler();
});

teardown(cleanUpWorkspace);

test("can be activated", () => {
let activationKeys = ['i', 'I', 'o', 'O', 'a', 'A'];
test("can be activated", async () => {
let activationKeys = ['o', 'I', 'i', 'O', 'a', 'A'];

for (let key of activationKeys) {
assert.equal(modeInsert.shouldBeActivated(key, ModeName.Normal), true, key);
}
await modeHandler.handleKeyEvent(key);
assertEqual(modeHandler.currentMode.name, ModeName.Insert);

assert.equal(modeInsert.shouldBeActivated("i", ModeName.Visual), false, "can be activated from visual");
await modeHandler.handleKeyEvent('<esc>');
}
});

test("can handle key events", async () => {
await modeInsert.handleKeyEvent("!");
await modeHandler.handleMultipleKeyEvents(['i', '!']);

return assertEqualLines(["!"]);
});

test("<esc> doesn't change cursor position", async () => {
await modeHandler.handleMultipleKeyEvents([
'i',
'h', 'e', 'l', 'l', 'o',
'<esc>'
]);

assertEqual(TextEditor.getSelection().start.character, 5, "<esc> moved cursor position.");
});

test("Can handle 'o'", async () => {
await TextEditor.insert("text");
await modeInsert.handleActivation("o");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't',
'<esc>',
'o'
]);

return assertEqualLines(["text", ""]);
});

test("Can handle 'O'", async () => {
await TextEditor.insert("text");
await modeInsert.handleActivation("O");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't',
'<esc>',
'O'
]);

return assertEqualLines(["", "text"]);
});

test("Can handle 'i'", async () => {
await TextEditor.insert("texttext");

motion = motion.moveTo(0, 4);
await modeInsert.handleActivation("i");
await modeInsert.handleKeyEvent("!");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't', 't', 'e', 'x', 't', // insert 'texttext'
'<esc>',
'^', 'l', 'l', 'l', 'l', // move to the 4th character
'i',
'!' // insert a !
]);

assertEqualLines(["text!text"]);
});

test("Can handle 'I'", async () => {
await TextEditor.insert("text");
motion = motion.moveTo(0, 3);

await modeInsert.handleActivation("I");
await modeInsert.handleKeyEvent("!");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't',
'<esc>',
'^', 'l', 'l', 'l',
'I',
'!',
]);

assertEqualLines(["!text"]);
});

test("Can handle 'a'", async () => {
await TextEditor.insert("texttext");

motion = motion.moveTo(0, 4);
await modeInsert.handleActivation("a");
await modeInsert.handleKeyEvent("!");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't', 't', 'e', 'x', 't', // insert 'texttext'
'<esc>',
'^', 'l', 'l', 'l', 'l', // move to the 4th character
'a',
'!' // append a !
]);

assertEqualLines(["textt!ext"]);
});

test("Can handle 'A'", async () => {
await TextEditor.insert("text");

motion = motion.moveTo(0, 0);
await modeInsert.handleActivation("A");
await modeInsert.handleKeyEvent("!");
await modeHandler.handleMultipleKeyEvents([
'i',
't', 'e', 'x', 't',
'<esc>',
'^',
'A',
'!',
]);

assertEqualLines(["text!"]);
});
Expand Down
10 changes: 10 additions & 0 deletions test/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ export function assertEqualLines(expectedLines: string[]) {
}
}

/**
* Assert that the first two arguments are equal, and fail a test otherwise.
*
* The only difference between this and assert.equal is that here we
* check to ensure the types of the variables are correct.
*/
export function assertEqual<T>(one: T, two: T, message: string = ""): void {
assert.equal(one, two, message);
}

export async function setupWorkspace(): Promise<any> {
const file = await createRandomFile("");
const doc = await vscode.workspace.openTextDocument(file);
Expand Down

0 comments on commit 9e507fc

Please sign in to comment.