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.
- 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
|
|