Merge pull request #16 from VSCodeVim/insert-mode

Command/Insert Modes
This commit is contained in:
Jason Poon 2015-11-20 02:21:20 -08:00
commit f53c37de69
17 changed files with 319 additions and 130 deletions

View File

@ -1,7 +1,10 @@
language: node_js
node_js:
- "0.12"
install: npm install
install:
- npm install
before_script:
- npm install -g gulp
script: gulp
- npm install -g gulp
- gulp init
script:
- gulp

View File

@ -1,10 +1,12 @@
environment:
node_js_version: "0.12"
node_js_version: "4.1.1"
TSD_GITHUB_TOKEN: "4c7f278997af83ba584aaa9c5722d5ecbbcb1dd9"
install:
- ps: install-product node $env:node_js_version
- npm install
- npm install -g gulp
- gulp init
build: off

View File

@ -22,16 +22,55 @@ export function activate(context: vscode.ExtensionContext) {
showCmdLine();
});
vscode.commands.registerCommand('extension.vimMode_esc', () => handleKeyEvent("esc"));
vscode.commands.registerCommand('extension.vimMode_semicolon', () => handleKeyEvent(":"));
vscode.commands.registerCommand('extension.vimMode_h', () => handleKeyEvent("h"));
vscode.commands.registerCommand('extension.vimMode_j', () => handleKeyEvent("j"));
vscode.commands.registerCommand('extension.vimMode_k', () => handleKeyEvent("k"));
vscode.commands.registerCommand('extension.vimMode_l', () => handleKeyEvent("l"));
vscode.commands.registerCommand('extension.vim_esc', () => handleKeyEvent("esc"));
vscode.commands.registerCommand('extension.vim_colon', () => handleKeyEvent(":"));
vscode.commands.registerCommand('extension.vim_space', () => handleKeyEvent("space"));
vscode.commands.registerCommand('extension.vim_a', () => handleKeyEvent("a"));
vscode.commands.registerCommand('extension.vim_b', () => handleKeyEvent("b"));
vscode.commands.registerCommand('extension.vim_c', () => handleKeyEvent("c"));
vscode.commands.registerCommand('extension.vim_d', () => handleKeyEvent("d"));
vscode.commands.registerCommand('extension.vim_e', () => handleKeyEvent("e"));
vscode.commands.registerCommand('extension.vim_f', () => handleKeyEvent("f"));
vscode.commands.registerCommand('extension.vim_g', () => handleKeyEvent("g"));
vscode.commands.registerCommand('extension.vim_h', () => handleKeyEvent("h"));
vscode.commands.registerCommand('extension.vim_i', () => handleKeyEvent("i"));
vscode.commands.registerCommand('extension.vim_j', () => handleKeyEvent("j"));
vscode.commands.registerCommand('extension.vim_k', () => handleKeyEvent("k"));
vscode.commands.registerCommand('extension.vim_l', () => handleKeyEvent("l"));
vscode.commands.registerCommand('extension.vim_m', () => handleKeyEvent("m"));
vscode.commands.registerCommand('extension.vim_n', () => handleKeyEvent("n"));
vscode.commands.registerCommand('extension.vim_o', () => handleKeyEvent("o"));
vscode.commands.registerCommand('extension.vim_p', () => handleKeyEvent("p"));
vscode.commands.registerCommand('extension.vim_q', () => handleKeyEvent("q"));
vscode.commands.registerCommand('extension.vim_r', () => handleKeyEvent("r"));
vscode.commands.registerCommand('extension.vim_s', () => handleKeyEvent("s"));
vscode.commands.registerCommand('extension.vim_t', () => handleKeyEvent("t"));
vscode.commands.registerCommand('extension.vim_u', () => handleKeyEvent("u"));
vscode.commands.registerCommand('extension.vim_v', () => handleKeyEvent("v"));
vscode.commands.registerCommand('extension.vim_w', () => handleKeyEvent("w"));
vscode.commands.registerCommand('extension.vim_x', () => handleKeyEvent("x"));
vscode.commands.registerCommand('extension.vim_y', () => handleKeyEvent("y"));
vscode.commands.registerCommand('extension.vim_z', () => handleKeyEvent("z"));
vscode.commands.registerCommand('extension.vim_A', () => handleKeyEvent("A"));
vscode.commands.registerCommand('extension.vim_I', () => handleKeyEvent("I"));
vscode.commands.registerCommand('extension.vim_O', () => handleKeyEvent("O"));
vscode.commands.registerCommand('extension.vim_0', () => handleKeyEvent("0"));
vscode.commands.registerCommand('extension.vim_1', () => handleKeyEvent("1"));
vscode.commands.registerCommand('extension.vim_2', () => handleKeyEvent("2"));
vscode.commands.registerCommand('extension.vim_3', () => handleKeyEvent("3"));
vscode.commands.registerCommand('extension.vim_4', () => handleKeyEvent("4"));
vscode.commands.registerCommand('extension.vim_5', () => handleKeyEvent("5"));
vscode.commands.registerCommand('extension.vim_6', () => handleKeyEvent("6"));
vscode.commands.registerCommand('extension.vim_7', () => handleKeyEvent("7"));
vscode.commands.registerCommand('extension.vim_8', () => handleKeyEvent("8"));
vscode.commands.registerCommand('extension.vim_9', () => handleKeyEvent("9"));
context.subscriptions.push(cmdLineDisposable);
}
function handleKeyEvent(key:string) {
modeHandler.HandleKeyEvent(key);
modeHandler.handleKeyEvent(key);
}

