codemirror-rails 4.2 → 4.3

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +10 -0
  4. data/codemirror-rails.gemspec +3 -4
  5. data/gemfiles/Gemfile.rails-3.2 +5 -0
  6. data/gemfiles/Gemfile.rails-4.0 +4 -0
  7. data/gemfiles/Gemfile.rails-4.1 +4 -0
  8. data/lib/codemirror/rails/version.rb +2 -2
  9. data/test/{dummy → dummy-3.2.x}/README.rdoc +0 -0
  10. data/test/{dummy → dummy-3.2.x}/Rakefile +0 -0
  11. data/test/{dummy → dummy-3.2.x}/app/assets/javascripts/application.js +0 -0
  12. data/test/{dummy → dummy-3.2.x}/app/assets/stylesheets/application.css +0 -0
  13. data/test/{dummy → dummy-3.2.x}/app/controllers/application_controller.rb +0 -0
  14. data/test/{dummy → dummy-3.2.x}/app/helpers/application_helper.rb +0 -0
  15. data/test/{dummy → dummy-3.2.x}/app/mailers/.gitkeep +0 -0
  16. data/test/{dummy → dummy-3.2.x}/app/models/.gitkeep +0 -0
  17. data/test/{dummy → dummy-3.2.x}/app/views/layouts/application.html.erb +0 -0
  18. data/test/{dummy → dummy-3.2.x}/config.ru +0 -0
  19. data/test/{dummy → dummy-3.2.x}/config/application.rb +0 -0
  20. data/test/{dummy → dummy-3.2.x}/config/boot.rb +0 -0
  21. data/test/{dummy → dummy-3.2.x}/config/database.yml +0 -0
  22. data/test/{dummy → dummy-3.2.x}/config/environment.rb +0 -0
  23. data/test/{dummy → dummy-3.2.x}/config/environments/development.rb +0 -0
  24. data/test/{dummy → dummy-3.2.x}/config/environments/production.rb +0 -0
  25. data/test/{dummy → dummy-3.2.x}/config/environments/test.rb +0 -0
  26. data/test/{dummy → dummy-3.2.x}/config/initializers/backtrace_silencers.rb +0 -0
  27. data/test/{dummy → dummy-3.2.x}/config/initializers/inflections.rb +0 -0
  28. data/test/{dummy → dummy-3.2.x}/config/initializers/mime_types.rb +0 -0
  29. data/test/{dummy → dummy-3.2.x}/config/initializers/secret_token.rb +0 -0
  30. data/test/{dummy → dummy-3.2.x}/config/initializers/session_store.rb +0 -0
  31. data/test/{dummy → dummy-3.2.x}/config/initializers/wrap_parameters.rb +0 -0
  32. data/test/{dummy → dummy-3.2.x}/config/locales/en.yml +0 -0
  33. data/test/{dummy → dummy-3.2.x}/config/routes.rb +0 -0
  34. data/test/{dummy → dummy-3.2.x}/db/.gitkeep +0 -0
  35. data/test/{dummy → dummy-3.2.x}/lib/assets/.gitkeep +0 -0
  36. data/test/{dummy → dummy-3.2.x}/log/.gitkeep +0 -0
  37. data/test/{dummy → dummy-3.2.x}/public/404.html +0 -0
  38. data/test/{dummy → dummy-3.2.x}/public/422.html +0 -0
  39. data/test/{dummy → dummy-3.2.x}/public/500.html +0 -0
  40. data/test/{dummy → dummy-3.2.x}/public/favicon.ico +0 -0
  41. data/test/{dummy → dummy-3.2.x}/script/rails +0 -0
  42. data/test/dummy-4.0.x/README.rdoc +28 -0
  43. data/test/dummy-4.0.x/Rakefile +6 -0
  44. data/test/dummy-4.0.x/app/assets/images/.keep +0 -0
  45. data/test/dummy-4.0.x/app/assets/javascripts/application.js +13 -0
  46. data/test/dummy-4.0.x/app/assets/stylesheets/application.css +13 -0
  47. data/test/dummy-4.0.x/app/controllers/application_controller.rb +5 -0
  48. data/test/dummy-4.0.x/app/controllers/concerns/.keep +0 -0
  49. data/test/dummy-4.0.x/app/helpers/application_helper.rb +2 -0
  50. data/test/dummy-4.0.x/app/mailers/.keep +0 -0
  51. data/test/dummy-4.0.x/app/models/.keep +0 -0
  52. data/test/dummy-4.0.x/app/models/concerns/.keep +0 -0
  53. data/test/dummy-4.0.x/app/views/layouts/application.html.erb +14 -0
  54. data/test/dummy-4.0.x/bin/bundle +3 -0
  55. data/test/dummy-4.0.x/bin/rails +4 -0
  56. data/test/dummy-4.0.x/bin/rake +4 -0
  57. data/test/dummy-4.0.x/config.ru +4 -0
  58. data/test/dummy-4.0.x/config/application.rb +23 -0
  59. data/test/dummy-4.0.x/config/boot.rb +5 -0
  60. data/test/dummy-4.0.x/config/database.yml +25 -0
  61. data/test/dummy-4.0.x/config/environment.rb +5 -0
  62. data/test/dummy-4.0.x/config/environments/development.rb +29 -0
  63. data/test/dummy-4.0.x/config/environments/production.rb +80 -0
  64. data/test/dummy-4.0.x/config/environments/test.rb +36 -0
  65. data/test/dummy-4.0.x/config/initializers/backtrace_silencers.rb +7 -0
  66. data/test/dummy-4.0.x/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/test/dummy-4.0.x/config/initializers/inflections.rb +16 -0
  68. data/test/dummy-4.0.x/config/initializers/mime_types.rb +5 -0
  69. data/test/dummy-4.0.x/config/initializers/secret_token.rb +12 -0
  70. data/test/dummy-4.0.x/config/initializers/session_store.rb +3 -0
  71. data/test/dummy-4.0.x/config/initializers/wrap_parameters.rb +14 -0
  72. data/test/dummy-4.0.x/config/locales/en.yml +23 -0
  73. data/test/dummy-4.0.x/config/routes.rb +56 -0
  74. data/test/dummy-4.0.x/lib/assets/.keep +0 -0
  75. data/test/dummy-4.0.x/log/.keep +0 -0
  76. data/test/dummy-4.0.x/public/404.html +58 -0
  77. data/test/dummy-4.0.x/public/422.html +58 -0
  78. data/test/dummy-4.0.x/public/500.html +57 -0
  79. data/test/dummy-4.0.x/public/favicon.ico +0 -0
  80. data/test/dummy-4.1.x/README.rdoc +28 -0
  81. data/test/dummy-4.1.x/Rakefile +6 -0
  82. data/test/dummy-4.1.x/app/assets/images/.keep +0 -0
  83. data/test/dummy-4.1.x/app/assets/javascripts/application.js +13 -0
  84. data/test/dummy-4.1.x/app/assets/stylesheets/application.css +15 -0
  85. data/test/dummy-4.1.x/app/controllers/application_controller.rb +5 -0
  86. data/test/dummy-4.1.x/app/controllers/concerns/.keep +0 -0
  87. data/test/dummy-4.1.x/app/helpers/application_helper.rb +2 -0
  88. data/test/dummy-4.1.x/app/mailers/.keep +0 -0
  89. data/test/dummy-4.1.x/app/models/.keep +0 -0
  90. data/test/dummy-4.1.x/app/models/concerns/.keep +0 -0
  91. data/test/dummy-4.1.x/app/views/layouts/application.html.erb +14 -0
  92. data/test/dummy-4.1.x/bin/bundle +3 -0
  93. data/test/dummy-4.1.x/bin/rails +4 -0
  94. data/test/dummy-4.1.x/bin/rake +4 -0
  95. data/test/dummy-4.1.x/config.ru +4 -0
  96. data/test/dummy-4.1.x/config/application.rb +23 -0
  97. data/test/dummy-4.1.x/config/boot.rb +5 -0
  98. data/test/dummy-4.1.x/config/database.yml +25 -0
  99. data/test/dummy-4.1.x/config/environment.rb +5 -0
  100. data/test/dummy-4.1.x/config/environments/development.rb +37 -0
  101. data/test/dummy-4.1.x/config/environments/production.rb +82 -0
  102. data/test/dummy-4.1.x/config/environments/test.rb +39 -0
  103. data/test/dummy-4.1.x/config/initializers/assets.rb +8 -0
  104. data/test/dummy-4.1.x/config/initializers/backtrace_silencers.rb +7 -0
  105. data/test/dummy-4.1.x/config/initializers/cookies_serializer.rb +3 -0
  106. data/test/dummy-4.1.x/config/initializers/filter_parameter_logging.rb +4 -0
  107. data/test/dummy-4.1.x/config/initializers/inflections.rb +16 -0
  108. data/test/dummy-4.1.x/config/initializers/mime_types.rb +4 -0
  109. data/test/dummy-4.1.x/config/initializers/session_store.rb +3 -0
  110. data/test/dummy-4.1.x/config/initializers/wrap_parameters.rb +14 -0
  111. data/test/dummy-4.1.x/config/locales/en.yml +23 -0
  112. data/test/dummy-4.1.x/config/routes.rb +56 -0
  113. data/test/dummy-4.1.x/config/secrets.yml +22 -0
  114. data/test/dummy-4.1.x/lib/assets/.keep +0 -0
  115. data/test/dummy-4.1.x/log/.keep +0 -0
  116. data/test/dummy-4.1.x/public/404.html +67 -0
  117. data/test/dummy-4.1.x/public/422.html +67 -0
  118. data/test/dummy-4.1.x/public/500.html +66 -0
  119. data/test/dummy-4.1.x/public/favicon.ico +0 -0
  120. data/test/integration/codemirror_rails_integration_test.rb +6 -4
  121. data/test/test_helper.rb +10 -7
  122. data/vendor/assets/javascripts/codemirror.js +131 -94
  123. data/vendor/assets/javascripts/codemirror/addons/dialog/dialog.js +8 -4
  124. data/vendor/assets/javascripts/codemirror/addons/display/rulers.js +1 -1
  125. data/vendor/assets/javascripts/codemirror/addons/edit/continuelist.js +1 -1
  126. data/vendor/assets/javascripts/codemirror/addons/fold/foldgutter.js +1 -1
  127. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +7 -0
  128. data/vendor/assets/javascripts/codemirror/addons/selection/active-line.js +3 -1
  129. data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +3 -2
  130. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +301 -88
  131. data/vendor/assets/javascripts/codemirror/modes/clike.js +6 -6
  132. data/vendor/assets/javascripts/codemirror/modes/css.js +3 -3
  133. data/vendor/assets/javascripts/codemirror/modes/cypher.js +1 -1
  134. data/vendor/assets/javascripts/codemirror/modes/javascript.js +29 -9
  135. data/vendor/assets/javascripts/codemirror/modes/python.js +3 -1
  136. data/vendor/assets/stylesheets/codemirror.css +29 -0
  137. data/vendor/assets/stylesheets/codemirror/addons/fold/foldgutter.css +0 -1
  138. data/vendor/assets/stylesheets/codemirror/themes/3024-day.css +4 -0
  139. data/vendor/assets/stylesheets/codemirror/themes/3024-night.css +3 -0
  140. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +4 -1
  141. data/vendor/assets/stylesheets/codemirror/themes/base16-dark.css +2 -0
  142. data/vendor/assets/stylesheets/codemirror/themes/base16-light.css +2 -0
  143. data/vendor/assets/stylesheets/codemirror/themes/blackboard.css +2 -0
  144. data/vendor/assets/stylesheets/codemirror/themes/cobalt.css +2 -0
  145. data/vendor/assets/stylesheets/codemirror/themes/erlang-dark.css +2 -0
  146. data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +2 -0
  147. data/vendor/assets/stylesheets/codemirror/themes/mbo.css +4 -2
  148. data/vendor/assets/stylesheets/codemirror/themes/midnight.css +2 -0
  149. data/vendor/assets/stylesheets/codemirror/themes/monokai.css +2 -0
  150. data/vendor/assets/stylesheets/codemirror/themes/neo.css +3 -0
  151. data/vendor/assets/stylesheets/codemirror/themes/night.css +2 -0
  152. data/vendor/assets/stylesheets/codemirror/themes/paraiso-dark.css +2 -0
  153. data/vendor/assets/stylesheets/codemirror/themes/paraiso-light.css +2 -0
  154. data/vendor/assets/stylesheets/codemirror/themes/pastel-on-dark.css +2 -0
  155. data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +2 -0
  156. data/vendor/assets/stylesheets/codemirror/themes/solarized.css +3 -0
  157. data/vendor/assets/stylesheets/codemirror/themes/the-matrix.css +2 -0
  158. data/vendor/assets/stylesheets/codemirror/themes/tomorrow-night-eighties.css +2 -0
  159. data/vendor/assets/stylesheets/codemirror/themes/twilight.css +2 -0
  160. data/vendor/assets/stylesheets/codemirror/themes/vibrant-ink.css +2 -0
  161. data/vendor/assets/stylesheets/codemirror/themes/xq-dark.css +2 -0
  162. metadata +135 -73
