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<string>, ?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<{anchor, head}>) → ?{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_(); };