View File

@ -1,10 +1,12 @@
var gulp = require('gulp');
var tslint = require('gulp-tslint');
var tsd = require('gulp-tsd');
var shell = require('gulp-shell');
var mocha = require('gulp-mocha');
var paths = {
scripts_ts: "src/**/*.ts",
tests_ts: "test/**/*.ts",
tests_js: [
// test with dependencies on 'vscode' do not run
"out/test/extension.test.js",
@ -13,18 +15,28 @@ var paths = {
]
};
gulp.task('init', ['tsd']);
gulp.task('compile', shell.task([
'node ./node_modules/vscode/bin/compile -p ./',
]));
gulp.task('tsd', function (callback) {
return gulp.src('./gulp_tsd.json').pipe(tsd({
command: 'reinstall',
config: './tsd.json'
}, callback));
});
gulp.task('tslint', function() {
return gulp.src(paths.scripts_ts)
return gulp.src([paths.scripts_ts, paths.tests_ts])
.pipe(tslint())
.pipe(tslint.report('prose', {
summarizeFailureOutput: true
}));
});
gulp.task('compile', shell.task([
'node ./node_modules/vscode/bin/compile -p ./',
]));
gulp.task('test', ['compile'], function () {
return gulp.src(paths.tests_js, {
read: false
@ -35,4 +47,4 @@ gulp.task('test', ['compile'], function () {
}));
});
gulp.task('default', ['tslint', 'test']);
gulp.task('default', ['tslint', 'test']);

View File

@ -1,12 +1,17 @@
{
"name": "vim",
"name": "vscodevim",
"description": "Vim emulation for VS Code",
"version": "0.0.1",
"publisher": "VSCodeVim",
"publisher": "vscodevim",
"contributors": [
"guillermooo",
"jpoon"
],
"repository": {
"type": "git",
"url": "https://github.com/VSCodeVim/Vim.git"
},
"license" : "https://github.com/VSCodeVim/Vim/blob/master/LICENSE",
"engines": {
"vscode": "0.10.x"
},
@ -25,61 +30,57 @@
}
],
"keybindings": [
{
"key": "Escape",
"command": "extension.vimMode_esc",
"when": "editorTextFocus"
},
{
"key": "Shift+;",
"command": "extension.vimMode_semicolon",
"when": "editorTextFocus"
},
{
"key": "H",
"command": "extension.vimMode_h",
"when": "editorTextFocus"
},
{
"key": "J",
"command": "extension.vimMode_j",
"when": "editorTextFocus"
},
{
"key": "K",
"command": "extension.vimMode_k",
"when": "editorTextFocus"
},
{
"key": "L",
"command": "extension.vimMode_l",
"when": "editorTextFocus"
},
{
"key": "Ctrl+H",
"command": "cursorLeft",
"when": "editorTextFocus"
},
{
"key": "Ctrl+J",
"command": "cursorDown",
"when": "editorTextFocus"
},
{
"key": "Ctrl+K",
"command": "cursorUp",
"when": "editorTextFocus"
},
{
"key": "Ctrl+L",
"command": "cursorRight",
"when": "editorTextFocus"
},
{
"key": "Ctrl+Shift+.",
"command": "extension.showCmdLine",
"when": "editorTextFocus"
}
{ "key": "Escape", "command": "extension.vim_esc", "when": "editorTextFocus" },
{ "key": "Shift+;", "command": "extension.vim_colon", "when": "editorTextFocus" },
{ "key": "space", "command": "extension.vim_space", "when": "editorTextFocus" },
{ "key": "a", "command": "extension.vim_a", "when": "editorTextFocus" },
{ "key": "b", "command": "extension.vim_b", "when": "editorTextFocus" },
{ "key": "c", "command": "extension.vim_c", "when": "editorTextFocus" },
{ "key": "d", "command": "extension.vim_d", "when": "editorTextFocus" },
{ "key": "e", "command": "extension.vim_e", "when": "editorTextFocus" },
{ "key": "f", "command": "extension.vim_f", "when": "editorTextFocus" },
{ "key": "g", "command": "extension.vim_g", "when": "editorTextFocus" },
{ "key": "h", "command": "extension.vim_h", "when": "editorTextFocus" },
{ "key": "i", "command": "extension.vim_i", "when": "editorTextFocus" },
{ "key": "j", "command": "extension.vim_j", "when": "editorTextFocus" },
{ "key": "k", "command": "extension.vim_k", "when": "editorTextFocus" },
{ "key": "l", "command": "extension.vim_l", "when": "editorTextFocus" },
{ "key": "m", "command": "extension.vim_m", "when": "editorTextFocus" },
{ "key": "n", "command": "extension.vim_n", "when": "editorTextFocus" },
{ "key": "o", "command": "extension.vim_o", "when": "editorTextFocus" },
{ "key": "p", "command": "extension.vim_p", "when": "editorTextFocus" },
{ "key": "q", "command": "extension.vim_q", "when": "editorTextFocus" },
{ "key": "r", "command": "extension.vim_r", "when": "editorTextFocus" },
{ "key": "s", "command": "extension.vim_s", "when": "editorTextFocus" },
{ "key": "t", "command": "extension.vim_t", "when": "editorTextFocus" },
{ "key": "u", "command": "extension.vim_u", "when": "editorTextFocus" },
{ "key": "v", "command": "extension.vim_v", "when": "editorTextFocus" },
{ "key": "w", "command": "extension.vim_w", "when": "editorTextFocus" },
{ "key": "x", "command": "extension.vim_x", "when": "editorTextFocus" },
{ "key": "y", "command": "extension.vim_y", "when": "editorTextFocus" },
{ "key": "z", "command": "extension.vim_z", "when": "editorTextFocus" },
{ "key": "Shift+a", "command": "extension.vim_A", "when": "editorTextFocus" },
{ "key": "Shift+i", "command": "extension.vim_I", "when": "editorTextFocus" },
{ "key": "Shift+o", "command": "extension.vim_O", "when": "editorTextFocus" },
{ "key": "0", "command": "extension.vim_0", "when": "editorTextFocus" },
{ "key": "1", "command": "extension.vim_1", "when": "editorTextFocus" },
{ "key": "2", "command": "extension.vim_2", "when": "editorTextFocus" },
{ "key": "3", "command": "extension.vim_3", "when": "editorTextFocus" },
{ "key": "4", "command": "extension.vim_4", "when": "editorTextFocus" },
{ "key": "5", "command": "extension.vim_5", "when": "editorTextFocus" },
{ "key": "6", "command": "extension.vim_6", "when": "editorTextFocus" },
{ "key": "7", "command": "extension.vim_7", "when": "editorTextFocus" },
{ "key": "8", "command": "extension.vim_8", "when": "editorTextFocus" },
{ "key": "9", "command": "extension.vim_9", "when": "editorTextFocus" },
{ "key": "Ctrl+H", "command": "cursorLeft", "when": "editorTextFocus" },
{ "key": "Ctrl+J", "command": "cursorDown", "when": "editorTextFocus" },
{ "key": "Ctrl+K", "command": "cursorUp", "when": "editorTextFocus" },
{ "key": "Ctrl+L", "command": "cursorRight", "when": "editorTextFocus" },
{ "key": "Ctrl+Shift+.", "command": "extension.showCmdLine", "when": "editorTextFocus" }
]
},
"scripts": {
@ -90,8 +91,14 @@
"gulp": "^3.9.0",
"gulp-mocha": "^2.2.0",
"gulp-shell": "^0.5.1",
"gulp-tsd": "0.0.4",
"gulp-tslint": "^3.6.0",
"gulp-typescript": "^2.9.2",
"tsd": "^0.6.5",
"typescript": "^1.6.2",
"vscode": "0.10.x"
},
"dependencies": {
"lodash": "^3.10.1"
}
}