@@ -38,10 +38,14 @@
38
38
  closeNotification(this, null);
39
39
  var dialog = dialogDiv(this, template, options && options.bottom);
40
40
  var closed = false, me = this;
41
- function close() {
42
- if (closed) return;
43
- closed = true;
44
- dialog.parentNode.removeChild(dialog);
41
+ function close(newVal) {
42
+ if (typeof newVal == 'string') {
43
+ inp.value = newVal;
44
+ } else {
45
+ if (closed) return;
46
+ closed = true;
47
+ dialog.parentNode.removeChild(dialog);
48
+ }
45
49
  }
46
50
  var inp = dialog.getElementsByTagName("input")[0], button;
47
51
  if (inp) {
@@ -43,7 +43,7 @@
43
43
  col = conf;
44
44
  } else {
45
45
  col = conf.column;
46
- if (conf.className) elt.className = " " + conf.className;
46
+ if (conf.className) elt.className += " " + conf.className;
47
47
  if (conf.color) elt.style.borderColor = conf.color;
48
48
  if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
49
49
  if (conf.width) elt.style.borderLeftWidth = conf.width;
@@ -11,7 +11,7 @@
11
11
  })(function(CodeMirror) {
12
12
  "use strict";
13
13
 
14
- var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/,
14
+ var listRE = /^(\s*)([*+-]|(\d+)\.)(\s+)/,
15
15
  unorderedBullets = "*+-";
16
16
 
17
17
  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
@@ -58,7 +58,7 @@
58
58
  function marker(spec) {
59
59
  if (typeof spec == "string") {
60
60
  var elt = document.createElement("div");
61
- elt.className = spec;
61
+ elt.className = spec + " CodeMirror-guttermarker-subtle";
62
62
  return elt;
63
63
  } else {
64
64
  return spec.cloneNode(true);
@@ -68,9 +68,16 @@
68
68
  if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
69
69
  if (token.type == "string") {
70
70
  prefix = token.string;
71
+ var n = 0;
71
72
  if (/['"]/.test(token.string.charAt(0))) {
72
73
  quote = token.string.charAt(0);
73
74
  prefix = token.string.slice(1);
75
+ n++;
76
+ }
77
+ var len = token.string.length;
78
+ if (/['"]/.test(token.string.charAt(len - 1))) {
79
+ quote = token.string.charAt(len - 1);
80
+ prefix = token.string.substr(n, len - 2);
74
81
  }
75
82
  replaceToken = true;
76
83
  }
@@ -49,7 +49,9 @@
49
49
  function updateActiveLines(cm, ranges) {
50
50
  var active = [];
51
51
  for (var i = 0; i < ranges.length; i++) {
52
- var line = cm.getLineHandleVisualStart(ranges[i].head.line);
52
+ var range = ranges[i];
53
+ if (!range.empty()) continue;
54
+ var line = cm.getLineHandleVisualStart(range.head.line);
53
55
  if (active[active.length - 1] != line) active.push(line);
54
56
  }
55
57
  if (sameArray(cm.state.activeLines, active)) return;
@@ -106,7 +106,7 @@
106
106
  cm.showHint({hint: this.getHint});
107
107
  },
108
108
 
109
- showType: function(cm, pos) { showType(this, cm, pos); },
109
+ showType: function(cm, pos, c) { showType(this, cm, pos, c); },
110
110
 
111
111
  updateArgHints: function(cm) { updateArgHints(this, cm); },
112
112
 
@@ -239,7 +239,7 @@
239
239
 
240
240
  // Type queries
241
241
 
242
- function showType(ts, cm, pos) {
242
+ function showType(ts, cm, pos, c) {
243
243
  ts.request(cm, "type", function(error, data) {
244
244
  if (error) return showError(ts, cm, error);
245
245
  if (ts.options.typeTip) {
@@ -254,6 +254,7 @@
254
254
  }
255
255
  }
256
256
  tempTooltip(cm, tip);
257
+ if (c) c();
257
258
  }, pos);
258
259
  }
259
260
 
@@ -356,32 +356,19 @@
356
356
  if (val) {
357
357
  cm.setOption('keyMap', 'vim');
358
358
  cm.setOption('disableInput', true);
359
+ cm.setOption('showCursorWhenSelecting', false);
359
360
  CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
360
- cm.on('beforeSelectionChange', beforeSelectionChange);
361
361
  cm.on('cursorActivity', onCursorActivity);
362
362
  maybeInitVimState(cm);
363
363
  CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
364
364
  } else if (cm.state.vim) {
365
365
  cm.setOption('keyMap', 'default');
366
366
  cm.setOption('disableInput', false);
367
- cm.off('beforeSelectionChange', beforeSelectionChange);
368
367
  cm.off('cursorActivity', onCursorActivity);
369
368
  CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
370
369
  cm.state.vim = null;
371
370
  }
372
371
  });
373
- function beforeSelectionChange(cm, obj) {
374
- var vim = cm.state.vim;
375
- if (vim.insertMode || vim.exMode) return;
376
-
377
- var head = obj.ranges[0].head;
378
- var anchor = obj.ranges[0].anchor;
379
- if (head.ch && head.ch == cm.doc.getLine(head.line).length) {
380
- var pos = Pos(head.line, head.ch - 1);
381
- obj.update([{anchor: cursorEqual(head, anchor) ? pos : anchor,
382
- head: pos}]);
383
- }
384
- }
385
372
  function getOnPasteFn(cm) {
386
373
  var vim = cm.state.vim;
387
374
  if (!vim.onPasteFn) {
@@ -411,7 +398,7 @@
411
398
  var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
412
399
  'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
413
400
  var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
414
- var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':']);
401
+ var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
415
402
 
416
403
  function isLine(cm, line) {
417
404
  return line >= cm.firstLine() && line <= cm.lastLine();
@@ -611,6 +598,8 @@
611
598
  // executed in between.
612
599
  lastMotion: null,
613
600
  marks: {},
601
+ // Mark for rendering fake cursor for visual mode.
602
+ fakeCursor: null,
614
603
  insertMode: false,
615
604
  // Repeat count for changes made in insert mode, triggered by key
616
605
  // sequences like 3,i. Only exists when insertMode is true.
@@ -618,7 +607,8 @@
618
607
  visualMode: false,
619
608
  // If we are in visual line mode. No effect if visualMode is false.
620
609
  visualLine: false,
621
- lastSelection: null
610
+ lastSelection: null,
611
+ lastPastedText: null
622
612
  };
623
613
  }
624
614
  return cm.state.vim;
@@ -636,7 +626,11 @@
636
626
  macroModeState: new MacroModeState,
637
627
  // Recording latest f, t, F or T motion command.
638
628
  lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
639
- registerController: new RegisterController({})
629
+ registerController: new RegisterController({}),
630
+ // search history buffer
631
+ searchHistoryController: new HistoryController({}),
632
+ // ex Command history buffer
633
+ exCommandHistoryController : new HistoryController({})
640
634
  };
641
635
  for (var optionName in options) {
642
636
  var option = options[optionName];
@@ -835,6 +829,7 @@
835
829
  this.unnamedRegister = registers['"'] = new Register();
836
830
  registers['.'] = new Register();
837
831
  registers[':'] = new Register();
832
+ registers['/'] = new Register();
838
833
  }
839
834
  RegisterController.prototype = {
840
835
  pushText: function(registerName, operator, text, linewise) {
@@ -906,7 +901,45 @@
906
901
  }
907
902
  }
908
903
  };
909
-
904
+ function HistoryController() {
905
+ this.historyBuffer = [];
906
+ this.iterator;
907
+ this.initialPrefix = null;
908
+ }
909
+ HistoryController.prototype = {
910
+ // the input argument here acts a user entered prefix for a small time
911
+ // until we start autocompletion in which case it is the autocompleted.
912
+ nextMatch: function (input, up) {
913
+ var historyBuffer = this.historyBuffer;
914
+ var dir = up ? -1 : 1;
915
+ if (this.initialPrefix === null) this.initialPrefix = input;
916
+ for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {
917
+ var element = historyBuffer[i];
918
+ for (var j = 0; j <= element.length; j++) {
919
+ if (this.initialPrefix == element.substring(0, j)) {
920
+ this.iterator = i;
921
+ return element;
922
+ }
923
+ }
924
+ }
925
+ // should return the user input in case we reach the end of buffer.
926
+ if (i >= historyBuffer.length) {
927
+ this.iterator = historyBuffer.length;
928
+ return this.initialPrefix;
929
+ }
930
+ // return the last autocompleted query or exCommand as it is.
931
+ if (i < 0 ) return input;
932
+ },
933
+ pushInput: function(input) {
934
+ var index = this.historyBuffer.indexOf(input);
935
+ if (index > -1) this.historyBuffer.splice(index, 1);
936
+ if (input.length) this.historyBuffer.push(input);
937
+ },
938
+ reset: function() {
939
+ this.initialPrefix = null;
940
+ this.iterator = this.historyBuffer.length;
941
+ }
942
+ };
910
943
  var commandDispatcher = {
911
944
  matchCommand: function(key, keyMap, vim) {
912
945
  var inputState = vim.inputState;
@@ -1093,6 +1126,8 @@
1093
1126
  var originalQuery = getSearchState(cm).getQuery();
1094
1127
  var originalScrollPos = cm.getScrollInfo();
1095
1128
  function handleQuery(query, ignoreCase, smartCase) {
1129
+ vimGlobalState.searchHistoryController.pushInput(query);
1130
+ vimGlobalState.searchHistoryController.reset();
1096
1131
  try {
1097
1132
  updateSearchQuery(cm, query, ignoreCase, smartCase);
1098
1133
  } catch (e) {
@@ -1113,7 +1148,16 @@
1113
1148
  logSearchQuery(macroModeState, query);
1114
1149
  }
1115
1150
  }
1116
- function onPromptKeyUp(_e, query) {
1151
+ function onPromptKeyUp(e, query, close) {
1152
+ var keyName = CodeMirror.keyName(e), up;
1153
+ if (keyName == 'Up' || keyName == 'Down') {
1154
+ up = keyName == 'Up' ? true : false;
1155
+ query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
1156
+ close(query);
1157
+ } else {
1158
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
1159
+ vimGlobalState.searchHistoryController.reset();
1160
+ }
1117
1161
  var parsedQuery;
1118
1162
  try {
1119
1163
  parsedQuery = updateSearchQuery(cm, query,
@@ -1128,13 +1172,14 @@
1128
1172
  cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
1129
1173
  }
1130
1174
  }
1131
- function onPromptKeyDown(e, _query, close) {
1175
+ function onPromptKeyDown(e, query, close) {
1132
1176
  var keyName = CodeMirror.keyName(e);
1133
1177
  if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
1178
+ vimGlobalState.searchHistoryController.pushInput(query);
1179
+ vimGlobalState.searchHistoryController.reset();
1134
1180
  updateSearchQuery(cm, originalQuery);
1135
1181
  clearSearchHighlight(cm);
1136
1182
  cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
1137
-
1138
1183
  CodeMirror.e_stop(e);
1139
1184
  close();
1140
1185
  cm.focus();
@@ -1192,15 +1237,27 @@
1192
1237
  function onPromptClose(input) {
1193
1238
  // Give the prompt some time to close so that if processCommand shows
1194
1239
  // an error, the elements don't overlap.
1240
+ vimGlobalState.exCommandHistoryController.pushInput(input);
1241
+ vimGlobalState.exCommandHistoryController.reset();
1195
1242
  exCommandDispatcher.processCommand(cm, input);
1196
1243
  }
1197
- function onPromptKeyDown(e, _input, close) {
1198
- var keyName = CodeMirror.keyName(e);
1244
+ function onPromptKeyDown(e, input, close) {
1245
+ var keyName = CodeMirror.keyName(e), up;
1199
1246
  if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
1247
+ vimGlobalState.exCommandHistoryController.pushInput(input);
1248
+ vimGlobalState.exCommandHistoryController.reset();
1200
1249
  CodeMirror.e_stop(e);
1201
1250
  close();
1202
1251
  cm.focus();
1203
1252
  }
1253
+ if (keyName == 'Up' || keyName == 'Down') {
1254
+ up = keyName == 'Up' ? true : false;
1255
+ input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
1256
+ close(input);
1257
+ } else {
1258
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
1259
+ vimGlobalState.exCommandHistoryController.reset();
1260
+ }
1204
1261
  }
1205
1262
  if (command.type == 'keyToEx') {
1206
1263
  // Handle user defined Ex to Ex mappings
@@ -1292,11 +1349,16 @@
1292
1349
  // The end of the selection has moved from after the start to
1293
1350
  // before the start. We will shift the start right by 1.
1294
1351
  selectionStart.ch += 1;
1352
+ curEnd.ch -= 1;
1295
1353
  } else if (cursorIsBefore(selectionEnd, selectionStart) &&
1296
1354
  (cursorEqual(selectionStart, curEnd) ||
1297
1355
  cursorIsBefore(selectionStart, curEnd))) {
1298
1356
  // The opposite happened. We will shift the start left by 1.
1299
1357
  selectionStart.ch -= 1;
1358
+ curEnd.ch += 1;
1359
+ }
1360
+ if (vim.lastHPos != Infinity) {
1361
+ vim.lastHPos = curEnd.ch;
1300
1362
  }
1301
1363
  selectionEnd = curEnd;
1302
1364
  selectionStart = (motionResult instanceof Array) ? curStart : selectionStart;
@@ -1345,7 +1407,7 @@
1345
1407
  } else if (!curEnd) {
1346
1408
  curEnd = copyCursor(curStart);
1347
1409
  }
1348
- if (motionArgs.inclusive && !(vim.visualMode && inverted)) {
1410
+ if (motionArgs.inclusive && !vim.visualMode) {
1349
1411
  // Move the selection end one to the right to include the last
1350
1412
  // character.
1351
1413
  curEnd.ch++;
@@ -1926,7 +1988,9 @@
1926
1988
  } else if (insertAt == 'endOfSelectedArea') {
1927
1989
  var selectionEnd = cm.getCursor('head');
1928
1990
  var selectionStart = cm.getCursor('anchor');
1929
- selectionEnd = cursorIsBefore(selectionStart, selectionEnd) ? Pos(selectionEnd.line, selectionEnd.ch+1) : (selectionEnd.line < selectionStart.line ? Pos(selectionStart.line, 0) : selectionEnd);
1991
+ if (selectionEnd.line < selectionStart.line) {
1992
+ selectionEnd = Pos(selectionStart.line, 0);
1993
+ }
1930
1994
  cm.setCursor(selectionEnd);
1931
1995
  exitVisualMode(cm);
1932
1996
  }
@@ -1968,16 +2032,7 @@
1968
2032
  cm, Pos(curStart.line, curStart.ch + repeat),
1969
2033
  true /** includeLineBreak */);
1970
2034
  }
1971
- // Make the initial selection.
1972
- if (!actionArgs.repeatIsExplicit && !vim.visualLine) {
1973
- // This is a strange case. Here the implicit repeat is 1. The
1974
- // following commands lets the cursor hover over the 1 character
1975
- // selection.
1976
- cm.setCursor(curEnd);
1977
- cm.setSelection(curEnd, curStart);
1978
- } else {
1979
- cm.setSelection(curStart, curEnd);
1980
- }
2035
+ cm.setSelection(curStart, curEnd);
1981
2036
  CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
1982
2037
  } else {
1983
2038
  curStart = cm.getCursor('anchor');
@@ -2007,9 +2062,20 @@
2007
2062
  : curStart);
2008
2063
  },
2009
2064
  reselectLastSelection: function(cm, _actionArgs, vim) {
2010
- if (vim.lastSelection) {
2011
- var lastSelection = vim.lastSelection;
2012
- cm.setSelection(lastSelection.curStart, lastSelection.curEnd);
2065
+ var lastSelection = vim.lastSelection;
2066
+ if (lastSelection) {
2067
+ var curStart = lastSelection.curStartMark.find();
2068
+ var curEnd = lastSelection.curEndMark.find();
2069
+ cm.setSelection(curStart, curEnd);
2070
+ if (vim.visualMode) {
2071
+ updateLastSelection(cm, vim);
2072
+ var selectionStart = cm.getCursor('anchor');
2073
+ var selectionEnd = cm.getCursor('head');
2074
+ updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
2075
+ : selectionEnd);
2076
+ updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
2077
+ : selectionStart);
2078
+ }
2013
2079
  if (lastSelection.visualLine) {
2014
2080
  vim.visualMode = true;
2015
2081
  vim.visualLine = true;
@@ -2123,12 +2189,19 @@
2123
2189
  var curPosFinal;
2124
2190
  var idx;
2125
2191
  if (vim.visualMode) {
2192
+ // save the pasted text for reselection if the need arises
2193
+ vim.lastPastedText = text;
2194
+ var lastSelectionCurEnd;
2126
2195
  var selectedArea = getSelectedAreaRange(cm, vim);
2127
2196
  var selectionStart = selectedArea[0];
2128
2197
  var selectionEnd = selectedArea[1];
2198
+ // save the curEnd marker before it get cleared due to cm.replaceRange.
2199
+ if (vim.lastSelection) lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
2129
2200
  // push the previously selected text to unnamed register
2130
2201
  vimGlobalState.registerController.unnamedRegister.setText(cm.getRange(selectionStart, selectionEnd));
2131
2202
  cm.replaceRange(text, selectionStart, selectionEnd);
2203
+ // restore the the curEnd marker
2204
+ if(lastSelectionCurEnd) vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
2132
2205
  curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
2133
2206
  if(linewise)curPosFinal.ch=0;
2134
2207
  } else {
@@ -2176,9 +2249,6 @@
2176
2249
  if (vim.visualMode){
2177
2250
  curStart=cm.getCursor('start');
2178
2251
  curEnd=cm.getCursor('end');
2179
- // workaround to catch the character under the cursor
2180
- // existing workaround doesn't cover actions
2181
- curEnd=cm.clipPos(Pos(curEnd.line, curEnd.ch+1));
2182
2252
  }else{
2183
2253
  var line = cm.getLine(curStart.line);
2184
2254
  replaceTo = curStart.ch + actionArgs.repeat;
@@ -2247,9 +2317,13 @@
2247
2317
  var selectedAreaRange = getSelectedAreaRange(cm, vim);
2248
2318
  var selectionStart = selectedAreaRange[0];
2249
2319
  var selectionEnd = selectedAreaRange[1];
2320
+ // save the curEnd marker to avoid its removal due to cm.replaceRange
2321
+ var lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
2250
2322
  var toLower = actionArgs.toLower;
2251
2323
  var text = cm.getRange(selectionStart, selectionEnd);
2252
2324
  cm.replaceRange(toLower ? text.toLowerCase() : text.toUpperCase(), selectionStart, selectionEnd);
2325
+ // restore the last selection curEnd marker
2326
+ vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
2253
2327
  cm.setCursor(selectionStart);
2254
2328
  }
2255
2329
  };
@@ -2338,8 +2412,10 @@
2338
2412
  var selectionEnd = cm.getCursor('head');
2339
2413
  var lastSelection = vim.lastSelection;
2340
2414
  if (!vim.visualMode) {
2341
- var line = lastSelection.curEnd.line - lastSelection.curStart.line;
2342
- var ch = line ? lastSelection.curEnd.ch : lastSelection.curEnd.ch - lastSelection.curStart.ch;
2415
+ var lastSelectionCurStart = vim.lastSelection.curStartMark.find();
2416
+ var lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
2417
+ var line = lastSelectionCurEnd.line - lastSelectionCurStart.line;
2418
+ var ch = line ? lastSelectionCurEnd.ch : lastSelectionCurEnd.ch - lastSelectionCurStart.ch;
2343
2419
  selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
2344
2420
  if (lastSelection.visualLine) {
2345
2421
  return [{line: selectionStart.line, ch: 0}, {line: selectionEnd.line, ch: lineLength(cm, selectionEnd.line)}];
@@ -2349,25 +2425,37 @@
2349
2425
  var tmp = selectionStart;
2350
2426
  selectionStart = selectionEnd;
2351
2427
  selectionEnd = tmp;
2352
- } else {
2353
- selectionEnd = cm.clipPos(Pos(selectionEnd.line, selectionEnd.ch+1));
2354
2428
  }
2355
2429
  exitVisualMode(cm);
2356
2430
  }
2357
2431
  return [selectionStart, selectionEnd];
2358
2432
  }
2433
+ function updateLastSelection(cm, vim) {
2434
+ // We need the vim mark '<' to get the selection in case of yank and put
2435
+ var selectionStart = vim.marks['<'].find() || cm.getCursor('anchor');
2436
+ var selectionEnd = vim.marks['>'].find() ||cm.getCursor('head');
2437
+ // To accommodate the effect lastPastedText in the last selection
2438
+ if (vim.lastPastedText) {
2439
+ selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length-1);
2440
+ vim.lastPastedText = null;
2441
+ }
2442
+ // can't use selection state here because yank has already reset its cursor
2443
+ // Also, Bookmarks make the visual selections robust to edit operations
2444
+ vim.lastSelection = {'curStartMark': cm.setBookmark(selectionStart), 'curEndMark': cm.setBookmark(selectionEnd), 'visualMode': vim.visualMode, 'visualLine': vim.visualLine};
2445
+ if (cursorIsBefore(selectionEnd, selectionStart)) {
2446
+ vim.lastSelection.curStartMark = cm.setBookmark(selectionEnd);
2447
+ vim.lastSelection.curEndMark = cm.setBookmark(selectionStart);
2448
+ }
2449
+ }
2359
2450
 
2360
2451
  function exitVisualMode(cm) {
2361
2452
  cm.off('mousedown', exitVisualMode);
2362
2453
  var vim = cm.state.vim;
2363
- // can't use selection state here because yank has already reset its cursor
2364
- vim.lastSelection = {'curStart': vim.marks['<'].find(),
2365
- 'curEnd': vim.marks['>'].find(), 'visualMode': vim.visualMode,
2366
- 'visualLine': vim.visualLine};
2367
- vim.visualMode = false;
2368
- vim.visualLine = false;
2369
2454
  var selectionStart = cm.getCursor('anchor');
2370
2455
  var selectionEnd = cm.getCursor('head');
2456
+ updateLastSelection(cm, vim);
2457
+ vim.visualMode = false;
2458
+ vim.visualLine = false;
2371
2459
  if (!cursorEqual(selectionStart, selectionEnd)) {
2372
2460
  // Clear the selection and set the cursor only if the selection has not
2373
2461
  // already been cleared. Otherwise we risk moving the cursor somewhere
@@ -2375,6 +2463,9 @@
2375
2463
  cm.setCursor(clipCursorToContent(cm, selectionEnd));
2376
2464
  }
2377
2465
  CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
2466
+ if (vim.fakeCursor) {
2467
+ vim.fakeCursor.clear();
2468
+ }
2378
2469
  }
2379
2470
 
2380
2471
  // Remove any trailing newlines from the selection. For
@@ -2949,6 +3040,18 @@
2949
3040
  onClose(prompt(shortText, ''));
2950
3041
  }
2951
3042
  }
3043
+ function splitBySlash(argString) {
3044
+ var slashes = findUnescapedSlashes(argString) || [];
3045
+ if (!slashes.length) return [];
3046
+ var tokens = [];
3047
+ // in case of strings like foo/bar
3048
+ if (slashes[0] !== 0) return;
3049
+ for (var i = 0; i < slashes.length; i++) {
3050
+ if (typeof slashes[i] == 'number')
3051
+ tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
3052
+ }
3053
+ return tokens;
3054
+ }
2952
3055
 
2953
3056
  function findUnescapedSlashes(str) {
2954
3057
  var escapeNextChar = false;
@@ -3072,6 +3175,9 @@
3072
3175
  * through to the Regex object.
3073
3176
  */
3074
3177
  function parseQuery(query, ignoreCase, smartCase) {
3178
+ // First update the last search register
3179
+ var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
3180
+ lastSearchRegister.setText(query);
3075
3181
  // Check if the query is already a regex.
3076
3182
  if (query instanceof RegExp) { return query; }
3077
3183
  // First try to extract regex + flags from the input. If no flags found,
@@ -3282,16 +3388,17 @@
3282
3388
  { name: 'redo', shortName: 'red' },
3283
3389
  { name: 'set', shortName: 'set' },
3284
3390
  { name: 'sort', shortName: 'sor' },
3285
- { name: 'substitute', shortName: 's' },
3391
+ { name: 'substitute', shortName: 's', possiblyAsync: true },
3286
3392
  { name: 'nohlsearch', shortName: 'noh' },
3287
3393
  { name: 'delmarks', shortName: 'delm' },
3288
- { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true }
3394
+ { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
3395
+ { name: 'global', shortName: 'g' }
3289
3396
  ];
3290
3397
  Vim.ExCommandDispatcher = function() {
3291
3398
  this.buildCommandMap_();
3292
3399
  };
3293
3400
  Vim.ExCommandDispatcher.prototype = {
3294
- processCommand: function(cm, input) {
3401
+ processCommand: function(cm, input, opt_params) {
3295
3402
  var vim = cm.state.vim;
3296
3403
  var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
3297
3404
  var previousCommand = commandHistoryRegister.toString();
@@ -3301,7 +3408,7 @@
3301
3408
  var inputStream = new CodeMirror.StringStream(input);
3302
3409
  // update ": with the latest command whether valid or invalid
3303
3410
  commandHistoryRegister.setText(input);
3304
- var params = {};
3411
+ var params = opt_params || {};
3305
3412
  params.input = input;
3306
3413
  try {
3307
3414
  this.parseInput_(cm, inputStream, params);
@@ -3309,6 +3416,7 @@
3309
3416
  showConfirm(cm, e);
3310
3417
  throw e;
3311
3418
  }
3419
+ var command;
3312
3420
  var commandName;
3313
3421
  if (!params.commandName) {
3314
3422
  // If only a line range is defined, move to the line.
@@ -3316,7 +3424,7 @@
3316
3424
  commandName = 'move';
3317
3425
  }
3318
3426
  } else {
3319
- var command = this.matchCommand_(params.commandName);
3427
+ command = this.matchCommand_(params.commandName);
3320
3428
  if (command) {
3321
3429
  commandName = command.name;
3322
3430
  if (command.excludeFromCommandHistory) {
@@ -3342,6 +3450,12 @@
3342
3450
  }
3343
3451
  try {
3344
3452
  exCommands[commandName](cm, params);
3453
+ // Possibly asynchronous commands (e.g. substitute, which might have a
3454
+ // user confirmation), are responsible for calling the callback when
3455
+ // done. All others have it taken care of for them here.
3456
+ if ((!command || !command.possiblyAsync) && params.callback) {
3457
+ params.callback();
3458
+ }
3345
3459
  } catch(e) {
3346
3460
  showConfirm(cm, e);
3347
3461
  throw e;
@@ -3693,26 +3807,78 @@
3693
3807
  }
3694
3808
  cm.replaceRange(text.join('\n'), curStart, curEnd);
3695
3809
  },
3810
+ global: function(cm, params) {
3811
+ // a global command is of the form
3812
+ // :[range]g/pattern/[cmd]
3813
+ // argString holds the string /pattern/[cmd]
3814
+ var argString = params.argString;
3815
+ if (!argString) {
3816
+ showConfirm(cm, 'Regular Expression missing from global');
3817
+ return;
3818
+ }
3819
+ // range is specified here
3820
+ var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
3821
+ var lineEnd = params.lineEnd || params.line || cm.lastLine();
3822
+ // get the tokens from argString
3823
+ var tokens = splitBySlash(argString);
3824
+ var regexPart = argString, cmd;
3825
+ if (tokens.length) {
3826
+ regexPart = tokens[0];
3827
+ cmd = tokens.slice(1, tokens.length).join('/');
3828
+ }
3829
+ if (regexPart) {
3830
+ // If regex part is empty, then use the previous query. Otherwise
3831
+ // use the regex part as the new query.
3832
+ try {
3833
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
3834
+ true /** smartCase */);
3835
+ } catch (e) {
3836
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
3837
+ return;
3838
+ }
3839
+ }
3840
+ // now that we have the regexPart, search for regex matches in the
3841
+ // specified range of lines
3842
+ var query = getSearchState(cm).getQuery();
3843
+ var matchedLines = [], content = '';
3844
+ for (var i = lineStart; i <= lineEnd; i++) {
3845
+ var matched = query.test(cm.getLine(i));
3846
+ if (matched) {
3847
+ matchedLines.push(i+1);
3848
+ content+= cm.getLine(i) + '<br>';
3849
+ }
3850
+ }
3851
+ // if there is no [cmd], just display the list of matched lines
3852
+ if (!cmd) {
3853
+ showConfirm(cm, content);
3854
+ return;
3855
+ }
3856
+ var index = 0;
3857
+ var nextCommand = function() {
3858
+ if (index < matchedLines.length) {
3859
+ var command = matchedLines[index] + cmd;
3860
+ exCommandDispatcher.processCommand(cm, command, {
3861
+ callback: nextCommand
3862
+ });
3863
+ }
3864
+ index++;
3865
+ };
3866
+ nextCommand();
3867
+ },
3696
3868
  substitute: function(cm, params) {
3697
3869
  if (!cm.getSearchCursor) {
3698
3870
  throw new Error('Search feature not available. Requires searchcursor.js or ' +
3699
3871
  'any other getSearchCursor implementation.');
3700
3872
  }
3701
3873
  var argString = params.argString;
3702
- var slashes = argString ? findUnescapedSlashes(argString) : [];
3703
- var replacePart = '';
3704
- if (slashes.length) {
3705
- if (slashes[0] !== 0) {
3706
- showConfirm(cm, 'Substitutions should be of the form ' +
3707
- ':s/pattern/replace/');
3708
- return;
3709
- }
3710
- var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
3711
- var flagsPart;
3712
- var count;
3713
- var confirm = false; // Whether to confirm each replace.
3714
- if (slashes[1]) {
3715
- replacePart = argString.substring(slashes[1] + 1, slashes[2]);
3874
+ var tokens = argString ? splitBySlash(argString) : [];
3875
+ var regexPart, replacePart = '', trailing, flagsPart, count;
3876
+ var confirm = false; // Whether to confirm each replace.
3877
+ var global = false; // True to replace all instances on a line, false to replace only 1.
3878
+ if (tokens.length) {
3879
+ regexPart = tokens[0];
3880
+ replacePart = tokens[1];
3881
+ if (replacePart !== undefined) {
3716
3882
  if (getOption('pcre')) {
3717
3883
  replacePart = unescapeRegexReplace(replacePart);
3718
3884
  } else {
@@ -3720,18 +3886,31 @@
3720
3886
  }
3721
3887
  vimGlobalState.lastSubstituteReplacePart = replacePart;
3722
3888
  }
3723
- if (slashes[2]) {
3724
- // After the 3rd slash, we can have flags followed by a space followed
3725
- // by count.
3726
- var trailing = argString.substring(slashes[2] + 1).split(' ');
3727
- flagsPart = trailing[0];
3728
- count = parseInt(trailing[1]);
3889
+ trailing = tokens[2] ? tokens[2].split(' ') : [];
3890
+ } else {
3891
+ // either the argString is empty or its of the form ' hello/world'
3892
+ // actually splitBySlash returns a list of tokens
3893
+ // only if the string starts with a '/'
3894
+ if (argString && argString.length) {
3895
+ showConfirm(cm, 'Substitutions should be of the form ' +
3896
+ ':s/pattern/replace/');
3897
+ return;
3729
3898
  }
3899
+ }
3900
+ // After the 3rd slash, we can have flags followed by a space followed
3901
+ // by count.
3902
+ if (trailing) {
3903
+ flagsPart = trailing[0];
3904
+ count = parseInt(trailing[1]);
3730
3905
  if (flagsPart) {
3731
3906
  if (flagsPart.indexOf('c') != -1) {
3732
3907
  confirm = true;
3733
3908
  flagsPart.replace('c', '');
3734
3909
  }
3910
+ if (flagsPart.indexOf('g') != -1) {
3911
+ global = true;
3912
+ flagsPart.replace('g', '');
3913
+ }
3735
3914
  regexPart = regexPart + '/' + flagsPart;
3736
3915
  }
3737
3916
  }
@@ -3761,7 +3940,7 @@
3761
3940
  }
3762
3941
  var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
3763
3942
  var cursor = cm.getSearchCursor(query, startPos);
3764
- doReplace(cm, confirm, lineStart, lineEnd, cursor, query, replacePart);
3943
+ doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
3765
3944
  },
3766
3945
  redo: CodeMirror.commands.redo,
3767
3946
  undo: CodeMirror.commands.undo,
@@ -3850,9 +4029,10 @@
3850
4029
  * @param {RegExp} query Query for performing matches with.
3851
4030
  * @param {string} replaceWith Text to replace matches with. May contain $1,
3852
4031
  * $2, etc for replacing captured groups using Javascript replace.
4032
+ * @param {function()} callback A callback for when the replace is done.
3853
4033
  */
3854
- function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query,
3855
- replaceWith) {
4034
+ function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,
4035
+ replaceWith, callback) {
3856
4036
  // Set up all the functions.
3857
4037
  cm.state.vim.exMode = true;
3858
4038
  var done = false;
@@ -3872,17 +4052,21 @@
3872
4052
  searchCursor.replace(newText);
3873
4053
  }
3874
4054
  function next() {
3875
- var found = searchCursor.findNext();
3876
- if (!found) {
3877
- done = true;
3878
- } else if (isInRange(searchCursor.from(), lineStart, lineEnd)) {
4055
+ var found;
4056
+ // The below only loops to skip over multiple occurrences on the same
4057
+ // line when 'global' is not true.
4058
+ while(found = searchCursor.findNext() &&
4059
+ isInRange(searchCursor.from(), lineStart, lineEnd)) {
4060
+ if (!global && lastPos && searchCursor.from().line == lastPos.line) {
4061
+ continue;
4062
+ }
3879
4063
  cm.scrollIntoView(searchCursor.from(), 30);
3880
4064
  cm.setSelection(searchCursor.from(), searchCursor.to());
3881
4065
  lastPos = searchCursor.from();
3882
4066
  done = false;
3883
- } else {
3884
- done = true;
4067
+ return;
3885
4068
  }
4069
+ done = true;
3886
4070
  }
3887
4071
  function stop(close) {
3888
4072
  if (close) { close(); }
@@ -3893,6 +4077,7 @@
3893
4077
  vim.exMode = false;
3894
4078
  vim.lastHPos = vim.lastHSPos = lastPos.ch;
3895
4079
  }
4080
+ if (callback) { callback(); }
3896
4081
  }
3897
4082
  function onPromptKeyDown(e, _value, close) {
3898
4083
  // Swallow all keys.
@@ -3904,7 +4089,13 @@
3904
4089
  case 'N':
3905
4090
  next(); break;
3906
4091
  case 'A':
3907
- cm.operation(replaceAll); break;
4092
+ // replaceAll contains a call to close of its own. We don't want it
4093
+ // to fire too early or multiple times.
4094
+ var savedCallback = callback;
4095
+ callback = undefined;
4096
+ cm.operation(replaceAll);
4097
+ callback = savedCallback;
4098
+ break;
3908
4099
  case 'L':
3909
4100
  replace();
3910
4101
  // fall through and exit.
@@ -3926,6 +4117,7 @@
3926
4117
  }
3927
4118
  if (!confirm) {
3928
4119
  replaceAll();
4120
+ if (callback) { callback(); };
3929
4121
  return;
3930
4122
  }
3931
4123
  showPrompt(cm, {
@@ -4061,8 +4253,10 @@
4061
4253
  text = text.substring(match.index + key.length);
4062
4254
  CodeMirror.Vim.handleKey(cm, key);
4063
4255
  if (vim.insertMode) {
4064
- repeatInsertModeChanges(
4065
- cm, register.insertModeChanges[imc++].changes, 1);
4256
+ var changes = register.insertModeChanges[imc++].changes;
4257
+ vimGlobalState.macroModeState.lastInsertModeChanges.changes =
4258
+ changes;
4259
+ repeatInsertModeChanges(cm, changes, 1);
4066
4260
  exitInsertMode(cm);
4067
4261
  }
4068
4262
  }
@@ -4137,6 +4331,25 @@
4137
4331
  } else if (cm.doc.history.lastSelOrigin == '*mouse') {
4138
4332
  // Reset lastHPos if mouse click was done in normal mode.
4139
4333
  vim.lastHPos = cm.doc.getCursor().ch;
4334
+ if (cm.somethingSelected()) {
4335
+ // If something is still selected, enter visual mode.
4336
+ vim.visualMode = true;
4337
+ }
4338
+ }
4339
+ if (vim.visualMode) {
4340
+ var from, head;
4341
+ from = head = cm.getCursor('head');
4342
+ var anchor = cm.getCursor('anchor');
4343
+ var to = Pos(head.line, from.ch + (cursorIsBefore(anchor, head) ? -1 : 1));
4344
+ if (cursorIsBefore(to, from)) {
4345
+ var temp = from;
4346
+ from = to;
4347
+ to = temp;
4348
+ }
4349
+ if (vim.fakeCursor) {
4350
+ vim.fakeCursor.clear();
4351
+ }
4352
+ vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
4140
4353
  }
4141
4354
  }
4142
4355