codemirror-rails 3.00 → 3.02

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README.md +5 -2
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/test/dummy/config/database.yml +25 -0
  4. data/test/dummy/db/.gitkeep +0 -0
  5. data/test/integration/codemirror_rails_integration_test.rb +1 -1
  6. data/vendor/assets/javascripts/codemirror.js +385 -152
  7. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +279 -66
  8. data/vendor/assets/javascripts/codemirror/modes/apl.js +160 -0
  9. data/vendor/assets/javascripts/codemirror/modes/asterisk.js +183 -0
  10. data/vendor/assets/javascripts/codemirror/modes/clike.js +2 -0
  11. data/vendor/assets/javascripts/codemirror/modes/clojure.js +3 -3
  12. data/vendor/assets/javascripts/codemirror/modes/css.js +2 -2
  13. data/vendor/assets/javascripts/codemirror/modes/d.js +205 -0
  14. data/vendor/assets/javascripts/codemirror/modes/gfm.js +2 -1
  15. data/vendor/assets/javascripts/codemirror/modes/javascript.js +13 -2
  16. data/vendor/assets/javascripts/codemirror/modes/markdown.js +8 -7
  17. data/vendor/assets/javascripts/codemirror/modes/properties.js +0 -0
  18. data/vendor/assets/javascripts/codemirror/modes/sass.js +349 -0
  19. data/vendor/assets/javascripts/codemirror/modes/sieve.js +37 -10
  20. data/vendor/assets/javascripts/codemirror/modes/sql.js +268 -0
  21. data/vendor/assets/javascripts/codemirror/modes/xquery.js +8 -8
  22. data/vendor/assets/javascripts/codemirror/utils/collapserange.js +68 -0
  23. data/vendor/assets/javascripts/codemirror/utils/dialog.js +1 -0
  24. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +2 -1
  25. data/vendor/assets/javascripts/codemirror/utils/formatting.js +9 -3
  26. data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +5 -4
  27. data/vendor/assets/javascripts/codemirror/utils/python-hint.js +93 -0
  28. data/vendor/assets/javascripts/codemirror/utils/runmode-standalone.js +51 -10
  29. data/vendor/assets/javascripts/codemirror/utils/search.js +20 -8
  30. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +17 -10
  31. data/vendor/assets/stylesheets/codemirror.css +10 -9
  32. metadata +47 -15
  33. data/vendor/assets/javascripts/codemirror/modes/xmlpure.js +0 -490
  34. data/vendor/assets/stylesheets/codemirror/modes/diff.css +0 -3
@@ -115,6 +115,10 @@
115
115
  motion: 'moveByWords',
116
116
  motionArgs: { forward: false, wordEnd: true, bigWord: true,
117
117
  inclusive: true }},
118
+ { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
119
+ motionArgs: { forward: false }},
120
+ { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
121
+ motionArgs: { forward: true }},
118
122
  { keys: ['Ctrl-f'], type: 'motion',
119
123
  motion: 'moveByPage', motionArgs: { forward: true }},
120
124
  { keys: ['Ctrl-b'], type: 'motion',
@@ -247,7 +251,7 @@
247
251
  var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
248
252
  'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
249
253
  var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
250
- numbers);
254
+ numbers).concat(['<', '>']);
251
255
  var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
252
256
  numbers).concat('-\"'.split(''));
253
257
 
@@ -720,7 +724,11 @@
720
724
  // Handle user defined Ex to Ex mappings
721
725
  exCommandDispatcher.processCommand(cm, command.exArgs.input);
722
726
  } else {
723
- showPrompt(cm, onPromptClose, ':');
727
+ if (vim.visualMode) {
728
+ showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>');
729
+ } else {
730
+ showPrompt(cm, onPromptClose, ':');
731
+ }
724
732
  }
725
733
  },
