diff --git a/demo/vim.html b/demo/vim.html
index 6a33a6c075b5c23987f545f7475d8ffddc248edc..46c4581e6161bfd3ea40abf5555d1162cc62c33b 100644
--- a/demo/vim.html
+++ b/demo/vim.html
@@ -47,17 +47,16 @@ int getchar(void)
 </textarea></form>
 <div style="font-size: 13px; width: 300px; height: 30px;">Key buffer: <span id="command-display"></span></div>
 
-<p>The vim keybindings are enabled by
-including <a href="../keymap/vim.js">keymap/vim.js</a> and setting
-the <code>vimMode</code> option to <code>true</code>. This will also
-automatically change the <code>keyMap</code> option to <code>"vim"</code>.</p>
+<p>The vim keybindings are enabled by including <code><a
+href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
+<code>keyMap</code> option to <code>vim</code>.</p>
 
 <p><strong>Features</strong></p>
 
 <ul>
   <li>All common motions and operators, including text objects</li>
   <li>Operator motion orthogonality</li>
-  <li>Visual mode - characterwise, linewise, partial support for blockwise</li>
+  <li>Visual mode - characterwise, linewise, blockwise</li>
   <li>Full macro support (q, @)</li>
   <li>Incremental highlighted search (/, ?, #, *, g#, g*)</li>
   <li>Search/replace with confirm (:substitute, :%s)</li>
@@ -71,9 +70,13 @@ automatically change the <code>keyMap</code> option to <code>"vim"</code>.</p>
   <li>Cross-buffer yank/paste</li>
 </ul>
 
-<p>Note that while the vim mode tries to emulate the most useful features of
-vim as faithfully as possible, it does not strive to become a complete vim
-implementation</p>
+<p>For the full list of key mappings and Ex commands, refer to the
+<code>defaultKeymap</code> and <code>defaultExCommandMap</code> at the
+top of <code><a href="../keymap/vim.js">keymap/vim.js</a></code>.
+
+<p>Note that while the vim mode tries to emulate the most useful
+features of vim as faithfully as possible, it does not strive to
+become a complete vim implementation</p>
 
     <script>
       CodeMirror.commands.save = function(){ alert("Saving"); };
diff --git a/doc/manual.html b/doc/manual.html
index 187a14ce5af377f1bb5428640be58d0bf1669457..faf49b3ecb1c46ae3a66228318881713b49fd6e3 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -55,6 +55,12 @@
     </li>
     <li><a href="#addons">Addons</a></li>
     <li><a href="#modeapi">Writing CodeMirror Modes</a></li>
+    <li><a href="#vimapi">Vim Mode API</a>
+      <ul>
+        <li><a href="#vimapi_configuration">Configuration</a></li>
+        <li><a href="#vimapi_extending">Extending VIM</a></li>
+      </ul>
+    </li>
   </ul>
 </div>
 
@@ -3156,6 +3162,129 @@ editor.setOption("extraKeys", {
     looked up through <a href="#getMode"><code>getMode</code></a>.</p>
 </section>
 
+<section id="vimapi">
+    <h2>VIM Mode API</h2>
+
+    <p>CodeMirror has a robust VIM mode that attempts to faithfully
+    emulate VIM's most useful features. It can be enabled by
+    including <a href="../keymap/vim.js"><code>keymap/vim.js</code>
+    </a> and setting the <code>keymap</code> option to
+    <code>vim</code>.</p>
+
+    <h3 id="vimapi_configuration">Configuration</h3>
+
+    <p>VIM mode accepts configuration options for customizing
+    behavior at run time. These methods can be called at any time
+    and will affect all existing CodeMirror instances unless
+    specified otherwise. The methods are exposed on the
+    <code><strong>CodeMirror.Vim</strong></code> object.</p>
+
+    <dl>
+      <dt id="vimapi_setOption"><code><strong>setOption(name: string, value: any, ?cm: CodeMirror, ?cfg: object)</strong></code></dt>
+      <dd>Sets the value of a VIM option. <code>name</code> should
+      be the name of an option. If <code>cfg.scope</code> is not set
+      and <code>cm</code> is provided, then sets the global and
+      instance values of the option. Otherwise, sets either the
+      global or instance value of the option depending on whether
+      <code>cfg.scope</code> is <code>global</code> or
+      <code>local</code>.</dd>
+      <dt id="vimapi_getOption"><code><strong>getOption(name: string, ?cm: CodeMirror: ?cfg: object)</strong></code></dt>
+      <dd>Gets the current value of a VIM option. If
+      <code>cfg.scope</code> is not set and <code>cm</code> is
+      provided, then gets the instance value of the option, falling
+      back to the global value if not set. If <code>cfg.scope</code> is provided, then gets the <code>global</code> or
+      <code>local</code> value without checking the other.</dd>
+
+      <dt id="vimapi_map"><code><strong>map(lhs: string, rhs: string, ?context: string)</strong></code></dt>
+      <dd>Maps a key sequence to another key sequence. Implements
+      VIM's <code>:map</code> command. To map ; to : in VIM would be
+      <code><strong>:map ; :</strong></code>. That would translate to
+      <code><strong>CodeMirror.Vim.map(';', ':');</strong></code>.
+      The <code>context</code> can be <code>normal</code>,
+      <code>visual</code>, or <code>insert</code>, which correspond
+      to <code>:nmap</code>, <code>:vmap</code>, and
+      <code>:imap</code>
+      respectively.</dd>
+
+      <dt id="vimapi_mapCommand"><code><strong>mapCommand(keys: string, type: string, name: string, ?args: object, ?extra: object)</strong></code></dt>
+      <dd>Maps a key sequence to a <code>motion</code>,
+      <code>operator</code>, or <code>action</code> type command.
+      The args object is passed through to the command when it is
+      invoked by the provided key sequence.
+      <code>extras.context</code> can be <code>normal</code>,
+      <code>visual</code>, or <code>insert</code>, to map the key
+      sequence only in the corresponding mode.
+      <code>extras.isEdit</code> is applicable only to actions,
+      determining whether it is recorded for replay for the
+      <code>.</code> single-repeat command.
+    </dl>
+
+    <h3 id="vimapi_extending">Extending VIM</h3>
+
+    <p>CodeMirror's VIM mode implements a large subset of VIM's core
+    editing functionality. But since there's always more to be
+    desired, there is a set of APIs for extending VIM's
+    functionality. As with the configuration API, the methods are
+    exposed on <code><strong>CodeMirror.Vim</strong></code> and may
+    be called at any time.</p>
+
+    <dl>
+      <dt id="vimapi_defineOption"><code><strong>defineOption(name: string, default: any, type: string, ?aliases: array&lt;string&gt;, ?callback: function (?value: any, ?cm: CodeMirror) → ?any)</strong></code></dt>
+      <dd>Defines a VIM style option and makes it available to the
+      <code>:set</code> command. Type can be <code>boolean</code> or
+      <code>string</code>, used for validation and by
+      <code>:set</code> to determine which syntax to accept. If a
+      <code>callback</code> is passed in, VIM does not store the value of the
+      option itself, but instead uses the callback as a setter/getter. If the
+      first argument to the callback is <code>undefined</code>, then the
+      callback should return the value of the option. Otherwise, it should set
+      instead. Since VIM options have global and instance values, whether a
+      <code>CodeMirror</code> instance is passed in denotes whether the global
+      or local value should be used. Consequently, it's possible for the
+      callback to be called twice for a single <code>setOption</code> or
+      <code>getOption</code> call. Note that right now, VIM does not support
+      defining buffer-local options that do not have global values. If an
+      option should not have a global value, either always ignore the
+      <code>cm</code> parameter in the callback, or always pass in a
+      <code>cfg.scope</code> to <code>setOption</code> and
+      <code>getOption</code>.</dd>
+
+      <dt id="vimapi_defineMotion"><code><strong>defineMotion(name: string, fn: function(cm: CodeMirror, head: {line, ch}, ?motionArgs: object}) → {line, ch})</strong></code></dt>
+      <dd>Defines a motion command for VIM. The motion should return
+      the desired result position of the cursor. <code>head</code>
+      is the current position of the cursor. It can differ from
+      <code>cm.getCursor('head')</code> if VIM is in visual mode.
+      <code>motionArgs</code> is the object passed into
+      <strong><code>mapCommand()</code></strong>.</dd>
+
+      <dt id="vimapi_defineOperator"><strong><code>defineOperator(name: string, fn: function(cm: CodeMirror, ?operatorArgs: object, ranges: array&lt;{anchor, head}&gt;) → ?{line, ch})</code></strong></dt>
+      <dd>Defines an operator command, similar to <strong><code>
+      defineMotion</code></strong>. <code>ranges</code> is the range
+      of text the operator should operate on. If the cursor should
+      be set to a certain position after the operation finishes, it
+      can return a cursor object.</dd>
+
+      <dt id="vimapi_defineActon"><strong><code>defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))</strong></code></dt>
+      <dd>Defines an action command, similar to
+      <strong><code>defineMotion</code></strong>. Action commands
+      can have arbitrary behavior, making them more flexible than
+      motions and operators, at the loss of orthogonality.</dd>
+
+      <dt id="vimapi_defineEx"><strong><code>defineEx(name: string, ?prefix: string, fn: function(cm: CodeMirror, ?params: object))</code></strong></dt>
+      <dd>Defines an Ex command, and maps it to <code>:name</code>.
+      If a prefix is provided, it, and any prefixed substring of the
+      <code>name</code> beginning with the <code>prefix</code> can
+      be used to invoke the command. <code>params.argString</code>
+      contains the part of the prompted string after the command
+      name. <code>params.args</code> is <code>params.argString</code>
+      split by whitespace. If the command was prefixed with a
+      <code><strong><a href="http://vimdoc.sourceforge.net/htmldoc/cmdline.html#cmdline-ranges">line range</a></strong></code>,
+      <code>params.line</code> and <code>params.lineEnd</code> will
+      be set.
+    </dl>
+
+</section>
+
 </article>
 
 <script>setTimeout(function(){CodeMirror.colorize();}, 20);</script>
