codemirror-rails 3.13 → 3.14
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/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +328 -250
- data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +7 -6
- data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +33 -7
- data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +14 -10
- data/vendor/assets/javascripts/codemirror/addons/edit/trailingspace.js +15 -0
- data/vendor/assets/javascripts/codemirror/addons/fold/brace-fold.js +70 -17
- data/vendor/assets/javascripts/codemirror/addons/fold/foldcode.js +56 -20
- data/vendor/assets/javascripts/codemirror/addons/fold/xml-fold.js +135 -39
- data/vendor/assets/javascripts/codemirror/addons/hint/html-hint.js +324 -571
- data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +199 -109
- data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +60 -113
- data/vendor/assets/javascripts/codemirror/addons/lint/coffeescript-lint.js +24 -0
- data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +431 -0
- data/vendor/assets/javascripts/codemirror/addons/mode/multiplex.js +7 -1
- data/vendor/assets/javascripts/codemirror/addons/search/match-highlighter.js +46 -20
- data/vendor/assets/javascripts/codemirror/addons/search/search.js +1 -1
- data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +370 -13
- data/vendor/assets/javascripts/codemirror/keymaps/extra.js +43 -0
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +535 -214
- data/vendor/assets/javascripts/codemirror/modes/clike.js +56 -0
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +19 -14
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +2 -2
- data/vendor/assets/javascripts/codemirror/modes/ruby.js +67 -16
- data/vendor/assets/javascripts/codemirror/modes/smarty.js +167 -110
- data/vendor/assets/javascripts/codemirror/modes/sql.js +97 -15
- data/vendor/assets/javascripts/codemirror/modes/xml.js +14 -18
- data/vendor/assets/stylesheets/codemirror.css +0 -1
- data/vendor/assets/stylesheets/codemirror/addons/merge/merge.css +82 -0
- metadata +7 -2
@@ -0,0 +1,43 @@
|
|
1
|
+
// A number of additional default bindings that are too obscure to
|
2
|
+
// include in the core codemirror.js file.
|
3
|
+
|
4
|
+
(function() {
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
var Pos = CodeMirror.Pos;
|
8
|
+
|
9
|
+
function moveLines(cm, start, end, dist) {
|
10
|
+
if (!dist || start > end) return 0;
|
11
|
+
|
12
|
+
var from = cm.clipPos(Pos(start, 0)), to = cm.clipPos(Pos(end));
|
13
|
+
var text = cm.getRange(from, to);
|
14
|
+
|
15
|
+
if (start <= cm.firstLine())
|
16
|
+
cm.replaceRange("", from, Pos(to.line + 1, 0));
|
17
|
+
else
|
18
|
+
cm.replaceRange("", Pos(from.line - 1), to);
|
19
|
+
var target = from.line + dist;
|
20
|
+
if (target <= cm.firstLine()) {
|
21
|
+
cm.replaceRange(text + "\n", Pos(target, 0));
|
22
|
+
return cm.firstLine() - from.line;
|
23
|
+
} else {
|
24
|
+
var targetPos = cm.clipPos(Pos(target - 1));
|
25
|
+
cm.replaceRange("\n" + text, targetPos);
|
26
|
+
return targetPos.line + 1 - from.line;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
function moveSelectedLines(cm, dist) {
|
31
|
+
var head = cm.getCursor("head"), anchor = cm.getCursor("anchor");
|
32
|
+
cm.operation(function() {
|
33
|
+
var moved = moveLines(cm, Math.min(head.line, anchor.line), Math.max(head.line, anchor.line), dist);
|
34
|
+
cm.setSelection(Pos(anchor.line + moved, anchor.ch), Pos(head.line + moved, head.ch));
|
35
|
+
});
|
36
|
+
}
|
37
|
+
|
38
|
+
CodeMirror.commands.moveLinesUp = function(cm) { moveSelectedLines(cm, -1); };
|
39
|
+
CodeMirror.commands.moveLinesDown = function(cm) { moveSelectedLines(cm, 1); };
|
40
|
+
|
41
|
+
CodeMirror.keyMap["default"]["Alt-Up"] = "moveLinesUp";
|
42
|
+
CodeMirror.keyMap["default"]["Alt-Down"] = "moveLinesDown";
|
43
|
+
})();
|
@@ -190,8 +190,8 @@
|
|
190
190
|
motionArgs: {toJumplist: true}},
|
191
191
|
{ keys: ['`', 'character'], type: 'motion', motion: 'goToMark',
|
192
192
|
motionArgs: {toJumplist: true}},
|
193
|
-
{ keys: [']', '`'
|
194
|
-
{ keys: ['[', '`'
|
193
|
+
{ keys: [']', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
|
194
|
+
{ keys: ['[', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
|
195
195
|
{ keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
|
196
196
|
{ keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
|
197
197
|
{ keys: [']', 'character'], type: 'motion',
|
@@ -241,34 +241,38 @@
|
|
241
241
|
actionArgs: { forward: true }},
|
242
242
|
{ keys: ['<C-o>'], type: 'action', action: 'jumpListWalk',
|
243
243
|
actionArgs: { forward: false }},
|
244
|
-
{ keys: ['a'], type: 'action', action: 'enterInsertMode',
|
244
|
+
{ keys: ['a'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
245
245
|
actionArgs: { insertAt: 'charAfter' }},
|
246
|
-
{ keys: ['A'], type: 'action', action: 'enterInsertMode',
|
246
|
+
{ keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
247
247
|
actionArgs: { insertAt: 'eol' }},
|
248
|
-
{ keys: ['i'], type: 'action', action: 'enterInsertMode',
|
248
|
+
{ keys: ['i'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
249
249
|
actionArgs: { insertAt: 'inplace' }},
|
250
|
-
{ keys: ['I'], type: 'action', action: 'enterInsertMode',
|
250
|
+
{ keys: ['I'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
251
251
|
actionArgs: { insertAt: 'firstNonBlank' }},
|
252
252
|
{ keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
|
253
|
+
isEdit: true, interlaceInsertRepeat: true,
|
253
254
|
actionArgs: { after: true }},
|
254
255
|
{ keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
|
256
|
+
isEdit: true, interlaceInsertRepeat: true,
|
255
257
|
actionArgs: { after: false }},
|
256
258
|
{ keys: ['v'], type: 'action', action: 'toggleVisualMode' },
|
257
259
|
{ keys: ['V'], type: 'action', action: 'toggleVisualMode',
|
258
260
|
actionArgs: { linewise: true }},
|
259
|
-
{ keys: ['J'], type: 'action', action: 'joinLines' },
|
260
|
-
{ keys: ['p'], type: 'action', action: 'paste',
|
261
|
-
actionArgs: { after: true }},
|
262
|
-
{ keys: ['P'], type: 'action', action: 'paste',
|
263
|
-
actionArgs: { after: false }},
|
264
|
-
{ keys: ['r', 'character'], type: 'action', action: 'replace' },
|
261
|
+
{ keys: ['J'], type: 'action', action: 'joinLines', isEdit: true },
|
262
|
+
{ keys: ['p'], type: 'action', action: 'paste', isEdit: true,
|
263
|
+
actionArgs: { after: true, isEdit: true }},
|
264
|
+
{ keys: ['P'], type: 'action', action: 'paste', isEdit: true,
|
265
|
+
actionArgs: { after: false, isEdit: true }},
|
266
|
+
{ keys: ['r', 'character'], type: 'action', action: 'replace', isEdit: true },
|
265
267
|
{ keys: ['@', 'character'], type: 'action', action: 'replayMacro' },
|
266
268
|
{ keys: ['q', 'character'], type: 'action', action: 'enterMacroRecordMode' },
|
267
|
-
|
269
|
+
// Handle Replace-mode as a special case of insert mode.
|
270
|
+
{ keys: ['R'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
271
|
+
actionArgs: { replace: true }},
|
268
272
|
{ keys: ['u'], type: 'action', action: 'undo' },
|
269
273
|
{ keys: ['<C-r>'], type: 'action', action: 'redo' },
|
270
274
|
{ keys: ['m', 'character'], type: 'action', action: 'setMark' },
|
271
|
-
{ keys: ['
|
275
|
+
{ keys: ['"', 'character'], type: 'action', action: 'setRegister' },
|
272
276
|
{ keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
|
273
277
|
actionArgs: { position: 'center' }},
|
274
278
|
{ keys: ['z', '.'], type: 'action', action: 'scrollToCursor',
|
@@ -286,8 +290,10 @@
|
|
286
290
|
motion: 'moveToFirstNonWhiteSpaceCharacter' },
|
287
291
|
{ keys: ['.'], type: 'action', action: 'repeatLastEdit' },
|
288
292
|
{ keys: ['<C-a>'], type: 'action', action: 'incrementNumberToken',
|
293
|
+
isEdit: true,
|
289
294
|
actionArgs: {increase: true, backtrack: false}},
|
290
295
|
{ keys: ['<C-x>'], type: 'action', action: 'incrementNumberToken',
|
296
|
+
isEdit: true,
|
291
297
|
actionArgs: {increase: false, backtrack: false}},
|
292
298
|
// Text object motions
|
293
299
|
{ keys: ['a', 'character'], type: 'motion',
|
@@ -309,9 +315,7 @@
|
|
309
315
|
];
|
310
316
|
|
311
317
|
var Vim = function() {
|
312
|
-
var alphabetRegex = /[A-Za-z]/;
|
313
318
|
var numberRegex = /[\d]/;
|
314
|
-
var whiteSpaceRegex = /\s/;
|
315
319
|
var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)];
|
316
320
|
function makeKeyRange(start, size) {
|
317
321
|
var keys = [];
|
@@ -323,18 +327,12 @@
|
|
323
327
|
var upperCaseAlphabet = makeKeyRange(65, 26);
|
324
328
|
var lowerCaseAlphabet = makeKeyRange(97, 26);
|
325
329
|
var numbers = makeKeyRange(48, 10);
|
326
|
-
var
|
327
|
-
var specialSymbols = SPECIAL_SYMBOLS.split('');
|
330
|
+
var specialSymbols = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;"\''.split('');
|
328
331
|
var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
|
329
332
|
'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
|
330
|
-
var validMarks =
|
331
|
-
|
332
|
-
var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
|
333
|
-
numbers).concat('-\"'.split(''));
|
333
|
+
var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
|
334
|
+
var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"']);
|
334
335
|
|
335
|
-
function isAlphabet(k) {
|
336
|
-
return alphabetRegex.test(k);
|
337
|
-
}
|
338
336
|
function isLine(cm, line) {
|
339
337
|
return line >= cm.firstLine() && line <= cm.lastLine();
|
340
338
|
}
|
@@ -350,18 +348,9 @@
|
|
350
348
|
function isUpperCase(k) {
|
351
349
|
return (/^[A-Z]$/).test(k);
|
352
350
|
}
|
353
|
-
function isAlphanumeric(k) {
|
354
|
-
return (/^[\w]$/).test(k);
|
355
|
-
}
|
356
|
-
function isWhiteSpace(k) {
|
357
|
-
return whiteSpaceRegex.test(k);
|
358
|
-
}
|
359
351
|
function isWhiteSpaceString(k) {
|
360
352
|
return (/^\s*$/).test(k);
|
361
353
|
}
|
362
|
-
function inRangeInclusive(x, start, end) {
|
363
|
-
return x >= start && x <= end;
|
364
|
-
}
|
365
354
|
function inArray(val, arr) {
|
366
355
|
for (var i = 0; i < arr.length; i++) {
|
367
356
|
if (arr[i] == val) {
|
@@ -441,6 +430,11 @@
|
|
441
430
|
return {
|
442
431
|
macroKeyBuffer: [],
|
443
432
|
latestRegister: undefined,
|
433
|
+
inReplay: false,
|
434
|
+
lastInsertModeChanges: {
|
435
|
+
changes: [], // Change list
|
436
|
+
expectCursorActivityForChange: false // Set to true on change, false on cursorActivity.
|
437
|
+
},
|
444
438
|
enteredMacroMode: undefined,
|
445
439
|
isMacroPlaying: false,
|
446
440
|
toggle: function(cm, registerName) {
|
@@ -453,8 +447,8 @@
|
|
453
447
|
'(recording)['+registerName+']', null, {bottom:true});
|
454
448
|
}
|
455
449
|
}
|
456
|
-
}
|
457
|
-
}
|
450
|
+
};
|
451
|
+
};
|
458
452
|
|
459
453
|
// Global Vim state. Call getVimGlobalState to get and initialize.
|
460
454
|
var vimGlobalState;
|
@@ -479,6 +473,12 @@
|
|
479
473
|
// Store instance state in the CodeMirror object.
|
480
474
|
cm.vimState = {
|
481
475
|
inputState: new InputState(),
|
476
|
+
// Vim's input state that triggered the last edit, used to repeat
|
477
|
+
// motions and operators with '.'.
|
478
|
+
lastEditInputState: undefined,
|
479
|
+
// Vim's action command before the last edit, used to repeat actions
|
480
|
+
// with '.' and insert mode repeat.
|
481
|
+
lastEditActionCommand: undefined,
|
482
482
|
// When using jk for navigation, if you move from a longer line to a
|
483
483
|
// shorter line, the cursor may clip to the end of the shorter line.
|
484
484
|
// If j is pressed again and cursor goes to the next line, the
|
@@ -491,6 +491,10 @@
|
|
491
491
|
// executed in between.
|
492
492
|
lastMotion: null,
|
493
493
|
marks: {},
|
494
|
+
insertMode: false,
|
495
|
+
// Repeat count for changes made in insert mode, triggered by key
|
496
|
+
// sequences like 3,i. Only exists when insertMode is true.
|
497
|
+
insertModeRepeat: undefined,
|
494
498
|
visualMode: false,
|
495
499
|
// If we are in visual line mode. No effect if visualMode is false.
|
496
500
|
visualLine: false
|
@@ -512,15 +516,21 @@
|
|
512
516
|
clearVimGlobalState_: function() {
|
513
517
|
vimGlobalState = null;
|
514
518
|
},
|
519
|
+
// Testing hook.
|
520
|
+
getVimGlobalState_: function() {
|
521
|
+
return vimGlobalState;
|
522
|
+
},
|
523
|
+
InsertModeKey: InsertModeKey,
|
515
524
|
map: function(lhs, rhs) {
|
516
525
|
// Add user defined key bindings.
|
517
526
|
exCommandDispatcher.map(lhs, rhs);
|
518
527
|
},
|
519
528
|
defineEx: function(name, prefix, func){
|
520
|
-
if (name.indexOf(prefix)
|
521
|
-
|
522
|
-
|
523
|
-
|
529
|
+
if (name.indexOf(prefix) !== 0) {
|
530
|
+
throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');
|
531
|
+
}
|
532
|
+
exCommands[name]=func;
|
533
|
+
exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
|
524
534
|
},
|
525
535
|
// Initializes vim state variable on the CodeMirror object. Should only be
|
526
536
|
// called lazily by handleKey or for testing.
|
@@ -536,9 +546,9 @@
|
|
536
546
|
if (macroModeState.enteredMacroMode) {
|
537
547
|
if (key == 'q') {
|
538
548
|
actions.exitMacroRecordMode();
|
549
|
+
vim.inputState = new InputState();
|
539
550
|
return;
|
540
551
|
}
|
541
|
-
logKey(macroModeState, key);
|
542
552
|
}
|
543
553
|
if (key == '<Esc>') {
|
544
554
|
// Clear input state and get back to normal mode.
|
@@ -575,6 +585,9 @@
|
|
575
585
|
this.handleKey(cm, command.toKeys[i]);
|
576
586
|
}
|
577
587
|
} else {
|
588
|
+
if (macroModeState.enteredMacroMode) {
|
589
|
+
logKey(macroModeState, key);
|
590
|
+
}
|
578
591
|
commandDispatcher.processCommand(cm, vim, command);
|
579
592
|
}
|
580
593
|
}
|
@@ -656,10 +669,13 @@
|
|
656
669
|
*/
|
657
670
|
function RegisterController(registers) {
|
658
671
|
this.registers = registers;
|
659
|
-
this.unamedRegister = registers['
|
672
|
+
this.unamedRegister = registers['"'] = new Register();
|
660
673
|
}
|
661
674
|
RegisterController.prototype = {
|
662
675
|
pushText: function(registerName, operator, text, linewise) {
|
676
|
+
if (linewise && text.charAt(0) == '\n') {
|
677
|
+
text = text.slice(1) + '\n';
|
678
|
+
}
|
663
679
|
// Lowercase and uppercase registers refer to the same register.
|
664
680
|
// Uppercase just means append.
|
665
681
|
var register = this.isValidRegister(registerName) ?
|
@@ -739,32 +755,31 @@
|
|
739
755
|
// stroke.
|
740
756
|
inputState.keyBuffer.push(key);
|
741
757
|
return null;
|
742
|
-
}
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
}
|
758
|
+
}
|
759
|
+
if (inputState.operator && command.type == 'action') {
|
760
|
+
// Ignore matched action commands after an operator. Operators
|
761
|
+
// only operate on motions. This check is really for text
|
762
|
+
// objects since aW, a[ etcs conflicts with a.
|
763
|
+
continue;
|
764
|
+
}
|
765
|
+
// Matches whole comand. Return the command.
|
766
|
+
if (command.keys[keys.length - 1] == 'character') {
|
767
|
+
inputState.selectedCharacter = keys[keys.length - 1];
|
768
|
+
if(inputState.selectedCharacter.length>1){
|
769
|
+
switch(inputState.selectedCharacter){
|
770
|
+
case '<CR>':
|
771
|
+
inputState.selectedCharacter='\n';
|
772
|
+
break;
|
773
|
+
case '<Space>':
|
774
|
+
inputState.selectedCharacter=' ';
|
775
|
+
break;
|
776
|
+
default:
|
777
|
+
continue;
|
763
778
|
}
|
764
779
|
}
|
765
|
-
inputState.keyBuffer = [];
|
766
|
-
return command;
|
767
780
|
}
|
781
|
+
inputState.keyBuffer = [];
|
782
|
+
return command;
|
768
783
|
}
|
769
784
|
}
|
770
785
|
// Clear the buffer since there are no partial matches.
|
@@ -860,7 +875,10 @@
|
|
860
875
|
actionArgs.repeatIsExplicit = repeatIsExplicit;
|
861
876
|
actionArgs.registerName = inputState.registerName;
|
862
877
|
vim.inputState = new InputState();
|
863
|
-
vim.lastMotion = null
|
878
|
+
vim.lastMotion = null;
|
879
|
+
if (command.isEdit) {
|
880
|
+
this.recordLastEdit(vim, inputState, command);
|
881
|
+
}
|
864
882
|
actions[command.action](cm, actionArgs, vim);
|
865
883
|
},
|
866
884
|
processSearch: function(cm, vim, command) {
|
@@ -890,11 +908,11 @@
|
|
890
908
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
891
909
|
handleQuery(query, true /** ignoreCase */, true /** smartCase */);
|
892
910
|
}
|
893
|
-
function onPromptKeyUp(
|
911
|
+
function onPromptKeyUp(_e, query) {
|
894
912
|
var parsedQuery;
|
895
913
|
try {
|
896
914
|
parsedQuery = updateSearchQuery(cm, query,
|
897
|
-
true /** ignoreCase */, true /** smartCase */)
|
915
|
+
true /** ignoreCase */, true /** smartCase */);
|
898
916
|
} catch (e) {
|
899
917
|
// Swallow bad regexes for incremental search.
|
900
918
|
}
|
@@ -905,7 +923,7 @@
|
|
905
923
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
906
924
|
}
|
907
925
|
}
|
908
|
-
function onPromptKeyDown(e,
|
926
|
+
function onPromptKeyDown(e, _query, close) {
|
909
927
|
var keyName = CodeMirror.keyName(e);
|
910
928
|
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
|
911
929
|
updateSearchQuery(cm, originalQuery);
|
@@ -961,9 +979,11 @@
|
|
961
979
|
},
|
962
980
|
processEx: function(cm, vim, command) {
|
963
981
|
function onPromptClose(input) {
|
982
|
+
// Give the prompt some time to close so that if processCommand shows
|
983
|
+
// an error, the elements don't overlap.
|
964
984
|
exCommandDispatcher.processCommand(cm, input);
|
965
985
|
}
|
966
|
-
function onPromptKeyDown(e,
|
986
|
+
function onPromptKeyDown(e, _input, close) {
|
967
987
|
var keyName = CodeMirror.keyName(e);
|
968
988
|
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
|
969
989
|
CodeMirror.e_stop(e);
|
@@ -1002,7 +1022,7 @@
|
|
1002
1022
|
var curEnd;
|
1003
1023
|
var repeat;
|
1004
1024
|
if (operator) {
|
1005
|
-
this.recordLastEdit(
|
1025
|
+
this.recordLastEdit(vim, inputState);
|
1006
1026
|
}
|
1007
1027
|
if (inputState.repeatOverride !== undefined) {
|
1008
1028
|
// If repeatOverride is specified, that takes precedence over the
|
@@ -1129,12 +1149,17 @@
|
|
1129
1149
|
exitVisualMode(cm, vim);
|
1130
1150
|
}
|
1131
1151
|
if (operatorArgs.enterInsertMode) {
|
1132
|
-
actions.enterInsertMode(cm);
|
1152
|
+
actions.enterInsertMode(cm, {}, vim);
|
1133
1153
|
}
|
1134
1154
|
}
|
1135
1155
|
},
|
1136
|
-
recordLastEdit: function(
|
1137
|
-
|
1156
|
+
recordLastEdit: function(vim, inputState, actionCommand) {
|
1157
|
+
var macroModeState = getVimGlobalState().macroModeState;
|
1158
|
+
if (macroModeState.inReplay) { return; }
|
1159
|
+
vim.lastEditInputState = inputState;
|
1160
|
+
vim.lastEditActionCommand = actionCommand;
|
1161
|
+
macroModeState.lastInsertModeChanges.changes = [];
|
1162
|
+
macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
|
1138
1163
|
}
|
1139
1164
|
};
|
1140
1165
|
|
@@ -1163,7 +1188,7 @@
|
|
1163
1188
|
var cur = cm.getCursor();
|
1164
1189
|
return { line: cur.line + motionArgs.repeat - 1, ch: Infinity };
|
1165
1190
|
},
|
1166
|
-
findNext: function(cm, motionArgs
|
1191
|
+
findNext: function(cm, motionArgs) {
|
1167
1192
|
var state = getSearchState(cm);
|
1168
1193
|
var query = state.getQuery();
|
1169
1194
|
if (!query) {
|
@@ -1175,7 +1200,7 @@
|
|
1175
1200
|
highlightSearchMatches(cm, query);
|
1176
1201
|
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
1177
1202
|
},
|
1178
|
-
goToMark: function(
|
1203
|
+
goToMark: function(_cm, motionArgs, vim) {
|
1179
1204
|
var mark = vim.marks[motionArgs.selectedCharacter];
|
1180
1205
|
if (mark) {
|
1181
1206
|
return mark.find();
|
@@ -1183,7 +1208,7 @@
|
|
1183
1208
|
return null;
|
1184
1209
|
},
|
1185
1210
|
jumpToMark: function(cm, motionArgs, vim) {
|
1186
|
-
var best = cm.getCursor();
|
1211
|
+
var best = cm.getCursor();
|
1187
1212
|
for (var i = 0; i < motionArgs.repeat; i++) {
|
1188
1213
|
var cursor = best;
|
1189
1214
|
for (var key in vim.marks) {
|
@@ -1192,7 +1217,7 @@
|
|
1192
1217
|
}
|
1193
1218
|
var mark = vim.marks[key].find();
|
1194
1219
|
var isWrongDirection = (motionArgs.forward) ?
|
1195
|
-
cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark)
|
1220
|
+
cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);
|
1196
1221
|
|
1197
1222
|
if (isWrongDirection) {
|
1198
1223
|
continue;
|
@@ -1254,7 +1279,7 @@
|
|
1254
1279
|
endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
1255
1280
|
vim.lastHPos = endCh;
|
1256
1281
|
}
|
1257
|
-
vim.lastHSPos = cm.charCoords({line:line, ch:endCh},
|
1282
|
+
vim.lastHSPos = cm.charCoords({line:line, ch:endCh},'div').left;
|
1258
1283
|
return { line: line, ch: endCh };
|
1259
1284
|
},
|
1260
1285
|
moveByDisplayLines: function(cm, motionArgs, vim) {
|
@@ -1267,10 +1292,10 @@
|
|
1267
1292
|
case this.moveToEol:
|
1268
1293
|
break;
|
1269
1294
|
default:
|
1270
|
-
vim.lastHSPos = cm.charCoords(cur,
|
1295
|
+
vim.lastHSPos = cm.charCoords(cur,'div').left;
|
1271
1296
|
}
|
1272
1297
|
var repeat = motionArgs.repeat;
|
1273
|
-
var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),
|
1298
|
+
var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);
|
1274
1299
|
if (res.hitSide) {
|
1275
1300
|
if (motionArgs.forward) {
|
1276
1301
|
var lastCharCoords = cm.charCoords(res, 'div');
|
@@ -1313,7 +1338,6 @@
|
|
1313
1338
|
return { line: line, ch: 0 };
|
1314
1339
|
},
|
1315
1340
|
moveByScroll: function(cm, motionArgs, vim) {
|
1316
|
-
var globalState = getVimGlobalState();
|
1317
1341
|
var scrollbox = cm.getScrollInfo();
|
1318
1342
|
var curEnd = null;
|
1319
1343
|
var repeat = motionArgs.repeat;
|
@@ -1359,16 +1383,16 @@
|
|
1359
1383
|
var repeat = motionArgs.repeat;
|
1360
1384
|
// repeat is equivalent to which column we want to move to!
|
1361
1385
|
vim.lastHPos = repeat - 1;
|
1362
|
-
vim.lastHSPos = cm.charCoords(cm.getCursor(),
|
1386
|
+
vim.lastHSPos = cm.charCoords(cm.getCursor(),'div').left;
|
1363
1387
|
return moveToColumn(cm, repeat);
|
1364
1388
|
},
|
1365
1389
|
moveToEol: function(cm, motionArgs, vim) {
|
1366
1390
|
var cur = cm.getCursor();
|
1367
1391
|
vim.lastHPos = Infinity;
|
1368
|
-
var retval={ line: cur.line + motionArgs.repeat - 1, ch: Infinity }
|
1392
|
+
var retval={ line: cur.line + motionArgs.repeat - 1, ch: Infinity };
|
1369
1393
|
var end=cm.clipPos(retval);
|
1370
1394
|
end.ch--;
|
1371
|
-
vim.lastHSPos = cm.charCoords(end,
|
1395
|
+
vim.lastHSPos = cm.charCoords(end,'div').left;
|
1372
1396
|
return retval;
|
1373
1397
|
},
|
1374
1398
|
moveToFirstNonWhiteSpaceCharacter: function(cm) {
|
@@ -1378,7 +1402,7 @@
|
|
1378
1402
|
return { line: cursor.line,
|
1379
1403
|
ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)) };
|
1380
1404
|
},
|
1381
|
-
moveToMatchedSymbol: function(cm
|
1405
|
+
moveToMatchedSymbol: function(cm) {
|
1382
1406
|
var cursor = cm.getCursor();
|
1383
1407
|
var line = cursor.line;
|
1384
1408
|
var ch = cursor.ch;
|
@@ -1440,7 +1464,7 @@
|
|
1440
1464
|
motionArgs.inclusive = forward ? true : false;
|
1441
1465
|
var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
|
1442
1466
|
if (!curEnd) {
|
1443
|
-
cm.moveH(increment, 'char')
|
1467
|
+
cm.moveH(increment, 'char');
|
1444
1468
|
return cm.getCursor();
|
1445
1469
|
}
|
1446
1470
|
curEnd.ch += increment;
|
@@ -1449,7 +1473,7 @@
|
|
1449
1473
|
};
|
1450
1474
|
|
1451
1475
|
var operators = {
|
1452
|
-
change: function(cm, operatorArgs,
|
1476
|
+
change: function(cm, operatorArgs, _vim, curStart, curEnd) {
|
1453
1477
|
getVimGlobalState().registerController.pushText(
|
1454
1478
|
operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd),
|
1455
1479
|
operatorArgs.linewise);
|
@@ -1467,12 +1491,27 @@
|
|
1467
1491
|
// curEnd should be on the first character of the new line.
|
1468
1492
|
cm.replaceRange('\n', curStart, curEnd);
|
1469
1493
|
} else {
|
1494
|
+
// Exclude trailing whitespace if the range is not all whitespace.
|
1495
|
+
var text = cm.getRange(curStart, curEnd);
|
1496
|
+
if (!isWhiteSpaceString(text)) {
|
1497
|
+
var match = (/\s+$/).exec(text);
|
1498
|
+
if (match) {
|
1499
|
+
curEnd = offsetCursor(curEnd, 0, - match[0].length);
|
1500
|
+
}
|
1501
|
+
}
|
1470
1502
|
cm.replaceRange('', curStart, curEnd);
|
1471
1503
|
}
|
1472
1504
|
cm.setCursor(curStart);
|
1473
1505
|
},
|
1474
1506
|
// delete is a javascript keyword.
|
1475
|
-
'delete': function(cm, operatorArgs,
|
1507
|
+
'delete': function(cm, operatorArgs, _vim, curStart, curEnd) {
|
1508
|
+
// If the ending line is past the last line, inclusive, instead of
|
1509
|
+
// including the trailing \n, include the \n before the starting line
|
1510
|
+
if (operatorArgs.linewise &&
|
1511
|
+
curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) {
|
1512
|
+
curStart.line--;
|
1513
|
+
curStart.ch = lineLength(cm, curStart.line);
|
1514
|
+
}
|
1476
1515
|
getVimGlobalState().registerController.pushText(
|
1477
1516
|
operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd),
|
1478
1517
|
operatorArgs.linewise);
|
@@ -1503,7 +1542,7 @@
|
|
1503
1542
|
cm.setCursor(curStart);
|
1504
1543
|
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1505
1544
|
},
|
1506
|
-
swapcase: function(cm,
|
1545
|
+
swapcase: function(cm, _operatorArgs, _vim, curStart, curEnd, curOriginal) {
|
1507
1546
|
var toSwap = cm.getRange(curStart, curEnd);
|
1508
1547
|
var swapped = '';
|
1509
1548
|
for (var i = 0; i < toSwap.length; i++) {
|
@@ -1514,7 +1553,7 @@
|
|
1514
1553
|
cm.replaceRange(swapped, curStart, curEnd);
|
1515
1554
|
cm.setCursor(curOriginal);
|
1516
1555
|
},
|
1517
|
-
yank: function(cm, operatorArgs,
|
1556
|
+
yank: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) {
|
1518
1557
|
getVimGlobalState().registerController.pushText(
|
1519
1558
|
operatorArgs.registerName, 'yank',
|
1520
1559
|
cm.getRange(curStart, curEnd), operatorArgs.linewise);
|
@@ -1538,7 +1577,7 @@
|
|
1538
1577
|
},
|
1539
1578
|
scrollToCursor: function(cm, actionArgs) {
|
1540
1579
|
var lineNum = cm.getCursor().line;
|
1541
|
-
var charCoords = cm.charCoords({line: lineNum, ch: 0},
|
1580
|
+
var charCoords = cm.charCoords({line: lineNum, ch: 0}, 'local');
|
1542
1581
|
var height = cm.getScrollInfo().clientHeight;
|
1543
1582
|
var y = charCoords.top;
|
1544
1583
|
var lineHeight = charCoords.bottom - y;
|
@@ -1564,7 +1603,7 @@
|
|
1564
1603
|
executeMacroKeyBuffer(cm, macroModeState, keyBuffer);
|
1565
1604
|
}
|
1566
1605
|
},
|
1567
|
-
exitMacroRecordMode: function(
|
1606
|
+
exitMacroRecordMode: function() {
|
1568
1607
|
var macroModeState = getVimGlobalState().macroModeState;
|
1569
1608
|
macroModeState.toggle();
|
1570
1609
|
parseKeyBufferToRegister(macroModeState.latestRegister,
|
@@ -1576,7 +1615,9 @@
|
|
1576
1615
|
macroModeState.toggle(cm, registerName);
|
1577
1616
|
emptyMacroKeyBuffer(macroModeState);
|
1578
1617
|
},
|
1579
|
-
enterInsertMode: function(cm, actionArgs) {
|
1618
|
+
enterInsertMode: function(cm, actionArgs, vim) {
|
1619
|
+
vim.insertMode = true;
|
1620
|
+
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
|
1580
1621
|
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
|
1581
1622
|
if (insertAt == 'eol') {
|
1582
1623
|
var cursor = cm.getCursor();
|
@@ -1588,6 +1629,19 @@
|
|
1588
1629
|
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1589
1630
|
}
|
1590
1631
|
cm.setOption('keyMap', 'vim-insert');
|
1632
|
+
if (actionArgs && actionArgs.replace) {
|
1633
|
+
// Handle Replace-mode as a special case of insert mode.
|
1634
|
+
cm.toggleOverwrite(true);
|
1635
|
+
cm.setOption('keyMap', 'vim-replace');
|
1636
|
+
} else {
|
1637
|
+
cm.setOption('keyMap', 'vim-insert');
|
1638
|
+
}
|
1639
|
+
if (!getVimGlobalState().macroModeState.inReplay) {
|
1640
|
+
// Only record if not replaying.
|
1641
|
+
cm.on('change', onChange);
|
1642
|
+
cm.on('cursorActivity', onCursorActivity);
|
1643
|
+
CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
|
1644
|
+
}
|
1591
1645
|
},
|
1592
1646
|
toggleVisualMode: function(cm, actionArgs, vim) {
|
1593
1647
|
var repeat = actionArgs.repeat;
|
@@ -1673,7 +1727,7 @@
|
|
1673
1727
|
cm.setCursor(curFinalPos);
|
1674
1728
|
});
|
1675
1729
|
},
|
1676
|
-
newLineAndEnterInsertMode: function(cm, actionArgs) {
|
1730
|
+
newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
|
1677
1731
|
var insertAt = cm.getCursor();
|
1678
1732
|
if (insertAt.line === cm.firstLine() && !actionArgs.after) {
|
1679
1733
|
// Special case for inserting newline before start of document.
|
@@ -1688,9 +1742,9 @@
|
|
1688
1742
|
CodeMirror.commands.newlineAndIndent;
|
1689
1743
|
newlineFn(cm);
|
1690
1744
|
}
|
1691
|
-
this.enterInsertMode(cm);
|
1745
|
+
this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
|
1692
1746
|
},
|
1693
|
-
paste: function(cm, actionArgs
|
1747
|
+
paste: function(cm, actionArgs) {
|
1694
1748
|
var cur = cm.getCursor();
|
1695
1749
|
var register = getVimGlobalState().registerController.getRegister(
|
1696
1750
|
actionArgs.registerName);
|
@@ -1733,12 +1787,15 @@
|
|
1733
1787
|
cm.setCursor(curPosFinal);
|
1734
1788
|
},
|
1735
1789
|
undo: function(cm, actionArgs) {
|
1736
|
-
|
1790
|
+
cm.operation(function() {
|
1791
|
+
repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
|
1792
|
+
cm.setCursor(cm.getCursor('anchor'));
|
1793
|
+
});
|
1737
1794
|
},
|
1738
1795
|
redo: function(cm, actionArgs) {
|
1739
1796
|
repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
|
1740
1797
|
},
|
1741
|
-
setRegister: function(
|
1798
|
+
setRegister: function(_cm, actionArgs, vim) {
|
1742
1799
|
vim.inputState.registerName = actionArgs.selectedCharacter;
|
1743
1800
|
},
|
1744
1801
|
setMark: function(cm, actionArgs, vim) {
|
@@ -1781,11 +1838,7 @@
|
|
1781
1838
|
}
|
1782
1839
|
}
|
1783
1840
|
},
|
1784
|
-
|
1785
|
-
cm.setOption('keyMap', 'vim-replace');
|
1786
|
-
cm.toggleOverwrite();
|
1787
|
-
},
|
1788
|
-
incrementNumberToken: function(cm, actionArgs, vim) {
|
1841
|
+
incrementNumberToken: function(cm, actionArgs) {
|
1789
1842
|
var cur = cm.getCursor();
|
1790
1843
|
var lineStr = cm.getLine(cur.line);
|
1791
1844
|
var re = /-?\d+/g;
|
@@ -1814,17 +1867,15 @@
|
|
1814
1867
|
cm.setCursor({line: cur.line, ch: start + numberStr.length - 1});
|
1815
1868
|
},
|
1816
1869
|
repeatLastEdit: function(cm, actionArgs, vim) {
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
vim.inputState = vim.lastEdit;
|
1825
|
-
commandDispatcher.evalInput(cm, vim);
|
1826
|
-
vim.inputState = currentInputState;
|
1870
|
+
var lastEditInputState = vim.lastEditInputState;
|
1871
|
+
if (!lastEditInputState) { return; }
|
1872
|
+
var repeat = actionArgs.repeat;
|
1873
|
+
if (repeat && actionArgs.repeatIsExplicit) {
|
1874
|
+
vim.lastEditInputState.repeatOverride = repeat;
|
1875
|
+
} else {
|
1876
|
+
repeat = vim.lastEditInputState.repeatOverride || repeat;
|
1827
1877
|
}
|
1878
|
+
repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
|
1828
1879
|
}
|
1829
1880
|
};
|
1830
1881
|
|
@@ -1853,7 +1904,7 @@
|
|
1853
1904
|
'\'': function(cm, inclusive) {
|
1854
1905
|
return findBeginningAndEnd(cm, "'", inclusive);
|
1855
1906
|
},
|
1856
|
-
'
|
1907
|
+
'"': function(cm, inclusive) {
|
1857
1908
|
return findBeginningAndEnd(cm, '"', inclusive);
|
1858
1909
|
}
|
1859
1910
|
};
|
@@ -1873,14 +1924,6 @@
|
|
1873
1924
|
var ch = Math.min(Math.max(0, cur.ch), maxCh);
|
1874
1925
|
return { line: line, ch: ch };
|
1875
1926
|
}
|
1876
|
-
// Merge arguments in place, for overriding arguments.
|
1877
|
-
function mergeArgs(to, from) {
|
1878
|
-
for (var prop in from) {
|
1879
|
-
if (from.hasOwnProperty(prop)) {
|
1880
|
-
to[prop] = from[prop];
|
1881
|
-
}
|
1882
|
-
}
|
1883
|
-
}
|
1884
1927
|
function copyArgs(args) {
|
1885
1928
|
var ret = {};
|
1886
1929
|
for (var prop in args) {
|
@@ -1893,17 +1936,6 @@
|
|
1893
1936
|
function offsetCursor(cur, offsetLine, offsetCh) {
|
1894
1937
|
return { line: cur.line + offsetLine, ch: cur.ch + offsetCh };
|
1895
1938
|
}
|
1896
|
-
function arrayEq(a1, a2) {
|
1897
|
-
if (a1.length != a2.length) {
|
1898
|
-
return false;
|
1899
|
-
}
|
1900
|
-
for (var i = 0; i < a1.length; i++) {
|
1901
|
-
if (a1[i] != a2[i]) {
|
1902
|
-
return false;
|
1903
|
-
}
|
1904
|
-
}
|
1905
|
-
return true;
|
1906
|
-
}
|
1907
1939
|
function matchKeysPartial(pressed, mapped) {
|
1908
1940
|
for (var i = 0; i < pressed.length; i++) {
|
1909
1941
|
// 'character' means any character. For mark, register commads, etc.
|
@@ -1913,14 +1945,6 @@
|
|
1913
1945
|
}
|
1914
1946
|
return true;
|
1915
1947
|
}
|
1916
|
-
function arrayIsSubsetFromBeginning(small, big) {
|
1917
|
-
for (var i = 0; i < small.length; i++) {
|
1918
|
-
if (small[i] != big[i]) {
|
1919
|
-
return false;
|
1920
|
-
}
|
1921
|
-
}
|
1922
|
-
return true;
|
1923
|
-
}
|
1924
1948
|
function repeatFn(cm, fn, repeat) {
|
1925
1949
|
return function() {
|
1926
1950
|
for (var i = 0; i < repeat; i++) {
|
@@ -1937,7 +1961,8 @@
|
|
1937
1961
|
function cursorIsBefore(cur1, cur2) {
|
1938
1962
|
if (cur1.line < cur2.line) {
|
1939
1963
|
return true;
|
1940
|
-
}
|
1964
|
+
}
|
1965
|
+
if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
|
1941
1966
|
return true;
|
1942
1967
|
}
|
1943
1968
|
return false;
|
@@ -1952,17 +1977,16 @@
|
|
1952
1977
|
return cm.getLine(lineNum).length;
|
1953
1978
|
}
|
1954
1979
|
function reverse(s){
|
1955
|
-
return s.split(
|
1980
|
+
return s.split('').reverse().join('');
|
1956
1981
|
}
|
1957
1982
|
function trim(s) {
|
1958
1983
|
if (s.trim) {
|
1959
1984
|
return s.trim();
|
1960
|
-
} else {
|
1961
|
-
return s.replace(/^\s+|\s+$/g, '');
|
1962
1985
|
}
|
1986
|
+
return s.replace(/^\s+|\s+$/g, '');
|
1963
1987
|
}
|
1964
1988
|
function escapeRegex(s) {
|
1965
|
-
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g,
|
1989
|
+
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
|
1966
1990
|
}
|
1967
1991
|
|
1968
1992
|
function exitVisualMode(cm, vim) {
|
@@ -1997,7 +2021,6 @@
|
|
1997
2021
|
// Find the line containing the last word, and clip all whitespace up
|
1998
2022
|
// to it.
|
1999
2023
|
for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
|
2000
|
-
var clipped = false;
|
2001
2024
|
curEnd.line--;
|
2002
2025
|
curEnd.ch = 0;
|
2003
2026
|
}
|
@@ -2012,7 +2035,7 @@
|
|
2012
2035
|
}
|
2013
2036
|
|
2014
2037
|
// Expand the selection to line ends.
|
2015
|
-
function expandSelectionToLine(
|
2038
|
+
function expandSelectionToLine(_cm, curStart, curEnd) {
|
2016
2039
|
curStart.ch = 0;
|
2017
2040
|
curEnd.ch = 0;
|
2018
2041
|
curEnd.line++;
|
@@ -2026,7 +2049,7 @@
|
|
2026
2049
|
return firstNonWS == -1 ? text.length : firstNonWS;
|
2027
2050
|
}
|
2028
2051
|
|
2029
|
-
function expandWordUnderCursor(cm, inclusive,
|
2052
|
+
function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
|
2030
2053
|
var cur = cm.getCursor();
|
2031
2054
|
var line = cm.getLine(cur.line);
|
2032
2055
|
var idx = cur.ch;
|
@@ -2137,7 +2160,7 @@
|
|
2137
2160
|
}
|
2138
2161
|
},
|
2139
2162
|
// TODO: The original Vim implementation only operates on level 1 and 2.
|
2140
|
-
// The current implementation doesn't check for code block level and
|
2163
|
+
// The current implementation doesn't check for code block level and
|
2141
2164
|
// therefore it operates on any levels.
|
2142
2165
|
method: {
|
2143
2166
|
init: function(state) {
|
@@ -2189,7 +2212,7 @@
|
|
2189
2212
|
reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
|
2190
2213
|
forward: forward,
|
2191
2214
|
depth: 0,
|
2192
|
-
curMoveThrough: false
|
2215
|
+
curMoveThrough: false
|
2193
2216
|
};
|
2194
2217
|
var mode = symbolToMode[symb];
|
2195
2218
|
if(!mode)return cur;
|
@@ -2298,7 +2321,7 @@
|
|
2298
2321
|
pos = (dir > 0) ? 0 : line.length;
|
2299
2322
|
}
|
2300
2323
|
// Should never get here.
|
2301
|
-
throw 'The impossible happened.';
|
2324
|
+
throw new Error('The impossible happened.');
|
2302
2325
|
}
|
2303
2326
|
|
2304
2327
|
/**
|
@@ -2484,16 +2507,6 @@
|
|
2484
2507
|
return { start: start, end: end };
|
2485
2508
|
}
|
2486
2509
|
|
2487
|
-
function regexLastIndexOf(string, pattern, startIndex) {
|
2488
|
-
for (var i = !startIndex ? string.length : startIndex;
|
2489
|
-
i >= 0; --i) {
|
2490
|
-
if (pattern.test(string.charAt(i))) {
|
2491
|
-
return i;
|
2492
|
-
}
|
2493
|
-
}
|
2494
|
-
return -1;
|
2495
|
-
}
|
2496
|
-
|
2497
2510
|
// Takes in a symbol and a cursor and tries to simulate text objects that
|
2498
2511
|
// have identical opening and closing symbols
|
2499
2512
|
// TODO support across multiple lines
|
@@ -2587,9 +2600,10 @@
|
|
2587
2600
|
onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp });
|
2588
2601
|
}
|
2589
2602
|
else {
|
2590
|
-
onClose(prompt(shortText,
|
2603
|
+
onClose(prompt(shortText, ''));
|
2591
2604
|
}
|
2592
2605
|
}
|
2606
|
+
|
2593
2607
|
function findUnescapedSlashes(str) {
|
2594
2608
|
var escapeNextChar = false;
|
2595
2609
|
var slashes = [];
|
@@ -2612,7 +2626,7 @@
|
|
2612
2626
|
* then both ignoreCase and smartCase are ignored, and 'i' will be passed
|
2613
2627
|
* through to the Regex object.
|
2614
2628
|
*/
|
2615
|
-
function parseQuery(
|
2629
|
+
function parseQuery(query, ignoreCase, smartCase) {
|
2616
2630
|
// Check if the query is already a regex.
|
2617
2631
|
if (query instanceof RegExp) { return query; }
|
2618
2632
|
// First try to extract regex + flags from the input. If no flags found,
|
@@ -2671,16 +2685,16 @@
|
|
2671
2685
|
}
|
2672
2686
|
function regexEqual(r1, r2) {
|
2673
2687
|
if (r1 instanceof RegExp && r2 instanceof RegExp) {
|
2674
|
-
var props = [
|
2688
|
+
var props = ['global', 'multiline', 'ignoreCase', 'source'];
|
2675
2689
|
for (var i = 0; i < props.length; i++) {
|
2676
2690
|
var prop = props[i];
|
2677
2691
|
if (r1[prop] !== r2[prop]) {
|
2678
|
-
return
|
2692
|
+
return false;
|
2679
2693
|
}
|
2680
2694
|
}
|
2681
|
-
return
|
2695
|
+
return true;
|
2682
2696
|
}
|
2683
|
-
return
|
2697
|
+
return false;
|
2684
2698
|
}
|
2685
2699
|
// Returns true if the query is valid.
|
2686
2700
|
function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
|
@@ -2688,7 +2702,7 @@
|
|
2688
2702
|
return;
|
2689
2703
|
}
|
2690
2704
|
var state = getSearchState(cm);
|
2691
|
-
var query = parseQuery(
|
2705
|
+
var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
|
2692
2706
|
if (!query) {
|
2693
2707
|
return;
|
2694
2708
|
}
|
@@ -2714,7 +2728,7 @@
|
|
2714
2728
|
if (match[0].length == 0) {
|
2715
2729
|
// Matched empty string, skip to next.
|
2716
2730
|
stream.next();
|
2717
|
-
return
|
2731
|
+
return 'searching';
|
2718
2732
|
}
|
2719
2733
|
if (!stream.sol()) {
|
2720
2734
|
// Backtrack 1 to match \b
|
@@ -2725,7 +2739,7 @@
|
|
2725
2739
|
}
|
2726
2740
|
}
|
2727
2741
|
stream.match(query);
|
2728
|
-
return
|
2742
|
+
return 'searching';
|
2729
2743
|
}
|
2730
2744
|
while (!stream.eol()) {
|
2731
2745
|
stream.next();
|
@@ -2765,7 +2779,8 @@
|
|
2765
2779
|
}
|
2766
2780
|
}
|
2767
2781
|
return cursor.from();
|
2768
|
-
});
|
2782
|
+
});
|
2783
|
+
}
|
2769
2784
|
function clearSearchHighlight(cm) {
|
2770
2785
|
cm.removeOverlay(getSearchState(cm).getOverlay());
|
2771
2786
|
getSearchState(cm).setOverlay(null);
|
@@ -2798,10 +2813,10 @@
|
|
2798
2813
|
}
|
2799
2814
|
function getUserVisibleLines(cm) {
|
2800
2815
|
var scrollInfo = cm.getScrollInfo();
|
2801
|
-
var
|
2802
|
-
var
|
2803
|
-
var from = cm.coordsChar({left:0, top:
|
2804
|
-
var bottomY = scrollInfo.clientHeight -
|
2816
|
+
var occludeToleranceTop = 6;
|
2817
|
+
var occludeToleranceBottom = 10;
|
2818
|
+
var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local');
|
2819
|
+
var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;
|
2805
2820
|
var to = cm.coordsChar({left:0, top: bottomY}, 'local');
|
2806
2821
|
return {top: from.line, bottom: to.line};
|
2807
2822
|
}
|
@@ -2815,6 +2830,7 @@
|
|
2815
2830
|
{ name: 'write', shortName: 'w', type: 'builtIn' },
|
2816
2831
|
{ name: 'undo', shortName: 'u', type: 'builtIn' },
|
2817
2832
|
{ name: 'redo', shortName: 'red', type: 'builtIn' },
|
2833
|
+
{ name: 'sort', shortName: 'sor', type: 'builtIn'},
|
2818
2834
|
{ name: 'substitute', shortName: 's', type: 'builtIn'},
|
2819
2835
|
{ name: 'nohlsearch', shortName: 'noh', type: 'builtIn'},
|
2820
2836
|
{ name: 'delmarks', shortName: 'delm', type: 'builtin'}
|
@@ -2824,6 +2840,10 @@
|
|
2824
2840
|
};
|
2825
2841
|
Vim.ExCommandDispatcher.prototype = {
|
2826
2842
|
processCommand: function(cm, input) {
|
2843
|
+
var vim = getVimState(cm);
|
2844
|
+
if (vim.visualMode) {
|
2845
|
+
exitVisualMode(cm, vim);
|
2846
|
+
}
|
2827
2847
|
var inputStream = new CodeMirror.StringStream(input);
|
2828
2848
|
var params = {};
|
2829
2849
|
params.input = input;
|
@@ -2847,7 +2867,7 @@
|
|
2847
2867
|
if (command.type == 'exToKey') {
|
2848
2868
|
// Handle Ex to Key mapping.
|
2849
2869
|
for (var i = 0; i < command.toKeys.length; i++) {
|
2850
|
-
|
2870
|
+
CodeMirror.Vim.handleKey(cm, command.toKeys[i]);
|
2851
2871
|
}
|
2852
2872
|
return;
|
2853
2873
|
} else if (command.type == 'exToEx') {
|
@@ -2861,7 +2881,11 @@
|
|
2861
2881
|
showConfirm(cm, 'Not an editor command ":' + input + '"');
|
2862
2882
|
return;
|
2863
2883
|
}
|
2864
|
-
|
2884
|
+
try {
|
2885
|
+
exCommands[commandName](cm, params);
|
2886
|
+
} catch(e) {
|
2887
|
+
showConfirm(cm, e);
|
2888
|
+
}
|
2865
2889
|
},
|
2866
2890
|
parseInput_: function(cm, inputStream, result) {
|
2867
2891
|
inputStream.eatWhile(':');
|
@@ -2900,13 +2924,11 @@
|
|
2900
2924
|
var mark = getVimState(cm).marks[inputStream.next()];
|
2901
2925
|
if (mark && mark.find()) {
|
2902
2926
|
return mark.find().line;
|
2903
|
-
} else {
|
2904
|
-
throw "Mark not set";
|
2905
2927
|
}
|
2906
|
-
|
2928
|
+
throw new Error('Mark not set');
|
2907
2929
|
default:
|
2908
2930
|
inputStream.backUp(1);
|
2909
|
-
return
|
2931
|
+
return undefined;
|
2910
2932
|
}
|
2911
2933
|
},
|
2912
2934
|
parseCommandArgs_: function(inputStream, params, command) {
|
@@ -3016,7 +3038,82 @@
|
|
3016
3038
|
linewise: true },
|
3017
3039
|
repeatOverride: params.line+1});
|
3018
3040
|
},
|
3041
|
+
sort: function(cm, params) {
|
3042
|
+
var reverse, ignoreCase, unique, number;
|
3043
|
+
function parseArgs() {
|
3044
|
+
if (params.argString) {
|
3045
|
+
var args = new CodeMirror.StringStream(params.argString);
|
3046
|
+
if (args.eat('!')) { reverse = true; }
|
3047
|
+
if (args.eol()) { return; }
|
3048
|
+
if (!args.eatSpace()) { throw new Error('invalid arguments ' + args.match(/.*/)[0]); }
|
3049
|
+
var opts = args.match(/[a-z]+/);
|
3050
|
+
if (opts) {
|
3051
|
+
opts = opts[0];
|
3052
|
+
ignoreCase = opts.indexOf('i') != -1;
|
3053
|
+
unique = opts.indexOf('u') != -1;
|
3054
|
+
var decimal = opts.indexOf('d') != -1 && 1;
|
3055
|
+
var hex = opts.indexOf('x') != -1 && 1;
|
3056
|
+
var octal = opts.indexOf('o') != -1 && 1;
|
3057
|
+
if (decimal + hex + octal > 1) { throw new Error('invalid arguments'); }
|
3058
|
+
number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
|
3059
|
+
}
|
3060
|
+
if (args.eatSpace() && args.match(/\/.*\//)) { throw new Error('patterns not supported'); }
|
3061
|
+
}
|
3062
|
+
}
|
3063
|
+
parseArgs();
|
3064
|
+
var lineStart = params.line || cm.firstLine();
|
3065
|
+
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
3066
|
+
if (lineStart == lineEnd) { return; }
|
3067
|
+
var curStart = { line: lineStart, ch: 0 };
|
3068
|
+
var curEnd = { line: lineEnd, ch: lineLength(cm, lineEnd) };
|
3069
|
+
var text = cm.getRange(curStart, curEnd).split('\n');
|
3070
|
+
var numberRegex = (number == 'decimal') ? /(-?)([\d]+)/ :
|
3071
|
+
(number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :
|
3072
|
+
(number == 'octal') ? /([0-7]+)/ : null;
|
3073
|
+
var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;
|
3074
|
+
var numPart = [], textPart = [];
|
3075
|
+
if (number) {
|
3076
|
+
for (var i = 0; i < text.length; i++) {
|
3077
|
+
if (numberRegex.exec(text[i])) {
|
3078
|
+
numPart.push(text[i]);
|
3079
|
+
} else {
|
3080
|
+
textPart.push(text[i]);
|
3081
|
+
}
|
3082
|
+
}
|
3083
|
+
} else {
|
3084
|
+
textPart = text;
|
3085
|
+
}
|
3086
|
+
function compareFn(a, b) {
|
3087
|
+
if (reverse) { var tmp; tmp = a; a = b; b = tmp; }
|
3088
|
+
if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }
|
3089
|
+
var anum = number && numberRegex.exec(a);
|
3090
|
+
var bnum = number && numberRegex.exec(b);
|
3091
|
+
if (!anum) { return a < b ? -1 : 1; }
|
3092
|
+
anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);
|
3093
|
+
bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);
|
3094
|
+
return anum - bnum;
|
3095
|
+
}
|
3096
|
+
numPart.sort(compareFn);
|
3097
|
+
textPart.sort(compareFn);
|
3098
|
+
text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);
|
3099
|
+
if (unique) { // Remove duplicate lines
|
3100
|
+
var textOld = text;
|
3101
|
+
var lastLine;
|
3102
|
+
text = [];
|
3103
|
+
for (var i = 0; i < textOld.length; i++) {
|
3104
|
+
if (textOld[i] != lastLine) {
|
3105
|
+
text.push(textOld[i]);
|
3106
|
+
}
|
3107
|
+
lastLine = textOld[i];
|
3108
|
+
}
|
3109
|
+
}
|
3110
|
+
cm.replaceRange(text.join('\n'), curStart, curEnd);
|
3111
|
+
},
|
3019
3112
|
substitute: function(cm, params) {
|
3113
|
+
if (!cm.getSearchCursor) {
|
3114
|
+
throw new Error('Search feature not available. Requires searchcursor.js or ' +
|
3115
|
+
'any other getSearchCursor implementation.');
|
3116
|
+
}
|
3020
3117
|
var argString = params.argString;
|
3021
3118
|
var slashes = findUnescapedSlashes(argString);
|
3022
3119
|
if (slashes[0] !== 0) {
|
@@ -3028,6 +3125,7 @@
|
|
3028
3125
|
var replacePart = '';
|
3029
3126
|
var flagsPart;
|
3030
3127
|
var count;
|
3128
|
+
var confirm = false; // Whether to confirm each replace.
|
3031
3129
|
if (slashes[1]) {
|
3032
3130
|
replacePart = argString.substring(slashes[1] + 1, slashes[2]);
|
3033
3131
|
}
|
@@ -3039,6 +3137,10 @@
|
|
3039
3137
|
count = parseInt(trailing[1]);
|
3040
3138
|
}
|
3041
3139
|
if (flagsPart) {
|
3140
|
+
if (flagsPart.indexOf('c') != -1) {
|
3141
|
+
confirm = true;
|
3142
|
+
flagsPart.replace('c', '');
|
3143
|
+
}
|
3042
3144
|
regexPart = regexPart + '/' + flagsPart;
|
3043
3145
|
}
|
3044
3146
|
if (regexPart) {
|
@@ -3054,27 +3156,15 @@
|
|
3054
3156
|
}
|
3055
3157
|
var state = getSearchState(cm);
|
3056
3158
|
var query = state.getQuery();
|
3057
|
-
var lineStart = params.line
|
3159
|
+
var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
|
3058
3160
|
var lineEnd = params.lineEnd || lineStart;
|
3059
3161
|
if (count) {
|
3060
3162
|
lineStart = lineEnd;
|
3061
3163
|
lineEnd = lineStart + count - 1;
|
3062
3164
|
}
|
3063
3165
|
var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
|
3064
|
-
|
3065
|
-
|
3066
|
-
cursor.findNext() &&
|
3067
|
-
isInRange(cursor.from(), lineStart, lineEnd);) {
|
3068
|
-
var text = cm.getRange(cursor.from(), cursor.to());
|
3069
|
-
var newText = text.replace(query, replacePart);
|
3070
|
-
cursor.replace(newText);
|
3071
|
-
}
|
3072
|
-
var vim = getVimState(cm);
|
3073
|
-
if (vim.visualMode) {
|
3074
|
-
exitVisualMode(cm, vim);
|
3075
|
-
}
|
3076
|
-
}
|
3077
|
-
cm.operation(doReplace);
|
3166
|
+
var cursor = cm.getSearchCursor(query, startPos);
|
3167
|
+
doReplace(cm, confirm, lineStart, lineEnd, cursor, query, replacePart);
|
3078
3168
|
},
|
3079
3169
|
redo: CodeMirror.commands.redo,
|
3080
3170
|
undo: CodeMirror.commands.undo,
|
@@ -3142,7 +3232,7 @@
|
|
3142
3232
|
delete state.marks[mark];
|
3143
3233
|
}
|
3144
3234
|
} else {
|
3145
|
-
showConfirm(cm, 'Invalid argument: ' + startMark +
|
3235
|
+
showConfirm(cm, 'Invalid argument: ' + startMark + '-');
|
3146
3236
|
return;
|
3147
3237
|
}
|
3148
3238
|
} else {
|
@@ -3155,6 +3245,95 @@
|
|
3155
3245
|
|
3156
3246
|
var exCommandDispatcher = new Vim.ExCommandDispatcher();
|
3157
3247
|
|
3248
|
+
/**
|
3249
|
+
* @param {CodeMirror} cm CodeMirror instance we are in.
|
3250
|
+
* @param {boolean} confirm Whether to confirm each replace.
|
3251
|
+
* @param {Cursor} lineStart Line to start replacing from.
|
3252
|
+
* @param {Cursor} lineEnd Line to stop replacing at.
|
3253
|
+
* @param {RegExp} query Query for performing matches with.
|
3254
|
+
* @param {string} replaceWith Text to replace matches with. May contain $1,
|
3255
|
+
* $2, etc for replacing captured groups using Javascript replace.
|
3256
|
+
*/
|
3257
|
+
function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query,
|
3258
|
+
replaceWith) {
|
3259
|
+
// Set up all the functions.
|
3260
|
+
var done = false;
|
3261
|
+
var lastPos = searchCursor.from();
|
3262
|
+
function replaceAll() {
|
3263
|
+
cm.operation(function() {
|
3264
|
+
while (!done) {
|
3265
|
+
replace();
|
3266
|
+
next();
|
3267
|
+
}
|
3268
|
+
stop();
|
3269
|
+
});
|
3270
|
+
}
|
3271
|
+
function replace() {
|
3272
|
+
var text = cm.getRange(searchCursor.from(), searchCursor.to());
|
3273
|
+
var newText = text.replace(query, replaceWith);
|
3274
|
+
searchCursor.replace(newText);
|
3275
|
+
}
|
3276
|
+
function next() {
|
3277
|
+
var found = searchCursor.findNext();
|
3278
|
+
if (!found) {
|
3279
|
+
done = true;
|
3280
|
+
} else if (isInRange(searchCursor.from(), lineStart, lineEnd)) {
|
3281
|
+
cm.scrollIntoView(searchCursor.from(), 30);
|
3282
|
+
cm.setSelection(searchCursor.from(), searchCursor.to());
|
3283
|
+
lastPos = searchCursor.from();
|
3284
|
+
done = false;
|
3285
|
+
} else {
|
3286
|
+
done = true;
|
3287
|
+
}
|
3288
|
+
}
|
3289
|
+
function stop(close) {
|
3290
|
+
if (close) { close(); }
|
3291
|
+
cm.focus();
|
3292
|
+
if (lastPos) {
|
3293
|
+
cm.setCursor(lastPos);
|
3294
|
+
var vim = getVimState(cm);
|
3295
|
+
vim.lastHPos = vim.lastHSPos = lastPos.ch;
|
3296
|
+
}
|
3297
|
+
}
|
3298
|
+
function onPromptKeyDown(e, _value, close) {
|
3299
|
+
// Swallow all keys.
|
3300
|
+
CodeMirror.e_stop(e);
|
3301
|
+
var keyName = CodeMirror.keyName(e);
|
3302
|
+
switch (keyName) {
|
3303
|
+
case 'Y':
|
3304
|
+
replace(); next(); break;
|
3305
|
+
case 'N':
|
3306
|
+
next(); break;
|
3307
|
+
case 'A':
|
3308
|
+
cm.operation(replaceAll); break;
|
3309
|
+
case 'L':
|
3310
|
+
replace();
|
3311
|
+
// fall through and exit.
|
3312
|
+
case 'Q':
|
3313
|
+
case 'Esc':
|
3314
|
+
case 'Ctrl-C':
|
3315
|
+
case 'Ctrl-[':
|
3316
|
+
stop(close);
|
3317
|
+
break;
|
3318
|
+
}
|
3319
|
+
if (done) { stop(close); }
|
3320
|
+
}
|
3321
|
+
|
3322
|
+
// Actually do replace.
|
3323
|
+
next();
|
3324
|
+
if (done) {
|
3325
|
+
throw new Error('No matches for ' + query.source);
|
3326
|
+
}
|
3327
|
+
if (!confirm) {
|
3328
|
+
replaceAll();
|
3329
|
+
return;
|
3330
|
+
}
|
3331
|
+
showPrompt(cm, {
|
3332
|
+
prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
|
3333
|
+
onKeyDown: onPromptKeyDown
|
3334
|
+
});
|
3335
|
+
}
|
3336
|
+
|
3158
3337
|
// Register Vim with CodeMirror
|
3159
3338
|
function buildVimKeyMap() {
|
3160
3339
|
/**
|
@@ -3188,13 +3367,13 @@
|
|
3188
3367
|
// Closure to bind CodeMirror, key, modifier.
|
3189
3368
|
function keyMapper(vimKey) {
|
3190
3369
|
return function(cm) {
|
3191
|
-
|
3370
|
+
CodeMirror.Vim.handleKey(cm, vimKey);
|
3192
3371
|
};
|
3193
3372
|
}
|
3194
3373
|
|
3195
|
-
var modifiers = ['Shift', 'Ctrl'];
|
3196
3374
|
var cmToVimKeymap = {
|
3197
3375
|
'nofallthrough': true,
|
3376
|
+
'disableInput': true,
|
3198
3377
|
'style': 'fat-cursor'
|
3199
3378
|
};
|
3200
3379
|
function bindKeys(keys, modifier) {
|
@@ -3224,8 +3403,24 @@
|
|
3224
3403
|
CodeMirror.keyMap.vim = buildVimKeyMap();
|
3225
3404
|
|
3226
3405
|
function exitInsertMode(cm) {
|
3406
|
+
var vim = getVimState(cm);
|
3407
|
+
vim.insertMode = false;
|
3408
|
+
var inReplay = getVimGlobalState().macroModeState.inReplay;
|
3409
|
+
if (!inReplay) {
|
3410
|
+
cm.off('change', onChange);
|
3411
|
+
cm.off('cursorActivity', onCursorActivity);
|
3412
|
+
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
|
3413
|
+
}
|
3414
|
+
if (!inReplay && vim.insertModeRepeat > 1) {
|
3415
|
+
// Perform insert mode repeat for commands like 3,a and 3,o.
|
3416
|
+
repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,
|
3417
|
+
true /** repeatForInsert */);
|
3418
|
+
vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
|
3419
|
+
}
|
3420
|
+
delete vim.insertModeRepeat;
|
3227
3421
|
cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
|
3228
3422
|
cm.setOption('keyMap', 'vim');
|
3423
|
+
cm.toggleOverwrite(false); // exit replace mode if we were in it.
|
3229
3424
|
}
|
3230
3425
|
|
3231
3426
|
CodeMirror.keyMap['vim-insert'] = {
|
@@ -3244,6 +3439,11 @@
|
|
3244
3439
|
fallthrough: ['default']
|
3245
3440
|
};
|
3246
3441
|
|
3442
|
+
CodeMirror.keyMap['vim-replace'] = {
|
3443
|
+
'Backspace': 'goCharLeft',
|
3444
|
+
fallthrough: ['vim-insert']
|
3445
|
+
};
|
3446
|
+
|
3247
3447
|
function parseRegisterToKeyBuffer(macroModeState, registerName) {
|
3248
3448
|
var match, key;
|
3249
3449
|
var register = getVimGlobalState().registerController.getRegister(registerName);
|
@@ -3251,7 +3451,7 @@
|
|
3251
3451
|
var macroKeyBuffer = macroModeState.macroKeyBuffer;
|
3252
3452
|
emptyMacroKeyBuffer(macroModeState);
|
3253
3453
|
do {
|
3254
|
-
match =
|
3454
|
+
match = (/<\w+-.+?>|<\w+>|./).exec(text);
|
3255
3455
|
if(match === null)break;
|
3256
3456
|
key = match[0];
|
3257
3457
|
text = text.substring(match.index + key.length);
|
@@ -3285,24 +3485,145 @@
|
|
3285
3485
|
macroKeyBuffer.push(key);
|
3286
3486
|
}
|
3287
3487
|
|
3288
|
-
|
3289
|
-
|
3290
|
-
|
3291
|
-
|
3488
|
+
/**
|
3489
|
+
* Listens for changes made in insert mode.
|
3490
|
+
* Should only be active in insert mode.
|
3491
|
+
*/
|
3492
|
+
function onChange(_cm, changeObj) {
|
3493
|
+
var macroModeState = getVimGlobalState().macroModeState;
|
3494
|
+
var lastChange = macroModeState.lastInsertModeChanges;
|
3495
|
+
while (changeObj) {
|
3496
|
+
lastChange.expectCursorActivityForChange = true;
|
3497
|
+
if (changeObj.origin == '+input' || changeObj.origin == 'paste'
|
3498
|
+
|| changeObj.origin === undefined /* only in testing */) {
|
3499
|
+
var text = changeObj.text.join('\n');
|
3500
|
+
lastChange.changes.push(text);
|
3501
|
+
}
|
3502
|
+
// Change objects may be chained with next.
|
3503
|
+
changeObj = changeObj.next;
|
3504
|
+
}
|
3292
3505
|
}
|
3293
3506
|
|
3294
|
-
|
3295
|
-
|
3296
|
-
|
3297
|
-
|
3298
|
-
|
3299
|
-
|
3507
|
+
/**
|
3508
|
+
* Listens for any kind of cursor activity on CodeMirror.
|
3509
|
+
* - For tracking cursor activity in insert mode.
|
3510
|
+
* - Should only be active in insert mode.
|
3511
|
+
*/
|
3512
|
+
function onCursorActivity() {
|
3513
|
+
var macroModeState = getVimGlobalState().macroModeState;
|
3514
|
+
var lastChange = macroModeState.lastInsertModeChanges;
|
3515
|
+
if (lastChange.expectCursorActivityForChange) {
|
3516
|
+
lastChange.expectCursorActivityForChange = false;
|
3517
|
+
} else {
|
3518
|
+
// Cursor moved outside the context of an edit. Reset the change.
|
3519
|
+
lastChange.changes = [];
|
3520
|
+
}
|
3521
|
+
}
|
3522
|
+
|
3523
|
+
/** Wrapper for special keys pressed in insert mode */
|
3524
|
+
function InsertModeKey(keyName) {
|
3525
|
+
this.keyName = keyName;
|
3526
|
+
}
|
3527
|
+
|
3528
|
+
/**
|
3529
|
+
* Handles raw key down events from the text area.
|
3530
|
+
* - Should only be active in insert mode.
|
3531
|
+
* - For recording deletes in insert mode.
|
3532
|
+
*/
|
3533
|
+
function onKeyEventTargetKeyDown(e) {
|
3534
|
+
var macroModeState = getVimGlobalState().macroModeState;
|
3535
|
+
var lastChange = macroModeState.lastInsertModeChanges;
|
3536
|
+
var keyName = CodeMirror.keyName(e);
|
3537
|
+
function onKeyFound() {
|
3538
|
+
lastChange.changes.push(new InsertModeKey(keyName));
|
3539
|
+
return true;
|
3540
|
+
}
|
3541
|
+
if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
|
3542
|
+
CodeMirror.lookupKey(keyName, ['vim-insert'], onKeyFound);
|
3543
|
+
}
|
3544
|
+
}
|
3545
|
+
|
3546
|
+
/**
|
3547
|
+
* Repeats the last edit, which includes exactly 1 command and at most 1
|
3548
|
+
* insert. Operator and motion commands are read from lastEditInputState,
|
3549
|
+
* while action commands are read from lastEditActionCommand.
|
3550
|
+
*
|
3551
|
+
* If repeatForInsert is true, then the function was called by
|
3552
|
+
* exitInsertMode to repeat the insert mode changes the user just made. The
|
3553
|
+
* corresponding enterInsertMode call was made with a count.
|
3554
|
+
*/
|
3555
|
+
function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
|
3556
|
+
var macroModeState = getVimGlobalState().macroModeState;
|
3557
|
+
macroModeState.inReplay = true;
|
3558
|
+
var isAction = !!vim.lastEditActionCommand;
|
3559
|
+
var cachedInputState = vim.inputState;
|
3560
|
+
function repeatCommand() {
|
3561
|
+
if (isAction) {
|
3562
|
+
commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);
|
3563
|
+
} else {
|
3564
|
+
commandDispatcher.evalInput(cm, vim);
|
3565
|
+
}
|
3566
|
+
}
|
3567
|
+
function repeatInsert(repeat) {
|
3568
|
+
if (macroModeState.lastInsertModeChanges.changes.length > 0) {
|
3569
|
+
// For some reason, repeat cw in desktop VIM will does not repeat
|
3570
|
+
// insert mode changes. Will conform to that behavior.
|
3571
|
+
repeat = !vim.lastEditActionCommand ? 1 : repeat;
|
3572
|
+
repeatLastInsertModeChanges(cm, repeat, macroModeState);
|
3573
|
+
}
|
3574
|
+
}
|
3575
|
+
vim.inputState = vim.lastEditInputState;
|
3576
|
+
if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {
|
3577
|
+
// o and O repeat have to be interlaced with insert repeats so that the
|
3578
|
+
// insertions appear on separate lines instead of the last line.
|
3579
|
+
for (var i = 0; i < repeat; i++) {
|
3580
|
+
repeatCommand();
|
3581
|
+
repeatInsert(1);
|
3582
|
+
}
|
3583
|
+
} else {
|
3584
|
+
if (!repeatForInsert) {
|
3585
|
+
// Hack to get the cursor to end up at the right place. If I is
|
3586
|
+
// repeated in insert mode repeat, cursor will be 1 insert
|
3587
|
+
// change set left of where it should be.
|
3588
|
+
repeatCommand();
|
3589
|
+
}
|
3590
|
+
repeatInsert(repeat);
|
3591
|
+
}
|
3592
|
+
vim.inputState = cachedInputState;
|
3593
|
+
if (vim.insertMode && !repeatForInsert) {
|
3594
|
+
// Don't exit insert mode twice. If repeatForInsert is set, then we
|
3595
|
+
// were called by an exitInsertMode call lower on the stack.
|
3596
|
+
exitInsertMode(cm);
|
3597
|
+
}
|
3598
|
+
macroModeState.inReplay = false;
|
3300
3599
|
};
|
3301
3600
|
|
3601
|
+
function repeatLastInsertModeChanges(cm, repeat, macroModeState) {
|
3602
|
+
var lastChange = macroModeState.lastInsertModeChanges;
|
3603
|
+
function keyHandler(binding) {
|
3604
|
+
if (typeof binding == 'string') {
|
3605
|
+
CodeMirror.commands[binding](cm);
|
3606
|
+
} else {
|
3607
|
+
binding(cm);
|
3608
|
+
}
|
3609
|
+
return true;
|
3610
|
+
}
|
3611
|
+
for (var i = 0; i < repeat; i++) {
|
3612
|
+
for (var j = 0; j < lastChange.changes.length; j++) {
|
3613
|
+
var change = lastChange.changes[j];
|
3614
|
+
if (change instanceof InsertModeKey) {
|
3615
|
+
CodeMirror.lookupKey(change.keyName, ['vim-insert'], keyHandler);
|
3616
|
+
} else {
|
3617
|
+
var cur = cm.getCursor();
|
3618
|
+
cm.replaceRange(change, cur, cur);
|
3619
|
+
}
|
3620
|
+
}
|
3621
|
+
}
|
3622
|
+
}
|
3623
|
+
|
3302
3624
|
return vimApi;
|
3303
3625
|
};
|
3304
3626
|
// Initialize Vim and make it available as an API.
|
3305
|
-
|
3306
|
-
CodeMirror.Vim = vim;
|
3627
|
+
CodeMirror.Vim = Vim();
|
3307
3628
|
}
|
3308
3629
|
)();
|