726
734
  evalInput: function(cm, vim) {
@@ -806,6 +814,12 @@
806
814
  // CodeMirror can't figure out that we changed directions...
807
815
  cm.setCursor(selectionStart);
808
816
  cm.setSelection(selectionStart, selectionEnd);
817
+ updateMark(cm, vim, '<',
818
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
819
+ : selectionEnd);
820
+ updateMark(cm, vim, '>',
821
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
822
+ : selectionStart);
809
823
  } else if (!operator) {
810
824
  curEnd = clipCursorToContent(cm, curEnd);
811
825
  cm.setCursor(curEnd.line, curEnd.ch);
@@ -923,6 +937,22 @@
923
937
  cm.setCursor(curStart);
924
938
  return curEnd;
925
939
  },
940
+ moveByParagraph: function(cm, motionArgs) {
941
+ var line = cm.getCursor().line;
942
+ var repeat = motionArgs.repeat;
943
+ var inc = motionArgs.forward ? 1 : -1;
944
+ for (var i = 0; i < repeat; i++) {
945
+ if ((!motionArgs.forward && line === 0) ||
946
+ (motionArgs.forward && line == cm.lineCount() - 1)) {
947
+ break;
948
+ }
949
+ line += inc;
950
+ while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
951
+ line += inc;
952
+ }
953
+ }
954
+ return { line: line, ch: 0 };
955
+ },
926
956
  moveByWords: function(cm, motionArgs) {
927
957
  return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
928
958
  !!motionArgs.wordEnd, !!motionArgs.bigWord);
@@ -1119,21 +1149,29 @@
1119
1149
  cm.setSelection(curStart, curEnd);
1120
1150
  }
1121
1151
  } else {
1152
+ curStart = cm.getCursor('anchor');
1153
+ curEnd = cm.getCursor('head');
1122
1154
  if (!vim.visualLine && actionArgs.linewise) {
1123
1155
  // Shift-V pressed in characterwise visual mode. Switch to linewise
1124
1156
  // visual mode instead of exiting visual mode.
1125
1157
  vim.visualLine = true;
1126
- curStart = cm.getCursor('anchor');
1127
- curEnd = cm.getCursor('head');
1128
1158
  curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
1129
1159
  lineLength(cm, curStart.line);
1130
1160
  curEnd.ch = cursorIsBefore(curStart, curEnd) ?
1131
1161
  lineLength(cm, curEnd.line) : 0;
1132
1162
  cm.setSelection(curStart, curEnd);
1163
+ } else if (vim.visualLine && !actionArgs.linewise) {
1164
+ // v pressed in linewise visual mode. Switch to characterwise visual
1165
+ // mode instead of exiting visual mode.
1166
+ vim.visualLine = false;
1133
1167
  } else {
1134
1168
  exitVisualMode(cm, vim);
1135
1169
  }
1136
1170
  }
1171
+ updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
1172
+ : curEnd);
1173
+ updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
1174
+ : curStart);
1137
1175
  },
1138
1176
  joinLines: function(cm, actionArgs, vim) {
1139
1177
  var curStart, curEnd;
@@ -1232,13 +1270,7 @@
1232
1270
  },
1233
1271
  setMark: function(cm, actionArgs, vim) {
1234
1272
  var markName = actionArgs.selectedCharacter;
1235
- if (!inArray(markName, validMarks)) {
1236
- return;
1237
- }
1238
- if (vim.marks[markName]) {
1239
- vim.marks[markName].clear();
1240
- }
1241
- vim.marks[markName] = cm.setBookmark(cm.getCursor());
1273
+ updateMark(cm, vim, markName, cm.getCursor());
1242
1274
  },
1243
1275
  replace: function(cm, actionArgs) {
1244
1276
  var replaceWith = actionArgs.selectedCharacter;
@@ -1636,6 +1668,16 @@
1636
1668
  return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
1637
1669
  }
1638
1670
 