diff --git a/keymap/vim.js b/keymap/vim.js
index 45f883b213a4fcd8eae17dd31c9b189d42233274..2ede2167b669084d1856f1e175ffa76bdbce24a1 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -3,42 +3,16 @@
 
 /**
  * Supported keybindings:
+ *   Too many to list. Refer to defaultKeyMap below.
  *
- *   Motion:
- *   h, j, k, l
- *   gj, gk
- *   e, E, w, W, b, B, ge, gE
- *   f<character>, F<character>, t<character>, T<character>
- *   $, ^, 0, -, +, _
- *   gg, G
- *   %
- *   '<character>, `<character>
- *
- *   Operator:
- *   d, y, c
- *   dd, yy, cc
- *   g~, g~g~
- *   >, <, >>, <<
- *
- *   Operator-Motion:
- *   x, X, D, Y, C, ~
- *
- *   Action:
- *   a, i, s, A, I, S, o, O
- *   zz, z., z<CR>, zt, zb, z-
- *   J
- *   u, Ctrl-r
- *   m<character>
- *   r<character>
- *
- *   Modes:
- *   ESC - leave insert mode, visual mode, and clear input state.
- *   Ctrl-[, Ctrl-c - same as ESC.
+ * Supported Ex commands:
+ *   Refer to defaultExCommandMap below.
  *
  * Registers: unnamed, -, a-z, A-Z, 0-9
  *   (Does not respect the special case for number registers when delete
  *    operator is made with these commands: %, (, ),  , /, ?, n, N, {, } )
  *   TODO: Implement the remaining registers.
+ *
  * Marks: a-z, A-Z, and 0-9
  *   TODO: Implement the remaining special marks. They have more complex
  *       behavior.
@@ -57,6 +31,7 @@
  *  6. Motion, operator, and action implementations
  *  7. Helper functions for the key handler, motions, operators, and actions
  *  8. Set up Vim to work as a keymap for CodeMirror.
+ *  9. Ex command implementations.
  */
 
 (function(mod) {
@@ -227,6 +202,34 @@
     { keys: ':', type: 'ex' }
   ];
 
+  /**
+   * Ex commands
+   * Care must be taken when adding to the default Ex command map. For any
+   * pair of commands that have a shared prefix, at least one of their
+   * shortNames must not match the prefix of the other command.
+   */
+  var defaultExCommandMap = [
+    { name: 'colorscheme', shortName: 'colo' },
+    { name: 'map' },
+    { name: 'imap', shortName: 'im' },
+    { name: 'nmap', shortName: 'nm' },
+    { name: 'vmap', shortName: 'vm' },
+    { name: 'unmap' },
+    { name: 'write', shortName: 'w' },
+    { name: 'undo', shortName: 'u' },
+    { name: 'redo', shortName: 'red' },
+    { name: 'set', shortName: 'se' },
+    { name: 'set', shortName: 'se' },
+    { name: 'setlocal', shortName: 'setl' },
+    { name: 'setglobal', shortName: 'setg' },
+    { name: 'sort', shortName: 'sor' },
+    { name: 'substitute', shortName: 's', possiblyAsync: true },
+    { name: 'nohlsearch', shortName: 'noh' },
+    { name: 'delmarks', shortName: 'delm' },
+    { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
+    { name: 'global', shortName: 'g' }
+  ];
+
   var Pos = CodeMirror.Pos;
 
   var Vim = function() {
@@ -3913,31 +3916,6 @@
       return {top: from.line, bottom: to.line};
     }
 
-    // Ex command handling
-    // Care must be taken when adding to the default Ex command map. For any
-    // pair of commands that have a shared prefix, at least one of their
-    // shortNames must not match the prefix of the other command.
-    var defaultExCommandMap = [
-      { name: 'colorscheme', shortName: 'colo' },
-      { name: 'map' },
-      { name: 'imap', shortName: 'im' },
-      { name: 'nmap', shortName: 'nm' },
-      { name: 'vmap', shortName: 'vm' },
-      { name: 'unmap' },
-      { name: 'write', shortName: 'w' },
-      { name: 'undo', shortName: 'u' },
-      { name: 'redo', shortName: 'red' },
-      { name: 'set', shortName: 'se' },
-      { name: 'set', shortName: 'se' },
-      { name: 'setlocal', shortName: 'setl' },
-      { name: 'setglobal', shortName: 'setg' },
-      { name: 'sort', shortName: 'sor' },
-      { name: 'substitute', shortName: 's', possiblyAsync: true },
-      { name: 'nohlsearch', shortName: 'noh' },
-      { name: 'delmarks', shortName: 'delm' },
-      { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
-      { name: 'global', shortName: 'g' }
-    ];
     var ExCommandDispatcher = function() {
       this.buildCommandMap_();
     };