codemirror-rails 4.6 → 4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +24 -25
  4. data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +1 -1
  5. data/vendor/assets/javascripts/codemirror/addons/dialog/dialog.js +4 -1
  6. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +7 -6
  7. data/vendor/assets/javascripts/codemirror/addons/edit/continuelist.js +21 -8
  8. data/vendor/assets/javascripts/codemirror/addons/hint/sql-hint.js +54 -20
  9. data/vendor/assets/javascripts/codemirror/addons/lint/lint.js +10 -15
  10. data/vendor/assets/javascripts/codemirror/addons/mode/simple.js +210 -0
  11. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode-standalone.js +5 -1
  12. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode.node.js +2 -4
  13. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +425 -554
  14. data/vendor/assets/javascripts/codemirror/modes/clike.js +28 -5
  15. data/vendor/assets/javascripts/codemirror/modes/clojure.js +5 -3
  16. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +8 -7
  17. data/vendor/assets/javascripts/codemirror/modes/d.js +1 -1
  18. data/vendor/assets/javascripts/codemirror/modes/dtd.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/modes/gherkin.js +1 -1
  20. data/vendor/assets/javascripts/codemirror/modes/go.js +1 -1
  21. data/vendor/assets/javascripts/codemirror/modes/haskell.js +1 -1
  22. data/vendor/assets/javascripts/codemirror/modes/javascript.js +3 -3
  23. data/vendor/assets/javascripts/codemirror/modes/julia.js +1 -1
  24. data/vendor/assets/javascripts/codemirror/modes/markdown.js +4 -3
  25. data/vendor/assets/javascripts/codemirror/modes/octave.js +1 -1
  26. data/vendor/assets/javascripts/codemirror/modes/perl.js +2 -2
  27. data/vendor/assets/javascripts/codemirror/modes/php.js +37 -44
  28. data/vendor/assets/javascripts/codemirror/modes/python.js +17 -4
  29. data/vendor/assets/javascripts/codemirror/modes/ruby.js +12 -11
  30. data/vendor/assets/javascripts/codemirror/modes/rust.js +2 -2
  31. data/vendor/assets/javascripts/codemirror/modes/smartymixed.js +5 -1
  32. data/vendor/assets/javascripts/codemirror/modes/sql.js +2 -1
  33. data/vendor/assets/javascripts/codemirror/modes/stex.js +2 -1
  34. data/vendor/assets/javascripts/codemirror/modes/tcl.js +1 -1
  35. data/vendor/assets/javascripts/codemirror/modes/textile.js +553 -0
  36. data/vendor/assets/javascripts/codemirror/modes/tornado.js +68 -0
  37. data/vendor/assets/javascripts/codemirror/modes/verilog.js +14 -9
  38. data/vendor/assets/stylesheets/codemirror.css +1 -1
  39. data/vendor/assets/stylesheets/codemirror/themes/base16-dark.css +2 -2
  40. data/vendor/assets/stylesheets/codemirror/themes/mdn-like.css +1 -1
  41. metadata +4 -1
@@ -74,7 +74,11 @@ CodeMirror.startState = function (mode, a1, a2) {
74
74
  };
75
75
 
76
76
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
77
- CodeMirror.defineMode = function (name, mode) { modes[name] = mode; };
77
+ CodeMirror.defineMode = function (name, mode) {
78
+ if (arguments.length > 2)
79
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
80
+ modes[name] = mode;
81
+ };
78
82
  CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; };