1671
+ function updateMark(cm, vim, markName, pos) {
1672
+ if (!inArray(markName, validMarks)) {
1673
+ return;
1674
+ }
1675
+ if (vim.marks[markName]) {
1676
+ vim.marks[markName].clear();
1677
+ }
1678
+ vim.marks[markName] = cm.setBookmark(pos);
1679
+ }
1680
+
1639
1681
  function charIdxInLine(start, line, character, forward, includeChar) {
1640
1682
  // Search for char in line.
1641
1683
  // motion_options: {forward, includeChar}
@@ -1804,6 +1846,12 @@
1804
1846
  setMarked: function(marked) {
1805
1847
  this.marked = marked;
1806
1848
  },
1849
+ getOverlay: function() {
1850
+ return this.searchOverlay;
1851
+ },
1852
+ setOverlay: function(overlay) {
1853
+ this.searchOverlay = overlay;
1854
+ },
1807
1855
  isReversed: function() {
1808
1856
  return getVimGlobalState().isReversed;
1809
1857
  },
@@ -1815,9 +1863,9 @@
1815
1863
  var vim = getVimState(cm);
1816
1864
  return vim.searchState_ || (vim.searchState_ = new SearchState());
1817
1865
  }
1818
- function dialog(cm, text, shortText, callback) {
1866
+ function dialog(cm, text, shortText, callback, initialValue) {
1819
1867
  if (cm.openDialog) {
1820
- cm.openDialog(text, callback, {bottom: true});
1868
+ cm.openDialog(text, callback, { bottom: true, value: initialValue });
1821
1869
  }
1822
1870
  else {
1823
1871
  callback(prompt(shortText, ""));
@@ -1899,9 +1947,10 @@
1899
1947
  return raw;
1900
1948
  }
1901
1949
  var searchPromptDesc = '(Javascript regexp)';
1902
- function showPrompt(cm, onPromptClose, prefix, desc) {
1950
+ function showPrompt(cm, onPromptClose, prefix, desc, initialValue) {
1903
1951
  var shortText = (prefix || '') + ' ' + (desc || '');
1904
- dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose);
1952
+ dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose,
1953
+ initialValue);
1905
1954
  }
1906
1955
  function regexEqual(r1, r2) {
1907
1956
  if (r1 instanceof RegExp && r2 instanceof RegExp) {
@@ -1934,17 +1983,53 @@
1934
1983
  state.setQuery(query);
1935
1984
  });
1936
1985
  }
1986
+ function searchOverlay(query) {
1987
+ return {
1988
+ token: function(stream) {
1989
+ var match = stream.match(query, false);
1990
+ if (match) {
1991
+ if (!stream.sol()) {
1992
+ // Backtrack 1 to match \b
1993
+ stream.backUp(1);
1994
+ if (!query.exec(stream.next() + match[0])) {
1995
+ stream.next();
1996
+ return null;
1997
+ }
1998
+ }
1999
+ stream.match(query);
2000
+ return "searching";
2001
+ }
2002
+ while (!stream.eol()) {
2003
+ stream.next();
2004
+ if (stream.match(query, false)) break;
2005
+ }
2006
+ },
2007
+ query: query
2008
+ };
2009
+ }
1937
2010
  function highlightSearchMatches(cm, query) {
1938
- // TODO: Highlight only text inside the viewport. Highlighting everything
1939
- // is inefficient and expensive.
1940
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
1941
- var marked = [];
1942
- for (var cursor = cm.getSearchCursor(query);
1943
- cursor.findNext();) {
1944
- marked.push(cm.markText(cursor.from(), cursor.to(),
1945
- { className: 'CodeMirror-searching' }));
2011
+ if (cm.addOverlay) {
2012
+ var overlay = getSearchState(cm).getOverlay();
2013
+ if (!overlay || query != overlay.query) {
2014
+ if (overlay) {
2015
+ cm.removeOverlay(overlay);
2016
+ }
2017
+ overlay = searchOverlay(query);
2018
+ cm.addOverlay(overlay);
2019
+ getSearchState(cm).setOverlay(overlay);
2020
+ }
2021
+ } else {
2022
+ // TODO: Highlight only text inside the viewport. Highlighting everything
2023
+ // is inefficient and expensive.
2024
+ if (cm.lineCount() < 2000) { // This is too expensive on big documents.
2025
+ var marked = [];
2026
+ for (var cursor = cm.getSearchCursor(query);
2027
+ cursor.findNext();) {
2028
+ marked.push(cm.markText(cursor.from(), cursor.to(),
2029
+ { className: 'cm-searching' }));
2030
+ }
2031
+ getSearchState(cm).setMarked(marked);
1946
2032
  }
1947
- getSearchState(cm).setMarked(marked);
1948
2033
  }
1949
2034
  }
1950
2035
  function findNext(cm, prev, repeat) {
@@ -1978,20 +2063,52 @@
1978
2063
  return cursor.from();
1979
2064
  });}
