codemirror-rails 4.7 → 4.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +229 -137
- data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +1 -1
- data/vendor/assets/javascripts/codemirror/addons/hint/javascript-hint.js +9 -9
- data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +9 -1
- data/vendor/assets/javascripts/codemirror/addons/mode/loadmode.js +19 -16
- data/vendor/assets/javascripts/codemirror/addons/mode/overlay.js +3 -3
- data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +19 -28
- data/vendor/assets/javascripts/codemirror/keymaps/sublime.js +14 -15
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +694 -752
- data/vendor/assets/javascripts/codemirror/modes/clike.js +15 -0
- data/vendor/assets/javascripts/codemirror/modes/css.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/dockerfile.js +80 -0
- data/vendor/assets/javascripts/codemirror/modes/gfm.js +2 -1
- data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +2 -2
- data/vendor/assets/javascripts/codemirror/modes/idl.js +290 -0
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +62 -55
- data/vendor/assets/javascripts/codemirror/modes/sparql.js +19 -5
- data/vendor/assets/javascripts/codemirror/modes/sql.js +0 -2
- data/vendor/assets/javascripts/codemirror/modes/stex.js +184 -193
- data/vendor/assets/javascripts/codemirror/modes/yaml.js +6 -1
- data/vendor/assets/stylesheets/codemirror.css +11 -2
- data/vendor/assets/stylesheets/codemirror/themes/3024-day.css +1 -1
- data/vendor/assets/stylesheets/codemirror/themes/solarized.css +0 -5
- metadata +3 -1
@@ -71,7 +71,7 @@
|
|
71
71
|
};
|
72
72
|
var closingBrackets = "";
|
73
73
|
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
|
74
|
-
|
74
|
+
closingBrackets += right;
|
75
75
|
map["'" + left + "'"] = function(cm) {
|
76
76
|
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
77
77
|
var ranges = cm.listSelections(), type, next;
|
@@ -93,7 +93,7 @@
|
|
93
93
|
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
94
94
|
|
95
95
|
function getCompletions(token, context, keywords, options) {
|
96
|
-
var found = [], start = token.string;
|
96
|
+
var found = [], start = token.string, global = options && options.globalScope || window;
|
97
97
|
function maybeAdd(str) {
|
98
98
|
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
|
99
99
|
}
|
@@ -112,28 +112,28 @@
|
|
112
112
|
if (options && options.additionalContext)
|
113
113
|
base = options.additionalContext[obj.string];
|
114
114
|
if (!options || options.useGlobalScope !== false)
|
115
|
-
base = base ||
|
115
|
+
base = base || global[obj.string];
|
116
116
|
} else if (obj.type == "string") {
|
117
117
|
base = "";
|
118
118
|
} else if (obj.type == "atom") {
|
119
119
|
base = 1;
|
120
120
|
} else if (obj.type == "function") {
|
121
|
-
if (
|
122
|
-
(typeof
|
123
|
-
base =
|
124
|
-
else if (
|
125
|
-
base =
|
121
|
+
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
122
|
+
(typeof global.jQuery == 'function'))
|
123
|
+
base = global.jQuery();
|
124
|
+
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
|
125
|
+
base = global._();
|
126
126
|
}
|
127
127
|
while (base != null && context.length)
|
128
128
|
base = base[context.pop().string];
|
129
129
|
if (base != null) gatherCompletions(base);
|
130
130
|
} else {
|
131
|
-
// If not, just look in the
|
131
|
+
// If not, just look in the global object and any local scope
|
132
132
|
// (reading into JS mode internals to get at the local and global variables)
|
133
133
|
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
134
134
|
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
|
135
135
|
if (!options || options.useGlobalScope !== false)
|
136
|
-
gatherCompletions(
|
136
|
+
gatherCompletions(global);
|
137
137
|
forEach(keywords, maybeAdd);
|
138
138
|
}
|
139
139
|
return found;
|
@@ -18,10 +18,17 @@
|
|
18
18
|
var quote = (options && options.quoteChar) || '"';
|
19
19
|
if (!tags) return;
|
20
20
|
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
21
|
+
if (/^<\/?$/.test(token.string) && token.end == cur.ch) {
|
22
|
+
var nextToken = cm.getTokenAt(Pos(cur.line, cur.ch + 1));
|
23
|
+
if (nextToken.start == cur.ch && /\btag\b/.test(nextToken.type))
|
24
|
+
token = nextToken;
|
25
|
+
}
|
21
26
|
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
22
27
|
if (inner.mode.name != "xml") return;
|
23
28
|
var result = [], replaceToken = false, prefix;
|
24
|
-
var tag = /\btag\b/.test(token.type)
|
29
|
+
var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
|
30
|
+
var tagName = tag && /^\w/.test(token.string), tagStart;
|
31
|
+
|
25
32
|
if (tagName) {
|
26
33
|
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
|
27
34
|
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
|
@@ -31,6 +38,7 @@
|
|
31
38
|
} else if (tag && token.string == "</") {
|
32
39
|
tagType = "close";
|
33
40
|
}
|
41
|
+
|
34
42
|
if (!tag && !inner.state.tagName || tagType) {
|
35
43
|
if (tagName)
|
36
44
|
prefix = token.string;
|
@@ -3,12 +3,12 @@
|
|
3
3
|
|
4
4
|
(function(mod) {
|
5
5
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
6
|
-
mod(require("../../lib/codemirror"));
|
6
|
+
mod(require("../../lib/codemirror"), "cjs");
|
7
7
|
else if (typeof define == "function" && define.amd) // AMD
|
8
|
-
define(["../../lib/codemirror"], mod);
|
8
|
+
define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
|
9
9
|
else // Plain browser env
|
10
|
-
mod(CodeMirror);
|
11
|
-
})(function(CodeMirror) {
|
10
|
+
mod(CodeMirror, "plain");
|
11
|
+
})(function(CodeMirror, env) {
|
12
12
|
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
|
13
13
|
|
14
14
|
var loading = {};
|
@@ -35,21 +35,24 @@
|
|
35
35
|
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
|
36
36
|
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
|
37
37
|
|
38
|
-
var
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if (CodeMirror.modes.hasOwnProperty(mode)) {
|
46
|
-
clearInterval(poll);
|
47
|
-
loading[mode] = null;
|
38
|
+
var file = CodeMirror.modeURL.replace(/%N/g, mode);
|
39
|
+
if (env == "plain") {
|
40
|
+
var script = document.createElement("script");
|
41
|
+
script.src = file;
|
42
|
+
var others = document.getElementsByTagName("script")[0];
|
43
|
+
var list = loading[mode] = [cont];
|
44
|
+
CodeMirror.on(script, "load", function() {
|
48
45
|
ensureDeps(mode, function() {
|
49
46
|
for (var i = 0; i < list.length; ++i) list[i]();
|
50
47
|
});
|
51
|
-
}
|
52
|
-
|
48
|
+
});
|
49
|
+
others.parentNode.insertBefore(script, others);
|
50
|
+
} else if (env == "cjs") {
|
51
|
+
require(file);
|
52
|
+
cont();
|
53
|
+
} else if (env == "amd") {
|
54
|
+
requirejs([file], cont);
|
55
|
+
}
|
53
56
|
};
|
54
57
|
|
55
58
|
CodeMirror.autoLoadMode = function(instance, mode) {
|
@@ -28,7 +28,7 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
|
|
28
28
|
overlay: CodeMirror.startState(overlay),
|
29
29
|
basePos: 0, baseCur: null,
|
30
30
|
overlayPos: 0, overlayCur: null,
|
31
|
-
|
31
|
+
streamSeen: null
|
32
32
|
};
|
33
33
|
},
|
34
34
|
copyState: function(state) {
|
@@ -41,9 +41,9 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
|
|
41
41
|
},
|
42
42
|
|
43
43
|
token: function(stream, state) {
|
44
|
-
if (stream
|
44
|
+
if (stream != state.streamSeen ||
|
45
45
|
Math.min(state.basePos, state.overlayPos) < stream.start) {
|
46
|
-
state.
|
46
|
+
state.streamSeen = stream;
|
47
47
|
state.basePos = state.overlayPos = stream.start;
|
48
48
|
}
|
49
49
|
|
@@ -256,7 +256,7 @@
|
|
256
256
|
|
257
257
|
// Actual keymap
|
258
258
|
|
259
|
-
var keyMap = CodeMirror.keyMap.emacs = {
|
259
|
+
var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
|
260
260
|
"Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
|
261
261
|
"Ctrl-K": repeated(function(cm) {
|
262
262
|
var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
|
@@ -353,27 +353,7 @@
|
|
353
353
|
"Alt-/": "autocomplete",
|
354
354
|
"Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
|
355
355
|
|
356
|
-
"Alt-G": function(cm) {
|
357
|
-
"Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
|
358
|
-
"Ctrl-Q": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-Q");},
|
359
|
-
"Ctrl-U": addPrefixMap
|
360
|
-
};
|
361
|
-
|
362
|
-
CodeMirror.keyMap["emacs-Ctrl-X"] = {
|
363
|
-
"Tab": function(cm) {
|
364
|
-
cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
|
365
|
-
},
|
366
|
-
"Ctrl-X": function(cm) {
|
367
|
-
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
|
368
|
-
},
|
369
|
-
|
370
|
-
"Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close",
|
371
|
-
"Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
|
372
|
-
auto: "emacs", nofallthrough: true, disableInput: true
|
373
|
-
};
|
374
|
-
|
375
|
-
CodeMirror.keyMap["emacs-Alt-G"] = {
|
376
|
-
"G": function(cm) {
|
356
|
+
"Alt-G G": function(cm) {
|
377
357
|
var prefix = getPrefix(cm, true);
|
378
358
|
if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
|
379
359
|
|
@@ -383,13 +363,24 @@
|
|
383
363
|
cm.setCursor(num - 1);
|
384
364
|
});
|
385
365
|
},
|
386
|
-
auto: "emacs", nofallthrough: true, disableInput: true
|
387
|
-
};
|
388
366
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
367
|
+
"Ctrl-X Tab": function(cm) {
|
368
|
+
cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
|
369
|
+
},
|
370
|
+
"Ctrl-X Ctrl-X": function(cm) {
|
371
|
+
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
|
372
|
+
},
|
373
|
+
"Ctrl-X Ctrl-S": "save",
|
374
|
+
"Ctrl-X Ctrl-W": "save",
|
375
|
+
"Ctrl-X S": "saveAll",
|
376
|
+
"Ctrl-X F": "open",
|
377
|
+
"Ctrl-X U": repeated("undo"),
|
378
|
+
"Ctrl-X K": "close",
|
379
|
+
"Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
|
380
|
+
|
381
|
+
"Ctrl-Q Tab": repeated("insertTab"),
|
382
|
+
"Ctrl-U": addPrefixMap
|
383
|
+
});
|
393
384
|
|
394
385
|
var prefixMap = {"Ctrl-G": clearPrefix};
|
395
386
|
function regPrefix(d) {
|
@@ -386,9 +386,7 @@
|
|
386
386
|
|
387
387
|
map["Alt-Q"] = "wrapLines";
|
388
388
|
|
389
|
-
var
|
390
|
-
|
391
|
-
map[ctrl + "K"] = function(cm) {cm.setOption("keyMap", "sublime-Ctrl-K");};
|
389
|
+
var cK = ctrl + "K ";
|
392
390
|
|
393
391
|
function modifyWordOrSelection(cm, mod) {
|
394
392
|
cm.operation(function() {
|
@@ -409,9 +407,9 @@
|
|
409
407
|
});
|
410
408
|
}
|
411
409
|
|
412
|
-
|
410
|
+
map[cK + ctrl + "Backspace"] = "delLineLeft";
|
413
411
|
|
414
|
-
cmds[
|
412
|
+
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
|
415
413
|
cm.operation(function() {
|
416
414
|
var ranges = cm.listSelections();
|
417
415
|
for (var i = ranges.length - 1; i >= 0; i--)
|
@@ -420,22 +418,22 @@
|
|
420
418
|
});
|
421
419
|
};
|
422
420
|
|
423
|
-
cmds[
|
421
|
+
cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) {
|
424
422
|
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
|
425
423
|
};
|
426
|
-
cmds[
|
424
|
+
cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) {
|
427
425
|
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
|
428
426
|
};
|
429
427
|
|
430
|
-
cmds[
|
428
|
+
cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) {
|
431
429
|
if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
|
432
430
|
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
433
431
|
};
|
434
|
-
cmds[
|
432
|
+
cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) {
|
435
433
|
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
436
434
|
if (found) cm.setSelection(cm.getCursor(), found);
|
437
435
|
};
|
438
|
-
cmds[
|
436
|
+
cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) {
|
439
437
|
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
440
438
|
if (found) {
|
441
439
|
var from = cm.getCursor(), to = found;
|
@@ -444,7 +442,7 @@
|
|
444
442
|
cm.replaceRange("", from, to);
|
445
443
|
}
|
446
444
|
};
|
447
|
-
cmds[
|
445
|
+
cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) {
|
448
446
|
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
449
447
|
if (found) {
|
450
448
|
cm.state.sublimeMark.clear();
|
@@ -452,13 +450,13 @@
|
|
452
450
|
cm.setCursor(found);
|
453
451
|
}
|
454
452
|
};
|
455
|
-
cmds[
|
453
|
+
cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) {
|
456
454
|
if (cm.state.sublimeKilled != null)
|
457
455
|
cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
|
458
456
|
};
|
459
457
|
|
460
|
-
|
461
|
-
cmds[
|
458
|
+
map[cK + ctrl + "G"] = "clearBookmarks";
|
459
|
+
cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) {
|
462
460
|
var pos = cm.cursorCoords(null, "local");
|
463
461
|
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
|
464
462
|
};
|
@@ -530,7 +528,7 @@
|
|
530
528
|
|
531
529
|
map["Shift-" + ctrl + "["] = "fold";
|
532
530
|
map["Shift-" + ctrl + "]"] = "unfold";
|
533
|
-
|
531
|
+
map[cK + ctrl + "0"] = map[cK + ctrl + "j"] = "unfoldAll";
|
534
532
|
|
535
533
|
map[ctrl + "I"] = "findIncremental";
|
536
534
|
map["Shift-" + ctrl + "I"] = "findIncrementalReverse";
|
@@ -538,4 +536,5 @@
|
|
538
536
|
map["F3"] = "findNext";
|
539
537
|
map["Shift-F3"] = "findPrev";
|
540
538
|
|
539
|
+
CodeMirror.normalizeKeyMap(map);
|
541
540
|
});
|
@@ -156,16 +156,22 @@
|
|
156
156
|
{ keys: 'c', type: 'operator', operator: 'change' },
|
157
157
|
{ keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},
|
158
158
|
{ keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},
|
159
|
-
{ keys: 'g~', type: 'operator', operator: '
|
159
|
+
{ keys: 'g~', type: 'operator', operator: 'changeCase' },
|
160
|
+
{ keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },
|
161
|
+
{ keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
|
160
162
|
{ keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
|
161
163
|
{ keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
|
162
164
|
// Operator-Motion dual commands
|
163
165
|
{ keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
|
164
166
|
{ keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
|
165
|
-
{ keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true },
|
166
|
-
{ keys: '
|
167
|
-
{ keys: '
|
168
|
-
{ keys: '
|
167
|
+
{ keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
168
|
+
{ keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},
|
169
|
+
{ keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
170
|
+
{ keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},
|
171
|
+
{ keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
172
|
+
{ keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},
|
173
|
+
{ keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},
|
174
|
+
{ keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},
|
169
175
|
{ keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
|
170
176
|
// Actions
|
171
177
|
{ keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
|
@@ -176,7 +182,8 @@
|
|
176
182
|
{ keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
|
177
183
|
{ keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
|
178
184
|
{ keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
|
179
|
-
{ keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank' }
|
185
|
+
{ keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },
|
186
|
+
{ keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
|
180
187
|
{ keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
|
181
188
|
{ keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
|
182
189
|
{ keys: 'v', type: 'action', action: 'toggleVisualMode' },
|
@@ -192,8 +199,8 @@
|
|
192
199
|
// Handle Replace-mode as a special case of insert mode.
|
193
200
|
{ keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
|
194
201
|
{ keys: 'u', type: 'action', action: 'undo', context: 'normal' },
|
195
|
-
{ keys: 'u', type: '
|
196
|
-
{ keys: 'U',type: '
|
202
|
+
{ keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },
|
203
|
+
{ keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },
|
197
204
|
{ keys: '<C-r>', type: 'action', action: 'redo' },
|
198
205
|
{ keys: 'm<character>', type: 'action', action: 'setMark' },
|
199
206
|
{ keys: '"<character>', type: 'action', action: 'setRegister' },
|
@@ -226,70 +233,92 @@
|
|
226
233
|
var specialKey = {Enter:'CR',Backspace:'BS',Delete:'Del'};
|
227
234
|
var mac = /Mac/.test(navigator.platform);
|
228
235
|
var Vim = function() {
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
CodeMirror.e_stop(e);
|
272
|
-
}
|
273
|
-
}
|
274
|
-
if (val) {
|
275
|
-
cm.setOption('keyMap', 'vim');
|
276
|
-
cm.setOption('disableInput', true);
|
277
|
-
cm.setOption('showCursorWhenSelecting', false);
|
278
|
-
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
279
|
-
cm.on('cursorActivity', onCursorActivity);
|
280
|
-
maybeInitVimState(cm);
|
281
|
-
CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
282
|
-
cm.on('keypress', handleKeyPress);
|
283
|
-
cm.on('keydown', handleKeyDown);
|
284
|
-
} else if (cm.state.vim) {
|
285
|
-
cm.setOption('keyMap', 'default');
|
286
|
-
cm.setOption('disableInput', false);
|
287
|
-
cm.off('cursorActivity', onCursorActivity);
|
288
|
-
CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
289
|
-
cm.state.vim = null;
|
290
|
-
cm.off('keypress', handleKeyPress);
|
291
|
-
cm.off('keydown', handleKeyDown);
|
236
|
+
function lookupKey(e) {
|
237
|
+
var keyCode = e.keyCode;
|
238
|
+
if (modifierCodes.indexOf(keyCode) != -1) { return; }
|
239
|
+
var hasModifier = e.ctrlKey || e.metaKey;
|
240
|
+
var key = CodeMirror.keyNames[keyCode];
|
241
|
+
key = specialKey[key] || key;
|
242
|
+
var name = '';
|
243
|
+
if (e.ctrlKey) { name += 'C-'; }
|
244
|
+
if (e.altKey) { name += 'A-'; }
|
245
|
+
if (mac && e.metaKey || (!hasModifier && e.shiftKey) && key.length < 2) {
|
246
|
+
// Shift key bindings can only specified for special characters.
|
247
|
+
return;
|
248
|
+
} else if (e.shiftKey && !/^[A-Za-z]$/.test(key)) {
|
249
|
+
name += 'S-';
|
250
|
+
}
|
251
|
+
if (key.length == 1) { key = key.toLowerCase(); }
|
252
|
+
name += key;
|
253
|
+
if (name.length > 1) { name = '<' + name + '>'; }
|
254
|
+
return name;
|
255
|
+
}
|
256
|
+
// Keys with modifiers are handled using keydown due to limitations of
|
257
|
+
// keypress event.
|
258
|
+
function handleKeyDown(cm, e) {
|
259
|
+
var name = lookupKey(e);
|
260
|
+
if (!name) { return; }
|
261
|
+
|
262
|
+
CodeMirror.signal(cm, 'vim-keypress', name);
|
263
|
+
if (CodeMirror.Vim.handleKey(cm, name, 'user')) {
|
264
|
+
CodeMirror.e_stop(e);
|
265
|
+
}
|
266
|
+
}
|
267
|
+
// Keys without modifiers are handled using keypress to work best with
|
268
|
+
// non-standard keyboard layouts.
|
269
|
+
function handleKeyPress(cm, e) {
|
270
|
+
var code = e.charCode || e.keyCode;
|
271
|
+
if (e.ctrlKey || e.metaKey || e.altKey ||
|
272
|
+
e.shiftKey && code < 32) { return; }
|
273
|
+
var name = String.fromCharCode(code);
|
274
|
+
|
275
|
+
CodeMirror.signal(cm, 'vim-keypress', name);
|
276
|
+
if (CodeMirror.Vim.handleKey(cm, name, 'user')) {
|
277
|
+
CodeMirror.e_stop(e);
|
292
278
|
}
|
279
|
+
}
|
280
|
+
|
281
|
+
function enterVimMode(cm) {
|
282
|
+
cm.setOption('disableInput', true);
|
283
|
+
cm.setOption('showCursorWhenSelecting', false);
|
284
|
+
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
285
|
+
cm.on('cursorActivity', onCursorActivity);
|
286
|
+
maybeInitVimState(cm);
|
287
|
+
CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
288
|
+
cm.on('keypress', handleKeyPress);
|
289
|
+
cm.on('keydown', handleKeyDown);
|
290
|
+
}
|
291
|
+
|
292
|
+
function leaveVimMode(cm) {
|
293
|
+
cm.setOption('disableInput', false);
|
294
|
+
cm.off('cursorActivity', onCursorActivity);
|
295
|
+
CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
296
|
+
cm.state.vim = null;
|
297
|
+
cm.off('keypress', handleKeyPress);
|
298
|
+
cm.off('keydown', handleKeyDown);
|
299
|
+
}
|
300
|
+
|
301
|
+
function detachVimMap(cm, next) {
|
302
|
+
if (this == CodeMirror.keyMap.vim)
|
303
|
+
CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
|
304
|
+
|
305
|
+
if (!next || next.attach != attachVimMap)
|
306
|
+
leaveVimMode(cm, false);
|
307
|
+
}
|
308
|
+
function attachVimMap(cm, prev) {
|
309
|
+
if (this == CodeMirror.keyMap.vim)
|
310
|
+
CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
|
311
|
+
|
312
|
+
if (!prev || prev.attach != attachVimMap)
|
313
|
+
enterVimMode(cm);
|
314
|
+
}
|
315
|
+
|
316
|
+
// Deprecated, simply setting the keymap works again.
|
317
|
+
CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
|
318
|
+
if (val && cm.getOption("keyMap") != "vim")
|
319
|
+
cm.setOption("keyMap", "vim");
|
320
|
+
else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))
|
321
|
+
cm.setOption("keyMap", "default");
|
293
322
|
});
|
294
323
|
function getOnPasteFn(cm) {
|
295
324
|
var vim = cm.state.vim;
|
@@ -533,8 +562,8 @@
|
|
533
562
|
visualBlock: false,
|
534
563
|
lastSelection: null,
|
535
564
|
lastPastedText: null,
|
536
|
-
|
537
|
-
|
565
|
+
sel: {
|
566
|
+
}
|
538
567
|
};
|
539
568
|
}
|
540
569
|
return cm.state.vim;
|
@@ -674,7 +703,7 @@
|
|
674
703
|
} else {
|
675
704
|
commandDispatcher.processCommand(cm, vim, command);
|
676
705
|
}
|
677
|
-
return
|
706
|
+
return true;
|
678
707
|
}
|
679
708
|
|
680
709
|
function handleKeyNonInsertMode() {
|
@@ -705,8 +734,18 @@
|
|
705
734
|
return true;
|
706
735
|
}
|
707
736
|
|
708
|
-
|
709
|
-
|
737
|
+
return cm.operation(function() {
|
738
|
+
cm.curOp.isVimOp = true;
|
739
|
+
try {
|
740
|
+
if (vim.insertMode) { return handleKeyInsertMode(); }
|
741
|
+
else { return handleKeyNonInsertMode(); }
|
742
|
+
} catch (e) {
|
743
|
+
// clear VIM state in case it's in a bad state.
|
744
|
+
cm.state.vim = undefined;
|
745
|
+
maybeInitVimState(cm);
|
746
|
+
throw e;
|
747
|
+
}
|
748
|
+
});
|
710
749
|
},
|
711
750
|
handleEx: function(cm, input) {
|
712
751
|
exCommandDispatcher.processCommand(cm, input);
|
@@ -960,10 +999,12 @@
|
|
960
999
|
break;
|
961
1000
|
case 'search':
|
962
1001
|
this.processSearch(cm, vim, command);
|
1002
|
+
clearInputState(cm);
|
963
1003
|
break;
|
964
1004
|
case 'ex':
|
965
1005
|
case 'keyToEx':
|
966
1006
|
this.processEx(cm, vim, command);
|
1007
|
+
clearInputState(cm);
|
967
1008
|
break;
|
968
1009
|
default:
|
969
1010
|
break;
|
@@ -1205,13 +1246,13 @@
|
|
1205
1246
|
var operator = inputState.operator;
|
1206
1247
|
var operatorArgs = inputState.operatorArgs || {};
|
1207
1248
|
var registerName = inputState.registerName;
|
1208
|
-
var
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
var
|
1213
|
-
var
|
1214
|
-
var
|
1249
|
+
var sel = vim.sel;
|
1250
|
+
// TODO: Make sure cm and vim selections are identical outside visual mode.
|
1251
|
+
var origHead = copyCursor(vim.visualMode ? sel.head: cm.getCursor('head'));
|
1252
|
+
var origAnchor = copyCursor(vim.visualMode ? sel.anchor : cm.getCursor('anchor'));
|
1253
|
+
var oldHead = copyCursor(origHead);
|
1254
|
+
var oldAnchor = copyCursor(origAnchor);
|
1255
|
+
var newHead, newAnchor;
|
1215
1256
|
var repeat;
|
1216
1257
|
if (operator) {
|
1217
1258
|
this.recordLastEdit(vim, inputState);
|
@@ -1238,7 +1279,7 @@
|
|
1238
1279
|
motionArgs.repeat = repeat;
|
1239
1280
|
clearInputState(cm);
|
1240
1281
|
if (motion) {
|
1241
|
-
var motionResult = motions[motion](cm, motionArgs, vim);
|
1282
|
+
var motionResult = motions[motion](cm, origHead, motionArgs, vim);
|
1242
1283
|
vim.lastMotion = motions[motion];
|
1243
1284
|
if (!motionResult) {
|
1244
1285
|
return;
|
@@ -1251,159 +1292,139 @@
|
|
1251
1292
|
recordJumpPosition(cm, cachedCursor, motionResult);
|
1252
1293
|
delete jumpList.cachedCursor;
|
1253
1294
|
} else {
|
1254
|
-
recordJumpPosition(cm,
|
1295
|
+
recordJumpPosition(cm, origHead, motionResult);
|
1255
1296
|
}
|
1256
1297
|
}
|
1257
1298
|
if (motionResult instanceof Array) {
|
1258
|
-
|
1259
|
-
|
1299
|
+
newAnchor = motionResult[0];
|
1300
|
+
newHead = motionResult[1];
|
1260
1301
|
} else {
|
1261
|
-
|
1302
|
+
newHead = motionResult;
|
1262
1303
|
}
|
1263
1304
|
// TODO: Handle null returns from motion commands better.
|
1264
|
-
if (!
|
1265
|
-
|
1305
|
+
if (!newHead) {
|
1306
|
+
newHead = copyCursor(origHead);
|
1266
1307
|
}
|
1267
1308
|
if (vim.visualMode) {
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
// left or right
|
1272
|
-
var offset = 0;
|
1273
|
-
if (cursorIsBefore(selectionStart, selectionEnd) &&
|
1274
|
-
(cursorEqual(selectionStart, curEnd) ||
|
1275
|
-
cursorIsBefore(curEnd, selectionStart))) {
|
1276
|
-
// The end of the selection has moved from after the start to
|
1277
|
-
// before the start. We will shift the start right by 1.
|
1278
|
-
selectionStart.ch += 1;
|
1279
|
-
offset = -1;
|
1280
|
-
} else if (cursorIsBefore(selectionEnd, selectionStart) &&
|
1281
|
-
(cursorEqual(selectionStart, curEnd) ||
|
1282
|
-
cursorIsBefore(selectionStart, curEnd))) {
|
1283
|
-
// The opposite happened. We will shift the start left by 1.
|
1284
|
-
selectionStart.ch -= 1;
|
1285
|
-
offset = 1;
|
1286
|
-
}
|
1287
|
-
// in case of visual Block selectionStart and curEnd
|
1288
|
-
// may not be on the same line,
|
1289
|
-
// Also, In case of v_o this should not happen.
|
1290
|
-
if (!vim.visualBlock && !(motionResult instanceof Array)) {
|
1291
|
-
curEnd.ch += offset;
|
1292
|
-
}
|
1293
|
-
if (vim.lastHPos != Infinity) {
|
1294
|
-
vim.lastHPos = curEnd.ch;
|
1295
|
-
}
|
1296
|
-
selectionEnd = curEnd;
|
1297
|
-
selectionStart = (motionResult instanceof Array) ? curStart : selectionStart;
|
1298
|
-
if (vim.visualLine) {
|
1299
|
-
if (cursorIsBefore(selectionStart, selectionEnd)) {
|
1300
|
-
selectionStart.ch = 0;
|
1301
|
-
|
1302
|
-
var lastLine = cm.lastLine();
|
1303
|
-
if (selectionEnd.line > lastLine) {
|
1304
|
-
selectionEnd.line = lastLine;
|
1305
|
-
}
|
1306
|
-
selectionEnd.ch = lineLength(cm, selectionEnd.line);
|
1307
|
-
} else {
|
1308
|
-
selectionEnd.ch = 0;
|
1309
|
-
selectionStart.ch = lineLength(cm, selectionStart.line);
|
1310
|
-
}
|
1311
|
-
} else if (vim.visualBlock) {
|
1312
|
-
// Select a block and
|
1313
|
-
// return the diagonally opposite end.
|
1314
|
-
selectionStart = selectBlock(cm, selectionEnd);
|
1315
|
-
}
|
1316
|
-
if (!vim.visualBlock) {
|
1317
|
-
cm.setSelection(selectionStart, selectionEnd);
|
1309
|
+
newHead = clipCursorToContent(cm, newHead, vim.visualBlock);
|
1310
|
+
if (newAnchor) {
|
1311
|
+
newAnchor = clipCursorToContent(cm, newAnchor, true);
|
1318
1312
|
}
|
1313
|
+
newAnchor = newAnchor || oldAnchor;
|
1314
|
+
sel.anchor = newAnchor;
|
1315
|
+
sel.head = newHead;
|
1316
|
+
updateCmSelection(cm);
|
1319
1317
|
updateMark(cm, vim, '<',
|
1320
|
-
cursorIsBefore(
|
1321
|
-
:
|
1318
|
+
cursorIsBefore(newAnchor, newHead) ? newAnchor
|
1319
|
+
: newHead);
|
1322
1320
|
updateMark(cm, vim, '>',
|
1323
|
-
cursorIsBefore(
|
1324
|
-
:
|
1321
|
+
cursorIsBefore(newAnchor, newHead) ? newHead
|
1322
|
+
: newAnchor);
|
1325
1323
|
} else if (!operator) {
|
1326
|
-
|
1327
|
-
cm.setCursor(
|
1324
|
+
newHead = clipCursorToContent(cm, newHead);
|
1325
|
+
cm.setCursor(newHead.line, newHead.ch);
|
1328
1326
|
}
|
1329
1327
|
}
|
1330
|
-
|
1331
1328
|
if (operator) {
|
1332
|
-
|
1333
|
-
vim.lastMotion = null;
|
1334
|
-
var lastSelection = vim.lastSelection;
|
1335
|
-
operatorArgs.repeat = repeat; // Indent in visual mode needs this.
|
1336
|
-
if (vim.visualMode) {
|
1337
|
-
curStart = selectionStart;
|
1338
|
-
curEnd = selectionEnd;
|
1339
|
-
motionArgs.inclusive = true;
|
1340
|
-
operatorArgs.shouldMoveCursor = false;
|
1341
|
-
}
|
1342
|
-
// Swap start and end if motion was backward.
|
1343
|
-
if (curEnd && cursorIsBefore(curEnd, curStart)) {
|
1344
|
-
var tmp = curStart;
|
1345
|
-
curStart = curEnd;
|
1346
|
-
curEnd = tmp;
|
1347
|
-
inverted = true;
|
1348
|
-
} else if (!curEnd) {
|
1349
|
-
curEnd = copyCursor(curStart);
|
1350
|
-
}
|
1351
|
-
if (motionArgs.inclusive && !vim.visualMode) {
|
1352
|
-
// Move the selection end one to the right to include the last
|
1353
|
-
// character.
|
1354
|
-
curEnd.ch++;
|
1355
|
-
}
|
1356
|
-
if (operatorArgs.selOffset) {
|
1329
|
+
if (operatorArgs.lastSel) {
|
1357
1330
|
// Replaying a visual mode operation
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
if (
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
}
|
1376
|
-
cm.setSelections(selections);
|
1377
|
-
var blockSelected = true;
|
1331
|
+
newAnchor = oldAnchor;
|
1332
|
+
var lastSel = operatorArgs.lastSel;
|
1333
|
+
var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
|
1334
|
+
var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
|
1335
|
+
if (lastSel.visualLine) {
|
1336
|
+
// Linewise Visual mode: The same number of lines.
|
1337
|
+
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
|
1338
|
+
} else if (lastSel.visualBlock) {
|
1339
|
+
// Blockwise Visual mode: The same number of lines and columns.
|
1340
|
+
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
|
1341
|
+
} else if (lastSel.head.line == lastSel.anchor.line) {
|
1342
|
+
// Normal Visual mode within one line: The same number of characters.
|
1343
|
+
newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset);
|
1344
|
+
} else {
|
1345
|
+
// Normal Visual mode with several lines: The same number of lines, in the
|
1346
|
+
// last line the same number of characters as in the last line the last time.
|
1347
|
+
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
|
1378
1348
|
}
|
1349
|
+
vim.visualMode = true;
|
1350
|
+
vim.visualLine = lastSel.visualLine;
|
1351
|
+
vim.visualBlock = lastSel.visualBlock;
|
1352
|
+
sel = vim.sel = {
|
1353
|
+
anchor: newAnchor,
|
1354
|
+
head: newHead
|
1355
|
+
};
|
1356
|
+
updateCmSelection(cm);
|
1379
1357
|
} else if (vim.visualMode) {
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1358
|
+
operatorArgs.lastSel = {
|
1359
|
+
anchor: copyCursor(sel.anchor),
|
1360
|
+
head: copyCursor(sel.head),
|
1361
|
+
visualBlock: vim.visualBlock,
|
1362
|
+
visualLine: vim.visualLine
|
1363
|
+
};
|
1385
1364
|
}
|
1386
|
-
var
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1365
|
+
var curStart, curEnd, linewise, mode;
|
1366
|
+
var cmSel;
|
1367
|
+
if (vim.visualMode) {
|
1368
|
+
// Init visual op
|
1369
|
+
curStart = cursorMin(sel.head, sel.anchor);
|
1370
|
+
curEnd = cursorMax(sel.head, sel.anchor);
|
1371
|
+
linewise = vim.visualLine || operatorArgs.linewise;
|
1372
|
+
mode = vim.visualBlock ? 'block' :
|
1373
|
+
linewise ? 'line' :
|
1374
|
+
'char';
|
1375
|
+
cmSel = makeCmSelection(cm, {
|
1376
|
+
anchor: curStart,
|
1377
|
+
head: curEnd
|
1378
|
+
}, mode);
|
1379
|
+
if (linewise) {
|
1380
|
+
var ranges = cmSel.ranges;
|
1381
|
+
if (mode == 'block') {
|
1382
|
+
// Linewise operators in visual block mode extend to end of line
|
1383
|
+
for (var i = 0; i < ranges.length; i++) {
|
1384
|
+
ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
|
1385
|
+
}
|
1386
|
+
} else if (mode == 'line') {
|
1387
|
+
ranges[0].head = Pos(ranges[0].head.line + 1, 0);
|
1388
|
+
}
|
1389
|
+
}
|
1390
|
+
} else {
|
1391
|
+
// Init motion op
|
1392
|
+
curStart = copyCursor(newAnchor || oldAnchor);
|
1393
|
+
curEnd = copyCursor(newHead || oldHead);
|
1394
|
+
if (cursorIsBefore(curEnd, curStart)) {
|
1395
|
+
var tmp = curStart;
|
1396
|
+
curStart = curEnd;
|
1397
|
+
curEnd = tmp;
|
1398
|
+
}
|
1399
|
+
linewise = motionArgs.linewise || operatorArgs.linewise;
|
1400
|
+
if (linewise) {
|
1401
|
+
// Expand selection to entire line.
|
1402
|
+
expandSelectionToLine(cm, curStart, curEnd);
|
1403
|
+
} else if (motionArgs.forward) {
|
1404
|
+
// Clip to trailing newlines only if the motion goes forward.
|
1405
|
+
clipToLine(cm, curStart, curEnd);
|
1406
|
+
}
|
1407
|
+
mode = 'char';
|
1408
|
+
var exclusive = !motionArgs.inclusive || linewise;
|
1409
|
+
cmSel = makeCmSelection(cm, {
|
1410
|
+
anchor: curStart,
|
1411
|
+
head: curEnd
|
1412
|
+
}, mode, exclusive);
|
1395
1413
|
}
|
1414
|
+
cm.setSelections(cmSel.ranges, cmSel.primary);
|
1415
|
+
vim.lastMotion = null;
|
1416
|
+
operatorArgs.repeat = repeat; // For indent in visual mode.
|
1396
1417
|
operatorArgs.registerName = registerName;
|
1397
1418
|
// Keep track of linewise as it affects how paste and change behave.
|
1398
1419
|
operatorArgs.linewise = linewise;
|
1399
|
-
|
1400
|
-
cm.
|
1401
|
-
}
|
1402
|
-
operators[operator](cm, operatorArgs, vim, curStart,
|
1403
|
-
curEnd, curOriginal);
|
1420
|
+
var operatorMoveTo = operators[operator](
|
1421
|
+
cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
|
1404
1422
|
if (vim.visualMode) {
|
1405
1423
|
exitVisualMode(cm);
|
1406
1424
|
}
|
1425
|
+
if (operatorMoveTo) {
|
1426
|
+
cm.setCursor(operatorMoveTo);
|
1427
|
+
}
|
1407
1428
|
}
|
1408
1429
|
},
|
1409
1430
|
recordLastEdit: function(vim, inputState, actionCommand) {
|
@@ -1422,7 +1443,7 @@
|
|
1422
1443
|
*/
|
1423
1444
|
// All of the functions below return Cursor objects.
|
1424
1445
|
var motions = {
|
1425
|
-
moveToTopLine: function(cm, motionArgs) {
|
1446
|
+
moveToTopLine: function(cm, _head, motionArgs) {
|
1426
1447
|
var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
|
1427
1448
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
1428
1449
|
},
|
@@ -1431,17 +1452,17 @@
|
|
1431
1452
|
var line = Math.floor((range.top + range.bottom) * 0.5);
|
1432
1453
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
1433
1454
|
},
|
1434
|
-
moveToBottomLine: function(cm, motionArgs) {
|
1455
|
+
moveToBottomLine: function(cm, _head, motionArgs) {
|
1435
1456
|
var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
|
1436
1457
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
1437
1458
|
},
|
1438
|
-
expandToLine: function(
|
1459
|
+
expandToLine: function(_cm, head, motionArgs) {
|
1439
1460
|
// Expands forward to end of line, and then to next line if repeat is
|
1440
1461
|
// >1. Does not handle backward motion!
|
1441
|
-
var cur =
|
1462
|
+
var cur = head;
|
1442
1463
|
return Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
1443
1464
|
},
|
1444
|
-
findNext: function(cm, motionArgs) {
|
1465
|
+
findNext: function(cm, _head, motionArgs) {
|
1445
1466
|
var state = getSearchState(cm);
|
1446
1467
|
var query = state.getQuery();
|
1447
1468
|
if (!query) {
|
@@ -1453,7 +1474,7 @@
|
|
1453
1474
|
highlightSearchMatches(cm, query);
|
1454
1475
|
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
1455
1476
|
},
|
1456
|
-
goToMark: function(cm, motionArgs, vim) {
|
1477
|
+
goToMark: function(cm, _head, motionArgs, vim) {
|
1457
1478
|
var mark = vim.marks[motionArgs.selectedCharacter];
|
1458
1479
|
if (mark) {
|
1459
1480
|
var pos = mark.find();
|
@@ -1461,22 +1482,19 @@
|
|
1461
1482
|
}
|
1462
1483
|
return null;
|
1463
1484
|
},
|
1464
|
-
moveToOtherHighlightedEnd: function(cm, motionArgs, vim) {
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
curEnd = Pos(ranges[curIndex].head.line, curEnd.ch);
|
1485
|
+
moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {
|
1486
|
+
if (vim.visualBlock && motionArgs.sameLine) {
|
1487
|
+
var sel = vim.sel;
|
1488
|
+
return [
|
1489
|
+
clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)),
|
1490
|
+
clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))
|
1491
|
+
];
|
1472
1492
|
} else {
|
1473
|
-
|
1493
|
+
return ([vim.sel.head, vim.sel.anchor]);
|
1474
1494
|
}
|
1475
|
-
cm.setCursor(curEnd);
|
1476
|
-
return ([curEnd, curStart]);
|
1477
1495
|
},
|
1478
|
-
jumpToMark: function(cm, motionArgs, vim) {
|
1479
|
-
var best =
|
1496
|
+
jumpToMark: function(cm, head, motionArgs, vim) {
|
1497
|
+
var best = head;
|
1480
1498
|
for (var i = 0; i < motionArgs.repeat; i++) {
|
1481
1499
|
var cursor = best;
|
1482
1500
|
for (var key in vim.marks) {
|
@@ -1513,14 +1531,14 @@
|
|
1513
1531
|
}
|
1514
1532
|
return best;
|
1515
1533
|
},
|
1516
|
-
moveByCharacters: function(
|
1517
|
-
var cur =
|
1534
|
+
moveByCharacters: function(_cm, head, motionArgs) {
|
1535
|
+
var cur = head;
|
1518
1536
|
var repeat = motionArgs.repeat;
|
1519
1537
|
var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
|
1520
1538
|
return Pos(cur.line, ch);
|
1521
1539
|
},
|
1522
|
-
moveByLines: function(cm, motionArgs, vim) {
|
1523
|
-
var cur =
|
1540
|
+
moveByLines: function(cm, head, motionArgs, vim) {
|
1541
|
+
var cur = head;
|
1524
1542
|
var endCh = cur.ch;
|
1525
1543
|
// Depending what our last motion was, we may want to do different
|
1526
1544
|
// things. If our last motion was moving vertically, we want to
|
@@ -1555,8 +1573,8 @@
|
|
1555
1573
|
vim.lastHSPos = cm.charCoords(Pos(line, endCh),'div').left;
|
1556
1574
|
return Pos(line, endCh);
|
1557
1575
|
},
|
1558
|
-
moveByDisplayLines: function(cm, motionArgs, vim) {
|
1559
|
-
var cur =
|
1576
|
+
moveByDisplayLines: function(cm, head, motionArgs, vim) {
|
1577
|
+
var cur = head;
|
1560
1578
|
switch (vim.lastMotion) {
|
1561
1579
|
case this.moveByDisplayLines:
|
1562
1580
|
case this.moveByScroll:
|
@@ -1583,16 +1601,16 @@
|
|
1583
1601
|
vim.lastHPos = res.ch;
|
1584
1602
|
return res;
|
1585
1603
|
},
|
1586
|
-
moveByPage: function(cm, motionArgs) {
|
1604
|
+
moveByPage: function(cm, head, motionArgs) {
|
1587
1605
|
// CodeMirror only exposes functions that move the cursor page down, so
|
1588
1606
|
// doing this bad hack to move the cursor and move it back. evalInput
|
1589
1607
|
// will move the cursor to where it should be in the end.
|
1590
|
-
var curStart =
|
1608
|
+
var curStart = head;
|
1591
1609
|
var repeat = motionArgs.repeat;
|
1592
1610
|
return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
|
1593
1611
|
},
|
1594
|
-
moveByParagraph: function(cm, motionArgs) {
|
1595
|
-
var line =
|
1612
|
+
moveByParagraph: function(cm, head, motionArgs) {
|
1613
|
+
var line = head.line;
|
1596
1614
|
var repeat = motionArgs.repeat;
|
1597
1615
|
var inc = motionArgs.forward ? 1 : -1;
|
1598
1616
|
for (var i = 0; i < repeat; i++) {
|
@@ -1607,16 +1625,16 @@
|
|
1607
1625
|
}
|
1608
1626
|
return Pos(line, 0);
|
1609
1627
|
},
|
1610
|
-
moveByScroll: function(cm, motionArgs, vim) {
|
1628
|
+
moveByScroll: function(cm, head, motionArgs, vim) {
|
1611
1629
|
var scrollbox = cm.getScrollInfo();
|
1612
1630
|
var curEnd = null;
|
1613
1631
|
var repeat = motionArgs.repeat;
|
1614
1632
|
if (!repeat) {
|
1615
1633
|
repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
|
1616
1634
|
}
|
1617
|
-
var orig = cm.charCoords(
|
1635
|
+
var orig = cm.charCoords(head, 'local');
|
1618
1636
|
motionArgs.repeat = repeat;
|
1619
|
-
var curEnd = motions.moveByDisplayLines(cm, motionArgs, vim);
|
1637
|
+
var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
|
1620
1638
|
if (!curEnd) {
|
1621
1639
|
return null;
|
1622
1640
|
}
|
@@ -1624,11 +1642,11 @@
|
|
1624
1642
|
cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
|
1625
1643
|
return curEnd;
|
1626
1644
|
},
|
1627
|
-
moveByWords: function(cm, motionArgs) {
|
1628
|
-
return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
|
1645
|
+
moveByWords: function(cm, head, motionArgs) {
|
1646
|
+
return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,
|
1629
1647
|
!!motionArgs.wordEnd, !!motionArgs.bigWord);
|
1630
1648
|
},
|
1631
|
-
moveTillCharacter: function(cm, motionArgs) {
|
1649
|
+
moveTillCharacter: function(cm, _head, motionArgs) {
|
1632
1650
|
var repeat = motionArgs.repeat;
|
1633
1651
|
var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
|
1634
1652
|
motionArgs.selectedCharacter);
|
@@ -1638,26 +1656,26 @@
|
|
1638
1656
|
curEnd.ch += increment;
|
1639
1657
|
return curEnd;
|
1640
1658
|
},
|
1641
|
-
moveToCharacter: function(cm, motionArgs) {
|
1659
|
+
moveToCharacter: function(cm, head, motionArgs) {
|
1642
1660
|
var repeat = motionArgs.repeat;
|
1643
1661
|
recordLastCharacterSearch(0, motionArgs);
|
1644
1662
|
return moveToCharacter(cm, repeat, motionArgs.forward,
|
1645
|
-
motionArgs.selectedCharacter) ||
|
1663
|
+
motionArgs.selectedCharacter) || head;
|
1646
1664
|
},
|
1647
|
-
moveToSymbol: function(cm, motionArgs) {
|
1665
|
+
moveToSymbol: function(cm, head, motionArgs) {
|
1648
1666
|
var repeat = motionArgs.repeat;
|
1649
1667
|
return findSymbol(cm, repeat, motionArgs.forward,
|
1650
|
-
motionArgs.selectedCharacter) ||
|
1668
|
+
motionArgs.selectedCharacter) || head;
|
1651
1669
|
},
|
1652
|
-
moveToColumn: function(cm, motionArgs, vim) {
|
1670
|
+
moveToColumn: function(cm, head, motionArgs, vim) {
|
1653
1671
|
var repeat = motionArgs.repeat;
|
1654
1672
|
// repeat is equivalent to which column we want to move to!
|
1655
1673
|
vim.lastHPos = repeat - 1;
|
1656
|
-
vim.lastHSPos = cm.charCoords(
|
1674
|
+
vim.lastHSPos = cm.charCoords(head,'div').left;
|
1657
1675
|
return moveToColumn(cm, repeat);
|
1658
1676
|
},
|
1659
|
-
moveToEol: function(cm, motionArgs, vim) {
|
1660
|
-
var cur =
|
1677
|
+
moveToEol: function(cm, head, motionArgs, vim) {
|
1678
|
+
var cur = head;
|
1661
1679
|
vim.lastHPos = Infinity;
|
1662
1680
|
var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
1663
1681
|
var end=cm.clipPos(retval);
|
@@ -1665,15 +1683,15 @@
|
|
1665
1683
|
vim.lastHSPos = cm.charCoords(end,'div').left;
|
1666
1684
|
return retval;
|
1667
1685
|
},
|
1668
|
-
moveToFirstNonWhiteSpaceCharacter: function(cm) {
|
1686
|
+
moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
|
1669
1687
|
// Go to the start of the line where the text begins, or the end for
|
1670
1688
|
// whitespace-only lines
|
1671
|
-
var cursor =
|
1689
|
+
var cursor = head;
|
1672
1690
|
return Pos(cursor.line,
|
1673
1691
|
findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
|
1674
1692
|
},
|
1675
|
-
moveToMatchedSymbol: function(cm) {
|
1676
|
-
var cursor =
|
1693
|
+
moveToMatchedSymbol: function(cm, head) {
|
1694
|
+
var cursor = head;
|
1677
1695
|
var line = cursor.line;
|
1678
1696
|
var ch = cursor.ch;
|
1679
1697
|
var lineText = cm.getLine(line);
|
@@ -1694,11 +1712,10 @@
|
|
1694
1712
|
return cursor;
|
1695
1713
|
}
|
1696
1714
|
},
|
1697
|
-
moveToStartOfLine: function(
|
1698
|
-
|
1699
|
-
return Pos(cursor.line, 0);
|
1715
|
+
moveToStartOfLine: function(_cm, head) {
|
1716
|
+
return Pos(head.line, 0);
|
1700
1717
|
},
|
1701
|
-
moveToLineOrEdgeOfDocument: function(cm, motionArgs) {
|
1718
|
+
moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
|
1702
1719
|
var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
|
1703
1720
|
if (motionArgs.repeatIsExplicit) {
|
1704
1721
|
lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
|
@@ -1706,7 +1723,7 @@
|
|
1706
1723
|
return Pos(lineNum,
|
1707
1724
|
findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
|
1708
1725
|
},
|
1709
|
-
textObjectManipulation: function(cm, motionArgs) {
|
1726
|
+
textObjectManipulation: function(cm, head, motionArgs) {
|
1710
1727
|
// TODO: lots of possible exceptions that can be thrown here. Try da(
|
1711
1728
|
// outside of a () block.
|
1712
1729
|
|
@@ -1735,9 +1752,9 @@
|
|
1735
1752
|
|
1736
1753
|
var tmp;
|
1737
1754
|
if (mirroredPairs[character]) {
|
1738
|
-
tmp = selectCompanionObject(cm, character, inclusive);
|
1755
|
+
tmp = selectCompanionObject(cm, head, character, inclusive);
|
1739
1756
|
} else if (selfPaired[character]) {
|
1740
|
-
tmp = findBeginningAndEnd(cm, character, inclusive);
|
1757
|
+
tmp = findBeginningAndEnd(cm, head, character, inclusive);
|
1741
1758
|
} else if (character === 'W') {
|
1742
1759
|
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
|
1743
1760
|
true /** bigWord */);
|
@@ -1756,7 +1773,7 @@
|
|
1756
1773
|
}
|
1757
1774
|
},
|
1758
1775
|
|
1759
|
-
repeatLastCharacterSearch: function(cm, motionArgs) {
|
1776
|
+
repeatLastCharacterSearch: function(cm, head, motionArgs) {
|
1760
1777
|
var lastSearch = vimGlobalState.lastChararacterSearch;
|
1761
1778
|
var repeat = motionArgs.repeat;
|
1762
1779
|
var forward = motionArgs.forward === lastSearch.forward;
|
@@ -1766,138 +1783,107 @@
|
|
1766
1783
|
var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
|
1767
1784
|
if (!curEnd) {
|
1768
1785
|
cm.moveH(increment, 'char');
|
1769
|
-
return
|
1786
|
+
return head;
|
1770
1787
|
}
|
1771
1788
|
curEnd.ch += increment;
|
1772
1789
|
return curEnd;
|
1773
1790
|
}
|
1774
1791
|
};
|
1775
1792
|
|
1793
|
+
function fillArray(val, times) {
|
1794
|
+
var arr = [];
|
1795
|
+
for (var i = 0; i < times; i++) {
|
1796
|
+
arr.push(val);
|
1797
|
+
}
|
1798
|
+
return arr;
|
1799
|
+
}
|
1800
|
+
/**
|
1801
|
+
* An operator acts on a text selection. It receives the list of selections
|
1802
|
+
* as input. The corresponding CodeMirror selection is guaranteed to
|
1803
|
+
* match the input selection.
|
1804
|
+
*/
|
1776
1805
|
var operators = {
|
1777
|
-
change: function(cm,
|
1778
|
-
var
|
1779
|
-
var
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
visualBlock = vim.lastSelection.visualBlock ? true : visualBlock;
|
1786
|
-
}
|
1787
|
-
var lastInsertModeChanges = vimGlobalState.macroModeState.lastInsertModeChanges;
|
1788
|
-
lastInsertModeChanges.inVisualBlock = visualBlock;
|
1789
|
-
var replacement = new Array(selections.length).join('1').split('1');
|
1790
|
-
// save the selectionEnd mark
|
1791
|
-
var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head');
|
1792
|
-
vimGlobalState.registerController.pushText(
|
1793
|
-
operatorArgs.registerName, 'change', text,
|
1794
|
-
operatorArgs.linewise);
|
1795
|
-
if (operatorArgs.linewise) {
|
1796
|
-
// 'C' in visual block extends the block till eol for all lines
|
1797
|
-
if (visualBlock){
|
1798
|
-
var startLine = curStart.line;
|
1799
|
-
while (startLine <= curEnd.line) {
|
1800
|
-
var endCh = lineLength(cm, startLine);
|
1801
|
-
var head = Pos(startLine, endCh);
|
1802
|
-
var anchor = Pos(startLine, curStart.ch);
|
1803
|
-
startLine++;
|
1804
|
-
cm.replaceRange('', anchor, head);
|
1805
|
-
}
|
1806
|
-
} else {
|
1807
|
-
// Push the next line back down, if there is a next line.
|
1808
|
-
replacement = '\n';
|
1809
|
-
if (curEnd.line == curStart.line && curEnd.line == cm.lastLine()) {
|
1810
|
-
replacement = '';
|
1811
|
-
}
|
1812
|
-
cm.replaceRange(replacement, curStart, curEnd);
|
1813
|
-
cm.indentLine(curStart.line, 'smart');
|
1814
|
-
// null ch so setCursor moves to end of line.
|
1815
|
-
curStart.ch = null;
|
1816
|
-
cm.setCursor(curStart);
|
1817
|
-
}
|
1818
|
-
} else {
|
1819
|
-
// Exclude trailing whitespace if the range is not all whitespace.
|
1820
|
-
var text = cm.getRange(curStart, curEnd);
|
1806
|
+
change: function(cm, args, ranges) {
|
1807
|
+
var finalHead, text;
|
1808
|
+
var vim = cm.state.vim;
|
1809
|
+
vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock;
|
1810
|
+
if (!vim.visualMode) {
|
1811
|
+
var anchor = ranges[0].anchor,
|
1812
|
+
head = ranges[0].head;
|
1813
|
+
text = cm.getRange(anchor, head);
|
1821
1814
|
if (!isWhiteSpaceString(text)) {
|
1815
|
+
// Exclude trailing whitespace if the range is not all whitespace.
|
1822
1816
|
var match = (/\s+$/).exec(text);
|
1823
1817
|
if (match) {
|
1824
|
-
|
1818
|
+
head = offsetCursor(head, 0, - match[0].length);
|
1819
|
+
text = text.slice(0, - match[0].length);
|
1825
1820
|
}
|
1826
1821
|
}
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1822
|
+
var wasLastLine = head.line - 1 == cm.lastLine();
|
1823
|
+
cm.replaceRange('', anchor, head);
|
1824
|
+
if (args.linewise && !wasLastLine) {
|
1825
|
+
// Push the next line back down, if there is a next line.
|
1826
|
+
CodeMirror.commands.newlineAndIndent(cm);
|
1827
|
+
// null ch so setCursor moves to end of line.
|
1828
|
+
anchor.ch = null;
|
1832
1829
|
}
|
1830
|
+
finalHead = anchor;
|
1831
|
+
} else {
|
1832
|
+
text = cm.getSelection();
|
1833
|
+
var replacement = fillArray('', ranges.length);
|
1834
|
+
cm.replaceSelections(replacement);
|
1835
|
+
finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
|
1833
1836
|
}
|
1834
|
-
|
1835
|
-
|
1837
|
+
vimGlobalState.registerController.pushText(
|
1838
|
+
args.registerName, 'change', text,
|
1839
|
+
args.linewise, ranges.length > 1);
|
1840
|
+
actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);
|
1836
1841
|
},
|
1837
1842
|
// delete is a javascript keyword.
|
1838
|
-
'delete': function(cm,
|
1839
|
-
var
|
1840
|
-
var
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
var text = cm.getSelection();
|
1855
|
-
vimGlobalState.registerController.pushText(
|
1856
|
-
operatorArgs.registerName, 'delete', text,
|
1857
|
-
operatorArgs.linewise, blockwise);
|
1858
|
-
var replacement = new Array(selections.length).join('1').split('1');
|
1859
|
-
// If the ending line is past the last line, inclusive, instead of
|
1860
|
-
// including the trailing \n, include the \n before the starting line
|
1861
|
-
if (operatorArgs.linewise &&
|
1862
|
-
curEnd.line == cm.lastLine() && curStart.line == curEnd.line) {
|
1863
|
-
if (curEnd.line == 0) {
|
1864
|
-
curStart.ch = 0;
|
1843
|
+
'delete': function(cm, args, ranges) {
|
1844
|
+
var finalHead, text;
|
1845
|
+
var vim = cm.state.vim;
|
1846
|
+
if (!vim.visualBlock) {
|
1847
|
+
var anchor = ranges[0].anchor,
|
1848
|
+
head = ranges[0].head;
|
1849
|
+
if (args.linewise &&
|
1850
|
+
head.line != cm.firstLine() &&
|
1851
|
+
anchor.line == cm.lastLine() &&
|
1852
|
+
anchor.line == head.line - 1) {
|
1853
|
+
// Special case for dd on last line (and first line).
|
1854
|
+
if (anchor.line == cm.firstLine()) {
|
1855
|
+
anchor.ch = 0;
|
1856
|
+
} else {
|
1857
|
+
anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
|
1858
|
+
}
|
1865
1859
|
}
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1860
|
+
text = cm.getRange(anchor, head);
|
1861
|
+
cm.replaceRange('', anchor, head);
|
1862
|
+
finalHead = anchor;
|
1863
|
+
if (args.linewise) {
|
1864
|
+
finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
|
1871
1865
|
}
|
1872
|
-
cm.replaceRange('', curStart, curEnd);
|
1873
1866
|
} else {
|
1867
|
+
text = cm.getSelection();
|
1868
|
+
var replacement = fillArray('', ranges.length);
|
1874
1869
|
cm.replaceSelections(replacement);
|
1870
|
+
finalHead = ranges[0].anchor;
|
1875
1871
|
}
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
if (vim.visualMode) {
|
1881
|
-
vim.marks['<'] = curStartMark;
|
1882
|
-
vim.marks['>'] = curEndMark;
|
1883
|
-
} else {
|
1884
|
-
vim.lastSelection.curStartMark = curStartMark;
|
1885
|
-
vim.lastSelection.curEndMark = curEndMark;
|
1886
|
-
}
|
1887
|
-
}
|
1888
|
-
if (operatorArgs.linewise) {
|
1889
|
-
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1890
|
-
} else {
|
1891
|
-
cm.setCursor(curStart);
|
1892
|
-
}
|
1872
|
+
vimGlobalState.registerController.pushText(
|
1873
|
+
args.registerName, 'delete', text,
|
1874
|
+
args.linewise, vim.visualBlock);
|
1875
|
+
return finalHead;
|
1893
1876
|
},
|
1894
|
-
indent: function(cm,
|
1895
|
-
var
|
1896
|
-
var
|
1877
|
+
indent: function(cm, args, ranges) {
|
1878
|
+
var vim = cm.state.vim;
|
1879
|
+
var startLine = ranges[0].anchor.line;
|
1880
|
+
var endLine = vim.visualBlock ?
|
1881
|
+
ranges[ranges.length - 1].anchor.line :
|
1882
|
+
ranges[0].head.line;
|
1897
1883
|
// In visual mode, n> shifts the selection right n times, instead of
|
1898
1884
|
// shifting n lines right once.
|
1899
|
-
var repeat = (vim.visualMode) ?
|
1900
|
-
if (
|
1885
|
+
var repeat = (vim.visualMode) ? args.repeat : 1;
|
1886
|
+
if (args.linewise) {
|
1901
1887
|
// The only way to delete a newline is to delete until the start of
|
1902
1888
|
// the next line, so in linewise mode evalInput will include the next
|
1903
1889
|
// line. We don't want this in indent, so we go back a line.
|
@@ -1905,39 +1891,52 @@
|
|
1905
1891
|
}
|
1906
1892
|
for (var i = startLine; i <= endLine; i++) {
|
1907
1893
|
for (var j = 0; j < repeat; j++) {
|
1908
|
-
cm.indentLine(i,
|
1894
|
+
cm.indentLine(i, args.indentRight);
|
1909
1895
|
}
|
1910
1896
|
}
|
1911
|
-
cm.
|
1912
|
-
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1897
|
+
return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
|
1913
1898
|
},
|
1914
|
-
|
1899
|
+
changeCase: function(cm, args, ranges, oldAnchor, newHead) {
|
1915
1900
|
var selections = cm.getSelections();
|
1916
|
-
var ranges = cm.listSelections();
|
1917
1901
|
var swapped = [];
|
1902
|
+
var toLower = args.toLower;
|
1918
1903
|
for (var j = 0; j < selections.length; j++) {
|
1919
1904
|
var toSwap = selections[j];
|
1920
1905
|
var text = '';
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1906
|
+
if (toLower === true) {
|
1907
|
+
text = toSwap.toLowerCase();
|
1908
|
+
} else if (toLower === false) {
|
1909
|
+
text = toSwap.toUpperCase();
|
1910
|
+
} else {
|
1911
|
+
for (var i = 0; i < toSwap.length; i++) {
|
1912
|
+
var character = toSwap.charAt(i);
|
1913
|
+
text += isUpperCase(character) ? character.toLowerCase() :
|
1914
|
+
character.toUpperCase();
|
1915
|
+
}
|
1925
1916
|
}
|
1926
1917
|
swapped.push(text);
|
1927
1918
|
}
|
1928
1919
|
cm.replaceSelections(swapped);
|
1929
|
-
|
1930
|
-
|
1931
|
-
if (!
|
1932
|
-
|
1920
|
+
if (args.shouldMoveCursor){
|
1921
|
+
return newHead;
|
1922
|
+
} else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
|
1923
|
+
return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
|
1924
|
+
} else if (args.linewise){
|
1925
|
+
return oldAnchor;
|
1926
|
+
} else {
|
1927
|
+
return cursorMin(ranges[0].anchor, ranges[0].head);
|
1933
1928
|
}
|
1934
1929
|
},
|
1935
|
-
yank: function(cm,
|
1930
|
+
yank: function(cm, args, ranges, oldAnchor) {
|
1931
|
+
var vim = cm.state.vim;
|
1936
1932
|
var text = cm.getSelection();
|
1933
|
+
var endPos = vim.visualMode
|
1934
|
+
? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
|
1935
|
+
: oldAnchor;
|
1937
1936
|
vimGlobalState.registerController.pushText(
|
1938
|
-
|
1939
|
-
text,
|
1940
|
-
|
1937
|
+
args.registerName, 'yank',
|
1938
|
+
text, args.linewise, vim.visualBlock);
|
1939
|
+
return endPos;
|
1941
1940
|
}
|
1942
1941
|
};
|
1943
1942
|
|
@@ -2029,41 +2028,40 @@
|
|
2029
2028
|
vim.insertMode = true;
|
2030
2029
|
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
|
2031
2030
|
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
var selectionEnd = selections[1];
|
2036
|
-
}
|
2031
|
+
var sel = vim.sel;
|
2032
|
+
var head = actionArgs.head || cm.getCursor('head');
|
2033
|
+
var height = cm.listSelections().length;
|
2037
2034
|
if (insertAt == 'eol') {
|
2038
|
-
|
2039
|
-
cursor = Pos(cursor.line, lineLength(cm, cursor.line));
|
2040
|
-
cm.setCursor(cursor);
|
2035
|
+
head = Pos(head.line, lineLength(cm, head.line));
|
2041
2036
|
} else if (insertAt == 'charAfter') {
|
2042
|
-
|
2037
|
+
head = offsetCursor(head, 0, 1);
|
2043
2038
|
} else if (insertAt == 'firstNonBlank') {
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2039
|
+
head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);
|
2040
|
+
} else if (insertAt == 'startOfSelectedArea') {
|
2041
|
+
if (!vim.visualBlock) {
|
2042
|
+
if (sel.head.line < sel.anchor.line) {
|
2043
|
+
head = sel.head;
|
2047
2044
|
} else {
|
2048
|
-
|
2049
|
-
cm.setCursor(selectionStart);
|
2045
|
+
head = Pos(sel.anchor.line, 0);
|
2050
2046
|
}
|
2051
|
-
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
2052
|
-
} else if (vim.visualBlock) {
|
2053
|
-
selectionEnd = Pos(selectionEnd.line, selectionStart.ch);
|
2054
|
-
cm.setCursor(selectionStart);
|
2055
|
-
selectBlock(cm, selectionEnd);
|
2056
2047
|
} else {
|
2057
|
-
|
2048
|
+
head = Pos(
|
2049
|
+
Math.min(sel.head.line, sel.anchor.line),
|
2050
|
+
Math.min(sel.head.ch, sel.anchor.ch));
|
2051
|
+
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
2058
2052
|
}
|
2059
2053
|
} else if (insertAt == 'endOfSelectedArea') {
|
2060
|
-
if (vim.visualBlock) {
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2054
|
+
if (!vim.visualBlock) {
|
2055
|
+
if (sel.head.line >= sel.anchor.line) {
|
2056
|
+
head = offsetCursor(sel.head, 0, 1);
|
2057
|
+
} else {
|
2058
|
+
head = Pos(sel.anchor.line, 0);
|
2059
|
+
}
|
2060
|
+
} else {
|
2061
|
+
head = Pos(
|
2062
|
+
Math.min(sel.head.line, sel.anchor.line),
|
2063
|
+
Math.max(sel.head.ch + 1, sel.anchor.ch));
|
2064
|
+
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
2067
2065
|
}
|
2068
2066
|
} else if (insertAt == 'inplace') {
|
2069
2067
|
if (vim.visualMode){
|
@@ -2089,129 +2087,68 @@
|
|
2089
2087
|
if (vim.visualMode) {
|
2090
2088
|
exitVisualMode(cm);
|
2091
2089
|
}
|
2090
|
+
selectForInsert(cm, head, height);
|
2092
2091
|
},
|
2093
2092
|
toggleVisualMode: function(cm, actionArgs, vim) {
|
2094
2093
|
var repeat = actionArgs.repeat;
|
2095
|
-
var
|
2096
|
-
var
|
2097
|
-
var selections = cm.listSelections();
|
2094
|
+
var anchor = cm.getCursor();
|
2095
|
+
var head;
|
2098
2096
|
// TODO: The repeat should actually select number of characters/lines
|
2099
2097
|
// equal to the repeat times the size of the previous visual
|
2100
2098
|
// operation.
|
2101
2099
|
if (!vim.visualMode) {
|
2102
|
-
|
2100
|
+
// Entering visual mode
|
2103
2101
|
vim.visualMode = true;
|
2104
2102
|
vim.visualLine = !!actionArgs.linewise;
|
2105
2103
|
vim.visualBlock = !!actionArgs.blockwise;
|
2106
|
-
|
2107
|
-
|
2108
|
-
curEnd = clipCursorToContent(
|
2109
|
-
cm, Pos(curStart.line + repeat - 1, lineLength(cm, curStart.line)),
|
2110
|
-
true /** includeLineBreak */);
|
2111
|
-
} else {
|
2112
|
-
curEnd = clipCursorToContent(
|
2113
|
-
cm, Pos(curStart.line, curStart.ch + repeat),
|
2104
|
+
head = clipCursorToContent(
|
2105
|
+
cm, Pos(anchor.line, anchor.ch + repeat - 1),
|
2114
2106
|
true /** includeLineBreak */);
|
2115
|
-
|
2116
|
-
|
2117
|
-
|
2107
|
+
vim.sel = {
|
2108
|
+
anchor: anchor,
|
2109
|
+
head: head
|
2110
|
+
};
|
2111
|
+
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
|
2112
|
+
updateCmSelection(cm);
|
2113
|
+
updateMark(cm, vim, '<', cursorMin(anchor, head));
|
2114
|
+
updateMark(cm, vim, '>', cursorMax(anchor, head));
|
2115
|
+
} else if (vim.visualLine ^ actionArgs.linewise ||
|
2116
|
+
vim.visualBlock ^ actionArgs.blockwise) {
|
2117
|
+
// Toggling between modes
|
2118
|
+
vim.visualLine = !!actionArgs.linewise;
|
2119
|
+
vim.visualBlock = !!actionArgs.blockwise;
|
2120
|
+
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
|
2121
|
+
updateCmSelection(cm);
|
2118
2122
|
} else {
|
2119
|
-
|
2120
|
-
curEnd = cm.getCursor('head');
|
2121
|
-
if (vim.visualLine) {
|
2122
|
-
if (actionArgs.blockwise) {
|
2123
|
-
// This means Ctrl-V pressed in linewise visual
|
2124
|
-
vim.visualBlock = true;
|
2125
|
-
selectBlock(cm, curEnd);
|
2126
|
-
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'blockwise'});
|
2127
|
-
} else if (!actionArgs.linewise) {
|
2128
|
-
// v pressed in linewise, switch to characterwise visual mode
|
2129
|
-
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual'});
|
2130
|
-
} else {
|
2131
|
-
exitVisualMode(cm);
|
2132
|
-
}
|
2133
|
-
vim.visualLine = false;
|
2134
|
-
} else if (vim.visualBlock) {
|
2135
|
-
if (actionArgs.linewise) {
|
2136
|
-
// Shift-V pressed in blockwise visual mode
|
2137
|
-
vim.visualLine = true;
|
2138
|
-
curStart = Pos(selections[0].anchor.line, 0);
|
2139
|
-
curEnd = Pos(selections[selections.length-1].anchor.line, lineLength(cm, selections[selections.length-1].anchor.line));
|
2140
|
-
cm.setSelection(curStart, curEnd);
|
2141
|
-
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'linewise'});
|
2142
|
-
} else if (!actionArgs.blockwise) {
|
2143
|
-
// v pressed in blockwise mode, Switch to characterwise
|
2144
|
-
if (curEnd != selections[0].head) {
|
2145
|
-
curStart = selections[0].anchor;
|
2146
|
-
} else {
|
2147
|
-
curStart = selections[selections.length-1].anchor;
|
2148
|
-
}
|
2149
|
-
cm.setSelection(curStart, curEnd);
|
2150
|
-
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual'});
|
2151
|
-
} else {
|
2152
|
-
exitVisualMode(cm);
|
2153
|
-
}
|
2154
|
-
vim.visualBlock = false;
|
2155
|
-
} else if (actionArgs.linewise) {
|
2156
|
-
// Shift-V pressed in characterwise visual mode. Switch to linewise
|
2157
|
-
// visual mode instead of exiting visual mode.
|
2158
|
-
vim.visualLine = true;
|
2159
|
-
curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
|
2160
|
-
lineLength(cm, curStart.line);
|
2161
|
-
curEnd.ch = cursorIsBefore(curStart, curEnd) ?
|
2162
|
-
lineLength(cm, curEnd.line) : 0;
|
2163
|
-
cm.setSelection(curStart, curEnd);
|
2164
|
-
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: "linewise"});
|
2165
|
-
} else if (actionArgs.blockwise) {
|
2166
|
-
vim.visualBlock = true;
|
2167
|
-
selectBlock(cm, curEnd);
|
2168
|
-
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'blockwise'});
|
2169
|
-
} else {
|
2170
|
-
exitVisualMode(cm);
|
2171
|
-
}
|
2123
|
+
exitVisualMode(cm);
|
2172
2124
|
}
|
2173
|
-
updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
|
2174
|
-
: curEnd);
|
2175
|
-
updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
|
2176
|
-
: curStart);
|
2177
2125
|
},
|
2178
2126
|
reselectLastSelection: function(cm, _actionArgs, vim) {
|
2179
|
-
var curStart = vim.marks['<'].find();
|
2180
|
-
var curEnd = vim.marks['>'].find();
|
2181
2127
|
var lastSelection = vim.lastSelection;
|
2128
|
+
if (vim.visualMode) {
|
2129
|
+
updateLastSelection(cm, vim);
|
2130
|
+
}
|
2182
2131
|
if (lastSelection) {
|
2183
|
-
|
2184
|
-
var
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2188
|
-
updateLastSelection(cm, vim, curStart, curEnd);
|
2189
|
-
if (blockwise) {
|
2190
|
-
cm.setCursor(selectionStart);
|
2191
|
-
selectionStart = selectBlock(cm, selectionEnd);
|
2192
|
-
} else {
|
2193
|
-
cm.setSelection(selectionStart, selectionEnd);
|
2194
|
-
selectionStart = cm.getCursor('anchor');
|
2195
|
-
selectionEnd = cm.getCursor('head');
|
2196
|
-
}
|
2197
|
-
if (vim.visualMode) {
|
2198
|
-
updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
|
2199
|
-
: selectionEnd);
|
2200
|
-
updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
|
2201
|
-
: selectionStart);
|
2132
|
+
var anchor = lastSelection.anchorMark.find();
|
2133
|
+
var head = lastSelection.headMark.find();
|
2134
|
+
if (!anchor || !head) {
|
2135
|
+
// If the marks have been destroyed due to edits, do nothing.
|
2136
|
+
return;
|
2202
2137
|
}
|
2203
|
-
|
2138
|
+
vim.sel = {
|
2139
|
+
anchor: anchor,
|
2140
|
+
head: head
|
2141
|
+
};
|
2204
2142
|
vim.visualMode = true;
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
|
2213
|
-
|
2214
|
-
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
|
2143
|
+
vim.visualLine = lastSelection.visualLine;
|
2144
|
+
vim.visualBlock = lastSelection.visualBlock;
|
2145
|
+
updateCmSelection(cm);
|
2146
|
+
updateMark(cm, vim, '<', cursorMin(anchor, head));
|
2147
|
+
updateMark(cm, vim, '>', cursorMax(anchor, head));
|
2148
|
+
CodeMirror.signal(cm, 'vim-mode-change', {
|
2149
|
+
mode: 'visual',
|
2150
|
+
subMode: vim.visualLine ? 'linewise' :
|
2151
|
+
vim.visualBlock ? 'blockwise' : ''});
|
2215
2152
|
}
|
2216
2153
|
},
|
2217
2154
|
joinLines: function(cm, actionArgs, vim) {
|
@@ -2228,18 +2165,19 @@
|
|
2228
2165
|
Infinity));
|
2229
2166
|
}
|
2230
2167
|
var finalCh = 0;
|
2231
|
-
|
2232
|
-
|
2233
|
-
|
2234
|
-
|
2235
|
-
|
2236
|
-
|
2237
|
-
|
2238
|
-
|
2239
|
-
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2168
|
+
for (var i = curStart.line; i < curEnd.line; i++) {
|
2169
|
+
finalCh = lineLength(cm, curStart.line);
|
2170
|
+
var tmp = Pos(curStart.line + 1,
|
2171
|
+
lineLength(cm, curStart.line + 1));
|
2172
|
+
var text = cm.getRange(curStart, tmp);
|
2173
|
+
text = text.replace(/\n\s*/g, ' ');
|
2174
|
+
cm.replaceRange(text, curStart, tmp);
|
2175
|
+
}
|
2176
|
+
var curFinalPos = Pos(curStart.line, finalCh);
|
2177
|
+
cm.setCursor(curFinalPos);
|
2178
|
+
if (vim.visualMode) {
|
2179
|
+
exitVisualMode(cm);
|
2180
|
+
}
|
2243
2181
|
},
|
2244
2182
|
newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
|
2245
2183
|
vim.insertMode = true;
|
@@ -2268,11 +2206,12 @@
|
|
2268
2206
|
return;
|
2269
2207
|
}
|
2270
2208
|
if (actionArgs.matchIndent) {
|
2271
|
-
|
2209
|
+
var tabSize = cm.getOption("tabSize");
|
2210
|
+
// length that considers tabs and tabSize
|
2272
2211
|
var whitespaceLength = function(str) {
|
2273
2212
|
var tabs = (str.split("\t").length - 1);
|
2274
2213
|
var spaces = (str.split(" ").length - 1);
|
2275
|
-
return tabs *
|
2214
|
+
return tabs * tabSize + spaces * 1;
|
2276
2215
|
};
|
2277
2216
|
var currentLine = cm.getLine(cm.getCursor().line);
|
2278
2217
|
var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
|
@@ -2285,8 +2224,8 @@
|
|
2285
2224
|
if (newIndent < 0) {
|
2286
2225
|
return "";
|
2287
2226
|
}
|
2288
|
-
else if (cm.
|
2289
|
-
var quotient = Math.floor(newIndent /
|
2227
|
+
else if (cm.getOption("indentWithTabs")) {
|
2228
|
+
var quotient = Math.floor(newIndent / tabSize);
|
2290
2229
|
return Array(quotient + 1).join('\t');
|
2291
2230
|
}
|
2292
2231
|
else {
|
@@ -2334,7 +2273,7 @@
|
|
2334
2273
|
var emptyStrings = new Array(selections.length).join('1').split('1');
|
2335
2274
|
// save the curEnd marker before it get cleared due to cm.replaceRange.
|
2336
2275
|
if (vim.lastSelection) {
|
2337
|
-
lastSelectionCurEnd = vim.lastSelection.
|
2276
|
+
lastSelectionCurEnd = vim.lastSelection.headMark.find();
|
2338
2277
|
}
|
2339
2278
|
// push the previously selected text to unnamed register
|
2340
2279
|
vimGlobalState.registerController.unnamedRegister.setText(selectedText);
|
@@ -2358,7 +2297,7 @@
|
|
2358
2297
|
}
|
2359
2298
|
// restore the the curEnd marker
|
2360
2299
|
if(lastSelectionCurEnd) {
|
2361
|
-
vim.lastSelection.
|
2300
|
+
vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
|
2362
2301
|
}
|
2363
2302
|
if (linewise) {
|
2364
2303
|
curPosFinal.ch=0;
|
@@ -2400,10 +2339,10 @@
|
|
2400
2339
|
}
|
2401
2340
|
}
|
2402
2341
|
}
|
2403
|
-
cm.setCursor(curPosFinal);
|
2404
2342
|
if (vim.visualMode) {
|
2405
2343
|
exitVisualMode(cm);
|
2406
2344
|
}
|
2345
|
+
cm.setCursor(curPosFinal);
|
2407
2346
|
},
|
2408
2347
|
undo: function(cm, actionArgs) {
|
2409
2348
|
cm.operation(function() {
|
@@ -2448,7 +2387,7 @@
|
|
2448
2387
|
replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
|
2449
2388
|
if (vim.visualBlock) {
|
2450
2389
|
// Tabs are split in visua block before replacing
|
2451
|
-
var spaces = new Array(cm.
|
2390
|
+
var spaces = new Array(cm.getOption("tabSize")+1).join(' ');
|
2452
2391
|
replaceWithStr = cm.getSelection();
|
2453
2392
|
replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
|
2454
2393
|
cm.replaceSelections(replaceWithStr);
|
@@ -2504,28 +2443,6 @@
|
|
2504
2443
|
}
|
2505
2444
|
repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
|
2506
2445
|
},
|
2507
|
-
changeCase: function(cm, actionArgs, vim) {
|
2508
|
-
var selectionStart = getSelectedAreaRange(cm, vim)[0];
|
2509
|
-
var text = cm.getSelection();
|
2510
|
-
var lastSelectionCurEnd;
|
2511
|
-
var blockSelection;
|
2512
|
-
if (vim.lastSelection) {
|
2513
|
-
// save the curEnd marker to avoid its removal due to cm.replaceRange
|
2514
|
-
lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
|
2515
|
-
blockSelection = vim.lastSelection.visualBlock;
|
2516
|
-
}
|
2517
|
-
var toLower = actionArgs.toLower;
|
2518
|
-
text = toLower ? text.toLowerCase() : text.toUpperCase();
|
2519
|
-
cm.replaceSelections(vim.visualBlock || blockSelection ? text.split('\n') : [text]);
|
2520
|
-
// restore the last selection curEnd marker
|
2521
|
-
if (lastSelectionCurEnd) {
|
2522
|
-
vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
|
2523
|
-
}
|
2524
|
-
cm.setCursor(selectionStart);
|
2525
|
-
if (vim.visualMode) {
|
2526
|
-
exitVisualMode(cm);
|
2527
|
-
}
|
2528
|
-
},
|
2529
2446
|
exitInsertMode: exitInsertMode
|
2530
2447
|
};
|
2531
2448
|
|
@@ -2554,8 +2471,18 @@
|
|
2554
2471
|
return ret;
|
2555
2472
|
}
|
2556
2473
|
function offsetCursor(cur, offsetLine, offsetCh) {
|
2474
|
+
if (typeof offsetLine === 'object') {
|
2475
|
+
offsetCh = offsetLine.ch;
|
2476
|
+
offsetLine = offsetLine.line;
|
2477
|
+
}
|
2557
2478
|
return Pos(cur.line + offsetLine, cur.ch + offsetCh);
|
2558
2479
|
}
|
2480
|
+
function getOffset(anchor, head) {
|
2481
|
+
return {
|
2482
|
+
line: head.line - anchor.line,
|
2483
|
+
ch: head.line - anchor.line
|
2484
|
+
};
|
2485
|
+
}
|
2559
2486
|
function commandMatches(keys, keyMap, context, inputState) {
|
2560
2487
|
// Partial matches are not applied. They inform the key handler
|
2561
2488
|
// that the current key sequence is a subsequence of a valid key
|
@@ -2628,9 +2555,15 @@
|
|
2628
2555
|
return false;
|
2629
2556
|
}
|
2630
2557
|
function cursorMin(cur1, cur2) {
|
2558
|
+
if (arguments.length > 2) {
|
2559
|
+
cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
|
2560
|
+
}
|
2631
2561
|
return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
|
2632
2562
|
}
|
2633
2563
|
function cursorMax(cur1, cur2) {
|
2564
|
+
if (arguments.length > 2) {
|
2565
|
+
cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
|
2566
|
+
}
|
2634
2567
|
return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
|
2635
2568
|
}
|
2636
2569
|
function cursorIsBetween(cur1, cur2, cur3) {
|
@@ -2668,121 +2601,59 @@
|
|
2668
2601
|
// Distance between selectionEnd.ch and any(first considered here) selection.ch
|
2669
2602
|
function selectBlock(cm, selectionEnd) {
|
2670
2603
|
var selections = [], ranges = cm.listSelections();
|
2671
|
-
var
|
2672
|
-
var
|
2673
|
-
var
|
2674
|
-
var
|
2675
|
-
|
2676
|
-
|
2677
|
-
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2684
|
-
|
2685
|
-
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2690
|
-
|
2691
|
-
|
2692
|
-
|
2693
|
-
|
2694
|
-
|
2695
|
-
|
2696
|
-
|
2697
|
-
|
2698
|
-
}
|
2699
|
-
} else if (curEnd.ch == lineLength(cm, curEnd.line)) {
|
2700
|
-
if (cursorEqual(ranges[primIndex].anchor, ranges[primIndex].head) && ranges.length>1) {
|
2701
|
-
if (direction == 'up') {
|
2702
|
-
if (contains || primIndex>0) {
|
2703
|
-
start = firstRange.line;
|
2704
|
-
end = selectionEnd.line;
|
2705
|
-
selectionStart = ranges[primIndex-1].anchor;
|
2706
|
-
}
|
2707
|
-
} else {
|
2708
|
-
if (contains || primIndex == 0) {
|
2709
|
-
end = lastRange.line;
|
2710
|
-
start = selectionEnd.line;
|
2711
|
-
selectionStart = ranges[primIndex+1].anchor;
|
2712
|
-
}
|
2713
|
-
}
|
2714
|
-
if (selectionEnd.ch >= selectionStart.ch) {
|
2715
|
-
selectionStart.ch--;
|
2716
|
-
}
|
2717
|
-
}
|
2718
|
-
}
|
2719
|
-
};
|
2720
|
-
switch(direction) {
|
2721
|
-
case 'up':
|
2722
|
-
start = contains ? firstRange.line : selectionEnd.line;
|
2723
|
-
end = contains ? selectionEnd.line : lastRange.line;
|
2724
|
-
selectionStart = lastRange;
|
2725
|
-
processSelectionCrossing();
|
2726
|
-
break;
|
2727
|
-
case 'down':
|
2728
|
-
start = contains ? selectionEnd.line : firstRange.line;
|
2729
|
-
end = contains ? lastRange.line : selectionEnd.line;
|
2730
|
-
selectionStart = firstRange;
|
2731
|
-
processSelectionCrossing();
|
2732
|
-
break;
|
2733
|
-
case 'left':
|
2734
|
-
if ((selectionEnd.ch <= selectionStart.ch) && (curEnd.ch > selectionStart.ch)) {
|
2735
|
-
selectionStart.ch++;
|
2736
|
-
selectionEnd.ch--;
|
2737
|
-
}
|
2738
|
-
break;
|
2739
|
-
case 'right':
|
2740
|
-
if ((selectionStart.ch <= selectionEnd.ch) && (curEnd.ch < selectionStart.ch)) {
|
2741
|
-
selectionStart.ch--;
|
2742
|
-
selectionEnd.ch++;
|
2743
|
-
}
|
2744
|
-
break;
|
2745
|
-
default:
|
2746
|
-
start = selectionStart.line;
|
2747
|
-
end = selectionEnd.line;
|
2748
|
-
}
|
2749
|
-
while (start <= end) {
|
2750
|
-
var anchor = {line: start, ch: selectionStart.ch};
|
2751
|
-
var head = {line: start, ch: selectionEnd.ch};
|
2752
|
-
var range = {anchor: anchor, head: head};
|
2604
|
+
var head = copyCursor(cm.clipPos(selectionEnd));
|
2605
|
+
var isClipped = !cursorEqual(selectionEnd, head);
|
2606
|
+
var curHead = cm.getCursor('head');
|
2607
|
+
var primIndex = getIndex(ranges, curHead);
|
2608
|
+
var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
|
2609
|
+
var max = ranges.length - 1;
|
2610
|
+
var index = max - primIndex > primIndex ? max : 0;
|
2611
|
+
var base = ranges[index].anchor;
|
2612
|
+
|
2613
|
+
var firstLine = Math.min(base.line, head.line);
|
2614
|
+
var lastLine = Math.max(base.line, head.line);
|
2615
|
+
var baseCh = base.ch, headCh = head.ch;
|
2616
|
+
|
2617
|
+
var dir = ranges[index].head.ch - baseCh;
|
2618
|
+
var newDir = headCh - baseCh;
|
2619
|
+
if (dir > 0 && newDir <= 0) {
|
2620
|
+
baseCh++;
|
2621
|
+
if (!isClipped) { headCh--; }
|
2622
|
+
} else if (dir < 0 && newDir >= 0) {
|
2623
|
+
baseCh--;
|
2624
|
+
if (!wasClipped) { headCh++; }
|
2625
|
+
} else if (dir < 0 && newDir == -1) {
|
2626
|
+
baseCh--;
|
2627
|
+
headCh++;
|
2628
|
+
}
|
2629
|
+
for (var line = firstLine; line <= lastLine; line++) {
|
2630
|
+
var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};
|
2753
2631
|
selections.push(range);
|
2754
|
-
if (cursorEqual(head, selectionEnd)) {
|
2755
|
-
primIndex = selections.indexOf(range);
|
2756
|
-
}
|
2757
|
-
start++;
|
2758
2632
|
}
|
2759
|
-
|
2760
|
-
|
2761
|
-
selectionEnd.ch =
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2633
|
+
primIndex = head.line == lastLine ? selections.length - 1 : 0;
|
2634
|
+
cm.setSelections(selections);
|
2635
|
+
selectionEnd.ch = headCh;
|
2636
|
+
base.ch = baseCh;
|
2637
|
+
return base;
|
2638
|
+
}
|
2639
|
+
function selectForInsert(cm, head, height) {
|
2640
|
+
var sel = [];
|
2641
|
+
for (var i = 0; i < height; i++) {
|
2642
|
+
var lineHead = offsetCursor(head, i, 0);
|
2643
|
+
sel.push({anchor: lineHead, head: lineHead});
|
2767
2644
|
}
|
2768
|
-
cm.setSelections(
|
2769
|
-
return selectionStart;
|
2645
|
+
cm.setSelections(sel, 0);
|
2770
2646
|
}
|
2771
2647
|
// getIndex returns the index of the cursor in the selections.
|
2772
2648
|
function getIndex(ranges, cursor, end) {
|
2773
|
-
var pos = -1;
|
2774
2649
|
for (var i = 0; i < ranges.length; i++) {
|
2775
|
-
var atAnchor = cursorEqual(ranges[i].anchor, cursor);
|
2776
|
-
var atHead = cursorEqual(ranges[i].head, cursor);
|
2777
|
-
if (
|
2778
|
-
|
2779
|
-
} else if (end == 'anchor') {
|
2780
|
-
pos = atAnchor ? i : pos;
|
2781
|
-
} else {
|
2782
|
-
pos = (atAnchor || atHead) ? i : pos;
|
2650
|
+
var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
|
2651
|
+
var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
|
2652
|
+
if (atAnchor || atHead) {
|
2653
|
+
return i;
|
2783
2654
|
}
|
2784
2655
|
}
|
2785
|
-
return
|
2656
|
+
return -1;
|
2786
2657
|
}
|
2787
2658
|
function getSelectedAreaRange(cm, vim) {
|
2788
2659
|
var lastSelection = vim.lastSelection;
|
@@ -2813,8 +2684,8 @@
|
|
2813
2684
|
}
|
2814
2685
|
cm.setSelections(selections);
|
2815
2686
|
} else {
|
2816
|
-
var start = lastSelection.
|
2817
|
-
var end = lastSelection.
|
2687
|
+
var start = lastSelection.anchorMark.find();
|
2688
|
+
var end = lastSelection.headMark.find();
|
2818
2689
|
var line = end.line - start.line;
|
2819
2690
|
var ch = end.ch - start.ch;
|
2820
2691
|
selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
|
@@ -2833,36 +2704,28 @@
|
|
2833
2704
|
return getCurrentSelectedAreaRange();
|
2834
2705
|
}
|
2835
2706
|
}
|
2836
|
-
|
2837
|
-
|
2838
|
-
|
2839
|
-
|
2840
|
-
|
2707
|
+
// Updates the previous selection with the current selection's values. This
|
2708
|
+
// should only be called in visual mode.
|
2709
|
+
function updateLastSelection(cm, vim) {
|
2710
|
+
var anchor = vim.sel.anchor;
|
2711
|
+
var head = vim.sel.head;
|
2841
2712
|
// To accommodate the effect of lastPastedText in the last selection
|
2842
2713
|
if (vim.lastPastedText) {
|
2843
|
-
|
2714
|
+
head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
|
2844
2715
|
vim.lastPastedText = null;
|
2845
2716
|
}
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
if (vim.visualBlock) {
|
2851
|
-
var height = Math.abs(selectionStart.line - selectionEnd.line)+1;
|
2852
|
-
var width = Math.abs(selectionStart.ch - selectionEnd.ch);
|
2853
|
-
var block = {height: height, width: width};
|
2854
|
-
}
|
2855
|
-
// can't use selection state here because yank has already reset its cursor
|
2856
|
-
// Also, Bookmarks make the visual selections robust to edit operations
|
2857
|
-
vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart),
|
2858
|
-
'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd),
|
2717
|
+
vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),
|
2718
|
+
'headMark': cm.setBookmark(head),
|
2719
|
+
'anchor': copyCursor(anchor),
|
2720
|
+
'head': copyCursor(head),
|
2859
2721
|
'visualMode': vim.visualMode,
|
2860
2722
|
'visualLine': vim.visualLine,
|
2861
|
-
'visualBlock':
|
2723
|
+
'visualBlock': vim.visualBlock};
|
2862
2724
|
}
|
2863
2725
|
function expandSelection(cm, start, end) {
|
2864
|
-
var
|
2865
|
-
var
|
2726
|
+
var sel = cm.state.vim.sel;
|
2727
|
+
var head = sel.head;
|
2728
|
+
var anchor = sel.anchor;
|
2866
2729
|
var tmp;
|
2867
2730
|
if (cursorIsBefore(end, start)) {
|
2868
2731
|
tmp = end;
|
@@ -2875,9 +2738,75 @@
|
|
2875
2738
|
} else {
|
2876
2739
|
anchor = cursorMin(start, anchor);
|
2877
2740
|
head = cursorMax(head, end);
|
2741
|
+
head = offsetCursor(head, 0, -1);
|
2742
|
+
if (head.ch == -1 && head.line != cm.firstLine()) {
|
2743
|
+
head = Pos(head.line - 1, lineLength(cm, head.line - 1));
|
2744
|
+
}
|
2878
2745
|
}
|
2879
2746
|
return [anchor, head];
|
2880
2747
|
}
|
2748
|
+
/**
|
2749
|
+
* Updates the CodeMirror selection to match the provided vim selection.
|
2750
|
+
* If no arguments are given, it uses the current vim selection state.
|
2751
|
+
*/
|
2752
|
+
function updateCmSelection(cm, sel, mode) {
|
2753
|
+
var vim = cm.state.vim;
|
2754
|
+
sel = sel || vim.sel;
|
2755
|
+
var mode = mode ||
|
2756
|
+
vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
|
2757
|
+
var cmSel = makeCmSelection(cm, sel, mode);
|
2758
|
+
cm.setSelections(cmSel.ranges, cmSel.primary);
|
2759
|
+
updateFakeCursor(cm);
|
2760
|
+
}
|
2761
|
+
function makeCmSelection(cm, sel, mode, exclusive) {
|
2762
|
+
var head = copyCursor(sel.head);
|
2763
|
+
var anchor = copyCursor(sel.anchor);
|
2764
|
+
if (mode == 'char') {
|
2765
|
+
var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
2766
|
+
var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
2767
|
+
head = offsetCursor(sel.head, 0, headOffset);
|
2768
|
+
anchor = offsetCursor(sel.anchor, 0, anchorOffset);
|
2769
|
+
return {
|
2770
|
+
ranges: [{anchor: anchor, head: head}],
|
2771
|
+
primary: 0
|
2772
|
+
};
|
2773
|
+
} else if (mode == 'line') {
|
2774
|
+
if (!cursorIsBefore(sel.head, sel.anchor)) {
|
2775
|
+
anchor.ch = 0;
|
2776
|
+
|
2777
|
+
var lastLine = cm.lastLine();
|
2778
|
+
if (head.line > lastLine) {
|
2779
|
+
head.line = lastLine;
|
2780
|
+
}
|
2781
|
+
head.ch = lineLength(cm, head.line);
|
2782
|
+
} else {
|
2783
|
+
head.ch = 0;
|
2784
|
+
anchor.ch = lineLength(cm, anchor.line);
|
2785
|
+
}
|
2786
|
+
return {
|
2787
|
+
ranges: [{anchor: anchor, head: head}],
|
2788
|
+
primary: 0
|
2789
|
+
};
|
2790
|
+
} else if (mode == 'block') {
|
2791
|
+
var top = Math.min(anchor.line, head.line),
|
2792
|
+
left = Math.min(anchor.ch, head.ch),
|
2793
|
+
bottom = Math.max(anchor.line, head.line),
|
2794
|
+
right = Math.max(anchor.ch, head.ch) + 1;
|
2795
|
+
var height = bottom - top + 1;
|
2796
|
+
var primary = head.line == top ? 0 : height - 1;
|
2797
|
+
var ranges = [];
|
2798
|
+
for (var i = 0; i < height; i++) {
|
2799
|
+
ranges.push({
|
2800
|
+
anchor: Pos(top + i, left),
|
2801
|
+
head: Pos(top + i, right)
|
2802
|
+
});
|
2803
|
+
}
|
2804
|
+
return {
|
2805
|
+
ranges: ranges,
|
2806
|
+
primary: primary
|
2807
|
+
};
|
2808
|
+
}
|
2809
|
+
}
|
2881
2810
|
function getHead(cm) {
|
2882
2811
|
var cur = cm.getCursor('head');
|
2883
2812
|
if (cm.getSelection().length == 1) {
|
@@ -2888,23 +2817,20 @@
|
|
2888
2817
|
return cur;
|
2889
2818
|
}
|
2890
2819
|
|
2891
|
-
|
2892
|
-
|
2820
|
+
/**
|
2821
|
+
* If moveHead is set to false, the CodeMirror selection will not be
|
2822
|
+
* touched. The caller assumes the responsibility of putting the cursor
|
2823
|
+
* in the right place.
|
2824
|
+
*/
|
2825
|
+
function exitVisualMode(cm, moveHead) {
|
2893
2826
|
var vim = cm.state.vim;
|
2894
|
-
|
2895
|
-
|
2896
|
-
// hack to place the cursor at the right place
|
2897
|
-
// in case of visual block
|
2898
|
-
if (vim.visualBlock && (cursorIsBefore(selectionStart, selectionEnd))) {
|
2899
|
-
selectionEnd.ch--;
|
2827
|
+
if (moveHead !== false) {
|
2828
|
+
cm.setCursor(clipCursorToContent(cm, vim.sel.head));
|
2900
2829
|
}
|
2901
2830
|
updateLastSelection(cm, vim);
|
2902
2831
|
vim.visualMode = false;
|
2903
2832
|
vim.visualLine = false;
|
2904
2833
|
vim.visualBlock = false;
|
2905
|
-
if (!cursorEqual(selectionStart, selectionEnd)) {
|
2906
|
-
cm.setCursor(clipCursorToContent(cm, selectionEnd));
|
2907
|
-
}
|
2908
2834
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
2909
2835
|
if (vim.fakeCursor) {
|
2910
2836
|
vim.fakeCursor.clear();
|
@@ -3234,6 +3160,7 @@
|
|
3234
3160
|
|
3235
3161
|
/**
|
3236
3162
|
* @param {CodeMirror} cm CodeMirror object.
|
3163
|
+
* @param {Pos} cur The position to start from.
|
3237
3164
|
* @param {int} repeat Number of words to move past.
|
3238
3165
|
* @param {boolean} forward True to search forward. False to search
|
3239
3166
|
* backward.
|
@@ -3243,8 +3170,7 @@
|
|
3243
3170
|
* False if only alphabet characters count as part of the word.
|
3244
3171
|
* @return {Cursor} The position the cursor should move to.
|
3245
3172
|
*/
|
3246
|
-
function moveToWord(cm, repeat, forward, wordEnd, bigWord) {
|
3247
|
-
var cur = cm.getCursor();
|
3173
|
+
function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
|
3248
3174
|
var curStart = copyCursor(cur);
|
3249
3175
|
var words = [];
|
3250
3176
|
if (forward && !wordEnd || !forward && wordEnd) {
|
@@ -3344,8 +3270,8 @@
|
|
3344
3270
|
|
3345
3271
|
// TODO: perhaps this finagling of start and end positions belonds
|
3346
3272
|
// in codmirror/replaceRange?
|
3347
|
-
function selectCompanionObject(cm, symb, inclusive) {
|
3348
|
-
var cur =
|
3273
|
+
function selectCompanionObject(cm, head, symb, inclusive) {
|
3274
|
+
var cur = head, start, end;
|
3349
3275
|
|
3350
3276
|
var bracketRegexp = ({
|
3351
3277
|
'(': /[()]/, ')': /[()]/,
|
@@ -3389,8 +3315,8 @@
|
|
3389
3315
|
// Takes in a symbol and a cursor and tries to simulate text objects that
|
3390
3316
|
// have identical opening and closing symbols
|
3391
3317
|
// TODO support across multiple lines
|
3392
|
-
function findBeginningAndEnd(cm, symb, inclusive) {
|
3393
|
-
var cur = copyCursor(
|
3318
|
+
function findBeginningAndEnd(cm, head, symb, inclusive) {
|
3319
|
+
var cur = copyCursor(head);
|
3394
3320
|
var line = cm.getLine(cur.line);
|
3395
3321
|
var chars = line.split('');
|
3396
3322
|
var start, end, i, len;
|
@@ -3838,10 +3764,10 @@
|
|
3838
3764
|
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
|
3839
3765
|
{ name: 'global', shortName: 'g' }
|
3840
3766
|
];
|
3841
|
-
|
3767
|
+
var ExCommandDispatcher = function() {
|
3842
3768
|
this.buildCommandMap_();
|
3843
3769
|
};
|
3844
|
-
|
3770
|
+
ExCommandDispatcher.prototype = {
|
3845
3771
|
processCommand: function(cm, input, opt_params) {
|
3846
3772
|
var vim = cm.state.vim;
|
3847
3773
|
var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
|
@@ -4440,7 +4366,7 @@
|
|
4440
4366
|
}
|
4441
4367
|
};
|
4442
4368
|
|
4443
|
-
var exCommandDispatcher = new
|
4369
|
+
var exCommandDispatcher = new ExCommandDispatcher();
|
4444
4370
|
|
4445
4371
|
/**
|
4446
4372
|
* @param {CodeMirror} cm CodeMirror instance we are in.
|
@@ -4528,6 +4454,7 @@
|
|
4528
4454
|
break;
|
4529
4455
|
}
|
4530
4456
|
if (done) { stop(close); }
|
4457
|
+
return true;
|
4531
4458
|
}
|
4532
4459
|
|
4533
4460
|
// Actually do replace.
|
@@ -4548,9 +4475,9 @@
|
|
4548
4475
|
}
|
4549
4476
|
|
4550
4477
|
CodeMirror.keyMap.vim = {
|
4551
|
-
|
4552
|
-
|
4553
|
-
|
4478
|
+
attach: attachVimMap,
|
4479
|
+
detach: detachVimMap
|
4480
|
+
};
|
4554
4481
|
|
4555
4482
|
function exitInsertMode(cm) {
|
4556
4483
|
var vim = cm.state.vim;
|
@@ -4620,16 +4547,22 @@
|
|
4620
4547
|
CodeMirror.commands.newlineAndIndent;
|
4621
4548
|
fn(cm);
|
4622
4549
|
},
|
4623
|
-
fallthrough: ['default']
|
4550
|
+
fallthrough: ['default'],
|
4551
|
+
attach: attachVimMap,
|
4552
|
+
detach: detachVimMap
|
4624
4553
|
};
|
4625
4554
|
|
4626
4555
|
CodeMirror.keyMap['await-second'] = {
|
4627
|
-
fallthrough: ['vim-insert']
|
4556
|
+
fallthrough: ['vim-insert'],
|
4557
|
+
attach: attachVimMap,
|
4558
|
+
detach: detachVimMap
|
4628
4559
|
};
|
4629
4560
|
|
4630
4561
|
CodeMirror.keyMap['vim-replace'] = {
|
4631
4562
|
'Backspace': 'goCharLeft',
|
4632
|
-
fallthrough: ['vim-insert']
|
4563
|
+
fallthrough: ['vim-insert'],
|
4564
|
+
attach: attachVimMap,
|
4565
|
+
detach: detachVimMap
|
4633
4566
|
};
|
4634
4567
|
|
4635
4568
|
function executeMacroRegister(cm, vim, macroModeState, registerName) {
|
@@ -4724,41 +4657,49 @@
|
|
4724
4657
|
// Cursor moved outside the context of an edit. Reset the change.
|
4725
4658
|
lastChange.changes = [];
|
4726
4659
|
}
|
4727
|
-
} else if (cm.
|
4728
|
-
// Reset lastHPos if mouse click was done in normal mode.
|
4729
|
-
vim.lastHPos = cm.doc.getCursor().ch;
|
4660
|
+
} else if (!cm.curOp.isVimOp) {
|
4730
4661
|
handleExternalSelection(cm, vim);
|
4731
4662
|
}
|
4732
4663
|
if (vim.visualMode) {
|
4733
|
-
|
4734
|
-
from = head = cm.getCursor('head');
|
4735
|
-
var anchor = cm.getCursor('anchor');
|
4736
|
-
var to = Pos(head.line, from.ch + (cursorIsBefore(anchor, head) ? -1 : 1));
|
4737
|
-
if (cursorIsBefore(to, from)) {
|
4738
|
-
var temp = from;
|
4739
|
-
from = to;
|
4740
|
-
to = temp;
|
4741
|
-
}
|
4742
|
-
if (vim.fakeCursor) {
|
4743
|
-
vim.fakeCursor.clear();
|
4744
|
-
}
|
4745
|
-
vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
|
4664
|
+
updateFakeCursor(cm);
|
4746
4665
|
}
|
4747
4666
|
}
|
4748
|
-
|
4667
|
+
function updateFakeCursor(cm) {
|
4668
|
+
var vim = cm.state.vim;
|
4669
|
+
var from = copyCursor(vim.sel.head);
|
4670
|
+
var to = offsetCursor(from, 0, 1);
|
4671
|
+
if (vim.fakeCursor) {
|
4672
|
+
vim.fakeCursor.clear();
|
4673
|
+
}
|
4674
|
+
vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
|
4675
|
+
}
|
4749
4676
|
function handleExternalSelection(cm, vim) {
|
4750
4677
|
var anchor = cm.getCursor('anchor');
|
4751
4678
|
var head = cm.getCursor('head');
|
4752
|
-
// Enter visual mode
|
4753
|
-
if (
|
4679
|
+
// Enter or exit visual mode to match mouse selection.
|
4680
|
+
if (vim.visualMode && cursorEqual(head, anchor) && lineLength(cm, head.line) > head.ch) {
|
4681
|
+
exitVisualMode(cm, false);
|
4682
|
+
} else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
|
4754
4683
|
vim.visualMode = true;
|
4755
4684
|
vim.visualLine = false;
|
4756
4685
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
|
4757
|
-
cm.on('mousedown', exitVisualMode);
|
4758
4686
|
}
|
4759
4687
|
if (vim.visualMode) {
|
4688
|
+
// Bind CodeMirror selection model to vim selection model.
|
4689
|
+
// Mouse selections are considered visual characterwise.
|
4690
|
+
var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
|
4691
|
+
var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
|
4692
|
+
head = offsetCursor(head, 0, headOffset);
|
4693
|
+
anchor = offsetCursor(anchor, 0, anchorOffset);
|
4694
|
+
vim.sel = {
|
4695
|
+
anchor: anchor,
|
4696
|
+
head: head
|
4697
|
+
};
|
4760
4698
|
updateMark(cm, vim, '<', cursorMin(head, anchor));
|
4761
4699
|
updateMark(cm, vim, '>', cursorMax(head, anchor));
|
4700
|
+
} else if (!vim.insertMode) {
|
4701
|
+
// Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.
|
4702
|
+
vim.lastHPos = cm.getCursor().ch;
|
4762
4703
|
}
|
4763
4704
|
}
|
4764
4705
|
|
@@ -4781,7 +4722,7 @@
|
|
4781
4722
|
return true;
|
4782
4723
|
}
|
4783
4724
|
if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
|
4784
|
-
CodeMirror.lookupKey(keyName,
|
4725
|
+
CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);
|
4785
4726
|
}
|
4786
4727
|
}
|
4787
4728
|
|
@@ -4850,32 +4791,33 @@
|
|
4850
4791
|
}
|
4851
4792
|
return true;
|
4852
4793
|
}
|
4853
|
-
var
|
4794
|
+
var head = cm.getCursor('head');
|
4854
4795
|
var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
|
4855
4796
|
if (inVisualBlock) {
|
4856
4797
|
// Set up block selection again for repeating the changes.
|
4857
4798
|
var vim = cm.state.vim;
|
4858
|
-
var
|
4859
|
-
var
|
4860
|
-
cm.
|
4861
|
-
selectBlock(cm, curEnd);
|
4799
|
+
var lastSel = vim.lastSelection;
|
4800
|
+
var offset = getOffset(lastSel.anchor, lastSel.head);
|
4801
|
+
selectForInsert(cm, head, offset.line + 1);
|
4862
4802
|
repeat = cm.listSelections().length;
|
4863
|
-
cm.setCursor(
|
4803
|
+
cm.setCursor(head);
|
4864
4804
|
}
|
4865
4805
|
for (var i = 0; i < repeat; i++) {
|
4806
|
+
if (inVisualBlock) {
|
4807
|
+
cm.setCursor(offsetCursor(head, i, 0));
|
4808
|
+
}
|
4866
4809
|
for (var j = 0; j < changes.length; j++) {
|
4867
4810
|
var change = changes[j];
|
4868
4811
|
if (change instanceof InsertModeKey) {
|
4869
|
-
CodeMirror.lookupKey(change.keyName,
|
4812
|
+
CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);
|
4870
4813
|
} else {
|
4871
4814
|
var cur = cm.getCursor();
|
4872
4815
|
cm.replaceRange(change, cur, cur);
|
4873
4816
|
}
|
4874
4817
|
}
|
4875
|
-
|
4876
|
-
|
4877
|
-
|
4878
|
-
}
|
4818
|
+
}
|
4819
|
+
if (inVisualBlock) {
|
4820
|
+
cm.setCursor(offsetCursor(head, 0, 1));
|
4879
4821
|
}
|
4880
4822
|
}
|
4881
4823
|
|