codemirror-rails 4.2 → 4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +10 -0
- data/codemirror-rails.gemspec +3 -4
- data/gemfiles/Gemfile.rails-3.2 +5 -0
- data/gemfiles/Gemfile.rails-4.0 +4 -0
- data/gemfiles/Gemfile.rails-4.1 +4 -0
- data/lib/codemirror/rails/version.rb +2 -2
- data/test/{dummy → dummy-3.2.x}/README.rdoc +0 -0
- data/test/{dummy → dummy-3.2.x}/Rakefile +0 -0
- data/test/{dummy → dummy-3.2.x}/app/assets/javascripts/application.js +0 -0
- data/test/{dummy → dummy-3.2.x}/app/assets/stylesheets/application.css +0 -0
- data/test/{dummy → dummy-3.2.x}/app/controllers/application_controller.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/app/helpers/application_helper.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/app/mailers/.gitkeep +0 -0
- data/test/{dummy → dummy-3.2.x}/app/models/.gitkeep +0 -0
- data/test/{dummy → dummy-3.2.x}/app/views/layouts/application.html.erb +0 -0
- data/test/{dummy → dummy-3.2.x}/config.ru +0 -0
- data/test/{dummy → dummy-3.2.x}/config/application.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/boot.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/database.yml +0 -0
- data/test/{dummy → dummy-3.2.x}/config/environment.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/environments/development.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/environments/production.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/environments/test.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/backtrace_silencers.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/inflections.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/mime_types.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/secret_token.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/session_store.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/initializers/wrap_parameters.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/config/locales/en.yml +0 -0
- data/test/{dummy → dummy-3.2.x}/config/routes.rb +0 -0
- data/test/{dummy → dummy-3.2.x}/db/.gitkeep +0 -0
- data/test/{dummy → dummy-3.2.x}/lib/assets/.gitkeep +0 -0
- data/test/{dummy → dummy-3.2.x}/log/.gitkeep +0 -0
- data/test/{dummy → dummy-3.2.x}/public/404.html +0 -0
- data/test/{dummy → dummy-3.2.x}/public/422.html +0 -0
- data/test/{dummy → dummy-3.2.x}/public/500.html +0 -0
- data/test/{dummy → dummy-3.2.x}/public/favicon.ico +0 -0
- data/test/{dummy → dummy-3.2.x}/script/rails +0 -0
- data/test/dummy-4.0.x/README.rdoc +28 -0
- data/test/dummy-4.0.x/Rakefile +6 -0
- data/test/dummy-4.0.x/app/assets/images/.keep +0 -0
- data/test/dummy-4.0.x/app/assets/javascripts/application.js +13 -0
- data/test/dummy-4.0.x/app/assets/stylesheets/application.css +13 -0
- data/test/dummy-4.0.x/app/controllers/application_controller.rb +5 -0
- data/test/dummy-4.0.x/app/controllers/concerns/.keep +0 -0
- data/test/dummy-4.0.x/app/helpers/application_helper.rb +2 -0
- data/test/dummy-4.0.x/app/mailers/.keep +0 -0
- data/test/dummy-4.0.x/app/models/.keep +0 -0
- data/test/dummy-4.0.x/app/models/concerns/.keep +0 -0
- data/test/dummy-4.0.x/app/views/layouts/application.html.erb +14 -0
- data/test/dummy-4.0.x/bin/bundle +3 -0
- data/test/dummy-4.0.x/bin/rails +4 -0
- data/test/dummy-4.0.x/bin/rake +4 -0
- data/test/dummy-4.0.x/config.ru +4 -0
- data/test/dummy-4.0.x/config/application.rb +23 -0
- data/test/dummy-4.0.x/config/boot.rb +5 -0
- data/test/dummy-4.0.x/config/database.yml +25 -0
- data/test/dummy-4.0.x/config/environment.rb +5 -0
- data/test/dummy-4.0.x/config/environments/development.rb +29 -0
- data/test/dummy-4.0.x/config/environments/production.rb +80 -0
- data/test/dummy-4.0.x/config/environments/test.rb +36 -0
- data/test/dummy-4.0.x/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy-4.0.x/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy-4.0.x/config/initializers/inflections.rb +16 -0
- data/test/dummy-4.0.x/config/initializers/mime_types.rb +5 -0
- data/test/dummy-4.0.x/config/initializers/secret_token.rb +12 -0
- data/test/dummy-4.0.x/config/initializers/session_store.rb +3 -0
- data/test/dummy-4.0.x/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy-4.0.x/config/locales/en.yml +23 -0
- data/test/dummy-4.0.x/config/routes.rb +56 -0
- data/test/dummy-4.0.x/lib/assets/.keep +0 -0
- data/test/dummy-4.0.x/log/.keep +0 -0
- data/test/dummy-4.0.x/public/404.html +58 -0
- data/test/dummy-4.0.x/public/422.html +58 -0
- data/test/dummy-4.0.x/public/500.html +57 -0
- data/test/dummy-4.0.x/public/favicon.ico +0 -0
- data/test/dummy-4.1.x/README.rdoc +28 -0
- data/test/dummy-4.1.x/Rakefile +6 -0
- data/test/dummy-4.1.x/app/assets/images/.keep +0 -0
- data/test/dummy-4.1.x/app/assets/javascripts/application.js +13 -0
- data/test/dummy-4.1.x/app/assets/stylesheets/application.css +15 -0
- data/test/dummy-4.1.x/app/controllers/application_controller.rb +5 -0
- data/test/dummy-4.1.x/app/controllers/concerns/.keep +0 -0
- data/test/dummy-4.1.x/app/helpers/application_helper.rb +2 -0
- data/test/dummy-4.1.x/app/mailers/.keep +0 -0
- data/test/dummy-4.1.x/app/models/.keep +0 -0
- data/test/dummy-4.1.x/app/models/concerns/.keep +0 -0
- data/test/dummy-4.1.x/app/views/layouts/application.html.erb +14 -0
- data/test/dummy-4.1.x/bin/bundle +3 -0
- data/test/dummy-4.1.x/bin/rails +4 -0
- data/test/dummy-4.1.x/bin/rake +4 -0
- data/test/dummy-4.1.x/config.ru +4 -0
- data/test/dummy-4.1.x/config/application.rb +23 -0
- data/test/dummy-4.1.x/config/boot.rb +5 -0
- data/test/dummy-4.1.x/config/database.yml +25 -0
- data/test/dummy-4.1.x/config/environment.rb +5 -0
- data/test/dummy-4.1.x/config/environments/development.rb +37 -0
- data/test/dummy-4.1.x/config/environments/production.rb +82 -0
- data/test/dummy-4.1.x/config/environments/test.rb +39 -0
- data/test/dummy-4.1.x/config/initializers/assets.rb +8 -0
- data/test/dummy-4.1.x/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy-4.1.x/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy-4.1.x/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy-4.1.x/config/initializers/inflections.rb +16 -0
- data/test/dummy-4.1.x/config/initializers/mime_types.rb +4 -0
- data/test/dummy-4.1.x/config/initializers/session_store.rb +3 -0
- data/test/dummy-4.1.x/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy-4.1.x/config/locales/en.yml +23 -0
- data/test/dummy-4.1.x/config/routes.rb +56 -0
- data/test/dummy-4.1.x/config/secrets.yml +22 -0
- data/test/dummy-4.1.x/lib/assets/.keep +0 -0
- data/test/dummy-4.1.x/log/.keep +0 -0
- data/test/dummy-4.1.x/public/404.html +67 -0
- data/test/dummy-4.1.x/public/422.html +67 -0
- data/test/dummy-4.1.x/public/500.html +66 -0
- data/test/dummy-4.1.x/public/favicon.ico +0 -0
- data/test/integration/codemirror_rails_integration_test.rb +6 -4
- data/test/test_helper.rb +10 -7
- data/vendor/assets/javascripts/codemirror.js +131 -94
- data/vendor/assets/javascripts/codemirror/addons/dialog/dialog.js +8 -4
- data/vendor/assets/javascripts/codemirror/addons/display/rulers.js +1 -1
- data/vendor/assets/javascripts/codemirror/addons/edit/continuelist.js +1 -1
- data/vendor/assets/javascripts/codemirror/addons/fold/foldgutter.js +1 -1
- data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +7 -0
- data/vendor/assets/javascripts/codemirror/addons/selection/active-line.js +3 -1
- data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +3 -2
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +301 -88
- data/vendor/assets/javascripts/codemirror/modes/clike.js +6 -6
- data/vendor/assets/javascripts/codemirror/modes/css.js +3 -3
- data/vendor/assets/javascripts/codemirror/modes/cypher.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +29 -9
- data/vendor/assets/javascripts/codemirror/modes/python.js +3 -1
- data/vendor/assets/stylesheets/codemirror.css +29 -0
- data/vendor/assets/stylesheets/codemirror/addons/fold/foldgutter.css +0 -1
- data/vendor/assets/stylesheets/codemirror/themes/3024-day.css +4 -0
- data/vendor/assets/stylesheets/codemirror/themes/3024-night.css +3 -0
- data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +4 -1
- data/vendor/assets/stylesheets/codemirror/themes/base16-dark.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/base16-light.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/blackboard.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/cobalt.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/erlang-dark.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/mbo.css +4 -2
- data/vendor/assets/stylesheets/codemirror/themes/midnight.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/monokai.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/neo.css +3 -0
- data/vendor/assets/stylesheets/codemirror/themes/night.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/paraiso-dark.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/paraiso-light.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/pastel-on-dark.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/solarized.css +3 -0
- data/vendor/assets/stylesheets/codemirror/themes/the-matrix.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/tomorrow-night-eighties.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/twilight.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/vibrant-ink.css +2 -0
- data/vendor/assets/stylesheets/codemirror/themes/xq-dark.css +2 -0
- 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 (
|
43
|
-
|
44
|
-
|
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
|
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;
|
@@ -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
|
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(
|
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,
|
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,
|
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 && !
|
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
|
-
|
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
|
-
|
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
|
-
|
2011
|
-
|
2012
|
-
|
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
|
2342
|
-
var
|
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
|
-
|
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
|
3703
|
-
var replacePart = '';
|
3704
|
-
|
3705
|
-
|
3706
|
-
|
3707
|
-
|
3708
|
-
|
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
|
-
|
3724
|
-
|
3725
|
-
|
3726
|
-
|
3727
|
-
|
3728
|
-
|
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
|
3876
|
-
|
3877
|
-
|
3878
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4065
|
-
|
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
|
|