79
83
  CodeMirror.resolveMode = function(spec) {
80
84
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
@@ -74,10 +74,8 @@ exports.startState = function(mode, a1, a2) {
74
74
 
75
75
  var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
76
76
  exports.defineMode = function(name, mode) {
77
- if (arguments.length > 2) {
78
- mode.dependencies = [];
79
- for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
80
- }
77
+ if (arguments.length > 2)
78
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
81
79
  modes[name] = mode;
82
80
  };
83
81
  exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
@@ -72,290 +72,205 @@
72
72
  var defaultKeymap = [
73
73
  // Key to key mapping. This goes first to make it possible to override
74
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'] },
81
- { keys: ['<C-Space>'], type: 'keyToKey', toKeys: ['W'] },
82
- { keys: ['<C-BS>'], type: 'keyToKey', toKeys: ['B'] },
83
- { keys: ['<S-Space>'], type: 'keyToKey', toKeys: ['w'] },
84
- { keys: ['<S-BS>'], type: 'keyToKey', toKeys: ['b'] },
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: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' },
90
- { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'},
91
- { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'], context: 'normal' },
92
- { keys: ['S'], type: 'keyToKey', toKeys: ['d', 'c', 'c'], context: 'visual' },
93
- { keys: ['<Home>'], type: 'keyToKey', toKeys: ['0'] },
94
- { keys: ['<End>'], type: 'keyToKey', toKeys: ['$'] },
95
- { keys: ['<PageUp>'], type: 'keyToKey', toKeys: ['<C-b>'] },
96
- { keys: ['<PageDown>'], type: 'keyToKey', toKeys: ['<C-f>'] },
97
- { keys: ['<CR>'], type: 'keyToKey', toKeys: ['j', '^'], context: 'normal' },
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' },
81
+ { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },
82
+ { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B' },
83
+ { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },
84
+ { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b' },
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' },
98
100
  // Motions
99
- { keys: ['H'], type: 'motion',
100
- motion: 'moveToTopLine',
101
- motionArgs: { linewise: true, toJumplist: true }},
102
- { keys: ['M'], type: 'motion',
103
- motion: 'moveToMiddleLine',
104
- motionArgs: { linewise: true, toJumplist: true }},
105
- { keys: ['L'], type: 'motion',
106
- motion: 'moveToBottomLine',
107
- motionArgs: { linewise: true, toJumplist: true }},
108
- { keys: ['h'], type: 'motion',
109
- motion: 'moveByCharacters',
110
- motionArgs: { forward: false }},
111
- { keys: ['l'], type: 'motion',
112
- motion: 'moveByCharacters',
113
- motionArgs: { forward: true }},
114
- { keys: ['j'], type: 'motion',
115
- motion: 'moveByLines',
116
- motionArgs: { forward: true, linewise: true }},
117
- { keys: ['k'], type: 'motion',
118
- motion: 'moveByLines',
119
- motionArgs: { forward: false, linewise: true }},
120
- { keys: ['g','j'], type: 'motion',
121
- motion: 'moveByDisplayLines',
122
- motionArgs: { forward: true }},
123
- { keys: ['g','k'], type: 'motion',
124
- motion: 'moveByDisplayLines',
125
- motionArgs: { forward: false }},
126
- { keys: ['w'], type: 'motion',
127
- motion: 'moveByWords',
128
- motionArgs: { forward: true, wordEnd: false }},
129
- { keys: ['W'], type: 'motion',
130
- motion: 'moveByWords',
131
- motionArgs: { forward: true, wordEnd: false, bigWord: true }},
132
- { keys: ['e'], type: 'motion',
133
- motion: 'moveByWords',
134
- motionArgs: { forward: true, wordEnd: true, inclusive: true }},
135
- { keys: ['E'], type: 'motion',
136
- motion: 'moveByWords',
137
- motionArgs: { forward: true, wordEnd: true, bigWord: true,
138
- inclusive: true }},
139
- { keys: ['b'], type: 'motion',
140
- motion: 'moveByWords',
141
- motionArgs: { forward: false, wordEnd: false }},
142
- { keys: ['B'], type: 'motion',
143
- motion: 'moveByWords',
144
- motionArgs: { forward: false, wordEnd: false, bigWord: true }},
145
- { keys: ['g', 'e'], type: 'motion',
146
- motion: 'moveByWords',
147
- motionArgs: { forward: false, wordEnd: true, inclusive: true }},
148
- { keys: ['g', 'E'], type: 'motion',
149
- motion: 'moveByWords',
150
- motionArgs: { forward: false, wordEnd: true, bigWord: true,
151
- inclusive: true }},
152
- { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
153
- motionArgs: { forward: false, toJumplist: true }},
154
- { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
155
- motionArgs: { forward: true, toJumplist: true }},
156
- { keys: ['<C-f>'], type: 'motion',
157
- motion: 'moveByPage', motionArgs: { forward: true }},
158
- { keys: ['<C-b>'], type: 'motion',
159
- motion: 'moveByPage', motionArgs: { forward: false }},
160
- { keys: ['<C-d>'], type: 'motion',
161
- motion: 'moveByScroll',
162
- motionArgs: { forward: true, explicitRepeat: true }},
163
- { keys: ['<C-u>'], type: 'motion',
164
- motion: 'moveByScroll',
165
- motionArgs: { forward: false, explicitRepeat: true }},
166
- { keys: ['g', 'g'], type: 'motion',
167
- motion: 'moveToLineOrEdgeOfDocument',
168
- motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
169
- { keys: ['G'], type: 'motion',
170
- motion: 'moveToLineOrEdgeOfDocument',
171
- motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
172
- { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' },
173
- { keys: ['^'], type: 'motion',
174
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
175
- { keys: ['+'], type: 'motion',
176
- motion: 'moveByLines',
177
- motionArgs: { forward: true, toFirstChar:true }},
178
- { keys: ['-'], type: 'motion',
179
- motion: 'moveByLines',
180
- motionArgs: { forward: false, toFirstChar:true }},
181
- { keys: ['_'], type: 'motion',
182
- motion: 'moveByLines',
183
- motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
184
- { keys: ['$'], type: 'motion',
185
- motion: 'moveToEol',
186
- motionArgs: { inclusive: true }},
187
- { keys: ['%'], type: 'motion',
188
- motion: 'moveToMatchedSymbol',
189
- motionArgs: { inclusive: true, toJumplist: true }},
190
- { keys: ['f', 'character'], type: 'motion',
191
- motion: 'moveToCharacter',
192
- motionArgs: { forward: true , inclusive: true }},
193
- { keys: ['F', 'character'], type: 'motion',
194
- motion: 'moveToCharacter',
195
- motionArgs: { forward: false }},
196
- { keys: ['t', 'character'], type: 'motion',
197
- motion: 'moveTillCharacter',
198
- motionArgs: { forward: true, inclusive: true }},
199
- { keys: ['T', 'character'], type: 'motion',
200
- motion: 'moveTillCharacter',
201
- motionArgs: { forward: false }},
202
- { keys: [';'], type: 'motion', motion: 'repeatLastCharacterSearch',
203
- motionArgs: { forward: true }},
204
- { keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch',
205
- motionArgs: { forward: false }},
206
- { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark',
207
- motionArgs: {toJumplist: true, linewise: true}},
208
- { keys: ['`', 'character'], type: 'motion', motion: 'goToMark',
209
- motionArgs: {toJumplist: true}},
210
- { keys: [']', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
211
- { keys: ['[', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
212
- { keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
213
- { keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
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 } },
214
145
  // the next two aren't motions but must come before more general motion declarations
215
- { keys: [']', 'p'], type: 'action', action: 'paste', isEdit: true,
216
- actionArgs: { after: true, isEdit: true, matchIndent: true}},
217
- { keys: ['[', 'p'], type: 'action', action: 'paste', isEdit: true,
218
- actionArgs: { after: false, isEdit: true, matchIndent: true}},
219
- { keys: [']', 'character'], type: 'motion',
220
- motion: 'moveToSymbol',
221
- motionArgs: { forward: true, toJumplist: true}},
222
- { keys: ['[', 'character'], type: 'motion',
223
- motion: 'moveToSymbol',
224
- motionArgs: { forward: false, toJumplist: true}},
225
- { keys: ['|'], type: 'motion',
226
- motion: 'moveToColumn',
227
- motionArgs: { }},
228
- { keys: ['o'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { }, context:'visual'},
229
- { keys: ['O'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
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'},
230
153
  // Operators
231
- { keys: ['d'], type: 'operator', operator: 'delete' },
232
- { keys: ['y'], type: 'operator', operator: 'yank' },
233
- { keys: ['c'], type: 'operator', operator: 'change' },
234
- { keys: ['>'], type: 'operator', operator: 'indent',
235
- operatorArgs: { indentRight: true }},
236
- { keys: ['<'], type: 'operator', operator: 'indent',
237
- operatorArgs: { indentRight: false }},
238
- { keys: ['g', '~'], type: 'operator', operator: 'swapcase' },
239
- { keys: ['n'], type: 'motion', motion: 'findNext',
240
- motionArgs: { forward: true, toJumplist: true }},
241
- { keys: ['N'], type: 'motion', motion: 'findNext',
242
- motionArgs: { forward: false, toJumplist: true }},
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: 'swapcase' },
160
+ { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
161
+ { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
243
162
  // Operator-Motion dual commands
244
- { keys: ['x'], type: 'operatorMotion', operator: 'delete',
245
- motion: 'moveByCharacters', motionArgs: { forward: true },
246
- operatorMotionArgs: { visualLine: false }},
247
- { keys: ['X'], type: 'operatorMotion', operator: 'delete',
248
- motion: 'moveByCharacters', motionArgs: { forward: false },
249
- operatorMotionArgs: { visualLine: true }},
250
- { keys: ['D'], type: 'operatorMotion', operator: 'delete',
251
- motion: 'moveToEol', motionArgs: { inclusive: true },
252
- operatorMotionArgs: { visualLine: true }},
253
- { keys: ['Y'], type: 'operatorMotion', operator: 'yank',
254
- motion: 'moveToEol', motionArgs: { inclusive: true },
255
- operatorMotionArgs: { visualLine: true }},
256
- { keys: ['C'], type: 'operatorMotion',
257
- operator: 'change',
258
- motion: 'moveToEol', motionArgs: { inclusive: true },
259
- operatorMotionArgs: { visualLine: true }},
260
- { keys: ['~'], type: 'operatorMotion',
261
- operator: 'swapcase', operatorArgs: { shouldMoveCursor: true },
262
- motion: 'moveByCharacters', motionArgs: { forward: true }},
163
+ { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
164
+ { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
165
+ { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }},
166
+ { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }},
167
+ { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }},
168
+ { keys: '~', type: 'operatorMotion', operator: 'swapcase', operatorArgs: { shouldMoveCursor: true }, motion: 'moveByCharacters', motionArgs: { forward: true }},
169
+ { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
263
170
  // Actions
264
- { keys: ['<C-i>'], type: 'action', action: 'jumpListWalk',
265
- actionArgs: { forward: true }},
266
- { keys: ['<C-o>'], type: 'action', action: 'jumpListWalk',
267
- actionArgs: { forward: false }},
268
- { keys: ['<C-e>'], type: 'action',
269
- action: 'scroll',
270
- actionArgs: { forward: true, linewise: true }},
271
- { keys: ['<C-y>'], type: 'action',
272
- action: 'scroll',
273
- actionArgs: { forward: false, linewise: true }},
274
- { keys: ['a'], type: 'action', action: 'enterInsertMode', isEdit: true,
275
- actionArgs: { insertAt: 'charAfter' }},
276
- { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true,
277
- actionArgs: { insertAt: 'eol' }},
278
- { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
279
- { keys: ['i'], type: 'action', action: 'enterInsertMode', isEdit: true,
280
- actionArgs: { insertAt: 'inplace' }},
281
- { keys: ['I'], type: 'action', action: 'enterInsertMode', isEdit: true,
282
- actionArgs: { insertAt: 'firstNonBlank' }},
283
- { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
284
- isEdit: true, interlaceInsertRepeat: true,
285
- actionArgs: { after: true }},
286
- { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
287
- isEdit: true, interlaceInsertRepeat: true,
288
- actionArgs: { after: false }},
289
- { keys: ['v'], type: 'action', action: 'toggleVisualMode' },
290
- { keys: ['V'], type: 'action', action: 'toggleVisualMode',
291
- actionArgs: { linewise: true }},
292
- { keys: ['<C-v>'], type: 'action', action: 'toggleVisualMode',
293
- actionArgs: { blockwise: true }},
294
- { keys: ['g', 'v'], type: 'action', action: 'reselectLastSelection' },
295
- { keys: ['J'], type: 'action', action: 'joinLines', isEdit: true },
296
- { keys: ['p'], type: 'action', action: 'paste', isEdit: true,
297
- actionArgs: { after: true, isEdit: true }},
298
- { keys: ['P'], type: 'action', action: 'paste', isEdit: true,
299
- actionArgs: { after: false, isEdit: true }},
300
- { keys: ['r', 'character'], type: 'action', action: 'replace', isEdit: true },
301
- { keys: ['@', 'character'], type: 'action', action: 'replayMacro' },
302
- { keys: ['q', 'character'], type: 'action', action: 'enterMacroRecordMode' },
171
+ { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
172
+ { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},
173
+ { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},
174
+ { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},
175
+ { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
176
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
177
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
178
+ { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
179
+ { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank' }},
180
+ { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
181
+ { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
182
+ { keys: 'v', type: 'action', action: 'toggleVisualMode' },
183
+ { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},
184
+ { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
185
+ { keys: 'gv', type: 'action', action: 'reselectLastSelection' },
186
+ { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
187
+ { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},
188
+ { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},
189
+ { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },
190
+ { keys: '@<character>', type: 'action', action: 'replayMacro' },
191
+ { keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },
303
192
  // Handle Replace-mode as a special case of insert mode.
304
- { keys: ['R'], type: 'action', action: 'enterInsertMode', isEdit: true,
305
- actionArgs: { replace: true }},
306
- { keys: ['u'], type: 'action', action: 'undo' },
307
- { keys: ['u'], type: 'action', action: 'changeCase', actionArgs: {toLower: true}, context: 'visual', isEdit: true },
308
- { keys: ['U'],type: 'action', action: 'changeCase', actionArgs: {toLower: false}, context: 'visual', isEdit: true },
309
- { keys: ['<C-r>'], type: 'action', action: 'redo' },
310
- { keys: ['m', 'character'], type: 'action', action: 'setMark' },
311
- { keys: ['"', 'character'], type: 'action', action: 'setRegister' },
312
- { keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
313
- actionArgs: { position: 'center' }},
314
- { keys: ['z', '.'], type: 'action', action: 'scrollToCursor',
315
- actionArgs: { position: 'center' },
316
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
317
- { keys: ['z', 't'], type: 'action', action: 'scrollToCursor',
318
- actionArgs: { position: 'top' }},
319
- { keys: ['z', '<CR>'], type: 'action', action: 'scrollToCursor',
320
- actionArgs: { position: 'top' },
321
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
322
- { keys: ['z', '-'], type: 'action', action: 'scrollToCursor',
323
- actionArgs: { position: 'bottom' }},
324
- { keys: ['z', 'b'], type: 'action', action: 'scrollToCursor',
325
- actionArgs: { position: 'bottom' },
326
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
327
- { keys: ['.'], type: 'action', action: 'repeatLastEdit' },
328
- { keys: ['<C-a>'], type: 'action', action: 'incrementNumberToken',
329
- isEdit: true,
330
- actionArgs: {increase: true, backtrack: false}},
331
- { keys: ['<C-x>'], type: 'action', action: 'incrementNumberToken',
332
- isEdit: true,
333
- actionArgs: {increase: false, backtrack: false}},
193
+ { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
194
+ { keys: 'u', type: 'action', action: 'undo', context: 'normal' },
195
+ { keys: 'u', type: 'action', action: 'changeCase', actionArgs: {toLower: true}, context: 'visual', isEdit: true },
196
+ { keys: 'U',type: 'action', action: 'changeCase', actionArgs: {toLower: false}, context: 'visual', isEdit: true },
197
+ { keys: '<C-r>', type: 'action', action: 'redo' },
198
+ { keys: 'm<character>', type: 'action', action: 'setMark' },
199
+ { keys: '"<character>', type: 'action', action: 'setRegister' },
200
+ { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},
201
+ { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
202
+ { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},
203
+ { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
204
+ { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},
205
+ { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
206
+ { keys: '.', type: 'action', action: 'repeatLastEdit' },
207
+ { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},
208
+ { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},
334
209
  // Text object motions
335
- { keys: ['a', 'character'], type: 'motion',
336
- motion: 'textObjectManipulation' },
337
- { keys: ['i', 'character'], type: 'motion',
338
- motion: 'textObjectManipulation',
339
- motionArgs: { textObjectInner: true }},
210
+ { keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },
211
+ { keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},
340
212
  // Search
341
- { keys: ['/'], type: 'search',
342
- searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
343
- { keys: ['?'], type: 'search',
344
- searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
345
- { keys: ['*'], type: 'search',
346
- searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
347
- { keys: ['#'], type: 'search',
348
- searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
349
- { keys: ['g', '*'], type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
350
- { keys: ['g', '#'], type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
213
+ { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
214
+ { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
215
+ { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
216
+ { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
217
+ { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
218
+ { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
351
219
  // Ex command
352
- { keys: [':'], type: 'ex' }
220
+ { keys: ':', type: 'ex' }
353
221
  ];
354
222
 
355
223
  var Pos = CodeMirror.Pos;
356
224
 
225
+ var modifierCodes = [16, 17, 18, 91];
226
+ var specialKey = {Enter:'CR',Backspace:'BS',Delete:'Del'};
227
+ var mac = /Mac/.test(navigator.platform);
357
228
  var Vim = function() {
358
229
  CodeMirror.defineOption('vimMode', false, function(cm, val) {
230
+ function lookupKey(e) {
231
+ var keyCode = e.keyCode;
232
+ if (modifierCodes.indexOf(keyCode) != -1) { return; }
233
+ var hasModifier = e.ctrlKey || e.metaKey;
234
+ var key = CodeMirror.keyNames[keyCode];
235
+ key = specialKey[key] || key;
236
+ var name = '';
237
+ if (e.ctrlKey) { name += 'C-'; }
238
+ if (e.altKey) { name += 'A-'; }
239
+ if (mac && e.metaKey || (!hasModifier && e.shiftKey) && key.length < 2) {
240
+ // Shift key bindings can only specified for special characters.
241
+ return;
242
+ } else if (e.shiftKey && !/^[A-Za-z]$/.test(key)) {
243
+ name += 'S-';
244
+ }
245
+ if (key.length == 1) { key = key.toLowerCase(); }
246
+ name += key;
247
+ if (name.length > 1) { name = '<' + name + '>'; }
248
+ return name;
249
+ }
250
+ // Keys with modifiers are handled using keydown due to limitations of
251
+ // keypress event.
252
+ function handleKeyDown(cm, e) {
253
+ var name = lookupKey(e);
254
+ if (!name) { return; }
255
+
256
+ CodeMirror.signal(cm, 'vim-keypress', name);
257
+ if (CodeMirror.Vim.handleKey(cm, name, 'user')) {
258
+ CodeMirror.e_stop(e);
259
+ }
260
+ }
261
+ // Keys without modifiers are handled using keypress to work best with
262
+ // non-standard keyboard layouts.
263
+ function handleKeyPress(cm, e) {
264
+ var code = e.charCode || e.keyCode;
265
+ if (e.ctrlKey || e.metaKey || e.altKey ||
266
+ e.shiftKey && code < 32) { return; }
267
+ var name = String.fromCharCode(code);
268
+
269
+ CodeMirror.signal(cm, 'vim-keypress', name);
270
+ if (CodeMirror.Vim.handleKey(cm, name, 'user')) {
271
+ CodeMirror.e_stop(e);
272
+ }
273
+ }
359
274
  if (val) {
360
275
  cm.setOption('keyMap', 'vim');
361
276
  cm.setOption('disableInput', true);
@@ -364,12 +279,16 @@
364
279
  cm.on('cursorActivity', onCursorActivity);
365
280
  maybeInitVimState(cm);
366
281
  CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
282
+ cm.on('keypress', handleKeyPress);
283
+ cm.on('keydown', handleKeyDown);
367
284
  } else if (cm.state.vim) {
368
285
  cm.setOption('keyMap', 'default');
369
286
  cm.setOption('disableInput', false);
370
287
  cm.off('cursorActivity', onCursorActivity);
371
288
  CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
372
289
  cm.state.vim = null;
290
+ cm.off('keypress', handleKeyPress);
291
+ cm.off('keydown', handleKeyDown);
373
292
  }
374
293
  });
375
294
  function getOnPasteFn(cm) {
@@ -397,9 +316,6 @@
397
316
  var upperCaseAlphabet = makeKeyRange(65, 26);
398
317
  var lowerCaseAlphabet = makeKeyRange(97, 26);
399
318
  var numbers = makeKeyRange(48, 10);
400
- var specialSymbols = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;"\''.split('');
401
- var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
402
- 'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
403
319
  var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
404
320
  var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
405
321
 
@@ -648,6 +564,7 @@
648
564
  }
649
565
  }
650
566
 
567
+ var lastInsertModeKeyTimer;
651
568
  var vimApi= {
652
569
  buildKeyMap: function() {
653
570
  // TODO: Convert keymap into dictionary format for fast lookup.
@@ -685,58 +602,111 @@
685
602
  },
686
603
  // This is the outermost function called by CodeMirror, after keys have
687
604
  // been mapped to their Vim equivalents.
688
- handleKey: function(cm, key) {
689
- var command;
605
+ handleKey: function(cm, key, origin) {
690
606
  var vim = maybeInitVimState(cm);
691
- var macroModeState = vimGlobalState.macroModeState;
692
- if (macroModeState.isRecording) {
693
- if (key == 'q') {
694
- macroModeState.exitMacroRecordMode();
695
- clearInputState(cm);
696
- return;
607
+ function handleMacroRecording() {
608
+ var macroModeState = vimGlobalState.macroModeState;
609
+ if (macroModeState.isRecording) {
610
+ if (key == 'q') {
611
+ macroModeState.exitMacroRecordMode();
612
+ clearInputState(cm);
613
+ return true;
614
+ }
615
+ if (origin != 'mapping') {
616
+ logKey(macroModeState, key);
617
+ }
697
618
  }
698
619
  }
699
- if (key == '<Esc>') {
700
- // Clear input state and get back to normal mode.
701
- clearInputState(cm);
702
- if (vim.visualMode) {
703
- exitVisualMode(cm);
620
+ function handleEsc() {
621
+ if (key == '<Esc>') {
622
+ // Clear input state and get back to normal mode.
623
+ clearInputState(cm);
624
+ if (vim.visualMode) {
625
+ exitVisualMode(cm);
626
+ } else if (vim.insertMode) {
627
+ exitInsertMode(cm);
628
+ }
629
+ return true;
704
630
  }
705
- return;
706
631
  }
707
- // Enter visual mode when the mouse selects text.
708
- if (!vim.visualMode &&
709
- !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
710
- vim.visualMode = true;
711
- vim.visualLine = false;
712
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
713
- cm.on('mousedown', exitVisualMode);
714
- }
715
- if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) {
716
- // Have to special case 0 since it's both a motion and a number.
717
- command = commandDispatcher.matchCommand(key, defaultKeymap, vim);
632
+ function doKeyToKey(keys) {
633
+ // TODO: prevent infinite recursion.
634
+ var match;
635
+ while (keys) {
636
+ // Pull off one command key, which is either a single character
637
+ // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
638
+ match = (/<\w+-.+?>|<\w+>|./).exec(keys);
639
+ key = match[0];
640
+ keys = keys.substring(match.index + key.length);
641
+ CodeMirror.Vim.handleKey(cm, key, 'mapping');
642
+ }
718
643
  }
719
- if (!command) {
720
- if (isNumber(key)) {
721
- // Increment count unless count is 0 and key is 0.
722
- vim.inputState.pushRepeatDigit(key);
644
+
645
+ function handleKeyInsertMode() {
646
+ if (handleEsc()) { return true; }
647
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
648
+ var keysAreChars = key.length == 1;
649
+ var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
650
+ // Need to check all key substrings in insert mode.
651
+ while (keys.length > 1 && match.type != 'full') {
652
+ var keys = vim.inputState.keyBuffer = keys.slice(1);
653
+ var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
654
+ if (thisMatch.type != 'none') { match = thisMatch; }
723
655
  }
724
- if (macroModeState.isRecording) {
725
- logKey(macroModeState, key);
656
+ if (match.type == 'none') { clearInputState(cm); return false; }
657
+ else if (match.type == 'partial') {
658
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
659
+ lastInsertModeKeyTimer = window.setTimeout(
660
+ function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },
661
+ getOption('insertModeEscKeysTimeout'));
662
+ return !keysAreChars;
726
663
  }
727
- return;
664
+
665
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
666
+ if (keysAreChars) {
667
+ var here = cm.getCursor();
668
+ cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
669
+ }
670
+ clearInputState(cm);
671
+ var command = match.command;
672
+ if (command.type == 'keyToKey') {
673
+ doKeyToKey(command.toKeys);
674
+ } else {
675
+ commandDispatcher.processCommand(cm, vim, command);
676
+ }
677
+ return !keysAreChars;
728
678
  }
729
- if (command.type == 'keyToKey') {
730
- // TODO: prevent infinite recursion.
731
- for (var i = 0; i < command.toKeys.length; i++) {
732
- this.handleKey(cm, command.toKeys[i]);
679
+
680
+ function handleKeyNonInsertMode() {
681
+ if (handleMacroRecording() || handleEsc()) { return true; };
682
+
683
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
684
+ if (/^[1-9]\d*$/.test(keys)) { return true; }
685
+
686
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
687
+ if (!keysMatcher) { clearInputState(cm); return false; }
688
+ var context = vim.visualMode ? 'visual' :
689
+ 'normal';
690
+ var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context);
691
+ if (match.type == 'none') { clearInputState(cm); return false; }
692
+ else if (match.type == 'partial') { return true; }
693
+
694
+ vim.inputState.keyBuffer = '';
695
+ var command = match.command;
696
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
697
+ if (keysMatcher[1] && keysMatcher[1] != '0') {
698
+ vim.inputState.pushRepeatDigit(keysMatcher[1]);
733
699
  }
734
- } else {
735
- if (macroModeState.isRecording) {
736
- logKey(macroModeState, key);
700
+ if (command.type == 'keyToKey') {
701
+ doKeyToKey(command.toKeys);
702
+ } else {
703
+ commandDispatcher.processCommand(cm, vim, command);
737
704
  }
738
- commandDispatcher.processCommand(cm, vim, command);
705
+ return true;
739
706
  }
707
+
708
+ if (vim.insertMode) { return handleKeyInsertMode(); }
709
+ else { return handleKeyNonInsertMode(); }
740
710
  },
741
711
  handleEx: function(cm, input) {
742
712
  exCommandDispatcher.processCommand(cm, input);
@@ -953,83 +923,25 @@
953
923
  }
954
924
  };
955
925
  var commandDispatcher = {
956
- matchCommand: function(key, keyMap, vim) {
957
- var inputState = vim.inputState;
958
- var keys = inputState.keyBuffer.concat(key);
959
- var matchedCommands = [];
960
- var selectedCharacter;
961
- for (var i = 0; i < keyMap.length; i++) {
962
- var command = keyMap[i];
963
- if (matchKeysPartial(keys, command.keys)) {
964
- if (inputState.operator && command.type == 'action') {
965
- // Ignore matched action commands after an operator. Operators
966
- // only operate on motions. This check is really for text
967
- // objects since aW, a[ etcs conflicts with a.
968
- continue;
969
- }
970
- // Match commands that take <character> as an argument.
971
- if (command.keys[keys.length - 1] == 'character') {
972
- selectedCharacter = keys[keys.length - 1];
973
- if (selectedCharacter.length>1){
974
- switch(selectedCharacter){
975
- case '<CR>':
976
- selectedCharacter='\n';
977
- break;
978
- case '<Space>':
979
- selectedCharacter=' ';
980
- break;
981
- default:
982
- continue;
983
- }
984
- }
985
- }
986
- // Add the command to the list of matched commands. Choose the best
987
- // command later.
988
- matchedCommands.push(command);
989
- }
926
+ matchCommand: function(keys, keyMap, inputState, context) {
927
+ var matches = commandMatches(keys, keyMap, context, inputState);
928
+ if (!matches.full && !matches.partial) {
929
+ return {type: 'none'};
930
+ } else if (!matches.full && matches.partial) {
931
+ return {type: 'partial'};
990
932
  }
991
933
 
992
- // Returns the command if it is a full match, or null if not.
993
- function getFullyMatchedCommandOrNull(command) {
994
- if (keys.length < command.keys.length) {
995
- // Matches part of a multi-key command. Buffer and wait for next
996
- // stroke.
997
- inputState.keyBuffer.push(key);
998
- return null;
999
- } else {
1000
- if (command.keys[keys.length - 1] == 'character') {
1001
- inputState.selectedCharacter = selectedCharacter;
1002
- }
1003
- // Clear the buffer since a full match was found.
1004
- inputState.keyBuffer = [];
1005
- return command;
934
+ var bestMatch;
935
+ for (var i = 0; i < matches.full.length; i++) {
936
+ var match = matches.full[i];
937
+ if (!bestMatch) {
938
+ bestMatch = match;
1006
939
  }
1007
940
  }
1008
-
1009
- if (!matchedCommands.length) {
1010
- // Clear the buffer since there were no matches.
1011
- inputState.keyBuffer = [];
1012
- return null;
1013
- } else if (matchedCommands.length == 1) {
1014
- return getFullyMatchedCommandOrNull(matchedCommands[0]);
1015
- } else {
1016
- // Find the best match in the list of matchedCommands.
1017
- var context = vim.visualMode ? 'visual' : 'normal';
1018
- var bestMatch; // Default to first in the list.
1019
- for (var i = 0; i < matchedCommands.length; i++) {
1020
- var current = matchedCommands[i];
1021
- if (current.context == context) {
1022
- bestMatch = current;
1023
- break;
1024
- } else if (!bestMatch && !current.context) {
1025
- // Only set an imperfect match to best match if no best match is
1026
- // set and the imperfect match is not restricted to another
1027
- // context.
1028
- bestMatch = current;
1029
- }
1030
- }
1031
- return getFullyMatchedCommandOrNull(bestMatch);
941
+ if (bestMatch.keys.slice(-11) == '<character>') {
942
+ inputState.selectedCharacter = lastChar(keys);
1032
943
  }
944
+ return {type: 'full', command: bestMatch};
1033
945
  },
1034
946
  processCommand: function(cm, vim, command) {
1035
947
  vim.inputState.repeatOverride = command.repeatOverride;
@@ -1584,8 +1496,8 @@
1584
1496
 
1585
1497
  var equal = cursorEqual(cursor, best);
1586
1498
  var between = (motionArgs.forward) ?
1587
- cusrorIsBetween(cursor, mark, best) :
1588
- cusrorIsBetween(best, mark, cursor);
1499
+ cursorIsBetween(cursor, mark, best) :
1500
+ cursorIsBetween(best, mark, cursor);
1589
1501
 
1590
1502
  if (equal || between) {
1591
1503
  best = mark;
@@ -1837,7 +1749,11 @@
1837
1749
  return null;
1838
1750
  }
1839
1751
 
1840
- return [tmp.start, tmp.end];
1752
+ if (!cm.state.vim.visualMode) {
1753
+ return [tmp.start, tmp.end];
1754
+ } else {
1755
+ return expandSelection(cm, tmp.start, tmp.end);
1756
+ }
1841
1757
  },
1842
1758
 
1843
1759
  repeatLastCharacterSearch: function(cm, motionArgs) {
@@ -1944,10 +1860,15 @@
1944
1860
  // including the trailing \n, include the \n before the starting line
1945
1861
  if (operatorArgs.linewise &&
1946
1862
  curEnd.line == cm.lastLine() && curStart.line == curEnd.line) {
1947
- var tmp = copyCursor(curEnd);
1948
- curStart.line--;
1949
- curStart.ch = lineLength(cm, curStart.line);
1950
- curEnd = tmp;
1863
+ if (curEnd.line == 0) {
1864
+ curStart.ch = 0;
1865
+ }
1866
+ else {
1867
+ var tmp = copyCursor(curEnd);
1868
+ curStart.line--;
1869
+ curStart.ch = lineLength(cm, curStart.line);
1870
+ curEnd = tmp;
1871
+ }
1951
1872
  cm.replaceRange('', curStart, curEnd);
1952
1873
  } else {
1953
1874
  cm.replaceSelections(replacement);
@@ -2604,7 +2525,8 @@
2604
2525
  if (vim.visualMode) {
2605
2526
  exitVisualMode(cm);
2606
2527
  }
2607
- }
2528
+ },
2529
+ exitInsertMode: exitInsertMode
2608
2530
  };
2609
2531
 
2610
2532
  /*
@@ -2634,14 +2556,54 @@
2634
2556
  function offsetCursor(cur, offsetLine, offsetCh) {
2635
2557
  return Pos(cur.line + offsetLine, cur.ch + offsetCh);
2636
2558
  }
2637
- function matchKeysPartial(pressed, mapped) {
2638
- for (var i = 0; i < pressed.length; i++) {
2639
- // 'character' means any character. For mark, register commads, etc.
2640
- if (pressed[i] != mapped[i] && mapped[i] != 'character') {
2641
- return false;
2559
+ function commandMatches(keys, keyMap, context, inputState) {
2560
+ // Partial matches are not applied. They inform the key handler
2561
+ // that the current key sequence is a subsequence of a valid key
2562
+ // sequence, so that the key buffer is not cleared.
2563
+ var match, partial = [], full = [];
2564
+ for (var i = 0; i < keyMap.length; i++) {
2565
+ var command = keyMap[i];
2566
+ if (context == 'insert' && command.context != 'insert' ||
2567
+ command.context && command.context != context ||
2568
+ inputState.operator && command.type == 'action' ||
2569
+ !(match = commandMatch(keys, command.keys))) { continue; }
2570
+ if (match == 'partial') { partial.push(command); }
2571
+ if (match == 'full') { full.push(command); }
2572
+ }
2573
+ return {
2574
+ partial: partial.length && partial,
2575
+ full: full.length && full
2576
+ };
2577
+ }
2578
+ function commandMatch(pressed, mapped) {
2579
+ if (mapped.slice(-11) == '<character>') {
2580
+ // Last character matches anything.
2581
+ var prefixLen = mapped.length - 11;
2582
+ var pressedPrefix = pressed.slice(0, prefixLen);
2583
+ var mappedPrefix = mapped.slice(0, prefixLen);
2584
+ return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
2585
+ mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
2586
+ } else {
2587
+ return pressed == mapped ? 'full' :
2588
+ mapped.indexOf(pressed) == 0 ? 'partial' : false;
2589
+ }
2590
+ }
2591
+ function lastChar(keys) {
2592
+ var match = /^.*(<[\w\-]+>)$/.exec(keys);
2593
+ var selectedCharacter = match ? match[1] : keys.slice(-1);
2594
+ if (selectedCharacter.length > 1){
2595
+ switch(selectedCharacter){
2596
+ case '<CR>':
2597
+ selectedCharacter='\n';
2598
+ break;
2599
+ case '<Space>':
2600
+ selectedCharacter=' ';
2601
+ break;
2602
+ default:
2603
+ break;
2642
2604
  }
2643
2605
  }
2644
- return true;
2606
+ return selectedCharacter;
2645
2607
  }
2646
2608
  function repeatFn(cm, fn, repeat) {
2647
2609
  return function() {
@@ -2665,7 +2627,13 @@
2665
2627
  }
2666
2628
  return false;
2667
2629
  }
2668
- function cusrorIsBetween(cur1, cur2, cur3) {
2630
+ function cursorMin(cur1, cur2) {
2631
+ return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
2632
+ }
2633
+ function cursorMax(cur1, cur2) {
2634
+ return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
2635
+ }
2636
+ function cursorIsBetween(cur1, cur2, cur3) {
2669
2637
  // returns true if cur2 is between cur1 and cur3.
2670
2638
  var cur1before2 = cursorIsBefore(cur1, cur2);
2671
2639
  var cur2before3 = cursorIsBefore(cur2, cur3);
@@ -2892,6 +2860,33 @@
2892
2860
  'visualLine': vim.visualLine,
2893
2861
  'visualBlock': block};
2894
2862
  }
2863
+ function expandSelection(cm, start, end) {
2864
+ var head = cm.getCursor('head');
2865
+ var anchor = cm.getCursor('anchor');
2866
+ var tmp;
2867
+ if (cursorIsBefore(end, start)) {
2868
+ tmp = end;
2869
+ end = start;
2870
+ start = tmp;
2871
+ }
2872
+ if (cursorIsBefore(head, anchor)) {
2873
+ head = cursorMin(start, head);
2874
+ anchor = cursorMax(anchor, end);
2875
+ } else {
2876
+ anchor = cursorMin(start, anchor);
2877
+ head = cursorMax(head, end);
2878
+ }
2879
+ return [anchor, head];
2880
+ }
2881
+ function getHead(cm) {
2882
+ var cur = cm.getCursor('head');
2883
+ if (cm.getSelection().length == 1) {
2884
+ // Small corner case when only 1 character is selected. The "real"
2885
+ // head is the left of head and anchor.
2886
+ cur = cursorMin(cur, cm.getCursor('anchor'));
2887
+ }
2888
+ return cur;
2889
+ }
2895
2890
 
2896
2891
  function exitVisualMode(cm) {
2897
2892
  cm.off('mousedown', exitVisualMode);
@@ -2964,7 +2959,7 @@
2964
2959
  }
2965
2960
 
2966
2961
  function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
2967
- var cur = cm.getCursor();
2962
+ var cur = getHead(cm);
2968
2963
  var line = cm.getLine(cur.line);
2969
2964
  var idx = cur.ch;
2970
2965
 
@@ -3350,7 +3345,7 @@
3350
3345
  // TODO: perhaps this finagling of start and end positions belonds
3351
3346
  // in codmirror/replaceRange?
3352
3347
  function selectCompanionObject(cm, symb, inclusive) {
3353
- var cur = cm.getCursor(), start, end;
3348
+ var cur = getHead(cm), start, end;
3354
3349
 
3355
3350
  var bracketRegexp = ({
3356
3351
  '(': /[()]/, ')': /[()]/,
@@ -3395,7 +3390,7 @@
3395
3390
  // have identical opening and closing symbols
3396
3391
  // TODO support across multiple lines
3397
3392
  function findBeginningAndEnd(cm, symb, inclusive) {
3398
- var cur = copyCursor(cm.getCursor());
3393
+ var cur = copyCursor(getHead(cm));
3399
3394
  var line = cm.getLine(cur.line);
3400
3395
  var chars = line.split('');
3401
3396
  var start, end, i, len;
@@ -3828,6 +3823,7 @@
3828
3823
  // shortNames must not match the prefix of the other command.
3829
3824
  var defaultExCommandMap = [
3830
3825
  { name: 'map' },
3826
+ { name: 'imap', shortName: 'im' },
3831
3827
  { name: 'nmap', shortName: 'nm' },
3832
3828
  { name: 'vmap', shortName: 'vm' },
3833
3829
  { name: 'unmap' },
@@ -3882,7 +3878,7 @@
3882
3878
  if (command.type == 'exToKey') {
3883
3879
  // Handle Ex to Key mapping.
3884
3880
  for (var i = 0; i < command.toKeys.length; i++) {
3885
- CodeMirror.Vim.handleKey(cm, command.toKeys[i]);
3881
+ CodeMirror.Vim.handleKey(cm, command.toKeys[i], 'mapping');
3886
3882
  }
3887
3883
  return;
3888
3884
  } else if (command.type == 'exToEx') {
@@ -4006,7 +4002,7 @@
4006
4002
  this.commandMap_[commandName] = {
4007
4003
  name: commandName,
4008
4004
  type: 'exToKey',
4009
- toKeys: parseKeyString(rhs),
4005
+ toKeys: rhs,
4010
4006
  user: true
4011
4007
  };
4012
4008
  }
@@ -4014,7 +4010,7 @@
4014
4010
  if (rhs != ':' && rhs.charAt(0) == ':') {
4015
4011
  // Key to Ex mapping.
4016
4012
  var mapping = {
4017
- keys: parseKeyString(lhs),
4013
+ keys: lhs,
4018
4014
  type: 'keyToEx',
4019
4015
  exArgs: { input: rhs.substring(1) },
4020
4016
  user: true};
@@ -4023,9 +4019,9 @@
4023
4019
  } else {
4024
4020
  // Key to key mapping
4025
4021
  var mapping = {
4026
- keys: parseKeyString(lhs),
4022
+ keys: lhs,
4027
4023
  type: 'keyToKey',
4028
- toKeys: parseKeyString(rhs),
4024
+ toKeys: rhs,
4029
4025
  user: true
4030
4026
  };
4031
4027
  if (ctx) { mapping.context = ctx; }
@@ -4034,15 +4030,6 @@
4034
4030
  }
4035
4031
  },
4036
4032
  unmap: function(lhs, ctx) {
4037
- var arrayEquals = function(a, b) {
4038
- if (a === b) return true;
4039
- if (a == null || b == null) return true;
4040
- if (a.length != b.length) return false;
4041
- for (var i = 0; i < a.length; i++) {
4042
- if (a[i] !== b[i]) return false;
4043
- }
4044
- return true;
4045
- };
4046
4033
  if (lhs != ':' && lhs.charAt(0) == ':') {
4047
4034
  // Ex to Ex or Ex to key mapping
4048
4035
  if (ctx) { throw Error('Mode not supported for ex mappings'); }
@@ -4053,9 +4040,9 @@
4053
4040
  }
4054
4041
  } else {
4055
4042
  // Key to Ex or key to key mapping
4056
- var keys = parseKeyString(lhs);
4043
+ var keys = lhs;
4057
4044
  for (var i = 0; i < defaultKeymap.length; i++) {
4058
- if (arrayEquals(keys, defaultKeymap[i].keys)
4045
+ if (keys == defaultKeymap[i].keys
4059
4046
  && defaultKeymap[i].context === ctx
4060
4047
  && defaultKeymap[i].user) {
4061
4048
  defaultKeymap.splice(i, 1);
@@ -4067,21 +4054,6 @@
4067
4054
  }
4068
4055
  };
4069
4056
 
4070
- // Converts a key string sequence of the form a<C-w>bd<Left> into Vim's
4071
- // keymap representation.
4072
- function parseKeyString(str) {
4073
- var key, match;
4074
- var keys = [];
4075
- while (str) {
4076
- match = (/<\w+-.+?>|<\w+>|./).exec(str);
4077
- if (match === null)break;
4078
- key = match[0];
4079
- str = str.substring(match.index + key.length);
4080
- keys.push(key);
4081
- }
4082
- return keys;
4083
- }
4084
-
4085
4057
  var exCommands = {
4086
4058
  map: function(cm, params, ctx) {
4087
4059
  var mapArgs = params.args;
@@ -4093,6 +4065,7 @@
4093
4065
  }
4094
4066
  exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
4095
4067
  },
4068
+ imap: function(cm, params) { this.map(cm, params, 'insert'); },
4096
4069
  nmap: function(cm, params) { this.map(cm, params, 'normal'); },
4097
4070
  vmap: function(cm, params) { this.map(cm, params, 'visual'); },
4098
4071
  unmap: function(cm, params, ctx) {
@@ -4574,65 +4547,10 @@
4574
4547
  });
4575
4548
  }
4576
4549
 
4577
- // Register Vim with CodeMirror
4578
- function buildVimKeyMap() {
4579
- /**
4580
- * Handle the raw key event from CodeMirror. Translate the
4581
- * Shift + key modifier to the resulting letter, while preserving other
4582
- * modifers.
4583
- */
4584
- function cmKeyToVimKey(key, modifier) {
4585
- var vimKey = key;
4586
- if (isUpperCase(vimKey) && modifier == 'Ctrl') {
4587
- vimKey = vimKey.toLowerCase();
4588
- }
4589
- if (modifier) {
4590
- // Vim will parse modifier+key combination as a single key.
4591
- vimKey = modifier.charAt(0) + '-' + vimKey;
4592
- }
4593
- var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[vimKey];
4594
- vimKey = specialKey ? specialKey : vimKey;
4595
- vimKey = vimKey.length > 1 ? '<'+ vimKey + '>' : vimKey;
4596
- return vimKey;
4597
- }
4598
-
4599
- // Closure to bind CodeMirror, key, modifier.
4600
- function keyMapper(vimKey) {
4601
- return function(cm) {
4602
- CodeMirror.signal(cm, 'vim-keypress', vimKey);
4603
- CodeMirror.Vim.handleKey(cm, vimKey);
4604
- };
4605
- }
4606
-
4607
- var cmToVimKeymap = {
4550
+ CodeMirror.keyMap.vim = {
4608
4551
  'nofallthrough': true,
4609
4552
  'style': 'fat-cursor'
4610
4553
  };
4611
- function bindKeys(keys, modifier) {
4612
- for (var i = 0; i < keys.length; i++) {
4613
- var key = keys[i];
4614
- if (!modifier && key.length == 1) {
4615
- // Wrap all keys without modifiers with '' to identify them by their
4616
- // key characters instead of key identifiers.
4617
- key = "'" + key + "'";
4618
- }
4619
- var vimKey = cmKeyToVimKey(keys[i], modifier);
4620
- var cmKey = modifier ? modifier + '-' + key : key;
4621
- cmToVimKeymap[cmKey] = keyMapper(vimKey);
4622
- }
4623
- }
4624
- bindKeys(upperCaseAlphabet);
4625
- bindKeys(lowerCaseAlphabet);
4626
- bindKeys(upperCaseAlphabet, 'Ctrl');
4627
- bindKeys(specialSymbols);
4628
- bindKeys(specialSymbols, 'Ctrl');
4629
- bindKeys(numbers);
4630
- bindKeys(numbers, 'Ctrl');
4631
- bindKeys(specialKeys);
4632
- bindKeys(specialKeys, 'Ctrl');
4633
- return cmToVimKeymap;
4634
- }
4635
- CodeMirror.keyMap.vim = buildVimKeyMap();
4636
4554
 
4637
4555
  function exitInsertMode(cm) {
4638
4556
  var vim = cm.state.vim;
@@ -4688,63 +4606,13 @@
4688
4606
  }
4689
4607
  }
4690
4608
 
4691
- defineOption('enableInsertModeEscKeys', false, 'boolean');
4692
- // Use this option to customize the two-character ESC keymap.
4693
- // If you want to use characters other than i j or k you'll have to add
4694
- // lines to the vim-insert and await-second keymaps later in this file.
4695
- defineOption('insertModeEscKeys', 'kj', 'string');
4696
4609
  // The timeout in milliseconds for the two-character ESC keymap should be
4697
4610
  // adjusted according to your typing speed to prevent false positives.
4698
4611
  defineOption('insertModeEscKeysTimeout', 200, 'number');
4699
- function firstEscCharacterHandler(ch) {
4700
- return function(cm){
4701
- var keys = getOption('insertModeEscKeys');
4702
- var firstEscCharacter = keys && keys.length > 1 && keys.charAt(0);
4703
- if (!getOption('enableInsertModeEscKeys')|| firstEscCharacter !== ch) {
4704
- return CodeMirror.Pass;
4705
- } else {
4706
- cm.replaceRange(ch, cm.getCursor(), cm.getCursor(), "+input");
4707
- cm.setOption('keyMap', 'await-second');
4708
- cm.state.vim.awaitingEscapeSecondCharacter = true;
4709
- setTimeout(
4710
- function(){
4711
- if(cm.state.vim.awaitingEscapeSecondCharacter) {
4712
- cm.state.vim.awaitingEscapeSecondCharacter = false;
4713
- cm.setOption('keyMap', 'vim-insert');
4714
- }
4715
- },
4716
- getOption('insertModeEscKeysTimeout'));
4717
- }
4718
- };
4719
- }
4720
- function secondEscCharacterHandler(ch){
4721
- return function(cm) {
4722
- var keys = getOption('insertModeEscKeys');
4723
- var secondEscCharacter = keys && keys.length > 1 && keys.charAt(1);
4724
- if (!getOption('enableInsertModeEscKeys')|| secondEscCharacter !== ch) {
4725
- return CodeMirror.Pass;
4726
- // This is not the handler you're looking for. Just insert as usual.
4727
- } else {
4728
- if (cm.state.vim.insertMode) {
4729
- var lastChange = vimGlobalState.macroModeState.lastInsertModeChanges;
4730
- if (lastChange && lastChange.changes.length) {
4731
- lastChange.changes.pop();
4732
- }
4733
- }
4734
- cm.state.vim.awaitingEscapeSecondCharacter = false;
4735
- cm.replaceRange('', {ch: cm.getCursor().ch - 1, line: cm.getCursor().line},
4736
- cm.getCursor(), "+input");
4737
- exitInsertMode(cm);
4738
- }
4739
- };
4740
- }
4741
4612
 
4742
4613
  CodeMirror.keyMap['vim-insert'] = {
4743
4614
  // TODO: override navigation keys so that Esc will cancel automatic
4744
4615
  // indentation from o, O, i_<CR>
4745
- 'Esc': exitInsertMode,
4746
- 'Ctrl-[': exitInsertMode,
4747
- 'Ctrl-C': exitInsertMode,
4748
4616
  'Ctrl-N': 'autocomplete',
4749
4617
  'Ctrl-P': 'autocomplete',
4750
4618
  'Enter': function(cm) {
@@ -4752,20 +4620,10 @@
4752
4620
  CodeMirror.commands.newlineAndIndent;
4753
4621
  fn(cm);
4754
4622
  },
4755
- // The next few lines are where you'd add additional handlers if
4756
- // you wanted to use keys other than i j and k for two-character
4757
- // escape sequences. Don't forget to add them in the await-second
4758
- // section as well.
4759
- "'i'": firstEscCharacterHandler('i'),
4760
- "'j'": firstEscCharacterHandler('j'),
4761
- "'k'": firstEscCharacterHandler('k'),
4762
4623
  fallthrough: ['default']
4763
4624
  };
4764
4625
 
4765
4626
  CodeMirror.keyMap['await-second'] = {
4766
- "'i'": secondEscCharacterHandler('i'),
4767
- "'j'": secondEscCharacterHandler('j'),
4768
- "'k'": secondEscCharacterHandler('k'),
4769
4627
  fallthrough: ['vim-insert']
4770
4628
  };
4771
4629
 
@@ -4789,7 +4647,7 @@
4789
4647
  match = (/<\w+-.+?>|<\w+>|./).exec(text);
4790
4648
  key = match[0];
4791
4649
  text = text.substring(match.index + key.length);
4792
- CodeMirror.Vim.handleKey(cm, key);
4650
+ CodeMirror.Vim.handleKey(cm, key, 'macro');
4793
4651
  if (vim.insertMode) {
4794
4652
  var changes = register.insertModeChanges[imc++].changes;
4795
4653
  vimGlobalState.macroModeState.lastInsertModeChanges.changes =
@@ -4869,10 +4727,7 @@
4869
4727
  } else if (cm.doc.history.lastSelOrigin == '*mouse') {
4870
4728
  // Reset lastHPos if mouse click was done in normal mode.
4871
4729
  vim.lastHPos = cm.doc.getCursor().ch;
4872
- if (cm.somethingSelected()) {
4873
- // If something is still selected, enter visual mode.
4874
- vim.visualMode = true;
4875
- }
4730
+ handleExternalSelection(cm, vim);
4876
4731
  }
4877
4732
  if (vim.visualMode) {
4878
4733
  var from, head;
@@ -4891,6 +4746,22 @@
4891
4746
  }
4892
4747
  }
4893
4748
 
4749
+ function handleExternalSelection(cm, vim) {
4750
+ var anchor = cm.getCursor('anchor');
4751
+ var head = cm.getCursor('head');
4752
+ // Enter visual mode when the mouse selects text.
4753
+ if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
4754
+ vim.visualMode = true;
4755
+ vim.visualLine = false;
4756
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
4757
+ cm.on('mousedown', exitVisualMode);
4758
+ }
4759
+ if (vim.visualMode) {
4760
+ updateMark(cm, vim, '<', cursorMin(head, anchor));
4761
+ updateMark(cm, vim, '>', cursorMax(head, anchor));
4762
+ }
4763
+ }
4764
+
4894
4765
  /** Wrapper for special keys pressed in insert mode */
4895
4766
  function InsertModeKey(keyName) {
4896
4767
  this.keyName = keyName;