codemirror-rails 4.7 → 4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +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
|
|