codemirror-rails 4.2 → 4.3

Sign up to get free protection for your applications and to get access to all the features.
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