1980
2065
  function clearSearchHighlight(cm) {
1981
- cm.operation(function() {
1982
- var state = getSearchState(cm);
1983
- if (!state.getQuery()) {
1984
- return;
1985
- }
1986
- var marked = state.getMarked();
1987
- if (!marked) {
1988
- return;
1989
- }
1990
- for (var i = 0; i < marked.length; ++i) {
1991
- marked[i].clear();
2066
+ if (cm.addOverlay) {
2067
+ cm.removeOverlay(getSearchState(cm).getOverlay());
2068
+ getSearchState(cm).setOverlay(null);
2069
+ } else {
2070
+ cm.operation(function() {
2071
+ var state = getSearchState(cm);
2072
+ if (!state.getQuery()) {
2073
+ return;
2074
+ }
2075
+ var marked = state.getMarked();
2076
+ if (!marked) {
2077
+ return;
2078
+ }
2079
+ for (var i = 0; i < marked.length; ++i) {
2080
+ marked[i].clear();
2081
+ }
2082
+ state.setMarked(null);
2083
+ });
2084
+ }
2085
+ }
2086
+ /**
2087
+ * Check if pos is in the specified range, INCLUSIVE.
2088
+ * Range can be specified with 1 or 2 arguments.
2089
+ * If the first range argument is an array, treat it as an array of line
2090
+ * numbers. Match pos against any of the lines.
2091
+ * If the first range argument is a number,
2092
+ * if there is only 1 range argument, check if pos has the same line
2093
+ * number
2094
+ * if there are 2 range arguments, then check if pos is in between the two
2095
+ * range arguments.
2096
+ */
2097
+ function isInRange(pos, start, end) {
2098
+ if (typeof pos != 'number') {
2099
+ // Assume it is a cursor position. Get the line number.
2100
+ pos = pos.line;
2101
+ }
2102
+ if (start instanceof Array) {
2103
+ return inArray(pos, start);
2104
+ } else {
2105
+ if (end) {
2106
+ return (pos >= start && pos <= end);
2107
+ } else {
2108
+ return pos == start;
1992
2109
  }
1993
- state.setMarked(null);
1994
- });}
2110
+ }
2111
+ }
1995
2112
 
1996
2113
  // Ex command handling
1997
2114
  // Care must be taken when adding to the default Ex command map. For any
@@ -2001,14 +2118,23 @@
2001
2118
  { name: 'map', type: 'builtIn' },
2002
2119
  { name: 'write', shortName: 'w', type: 'builtIn' },
2003
2120
  { name: 'undo', shortName: 'u', type: 'builtIn' },
2004
- { name: 'redo', shortName: 'red', type: 'builtIn' }
2121
+ { name: 'redo', shortName: 'red', type: 'builtIn' },
2122
+ { name: 'substitute', shortName: 's', type: 'builtIn'}
2005
2123
  ];