View File

@ -1,5 +1,5 @@
export enum ModeName {
Normal,
Command,
Insert,
Visual,
}
@ -24,11 +24,16 @@ export abstract class Mode {
}
set IsActive(val : boolean) {
if (val !== this.isActive) {
this.isActive = val;
this.keyHistory = [];
}
this.isActive = val;
}
public HandleDeactivation() : void {
this.keyHistory = [];
}
abstract ShouldBeActivated(key : string, currentMode : ModeName) : boolean;
abstract HandleActivation(key : string) : void;
abstract HandleKeyEvent(key : string) : void;
}

View File

@ -1,18 +1,27 @@
import * as baseMode from './mode';
import {ModeName, Mode} from './mode';
import {showCmdLine} from './../cmd_line/main';
import * as vscode from 'vscode';
export default class CommandMode extends baseMode.Mode {
export default class CommandMode extends Mode {
constructor() {
super(baseMode.ModeName.Normal);
super(ModeName.Command);
}
ShouldBeActivated(key : string, currentMode : ModeName) : boolean {
return (key === 'esc');
}
HandleActivation(key : string) : void {
// do nothing
}
HandleKeyEvent(key : string) : void {
this.keyHistory.push(key);
var commands = vscode.commands.getCommands();
commands.then(c => console.log(c));
switch (key) {
case ':':
showCmdLine();
break;
case 'h':
vscode.commands.executeCommand("cursorLeft");
break;

View File

@ -1,23 +1,27 @@
import {window, StatusBarAlignment, StatusBarItem} from 'vscode';
import * as _ from 'lodash';
import * as vscode from 'vscode';
import {Mode, ModeName} from './mode';
import {showCmdLine} from './../cmd_line/main';
import CommandMode from './mode_command';
import InsertMode from './mode_insert';
import VisualMode from './mode_visual';
export default class ModeHandler {
private modes : Mode[];
private statusBarItem : StatusBarItem;
private statusBarItem : vscode.StatusBarItem;
constructor() {
this.modes = [
new CommandMode(),
new InsertMode(),
new VisualMode(),
];
this.SetCurrentModeByName(ModeName.Normal);
this.setCurrentModeByName(ModeName.Command);
}
public get CurrentMode() : Mode {
get currentMode() : Mode {
var currentMode = this.modes.find((mode, index) => {
return mode.IsActive;
});
@ -25,49 +29,44 @@ export default class ModeHandler {
return currentMode;
}
public SetCurrentModeByName(modeName : ModeName) {
setCurrentModeByName(modeName : ModeName) {
this.modes.forEach(mode => {
mode.IsActive = (mode.Name === modeName);
});
this.setupStatusBarItem(ModeName[modeName]);
var statusBarText = (this.currentMode.Name === ModeName.Command) ? '' : ModeName[modeName];
this.setupStatusBarItem(statusBarText.toUpperCase());
}
public HandleKeyEvent(key : string) : void {
var isHandled = false;
var currentModeName = this.CurrentMode.Name;
switch (currentModeName) {
case ModeName.Normal:
if (key === "i" || key === "a" || key === "I" || key === "A" || key === "o" || key === "O") {
this.SetCurrentModeByName(ModeName.Insert);
isHandled = true;
} else if (key === ":") {
showCmdLine();
isHandled = true;
}
break;
case ModeName.Insert:
if (key === "esc") {
this.SetCurrentModeByName(ModeName.Normal);
isHandled = true;
}
break;
case ModeName.Visual:
break;
handleKeyEvent(key : string) : void {
var currentModeName = this.currentMode.Name;
var nextMode : Mode;
var inactiveModes = _.filter(this.modes, (m) => !m.IsActive);
_.forEach(inactiveModes, (m, i) => {
if (m.ShouldBeActivated(key, currentModeName)) {
nextMode = m;
}
});
if (nextMode) {
this.currentMode.HandleDeactivation();
nextMode.HandleActivation(key);
this.setCurrentModeByName(nextMode.Name);
return;
}
if (!isHandled) {
this.CurrentMode.HandleKeyEvent(key);
}
this.currentMode.HandleKeyEvent(key);
}
private setupStatusBarItem(text : string) : void {
if (!this.statusBarItem) {
this.statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
}
this.statusBarItem.text = 'vim: ' + text;
this.statusBarItem.text = (text) ? '-- ' + text + ' --' : '';
this.statusBarItem.show();
}
}

View File

@ -1,11 +1,70 @@
import * as baseMode from './mode';
import {ModeName, Mode} from './mode';
import * as vscode from 'vscode';
export default class InsertMode extends baseMode.Mode {
export default class InsertMode extends Mode {
private activationKeyHandler : { [ key : string] : (position : vscode.Position) => vscode.Position; } = {};
constructor() {
super(baseMode.ModeName.Normal);
super(ModeName.Insert);
this.activationKeyHandler = {
// insert at cursor
"i" : (position) => { return position; },
// insert at the beginning of the line
"I" : (position) => { return new vscode.Position(position.line, 0); },
// append after the cursor
"a" : (position) => { return new vscode.Position(position.line, position.character + 1); },
// append at the end of the line
"A" : (position) => { return position; },
// open blank line below current line
"o" : (position) => {
vscode.commands.executeCommand("editor.action.insertLineAfter");
return new vscode.Position(position.line + 1, 0);
},
// open blank line above current line
"O" : (position) => {
vscode.commands.executeCommand("editor.action.insertLineBefore");
return new vscode.Position(position.line, 0);
}
};
}
ShouldBeActivated(key : string, currentMode : ModeName) : boolean {
return key in this.activationKeyHandler;
}
HandleActivation(key : string) : void {
const editor = vscode.window.activeTextEditor;
const currentPosition = editor.selection.active;
var newPosition = this.activationKeyHandler[key](currentPosition);
var newSelection = new vscode.Selection(newPosition, newPosition);
editor.selection = newSelection;
}
HandleKeyEvent(key : string) : void {
this.keyHistory.push(key);
const editor = vscode.window.activeTextEditor;
const position = editor.selection.active;
editor.edit(t => {
t.insert(position, this.Translate(key));
});
}
private Translate(raw : string) : string {
switch (raw) {
case 'space':
return ' ';
default:
return raw;
}
}
}

19
src/mode/mode_visual.ts Normal file
View File

@ -0,0 +1,19 @@
import {ModeName, Mode} from './mode';
export default class VisualMode extends Mode {
constructor() {
super(ModeName.Visual);
}
ShouldBeActivated(key : string, currentMode : ModeName) : boolean {
return (key === "v" || key === "V");
}
HandleActivation(key : string) : void {
// do nothing
}
HandleKeyEvent(key : string) : void {
this.keyHistory.push(key);
}
}

View File

@ -1,7 +1,7 @@
// The module 'assert' provides assertion methods from node
import * as assert from 'assert';
import * as lexer from '../src/cmd_line/lexer'
import {Token, TokenType} from '../src/cmd_line/token'
import * as lexer from '../src/cmd_line/lexer';
import {Token, TokenType} from '../src/cmd_line/token';
suite("Cmd line tests - lexing", () => {

View File

@ -0,0 +1,26 @@
import * as assert from 'assert';
import {ModeName} from '../../src/mode/mode';
import ModeHandler from '../../src/mode/mode_handler';
suite("Mode Handler", () => {
test("ctor", () => {
var modeHandler = new ModeHandler();
assert.equal(modeHandler.currentMode.Name, ModeName.Command);
assert.equal(modeHandler.currentMode.IsActive, true);
});
test("can set current mode", () => {
var modeHandler = new ModeHandler();
modeHandler.setCurrentModeByName(ModeName.Command);
assert.equal(modeHandler.currentMode.Name, ModeName.Command);
modeHandler.setCurrentModeByName(ModeName.Insert);
assert.equal(modeHandler.currentMode.Name, ModeName.Insert);
modeHandler.setCurrentModeByName(ModeName.Visual);
assert.equal(modeHandler.currentMode.Name, ModeName.Visual);
});
});

View File

@ -1,5 +1,4 @@
import * as assert from 'assert';
import * as myExtension from '../extension';
import * as parser from '../src/cmd_line/parser';
import * as node from '../src/cmd_line/node';
import * as token from '../src/cmd_line/token';

View File

@ -1,6 +1,5 @@
import * as assert from 'assert';
import * as myExtension from '../extension';
import * as lexerState from '../src/cmd_line/scanner'
import * as lexerState from '../src/cmd_line/scanner';
suite("Cmd line tests - lexer state", () => {
@ -25,7 +24,7 @@ suite("Cmd line tests - lexer state", () => {
var state = new lexerState.Scanner("dog");
assert.equal(state.next(), "d");
assert.equal(state.next(), "o");
assert.equal(state.next(), "g")
assert.equal(state.next(), "g");
assert.equal(state.next(), lexerState.Scanner.EOF);
});

View File

@ -2,7 +2,6 @@
import * as assert from 'assert';
import {commandParsers} from '../src/cmd_line/subparser';
import {WriteCommandArguments} from '../src/cmd_line/command_node';
suite("subparsers - :write args", () => {

12
tsd.json Normal file
View File

@ -0,0 +1,12 @@
{
"version": "v4",
"repo": "borisyankov/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"lodash/lodash.d.ts": {
"commit": "c560ba6f0f55ce6f50e9d06e37b5bc0e2b9117b0"
}
}
}

View File

@ -35,7 +35,7 @@
"no-string-literal": true,
"no-switch-case-fall-through": true,
"no-trailing-comma": true,
"no-trailing-whitespace": true,
"no-trailing-whitespace": false,
"no-unused-expression": true,
"no-unused-variable": true,
"no-unreachable": true,