Permalink
Browse files

Extracted from Stockholm Vim talk.

  • Loading branch information...
0 parents commit bccfb8c0ae16664e38a3ecdc62c27fdc919089e8 @georgebrock georgebrock committed Nov 29, 2012
@@ -0,0 +1,58 @@
+.vimulator {
+ width: 960px;
+ margin: 20px auto;
+ font-family: 'Arial', sans-serif;
+}
+.vimulator pre {
+ position: relative;
+ z-index: 2;
+ font-size: 3em;
+ line-height: 1.2;
+ height: 6em;
+ background: #ccc;
+}
+.vimulator .cursor {
+ background: #f00;
+ color: #fff;
+}
+.vimulator .insert .cursor {
+ background: transparent;
+ color: #000;
+ border-left: 5px solid #f00;
+ margin-left: -5px;
+ width: 0;
+}
+.vimulator ol {
+ position: relative;
+ z-index: 1;
+ font-size: 2em;
+ list-style: none;
+ line-height: 2em;
+ margin: 0;
+ padding: 0;
+}
+.vimulator ol li {
+ display: none;
+ margin-bottom: 1em;
+ -webkit-transition: opacity 0.5s linear;
+}
+.vimulator ol li:nth-child(1) { display: block; opacity: 1; }
+.vimulator ol li:nth-child(2) { display: block; opacity: 0.75; }
+.vimulator ol li:nth-child(3) { display: block; opacity: 0.5; }
+.vimulator ol li:nth-child(4) { display: block; opacity: 0.25; }
+.vimulator ol li:nth-child(5) { display: block; opacity: 0; }
+
+.vimulator ol kbd,
+.vimulator ol b {
+ font-family: monospace;
+ display: inline-block;
+ border: 1px solid #333;
+ width: 2em;
+ height: 2em;
+ text-align: center;
+ font-weight: bold;
+}
+.vimulator ol b {
+ border-style: dotted;
+ color: #fff;
+}
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang='en'>
+ <head>
+ <title>Vimulator</title>
+ <link rel='stylesheet' href='css/base.css'>
+ </head>
+ <body>
+ <div id='vimulator'>
+ <pre id='text'>This is some text
+That you might want to edit
+With the tiny, fake vim
+ Boom!</pre>
+ <ol id='commands'>
+ </ol>
+ </div>
+ <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'></script>
+ <script src='js/base.js'></script>
+ <script src='js/operation.js'></script>
+ <script src='js/command.js'></script>
+ <script src='js/command_list.js'></script>
+ <script src='js/normal_mode.js'></script>
+ <script src='js/normal_mode/motions.js'></script>
+ <script src='js/normal_mode/insertion.js'></script>
+ <script src='js/normal_mode/edits.js'></script>
+ <script src='js/normal_mode/line_search.js'></script>
+ <script src='js/normal_mode/operators.js'></script>
+ <script src='js/insert_mode.js'></script>
+ <script src='js/words.js'></script>
+ <script src='js/demo.js'></script>
+ <script>
+ window.vimulator = new Vimulator.Demo('#vimulator');
+ </script>
+ </body>
+</html>
@@ -0,0 +1,150 @@
+(function () {
+ window.Vimulator = {};
+
+ Vimulator.Base = function () {
+ this.init.apply(this, arguments);
+ };
+
+ Vimulator.Base.prototype.init = function (container) {
+ this.modes = {
+ normal: new Vimulator.NormalMode(this),
+ insert: new Vimulator.InsertMode(this)
+ };
+
+ this.container = $(container).addClass('vimulator');
+ this.textContainer = this.container.find('pre');
+ this.commandList = this.container.find('ol');
+ this.bindKeyListeners();
+
+ this.setMode("normal");
+ this.cursor = {row: 0, col: 0};
+ this.lines = this.textContainer.text().split('\n');
+
+ this.render();
+ };
+
+ Vimulator.Base.prototype.setMode = function (name) {
+ this.mode = this.modes[name];
+ if (!this.mode) {
+ throw new Error("Illegal mode");
+ }
+ };
+
+ Vimulator.Base.prototype.bindKeyListeners = function () {
+ var vim = this;
+
+ // Use keyup for special characters like escape
+ $(window).keyup(function (e) {
+ if (e.keyCode === 27) {
+ vim.keyPress(e.keyCode);
+ return false;
+ }
+ });
+
+ // Use keypress for general characters
+ $(window).keypress(function (e) {
+ vim.keyPress(e.keyCode);
+ return false;
+ });
+ };
+
+ Vimulator.Base.prototype.keyPress = function (code) {
+ var chr, op;
+ chr = String.fromCharCode(code);
+ op = this.mode.keyPress(chr);
+ this.render(op);
+ };
+
+ Vimulator.Base.prototype.render = function (op) {
+ var vim, rendered;
+
+ vim = this;
+ rendered = jQuery.map(this.lines, function (line, i) {
+ var chr;
+
+ if (i == vim.cursor.row) {
+ chr = line.substr(vim.cursor.col, 1) || ' ';
+ return line.substr(0, vim.cursor.col) +
+ '<mark class="cursor">' + chr + '</mark>' +
+ line.substr(vim.cursor.col + 1);
+ } else {
+ return line;
+ }
+ });
+
+ this.textContainer
+ .html(rendered.join('\n'))
+ .attr('class', this.mode.name);
+ };
+
+ Vimulator.Base.prototype.moveCursorRow = function (row) {
+ this.cursor.row = row;
+ if (row === '$' || this.cursor.row >= this.lines.length) {
+ this.cursor.row = this.lines.length - 1;
+ }
+ if (this.cursor.row < 0) {
+ this.cursor.row = 0;
+ }
+ };
+ Vimulator.Base.prototype.moveCursorCol = function (col) {
+ this.cursor.col = col;
+ if (col === '$' || this.cursor.col >= this.lines[this.cursor.row].length) {
+ this.cursor.col = this.lines[this.cursor.row].length - 1;
+ }
+ if (this.cursor.col < 0) {
+ this.cursor.col = 0;
+ }
+ };
+ Vimulator.Base.prototype.moveCursor = function(row, col) {
+ this.moveCursorRow(row);
+ this.moveCursorCol(col);
+ };
+ Vimulator.Base.prototype.moveCursorRelative = function(rows, cols) {
+ var row, col;
+ if (rows === '$') { row = '$'; } else { row = this.cursor.row + rows; }
+ if (cols === '$') { col = '$'; } else { col = this.cursor.col + cols; }
+ return this.moveCursor(row, col);
+ };
+
+ Vimulator.Base.prototype.currentLine = function () {
+ return this.lines[this.cursor.row];
+ };
+
+ Vimulator.Base.prototype.appendText = function (text) {
+ line = this.currentLine();
+ this.lines[this.cursor.row] =
+ line.substr(0, this.cursor.col) +
+ text +
+ line.substr(this.cursor.col);
+ this.cursor.col += text.length;
+ };
+
+ Vimulator.Base.prototype.insertRowBelow = function (text, index) {
+ index = index || this.cursor.row;
+ this.lines.splice(index + 1, 0, text);
+ };
+ Vimulator.Base.prototype.insertRowAbove = function (text, index) {
+ index = index || this.cursor.row;
+ this.lines.splice(index, 0, text);
+ };
+ Vimulator.Base.prototype.replaceRow = function (text, index) {
+ index = (typeof index === "undefined" ? this.cursor.row : index);
+ this.lines[index] = text;
+ };
+ Vimulator.Base.prototype.removeRows = function (start, end) {
+ this.lines.splice(start, end - start);
+ };
+
+ Vimulator.Base.prototype.removeRange = function(start, end) {
+ if (start.row > end.row || start.row == end.row && start.col > end.col) {
+ return this.removeRange(end, start);
+ }
+
+ this.lines[start.row] =
+ this.lines[start.row].substr(0, start.col) +
+ this.lines[end.row].substr(end.col);
+
+ this.lines.splice(start.row + 1, end.row - start.row);
+ this.moveCursor(start.row, start.col);
+ };
+}());
@@ -0,0 +1,25 @@
+(function () {
+ Vimulator.Command = function (options) {
+ this.argType = options.argument || "none";
+ this.callback = options.callback;
+ this.subCommands = options.subCommands;
+ this.defaultCount = "defaultCount" in options ? options.defaultCount : 1;
+ };
+
+ Vimulator.Command.prototype.wantsOperation = function () {
+ return this.argType === "operation";
+ };
+
+ Vimulator.Command.prototype.wantsLiteral = function () {
+ return this.argType === "literal";
+ };
+
+ Vimulator.Command.prototype.getCommand = function (key) {
+ return this.subCommands.getCommand(key);
+ };
+
+ Vimulator.Command.prototype.execute = function (vim, count, argument) {
+ count = count === null ? this.defaultCount : count;
+ this.callback(vim, count, argument);
+ };
+}());
@@ -0,0 +1,28 @@
+(function () {
+ Vimulator.CommandList = function () {
+ var k, sources, source, commands;
+
+ sources = Array.prototype.slice.call(arguments);
+ commands = sources.pop();
+
+ while (sources.length > 0) {
+ source = sources.pop();
+ (function () {
+ var constructor = function () {};
+ constructor.prototype = commands;
+ commands = new constructor();
+ for (k in source) {
+ if (source.hasOwnProperty(k)) {
+ commands[k] = source[k];
+ }
+ }
+ }());
+ }
+
+ this.commands = commands;
+ };
+
+ Vimulator.CommandList.prototype.getCommand = function (key) {
+ return this.commands[key];
+ };
+}());
@@ -0,0 +1,26 @@
+(function () {
+ Vimulator.Demo = function () {
+ this.init.apply(this, arguments);
+ };
+ Vimulator.Demo.prototype = new Vimulator.Base();
+ Vimulator.Demo.prototype.delay = 500;
+
+ Vimulator.Demo.prototype.render = function (op) {
+ var vim, li;
+
+ vim = this;
+ setTimeout(function () {
+ Vimulator.Base.prototype.render.apply(vim);
+ }, this.delay);
+
+ if (op) {
+ li = this.commandList.find("li:first");
+ if (li.length === 0 || li.hasClass("complete")) {
+ li = $("<li></li>").prependTo(this.commandList);
+ }
+
+ li.html(op.description())
+ .toggleClass("complete", op.complete());
+ }
+ };
+}());
@@ -0,0 +1,16 @@
+(function () {
+ Vimulator.InsertMode = function (vim) {
+ this.name = "insert";
+ this.vim = vim;
+ };
+
+ Vimulator.InsertMode.prototype.keyPress = function (key) {
+ // Escape
+ if (key === '\u001B') {
+ this.vim.setMode("normal");
+ this.vim.moveCursorRelative(0, -1);
+ } else {
+ this.vim.appendText(key);
+ }
+ };
+}());
@@ -0,0 +1,40 @@
+(function () {
+ Vimulator.NormalMode = function (vim) {
+ this.name = "normal";
+ this.vim = vim;
+ this.buildOperation();
+ };
+
+ Vimulator.NormalMode.prototype.buildOperation = function () {
+ this.currentOperation = new Vimulator.Operation(this.commandList());
+ };
+
+ Vimulator.NormalMode.prototype.commandList = function () {
+ this.commands = this.commands || new Vimulator.CommandList(
+ Vimulator.NormalMode.Motions,
+ Vimulator.NormalMode.Insertion,
+ Vimulator.NormalMode.Edits,
+ Vimulator.NormalMode.LineSearch,
+ Vimulator.NormalMode.Operators
+ );
+ return this.commands;
+ };
+
+ Vimulator.NormalMode.prototype.keyPress = function (key) {
+ var op = this.currentOperation;
+
+ // Escape
+ if (key === '\u001B') {
+ this.buildOperation();
+ } else {
+ op.keyPress(key);
+
+ if (op.complete()) {
+ op.execute(this.vim);
+ this.buildOperation();
+ }
+ }
+
+ return op;
+ };
+}());
Oops, something went wrong.

0 comments on commit bccfb8c

Please sign in to comment.