source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/applications/admin/static/codemirror/keymap/vim.js

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 188.9 KB
Line 
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4/**
5 * Supported keybindings:
6 *
7 *   Motion:
8 *   h, j, k, l
9 *   gj, gk
10 *   e, E, w, W, b, B, ge, gE
11 *   f<character>, F<character>, t<character>, T<character>
12 *   $, ^, 0, -, +, _
13 *   gg, G
14 *   %
15 *   '<character>, `<character>
16 *
17 *   Operator:
18 *   d, y, c
19 *   dd, yy, cc
20 *   g~, g~g~
21 *   >, <, >>, <<
22 *
23 *   Operator-Motion:
24 *   x, X, D, Y, C, ~
25 *
26 *   Action:
27 *   a, i, s, A, I, S, o, O
28 *   zz, z., z<CR>, zt, zb, z-
29 *   J
30 *   u, Ctrl-r
31 *   m<character>
32 *   r<character>
33 *
34 *   Modes:
35 *   ESC - leave insert mode, visual mode, and clear input state.
36 *   Ctrl-[, Ctrl-c - same as ESC.
37 *
38 * Registers: unnamed, -, a-z, A-Z, 0-9
39 *   (Does not respect the special case for number registers when delete
40 *    operator is made with these commands: %, (, ),  , /, ?, n, N, {, } )
41 *   TODO: Implement the remaining registers.
42 * Marks: a-z, A-Z, and 0-9
43 *   TODO: Implement the remaining special marks. They have more complex
44 *       behavior.
45 *
46 * Events:
47 *  'vim-mode-change' - raised on the editor anytime the current mode changes,
48 *                      Event object: {mode: "visual", subMode: "linewise"}
49 *
50 * Code structure:
51 *  1. Default keymap
52 *  2. Variable declarations and short basic helpers
53 *  3. Instance (External API) implementation
54 *  4. Internal state tracking objects (input state, counter) implementation
55 *     and instanstiation
56 *  5. Key handler (the main command dispatcher) implementation
57 *  6. Motion, operator, and action implementations
58 *  7. Helper functions for the key handler, motions, operators, and actions
59 *  8. Set up Vim to work as a keymap for CodeMirror.
60 */
61
62(function(mod) {
63  if (typeof exports == "object" && typeof module == "object") // CommonJS
64    mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/dialog/dialog"), require("../addon/edit/matchbrackets.js"));
65  else if (typeof define == "function" && define.amd) // AMD
66    define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog", "../addon/edit/matchbrackets"], mod);
67  else // Plain browser env
68    mod(CodeMirror);
69})(function(CodeMirror) {
70  'use strict';
71
72  var defaultKeymap = [
73    // Key to key mapping. This goes first to make it possible to override
74    // existing mappings.
75    { keys: '<Left>', type: 'keyToKey', toKeys: 'h' },
76    { keys: '<Right>', type: 'keyToKey', toKeys: 'l' },
77    { keys: '<Up>', type: 'keyToKey', toKeys: 'k' },
78    { keys: '<Down>', type: 'keyToKey', toKeys: 'j' },
79    { keys: '<Space>', type: 'keyToKey', toKeys: 'l' },
80    { keys: '<BS>', type: 'keyToKey', toKeys: 'h', context: 'normal'},
81    { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },
82    { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B', context: 'normal' },
83    { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },
84    { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b', context: 'normal' },
85    { keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },
86    { keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },
87    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },
88    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },
89    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
90    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
91    { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },
92    { keys: 's', type: 'keyToKey', toKeys: 'xi', context: 'visual'},
93    { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },
94    { keys: 'S', type: 'keyToKey', toKeys: 'dcc', context: 'visual' },
95    { keys: '<Home>', type: 'keyToKey', toKeys: '0' },
96    { keys: '<End>', type: 'keyToKey', toKeys: '$' },
97    { keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },
98    { keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },
99    { keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
100    // Motions
101    { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},
102    { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},
103    { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},
104    { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},
105    { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},
106    { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},
107    { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},
108    { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},
109    { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},
110    { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},
111    { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},
112    { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},
113    { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},
114    { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},
115    { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},
116    { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},
117    { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},
118    { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},
119    { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},
120    { keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},
121    { keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},
122    { keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},
123    { keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},
124    { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
125    { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
126    { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },
127    { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },
128    { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},
129    { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},
130    { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
131    { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},
132    { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},
133    { keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},
134    { keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},
135    { keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},
136    { keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},
137    { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},
138    { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},
139    { keys: '\'<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},
140    { keys: '`<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},
141    { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
142    { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
143    { keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
144    { keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
145    // the next two aren't motions but must come before more general motion declarations
146    { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},
147    { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},
148    { keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},
149    { keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},
150    { keys: '|', type: 'motion', motion: 'moveToColumn'},
151    { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},
152    { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
153    // Operators
154    { keys: 'd', type: 'operator', operator: 'delete' },
155    { keys: 'y', type: 'operator', operator: 'yank' },
156    { keys: 'c', type: 'operator', operator: 'change' },
157    { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},
158    { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},
159    { keys: 'g~', type: 'operator', operator: 'changeCase' },
160    { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },
161    { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
162    { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
163    { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
164    // Operator-Motion dual commands
165    { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
166    { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
167    { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
168    { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},
169    { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
170    { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},
171    { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
172    { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},
173    { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},
174    { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},
175    { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
176    // Actions
177    { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
178    { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},
179    { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},
180    { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},
181    { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
182    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
183    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
184    { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
185    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },
186    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
187    { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
188    { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
189    { keys: 'v', type: 'action', action: 'toggleVisualMode' },
190    { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},
191    { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
192    { keys: 'gv', type: 'action', action: 'reselectLastSelection' },
193    { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
194    { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},
195    { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},
196    { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },
197    { keys: '@<character>', type: 'action', action: 'replayMacro' },
198    { keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },
199    // Handle Replace-mode as a special case of insert mode.
200    { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
201    { keys: 'u', type: 'action', action: 'undo', context: 'normal' },
202    { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },
203    { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },
204    { keys: '<C-r>', type: 'action', action: 'redo' },
205    { keys: 'm<character>', type: 'action', action: 'setMark' },
206    { keys: '"<character>', type: 'action', action: 'setRegister' },
207    { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},
208    { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
209    { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},
210    { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
211    { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},
212    { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
213    { keys: '.', type: 'action', action: 'repeatLastEdit' },
214    { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},
215    { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},
216    // Text object motions
217    { keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },
218    { keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},
219    // Search
220    { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
221    { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
222    { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
223    { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
224    { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
225    { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
226    // Ex command
227    { keys: ':', type: 'ex' }
228  ];
229
230  var Pos = CodeMirror.Pos;
231
232  var Vim = function() {
233    function enterVimMode(cm) {
234      cm.setOption('disableInput', true);
235      cm.setOption('showCursorWhenSelecting', false);
236      CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
237      cm.on('cursorActivity', onCursorActivity);
238      maybeInitVimState(cm);
239      CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
240    }
241
242    function leaveVimMode(cm) {
243      cm.setOption('disableInput', false);
244      cm.off('cursorActivity', onCursorActivity);
245      CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
246      cm.state.vim = null;
247    }
248
249    function detachVimMap(cm, next) {
250      if (this == CodeMirror.keyMap.vim)
251        CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
252
253      if (!next || next.attach != attachVimMap)
254        leaveVimMode(cm, false);
255    }
256    function attachVimMap(cm, prev) {
257      if (this == CodeMirror.keyMap.vim)
258        CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
259
260      if (!prev || prev.attach != attachVimMap)
261        enterVimMode(cm);
262    }
263
264    // Deprecated, simply setting the keymap works again.
265    CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
266      if (val && cm.getOption("keyMap") != "vim")
267        cm.setOption("keyMap", "vim");
268      else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))
269        cm.setOption("keyMap", "default");
270    });
271
272    function cmKey(key, cm) {
273      if (!cm) { return undefined; }
274      var vimKey = cmKeyToVimKey(key);
275      if (!vimKey) {
276        return false;
277      }
278      var cmd = CodeMirror.Vim.findKey(cm, vimKey);
279      if (typeof cmd == 'function') {
280        CodeMirror.signal(cm, 'vim-keypress', vimKey);
281      }
282      return cmd;
283    }
284
285    var modifiers = {'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'};
286    var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del'};
287    function cmKeyToVimKey(key) {
288      if (key.charAt(0) == '\'') {
289        // Keypress character binding of format "'a'"
290        return key.charAt(1);
291      }
292      var pieces = key.split('-');
293      if (/-$/.test(key)) {
294        // If the - key was typed, split will result in 2 extra empty strings
295        // in the array. Replace them with 1 '-'.
296        pieces.splice(-2, 2, '-');
297      }
298      var lastPiece = pieces[pieces.length - 1];
299      if (pieces.length == 1 && pieces[0].length == 1) {
300        // No-modifier bindings use literal character bindings above. Skip.
301        return false;
302      } else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {
303        // Ignore Shift+char bindings as they should be handled by literal character.
304        return false;
305      }
306      var hasCharacter = false;
307      for (var i = 0; i < pieces.length; i++) {
308        var piece = pieces[i];
309        if (piece in modifiers) { pieces[i] = modifiers[piece]; }
310        else { hasCharacter = true; }
311        if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }
312      }
313      if (!hasCharacter) {
314        // Vim does not support modifier only keys.
315        return false;
316      }
317      // TODO: Current bindings expect the character to be lowercase, but
318      // it looks like vim key notation uses uppercase.
319      if (isUpperCase(lastPiece)) {
320        pieces[pieces.length - 1] = lastPiece.toLowerCase();
321      }
322      return '<' + pieces.join('-') + '>';
323    }
324
325    function getOnPasteFn(cm) {
326      var vim = cm.state.vim;
327      if (!vim.onPasteFn) {
328        vim.onPasteFn = function() {
329          if (!vim.insertMode) {
330            cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
331            actions.enterInsertMode(cm, {}, vim);
332          }
333        };
334      }
335      return vim.onPasteFn;
336    }
337
338    var numberRegex = /[\d]/;
339    var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)];
340    function makeKeyRange(start, size) {
341      var keys = [];
342      for (var i = start; i < start + size; i++) {
343        keys.push(String.fromCharCode(i));
344      }
345      return keys;
346    }
347    var upperCaseAlphabet = makeKeyRange(65, 26);
348    var lowerCaseAlphabet = makeKeyRange(97, 26);
349    var numbers = makeKeyRange(48, 10);
350    var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
351    var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
352
353    function isLine(cm, line) {
354      return line >= cm.firstLine() && line <= cm.lastLine();
355    }
356    function isLowerCase(k) {
357      return (/^[a-z]$/).test(k);
358    }
359    function isMatchableSymbol(k) {
360      return '()[]{}'.indexOf(k) != -1;
361    }
362    function isNumber(k) {
363      return numberRegex.test(k);
364    }
365    function isUpperCase(k) {
366      return (/^[A-Z]$/).test(k);
367    }
368    function isWhiteSpaceString(k) {
369      return (/^\s*$/).test(k);
370    }
371    function inArray(val, arr) {
372      for (var i = 0; i < arr.length; i++) {
373        if (arr[i] == val) {
374          return true;
375        }
376      }
377      return false;
378    }
379
380    var options = {};
381    function defineOption(name, defaultValue, type) {
382      if (defaultValue === undefined) { throw Error('defaultValue is required'); }
383      if (!type) { type = 'string'; }
384      options[name] = {
385        type: type,
386        defaultValue: defaultValue
387      };
388      setOption(name, defaultValue);
389    }
390
391    function setOption(name, value) {
392      var option = options[name];
393      if (!option) {
394        throw Error('Unknown option: ' + name);
395      }
396      if (option.type == 'boolean') {
397        if (value && value !== true) {
398          throw Error('Invalid argument: ' + name + '=' + value);
399        } else if (value !== false) {
400          // Boolean options are set to true if value is not defined.
401          value = true;
402        }
403      }
404      option.value = option.type == 'boolean' ? !!value : value;
405    }
406
407    function getOption(name) {
408      var option = options[name];
409      if (!option) {
410        throw Error('Unknown option: ' + name);
411      }
412      return option.value;
413    }
414
415    var createCircularJumpList = function() {
416      var size = 100;
417      var pointer = -1;
418      var head = 0;
419      var tail = 0;
420      var buffer = new Array(size);
421      function add(cm, oldCur, newCur) {
422        var current = pointer % size;
423        var curMark = buffer[current];
424        function useNextSlot(cursor) {
425          var next = ++pointer % size;
426          var trashMark = buffer[next];
427          if (trashMark) {
428            trashMark.clear();
429          }
430          buffer[next] = cm.setBookmark(cursor);
431        }
432        if (curMark) {
433          var markPos = curMark.find();
434          // avoid recording redundant cursor position
435          if (markPos && !cursorEqual(markPos, oldCur)) {
436            useNextSlot(oldCur);
437          }
438        } else {
439          useNextSlot(oldCur);
440        }
441        useNextSlot(newCur);
442        head = pointer;
443        tail = pointer - size + 1;
444        if (tail < 0) {
445          tail = 0;
446        }
447      }
448      function move(cm, offset) {
449        pointer += offset;
450        if (pointer > head) {
451          pointer = head;
452        } else if (pointer < tail) {
453          pointer = tail;
454        }
455        var mark = buffer[(size + pointer) % size];
456        // skip marks that are temporarily removed from text buffer
457        if (mark && !mark.find()) {
458          var inc = offset > 0 ? 1 : -1;
459          var newCur;
460          var oldCur = cm.getCursor();
461          do {
462            pointer += inc;
463            mark = buffer[(size + pointer) % size];
464            // skip marks that are the same as current position
465            if (mark &&
466                (newCur = mark.find()) &&
467                !cursorEqual(oldCur, newCur)) {
468              break;
469            }
470          } while (pointer < head && pointer > tail);
471        }
472        return mark;
473      }
474      return {
475        cachedCursor: undefined, //used for # and * jumps
476        add: add,
477        move: move
478      };
479    };
480
481    // Returns an object to track the changes associated insert mode.  It
482    // clones the object that is passed in, or creates an empty object one if
483    // none is provided.
484    var createInsertModeChanges = function(c) {
485      if (c) {
486        // Copy construction
487        return {
488          changes: c.changes,
489          expectCursorActivityForChange: c.expectCursorActivityForChange
490        };
491      }
492      return {
493        // Change list
494        changes: [],
495        // Set to true on change, false on cursorActivity.
496        expectCursorActivityForChange: false
497      };
498    };
499
500    function MacroModeState() {
501      this.latestRegister = undefined;
502      this.isPlaying = false;
503      this.isRecording = false;
504      this.replaySearchQueries = [];
505      this.onRecordingDone = undefined;
506      this.lastInsertModeChanges = createInsertModeChanges();
507    }
508    MacroModeState.prototype = {
509      exitMacroRecordMode: function() {
510        var macroModeState = vimGlobalState.macroModeState;
511        if (macroModeState.onRecordingDone) {
512          macroModeState.onRecordingDone(); // close dialog
513        }
514        macroModeState.onRecordingDone = undefined;
515        macroModeState.isRecording = false;
516      },
517      enterMacroRecordMode: function(cm, registerName) {
518        var register =
519            vimGlobalState.registerController.getRegister(registerName);
520        if (register) {
521          register.clear();
522          this.latestRegister = registerName;
523          if (cm.openDialog) {
524            this.onRecordingDone = cm.openDialog(
525                '(recording)['+registerName+']', null, {bottom:true});
526          }
527          this.isRecording = true;
528        }
529      }
530    };
531
532    function maybeInitVimState(cm) {
533      if (!cm.state.vim) {
534        // Store instance state in the CodeMirror object.
535        cm.state.vim = {
536          inputState: new InputState(),
537          // Vim's input state that triggered the last edit, used to repeat
538          // motions and operators with '.'.
539          lastEditInputState: undefined,
540          // Vim's action command before the last edit, used to repeat actions
541          // with '.' and insert mode repeat.
542          lastEditActionCommand: undefined,
543          // When using jk for navigation, if you move from a longer line to a
544          // shorter line, the cursor may clip to the end of the shorter line.
545          // If j is pressed again and cursor goes to the next line, the
546          // cursor should go back to its horizontal position on the longer
547          // line if it can. This is to keep track of the horizontal position.
548          lastHPos: -1,
549          // Doing the same with screen-position for gj/gk
550          lastHSPos: -1,
551          // The last motion command run. Cleared if a non-motion command gets
552          // executed in between.
553          lastMotion: null,
554          marks: {},
555          // Mark for rendering fake cursor for visual mode.
556          fakeCursor: null,
557          insertMode: false,
558          // Repeat count for changes made in insert mode, triggered by key
559          // sequences like 3,i. Only exists when insertMode is true.
560          insertModeRepeat: undefined,
561          visualMode: false,
562          // If we are in visual line mode. No effect if visualMode is false.
563          visualLine: false,
564          visualBlock: false,
565          lastSelection: null,
566          lastPastedText: null,
567          sel: {
568          }
569        };
570      }
571      return cm.state.vim;
572    }
573    var vimGlobalState;
574    function resetVimGlobalState() {
575      vimGlobalState = {
576        // The current search query.
577        searchQuery: null,
578        // Whether we are searching backwards.
579        searchIsReversed: false,
580        // Replace part of the last substituted pattern
581        lastSubstituteReplacePart: undefined,
582        jumpList: createCircularJumpList(),
583        macroModeState: new MacroModeState,
584        // Recording latest f, t, F or T motion command.
585        lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
586        registerController: new RegisterController({}),
587        // search history buffer
588        searchHistoryController: new HistoryController({}),
589        // ex Command history buffer
590        exCommandHistoryController : new HistoryController({})
591      };
592      for (var optionName in options) {
593        var option = options[optionName];
594        option.value = option.defaultValue;
595      }
596    }
597
598    var lastInsertModeKeyTimer;
599    var vimApi= {
600      buildKeyMap: function() {
601        // TODO: Convert keymap into dictionary format for fast lookup.
602      },
603      // Testing hook, though it might be useful to expose the register
604      // controller anyways.
605      getRegisterController: function() {
606        return vimGlobalState.registerController;
607      },
608      // Testing hook.
609      resetVimGlobalState_: resetVimGlobalState,
610
611      // Testing hook.
612      getVimGlobalState_: function() {
613        return vimGlobalState;
614      },
615
616      // Testing hook.
617      maybeInitVimState_: maybeInitVimState,
618
619      suppressErrorLogging: false,
620
621      InsertModeKey: InsertModeKey,
622      map: function(lhs, rhs, ctx) {
623        // Add user defined key bindings.
624        exCommandDispatcher.map(lhs, rhs, ctx);
625      },
626      setOption: setOption,
627      getOption: getOption,
628      defineOption: defineOption,
629      defineEx: function(name, prefix, func){
630        if (name.indexOf(prefix) !== 0) {
631          throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');
632        }
633        exCommands[name]=func;
634        exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
635      },
636      handleKey: function (cm, key, origin) {
637        var command = this.findKey(cm, key, origin);
638        if (typeof command === 'function') {
639          return command();
640        }
641      },
642      /**
643       * This is the outermost function called by CodeMirror, after keys have
644       * been mapped to their Vim equivalents.
645       *
646       * Finds a command based on the key (and cached keys if there is a
647       * multi-key sequence). Returns `undefined` if no key is matched, a noop
648       * function if a partial match is found (multi-key), and a function to
649       * execute the bound command if a a key is matched. The function always
650       * returns true.
651       */
652      findKey: function(cm, key, origin) {
653        var vim = maybeInitVimState(cm);
654        function handleMacroRecording() {
655          var macroModeState = vimGlobalState.macroModeState;
656          if (macroModeState.isRecording) {
657            if (key == 'q') {
658              macroModeState.exitMacroRecordMode();
659              clearInputState(cm);
660              return true;
661            }
662            if (origin != 'mapping') {
663              logKey(macroModeState, key);
664            }
665          }
666        }
667        function handleEsc() {
668          if (key == '<Esc>') {
669            // Clear input state and get back to normal mode.
670            clearInputState(cm);
671            if (vim.visualMode) {
672              exitVisualMode(cm);
673            } else if (vim.insertMode) {
674              exitInsertMode(cm);
675            }
676            return true;
677          }
678        }
679        function doKeyToKey(keys) {
680          // TODO: prevent infinite recursion.
681          var match;
682          while (keys) {
683            // Pull off one command key, which is either a single character
684            // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
685            match = (/<\w+-.+?>|<\w+>|./).exec(keys);
686            key = match[0];
687            keys = keys.substring(match.index + key.length);
688            CodeMirror.Vim.handleKey(cm, key, 'mapping');
689          }
690        }
691
692        function handleKeyInsertMode() {
693          if (handleEsc()) { return true; }
694          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
695          var keysAreChars = key.length == 1;
696          var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
697          // Need to check all key substrings in insert mode.
698          while (keys.length > 1 && match.type != 'full') {
699            var keys = vim.inputState.keyBuffer = keys.slice(1);
700            var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
701            if (thisMatch.type != 'none') { match = thisMatch; }
702          }
703          if (match.type == 'none') { clearInputState(cm); return false; }
704          else if (match.type == 'partial') {
705            if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
706            lastInsertModeKeyTimer = window.setTimeout(
707              function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },
708              getOption('insertModeEscKeysTimeout'));
709            return !keysAreChars;
710          }
711
712          if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
713          if (keysAreChars) {
714            var here = cm.getCursor();
715            cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
716          }
717          clearInputState(cm);
718          return match.command;
719        }
720
721        function handleKeyNonInsertMode() {
722          if (handleMacroRecording() || handleEsc()) { return true; };
723
724          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
725          if (/^[1-9]\d*$/.test(keys)) { return true; }
726
727          var keysMatcher = /^(\d*)(.*)$/.exec(keys);
728          if (!keysMatcher) { clearInputState(cm); return false; }
729          var context = vim.visualMode ? 'visual' :
730                                         'normal';
731          var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context);
732          if (match.type == 'none') { clearInputState(cm); return false; }
733          else if (match.type == 'partial') { return true; }
734
735          vim.inputState.keyBuffer = '';
736          var keysMatcher = /^(\d*)(.*)$/.exec(keys);
737          if (keysMatcher[1] && keysMatcher[1] != '0') {
738            vim.inputState.pushRepeatDigit(keysMatcher[1]);
739          }
740          return match.command;
741        }
742
743        var command;
744        if (vim.insertMode) { command = handleKeyInsertMode(); }
745        else { command = handleKeyNonInsertMode(); }
746        if (command === false) {
747          return undefined;
748        } else if (command === true) {
749          // TODO: Look into using CodeMirror's multi-key handling.
750          // Return no-op since we are caching the key. Counts as handled, but
751          // don't want act on it just yet.
752          return function() {};
753        } else {
754          return function() {
755            return cm.operation(function() {
756              cm.curOp.isVimOp = true;
757              try {
758                if (command.type == 'keyToKey') {
759                  doKeyToKey(command.toKeys);
760                } else {
761                  commandDispatcher.processCommand(cm, vim, command);
762                }
763              } catch (e) {
764                // clear VIM state in case it's in a bad state.
765                cm.state.vim = undefined;
766                maybeInitVimState(cm);
767                if (!CodeMirror.Vim.suppressErrorLogging) {
768                  console['log'](e);
769                }
770                throw e;
771              }
772              return true;
773            });
774          };
775        }
776      },
777      handleEx: function(cm, input) {
778        exCommandDispatcher.processCommand(cm, input);
779      },
780
781      defineMotion: defineMotion,
782      defineAction: defineAction,
783      defineOperator: defineOperator,
784      mapCommand: mapCommand,
785      _mapCommand: _mapCommand,
786
787      exitVisualMode: exitVisualMode,
788      exitInsertMode: exitInsertMode
789    };
790
791    // Represents the current input state.
792    function InputState() {
793      this.prefixRepeat = [];
794      this.motionRepeat = [];
795
796      this.operator = null;
797      this.operatorArgs = null;
798      this.motion = null;
799      this.motionArgs = null;
800      this.keyBuffer = []; // For matching multi-key commands.
801      this.registerName = null; // Defaults to the unnamed register.
802    }
803    InputState.prototype.pushRepeatDigit = function(n) {
804      if (!this.operator) {
805        this.prefixRepeat = this.prefixRepeat.concat(n);
806      } else {
807        this.motionRepeat = this.motionRepeat.concat(n);
808      }
809    };
810    InputState.prototype.getRepeat = function() {
811      var repeat = 0;
812      if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
813        repeat = 1;
814        if (this.prefixRepeat.length > 0) {
815          repeat *= parseInt(this.prefixRepeat.join(''), 10);
816        }
817        if (this.motionRepeat.length > 0) {
818          repeat *= parseInt(this.motionRepeat.join(''), 10);
819        }
820      }
821      return repeat;
822    };
823
824    function clearInputState(cm, reason) {
825      cm.state.vim.inputState = new InputState();
826      CodeMirror.signal(cm, 'vim-command-done', reason);
827    }
828
829    /*
830     * Register stores information about copy and paste registers.  Besides
831     * text, a register must store whether it is linewise (i.e., when it is
832     * pasted, should it insert itself into a new line, or should the text be
833     * inserted at the cursor position.)
834     */
835    function Register(text, linewise, blockwise) {
836      this.clear();
837      this.keyBuffer = [text || ''];
838      this.insertModeChanges = [];
839      this.searchQueries = [];
840      this.linewise = !!linewise;
841      this.blockwise = !!blockwise;
842    }
843    Register.prototype = {
844      setText: function(text, linewise, blockwise) {
845        this.keyBuffer = [text || ''];
846        this.linewise = !!linewise;
847        this.blockwise = !!blockwise;
848      },
849      pushText: function(text, linewise) {
850        // if this register has ever been set to linewise, use linewise.
851        if (linewise) {
852          if (!this.linewise) {
853            this.keyBuffer.push('\n');
854          }
855          this.linewise = true;
856        }
857        this.keyBuffer.push(text);
858      },
859      pushInsertModeChanges: function(changes) {
860        this.insertModeChanges.push(createInsertModeChanges(changes));
861      },
862      pushSearchQuery: function(query) {
863        this.searchQueries.push(query);
864      },
865      clear: function() {
866        this.keyBuffer = [];
867        this.insertModeChanges = [];
868        this.searchQueries = [];
869        this.linewise = false;
870      },
871      toString: function() {
872        return this.keyBuffer.join('');
873      }
874    };
875
876    /*
877     * vim registers allow you to keep many independent copy and paste buffers.
878     * See http://usevim.com/2012/04/13/registers/ for an introduction.
879     *
880     * RegisterController keeps the state of all the registers.  An initial
881     * state may be passed in.  The unnamed register '"' will always be
882     * overridden.
883     */
884    function RegisterController(registers) {
885      this.registers = registers;
886      this.unnamedRegister = registers['"'] = new Register();
887      registers['.'] = new Register();
888      registers[':'] = new Register();
889      registers['/'] = new Register();
890    }
891    RegisterController.prototype = {
892      pushText: function(registerName, operator, text, linewise, blockwise) {
893        if (linewise && text.charAt(0) == '\n') {
894          text = text.slice(1) + '\n';
895        }
896        if (linewise && text.charAt(text.length - 1) !== '\n'){
897          text += '\n';
898        }
899        // Lowercase and uppercase registers refer to the same register.
900        // Uppercase just means append.
901        var register = this.isValidRegister(registerName) ?
902            this.getRegister(registerName) : null;
903        // if no register/an invalid register was specified, things go to the
904        // default registers
905        if (!register) {
906          switch (operator) {
907            case 'yank':
908              // The 0 register contains the text from the most recent yank.
909              this.registers['0'] = new Register(text, linewise, blockwise);
910              break;
911            case 'delete':
912            case 'change':
913              if (text.indexOf('\n') == -1) {
914                // Delete less than 1 line. Update the small delete register.
915                this.registers['-'] = new Register(text, linewise);
916              } else {
917                // Shift down the contents of the numbered registers and put the
918                // deleted text into register 1.
919                this.shiftNumericRegisters_();
920                this.registers['1'] = new Register(text, linewise);
921              }
922              break;
923          }
924          // Make sure the unnamed register is set to what just happened
925          this.unnamedRegister.setText(text, linewise, blockwise);
926          return;
927        }
928
929        // If we've gotten to this point, we've actually specified a register
930        var append = isUpperCase(registerName);
931        if (append) {
932          register.pushText(text, linewise);
933        } else {
934          register.setText(text, linewise, blockwise);
935        }
936        // The unnamed register always has the same value as the last used
937        // register.
938        this.unnamedRegister.setText(register.toString(), linewise);
939      },
940      // Gets the register named @name.  If one of @name doesn't already exist,
941      // create it.  If @name is invalid, return the unnamedRegister.
942      getRegister: function(name) {
943        if (!this.isValidRegister(name)) {
944          return this.unnamedRegister;
945        }
946        name = name.toLowerCase();
947        if (!this.registers[name]) {
948          this.registers[name] = new Register();
949        }
950        return this.registers[name];
951      },
952      isValidRegister: function(name) {
953        return name && inArray(name, validRegisters);
954      },
955      shiftNumericRegisters_: function() {
956        for (var i = 9; i >= 2; i--) {
957          this.registers[i] = this.getRegister('' + (i - 1));
958        }
959      }
960    };
961    function HistoryController() {
962        this.historyBuffer = [];
963        this.iterator;
964        this.initialPrefix = null;
965    }
966    HistoryController.prototype = {
967      // the input argument here acts a user entered prefix for a small time
968      // until we start autocompletion in which case it is the autocompleted.
969      nextMatch: function (input, up) {
970        var historyBuffer = this.historyBuffer;
971        var dir = up ? -1 : 1;
972        if (this.initialPrefix === null) this.initialPrefix = input;
973        for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {
974          var element = historyBuffer[i];
975          for (var j = 0; j <= element.length; j++) {
976            if (this.initialPrefix == element.substring(0, j)) {
977              this.iterator = i;
978              return element;
979            }
980          }
981        }
982        // should return the user input in case we reach the end of buffer.
983        if (i >= historyBuffer.length) {
984          this.iterator = historyBuffer.length;
985          return this.initialPrefix;
986        }
987        // return the last autocompleted query or exCommand as it is.
988        if (i < 0 ) return input;
989      },
990      pushInput: function(input) {
991        var index = this.historyBuffer.indexOf(input);
992        if (index > -1) this.historyBuffer.splice(index, 1);
993        if (input.length) this.historyBuffer.push(input);
994      },
995      reset: function() {
996        this.initialPrefix = null;
997        this.iterator = this.historyBuffer.length;
998      }
999    };
1000    var commandDispatcher = {
1001      matchCommand: function(keys, keyMap, inputState, context) {
1002        var matches = commandMatches(keys, keyMap, context, inputState);
1003        if (!matches.full && !matches.partial) {
1004          return {type: 'none'};
1005        } else if (!matches.full && matches.partial) {
1006          return {type: 'partial'};
1007        }
1008
1009        var bestMatch;
1010        for (var i = 0; i < matches.full.length; i++) {
1011          var match = matches.full[i];
1012          if (!bestMatch) {
1013            bestMatch = match;
1014          }
1015        }
1016        if (bestMatch.keys.slice(-11) == '<character>') {
1017          inputState.selectedCharacter = lastChar(keys);
1018        }
1019        return {type: 'full', command: bestMatch};
1020      },
1021      processCommand: function(cm, vim, command) {
1022        vim.inputState.repeatOverride = command.repeatOverride;
1023        switch (command.type) {
1024          case 'motion':
1025            this.processMotion(cm, vim, command);
1026            break;
1027          case 'operator':
1028            this.processOperator(cm, vim, command);
1029            break;
1030          case 'operatorMotion':
1031            this.processOperatorMotion(cm, vim, command);
1032            break;
1033          case 'action':
1034            this.processAction(cm, vim, command);
1035            break;
1036          case 'search':
1037            this.processSearch(cm, vim, command);
1038            clearInputState(cm);
1039            break;
1040          case 'ex':
1041          case 'keyToEx':
1042            this.processEx(cm, vim, command);
1043            clearInputState(cm);
1044            break;
1045          default:
1046            break;
1047        }
1048      },
1049      processMotion: function(cm, vim, command) {
1050        vim.inputState.motion = command.motion;
1051        vim.inputState.motionArgs = copyArgs(command.motionArgs);
1052        this.evalInput(cm, vim);
1053      },
1054      processOperator: function(cm, vim, command) {
1055        var inputState = vim.inputState;
1056        if (inputState.operator) {
1057          if (inputState.operator == command.operator) {
1058            // Typing an operator twice like 'dd' makes the operator operate
1059            // linewise
1060            inputState.motion = 'expandToLine';
1061            inputState.motionArgs = { linewise: true };
1062            this.evalInput(cm, vim);
1063            return;
1064          } else {
1065            // 2 different operators in a row doesn't make sense.
1066            clearInputState(cm);
1067          }
1068        }
1069        inputState.operator = command.operator;
1070        inputState.operatorArgs = copyArgs(command.operatorArgs);
1071        if (vim.visualMode) {
1072          // Operating on a selection in visual mode. We don't need a motion.
1073          this.evalInput(cm, vim);
1074        }
1075      },
1076      processOperatorMotion: function(cm, vim, command) {
1077        var visualMode = vim.visualMode;
1078        var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
1079        if (operatorMotionArgs) {
1080          // Operator motions may have special behavior in visual mode.
1081          if (visualMode && operatorMotionArgs.visualLine) {
1082            vim.visualLine = true;
1083          }
1084        }
1085        this.processOperator(cm, vim, command);
1086        if (!visualMode) {
1087          this.processMotion(cm, vim, command);
1088        }
1089      },
1090      processAction: function(cm, vim, command) {
1091        var inputState = vim.inputState;
1092        var repeat = inputState.getRepeat();
1093        var repeatIsExplicit = !!repeat;
1094        var actionArgs = copyArgs(command.actionArgs) || {};
1095        if (inputState.selectedCharacter) {
1096          actionArgs.selectedCharacter = inputState.selectedCharacter;
1097        }
1098        // Actions may or may not have motions and operators. Do these first.
1099        if (command.operator) {
1100          this.processOperator(cm, vim, command);
1101        }
1102        if (command.motion) {
1103          this.processMotion(cm, vim, command);
1104        }
1105        if (command.motion || command.operator) {
1106          this.evalInput(cm, vim);
1107        }
1108        actionArgs.repeat = repeat || 1;
1109        actionArgs.repeatIsExplicit = repeatIsExplicit;
1110        actionArgs.registerName = inputState.registerName;
1111        clearInputState(cm);
1112        vim.lastMotion = null;
1113        if (command.isEdit) {
1114          this.recordLastEdit(vim, inputState, command);
1115        }
1116        actions[command.action](cm, actionArgs, vim);
1117      },
1118      processSearch: function(cm, vim, command) {
1119        if (!cm.getSearchCursor) {
1120          // Search depends on SearchCursor.
1121          return;
1122        }
1123        var forward = command.searchArgs.forward;
1124        var wholeWordOnly = command.searchArgs.wholeWordOnly;
1125        getSearchState(cm).setReversed(!forward);
1126        var promptPrefix = (forward) ? '/' : '?';
1127        var originalQuery = getSearchState(cm).getQuery();
1128        var originalScrollPos = cm.getScrollInfo();
1129        function handleQuery(query, ignoreCase, smartCase) {
1130          vimGlobalState.searchHistoryController.pushInput(query);
1131          vimGlobalState.searchHistoryController.reset();
1132          try {
1133            updateSearchQuery(cm, query, ignoreCase, smartCase);
1134          } catch (e) {
1135            showConfirm(cm, 'Invalid regex: ' + query);
1136            return;
1137          }
1138          commandDispatcher.processMotion(cm, vim, {
1139            type: 'motion',
1140            motion: 'findNext',
1141            motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
1142          });
1143        }
1144        function onPromptClose(query) {
1145          cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
1146          handleQuery(query, true /** ignoreCase */, true /** smartCase */);
1147          var macroModeState = vimGlobalState.macroModeState;
1148          if (macroModeState.isRecording) {
1149            logSearchQuery(macroModeState, query);
1150          }
1151        }
1152        function onPromptKeyUp(e, query, close) {
1153          var keyName = CodeMirror.keyName(e), up;
1154          if (keyName == 'Up' || keyName == 'Down') {
1155            up = keyName == 'Up' ? true : false;
1156            query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
1157            close(query);
1158          } else {
1159            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
1160              vimGlobalState.searchHistoryController.reset();
1161          }
1162          var parsedQuery;
1163          try {
1164            parsedQuery = updateSearchQuery(cm, query,
1165                true /** ignoreCase */, true /** smartCase */);
1166          } catch (e) {
1167            // Swallow bad regexes for incremental search.
1168          }
1169          if (parsedQuery) {
1170            cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
1171          } else {
1172            clearSearchHighlight(cm);
1173            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
1174          }
1175        }
1176        function onPromptKeyDown(e, query, close) {
1177          var keyName = CodeMirror.keyName(e);
1178          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
1179            vimGlobalState.searchHistoryController.pushInput(query);
1180            vimGlobalState.searchHistoryController.reset();
1181            updateSearchQuery(cm, originalQuery);
1182            clearSearchHighlight(cm);
1183            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
1184            CodeMirror.e_stop(e);
1185            close();
1186            cm.focus();
1187          }
1188        }
1189        switch (command.searchArgs.querySrc) {
1190          case 'prompt':
1191            var macroModeState = vimGlobalState.macroModeState;
1192            if (macroModeState.isPlaying) {
1193              var query = macroModeState.replaySearchQueries.shift();
1194              handleQuery(query, true /** ignoreCase */, false /** smartCase */);
1195            } else {
1196              showPrompt(cm, {
1197                  onClose: onPromptClose,
1198                  prefix: promptPrefix,
1199                  desc: searchPromptDesc,
1200                  onKeyUp: onPromptKeyUp,
1201                  onKeyDown: onPromptKeyDown
1202              });
1203            }
1204            break;
1205          case 'wordUnderCursor':
1206            var word = expandWordUnderCursor(cm, false /** inclusive */,
1207                true /** forward */, false /** bigWord */,
1208                true /** noSymbol */);
1209            var isKeyword = true;
1210            if (!word) {
1211              word = expandWordUnderCursor(cm, false /** inclusive */,
1212                  true /** forward */, false /** bigWord */,
1213                  false /** noSymbol */);
1214              isKeyword = false;
1215            }
1216            if (!word) {
1217              return;
1218            }
1219            var query = cm.getLine(word.start.line).substring(word.start.ch,
1220                word.end.ch);
1221            if (isKeyword && wholeWordOnly) {
1222                query = '\\b' + query + '\\b';
1223            } else {
1224              query = escapeRegex(query);
1225            }
1226
1227            // cachedCursor is used to save the old position of the cursor
1228            // when * or # causes vim to seek for the nearest word and shift
1229            // the cursor before entering the motion.
1230            vimGlobalState.jumpList.cachedCursor = cm.getCursor();
1231            cm.setCursor(word.start);
1232
1233            handleQuery(query, true /** ignoreCase */, false /** smartCase */);
1234            break;
1235        }
1236      },
1237      processEx: function(cm, vim, command) {
1238        function onPromptClose(input) {
1239          // Give the prompt some time to close so that if processCommand shows
1240          // an error, the elements don't overlap.
1241          vimGlobalState.exCommandHistoryController.pushInput(input);
1242          vimGlobalState.exCommandHistoryController.reset();
1243          exCommandDispatcher.processCommand(cm, input);
1244        }
1245        function onPromptKeyDown(e, input, close) {
1246          var keyName = CodeMirror.keyName(e), up;
1247          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
1248            vimGlobalState.exCommandHistoryController.pushInput(input);
1249            vimGlobalState.exCommandHistoryController.reset();
1250            CodeMirror.e_stop(e);
1251            close();
1252            cm.focus();
1253          }
1254          if (keyName == 'Up' || keyName == 'Down') {
1255            up = keyName == 'Up' ? true : false;
1256            input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
1257            close(input);
1258          } else {
1259            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
1260              vimGlobalState.exCommandHistoryController.reset();
1261          }
1262        }
1263        if (command.type == 'keyToEx') {
1264          // Handle user defined Ex to Ex mappings
1265          exCommandDispatcher.processCommand(cm, command.exArgs.input);
1266        } else {
1267          if (vim.visualMode) {
1268            showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
1269                onKeyDown: onPromptKeyDown});
1270          } else {
1271            showPrompt(cm, { onClose: onPromptClose, prefix: ':',
1272                onKeyDown: onPromptKeyDown});
1273          }
1274        }
1275      },
1276      evalInput: function(cm, vim) {
1277        // If the motion comand is set, execute both the operator and motion.
1278        // Otherwise return.
1279        var inputState = vim.inputState;
1280        var motion = inputState.motion;
1281        var motionArgs = inputState.motionArgs || {};
1282        var operator = inputState.operator;
1283        var operatorArgs = inputState.operatorArgs || {};
1284        var registerName = inputState.registerName;
1285        var sel = vim.sel;
1286        // TODO: Make sure cm and vim selections are identical outside visual mode.
1287        var origHead = copyCursor(vim.visualMode ? sel.head: cm.getCursor('head'));
1288        var origAnchor = copyCursor(vim.visualMode ? sel.anchor : cm.getCursor('anchor'));
1289        var oldHead = copyCursor(origHead);
1290        var oldAnchor = copyCursor(origAnchor);
1291        var newHead, newAnchor;
1292        var repeat;
1293        if (operator) {
1294          this.recordLastEdit(vim, inputState);
1295        }
1296        if (inputState.repeatOverride !== undefined) {
1297          // If repeatOverride is specified, that takes precedence over the
1298          // input state's repeat. Used by Ex mode and can be user defined.
1299          repeat = inputState.repeatOverride;
1300        } else {
1301          repeat = inputState.getRepeat();
1302        }
1303        if (repeat > 0 && motionArgs.explicitRepeat) {
1304          motionArgs.repeatIsExplicit = true;
1305        } else if (motionArgs.noRepeat ||
1306            (!motionArgs.explicitRepeat && repeat === 0)) {
1307          repeat = 1;
1308          motionArgs.repeatIsExplicit = false;
1309        }
1310        if (inputState.selectedCharacter) {
1311          // If there is a character input, stick it in all of the arg arrays.
1312          motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
1313              inputState.selectedCharacter;
1314        }
1315        motionArgs.repeat = repeat;
1316        clearInputState(cm);
1317        if (motion) {
1318          var motionResult = motions[motion](cm, origHead, motionArgs, vim);
1319          vim.lastMotion = motions[motion];
1320          if (!motionResult) {
1321            return;
1322          }
1323          if (motionArgs.toJumplist) {
1324            var jumpList = vimGlobalState.jumpList;
1325            // if the current motion is # or *, use cachedCursor
1326            var cachedCursor = jumpList.cachedCursor;
1327            if (cachedCursor) {
1328              recordJumpPosition(cm, cachedCursor, motionResult);
1329              delete jumpList.cachedCursor;
1330            } else {
1331              recordJumpPosition(cm, origHead, motionResult);
1332            }
1333          }
1334          if (motionResult instanceof Array) {
1335            newAnchor = motionResult[0];
1336            newHead = motionResult[1];
1337          } else {
1338            newHead = motionResult;
1339          }
1340          // TODO: Handle null returns from motion commands better.
1341          if (!newHead) {
1342            newHead = copyCursor(origHead);
1343          }
1344          if (vim.visualMode) {
1345            if (!(vim.visualBlock && newHead.ch === Infinity)) {
1346              newHead = clipCursorToContent(cm, newHead, vim.visualBlock);
1347            }
1348            if (newAnchor) {
1349              newAnchor = clipCursorToContent(cm, newAnchor, true);
1350            }
1351            newAnchor = newAnchor || oldAnchor;
1352            sel.anchor = newAnchor;
1353            sel.head = newHead;
1354            updateCmSelection(cm);
1355            updateMark(cm, vim, '<',
1356                cursorIsBefore(newAnchor, newHead) ? newAnchor
1357                    : newHead);
1358            updateMark(cm, vim, '>',
1359                cursorIsBefore(newAnchor, newHead) ? newHead
1360                    : newAnchor);
1361          } else if (!operator) {
1362            newHead = clipCursorToContent(cm, newHead);
1363            cm.setCursor(newHead.line, newHead.ch);
1364          }
1365        }
1366        if (operator) {
1367          if (operatorArgs.lastSel) {
1368            // Replaying a visual mode operation
1369            newAnchor = oldAnchor;
1370            var lastSel = operatorArgs.lastSel;
1371            var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
1372            var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
1373            if (lastSel.visualLine) {
1374              // Linewise Visual mode: The same number of lines.
1375              newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
1376            } else if (lastSel.visualBlock) {
1377              // Blockwise Visual mode: The same number of lines and columns.
1378              newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
1379            } else if (lastSel.head.line == lastSel.anchor.line) {
1380              // Normal Visual mode within one line: The same number of characters.
1381              newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset);
1382            } else {
1383              // Normal Visual mode with several lines: The same number of lines, in the
1384              // last line the same number of characters as in the last line the last time.
1385              newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
1386            }
1387            vim.visualMode = true;
1388            vim.visualLine = lastSel.visualLine;
1389            vim.visualBlock = lastSel.visualBlock;
1390            sel = vim.sel = {
1391              anchor: newAnchor,
1392              head: newHead
1393            };
1394            updateCmSelection(cm);
1395          } else if (vim.visualMode) {
1396            operatorArgs.lastSel = {
1397              anchor: copyCursor(sel.anchor),
1398              head: copyCursor(sel.head),
1399              visualBlock: vim.visualBlock,
1400              visualLine: vim.visualLine
1401            };
1402          }
1403          var curStart, curEnd, linewise, mode;
1404          var cmSel;
1405          if (vim.visualMode) {
1406            // Init visual op
1407            curStart = cursorMin(sel.head, sel.anchor);
1408            curEnd = cursorMax(sel.head, sel.anchor);
1409            linewise = vim.visualLine || operatorArgs.linewise;
1410            mode = vim.visualBlock ? 'block' :
1411                   linewise ? 'line' :
1412                   'char';
1413            cmSel = makeCmSelection(cm, {
1414              anchor: curStart,
1415              head: curEnd
1416            }, mode);
1417            if (linewise) {
1418              var ranges = cmSel.ranges;
1419              if (mode == 'block') {
1420                // Linewise operators in visual block mode extend to end of line
1421                for (var i = 0; i < ranges.length; i++) {
1422                  ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
1423                }
1424              } else if (mode == 'line') {
1425                ranges[0].head = Pos(ranges[0].head.line + 1, 0);
1426              }
1427            }
1428          } else {
1429            // Init motion op
1430            curStart = copyCursor(newAnchor || oldAnchor);
1431            curEnd = copyCursor(newHead || oldHead);
1432            if (cursorIsBefore(curEnd, curStart)) {
1433              var tmp = curStart;
1434              curStart = curEnd;
1435              curEnd = tmp;
1436            }
1437            linewise = motionArgs.linewise || operatorArgs.linewise;
1438            if (linewise) {
1439              // Expand selection to entire line.
1440              expandSelectionToLine(cm, curStart, curEnd);
1441            } else if (motionArgs.forward) {
1442              // Clip to trailing newlines only if the motion goes forward.
1443              clipToLine(cm, curStart, curEnd);
1444            }
1445            mode = 'char';
1446            var exclusive = !motionArgs.inclusive || linewise;
1447            cmSel = makeCmSelection(cm, {
1448              anchor: curStart,
1449              head: curEnd
1450            }, mode, exclusive);
1451          }
1452          cm.setSelections(cmSel.ranges, cmSel.primary);
1453          vim.lastMotion = null;
1454          operatorArgs.repeat = repeat; // For indent in visual mode.
1455          operatorArgs.registerName = registerName;
1456          // Keep track of linewise as it affects how paste and change behave.
1457          operatorArgs.linewise = linewise;
1458          var operatorMoveTo = operators[operator](
1459            cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
1460          if (vim.visualMode) {
1461            exitVisualMode(cm, operatorMoveTo != null);
1462          }
1463          if (operatorMoveTo) {
1464            cm.setCursor(operatorMoveTo);
1465          }
1466        }
1467      },
1468      recordLastEdit: function(vim, inputState, actionCommand) {
1469        var macroModeState = vimGlobalState.macroModeState;
1470        if (macroModeState.isPlaying) { return; }
1471        vim.lastEditInputState = inputState;
1472        vim.lastEditActionCommand = actionCommand;
1473        macroModeState.lastInsertModeChanges.changes = [];
1474        macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
1475      }
1476    };
1477
1478    /**
1479     * typedef {Object{line:number,ch:number}} Cursor An object containing the
1480     *     position of the cursor.
1481     */
1482    // All of the functions below return Cursor objects.
1483    var motions = {
1484      moveToTopLine: function(cm, _head, motionArgs) {
1485        var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
1486        return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
1487      },
1488      moveToMiddleLine: function(cm) {
1489        var range = getUserVisibleLines(cm);
1490        var line = Math.floor((range.top + range.bottom) * 0.5);
1491        return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
1492      },
1493      moveToBottomLine: function(cm, _head, motionArgs) {
1494        var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
1495        return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
1496      },
1497      expandToLine: function(_cm, head, motionArgs) {
1498        // Expands forward to end of line, and then to next line if repeat is
1499        // >1. Does not handle backward motion!
1500        var cur = head;
1501        return Pos(cur.line + motionArgs.repeat - 1, Infinity);
1502      },
1503      findNext: function(cm, _head, motionArgs) {
1504        var state = getSearchState(cm);
1505        var query = state.getQuery();
1506        if (!query) {
1507          return;
1508        }
1509        var prev = !motionArgs.forward;
1510        // If search is initiated with ? instead of /, negate direction.
1511        prev = (state.isReversed()) ? !prev : prev;
1512        highlightSearchMatches(cm, query);
1513        return findNext(cm, prev/** prev */, query, motionArgs.repeat);
1514      },
1515      goToMark: function(cm, _head, motionArgs, vim) {
1516        var mark = vim.marks[motionArgs.selectedCharacter];
1517        if (mark) {
1518          var pos = mark.find();
1519          return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
1520        }
1521        return null;
1522      },
1523      moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {
1524        if (vim.visualBlock && motionArgs.sameLine) {
1525          var sel = vim.sel;
1526          return [
1527            clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)),
1528            clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))
1529          ];
1530        } else {
1531          return ([vim.sel.head, vim.sel.anchor]);
1532        }
1533      },
1534      jumpToMark: function(cm, head, motionArgs, vim) {
1535        var best = head;
1536        for (var i = 0; i < motionArgs.repeat; i++) {
1537          var cursor = best;
1538          for (var key in vim.marks) {
1539            if (!isLowerCase(key)) {
1540              continue;
1541            }
1542            var mark = vim.marks[key].find();
1543            var isWrongDirection = (motionArgs.forward) ?
1544              cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);
1545
1546            if (isWrongDirection) {
1547              continue;
1548            }
1549            if (motionArgs.linewise && (mark.line == cursor.line)) {
1550              continue;
1551            }
1552
1553            var equal = cursorEqual(cursor, best);
1554            var between = (motionArgs.forward) ?
1555              cursorIsBetween(cursor, mark, best) :
1556              cursorIsBetween(best, mark, cursor);
1557
1558            if (equal || between) {
1559              best = mark;
1560            }
1561          }
1562        }
1563
1564        if (motionArgs.linewise) {
1565          // Vim places the cursor on the first non-whitespace character of
1566          // the line if there is one, else it places the cursor at the end
1567          // of the line, regardless of whether a mark was found.
1568          best = Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));
1569        }
1570        return best;
1571      },
1572      moveByCharacters: function(_cm, head, motionArgs) {
1573        var cur = head;
1574        var repeat = motionArgs.repeat;
1575        var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
1576        return Pos(cur.line, ch);
1577      },
1578      moveByLines: function(cm, head, motionArgs, vim) {
1579        var cur = head;
1580        var endCh = cur.ch;
1581        // Depending what our last motion was, we may want to do different
1582        // things. If our last motion was moving vertically, we want to
1583        // preserve the HPos from our last horizontal move.  If our last motion
1584        // was going to the end of a line, moving vertically we should go to
1585        // the end of the line, etc.
1586        switch (vim.lastMotion) {
1587          case this.moveByLines:
1588          case this.moveByDisplayLines:
1589          case this.moveByScroll:
1590          case this.moveToColumn:
1591          case this.moveToEol:
1592            endCh = vim.lastHPos;
1593            break;
1594          default:
1595            vim.lastHPos = endCh;
1596        }
1597        var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);
1598        var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
1599        var first = cm.firstLine();
1600        var last = cm.lastLine();
1601        // Vim cancels linewise motions that start on an edge and move beyond
1602        // that edge. It does not cancel motions that do not start on an edge.
1603        if ((line < first && cur.line == first) ||
1604            (line > last && cur.line == last)) {
1605          return;
1606        }
1607        if (motionArgs.toFirstChar){
1608          endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
1609          vim.lastHPos = endCh;
1610        }
1611        vim.lastHSPos = cm.charCoords(Pos(line, endCh),'div').left;
1612        return Pos(line, endCh);
1613      },
1614      moveByDisplayLines: function(cm, head, motionArgs, vim) {
1615        var cur = head;
1616        switch (vim.lastMotion) {
1617          case this.moveByDisplayLines:
1618          case this.moveByScroll:
1619          case this.moveByLines:
1620          case this.moveToColumn:
1621          case this.moveToEol:
1622            break;
1623          default:
1624            vim.lastHSPos = cm.charCoords(cur,'div').left;
1625        }
1626        var repeat = motionArgs.repeat;
1627        var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);
1628        if (res.hitSide) {
1629          if (motionArgs.forward) {
1630            var lastCharCoords = cm.charCoords(res, 'div');
1631            var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };
1632            var res = cm.coordsChar(goalCoords, 'div');
1633          } else {
1634            var resCoords = cm.charCoords(Pos(cm.firstLine(), 0), 'div');
1635            resCoords.left = vim.lastHSPos;
1636            res = cm.coordsChar(resCoords, 'div');
1637          }
1638        }
1639        vim.lastHPos = res.ch;
1640        return res;
1641      },
1642      moveByPage: function(cm, head, motionArgs) {
1643        // CodeMirror only exposes functions that move the cursor page down, so
1644        // doing this bad hack to move the cursor and move it back. evalInput
1645        // will move the cursor to where it should be in the end.
1646        var curStart = head;
1647        var repeat = motionArgs.repeat;
1648        return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
1649      },
1650      moveByParagraph: function(cm, head, motionArgs) {
1651        var dir = motionArgs.forward ? 1 : -1;
1652        return findParagraph(cm, head, motionArgs.repeat, dir);
1653      },
1654      moveByScroll: function(cm, head, motionArgs, vim) {
1655        var scrollbox = cm.getScrollInfo();
1656        var curEnd = null;
1657        var repeat = motionArgs.repeat;
1658        if (!repeat) {
1659          repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
1660        }
1661        var orig = cm.charCoords(head, 'local');
1662        motionArgs.repeat = repeat;
1663        var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
1664        if (!curEnd) {
1665          return null;
1666        }
1667        var dest = cm.charCoords(curEnd, 'local');
1668        cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
1669        return curEnd;
1670      },
1671      moveByWords: function(cm, head, motionArgs) {
1672        return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,
1673            !!motionArgs.wordEnd, !!motionArgs.bigWord);
1674      },
1675      moveTillCharacter: function(cm, _head, motionArgs) {
1676        var repeat = motionArgs.repeat;
1677        var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
1678            motionArgs.selectedCharacter);
1679        var increment = motionArgs.forward ? -1 : 1;
1680        recordLastCharacterSearch(increment, motionArgs);
1681        if (!curEnd) return null;
1682        curEnd.ch += increment;
1683        return curEnd;
1684      },
1685      moveToCharacter: function(cm, head, motionArgs) {
1686        var repeat = motionArgs.repeat;
1687        recordLastCharacterSearch(0, motionArgs);
1688        return moveToCharacter(cm, repeat, motionArgs.forward,
1689            motionArgs.selectedCharacter) || head;
1690      },
1691      moveToSymbol: function(cm, head, motionArgs) {
1692        var repeat = motionArgs.repeat;
1693        return findSymbol(cm, repeat, motionArgs.forward,
1694            motionArgs.selectedCharacter) || head;
1695      },
1696      moveToColumn: function(cm, head, motionArgs, vim) {
1697        var repeat = motionArgs.repeat;
1698        // repeat is equivalent to which column we want to move to!
1699        vim.lastHPos = repeat - 1;
1700        vim.lastHSPos = cm.charCoords(head,'div').left;
1701        return moveToColumn(cm, repeat);
1702      },
1703      moveToEol: function(cm, head, motionArgs, vim) {
1704        var cur = head;
1705        vim.lastHPos = Infinity;
1706        var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
1707        var end=cm.clipPos(retval);
1708        end.ch--;
1709        vim.lastHSPos = cm.charCoords(end,'div').left;
1710        return retval;
1711      },
1712      moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
1713        // Go to the start of the line where the text begins, or the end for
1714        // whitespace-only lines
1715        var cursor = head;
1716        return Pos(cursor.line,
1717                   findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
1718      },
1719      moveToMatchedSymbol: function(cm, head) {
1720        var cursor = head;
1721        var line = cursor.line;
1722        var ch = cursor.ch;
1723        var lineText = cm.getLine(line);
1724        var symbol;
1725        do {
1726          symbol = lineText.charAt(ch++);
1727          if (symbol && isMatchableSymbol(symbol)) {
1728            var style = cm.getTokenTypeAt(Pos(line, ch));
1729            if (style !== "string" && style !== "comment") {
1730              break;
1731            }
1732          }
1733        } while (symbol);
1734        if (symbol) {
1735          var matched = cm.findMatchingBracket(Pos(line, ch));
1736          return matched.to;
1737        } else {
1738          return cursor;
1739        }
1740      },
1741      moveToStartOfLine: function(_cm, head) {
1742        return Pos(head.line, 0);
1743      },
1744      moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
1745        var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
1746        if (motionArgs.repeatIsExplicit) {
1747          lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
1748        }
1749        return Pos(lineNum,
1750                   findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
1751      },
1752      textObjectManipulation: function(cm, head, motionArgs, vim) {
1753        // TODO: lots of possible exceptions that can be thrown here. Try da(
1754        //     outside of a () block.
1755
1756        // TODO: adding <> >< to this map doesn't work, presumably because
1757        // they're operators
1758        var mirroredPairs = {'(': ')', ')': '(',
1759                             '{': '}', '}': '{',
1760                             '[': ']', ']': '['};
1761        var selfPaired = {'\'': true, '"': true};
1762
1763        var character = motionArgs.selectedCharacter;
1764        // 'b' refers to  '()' block.
1765        // 'B' refers to  '{}' block.
1766        if (character == 'b') {
1767          character = '(';
1768        } else if (character == 'B') {
1769          character = '{';
1770        }
1771
1772        // Inclusive is the difference between a and i
1773        // TODO: Instead of using the additional text object map to perform text
1774        //     object operations, merge the map into the defaultKeyMap and use
1775        //     motionArgs to define behavior. Define separate entries for 'aw',
1776        //     'iw', 'a[', 'i[', etc.
1777        var inclusive = !motionArgs.textObjectInner;
1778
1779        var tmp;
1780        if (mirroredPairs[character]) {
1781          tmp = selectCompanionObject(cm, head, character, inclusive);
1782        } else if (selfPaired[character]) {
1783          tmp = findBeginningAndEnd(cm, head, character, inclusive);
1784        } else if (character === 'W') {
1785          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
1786                                                     true /** bigWord */);
1787        } else if (character === 'w') {
1788          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
1789                                                     false /** bigWord */);
1790        } else if (character === 'p') {
1791          tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
1792          motionArgs.linewise = true;
1793          if (vim.visualMode) {
1794            if (!vim.visualLine) { vim.visualLine = true; }
1795          } else {
1796            var operatorArgs = vim.inputState.operatorArgs;
1797            if (operatorArgs) { operatorArgs.linewise = true; }
1798            tmp.end.line--;
1799          }
1800        } else {
1801          // No text object defined for this, don't move.
1802          return null;
1803        }
1804
1805        if (!cm.state.vim.visualMode) {
1806          return [tmp.start, tmp.end];
1807        } else {
1808          return expandSelection(cm, tmp.start, tmp.end);
1809        }
1810      },
1811
1812      repeatLastCharacterSearch: function(cm, head, motionArgs) {
1813        var lastSearch = vimGlobalState.lastChararacterSearch;
1814        var repeat = motionArgs.repeat;
1815        var forward = motionArgs.forward === lastSearch.forward;
1816        var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
1817        cm.moveH(-increment, 'char');
1818        motionArgs.inclusive = forward ? true : false;
1819        var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
1820        if (!curEnd) {
1821          cm.moveH(increment, 'char');
1822          return head;
1823        }
1824        curEnd.ch += increment;
1825        return curEnd;
1826      }
1827    };
1828
1829    function defineMotion(name, fn) {
1830      motions[name] = fn;
1831    }
1832
1833    function fillArray(val, times) {
1834      var arr = [];
1835      for (var i = 0; i < times; i++) {
1836        arr.push(val);
1837      }
1838      return arr;
1839    }
1840    /**
1841     * An operator acts on a text selection. It receives the list of selections
1842     * as input. The corresponding CodeMirror selection is guaranteed to
1843    * match the input selection.
1844     */
1845    var operators = {
1846      change: function(cm, args, ranges) {
1847        var finalHead, text;
1848        var vim = cm.state.vim;
1849        vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock;
1850        if (!vim.visualMode) {
1851          var anchor = ranges[0].anchor,
1852              head = ranges[0].head;
1853          text = cm.getRange(anchor, head);
1854          if (!isWhiteSpaceString(text)) {
1855            // Exclude trailing whitespace if the range is not all whitespace.
1856            var match = (/\s+$/).exec(text);
1857            if (match) {
1858              head = offsetCursor(head, 0, - match[0].length);
1859              text = text.slice(0, - match[0].length);
1860            }
1861          }
1862          var wasLastLine = head.line - 1 == cm.lastLine();
1863          cm.replaceRange('', anchor, head);
1864          if (args.linewise && !wasLastLine) {
1865            // Push the next line back down, if there is a next line.
1866            CodeMirror.commands.newlineAndIndent(cm);
1867            // null ch so setCursor moves to end of line.
1868            anchor.ch = null;
1869          }
1870          finalHead = anchor;
1871        } else {
1872          text = cm.getSelection();
1873          var replacement = fillArray('', ranges.length);
1874          cm.replaceSelections(replacement);
1875          finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
1876        }
1877        vimGlobalState.registerController.pushText(
1878            args.registerName, 'change', text,
1879            args.linewise, ranges.length > 1);
1880        actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);
1881      },
1882      // delete is a javascript keyword.
1883      'delete': function(cm, args, ranges) {
1884        var finalHead, text;
1885        var vim = cm.state.vim;
1886        if (!vim.visualBlock) {
1887          var anchor = ranges[0].anchor,
1888              head = ranges[0].head;
1889          if (args.linewise &&
1890              head.line != cm.firstLine() &&
1891              anchor.line == cm.lastLine() &&
1892              anchor.line == head.line - 1) {
1893            // Special case for dd on last line (and first line).
1894            if (anchor.line == cm.firstLine()) {
1895              anchor.ch = 0;
1896            } else {
1897              anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
1898            }
1899          }
1900          text = cm.getRange(anchor, head);
1901          cm.replaceRange('', anchor, head);
1902          finalHead = anchor;
1903          if (args.linewise) {
1904            finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
1905          }
1906        } else {
1907          text = cm.getSelection();
1908          var replacement = fillArray('', ranges.length);
1909          cm.replaceSelections(replacement);
1910          finalHead = ranges[0].anchor;
1911        }
1912        vimGlobalState.registerController.pushText(
1913            args.registerName, 'delete', text,
1914            args.linewise, vim.visualBlock);
1915        return clipCursorToContent(cm, finalHead);
1916      },
1917      indent: function(cm, args, ranges) {
1918        var vim = cm.state.vim;
1919        var startLine = ranges[0].anchor.line;
1920        var endLine = vim.visualBlock ?
1921          ranges[ranges.length - 1].anchor.line :
1922          ranges[0].head.line;
1923        // In visual mode, n> shifts the selection right n times, instead of
1924        // shifting n lines right once.
1925        var repeat = (vim.visualMode) ? args.repeat : 1;
1926        if (args.linewise) {
1927          // The only way to delete a newline is to delete until the start of
1928          // the next line, so in linewise mode evalInput will include the next
1929          // line. We don't want this in indent, so we go back a line.
1930          endLine--;
1931        }
1932        for (var i = startLine; i <= endLine; i++) {
1933          for (var j = 0; j < repeat; j++) {
1934            cm.indentLine(i, args.indentRight);
1935          }
1936        }
1937        return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
1938      },
1939      changeCase: function(cm, args, ranges, oldAnchor, newHead) {
1940        var selections = cm.getSelections();
1941        var swapped = [];
1942        var toLower = args.toLower;
1943        for (var j = 0; j < selections.length; j++) {
1944          var toSwap = selections[j];
1945          var text = '';
1946          if (toLower === true) {
1947            text = toSwap.toLowerCase();
1948          } else if (toLower === false) {
1949            text = toSwap.toUpperCase();
1950          } else {
1951            for (var i = 0; i < toSwap.length; i++) {
1952              var character = toSwap.charAt(i);
1953              text += isUpperCase(character) ? character.toLowerCase() :
1954                  character.toUpperCase();
1955            }
1956          }
1957          swapped.push(text);
1958        }
1959        cm.replaceSelections(swapped);
1960        if (args.shouldMoveCursor){
1961          return newHead;
1962        } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
1963          return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
1964        } else if (args.linewise){
1965          return oldAnchor;
1966        } else {
1967          return cursorMin(ranges[0].anchor, ranges[0].head);
1968        }
1969      },
1970      yank: function(cm, args, ranges, oldAnchor) {
1971        var vim = cm.state.vim;
1972        var text = cm.getSelection();
1973        var endPos = vim.visualMode
1974          ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
1975          : oldAnchor;
1976        vimGlobalState.registerController.pushText(
1977            args.registerName, 'yank',
1978            text, args.linewise, vim.visualBlock);
1979        return endPos;
1980      }
1981    };
1982
1983    function defineOperator(name, fn) {
1984      operators[name] = fn;
1985    }
1986
1987    var actions = {
1988      jumpListWalk: function(cm, actionArgs, vim) {
1989        if (vim.visualMode) {
1990          return;
1991        }
1992        var repeat = actionArgs.repeat;
1993        var forward = actionArgs.forward;
1994        var jumpList = vimGlobalState.jumpList;
1995
1996        var mark = jumpList.move(cm, forward ? repeat : -repeat);
1997        var markPos = mark ? mark.find() : undefined;
1998        markPos = markPos ? markPos : cm.getCursor();
1999        cm.setCursor(markPos);
2000      },
2001      scroll: function(cm, actionArgs, vim) {
2002        if (vim.visualMode) {
2003          return;
2004        }
2005        var repeat = actionArgs.repeat || 1;
2006        var lineHeight = cm.defaultTextHeight();
2007        var top = cm.getScrollInfo().top;
2008        var delta = lineHeight * repeat;
2009        var newPos = actionArgs.forward ? top + delta : top - delta;
2010        var cursor = copyCursor(cm.getCursor());
2011        var cursorCoords = cm.charCoords(cursor, 'local');
2012        if (actionArgs.forward) {
2013          if (newPos > cursorCoords.top) {
2014             cursor.line += (newPos - cursorCoords.top) / lineHeight;
2015             cursor.line = Math.ceil(cursor.line);
2016             cm.setCursor(cursor);
2017             cursorCoords = cm.charCoords(cursor, 'local');
2018             cm.scrollTo(null, cursorCoords.top);
2019          } else {
2020             // Cursor stays within bounds.  Just reposition the scroll window.
2021             cm.scrollTo(null, newPos);
2022          }
2023        } else {
2024          var newBottom = newPos + cm.getScrollInfo().clientHeight;
2025          if (newBottom < cursorCoords.bottom) {
2026             cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
2027             cursor.line = Math.floor(cursor.line);
2028             cm.setCursor(cursor);
2029             cursorCoords = cm.charCoords(cursor, 'local');
2030             cm.scrollTo(
2031                 null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);
2032          } else {
2033             // Cursor stays within bounds.  Just reposition the scroll window.
2034             cm.scrollTo(null, newPos);
2035          }
2036        }
2037      },
2038      scrollToCursor: function(cm, actionArgs) {
2039        var lineNum = cm.getCursor().line;
2040        var charCoords = cm.charCoords(Pos(lineNum, 0), 'local');
2041        var height = cm.getScrollInfo().clientHeight;
2042        var y = charCoords.top;
2043        var lineHeight = charCoords.bottom - y;
2044        switch (actionArgs.position) {
2045          case 'center': y = y - (height / 2) + lineHeight;
2046            break;
2047          case 'bottom': y = y - height + lineHeight*1.4;
2048            break;
2049          case 'top': y = y + lineHeight*0.4;
2050            break;
2051        }
2052        cm.scrollTo(null, y);
2053      },
2054      replayMacro: function(cm, actionArgs, vim) {
2055        var registerName = actionArgs.selectedCharacter;
2056        var repeat = actionArgs.repeat;
2057        var macroModeState = vimGlobalState.macroModeState;
2058        if (registerName == '@') {
2059          registerName = macroModeState.latestRegister;
2060        }
2061        while(repeat--){
2062          executeMacroRegister(cm, vim, macroModeState, registerName);
2063        }
2064      },
2065      enterMacroRecordMode: function(cm, actionArgs) {
2066        var macroModeState = vimGlobalState.macroModeState;
2067        var registerName = actionArgs.selectedCharacter;
2068        macroModeState.enterMacroRecordMode(cm, registerName);
2069      },
2070      enterInsertMode: function(cm, actionArgs, vim) {
2071        if (cm.getOption('readOnly')) { return; }
2072        vim.insertMode = true;
2073        vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
2074        var insertAt = (actionArgs) ? actionArgs.insertAt : null;
2075        var sel = vim.sel;
2076        var head = actionArgs.head || cm.getCursor('head');
2077        var height = cm.listSelections().length;
2078        if (insertAt == 'eol') {
2079          head = Pos(head.line, lineLength(cm, head.line));
2080        } else if (insertAt == 'charAfter') {
2081          head = offsetCursor(head, 0, 1);
2082        } else if (insertAt == 'firstNonBlank') {
2083          head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);
2084        } else if (insertAt == 'startOfSelectedArea') {
2085          if (!vim.visualBlock) {
2086            if (sel.head.line < sel.anchor.line) {
2087              head = sel.head;
2088            } else {
2089              head = Pos(sel.anchor.line, 0);
2090            }
2091          } else {
2092            head = Pos(
2093                Math.min(sel.head.line, sel.anchor.line),
2094                Math.min(sel.head.ch, sel.anchor.ch));
2095            height = Math.abs(sel.head.line - sel.anchor.line) + 1;
2096          }
2097        } else if (insertAt == 'endOfSelectedArea') {
2098          if (!vim.visualBlock) {
2099            if (sel.head.line >= sel.anchor.line) {
2100              head = offsetCursor(sel.head, 0, 1);
2101            } else {
2102              head = Pos(sel.anchor.line, 0);
2103            }
2104          } else {
2105            head = Pos(
2106                Math.min(sel.head.line, sel.anchor.line),
2107                Math.max(sel.head.ch + 1, sel.anchor.ch));
2108            height = Math.abs(sel.head.line - sel.anchor.line) + 1;
2109          }
2110        } else if (insertAt == 'inplace') {
2111          if (vim.visualMode){
2112            return;
2113          }
2114        }
2115        cm.setOption('keyMap', 'vim-insert');
2116        cm.setOption('disableInput', false);
2117        if (actionArgs && actionArgs.replace) {
2118          // Handle Replace-mode as a special case of insert mode.
2119          cm.toggleOverwrite(true);
2120          cm.setOption('keyMap', 'vim-replace');
2121          CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
2122        } else {
2123          cm.setOption('keyMap', 'vim-insert');
2124          CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
2125        }
2126        if (!vimGlobalState.macroModeState.isPlaying) {
2127          // Only record if not replaying.
2128          cm.on('change', onChange);
2129          CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
2130        }
2131        if (vim.visualMode) {
2132          exitVisualMode(cm);
2133        }
2134        selectForInsert(cm, head, height);
2135      },
2136      toggleVisualMode: function(cm, actionArgs, vim) {
2137        var repeat = actionArgs.repeat;
2138        var anchor = cm.getCursor();
2139        var head;
2140        // TODO: The repeat should actually select number of characters/lines
2141        //     equal to the repeat times the size of the previous visual
2142        //     operation.
2143        if (!vim.visualMode) {
2144          // Entering visual mode
2145          vim.visualMode = true;
2146          vim.visualLine = !!actionArgs.linewise;
2147          vim.visualBlock = !!actionArgs.blockwise;
2148          head = clipCursorToContent(
2149              cm, Pos(anchor.line, anchor.ch + repeat - 1),
2150              true /** includeLineBreak */);
2151          vim.sel = {
2152            anchor: anchor,
2153            head: head
2154          };
2155          CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
2156          updateCmSelection(cm);
2157          updateMark(cm, vim, '<', cursorMin(anchor, head));
2158          updateMark(cm, vim, '>', cursorMax(anchor, head));
2159        } else if (vim.visualLine ^ actionArgs.linewise ||
2160            vim.visualBlock ^ actionArgs.blockwise) {
2161          // Toggling between modes
2162          vim.visualLine = !!actionArgs.linewise;
2163          vim.visualBlock = !!actionArgs.blockwise;
2164          CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
2165          updateCmSelection(cm);
2166        } else {
2167          exitVisualMode(cm);
2168        }
2169      },
2170      reselectLastSelection: function(cm, _actionArgs, vim) {
2171        var lastSelection = vim.lastSelection;
2172        if (vim.visualMode) {
2173          updateLastSelection(cm, vim);
2174        }
2175        if (lastSelection) {
2176          var anchor = lastSelection.anchorMark.find();
2177          var head = lastSelection.headMark.find();
2178          if (!anchor || !head) {
2179            // If the marks have been destroyed due to edits, do nothing.
2180            return;
2181          }
2182          vim.sel = {
2183            anchor: anchor,
2184            head: head
2185          };
2186          vim.visualMode = true;
2187          vim.visualLine = lastSelection.visualLine;
2188          vim.visualBlock = lastSelection.visualBlock;
2189          updateCmSelection(cm);
2190          updateMark(cm, vim, '<', cursorMin(anchor, head));
2191          updateMark(cm, vim, '>', cursorMax(anchor, head));
2192          CodeMirror.signal(cm, 'vim-mode-change', {
2193            mode: 'visual',
2194            subMode: vim.visualLine ? 'linewise' :
2195                     vim.visualBlock ? 'blockwise' : ''});
2196        }
2197      },
2198      joinLines: function(cm, actionArgs, vim) {
2199        var curStart, curEnd;
2200        if (vim.visualMode) {
2201          curStart = cm.getCursor('anchor');
2202          curEnd = cm.getCursor('head');
2203          if (cursorIsBefore(curEnd, curStart)) {
2204            var tmp = curEnd;
2205            curEnd = curStart;
2206            curStart = tmp;
2207          }
2208          curEnd.ch = lineLength(cm, curEnd.line) - 1;
2209        } else {
2210          // Repeat is the number of lines to join. Minimum 2 lines.
2211          var repeat = Math.max(actionArgs.repeat, 2);
2212          curStart = cm.getCursor();
2213          curEnd = clipCursorToContent(cm, Pos(curStart.line + repeat - 1,
2214                                               Infinity));
2215        }
2216        var finalCh = 0;
2217        for (var i = curStart.line; i < curEnd.line; i++) {
2218          finalCh = lineLength(cm, curStart.line);
2219          var tmp = Pos(curStart.line + 1,
2220                        lineLength(cm, curStart.line + 1));
2221          var text = cm.getRange(curStart, tmp);
2222          text = text.replace(/\n\s*/g, ' ');
2223          cm.replaceRange(text, curStart, tmp);
2224        }
2225        var curFinalPos = Pos(curStart.line, finalCh);
2226        if (vim.visualMode) {
2227          exitVisualMode(cm, false);
2228        }
2229        cm.setCursor(curFinalPos);
2230      },
2231      newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
2232        vim.insertMode = true;
2233        var insertAt = copyCursor(cm.getCursor());
2234        if (insertAt.line === cm.firstLine() && !actionArgs.after) {
2235          // Special case for inserting newline before start of document.
2236          cm.replaceRange('\n', Pos(cm.firstLine(), 0));
2237          cm.setCursor(cm.firstLine(), 0);
2238        } else {
2239          insertAt.line = (actionArgs.after) ? insertAt.line :
2240              insertAt.line - 1;
2241          insertAt.ch = lineLength(cm, insertAt.line);
2242          cm.setCursor(insertAt);
2243          var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
2244              CodeMirror.commands.newlineAndIndent;
2245          newlineFn(cm);
2246        }
2247        this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
2248      },
2249      paste: function(cm, actionArgs, vim) {
2250        var cur = copyCursor(cm.getCursor());
2251        var register = vimGlobalState.registerController.getRegister(
2252            actionArgs.registerName);
2253        var text = register.toString();
2254        if (!text) {
2255          return;
2256        }
2257        if (actionArgs.matchIndent) {
2258          var tabSize = cm.getOption("tabSize");
2259          // length that considers tabs and tabSize
2260          var whitespaceLength = function(str) {
2261            var tabs = (str.split("\t").length - 1);
2262            var spaces = (str.split(" ").length - 1);
2263            return tabs * tabSize + spaces * 1;
2264          };
2265          var currentLine = cm.getLine(cm.getCursor().line);
2266          var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
2267          // chomp last newline b/c don't want it to match /^\s*/gm
2268          var chompedText = text.replace(/\n$/, '');
2269          var wasChomped = text !== chompedText;
2270          var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
2271          var text = chompedText.replace(/^\s*/gm, function(wspace) {
2272            var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
2273            if (newIndent < 0) {
2274              return "";
2275            }
2276            else if (cm.getOption("indentWithTabs")) {
2277              var quotient = Math.floor(newIndent / tabSize);
2278              return Array(quotient + 1).join('\t');
2279            }
2280            else {
2281              return Array(newIndent + 1).join(' ');
2282            }
2283          });
2284          text += wasChomped ? "\n" : "";
2285        }
2286        if (actionArgs.repeat > 1) {
2287          var text = Array(actionArgs.repeat + 1).join(text);
2288        }
2289        var linewise = register.linewise;
2290        var blockwise = register.blockwise;
2291        if (linewise) {
2292          if(vim.visualMode) {
2293            text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
2294          } else if (actionArgs.after) {
2295            // Move the newline at the end to the start instead, and paste just
2296            // before the newline character of the line we are on right now.
2297            text = '\n' + text.slice(0, text.length - 1);
2298            cur.ch = lineLength(cm, cur.line);
2299          } else {
2300            cur.ch = 0;
2301          }
2302        } else {
2303          if (blockwise) {
2304            text = text.split('\n');
2305            for (var i = 0; i < text.length; i++) {
2306              text[i] = (text[i] == '') ? ' ' : text[i];
2307            }
2308          }
2309          cur.ch += actionArgs.after ? 1 : 0;
2310        }
2311        var curPosFinal;
2312        var idx;
2313        if (vim.visualMode) {
2314          //  save the pasted text for reselection if the need arises
2315          vim.lastPastedText = text;
2316          var lastSelectionCurEnd;
2317          var selectedArea = getSelectedAreaRange(cm, vim);
2318          var selectionStart = selectedArea[0];
2319          var selectionEnd = selectedArea[1];
2320          var selectedText = cm.getSelection();
2321          var selections = cm.listSelections();
2322          var emptyStrings = new Array(selections.length).join('1').split('1');
2323          // save the curEnd marker before it get cleared due to cm.replaceRange.
2324          if (vim.lastSelection) {
2325            lastSelectionCurEnd = vim.lastSelection.headMark.find();
2326          }
2327          // push the previously selected text to unnamed register
2328          vimGlobalState.registerController.unnamedRegister.setText(selectedText);
2329          if (blockwise) {
2330            // first delete the selected text
2331            cm.replaceSelections(emptyStrings);
2332            // Set new selections as per the block length of the yanked text
2333            selectionEnd = Pos(selectionStart.line + text.length-1, selectionStart.ch);
2334            cm.setCursor(selectionStart);
2335            selectBlock(cm, selectionEnd);
2336            cm.replaceSelections(text);
2337            curPosFinal = selectionStart;
2338          } else if (vim.visualBlock) {
2339            cm.replaceSelections(emptyStrings);
2340            cm.setCursor(selectionStart);
2341            cm.replaceRange(text, selectionStart, selectionStart);
2342            curPosFinal = selectionStart;
2343          } else {
2344            cm.replaceRange(text, selectionStart, selectionEnd);
2345            curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
2346          }
2347          // restore the the curEnd marker
2348          if(lastSelectionCurEnd) {
2349            vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
2350          }
2351          if (linewise) {
2352            curPosFinal.ch=0;
2353          }
2354        } else {
2355          if (blockwise) {
2356            cm.setCursor(cur);
2357            for (var i = 0; i < text.length; i++) {
2358              var line = cur.line+i;
2359              if (line > cm.lastLine()) {
2360                cm.replaceRange('\n',  Pos(line, 0));
2361              }
2362              var lastCh = lineLength(cm, line);
2363              if (lastCh < cur.ch) {
2364                extendLineToColumn(cm, line, cur.ch);
2365              }
2366            }
2367            cm.setCursor(cur);
2368            selectBlock(cm, Pos(cur.line + text.length-1, cur.ch));
2369            cm.replaceSelections(text);
2370            curPosFinal = cur;
2371          } else {
2372            cm.replaceRange(text, cur);
2373            // Now fine tune the cursor to where we want it.
2374            if (linewise && actionArgs.after) {
2375              curPosFinal = Pos(
2376              cur.line + 1,
2377              findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
2378            } else if (linewise && !actionArgs.after) {
2379              curPosFinal = Pos(
2380                cur.line,
2381                findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
2382            } else if (!linewise && actionArgs.after) {
2383              idx = cm.indexFromPos(cur);
2384              curPosFinal = cm.posFromIndex(idx + text.length - 1);
2385            } else {
2386              idx = cm.indexFromPos(cur);
2387              curPosFinal = cm.posFromIndex(idx + text.length);
2388            }
2389          }
2390        }
2391        if (vim.visualMode) {
2392          exitVisualMode(cm, false);
2393        }
2394        cm.setCursor(curPosFinal);
2395      },
2396      undo: function(cm, actionArgs) {
2397        cm.operation(function() {
2398          repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
2399          cm.setCursor(cm.getCursor('anchor'));
2400        });
2401      },
2402      redo: function(cm, actionArgs) {
2403        repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
2404      },
2405      setRegister: function(_cm, actionArgs, vim) {
2406        vim.inputState.registerName = actionArgs.selectedCharacter;
2407      },
2408      setMark: function(cm, actionArgs, vim) {
2409        var markName = actionArgs.selectedCharacter;
2410        updateMark(cm, vim, markName, cm.getCursor());
2411      },
2412      replace: function(cm, actionArgs, vim) {
2413        var replaceWith = actionArgs.selectedCharacter;
2414        var curStart = cm.getCursor();
2415        var replaceTo;
2416        var curEnd;
2417        var selections = cm.listSelections();
2418        if (vim.visualMode) {
2419          curStart = cm.getCursor('start');
2420          curEnd = cm.getCursor('end');
2421        } else {
2422          var line = cm.getLine(curStart.line);
2423          replaceTo = curStart.ch + actionArgs.repeat;
2424          if (replaceTo > line.length) {
2425            replaceTo=line.length;
2426          }
2427          curEnd = Pos(curStart.line, replaceTo);
2428        }
2429        if (replaceWith=='\n') {
2430          if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
2431          // special case, where vim help says to replace by just one line-break
2432          (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
2433        } else {
2434          var replaceWithStr = cm.getRange(curStart, curEnd);
2435          //replace all characters in range by selected, but keep linebreaks
2436          replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
2437          if (vim.visualBlock) {
2438            // Tabs are split in visua block before replacing
2439            var spaces = new Array(cm.getOption("tabSize")+1).join(' ');
2440            replaceWithStr = cm.getSelection();
2441            replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
2442            cm.replaceSelections(replaceWithStr);
2443          } else {
2444            cm.replaceRange(replaceWithStr, curStart, curEnd);
2445          }
2446          if (vim.visualMode) {
2447            curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
2448                         selections[0].anchor : selections[0].head;
2449            cm.setCursor(curStart);
2450            exitVisualMode(cm, false);
2451          } else {
2452            cm.setCursor(offsetCursor(curEnd, 0, -1));
2453          }
2454        }
2455      },
2456      incrementNumberToken: function(cm, actionArgs) {
2457        var cur = cm.getCursor();
2458        var lineStr = cm.getLine(cur.line);
2459        var re = /-?\d+/g;
2460        var match;
2461        var start;
2462        var end;
2463        var numberStr;
2464        var token;
2465        while ((match = re.exec(lineStr)) !== null) {
2466          token = match[0];
2467          start = match.index;
2468          end = start + token.length;
2469          if (cur.ch < end)break;
2470        }
2471        if (!actionArgs.backtrack && (end <= cur.ch))return;
2472        if (token) {
2473          var increment = actionArgs.increase ? 1 : -1;
2474          var number = parseInt(token) + (increment * actionArgs.repeat);
2475          var from = Pos(cur.line, start);
2476          var to = Pos(cur.line, end);
2477          numberStr = number.toString();
2478          cm.replaceRange(numberStr, from, to);
2479        } else {
2480          return;
2481        }
2482        cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
2483      },
2484      repeatLastEdit: function(cm, actionArgs, vim) {
2485        var lastEditInputState = vim.lastEditInputState;
2486        if (!lastEditInputState) { return; }
2487        var repeat = actionArgs.repeat;
2488        if (repeat && actionArgs.repeatIsExplicit) {
2489          vim.lastEditInputState.repeatOverride = repeat;
2490        } else {
2491          repeat = vim.lastEditInputState.repeatOverride || repeat;
2492        }
2493        repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
2494      },
2495      exitInsertMode: exitInsertMode
2496    };
2497
2498    function defineAction(name, fn) {
2499      actions[name] = fn;
2500    }
2501
2502    /*
2503     * Below are miscellaneous utility functions used by vim.js
2504     */
2505
2506    /**
2507     * Clips cursor to ensure that line is within the buffer's range
2508     * If includeLineBreak is true, then allow cur.ch == lineLength.
2509     */
2510    function clipCursorToContent(cm, cur, includeLineBreak) {
2511      var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
2512      var maxCh = lineLength(cm, line) - 1;
2513      maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
2514      var ch = Math.min(Math.max(0, cur.ch), maxCh);
2515      return Pos(line, ch);
2516    }
2517    function copyArgs(args) {
2518      var ret = {};
2519      for (var prop in args) {
2520        if (args.hasOwnProperty(prop)) {
2521          ret[prop] = args[prop];
2522        }
2523      }
2524      return ret;
2525    }
2526    function offsetCursor(cur, offsetLine, offsetCh) {
2527      if (typeof offsetLine === 'object') {
2528        offsetCh = offsetLine.ch;
2529        offsetLine = offsetLine.line;
2530      }
2531      return Pos(cur.line + offsetLine, cur.ch + offsetCh);
2532    }
2533    function getOffset(anchor, head) {
2534      return {
2535        line: head.line - anchor.line,
2536        ch: head.line - anchor.line
2537      };
2538    }
2539    function commandMatches(keys, keyMap, context, inputState) {
2540      // Partial matches are not applied. They inform the key handler
2541      // that the current key sequence is a subsequence of a valid key
2542      // sequence, so that the key buffer is not cleared.
2543      var match, partial = [], full = [];
2544      for (var i = 0; i < keyMap.length; i++) {
2545        var command = keyMap[i];
2546        if (context == 'insert' && command.context != 'insert' ||
2547            command.context && command.context != context ||
2548            inputState.operator && command.type == 'action' ||
2549            !(match = commandMatch(keys, command.keys))) { continue; }
2550        if (match == 'partial') { partial.push(command); }
2551        if (match == 'full') { full.push(command); }
2552      }
2553      return {
2554        partial: partial.length && partial,
2555        full: full.length && full
2556      };
2557    }
2558    function commandMatch(pressed, mapped) {
2559      if (mapped.slice(-11) == '<character>') {
2560        // Last character matches anything.
2561        var prefixLen = mapped.length - 11;
2562        var pressedPrefix = pressed.slice(0, prefixLen);
2563        var mappedPrefix = mapped.slice(0, prefixLen);
2564        return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
2565               mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
2566      } else {
2567        return pressed == mapped ? 'full' :
2568               mapped.indexOf(pressed) == 0 ? 'partial' : false;
2569      }
2570    }
2571    function lastChar(keys) {
2572      var match = /^.*(<[\w\-]+>)$/.exec(keys);
2573      var selectedCharacter = match ? match[1] : keys.slice(-1);
2574      if (selectedCharacter.length > 1){
2575        switch(selectedCharacter){
2576          case '<CR>':
2577            selectedCharacter='\n';
2578            break;
2579          case '<Space>':
2580            selectedCharacter=' ';
2581            break;
2582          default:
2583            break;
2584        }
2585      }
2586      return selectedCharacter;
2587    }
2588    function repeatFn(cm, fn, repeat) {
2589      return function() {
2590        for (var i = 0; i < repeat; i++) {
2591          fn(cm);
2592        }
2593      };
2594    }
2595    function copyCursor(cur) {
2596      return Pos(cur.line, cur.ch);
2597    }
2598    function cursorEqual(cur1, cur2) {
2599      return cur1.ch == cur2.ch && cur1.line == cur2.line;
2600    }
2601    function cursorIsBefore(cur1, cur2) {
2602      if (cur1.line < cur2.line) {
2603        return true;
2604      }
2605      if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
2606        return true;
2607      }
2608      return false;
2609    }
2610    function cursorMin(cur1, cur2) {
2611      if (arguments.length > 2) {
2612        cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
2613      }
2614      return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
2615    }
2616    function cursorMax(cur1, cur2) {
2617      if (arguments.length > 2) {
2618        cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
2619      }
2620      return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
2621    }
2622    function cursorIsBetween(cur1, cur2, cur3) {
2623      // returns true if cur2 is between cur1 and cur3.
2624      var cur1before2 = cursorIsBefore(cur1, cur2);
2625      var cur2before3 = cursorIsBefore(cur2, cur3);
2626      return cur1before2 && cur2before3;
2627    }
2628    function lineLength(cm, lineNum) {
2629      return cm.getLine(lineNum).length;
2630    }
2631    function reverse(s){
2632      return s.split('').reverse().join('');
2633    }
2634    function trim(s) {
2635      if (s.trim) {
2636        return s.trim();
2637      }
2638      return s.replace(/^\s+|\s+$/g, '');
2639    }
2640    function escapeRegex(s) {
2641      return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
2642    }
2643    function extendLineToColumn(cm, lineNum, column) {
2644      var endCh = lineLength(cm, lineNum);
2645      var spaces = new Array(column-endCh+1).join(' ');
2646      cm.setCursor(Pos(lineNum, endCh));
2647      cm.replaceRange(spaces, cm.getCursor());
2648    }
2649    // This functions selects a rectangular block
2650    // of text with selectionEnd as any of its corner
2651    // Height of block:
2652    // Difference in selectionEnd.line and first/last selection.line
2653    // Width of the block:
2654    // Distance between selectionEnd.ch and any(first considered here) selection.ch
2655    function selectBlock(cm, selectionEnd) {
2656      var selections = [], ranges = cm.listSelections();
2657      var head = copyCursor(cm.clipPos(selectionEnd));
2658      var isClipped = !cursorEqual(selectionEnd, head);
2659      var curHead = cm.getCursor('head');
2660      var primIndex = getIndex(ranges, curHead);
2661      var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
2662      var max = ranges.length - 1;
2663      var index = max - primIndex > primIndex ? max : 0;
2664      var base = ranges[index].anchor;
2665
2666      var firstLine = Math.min(base.line, head.line);
2667      var lastLine = Math.max(base.line, head.line);
2668      var baseCh = base.ch, headCh = head.ch;
2669
2670      var dir = ranges[index].head.ch - baseCh;
2671      var newDir = headCh - baseCh;
2672      if (dir > 0 && newDir <= 0) {
2673        baseCh++;
2674        if (!isClipped) { headCh--; }
2675      } else if (dir < 0 && newDir >= 0) {
2676        baseCh--;
2677        if (!wasClipped) { headCh++; }
2678      } else if (dir < 0 && newDir == -1) {
2679        baseCh--;
2680        headCh++;
2681      }
2682      for (var line = firstLine; line <= lastLine; line++) {
2683        var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};
2684        selections.push(range);
2685      }
2686      primIndex = head.line == lastLine ? selections.length - 1 : 0;
2687      cm.setSelections(selections);
2688      selectionEnd.ch = headCh;
2689      base.ch = baseCh;
2690      return base;
2691    }
2692    function selectForInsert(cm, head, height) {
2693      var sel = [];
2694      for (var i = 0; i < height; i++) {
2695        var lineHead = offsetCursor(head, i, 0);
2696        sel.push({anchor: lineHead, head: lineHead});
2697      }
2698      cm.setSelections(sel, 0);
2699    }
2700    // getIndex returns the index of the cursor in the selections.
2701    function getIndex(ranges, cursor, end) {
2702      for (var i = 0; i < ranges.length; i++) {
2703        var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
2704        var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
2705        if (atAnchor || atHead) {
2706          return i;
2707        }
2708      }
2709      return -1;
2710    }
2711    function getSelectedAreaRange(cm, vim) {
2712      var lastSelection = vim.lastSelection;
2713      var getCurrentSelectedAreaRange = function() {
2714        var selections = cm.listSelections();
2715        var start =  selections[0];
2716        var end = selections[selections.length-1];
2717        var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
2718        var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
2719        return [selectionStart, selectionEnd];
2720      };
2721      var getLastSelectedAreaRange = function() {
2722        var selectionStart = cm.getCursor();
2723        var selectionEnd = cm.getCursor();
2724        var block = lastSelection.visualBlock;
2725        if (block) {
2726          var width = block.width;
2727          var height = block.height;
2728          selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
2729          var selections = [];
2730          // selectBlock creates a 'proper' rectangular block.
2731          // We do not want that in all cases, so we manually set selections.
2732          for (var i = selectionStart.line; i < selectionEnd.line; i++) {
2733            var anchor = Pos(i, selectionStart.ch);
2734            var head = Pos(i, selectionEnd.ch);
2735            var range = {anchor: anchor, head: head};
2736            selections.push(range);
2737          }
2738          cm.setSelections(selections);
2739        } else {
2740          var start = lastSelection.anchorMark.find();
2741          var end = lastSelection.headMark.find();
2742          var line = end.line - start.line;
2743          var ch = end.ch - start.ch;
2744          selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
2745          if (lastSelection.visualLine) {
2746            selectionStart = Pos(selectionStart.line, 0);
2747            selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
2748          }
2749          cm.setSelection(selectionStart, selectionEnd);
2750        }
2751        return [selectionStart, selectionEnd];
2752      };
2753      if (!vim.visualMode) {
2754      // In case of replaying the action.
2755        return getLastSelectedAreaRange();
2756      } else {
2757        return getCurrentSelectedAreaRange();
2758      }
2759    }
2760    // Updates the previous selection with the current selection's values. This
2761    // should only be called in visual mode.
2762    function updateLastSelection(cm, vim) {
2763      var anchor = vim.sel.anchor;
2764      var head = vim.sel.head;
2765      // To accommodate the effect of lastPastedText in the last selection
2766      if (vim.lastPastedText) {
2767        head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
2768        vim.lastPastedText = null;
2769      }
2770      vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),
2771                           'headMark': cm.setBookmark(head),
2772                           'anchor': copyCursor(anchor),
2773                           'head': copyCursor(head),
2774                           'visualMode': vim.visualMode,
2775                           'visualLine': vim.visualLine,
2776                           'visualBlock': vim.visualBlock};
2777    }
2778    function expandSelection(cm, start, end) {
2779      var sel = cm.state.vim.sel;
2780      var head = sel.head;
2781      var anchor = sel.anchor;
2782      var tmp;
2783      if (cursorIsBefore(end, start)) {
2784        tmp = end;
2785        end = start;
2786        start = tmp;
2787      }
2788      if (cursorIsBefore(head, anchor)) {
2789        head = cursorMin(start, head);
2790        anchor = cursorMax(anchor, end);
2791      } else {
2792        anchor = cursorMin(start, anchor);
2793        head = cursorMax(head, end);
2794        head = offsetCursor(head, 0, -1);
2795        if (head.ch == -1 && head.line != cm.firstLine()) {
2796          head = Pos(head.line - 1, lineLength(cm, head.line - 1));
2797        }
2798      }
2799      return [anchor, head];
2800    }
2801    /**
2802     * Updates the CodeMirror selection to match the provided vim selection.
2803     * If no arguments are given, it uses the current vim selection state.
2804     */
2805    function updateCmSelection(cm, sel, mode) {
2806      var vim = cm.state.vim;
2807      sel = sel || vim.sel;
2808      var mode = mode ||
2809        vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
2810      var cmSel = makeCmSelection(cm, sel, mode);
2811      cm.setSelections(cmSel.ranges, cmSel.primary);
2812      updateFakeCursor(cm);
2813    }
2814    function makeCmSelection(cm, sel, mode, exclusive) {
2815      var head = copyCursor(sel.head);
2816      var anchor = copyCursor(sel.anchor);
2817      if (mode == 'char') {
2818        var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
2819        var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
2820        head = offsetCursor(sel.head, 0, headOffset);
2821        anchor = offsetCursor(sel.anchor, 0, anchorOffset);
2822        return {
2823          ranges: [{anchor: anchor, head: head}],
2824          primary: 0
2825        };
2826      } else if (mode == 'line') {
2827        if (!cursorIsBefore(sel.head, sel.anchor)) {
2828          anchor.ch = 0;
2829
2830          var lastLine = cm.lastLine();
2831          if (head.line > lastLine) {
2832            head.line = lastLine;
2833          }
2834          head.ch = lineLength(cm, head.line);
2835        } else {
2836          head.ch = 0;
2837          anchor.ch = lineLength(cm, anchor.line);
2838        }
2839        return {
2840          ranges: [{anchor: anchor, head: head}],
2841          primary: 0
2842        };
2843      } else if (mode == 'block') {
2844        var top = Math.min(anchor.line, head.line),
2845            left = Math.min(anchor.ch, head.ch),
2846            bottom = Math.max(anchor.line, head.line),
2847            right = Math.max(anchor.ch, head.ch) + 1;
2848        var height = bottom - top + 1;
2849        var primary = head.line == top ? 0 : height - 1;
2850        var ranges = [];
2851        for (var i = 0; i < height; i++) {
2852          ranges.push({
2853            anchor: Pos(top + i, left),
2854            head: Pos(top + i, right)
2855          });
2856        }
2857        return {
2858          ranges: ranges,
2859          primary: primary
2860        };
2861      }
2862    }
2863    function getHead(cm) {
2864      var cur = cm.getCursor('head');
2865      if (cm.getSelection().length == 1) {
2866        // Small corner case when only 1 character is selected. The "real"
2867        // head is the left of head and anchor.
2868        cur = cursorMin(cur, cm.getCursor('anchor'));
2869      }
2870      return cur;
2871    }
2872
2873    /**
2874     * If moveHead is set to false, the CodeMirror selection will not be
2875     * touched. The caller assumes the responsibility of putting the cursor
2876    * in the right place.
2877     */
2878    function exitVisualMode(cm, moveHead) {
2879      var vim = cm.state.vim;
2880      if (moveHead !== false) {
2881        cm.setCursor(clipCursorToContent(cm, vim.sel.head));
2882      }
2883      updateLastSelection(cm, vim);
2884      vim.visualMode = false;
2885      vim.visualLine = false;
2886      vim.visualBlock = false;
2887      CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
2888      if (vim.fakeCursor) {
2889        vim.fakeCursor.clear();
2890      }
2891    }
2892
2893    // Remove any trailing newlines from the selection. For
2894    // example, with the caret at the start of the last word on the line,
2895    // 'dw' should word, but not the newline, while 'w' should advance the
2896    // caret to the first character of the next line.
2897    function clipToLine(cm, curStart, curEnd) {
2898      var selection = cm.getRange(curStart, curEnd);
2899      // Only clip if the selection ends with trailing newline + whitespace
2900      if (/\n\s*$/.test(selection)) {
2901        var lines = selection.split('\n');
2902        // We know this is all whitepsace.
2903        lines.pop();
2904
2905        // Cases:
2906        // 1. Last word is an empty line - do not clip the trailing '\n'
2907        // 2. Last word is not an empty line - clip the trailing '\n'
2908        var line;
2909        // Find the line containing the last word, and clip all whitespace up
2910        // to it.
2911        for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
2912          curEnd.line--;
2913          curEnd.ch = 0;
2914        }
2915        // If the last word is not an empty line, clip an additional newline
2916        if (line) {
2917          curEnd.line--;
2918          curEnd.ch = lineLength(cm, curEnd.line);
2919        } else {
2920          curEnd.ch = 0;
2921        }
2922      }
2923    }
2924
2925    // Expand the selection to line ends.
2926    function expandSelectionToLine(_cm, curStart, curEnd) {
2927      curStart.ch = 0;
2928      curEnd.ch = 0;
2929      curEnd.line++;
2930    }
2931
2932    function findFirstNonWhiteSpaceCharacter(text) {
2933      if (!text) {
2934        return 0;
2935      }
2936      var firstNonWS = text.search(/\S/);
2937      return firstNonWS == -1 ? text.length : firstNonWS;
2938    }
2939
2940    function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
2941      var cur = getHead(cm);
2942      var line = cm.getLine(cur.line);
2943      var idx = cur.ch;
2944
2945      // Seek to first word or non-whitespace character, depending on if
2946      // noSymbol is true.
2947      var textAfterIdx = line.substring(idx);
2948      var firstMatchedChar;
2949      if (noSymbol) {
2950        firstMatchedChar = textAfterIdx.search(/\w/);
2951      } else {
2952        firstMatchedChar = textAfterIdx.search(/\S/);
2953      }
2954      if (firstMatchedChar == -1) {
2955        return null;
2956      }
2957      idx += firstMatchedChar;
2958      textAfterIdx = line.substring(idx);
2959      var textBeforeIdx = line.substring(0, idx);
2960
2961      var matchRegex;
2962      // Greedy matchers for the "word" we are trying to expand.
2963      if (bigWord) {
2964        matchRegex = /^\S+/;
2965      } else {
2966        if ((/\w/).test(line.charAt(idx))) {
2967          matchRegex = /^\w+/;
2968        } else {
2969          matchRegex = /^[^\w\s]+/;
2970        }
2971      }
2972
2973      var wordAfterRegex = matchRegex.exec(textAfterIdx);
2974      var wordStart = idx;
2975      var wordEnd = idx + wordAfterRegex[0].length;
2976      // TODO: Find a better way to do this. It will be slow on very long lines.
2977      var revTextBeforeIdx = reverse(textBeforeIdx);
2978      var wordBeforeRegex = matchRegex.exec(revTextBeforeIdx);
2979      if (wordBeforeRegex) {
2980        wordStart -= wordBeforeRegex[0].length;
2981      }
2982
2983      if (inclusive) {
2984        // If present, trim all whitespace after word.
2985        // Otherwise, trim all whitespace before word.
2986        var textAfterWordEnd = line.substring(wordEnd);
2987        var whitespacesAfterWord = textAfterWordEnd.match(/^\s*/)[0].length;
2988        if (whitespacesAfterWord > 0) {
2989          wordEnd += whitespacesAfterWord;
2990        } else {
2991          var revTrim = revTextBeforeIdx.length - wordStart;
2992          var textBeforeWordStart = revTextBeforeIdx.substring(revTrim);
2993          var whitespacesBeforeWord = textBeforeWordStart.match(/^\s*/)[0].length;
2994          wordStart -= whitespacesBeforeWord;
2995        }
2996      }
2997
2998      return { start: Pos(cur.line, wordStart),
2999               end: Pos(cur.line, wordEnd) };
3000    }
3001
3002    function recordJumpPosition(cm, oldCur, newCur) {
3003      if (!cursorEqual(oldCur, newCur)) {
3004        vimGlobalState.jumpList.add(cm, oldCur, newCur);
3005      }
3006    }
3007
3008    function recordLastCharacterSearch(increment, args) {
3009        vimGlobalState.lastChararacterSearch.increment = increment;
3010        vimGlobalState.lastChararacterSearch.forward = args.forward;
3011        vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter;
3012    }
3013
3014    var symbolToMode = {
3015        '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',
3016        '[': 'section', ']': 'section',
3017        '*': 'comment', '/': 'comment',
3018        'm': 'method', 'M': 'method',
3019        '#': 'preprocess'
3020    };
3021    var findSymbolModes = {
3022      bracket: {
3023        isComplete: function(state) {
3024          if (state.nextCh === state.symb) {
3025            state.depth++;
3026            if (state.depth >= 1)return true;
3027          } else if (state.nextCh === state.reverseSymb) {
3028            state.depth--;
3029          }
3030          return false;
3031        }
3032      },
3033      section: {
3034        init: function(state) {
3035          state.curMoveThrough = true;
3036          state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';
3037        },
3038        isComplete: function(state) {
3039          return state.index === 0 && state.nextCh === state.symb;
3040        }
3041      },
3042      comment: {
3043        isComplete: function(state) {
3044          var found = state.lastCh === '*' && state.nextCh === '/';
3045          state.lastCh = state.nextCh;
3046          return found;
3047        }
3048      },
3049      // TODO: The original Vim implementation only operates on level 1 and 2.
3050      // The current implementation doesn't check for code block level and
3051      // therefore it operates on any levels.
3052      method: {
3053        init: function(state) {
3054          state.symb = (state.symb === 'm' ? '{' : '}');
3055          state.reverseSymb = state.symb === '{' ? '}' : '{';
3056        },
3057        isComplete: function(state) {
3058          if (state.nextCh === state.symb)return true;
3059          return false;
3060        }
3061      },
3062      preprocess: {
3063        init: function(state) {
3064          state.index = 0;
3065        },
3066        isComplete: function(state) {
3067          if (state.nextCh === '#') {
3068            var token = state.lineText.match(/#(\w+)/)[1];
3069            if (token === 'endif') {
3070              if (state.forward && state.depth === 0) {
3071                return true;
3072              }
3073              state.depth++;
3074            } else if (token === 'if') {
3075              if (!state.forward && state.depth === 0) {
3076                return true;
3077              }
3078              state.depth--;
3079            }
3080            if (token === 'else' && state.depth === 0)return true;
3081          }
3082          return false;
3083        }
3084      }
3085    };
3086    function findSymbol(cm, repeat, forward, symb) {
3087      var cur = copyCursor(cm.getCursor());
3088      var increment = forward ? 1 : -1;
3089      var endLine = forward ? cm.lineCount() : -1;
3090      var curCh = cur.ch;
3091      var line = cur.line;
3092      var lineText = cm.getLine(line);
3093      var state = {
3094        lineText: lineText,
3095        nextCh: lineText.charAt(curCh),
3096        lastCh: null,
3097        index: curCh,
3098        symb: symb,
3099        reverseSymb: (forward ?  { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
3100        forward: forward,
3101        depth: 0,
3102        curMoveThrough: false
3103      };
3104      var mode = symbolToMode[symb];
3105      if (!mode)return cur;
3106      var init = findSymbolModes[mode].init;
3107      var isComplete = findSymbolModes[mode].isComplete;
3108      if (init) { init(state); }
3109      while (line !== endLine && repeat) {
3110        state.index += increment;
3111        state.nextCh = state.lineText.charAt(state.index);
3112        if (!state.nextCh) {
3113          line += increment;
3114          state.lineText = cm.getLine(line) || '';
3115          if (increment > 0) {
3116            state.index = 0;
3117          } else {
3118            var lineLen = state.lineText.length;
3119            state.index = (lineLen > 0) ? (lineLen-1) : 0;
3120          }
3121          state.nextCh = state.lineText.charAt(state.index);
3122        }
3123        if (isComplete(state)) {
3124          cur.line = line;
3125          cur.ch = state.index;
3126          repeat--;
3127        }
3128      }
3129      if (state.nextCh || state.curMoveThrough) {
3130        return Pos(line, state.index);
3131      }
3132      return cur;
3133    }
3134
3135    /*
3136     * Returns the boundaries of the next word. If the cursor in the middle of
3137     * the word, then returns the boundaries of the current word, starting at
3138     * the cursor. If the cursor is at the start/end of a word, and we are going
3139     * forward/backward, respectively, find the boundaries of the next word.
3140     *
3141     * @param {CodeMirror} cm CodeMirror object.
3142     * @param {Cursor} cur The cursor position.
3143     * @param {boolean} forward True to search forward. False to search
3144     *     backward.
3145     * @param {boolean} bigWord True if punctuation count as part of the word.
3146     *     False if only [a-zA-Z0-9] characters count as part of the word.
3147     * @param {boolean} emptyLineIsWord True if empty lines should be treated
3148     *     as words.
3149     * @return {Object{from:number, to:number, line: number}} The boundaries of
3150     *     the word, or null if there are no more words.
3151     */
3152    function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {
3153      var lineNum = cur.line;
3154      var pos = cur.ch;
3155      var line = cm.getLine(lineNum);
3156      var dir = forward ? 1 : -1;
3157      var regexps = bigWord ? bigWordRegexp : wordRegexp;
3158
3159      if (emptyLineIsWord && line == '') {
3160        lineNum += dir;
3161        line = cm.getLine(lineNum);
3162        if (!isLine(cm, lineNum)) {
3163          return null;
3164        }
3165        pos = (forward) ? 0 : line.length;
3166      }
3167
3168      while (true) {
3169        if (emptyLineIsWord && line == '') {
3170          return { from: 0, to: 0, line: lineNum };
3171        }
3172        var stop = (dir > 0) ? line.length : -1;
3173        var wordStart = stop, wordEnd = stop;
3174        // Find bounds of next word.
3175        while (pos != stop) {
3176          var foundWord = false;
3177          for (var i = 0; i < regexps.length && !foundWord; ++i) {
3178            if (regexps[i].test(line.charAt(pos))) {
3179              wordStart = pos;
3180              // Advance to end of word.
3181              while (pos != stop && regexps[i].test(line.charAt(pos))) {
3182                pos += dir;
3183              }
3184              wordEnd = pos;
3185              foundWord = wordStart != wordEnd;
3186              if (wordStart == cur.ch && lineNum == cur.line &&
3187                  wordEnd == wordStart + dir) {
3188                // We started at the end of a word. Find the next one.
3189                continue;
3190              } else {
3191                return {
3192                  from: Math.min(wordStart, wordEnd + 1),
3193                  to: Math.max(wordStart, wordEnd),
3194                  line: lineNum };
3195              }
3196            }
3197          }
3198          if (!foundWord) {
3199            pos += dir;
3200          }
3201        }
3202        // Advance to next/prev line.
3203        lineNum += dir;
3204        if (!isLine(cm, lineNum)) {
3205          return null;
3206        }
3207        line = cm.getLine(lineNum);
3208        pos = (dir > 0) ? 0 : line.length;
3209      }
3210      // Should never get here.
3211      throw new Error('The impossible happened.');
3212    }
3213
3214    /**
3215     * @param {CodeMirror} cm CodeMirror object.
3216     * @param {Pos} cur The position to start from.
3217     * @param {int} repeat Number of words to move past.
3218     * @param {boolean} forward True to search forward. False to search
3219     *     backward.
3220     * @param {boolean} wordEnd True to move to end of word. False to move to
3221     *     beginning of word.
3222     * @param {boolean} bigWord True if punctuation count as part of the word.
3223     *     False if only alphabet characters count as part of the word.
3224     * @return {Cursor} The position the cursor should move to.
3225     */
3226    function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
3227      var curStart = copyCursor(cur);
3228      var words = [];
3229      if (forward && !wordEnd || !forward && wordEnd) {
3230        repeat++;
3231      }
3232      // For 'e', empty lines are not considered words, go figure.
3233      var emptyLineIsWord = !(forward && wordEnd);
3234      for (var i = 0; i < repeat; i++) {
3235        var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);
3236        if (!word) {
3237          var eodCh = lineLength(cm, cm.lastLine());
3238          words.push(forward
3239              ? {line: cm.lastLine(), from: eodCh, to: eodCh}
3240              : {line: 0, from: 0, to: 0});
3241          break;
3242        }
3243        words.push(word);
3244        cur = Pos(word.line, forward ? (word.to - 1) : word.from);
3245      }
3246      var shortCircuit = words.length != repeat;
3247      var firstWord = words[0];
3248      var lastWord = words.pop();
3249      if (forward && !wordEnd) {
3250        // w
3251        if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
3252          // We did not start in the middle of a word. Discard the extra word at the end.
3253          lastWord = words.pop();
3254        }
3255        return Pos(lastWord.line, lastWord.from);
3256      } else if (forward && wordEnd) {
3257        return Pos(lastWord.line, lastWord.to - 1);
3258      } else if (!forward && wordEnd) {
3259        // ge
3260        if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
3261          // We did not start in the middle of a word. Discard the extra word at the end.
3262          lastWord = words.pop();
3263        }
3264        return Pos(lastWord.line, lastWord.to);
3265      } else {
3266        // b
3267        return Pos(lastWord.line, lastWord.from);
3268      }
3269    }
3270
3271    function moveToCharacter(cm, repeat, forward, character) {
3272      var cur = cm.getCursor();
3273      var start = cur.ch;
3274      var idx;
3275      for (var i = 0; i < repeat; i ++) {
3276        var line = cm.getLine(cur.line);
3277        idx = charIdxInLine(start, line, character, forward, true);
3278        if (idx == -1) {
3279          return null;
3280        }
3281        start = idx;
3282      }
3283      return Pos(cm.getCursor().line, idx);
3284    }
3285
3286    function moveToColumn(cm, repeat) {
3287      // repeat is always >= 1, so repeat - 1 always corresponds
3288      // to the column we want to go to.
3289      var line = cm.getCursor().line;
3290      return clipCursorToContent(cm, Pos(line, repeat - 1));
3291    }
3292
3293    function updateMark(cm, vim, markName, pos) {
3294      if (!inArray(markName, validMarks)) {
3295        return;
3296      }
3297      if (vim.marks[markName]) {
3298        vim.marks[markName].clear();
3299      }
3300      vim.marks[markName] = cm.setBookmark(pos);
3301    }
3302
3303    function charIdxInLine(start, line, character, forward, includeChar) {
3304      // Search for char in line.
3305      // motion_options: {forward, includeChar}
3306      // If includeChar = true, include it too.
3307      // If forward = true, search forward, else search backwards.
3308      // If char is not found on this line, do nothing
3309      var idx;
3310      if (forward) {
3311        idx = line.indexOf(character, start + 1);
3312        if (idx != -1 && !includeChar) {
3313          idx -= 1;
3314        }
3315      } else {
3316        idx = line.lastIndexOf(character, start - 1);
3317        if (idx != -1 && !includeChar) {
3318          idx += 1;
3319        }
3320      }
3321      return idx;
3322    }
3323
3324    function findParagraph(cm, head, repeat, dir, inclusive) {
3325      var line = head.line;
3326      var min = cm.firstLine();
3327      var max = cm.lastLine();
3328      var start, end, i = line;
3329      function isEmpty(i) { return !cm.getLine(i); }
3330      function isBoundary(i, dir, any) {
3331        if (any) { return isEmpty(i) != isEmpty(i + dir); }
3332        return !isEmpty(i) && isEmpty(i + dir);
3333      }
3334      if (dir) {
3335        while (min <= i && i <= max && repeat > 0) {
3336          if (isBoundary(i, dir)) { repeat--; }
3337          i += dir;
3338        }
3339        return new Pos(i, 0);
3340      }
3341
3342      var vim = cm.state.vim;
3343      if (vim.visualLine && isBoundary(line, 1, true)) {
3344        var anchor = vim.sel.anchor;
3345        if (isBoundary(anchor.line, -1, true)) {
3346          if (!inclusive || anchor.line != line) {
3347            line += 1;
3348          }
3349        }
3350      }
3351      var startState = isEmpty(line);
3352      for (i = line; i <= max && repeat; i++) {
3353        if (isBoundary(i, 1, true)) {
3354          if (!inclusive || isEmpty(i) != startState) {
3355            repeat--;
3356          }
3357        }
3358      }
3359      end = new Pos(i, 0);
3360      // select boundary before paragraph for the last one
3361      if (i > max && !startState) { startState = true; }
3362      else { inclusive = false; }
3363      for (i = line; i > min; i--) {
3364        if (!inclusive || isEmpty(i) == startState || i == line) {
3365          if (isBoundary(i, -1, true)) { break; }
3366        }
3367      }
3368      start = new Pos(i, 0);
3369      return { start: start, end: end };
3370    }
3371
3372    // TODO: perhaps this finagling of start and end positions belonds
3373    // in codmirror/replaceRange?
3374    function selectCompanionObject(cm, head, symb, inclusive) {
3375      var cur = head, start, end;
3376
3377      var bracketRegexp = ({
3378        '(': /[()]/, ')': /[()]/,
3379        '[': /[[\]]/, ']': /[[\]]/,
3380        '{': /[{}]/, '}': /[{}]/})[symb];
3381      var openSym = ({
3382        '(': '(', ')': '(',
3383        '[': '[', ']': '[',
3384        '{': '{', '}': '{'})[symb];
3385      var curChar = cm.getLine(cur.line).charAt(cur.ch);
3386      // Due to the behavior of scanForBracket, we need to add an offset if the
3387      // cursor is on a matching open bracket.
3388      var offset = curChar === openSym ? 1 : 0;
3389
3390      start = cm.scanForBracket(Pos(cur.line, cur.ch + offset), -1, null, {'bracketRegex': bracketRegexp});
3391      end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, null, {'bracketRegex': bracketRegexp});
3392
3393      if (!start || !end) {
3394        return { start: cur, end: cur };
3395      }
3396
3397      start = start.pos;
3398      end = end.pos;
3399
3400      if ((start.line == end.line && start.ch > end.ch)
3401          || (start.line > end.line)) {
3402        var tmp = start;
3403        start = end;
3404        end = tmp;
3405      }
3406
3407      if (inclusive) {
3408        end.ch += 1;
3409      } else {
3410        start.ch += 1;
3411      }
3412
3413      return { start: start, end: end };
3414    }
3415
3416    // Takes in a symbol and a cursor and tries to simulate text objects that
3417    // have identical opening and closing symbols
3418    // TODO support across multiple lines
3419    function findBeginningAndEnd(cm, head, symb, inclusive) {
3420      var cur = copyCursor(head);
3421      var line = cm.getLine(cur.line);
3422      var chars = line.split('');
3423      var start, end, i, len;
3424      var firstIndex = chars.indexOf(symb);
3425
3426      // the decision tree is to always look backwards for the beginning first,
3427      // but if the cursor is in front of the first instance of the symb,
3428      // then move the cursor forward
3429      if (cur.ch < firstIndex) {
3430        cur.ch = firstIndex;
3431        // Why is this line even here???
3432        // cm.setCursor(cur.line, firstIndex+1);
3433      }
3434      // otherwise if the cursor is currently on the closing symbol
3435      else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
3436        end = cur.ch; // assign end to the current cursor
3437        --cur.ch; // make sure to look backwards
3438      }
3439
3440      // if we're currently on the symbol, we've got a start
3441      if (chars[cur.ch] == symb && !end) {
3442        start = cur.ch + 1; // assign start to ahead of the cursor
3443      } else {
3444        // go backwards to find the start
3445        for (i = cur.ch; i > -1 && !start; i--) {
3446          if (chars[i] == symb) {
3447            start = i + 1;
3448          }
3449        }
3450      }
3451
3452      // look forwards for the end symbol
3453      if (start && !end) {
3454        for (i = start, len = chars.length; i < len && !end; i++) {
3455          if (chars[i] == symb) {
3456            end = i;
3457          }
3458        }
3459      }
3460
3461      // nothing found
3462      if (!start || !end) {
3463        return { start: cur, end: cur };
3464      }
3465
3466      // include the symbols
3467      if (inclusive) {
3468        --start; ++end;
3469      }
3470
3471      return {
3472        start: Pos(cur.line, start),
3473        end: Pos(cur.line, end)
3474      };
3475    }
3476
3477    // Search functions
3478    defineOption('pcre', true, 'boolean');
3479    function SearchState() {}
3480    SearchState.prototype = {
3481      getQuery: function() {
3482        return vimGlobalState.query;
3483      },
3484      setQuery: function(query) {
3485        vimGlobalState.query = query;
3486      },
3487      getOverlay: function() {
3488        return this.searchOverlay;
3489      },
3490      setOverlay: function(overlay) {
3491        this.searchOverlay = overlay;
3492      },
3493      isReversed: function() {
3494        return vimGlobalState.isReversed;
3495      },
3496      setReversed: function(reversed) {
3497        vimGlobalState.isReversed = reversed;
3498      },
3499      getScrollbarAnnotate: function() {
3500        return this.annotate;
3501      },
3502      setScrollbarAnnotate: function(annotate) {
3503        this.annotate = annotate;
3504      }
3505    };
3506    function getSearchState(cm) {
3507      var vim = cm.state.vim;
3508      return vim.searchState_ || (vim.searchState_ = new SearchState());
3509    }
3510    function dialog(cm, template, shortText, onClose, options) {
3511      if (cm.openDialog) {
3512        cm.openDialog(template, onClose, { bottom: true, value: options.value,
3513            onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp });
3514      }
3515      else {
3516        onClose(prompt(shortText, ''));
3517      }
3518    }
3519    function splitBySlash(argString) {
3520      var slashes = findUnescapedSlashes(argString) || [];
3521      if (!slashes.length) return [];
3522      var tokens = [];
3523      // in case of strings like foo/bar
3524      if (slashes[0] !== 0) return;
3525      for (var i = 0; i < slashes.length; i++) {
3526        if (typeof slashes[i] == 'number')
3527          tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
3528      }
3529      return tokens;
3530    }
3531
3532    function findUnescapedSlashes(str) {
3533      var escapeNextChar = false;
3534      var slashes = [];
3535      for (var i = 0; i < str.length; i++) {
3536        var c = str.charAt(i);
3537        if (!escapeNextChar && c == '/') {
3538          slashes.push(i);
3539        }
3540        escapeNextChar = !escapeNextChar && (c == '\\');
3541      }
3542      return slashes;
3543    }
3544
3545    // Translates a search string from ex (vim) syntax into javascript form.
3546    function translateRegex(str) {
3547      // When these match, add a '\' if unescaped or remove one if escaped.
3548      var specials = '|(){';
3549      // Remove, but never add, a '\' for these.
3550      var unescape = '}';
3551      var escapeNextChar = false;
3552      var out = [];
3553      for (var i = -1; i < str.length; i++) {
3554        var c = str.charAt(i) || '';
3555        var n = str.charAt(i+1) || '';
3556        var specialComesNext = (n && specials.indexOf(n) != -1);
3557        if (escapeNextChar) {
3558          if (c !== '\\' || !specialComesNext) {
3559            out.push(c);
3560          }
3561          escapeNextChar = false;
3562        } else {
3563          if (c === '\\') {
3564            escapeNextChar = true;
3565            // Treat the unescape list as special for removing, but not adding '\'.
3566            if (n && unescape.indexOf(n) != -1) {
3567              specialComesNext = true;
3568            }
3569            // Not passing this test means removing a '\'.
3570            if (!specialComesNext || n === '\\') {
3571              out.push(c);
3572            }
3573          } else {
3574            out.push(c);
3575            if (specialComesNext && n !== '\\') {
3576              out.push('\\');
3577            }
3578          }
3579        }
3580      }
3581      return out.join('');
3582    }
3583
3584    // Translates the replace part of a search and replace from ex (vim) syntax into
3585    // javascript form.  Similar to translateRegex, but additionally fixes back references
3586    // (translates '\[0..9]' to '$[0..9]') and follows different rules for escaping '$'.
3587    function translateRegexReplace(str) {
3588      var escapeNextChar = false;
3589      var out = [];
3590      for (var i = -1; i < str.length; i++) {
3591        var c = str.charAt(i) || '';
3592        var n = str.charAt(i+1) || '';
3593        if (escapeNextChar) {
3594          // At any point in the loop, escapeNextChar is true if the previous
3595          // character was a '\' and was not escaped.
3596          out.push(c);
3597          escapeNextChar = false;
3598        } else {
3599          if (c === '\\') {
3600            escapeNextChar = true;
3601            if ((isNumber(n) || n === '$')) {
3602              out.push('$');
3603            } else if (n !== '/' && n !== '\\') {
3604              out.push('\\');
3605            }
3606          } else {
3607            if (c === '$') {
3608              out.push('$');
3609            }
3610            out.push(c);
3611            if (n === '/') {
3612              out.push('\\');
3613            }
3614          }
3615        }
3616      }
3617      return out.join('');
3618    }
3619
3620    // Unescape \ and / in the replace part, for PCRE mode.
3621    function unescapeRegexReplace(str) {
3622      var stream = new CodeMirror.StringStream(str);
3623      var output = [];
3624      while (!stream.eol()) {
3625        // Search for \.
3626        while (stream.peek() && stream.peek() != '\\') {
3627          output.push(stream.next());
3628        }
3629        if (stream.match('\\/', true)) {
3630          // \/ => /
3631          output.push('/');
3632        } else if (stream.match('\\\\', true)) {
3633          // \\ => \
3634          output.push('\\');
3635        } else {
3636          // Don't change anything
3637          output.push(stream.next());
3638        }
3639      }
3640      return output.join('');
3641    }
3642
3643    /**
3644     * Extract the regular expression from the query and return a Regexp object.
3645     * Returns null if the query is blank.
3646     * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.
3647     * If smartCase is passed in, and the query contains uppercase letters,
3648     *   then ignoreCase is overridden, and the 'i' flag will not be set.
3649     * If the query contains the /i in the flag part of the regular expression,
3650     *   then both ignoreCase and smartCase are ignored, and 'i' will be passed
3651     *   through to the Regex object.
3652     */
3653    function parseQuery(query, ignoreCase, smartCase) {
3654      // First update the last search register
3655      var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
3656      lastSearchRegister.setText(query);
3657      // Check if the query is already a regex.
3658      if (query instanceof RegExp) { return query; }
3659      // First try to extract regex + flags from the input. If no flags found,
3660      // extract just the regex. IE does not accept flags directly defined in
3661      // the regex string in the form /regex/flags
3662      var slashes = findUnescapedSlashes(query);
3663      var regexPart;
3664      var forceIgnoreCase;
3665      if (!slashes.length) {
3666        // Query looks like 'regexp'
3667        regexPart = query;
3668      } else {
3669        // Query looks like 'regexp/...'
3670        regexPart = query.substring(0, slashes[0]);
3671        var flagsPart = query.substring(slashes[0]);
3672        forceIgnoreCase = (flagsPart.indexOf('i') != -1);
3673      }
3674      if (!regexPart) {
3675        return null;
3676      }
3677      if (!getOption('pcre')) {
3678        regexPart = translateRegex(regexPart);
3679      }
3680      if (smartCase) {
3681        ignoreCase = (/^[^A-Z]*$/).test(regexPart);
3682      }
3683      var regexp = new RegExp(regexPart,
3684          (ignoreCase || forceIgnoreCase) ? 'i' : undefined);
3685      return regexp;
3686    }
3687    function showConfirm(cm, text) {
3688      if (cm.openNotification) {
3689        cm.openNotification('<span style="color: red">' + text + '</span>',
3690                            {bottom: true, duration: 5000});
3691      } else {
3692        alert(text);
3693      }
3694    }
3695    function makePrompt(prefix, desc) {
3696      var raw = '';
3697      if (prefix) {
3698        raw += '<span style="font-family: monospace">' + prefix + '</span>';
3699      }
3700      raw += '<input type="text"/> ' +
3701          '<span style="color: #888">';
3702      if (desc) {
3703        raw += '<span style="color: #888">';
3704        raw += desc;
3705        raw += '</span>';
3706      }
3707      return raw;
3708    }
3709    var searchPromptDesc = '(Javascript regexp)';
3710    function showPrompt(cm, options) {
3711      var shortText = (options.prefix || '') + ' ' + (options.desc || '');
3712      var prompt = makePrompt(options.prefix, options.desc);
3713      dialog(cm, prompt, shortText, options.onClose, options);
3714    }
3715    function regexEqual(r1, r2) {
3716      if (r1 instanceof RegExp && r2 instanceof RegExp) {
3717          var props = ['global', 'multiline', 'ignoreCase', 'source'];
3718          for (var i = 0; i < props.length; i++) {
3719              var prop = props[i];
3720              if (r1[prop] !== r2[prop]) {
3721                  return false;
3722              }
3723          }
3724          return true;
3725      }
3726      return false;
3727    }
3728    // Returns true if the query is valid.
3729    function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
3730      if (!rawQuery) {
3731        return;
3732      }
3733      var state = getSearchState(cm);
3734      var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
3735      if (!query) {
3736        return;
3737      }
3738      highlightSearchMatches(cm, query);
3739      if (regexEqual(query, state.getQuery())) {
3740        return query;
3741      }
3742      state.setQuery(query);
3743      return query;
3744    }
3745    function searchOverlay(query) {
3746      if (query.source.charAt(0) == '^') {
3747        var matchSol = true;
3748      }
3749      return {
3750        token: function(stream) {
3751          if (matchSol && !stream.sol()) {
3752            stream.skipToEnd();
3753            return;
3754          }
3755          var match = stream.match(query, false);
3756          if (match) {
3757            if (match[0].length == 0) {
3758              // Matched empty string, skip to next.
3759              stream.next();
3760              return 'searching';
3761            }
3762            if (!stream.sol()) {
3763              // Backtrack 1 to match \b
3764              stream.backUp(1);
3765              if (!query.exec(stream.next() + match[0])) {
3766                stream.next();
3767                return null;
3768              }
3769            }
3770            stream.match(query);
3771            return 'searching';
3772          }
3773          while (!stream.eol()) {
3774            stream.next();
3775            if (stream.match(query, false)) break;
3776          }
3777        },
3778        query: query
3779      };
3780    }
3781    function highlightSearchMatches(cm, query) {
3782      var searchState = getSearchState(cm);
3783      var overlay = searchState.getOverlay();
3784      if (!overlay || query != overlay.query) {
3785        if (overlay) {
3786          cm.removeOverlay(overlay);
3787        }
3788        overlay = searchOverlay(query);
3789        cm.addOverlay(overlay);
3790        if (cm.showMatchesOnScrollbar) {
3791          if (searchState.getScrollbarAnnotate()) {
3792            searchState.getScrollbarAnnotate().clear();
3793          }
3794          searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
3795        }
3796        searchState.setOverlay(overlay);
3797      }
3798    }
3799    function findNext(cm, prev, query, repeat) {
3800      if (repeat === undefined) { repeat = 1; }
3801      return cm.operation(function() {
3802        var pos = cm.getCursor();
3803        var cursor = cm.getSearchCursor(query, pos);
3804        for (var i = 0; i < repeat; i++) {
3805          var found = cursor.find(prev);
3806          if (i == 0 && found && cursorEqual(cursor.from(), pos)) { found = cursor.find(prev); }
3807          if (!found) {
3808            // SearchCursor may have returned null because it hit EOF, wrap
3809            // around and try again.
3810            cursor = cm.getSearchCursor(query,
3811                (prev) ? Pos(cm.lastLine()) : Pos(cm.firstLine(), 0) );
3812            if (!cursor.find(prev)) {
3813              return;
3814            }
3815          }
3816        }
3817        return cursor.from();
3818      });
3819    }
3820    function clearSearchHighlight(cm) {
3821      var state = getSearchState(cm);
3822      cm.removeOverlay(getSearchState(cm).getOverlay());
3823      state.setOverlay(null);
3824      if (state.getScrollbarAnnotate()) {
3825        state.getScrollbarAnnotate().clear();
3826        state.setScrollbarAnnotate(null);
3827      }
3828    }
3829    /**
3830     * Check if pos is in the specified range, INCLUSIVE.
3831     * Range can be specified with 1 or 2 arguments.
3832     * If the first range argument is an array, treat it as an array of line
3833     * numbers. Match pos against any of the lines.
3834     * If the first range argument is a number,
3835     *   if there is only 1 range argument, check if pos has the same line
3836     *       number
3837     *   if there are 2 range arguments, then check if pos is in between the two
3838     *       range arguments.
3839     */
3840    function isInRange(pos, start, end) {
3841      if (typeof pos != 'number') {
3842        // Assume it is a cursor position. Get the line number.
3843        pos = pos.line;
3844      }
3845      if (start instanceof Array) {
3846        return inArray(pos, start);
3847      } else {
3848        if (end) {
3849          return (pos >= start && pos <= end);
3850        } else {
3851          return pos == start;
3852        }
3853      }
3854    }
3855    function getUserVisibleLines(cm) {
3856      var scrollInfo = cm.getScrollInfo();
3857      var occludeToleranceTop = 6;
3858      var occludeToleranceBottom = 10;
3859      var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local');
3860      var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;
3861      var to = cm.coordsChar({left:0, top: bottomY}, 'local');
3862      return {top: from.line, bottom: to.line};
3863    }
3864
3865    // Ex command handling
3866    // Care must be taken when adding to the default Ex command map. For any
3867    // pair of commands that have a shared prefix, at least one of their
3868    // shortNames must not match the prefix of the other command.
3869    var defaultExCommandMap = [
3870      { name: 'map' },
3871      { name: 'imap', shortName: 'im' },
3872      { name: 'nmap', shortName: 'nm' },
3873      { name: 'vmap', shortName: 'vm' },
3874      { name: 'unmap' },
3875      { name: 'write', shortName: 'w' },
3876      { name: 'undo', shortName: 'u' },
3877      { name: 'redo', shortName: 'red' },
3878      { name: 'set', shortName: 'set' },
3879      { name: 'sort', shortName: 'sor' },
3880      { name: 'substitute', shortName: 's', possiblyAsync: true },
3881      { name: 'nohlsearch', shortName: 'noh' },
3882      { name: 'delmarks', shortName: 'delm' },
3883      { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
3884      { name: 'global', shortName: 'g' }
3885    ];
3886    var ExCommandDispatcher = function() {
3887      this.buildCommandMap_();
3888    };
3889    ExCommandDispatcher.prototype = {
3890      processCommand: function(cm, input, opt_params) {
3891        var vim = cm.state.vim;
3892        var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
3893        var previousCommand = commandHistoryRegister.toString();
3894        if (vim.visualMode) {
3895          exitVisualMode(cm);
3896        }
3897        var inputStream = new CodeMirror.StringStream(input);
3898        // update ": with the latest command whether valid or invalid
3899        commandHistoryRegister.setText(input);
3900        var params = opt_params || {};
3901        params.input = input;
3902        try {
3903          this.parseInput_(cm, inputStream, params);
3904        } catch(e) {
3905          showConfirm(cm, e);
3906          throw e;
3907        }
3908        var command;
3909        var commandName;
3910        if (!params.commandName) {
3911          // If only a line range is defined, move to the line.
3912          if (params.line !== undefined) {
3913            commandName = 'move';
3914          }
3915        } else {
3916          command = this.matchCommand_(params.commandName);
3917          if (command) {
3918            commandName = command.name;
3919            if (command.excludeFromCommandHistory) {
3920              commandHistoryRegister.setText(previousCommand);
3921            }
3922            this.parseCommandArgs_(inputStream, params, command);
3923            if (command.type == 'exToKey') {
3924              // Handle Ex to Key mapping.
3925              for (var i = 0; i < command.toKeys.length; i++) {
3926                CodeMirror.Vim.handleKey(cm, command.toKeys[i], 'mapping');
3927              }
3928              return;
3929            } else if (command.type == 'exToEx') {
3930              // Handle Ex to Ex mapping.
3931              this.processCommand(cm, command.toInput);
3932              return;
3933            }
3934          }
3935        }
3936        if (!commandName) {
3937          showConfirm(cm, 'Not an editor command ":' + input + '"');
3938          return;
3939        }
3940        try {
3941          exCommands[commandName](cm, params);
3942          // Possibly asynchronous commands (e.g. substitute, which might have a
3943          // user confirmation), are responsible for calling the callback when
3944          // done. All others have it taken care of for them here.
3945          if ((!command || !command.possiblyAsync) && params.callback) {
3946            params.callback();
3947          }
3948        } catch(e) {
3949          showConfirm(cm, e);
3950          throw e;
3951        }
3952      },
3953      parseInput_: function(cm, inputStream, result) {
3954        inputStream.eatWhile(':');
3955        // Parse range.
3956        if (inputStream.eat('%')) {
3957          result.line = cm.firstLine();
3958          result.lineEnd = cm.lastLine();
3959        } else {
3960          result.line = this.parseLineSpec_(cm, inputStream);
3961          if (result.line !== undefined && inputStream.eat(',')) {
3962            result.lineEnd = this.parseLineSpec_(cm, inputStream);
3963          }
3964        }
3965
3966        // Parse command name.
3967        var commandMatch = inputStream.match(/^(\w+)/);
3968        if (commandMatch) {
3969          result.commandName = commandMatch[1];
3970        } else {
3971          result.commandName = inputStream.match(/.*/)[0];
3972        }
3973
3974        return result;
3975      },
3976      parseLineSpec_: function(cm, inputStream) {
3977        var numberMatch = inputStream.match(/^(\d+)/);
3978        if (numberMatch) {
3979          return parseInt(numberMatch[1], 10) - 1;
3980        }
3981        switch (inputStream.next()) {
3982          case '.':
3983            return cm.getCursor().line;
3984          case '$':
3985            return cm.lastLine();
3986          case '\'':
3987            var mark = cm.state.vim.marks[inputStream.next()];
3988            if (mark && mark.find()) {
3989              return mark.find().line;
3990            }
3991            throw new Error('Mark not set');
3992          default:
3993            inputStream.backUp(1);
3994            return undefined;
3995        }
3996      },
3997      parseCommandArgs_: function(inputStream, params, command) {
3998        if (inputStream.eol()) {
3999          return;
4000        }
4001        params.argString = inputStream.match(/.*/)[0];
4002        // Parse command-line arguments
4003        var delim = command.argDelimiter || /\s+/;
4004        var args = trim(params.argString).split(delim);
4005        if (args.length && args[0]) {
4006          params.args = args;
4007        }
4008      },
4009      matchCommand_: function(commandName) {
4010        // Return the command in the command map that matches the shortest
4011        // prefix of the passed in command name. The match is guaranteed to be
4012        // unambiguous if the defaultExCommandMap's shortNames are set up
4013        // correctly. (see @code{defaultExCommandMap}).
4014        for (var i = commandName.length; i > 0; i--) {
4015          var prefix = commandName.substring(0, i);
4016          if (this.commandMap_[prefix]) {
4017            var command = this.commandMap_[prefix];
4018            if (command.name.indexOf(commandName) === 0) {
4019              return command;
4020            }
4021          }
4022        }
4023        return null;
4024      },
4025      buildCommandMap_: function() {
4026        this.commandMap_ = {};
4027        for (var i = 0; i < defaultExCommandMap.length; i++) {
4028          var command = defaultExCommandMap[i];
4029          var key = command.shortName || command.name;
4030          this.commandMap_[key] = command;
4031        }
4032      },
4033      map: function(lhs, rhs, ctx) {
4034        if (lhs != ':' && lhs.charAt(0) == ':') {
4035          if (ctx) { throw Error('Mode not supported for ex mappings'); }
4036          var commandName = lhs.substring(1);
4037          if (rhs != ':' && rhs.charAt(0) == ':') {
4038            // Ex to Ex mapping
4039            this.commandMap_[commandName] = {
4040              name: commandName,
4041              type: 'exToEx',
4042              toInput: rhs.substring(1),
4043              user: true
4044            };
4045          } else {
4046            // Ex to key mapping
4047            this.commandMap_[commandName] = {
4048              name: commandName,
4049              type: 'exToKey',
4050              toKeys: rhs,
4051              user: true
4052            };
4053          }
4054        } else {
4055          if (rhs != ':' && rhs.charAt(0) == ':') {
4056            // Key to Ex mapping.
4057            var mapping = {
4058              keys: lhs,
4059              type: 'keyToEx',
4060              exArgs: { input: rhs.substring(1) },
4061              user: true};
4062            if (ctx) { mapping.context = ctx; }
4063            defaultKeymap.unshift(mapping);
4064          } else {
4065            // Key to key mapping
4066            var mapping = {
4067              keys: lhs,
4068              type: 'keyToKey',
4069              toKeys: rhs,
4070              user: true
4071            };
4072            if (ctx) { mapping.context = ctx; }
4073            defaultKeymap.unshift(mapping);
4074          }
4075        }
4076      },
4077      unmap: function(lhs, ctx) {
4078        if (lhs != ':' && lhs.charAt(0) == ':') {
4079          // Ex to Ex or Ex to key mapping
4080          if (ctx) { throw Error('Mode not supported for ex mappings'); }
4081          var commandName = lhs.substring(1);
4082          if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
4083            delete this.commandMap_[commandName];
4084            return;
4085          }
4086        } else {
4087          // Key to Ex or key to key mapping
4088          var keys = lhs;
4089          for (var i = 0; i < defaultKeymap.length; i++) {
4090            if (keys == defaultKeymap[i].keys
4091                && defaultKeymap[i].context === ctx
4092                && defaultKeymap[i].user) {
4093              defaultKeymap.splice(i, 1);
4094              return;
4095            }
4096          }
4097        }
4098        throw Error('No such mapping.');
4099      }
4100    };
4101
4102    var exCommands = {
4103      map: function(cm, params, ctx) {
4104        var mapArgs = params.args;
4105        if (!mapArgs || mapArgs.length < 2) {
4106          if (cm) {
4107            showConfirm(cm, 'Invalid mapping: ' + params.input);
4108          }
4109          return;
4110        }
4111        exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
4112      },
4113      imap: function(cm, params) { this.map(cm, params, 'insert'); },
4114      nmap: function(cm, params) { this.map(cm, params, 'normal'); },
4115      vmap: function(cm, params) { this.map(cm, params, 'visual'); },
4116      unmap: function(cm, params, ctx) {
4117        var mapArgs = params.args;
4118        if (!mapArgs || mapArgs.length < 1) {
4119          if (cm) {
4120            showConfirm(cm, 'No such mapping: ' + params.input);
4121          }
4122          return;
4123        }
4124        exCommandDispatcher.unmap(mapArgs[0], ctx);
4125      },
4126      move: function(cm, params) {
4127        commandDispatcher.processCommand(cm, cm.state.vim, {
4128            type: 'motion',
4129            motion: 'moveToLineOrEdgeOfDocument',
4130            motionArgs: { forward: false, explicitRepeat: true,
4131              linewise: true },
4132            repeatOverride: params.line+1});
4133      },
4134      set: function(cm, params) {
4135        var setArgs = params.args;
4136        if (!setArgs || setArgs.length < 1) {
4137          if (cm) {
4138            showConfirm(cm, 'Invalid mapping: ' + params.input);
4139          }
4140          return;
4141        }
4142        var expr = setArgs[0].split('=');
4143        var optionName = expr[0];
4144        var value = expr[1];
4145        var forceGet = false;
4146
4147        if (optionName.charAt(optionName.length - 1) == '?') {
4148          // If post-fixed with ?, then the set is actually a get.
4149          if (value) { throw Error('Trailing characters: ' + params.argString); }
4150          optionName = optionName.substring(0, optionName.length - 1);
4151          forceGet = true;
4152        }
4153        if (value === undefined && optionName.substring(0, 2) == 'no') {
4154          // To set boolean options to false, the option name is prefixed with
4155          // 'no'.
4156          optionName = optionName.substring(2);
4157          value = false;
4158        }
4159        var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
4160        if (optionIsBoolean && value == undefined) {
4161          // Calling set with a boolean option sets it to true.
4162          value = true;
4163        }
4164        if (!optionIsBoolean && !value || forceGet) {
4165          var oldValue = getOption(optionName);
4166          // If no value is provided, then we assume this is a get.
4167          if (oldValue === true || oldValue === false) {
4168            showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
4169          } else {
4170            showConfirm(cm, '  ' + optionName + '=' + oldValue);
4171          }
4172        } else {
4173          setOption(optionName, value);
4174        }
4175      },
4176      registers: function(cm,params) {
4177        var regArgs = params.args;
4178        var registers = vimGlobalState.registerController.registers;
4179        var regInfo = '----------Registers----------<br><br>';
4180        if (!regArgs) {
4181          for (var registerName in registers) {
4182            var text = registers[registerName].toString();
4183            if (text.length) {
4184              regInfo += '"' + registerName + '    ' + text + '<br>';
4185            }
4186          }
4187        } else {
4188          var registerName;
4189          regArgs = regArgs.join('');
4190          for (var i = 0; i < regArgs.length; i++) {
4191            registerName = regArgs.charAt(i);
4192            if (!vimGlobalState.registerController.isValidRegister(registerName)) {
4193              continue;
4194            }
4195            var register = registers[registerName] || new Register();
4196            regInfo += '"' + registerName + '    ' + register.toString() + '<br>';
4197          }
4198        }
4199        showConfirm(cm, regInfo);
4200      },
4201      sort: function(cm, params) {
4202        var reverse, ignoreCase, unique, number;
4203        function parseArgs() {
4204          if (params.argString) {
4205            var args = new CodeMirror.StringStream(params.argString);
4206            if (args.eat('!')) { reverse = true; }
4207            if (args.eol()) { return; }
4208            if (!args.eatSpace()) { return 'Invalid arguments'; }
4209            var opts = args.match(/[a-z]+/);
4210            if (opts) {
4211              opts = opts[0];
4212              ignoreCase = opts.indexOf('i') != -1;
4213              unique = opts.indexOf('u') != -1;
4214              var decimal = opts.indexOf('d') != -1 && 1;
4215              var hex = opts.indexOf('x') != -1 && 1;
4216              var octal = opts.indexOf('o') != -1 && 1;
4217              if (decimal + hex + octal > 1) { return 'Invalid arguments'; }
4218              number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
4219            }
4220            if (args.eatSpace() && args.match(/\/.*\//)) { 'patterns not supported'; }
4221          }
4222        }
4223        var err = parseArgs();
4224        if (err) {
4225          showConfirm(cm, err + ': ' + params.argString);
4226          return;
4227        }
4228        var lineStart = params.line || cm.firstLine();
4229        var lineEnd = params.lineEnd || params.line || cm.lastLine();
4230        if (lineStart == lineEnd) { return; }
4231        var curStart = Pos(lineStart, 0);
4232        var curEnd = Pos(lineEnd, lineLength(cm, lineEnd));
4233        var text = cm.getRange(curStart, curEnd).split('\n');
4234        var numberRegex = (number == 'decimal') ? /(-?)([\d]+)/ :
4235           (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :
4236           (number == 'octal') ? /([0-7]+)/ : null;
4237        var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;
4238        var numPart = [], textPart = [];
4239        if (number) {
4240          for (var i = 0; i < text.length; i++) {
4241            if (numberRegex.exec(text[i])) {
4242              numPart.push(text[i]);
4243            } else {
4244              textPart.push(text[i]);
4245            }
4246          }
4247        } else {
4248          textPart = text;
4249        }
4250        function compareFn(a, b) {
4251          if (reverse) { var tmp; tmp = a; a = b; b = tmp; }
4252          if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }
4253          var anum = number && numberRegex.exec(a);
4254          var bnum = number && numberRegex.exec(b);
4255          if (!anum) { return a < b ? -1 : 1; }
4256          anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);
4257          bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);
4258          return anum - bnum;
4259        }
4260        numPart.sort(compareFn);
4261        textPart.sort(compareFn);
4262        text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);
4263        if (unique) { // Remove duplicate lines
4264          var textOld = text;
4265          var lastLine;
4266          text = [];
4267          for (var i = 0; i < textOld.length; i++) {
4268            if (textOld[i] != lastLine) {
4269              text.push(textOld[i]);
4270            }
4271            lastLine = textOld[i];
4272          }
4273        }
4274        cm.replaceRange(text.join('\n'), curStart, curEnd);
4275      },
4276      global: function(cm, params) {
4277        // a global command is of the form
4278        // :[range]g/pattern/[cmd]
4279        // argString holds the string /pattern/[cmd]
4280        var argString = params.argString;
4281        if (!argString) {
4282          showConfirm(cm, 'Regular Expression missing from global');
4283          return;
4284        }
4285        // range is specified here
4286        var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
4287        var lineEnd = params.lineEnd || params.line || cm.lastLine();
4288        // get the tokens from argString
4289        var tokens = splitBySlash(argString);
4290        var regexPart = argString, cmd;
4291        if (tokens.length) {
4292          regexPart = tokens[0];
4293          cmd = tokens.slice(1, tokens.length).join('/');
4294        }
4295        if (regexPart) {
4296          // If regex part is empty, then use the previous query. Otherwise
4297          // use the regex part as the new query.
4298          try {
4299           updateSearchQuery(cm, regexPart, true /** ignoreCase */,
4300             true /** smartCase */);
4301          } catch (e) {
4302           showConfirm(cm, 'Invalid regex: ' + regexPart);
4303           return;
4304          }
4305        }
4306        // now that we have the regexPart, search for regex matches in the
4307        // specified range of lines
4308        var query = getSearchState(cm).getQuery();
4309        var matchedLines = [], content = '';
4310        for (var i = lineStart; i <= lineEnd; i++) {
4311          var matched = query.test(cm.getLine(i));
4312          if (matched) {
4313            matchedLines.push(i+1);
4314            content+= cm.getLine(i) + '<br>';
4315          }
4316        }
4317        // if there is no [cmd], just display the list of matched lines
4318        if (!cmd) {
4319          showConfirm(cm, content);
4320          return;
4321        }
4322        var index = 0;
4323        var nextCommand = function() {
4324          if (index < matchedLines.length) {
4325            var command = matchedLines[index] + cmd;
4326            exCommandDispatcher.processCommand(cm, command, {
4327              callback: nextCommand
4328            });
4329          }
4330          index++;
4331        };
4332        nextCommand();
4333      },
4334      substitute: function(cm, params) {
4335        if (!cm.getSearchCursor) {
4336          throw new Error('Search feature not available. Requires searchcursor.js or ' +
4337              'any other getSearchCursor implementation.');
4338        }
4339        var argString = params.argString;
4340        var tokens = argString ? splitBySlash(argString) : [];
4341        var regexPart, replacePart = '', trailing, flagsPart, count;
4342        var confirm = false; // Whether to confirm each replace.
4343        var global = false; // True to replace all instances on a line, false to replace only 1.
4344        if (tokens.length) {
4345          regexPart = tokens[0];
4346          replacePart = tokens[1];
4347          if (replacePart !== undefined) {
4348            if (getOption('pcre')) {
4349              replacePart = unescapeRegexReplace(replacePart);
4350            } else {
4351              replacePart = translateRegexReplace(replacePart);
4352            }
4353            vimGlobalState.lastSubstituteReplacePart = replacePart;
4354          }
4355          trailing = tokens[2] ? tokens[2].split(' ') : [];
4356        } else {
4357          // either the argString is empty or its of the form ' hello/world'
4358          // actually splitBySlash returns a list of tokens
4359          // only if the string starts with a '/'
4360          if (argString && argString.length) {
4361            showConfirm(cm, 'Substitutions should be of the form ' +
4362                ':s/pattern/replace/');
4363            return;
4364          }
4365        }
4366        // After the 3rd slash, we can have flags followed by a space followed
4367        // by count.
4368        if (trailing) {
4369          flagsPart = trailing[0];
4370          count = parseInt(trailing[1]);
4371          if (flagsPart) {
4372            if (flagsPart.indexOf('c') != -1) {
4373              confirm = true;
4374              flagsPart.replace('c', '');
4375            }
4376            if (flagsPart.indexOf('g') != -1) {
4377              global = true;
4378              flagsPart.replace('g', '');
4379            }
4380            regexPart = regexPart + '/' + flagsPart;
4381          }
4382        }
4383        if (regexPart) {
4384          // If regex part is empty, then use the previous query. Otherwise use
4385          // the regex part as the new query.
4386          try {
4387            updateSearchQuery(cm, regexPart, true /** ignoreCase */,
4388              true /** smartCase */);
4389          } catch (e) {
4390            showConfirm(cm, 'Invalid regex: ' + regexPart);
4391            return;
4392          }
4393        }
4394        replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
4395        if (replacePart === undefined) {
4396          showConfirm(cm, 'No previous substitute regular expression');
4397          return;
4398        }
4399        var state = getSearchState(cm);
4400        var query = state.getQuery();
4401        var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
4402        var lineEnd = params.lineEnd || lineStart;
4403        if (count) {
4404          lineStart = lineEnd;
4405          lineEnd = lineStart + count - 1;
4406        }
4407        var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
4408        var cursor = cm.getSearchCursor(query, startPos);
4409        doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
4410      },
4411      redo: CodeMirror.commands.redo,
4412      undo: CodeMirror.commands.undo,
4413      write: function(cm) {
4414        if (CodeMirror.commands.save) {
4415          // If a save command is defined, call it.
4416          CodeMirror.commands.save(cm);
4417        } else {
4418          // Saves to text area if no save command is defined.
4419          cm.save();
4420        }
4421      },
4422      nohlsearch: function(cm) {
4423        clearSearchHighlight(cm);
4424      },
4425      delmarks: function(cm, params) {
4426        if (!params.argString || !trim(params.argString)) {
4427          showConfirm(cm, 'Argument required');
4428          return;
4429        }
4430
4431        var state = cm.state.vim;
4432        var stream = new CodeMirror.StringStream(trim(params.argString));
4433        while (!stream.eol()) {
4434          stream.eatSpace();
4435
4436          // Record the streams position at the beginning of the loop for use
4437          // in error messages.
4438          var count = stream.pos;
4439
4440          if (!stream.match(/[a-zA-Z]/, false)) {
4441            showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
4442            return;
4443          }
4444
4445          var sym = stream.next();
4446          // Check if this symbol is part of a range
4447          if (stream.match('-', true)) {
4448            // This symbol is part of a range.
4449
4450            // The range must terminate at an alphabetic character.
4451            if (!stream.match(/[a-zA-Z]/, false)) {
4452              showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
4453              return;
4454            }
4455
4456            var startMark = sym;
4457            var finishMark = stream.next();
4458            // The range must terminate at an alphabetic character which
4459            // shares the same case as the start of the range.
4460            if (isLowerCase(startMark) && isLowerCase(finishMark) ||
4461                isUpperCase(startMark) && isUpperCase(finishMark)) {
4462              var start = startMark.charCodeAt(0);
4463              var finish = finishMark.charCodeAt(0);
4464              if (start >= finish) {
4465                showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
4466                return;
4467              }
4468
4469              // Because marks are always ASCII values, and we have
4470              // determined that they are the same case, we can use
4471              // their char codes to iterate through the defined range.
4472              for (var j = 0; j <= finish - start; j++) {
4473                var mark = String.fromCharCode(start + j);
4474                delete state.marks[mark];
4475              }
4476            } else {
4477              showConfirm(cm, 'Invalid argument: ' + startMark + '-');
4478              return;
4479            }
4480          } else {
4481            // This symbol is a valid mark, and is not part of a range.
4482            delete state.marks[sym];
4483          }
4484        }
4485      }
4486    };
4487
4488    var exCommandDispatcher = new ExCommandDispatcher();
4489
4490    /**
4491    * @param {CodeMirror} cm CodeMirror instance we are in.
4492    * @param {boolean} confirm Whether to confirm each replace.
4493    * @param {Cursor} lineStart Line to start replacing from.
4494    * @param {Cursor} lineEnd Line to stop replacing at.
4495    * @param {RegExp} query Query for performing matches with.
4496    * @param {string} replaceWith Text to replace matches with. May contain $1,
4497    *     $2, etc for replacing captured groups using Javascript replace.
4498    * @param {function()} callback A callback for when the replace is done.
4499    */
4500    function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,
4501        replaceWith, callback) {
4502      // Set up all the functions.
4503      cm.state.vim.exMode = true;
4504      var done = false;
4505      var lastPos = searchCursor.from();
4506      function replaceAll() {
4507        cm.operation(function() {
4508          while (!done) {
4509            replace();
4510            next();
4511          }
4512          stop();
4513        });
4514      }
4515      function replace() {
4516        var text = cm.getRange(searchCursor.from(), searchCursor.to());
4517        var newText = text.replace(query, replaceWith);
4518        searchCursor.replace(newText);
4519      }
4520      function next() {
4521        var found;
4522        // The below only loops to skip over multiple occurrences on the same
4523        // line when 'global' is not true.
4524        while(found = searchCursor.findNext() &&
4525              isInRange(searchCursor.from(), lineStart, lineEnd)) {
4526          if (!global && lastPos && searchCursor.from().line == lastPos.line) {
4527            continue;
4528          }
4529          cm.scrollIntoView(searchCursor.from(), 30);
4530          cm.setSelection(searchCursor.from(), searchCursor.to());
4531          lastPos = searchCursor.from();
4532          done = false;
4533          return;
4534        }
4535        done = true;
4536      }
4537      function stop(close) {
4538        if (close) { close(); }
4539        cm.focus();
4540        if (lastPos) {
4541          cm.setCursor(lastPos);
4542          var vim = cm.state.vim;
4543          vim.exMode = false;
4544          vim.lastHPos = vim.lastHSPos = lastPos.ch;
4545        }
4546        if (callback) { callback(); }
4547      }
4548      function onPromptKeyDown(e, _value, close) {
4549        // Swallow all keys.
4550        CodeMirror.e_stop(e);
4551        var keyName = CodeMirror.keyName(e);
4552        switch (keyName) {
4553          case 'Y':
4554            replace(); next(); break;
4555          case 'N':
4556            next(); break;
4557          case 'A':
4558            // replaceAll contains a call to close of its own. We don't want it
4559            // to fire too early or multiple times.
4560            var savedCallback = callback;
4561            callback = undefined;
4562            cm.operation(replaceAll);
4563            callback = savedCallback;
4564            break;
4565          case 'L':
4566            replace();
4567            // fall through and exit.
4568          case 'Q':
4569          case 'Esc':
4570          case 'Ctrl-C':
4571          case 'Ctrl-[':
4572            stop(close);
4573            break;
4574        }
4575        if (done) { stop(close); }
4576        return true;
4577      }
4578
4579      // Actually do replace.
4580      next();
4581      if (done) {
4582        showConfirm(cm, 'No matches for ' + query.source);
4583        return;
4584      }
4585      if (!confirm) {
4586        replaceAll();
4587        if (callback) { callback(); };
4588        return;
4589      }
4590      showPrompt(cm, {
4591        prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
4592        onKeyDown: onPromptKeyDown
4593      });
4594    }
4595
4596    CodeMirror.keyMap.vim = {
4597      attach: attachVimMap,
4598      detach: detachVimMap,
4599      call: cmKey
4600    };
4601
4602    function exitInsertMode(cm) {
4603      var vim = cm.state.vim;
4604      var macroModeState = vimGlobalState.macroModeState;
4605      var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
4606      var isPlaying = macroModeState.isPlaying;
4607      var lastChange = macroModeState.lastInsertModeChanges;
4608      // In case of visual block, the insertModeChanges are not saved as a
4609      // single word, so we convert them to a single word
4610      // so as to update the ". register as expected in real vim.
4611      var text = [];
4612      if (!isPlaying) {
4613        var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
4614        var changes = lastChange.changes;
4615        var text = [];
4616        var i = 0;
4617        // In case of multiple selections in blockwise visual,
4618        // the inserted text, for example: 'f<Backspace>oo', is stored as
4619        // 'f', 'f', InsertModeKey 'o', 'o', 'o', 'o'. (if you have a block with 2 lines).
4620        // We push the contents of the changes array as per the following:
4621        // 1. In case of InsertModeKey, just increment by 1.
4622        // 2. In case of a character, jump by selLength (2 in the example).
4623        while (i < changes.length) {
4624          // This loop will convert 'ff<bs>oooo' to 'f<bs>oo'.
4625          text.push(changes[i]);
4626          if (changes[i] instanceof InsertModeKey) {
4627             i++;
4628          } else {
4629             i+= selLength;
4630          }
4631        }
4632        lastChange.changes = text;
4633        cm.off('change', onChange);
4634        CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
4635      }
4636      if (!isPlaying && vim.insertModeRepeat > 1) {
4637        // Perform insert mode repeat for commands like 3,a and 3,o.
4638        repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,
4639            true /** repeatForInsert */);
4640        vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
4641      }
4642      delete vim.insertModeRepeat;
4643      vim.insertMode = false;
4644      cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);
4645      cm.setOption('keyMap', 'vim');
4646      cm.setOption('disableInput', true);
4647      cm.toggleOverwrite(false); // exit replace mode if we were in it.
4648      // update the ". register before exiting insert mode
4649      insertModeChangeRegister.setText(lastChange.changes.join(''));
4650      CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
4651      if (macroModeState.isRecording) {
4652        logInsertModeChange(macroModeState);
4653      }
4654    }
4655
4656    function _mapCommand(command) {
4657      defaultKeymap.push(command);
4658    }
4659
4660    function mapCommand(keys, type, name, args, extra) {
4661      var command = {keys: keys, type: type};
4662      command[type] = name;
4663      command[type + "Args"] = args;
4664      for (var key in extra)
4665        command[key] = extra[key];
4666      _mapCommand(command);
4667    }
4668
4669    // The timeout in milliseconds for the two-character ESC keymap should be
4670    // adjusted according to your typing speed to prevent false positives.
4671    defineOption('insertModeEscKeysTimeout', 200, 'number');
4672
4673    CodeMirror.keyMap['vim-insert'] = {
4674      // TODO: override navigation keys so that Esc will cancel automatic
4675      // indentation from o, O, i_<CR>
4676      'Ctrl-N': 'autocomplete',
4677      'Ctrl-P': 'autocomplete',
4678      'Enter': function(cm) {
4679        var fn = CodeMirror.commands.newlineAndIndentContinueComment ||
4680            CodeMirror.commands.newlineAndIndent;
4681        fn(cm);
4682      },
4683      fallthrough: ['default'],
4684      attach: attachVimMap,
4685      detach: detachVimMap,
4686      call: cmKey
4687    };
4688
4689    CodeMirror.keyMap['vim-replace'] = {
4690      'Backspace': 'goCharLeft',
4691      fallthrough: ['vim-insert'],
4692      attach: attachVimMap,
4693      detach: detachVimMap,
4694      call: cmKey
4695    };
4696
4697    function executeMacroRegister(cm, vim, macroModeState, registerName) {
4698      var register = vimGlobalState.registerController.getRegister(registerName);
4699      var keyBuffer = register.keyBuffer;
4700      var imc = 0;
4701      macroModeState.isPlaying = true;
4702      macroModeState.replaySearchQueries = register.searchQueries.slice(0);
4703      for (var i = 0; i < keyBuffer.length; i++) {
4704        var text = keyBuffer[i];
4705        var match, key;
4706        while (text) {
4707          // Pull off one command key, which is either a single character
4708          // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
4709          match = (/<\w+-.+?>|<\w+>|./).exec(text);
4710          key = match[0];
4711          text = text.substring(match.index + key.length);
4712          CodeMirror.Vim.handleKey(cm, key, 'macro');
4713          if (vim.insertMode) {
4714            var changes = register.insertModeChanges[imc++].changes;
4715            vimGlobalState.macroModeState.lastInsertModeChanges.changes =
4716                changes;
4717            repeatInsertModeChanges(cm, changes, 1);
4718            exitInsertMode(cm);
4719          }
4720        }
4721      };
4722      macroModeState.isPlaying = false;
4723    }
4724
4725    function logKey(macroModeState, key) {
4726      if (macroModeState.isPlaying) { return; }
4727      var registerName = macroModeState.latestRegister;
4728      var register = vimGlobalState.registerController.getRegister(registerName);
4729      if (register) {
4730        register.pushText(key);
4731      }
4732    }
4733
4734    function logInsertModeChange(macroModeState) {
4735      if (macroModeState.isPlaying) { return; }
4736      var registerName = macroModeState.latestRegister;
4737      var register = vimGlobalState.registerController.getRegister(registerName);
4738      if (register) {
4739        register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
4740      }
4741    }
4742
4743    function logSearchQuery(macroModeState, query) {
4744      if (macroModeState.isPlaying) { return; }
4745      var registerName = macroModeState.latestRegister;
4746      var register = vimGlobalState.registerController.getRegister(registerName);
4747      if (register) {
4748        register.pushSearchQuery(query);
4749      }
4750    }
4751
4752    /**
4753     * Listens for changes made in insert mode.
4754     * Should only be active in insert mode.
4755     */
4756    function onChange(_cm, changeObj) {
4757      var macroModeState = vimGlobalState.macroModeState;
4758      var lastChange = macroModeState.lastInsertModeChanges;
4759      if (!macroModeState.isPlaying) {
4760        while(changeObj) {
4761          lastChange.expectCursorActivityForChange = true;
4762          if (changeObj.origin == '+input' || changeObj.origin == 'paste'
4763              || changeObj.origin === undefined /* only in testing */) {
4764            var text = changeObj.text.join('\n');
4765            lastChange.changes.push(text);
4766          }
4767          // Change objects may be chained with next.
4768          changeObj = changeObj.next;
4769        }
4770      }
4771    }
4772
4773    /**
4774    * Listens for any kind of cursor activity on CodeMirror.
4775    */
4776    function onCursorActivity(cm) {
4777      var vim = cm.state.vim;
4778      if (vim.insertMode) {
4779        // Tracking cursor activity in insert mode (for macro support).
4780        var macroModeState = vimGlobalState.macroModeState;
4781        if (macroModeState.isPlaying) { return; }
4782        var lastChange = macroModeState.lastInsertModeChanges;
4783        if (lastChange.expectCursorActivityForChange) {
4784          lastChange.expectCursorActivityForChange = false;
4785        } else {
4786          // Cursor moved outside the context of an edit. Reset the change.
4787          lastChange.changes = [];
4788        }
4789      } else if (!cm.curOp.isVimOp) {
4790        handleExternalSelection(cm, vim);
4791      }
4792      if (vim.visualMode) {
4793        updateFakeCursor(cm);
4794      }
4795    }
4796    function updateFakeCursor(cm) {
4797      var vim = cm.state.vim;
4798      var from = copyCursor(vim.sel.head);
4799      var to = offsetCursor(from, 0, 1);
4800      if (vim.fakeCursor) {
4801        vim.fakeCursor.clear();
4802      }
4803      vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
4804    }
4805    function handleExternalSelection(cm, vim) {
4806      var anchor = cm.getCursor('anchor');
4807      var head = cm.getCursor('head');
4808      // Enter or exit visual mode to match mouse selection.
4809      if (vim.visualMode && cursorEqual(head, anchor) && lineLength(cm, head.line) > head.ch) {
4810        exitVisualMode(cm, false);
4811      } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
4812        vim.visualMode = true;
4813        vim.visualLine = false;
4814        CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
4815      }
4816      if (vim.visualMode) {
4817        // Bind CodeMirror selection model to vim selection model.
4818        // Mouse selections are considered visual characterwise.
4819        var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
4820        var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
4821        head = offsetCursor(head, 0, headOffset);
4822        anchor = offsetCursor(anchor, 0, anchorOffset);
4823        vim.sel = {
4824          anchor: anchor,
4825          head: head
4826        };
4827        updateMark(cm, vim, '<', cursorMin(head, anchor));
4828        updateMark(cm, vim, '>', cursorMax(head, anchor));
4829      } else if (!vim.insertMode) {
4830        // Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.
4831        vim.lastHPos = cm.getCursor().ch;
4832      }
4833    }
4834
4835    /** Wrapper for special keys pressed in insert mode */
4836    function InsertModeKey(keyName) {
4837      this.keyName = keyName;
4838    }
4839
4840    /**
4841    * Handles raw key down events from the text area.
4842    * - Should only be active in insert mode.
4843    * - For recording deletes in insert mode.
4844    */
4845    function onKeyEventTargetKeyDown(e) {
4846      var macroModeState = vimGlobalState.macroModeState;
4847      var lastChange = macroModeState.lastInsertModeChanges;
4848      var keyName = CodeMirror.keyName(e);
4849      if (!keyName) { return; }
4850      function onKeyFound() {
4851        lastChange.changes.push(new InsertModeKey(keyName));
4852        return true;
4853      }
4854      if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
4855        CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);
4856      }
4857    }
4858
4859    /**
4860     * Repeats the last edit, which includes exactly 1 command and at most 1
4861     * insert. Operator and motion commands are read from lastEditInputState,
4862     * while action commands are read from lastEditActionCommand.
4863     *
4864     * If repeatForInsert is true, then the function was called by
4865     * exitInsertMode to repeat the insert mode changes the user just made. The
4866     * corresponding enterInsertMode call was made with a count.
4867     */
4868    function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
4869      var macroModeState = vimGlobalState.macroModeState;
4870      macroModeState.isPlaying = true;
4871      var isAction = !!vim.lastEditActionCommand;
4872      var cachedInputState = vim.inputState;
4873      function repeatCommand() {
4874        if (isAction) {
4875          commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);
4876        } else {
4877          commandDispatcher.evalInput(cm, vim);
4878        }
4879      }
4880      function repeatInsert(repeat) {
4881        if (macroModeState.lastInsertModeChanges.changes.length > 0) {
4882          // For some reason, repeat cw in desktop VIM does not repeat
4883          // insert mode changes. Will conform to that behavior.
4884          repeat = !vim.lastEditActionCommand ? 1 : repeat;
4885          var changeObject = macroModeState.lastInsertModeChanges;
4886          repeatInsertModeChanges(cm, changeObject.changes, repeat);
4887        }
4888      }
4889      vim.inputState = vim.lastEditInputState;
4890      if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {
4891        // o and O repeat have to be interlaced with insert repeats so that the
4892        // insertions appear on separate lines instead of the last line.
4893        for (var i = 0; i < repeat; i++) {
4894          repeatCommand();
4895          repeatInsert(1);
4896        }
4897      } else {
4898        if (!repeatForInsert) {
4899          // Hack to get the cursor to end up at the right place. If I is
4900          // repeated in insert mode repeat, cursor will be 1 insert
4901          // change set left of where it should be.
4902          repeatCommand();
4903        }
4904        repeatInsert(repeat);
4905      }
4906      vim.inputState = cachedInputState;
4907      if (vim.insertMode && !repeatForInsert) {
4908        // Don't exit insert mode twice. If repeatForInsert is set, then we
4909        // were called by an exitInsertMode call lower on the stack.
4910        exitInsertMode(cm);
4911      }
4912      macroModeState.isPlaying = false;
4913    };
4914
4915    function repeatInsertModeChanges(cm, changes, repeat) {
4916      function keyHandler(binding) {
4917        if (typeof binding == 'string') {
4918          CodeMirror.commands[binding](cm);
4919        } else {
4920          binding(cm);
4921        }
4922        return true;
4923      }
4924      var head = cm.getCursor('head');
4925      var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
4926      if (inVisualBlock) {
4927        // Set up block selection again for repeating the changes.
4928        var vim = cm.state.vim;
4929        var lastSel = vim.lastSelection;
4930        var offset = getOffset(lastSel.anchor, lastSel.head);
4931        selectForInsert(cm, head, offset.line + 1);
4932        repeat = cm.listSelections().length;
4933        cm.setCursor(head);
4934      }
4935      for (var i = 0; i < repeat; i++) {
4936        if (inVisualBlock) {
4937          cm.setCursor(offsetCursor(head, i, 0));
4938        }
4939        for (var j = 0; j < changes.length; j++) {
4940          var change = changes[j];
4941          if (change instanceof InsertModeKey) {
4942            CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);
4943          } else {
4944            var cur = cm.getCursor();
4945            cm.replaceRange(change, cur, cur);
4946          }
4947        }
4948      }
4949      if (inVisualBlock) {
4950        cm.setCursor(offsetCursor(head, 0, 1));
4951      }
4952    }
4953
4954    resetVimGlobalState();
4955    return vimApi;
4956  };
4957  // Initialize Vim and make it available as an API.
4958  CodeMirror.Vim = Vim();
4959});
Note: See TracBrowser for help on using the repository browser.