2006
- var ExCommandDispatcher = function() {
2124
+ Vim.ExCommandDispatcher = function() {
2007
2125
  this.buildCommandMap_();
2008
2126
  };
2009
- ExCommandDispatcher.prototype = {
2127
+ Vim.ExCommandDispatcher.prototype = {
2010
2128
  processCommand: function(cm, input) {
2011
- var params = this.parseInput_(input);
2129
+ var inputStream = new CodeMirror.StringStream(input);
2130
+ var params = {};
2131
+ params.input = input;
2132
+ try {
2133
+ this.parseInput_(cm, inputStream, params);
2134
+ } catch(e) {
2135
+ showConfirm(cm, e);
2136
+ return;
2137
+ }
2012
2138
  var commandName;
2013
2139
  if (!params.commandName) {
2014
2140
  // If only a line range is defined, move to the line.
@@ -2019,6 +2145,7 @@
2019
2145
  var command = this.matchCommand_(params.commandName);
2020
2146
  if (command) {
2021
2147
  commandName = command.name;
2148
+ this.parseCommandArgs_(inputStream, params, command);
2022
2149
  if (command.type == 'exToKey') {
2023
2150
  // Handle Ex to Key mapping.
2024
2151
  for (var i = 0; i < command.toKeys.length; i++) {
@@ -2038,37 +2165,63 @@
2038
2165
  }
2039
2166
  exCommands[commandName](cm, params);
2040
2167
  },
2041
- parseInput_: function(input) {
2042
- var result = {};
2043
- result.input = input;
2044
- var idx = 0;
2045
- // Trim preceding ':'.
2046
- var colons = (/^:+/).exec(input);
2047
- if (colons) {
2048
- idx += colons[0].length;
2049
- }
2050
-
2168
+ parseInput_: function(cm, inputStream, result) {
2169
+ inputStream.eatWhile(':');
2051
2170
  // Parse range.
2052
- var numberMatch = (/^(\d+)/).exec(input.substring(idx));
2053
- if (numberMatch) {
2054
- result.line = parseInt(numberMatch[1], 10);
2055
- idx += numberMatch[0].length;
2171
+ if (inputStream.eat('%')) {
2172
+ result.line = 0;
2173
+ result.lineEnd = cm.lineCount() - 1;
2174
+ } else {
2175
+ result.line = this.parseLineSpec_(cm, inputStream);
2176
+ if (result.line !== undefined && inputStream.eat(',')) {
2177
+ result.lineEnd = this.parseLineSpec_(cm, inputStream);
2178
+ }
2056
2179
  }
2057
2180
 
2058
2181
  // Parse command name.
2059
- var commandMatch = (/^(\w+)/).exec(input.substring(idx));
2182
+ var commandMatch = inputStream.match(/^(\w+)/);
2060
2183
  if (commandMatch) {
2061
2184
  result.commandName = commandMatch[1];
2062
- idx += commandMatch[1].length;
2185
+ } else {
2186
+ result.commandName = inputStream.match(/.*/)[0];
2063
2187
  }
2064
2188
 
2189
+ return result;
2190
+ },
2191
+ parseLineSpec_: function(cm, inputStream) {
2192
+ var numberMatch = inputStream.match(/^(\d+)/);
2193
+ if (numberMatch) {
2194
+ return parseInt(numberMatch[1], 10) - 1;
2195
+ }
2196
+ switch (inputStream.next()) {
2197
+ case '.':
2198
+ return cm.getCursor().line;
2199
+ case '$':
2200
+ return cm.lineCount() - 1;
2201
+ case '\'':
2202
+ var mark = getVimState(cm).marks[inputStream.next()];
2203
+ if (mark && mark.find()) {
2204
+ return mark.find().line;
2205
+ } else {
2206
+ throw "Mark not set";
2207
+ }
2208
+ break;
2209
+ default:
2210
+ inputStream.backUp(1);
2211
+ return cm.getCursor().line;
2212
+ }
2213
+ },
2214
+ parseCommandArgs_: function(inputStream, params, command) {
2215
+ if (inputStream.eol()) {
2216
+ return;
2217
+ }
2218
+ params.argString = inputStream.match(/.*/)[0];
2065
2219
  // Parse command-line arguments
2066
- var args = trim(input.substring(idx)).split(/\s+/);
2220
+ var delim = command.argDelimiter || /\s+/;
2221
+ var args = trim(params.argString).split(delim);
2067
2222
  if (args.length && args[0]) {
2068
- result.commandArgs = args;
2223
+ params.args = args;
2069
2224
  }
2070
-
2071
- return result;
2072
2225
  },
2073
2226
  matchCommand_: function(commandName) {
2074
2227
  // Return the command in the command map that matches the shortest
@@ -2095,9 +2248,9 @@
2095
2248
  }
2096
2249
  },
2097
2250
  map: function(lhs, rhs) {
2098
- if (lhs.charAt(0) == ':') {
2251
+ if (lhs != ':' && lhs.charAt(0) == ':') {
2099
2252
  var commandName = lhs.substring(1);
2100
- if (rhs.charAt(0) == ':') {
2253
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2101
2254
  // Ex to Ex mapping
2102
2255
  this.commandMap_[commandName] = {
2103
2256
  name: commandName,
@@ -2113,7 +2266,7 @@
2113
2266
  };
2114
2267
  }
2115
2268
  } else {
2116
- if (rhs.charAt(0) == ':') {
2269
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2117
2270
  // Key to Ex mapping.
2118
2271
  defaultKeymap.unshift({
2119
2272
  keys: parseKeyString(lhs),
@@ -2172,7 +2325,7 @@
2172
2325
 
2173
2326
  var exCommands = {
2174
2327
  map: function(cm, params) {
2175
- var mapArgs = params.commandArgs;
2328
+ var mapArgs = params.args;
2176
2329
  if (!mapArgs || mapArgs.length < 2) {
2177
2330
  if (cm) {
2178
2331
  showConfirm(cm, 'Invalid mapping: ' + params.input);
@@ -2187,6 +2340,66 @@
2187
2340
  motionArgs: { forward: false, explicitRepeat: true,
2188
2341
  linewise: true, repeat: params.line }});
2189
2342
  },
2343
+ substitute: function(cm, params) {
2344
+ var argString = params.argString;
2345
+ var slashes = findUnescapedSlashes(argString);
2346
+ if (slashes[0] !== 0) {
2347
+ showConfirm(cm, 'Substitutions should be of the form ' +
2348
+ ':s/pattern/replace/');
2349
+ return;
2350
+ }
2351
+ var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
2352
+ var replacePart = '';
2353
+ var flagsPart;
2354
+ var count;
2355
+ if (slashes[1]) {
2356
+ replacePart = argString.substring(slashes[1] + 1, slashes[2]);
2357
+ }
2358
+ if (slashes[2]) {
2359
+ // After the 3rd slash, we can have flags followed by a space followed
2360
+ // by count.
2361
+ var trailing = argString.substring(slashes[2] + 1).split(' ');
2362
+ flagsPart = trailing[0];
2363
+ count = parseInt(trailing[1]);
2364
+ }
2365
+ if (flagsPart) {
2366
+ regexPart = regexPart + '/' + flagsPart;
2367
+ }
2368
+ if (regexPart) {
2369
+ // If regex part is empty, then use the previous query. Otherwise use
2370
+ // the regex part as the new query.
2371
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
2372
+ true /** smartCase */);
2373
+ }
2374
+ var state = getSearchState(cm);
2375
+ var query = state.getQuery();
2376
+ var lineStart = params.line || 0;
2377
+ var lineEnd = params.lineEnd || lineStart;
2378
+ if (count) {
2379
+ lineStart = lineEnd;
2380
+ lineEnd = lineStart + count - 1;
2381
+ }
2382
+ var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
2383
+ function doReplace() {
2384
+ for (var cursor = cm.getSearchCursor(query, startPos);
2385
+ cursor.findNext() &&
2386
+ isInRange(cursor.from(), lineStart, lineEnd);) {
2387
+ var text = cm.getRange(cursor.from(), cursor.to());
2388
+ var newText = text.replace(query, replacePart);
2389
+ cursor.replace(newText);
2390
+ }
2391
+ var vim = getVimState(cm);
2392
+ if (vim.visualMode) {
2393
+ exitVisualMode(cm, vim);
2394
+ }
2395
+ }
2396
+ if (cm.compoundChange) {
2397
+ // Only exists in v2
2398
+ cm.compoundChange(doReplace);
2399
+ } else {
2400
+ cm.operation(doReplace);
2401
+ }
2402
+ },
2190
2403
  redo: CodeMirror.commands.redo,
2191
2404
  undo: CodeMirror.commands.undo,
2192
2405
  write: function(cm) {
@@ -2200,7 +2413,7 @@
2200
2413
  }
2201
2414
  };
2202
2415
 
2203
- var exCommandDispatcher = new ExCommandDispatcher();
2416
+ var exCommandDispatcher = new Vim.ExCommandDispatcher();
2204
2417
 
2205
2418
  // Register Vim with CodeMirror
2206
2419
  function buildVimKeyMap() {