codemirror-rails 4.3 → 4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +321 -158
- data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +11 -0
- data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +20 -5
- data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +2 -2
- data/vendor/assets/javascripts/codemirror/addons/lint/lint.js +2 -3
- data/vendor/assets/javascripts/codemirror/addons/search/search.js +1 -0
- data/vendor/assets/javascripts/codemirror/addons/search/searchcursor.js +1 -1
- data/vendor/assets/javascripts/codemirror/keymaps/sublime.js +23 -5
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +425 -118
- data/vendor/assets/javascripts/codemirror/modes/clike.js +1 -0
- data/vendor/assets/javascripts/codemirror/modes/css.js +3 -3
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +2 -0
- data/vendor/assets/javascripts/codemirror/modes/kotlin.js +280 -0
- data/vendor/assets/javascripts/codemirror/modes/puppet.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/ruby.js +18 -4
- data/vendor/assets/javascripts/codemirror/modes/smartymixed.js +17 -12
- data/vendor/assets/javascripts/codemirror/modes/vbscript.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/yaml.js +1 -1
- metadata +2 -1
@@ -153,6 +153,17 @@
|
|
153
153
|
!/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
|
154
154
|
return false;
|
155
155
|
|
156
|
+
// Avoid killing block comments completely outside the selection.
|
157
|
+
// Positions of the last startString before the start of the selection, and the first endString after it.
|
158
|
+
var lastStart = startLine.lastIndexOf(startString, from.ch);
|
159
|
+
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
|
160
|
+
if (lastStart != -1 && firstEnd != -1) return false;
|
161
|
+
// Positions of the first endString after the end of the selection, and the last startString before it.
|
162
|
+
firstEnd = endLine.indexOf(endString, to.ch);
|
163
|
+
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
|
164
|
+
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
|
165
|
+
if (firstEnd != -1 && lastStart != -1) return false;
|
166
|
+
|
156
167
|
self.operation(function() {
|
157
168
|
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
158
169
|
Pos(end, close + endString.length));
|
@@ -36,6 +36,22 @@
|
|
36
36
|
return str.length == 2 ? str : null;
|
37
37
|
}
|
38
38
|
|
39
|
+
// Project the token type that will exists after the given char is
|
40
|
+
// typed, and use it to determine whether it would cause the start
|
41
|
+
// of a string token.
|
42
|
+
function enteringString(cm, pos, ch) {
|
43
|
+
var line = cm.getLine(pos.line);
|
44
|
+
var token = cm.getTokenAt(pos);
|
45
|
+
if (/\bstring2?\b/.test(token.type)) return false;
|
46
|
+
var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
|
47
|
+
stream.pos = stream.start = token.start;
|
48
|
+
for (;;) {
|
49
|
+
var type1 = cm.getMode().token(stream, token.state);
|
50
|
+
if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
|
51
|
+
stream.start = stream.pos;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
39
55
|
function buildKeymap(pairs) {
|
40
56
|
var map = {
|
41
57
|
name : "autoCloseBrackets",
|
@@ -61,8 +77,6 @@
|
|
61
77
|
var ranges = cm.listSelections(), type, next;
|
62
78
|
for (var i = 0; i < ranges.length; i++) {
|
63
79
|
var range = ranges[i], cur = range.head, curType;
|
64
|
-
if (left == "'" && cm.getTokenTypeAt(cur) == "comment")
|
65
|
-
return CodeMirror.Pass;
|
66
80
|
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
67
81
|
if (!range.empty())
|
68
82
|
curType = "surround";
|
@@ -75,9 +89,10 @@
|
|
75
89
|
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left &&
|
76
90
|
(cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left))
|
77
91
|
curType = "addFour";
|
78
|
-
else if (left ==
|
79
|
-
|
80
|
-
|
92
|
+
else if (left == '"' || left == "'") {
|
93
|
+
if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, left)) curType = "both";
|
94
|
+
else return CodeMirror.Pass;
|
95
|
+
} else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next))
|
81
96
|
curType = "both";
|
82
97
|
else
|
83
98
|
return CodeMirror.Pass;
|
@@ -228,9 +228,9 @@
|
|
228
228
|
(completion.options.container || document.body).appendChild(hints);
|
229
229
|
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
|
230
230
|
if (overlapY > 0) {
|
231
|
-
var height = box.bottom - box.top, curTop =
|
231
|
+
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
|
232
232
|
if (curTop - height > 0) { // Fits above cursor
|
233
|
-
hints.style.top = (top =
|
233
|
+
hints.style.top = (top = pos.top - height) + "px";
|
234
234
|
below = false;
|
235
235
|
} else if (height > winH) {
|
236
236
|
hints.style.height = (winH - 5) + "px";
|
@@ -11,7 +11,6 @@
|
|
11
11
|
})(function(CodeMirror) {
|
12
12
|
"use strict";
|
13
13
|
var GUTTER_ID = "CodeMirror-lint-markers";
|
14
|
-
var SEVERITIES = /^(?:error|warning)$/;
|
15
14
|
|
16
15
|
function showTooltip(e, content) {
|
17
16
|
var tt = document.createElement("div");
|
@@ -110,7 +109,7 @@
|
|
110
109
|
|
111
110
|
function annotationTooltip(ann) {
|
112
111
|
var severity = ann.severity;
|
113
|
-
if (!
|
112
|
+
if (!severity) severity = "error";
|
114
113
|
var tip = document.createElement("div");
|
115
114
|
tip.className = "CodeMirror-lint-message-" + severity;
|
116
115
|
tip.appendChild(document.createTextNode(ann.message));
|
@@ -141,7 +140,7 @@
|
|
141
140
|
for (var i = 0; i < anns.length; ++i) {
|
142
141
|
var ann = anns[i];
|
143
142
|
var severity = ann.severity;
|
144
|
-
if (!
|
143
|
+
if (!severity) severity = "error";
|
145
144
|
maxSeverity = getMaxSeverity(maxSeverity, severity);
|
146
145
|
|
147
146
|
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
|
@@ -110,6 +110,7 @@
|
|
110
110
|
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
|
111
111
|
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
|
112
112
|
function replace(cm, all) {
|
113
|
+
if (cm.getOption("readOnly")) return;
|
113
114
|
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) {
|
114
115
|
if (!query) return;
|
115
116
|
query = parseQuery(query);
|
@@ -107,7 +107,7 @@
|
|
107
107
|
var from = Pos(pos.line, cut);
|
108
108
|
for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
|
109
109
|
if (target[i] != fold(doc.getLine(ln))) return;
|
110
|
-
if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
|
110
|
+
if (fold(doc.getLine(ln).slice(0, origTarget[last].length)) != target[last]) return;
|
111
111
|
return {from: from, to: Pos(ln, origTarget[last].length)};
|
112
112
|
}
|
113
113
|
};
|
@@ -481,7 +481,7 @@
|
|
481
481
|
});
|
482
482
|
};
|
483
483
|
|
484
|
-
function
|
484
|
+
function getTarget(cm) {
|
485
485
|
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
486
486
|
if (CodeMirror.cmpPos(from, to) == 0) {
|
487
487
|
var word = wordAt(cm, from);
|
@@ -489,9 +489,14 @@
|
|
489
489
|
from = word.from;
|
490
490
|
to = word.to;
|
491
491
|
}
|
492
|
+
return {from: from, to: to, query: cm.getRange(from, to), word: word};
|
493
|
+
}
|
492
494
|
|
493
|
-
|
494
|
-
var
|
495
|
+
function findAndGoTo(cm, forward) {
|
496
|
+
var target = getTarget(cm);
|
497
|
+
if (!target) return;
|
498
|
+
var query = target.query;
|
499
|
+
var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
|
495
500
|
|
496
501
|
if (forward ? cur.findNext() : cur.findPrevious()) {
|
497
502
|
cm.setSelection(cur.from(), cur.to());
|
@@ -500,12 +505,25 @@
|
|
500
505
|
: cm.clipPos(Pos(cm.lastLine())));
|
501
506
|
if (forward ? cur.findNext() : cur.findPrevious())
|
502
507
|
cm.setSelection(cur.from(), cur.to());
|
503
|
-
else if (word)
|
504
|
-
cm.setSelection(from, to);
|
508
|
+
else if (target.word)
|
509
|
+
cm.setSelection(target.from, target.to);
|
505
510
|
}
|
506
511
|
};
|
507
512
|
cmds[map[ctrl + "F3"] = "findUnder"] = function(cm) { findAndGoTo(cm, true); };
|
508
513
|
cmds[map["Shift-" + ctrl + "F3"] = "findUnderPrevious"] = function(cm) { findAndGoTo(cm,false); };
|
514
|
+
cmds[map["Alt-F3"] = "findAllUnder"] = function(cm) {
|
515
|
+
var target = getTarget(cm);
|
516
|
+
if (!target) return;
|
517
|
+
var cur = cm.getSearchCursor(target.query);
|
518
|
+
var matches = [];
|
519
|
+
var primaryIndex = -1;
|
520
|
+
while (cur.findNext()) {
|
521
|
+
matches.push({anchor: cur.from(), head: cur.to()});
|
522
|
+
if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
|
523
|
+
primaryIndex++;
|
524
|
+
}
|
525
|
+
cm.setSelections(matches, primaryIndex);
|
526
|
+
};
|
509
527
|
|
510
528
|
map["Shift-" + ctrl + "["] = "fold";
|
511
529
|
map["Shift-" + ctrl + "]"] = "unfold";
|
@@ -225,7 +225,8 @@
|
|
225
225
|
{ keys: ['|'], type: 'motion',
|
226
226
|
motion: 'moveToColumn',
|
227
227
|
motionArgs: { }},
|
228
|
-
{ keys: ['o'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { },context:'visual'},
|
228
|
+
{ keys: ['o'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { }, context:'visual'},
|
229
|
+
{ keys: ['O'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
|
229
230
|
// Operators
|
230
231
|
{ keys: ['d'], type: 'operator', operator: 'delete' },
|
231
232
|
{ keys: ['y'], type: 'operator', operator: 'yank' },
|
@@ -288,6 +289,8 @@
|
|
288
289
|
{ keys: ['v'], type: 'action', action: 'toggleVisualMode' },
|
289
290
|
{ keys: ['V'], type: 'action', action: 'toggleVisualMode',
|
290
291
|
actionArgs: { linewise: true }},
|
292
|
+
{ keys: ['<C-v>'], type: 'action', action: 'toggleVisualMode',
|
293
|
+
actionArgs: { blockwise: true }},
|
291
294
|
{ keys: ['g', 'v'], type: 'action', action: 'reselectLastSelection' },
|
292
295
|
{ keys: ['J'], type: 'action', action: 'joinLines', isEdit: true },
|
293
296
|
{ keys: ['p'], type: 'action', action: 'paste', isEdit: true,
|
@@ -558,7 +561,9 @@
|
|
558
561
|
MacroModeState.prototype = {
|
559
562
|
exitMacroRecordMode: function() {
|
560
563
|
var macroModeState = vimGlobalState.macroModeState;
|
561
|
-
macroModeState.onRecordingDone
|
564
|
+
if (macroModeState.onRecordingDone) {
|
565
|
+
macroModeState.onRecordingDone(); // close dialog
|
566
|
+
}
|
562
567
|
macroModeState.onRecordingDone = undefined;
|
563
568
|
macroModeState.isRecording = false;
|
564
569
|
},
|
@@ -568,8 +573,10 @@
|
|
568
573
|
if (register) {
|
569
574
|
register.clear();
|
570
575
|
this.latestRegister = registerName;
|
571
|
-
|
572
|
-
|
576
|
+
if (cm.openDialog) {
|
577
|
+
this.onRecordingDone = cm.openDialog(
|
578
|
+
'(recording)['+registerName+']', null, {bottom:true});
|
579
|
+
}
|
573
580
|
this.isRecording = true;
|
574
581
|
}
|
575
582
|
}
|
@@ -607,6 +614,7 @@
|
|
607
614
|
visualMode: false,
|
608
615
|
// If we are in visual line mode. No effect if visualMode is false.
|
609
616
|
visualLine: false,
|
617
|
+
visualBlock: false,
|
610
618
|
lastSelection: null,
|
611
619
|
lastPastedText: null
|
612
620
|
};
|
@@ -1343,19 +1351,28 @@
|
|
1343
1351
|
if (vim.visualMode) {
|
1344
1352
|
// Check if the selection crossed over itself. Will need to shift
|
1345
1353
|
// the start point if that happened.
|
1354
|
+
// offset is set to -1 or 1 to shift the curEnd
|
1355
|
+
// left or right
|
1356
|
+
var offset = 0;
|
1346
1357
|
if (cursorIsBefore(selectionStart, selectionEnd) &&
|
1347
1358
|
(cursorEqual(selectionStart, curEnd) ||
|
1348
1359
|
cursorIsBefore(curEnd, selectionStart))) {
|
1349
1360
|
// The end of the selection has moved from after the start to
|
1350
1361
|
// before the start. We will shift the start right by 1.
|
1351
1362
|
selectionStart.ch += 1;
|
1352
|
-
|
1363
|
+
offset = -1;
|
1353
1364
|
} else if (cursorIsBefore(selectionEnd, selectionStart) &&
|
1354
1365
|
(cursorEqual(selectionStart, curEnd) ||
|
1355
1366
|
cursorIsBefore(selectionStart, curEnd))) {
|
1356
1367
|
// The opposite happened. We will shift the start left by 1.
|
1357
1368
|
selectionStart.ch -= 1;
|
1358
|
-
|
1369
|
+
offset = 1;
|
1370
|
+
}
|
1371
|
+
// in case of visual Block selectionStart and curEnd
|
1372
|
+
// may not be on the same line,
|
1373
|
+
// Also, In case of v_o this should not happen.
|
1374
|
+
if (!vim.visualBlock && !(motionResult instanceof Array)) {
|
1375
|
+
curEnd.ch += offset;
|
1359
1376
|
}
|
1360
1377
|
if (vim.lastHPos != Infinity) {
|
1361
1378
|
vim.lastHPos = curEnd.ch;
|
@@ -1375,8 +1392,14 @@
|
|
1375
1392
|
selectionEnd.ch = 0;
|
1376
1393
|
selectionStart.ch = lineLength(cm, selectionStart.line);
|
1377
1394
|
}
|
1395
|
+
} else if (vim.visualBlock) {
|
1396
|
+
// Select a block and
|
1397
|
+
// return the diagonally opposite end.
|
1398
|
+
selectionStart = selectBlock(cm, selectionEnd);
|
1399
|
+
}
|
1400
|
+
if (!vim.visualBlock) {
|
1401
|
+
cm.setSelection(selectionStart, selectionEnd);
|
1378
1402
|
}
|
1379
|
-
cm.setSelection(selectionStart, selectionEnd);
|
1380
1403
|
updateMark(cm, vim, '<',
|
1381
1404
|
cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
|
1382
1405
|
: selectionEnd);
|
@@ -1392,11 +1415,13 @@
|
|
1392
1415
|
if (operator) {
|
1393
1416
|
var inverted = false;
|
1394
1417
|
vim.lastMotion = null;
|
1418
|
+
var lastSelection = vim.lastSelection;
|
1395
1419
|
operatorArgs.repeat = repeat; // Indent in visual mode needs this.
|
1396
1420
|
if (vim.visualMode) {
|
1397
1421
|
curStart = selectionStart;
|
1398
1422
|
curEnd = selectionEnd;
|
1399
1423
|
motionArgs.inclusive = true;
|
1424
|
+
operatorArgs.shouldMoveCursor = false;
|
1400
1425
|
}
|
1401
1426
|
// Swap start and end if motion was backward.
|
1402
1427
|
if (curEnd && cursorIsBefore(curEnd, curStart)) {
|
@@ -1417,6 +1442,24 @@
|
|
1417
1442
|
curEnd.line = curStart.line + operatorArgs.selOffset.line;
|
1418
1443
|
if (operatorArgs.selOffset.line) {curEnd.ch = operatorArgs.selOffset.ch; }
|
1419
1444
|
else { curEnd.ch = curStart.ch + operatorArgs.selOffset.ch; }
|
1445
|
+
// In case of blockwise visual
|
1446
|
+
if (lastSelection && lastSelection.visualBlock) {
|
1447
|
+
var block = lastSelection.visualBlock;
|
1448
|
+
var width = block.width;
|
1449
|
+
var height = block.height;
|
1450
|
+
curEnd = Pos(curStart.line + height, curStart.ch + width);
|
1451
|
+
// selectBlock creates a 'proper' rectangular block.
|
1452
|
+
// We do not want that in all cases, so we manually set selections.
|
1453
|
+
var selections = [];
|
1454
|
+
for (var i = curStart.line; i < curEnd.line; i++) {
|
1455
|
+
var anchor = Pos(i, curStart.ch);
|
1456
|
+
var head = Pos(i, curEnd.ch);
|
1457
|
+
var range = {anchor: anchor, head: head};
|
1458
|
+
selections.push(range);
|
1459
|
+
}
|
1460
|
+
cm.setSelections(selections);
|
1461
|
+
var blockSelected = true;
|
1462
|
+
}
|
1420
1463
|
} else if (vim.visualMode) {
|
1421
1464
|
var selOffset = Pos();
|
1422
1465
|
selOffset.line = curEnd.line - curStart.line;
|
@@ -1437,6 +1480,9 @@
|
|
1437
1480
|
operatorArgs.registerName = registerName;
|
1438
1481
|
// Keep track of linewise as it affects how paste and change behave.
|
1439
1482
|
operatorArgs.linewise = linewise;
|
1483
|
+
if (!vim.visualBlock && !blockSelected) {
|
1484
|
+
cm.setSelection(curStart, curEnd);
|
1485
|
+
}
|
1440
1486
|
operators[operator](cm, operatorArgs, vim, curStart,
|
1441
1487
|
curEnd, curOriginal);
|
1442
1488
|
if (vim.visualMode) {
|
@@ -1499,15 +1545,19 @@
|
|
1499
1545
|
}
|
1500
1546
|
return null;
|
1501
1547
|
},
|
1502
|
-
moveToOtherHighlightedEnd: function(cm) {
|
1503
|
-
var
|
1504
|
-
var
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1548
|
+
moveToOtherHighlightedEnd: function(cm, motionArgs, vim) {
|
1549
|
+
var ranges = cm.listSelections();
|
1550
|
+
var curEnd = cm.getCursor('head');
|
1551
|
+
var curStart = ranges[0].anchor;
|
1552
|
+
var curIndex = cursorEqual(ranges[0].head, curEnd) ? ranges.length-1 : 0;
|
1553
|
+
if (motionArgs.sameLine && vim.visualBlock) {
|
1554
|
+
curStart = Pos(curEnd.line, ranges[curIndex].anchor.ch);
|
1555
|
+
curEnd = Pos(ranges[curIndex].head.line, curEnd.ch);
|
1556
|
+
} else {
|
1557
|
+
curStart = ranges[curIndex].anchor;
|
1558
|
+
}
|
1559
|
+
cm.setCursor(curEnd);
|
1560
|
+
return ([curEnd, curStart]);
|
1511
1561
|
},
|
1512
1562
|
jumpToMark: function(cm, motionArgs, vim) {
|
1513
1563
|
var best = cm.getCursor();
|
@@ -1804,17 +1854,41 @@
|
|
1804
1854
|
};
|
1805
1855
|
|
1806
1856
|
var operators = {
|
1807
|
-
change: function(cm, operatorArgs,
|
1857
|
+
change: function(cm, operatorArgs, vim) {
|
1858
|
+
var selections = cm.listSelections();
|
1859
|
+
var start = selections[0], end = selections[selections.length-1];
|
1860
|
+
var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
|
1861
|
+
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
|
1862
|
+
var text = cm.getSelection();
|
1863
|
+
var replacement = new Array(selections.length).join('1').split('1');
|
1864
|
+
// save the selectionEnd mark
|
1865
|
+
var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head');
|
1808
1866
|
vimGlobalState.registerController.pushText(
|
1809
|
-
operatorArgs.registerName, 'change',
|
1867
|
+
operatorArgs.registerName, 'change', text,
|
1810
1868
|
operatorArgs.linewise);
|
1811
1869
|
if (operatorArgs.linewise) {
|
1812
|
-
//
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1870
|
+
// 'C' in visual block extends the block till eol for all lines
|
1871
|
+
if (vim.visualBlock){
|
1872
|
+
var startLine = curStart.line;
|
1873
|
+
while (startLine <= curEnd.line) {
|
1874
|
+
var endCh = lineLength(cm, startLine);
|
1875
|
+
var head = Pos(startLine, endCh);
|
1876
|
+
var anchor = Pos(startLine, curStart.ch);
|
1877
|
+
startLine++;
|
1878
|
+
cm.replaceRange('', anchor, head);
|
1879
|
+
}
|
1880
|
+
} else {
|
1881
|
+
// Push the next line back down, if there is a next line.
|
1882
|
+
replacement = '\n';
|
1883
|
+
if (curEnd.line == curStart.line && curEnd.line == cm.lastLine()) {
|
1884
|
+
replacement = '';
|
1885
|
+
}
|
1886
|
+
cm.replaceRange(replacement, curStart, curEnd);
|
1887
|
+
cm.indentLine(curStart.line, 'smart');
|
1888
|
+
// null ch so setCursor moves to end of line.
|
1889
|
+
curStart.ch = null;
|
1890
|
+
cm.setCursor(curStart);
|
1891
|
+
}
|
1818
1892
|
} else {
|
1819
1893
|
// Exclude trailing whitespace if the range is not all whitespace.
|
1820
1894
|
var text = cm.getRange(curStart, curEnd);
|
@@ -1824,24 +1898,60 @@
|
|
1824
1898
|
curEnd = offsetCursor(curEnd, 0, - match[0].length);
|
1825
1899
|
}
|
1826
1900
|
}
|
1827
|
-
|
1901
|
+
if (vim.visualBlock) {
|
1902
|
+
cm.replaceSelections(replacement);
|
1903
|
+
} else {
|
1904
|
+
cm.setCursor(curStart);
|
1905
|
+
cm.replaceRange('', curStart, curEnd);
|
1906
|
+
}
|
1828
1907
|
}
|
1908
|
+
vim.marks['>'] = cm.setBookmark(selectionEnd);
|
1829
1909
|
actions.enterInsertMode(cm, {}, cm.state.vim);
|
1830
|
-
cm.setCursor(curStart);
|
1831
1910
|
},
|
1832
1911
|
// delete is a javascript keyword.
|
1833
|
-
'delete': function(cm, operatorArgs,
|
1912
|
+
'delete': function(cm, operatorArgs, vim) {
|
1913
|
+
var selections = cm.listSelections();
|
1914
|
+
var start = selections[0], end = selections[selections.length-1];
|
1915
|
+
var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
|
1916
|
+
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
|
1917
|
+
// Save the '>' mark before cm.replaceRange clears it.
|
1918
|
+
var selectionEnd, selectionStart;
|
1919
|
+
if (vim.visualMode) {
|
1920
|
+
selectionEnd = vim.marks['>'].find();
|
1921
|
+
selectionStart = vim.marks['<'].find();
|
1922
|
+
} else if (vim.lastSelection) {
|
1923
|
+
selectionEnd = vim.lastSelection.curStartMark.find();
|
1924
|
+
selectionStart = vim.lastSelection.curEndMark.find();
|
1925
|
+
}
|
1926
|
+
var text = cm.getSelection();
|
1927
|
+
vimGlobalState.registerController.pushText(
|
1928
|
+
operatorArgs.registerName, 'delete', text,
|
1929
|
+
operatorArgs.linewise);
|
1930
|
+
var replacement = new Array(selections.length).join('1').split('1');
|
1834
1931
|
// If the ending line is past the last line, inclusive, instead of
|
1835
1932
|
// including the trailing \n, include the \n before the starting line
|
1836
1933
|
if (operatorArgs.linewise &&
|
1837
|
-
curEnd.line
|
1934
|
+
curEnd.line == cm.lastLine() && curStart.line == curEnd.line) {
|
1935
|
+
var tmp = copyCursor(curEnd);
|
1838
1936
|
curStart.line--;
|
1839
1937
|
curStart.ch = lineLength(cm, curStart.line);
|
1938
|
+
curEnd = tmp;
|
1939
|
+
cm.replaceRange('', curStart, curEnd);
|
1940
|
+
} else {
|
1941
|
+
cm.replaceSelections(replacement);
|
1942
|
+
}
|
1943
|
+
// restore the saved bookmark
|
1944
|
+
if (selectionEnd) {
|
1945
|
+
var curStartMark = cm.setBookmark(selectionStart);
|
1946
|
+
var curEndMark = cm.setBookmark(selectionEnd);
|
1947
|
+
if (vim.visualMode) {
|
1948
|
+
vim.marks['<'] = curStartMark;
|
1949
|
+
vim.marks['>'] = curEndMark;
|
1950
|
+
} else {
|
1951
|
+
vim.lastSelection.curStartMark = curStartMark;
|
1952
|
+
vim.lastSelection.curEndMark = curEndMark;
|
1953
|
+
}
|
1840
1954
|
}
|
1841
|
-
vimGlobalState.registerController.pushText(
|
1842
|
-
operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd),
|
1843
|
-
operatorArgs.linewise);
|
1844
|
-
cm.replaceRange('', curStart, curEnd);
|
1845
1955
|
if (operatorArgs.linewise) {
|
1846
1956
|
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1847
1957
|
} else {
|
@@ -1868,23 +1978,32 @@
|
|
1868
1978
|
cm.setCursor(curStart);
|
1869
1979
|
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
1870
1980
|
},
|
1871
|
-
swapcase: function(cm, operatorArgs, _vim,
|
1872
|
-
var
|
1873
|
-
var
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1981
|
+
swapcase: function(cm, operatorArgs, _vim, _curStart, _curEnd, _curOriginal) {
|
1982
|
+
var selections = cm.getSelections();
|
1983
|
+
var ranges = cm.listSelections();
|
1984
|
+
var swapped = [];
|
1985
|
+
for (var j = 0; j < selections.length; j++) {
|
1986
|
+
var toSwap = selections[j];
|
1987
|
+
var text = '';
|
1988
|
+
for (var i = 0; i < toSwap.length; i++) {
|
1989
|
+
var character = toSwap.charAt(i);
|
1990
|
+
text += isUpperCase(character) ? character.toLowerCase() :
|
1991
|
+
character.toUpperCase();
|
1992
|
+
}
|
1993
|
+
swapped.push(text);
|
1994
|
+
}
|
1995
|
+
cm.replaceSelections(swapped);
|
1996
|
+
var curStart = ranges[0].anchor;
|
1997
|
+
var curEnd = ranges[0].head;
|
1880
1998
|
if (!operatorArgs.shouldMoveCursor) {
|
1881
|
-
cm.setCursor(
|
1999
|
+
cm.setCursor(cursorIsBefore(curStart, curEnd) ? curStart : curEnd);
|
1882
2000
|
}
|
1883
2001
|
},
|
1884
|
-
yank: function(cm, operatorArgs, _vim,
|
2002
|
+
yank: function(cm, operatorArgs, _vim, _curStart, _curEnd, curOriginal) {
|
2003
|
+
var text = cm.getSelection();
|
1885
2004
|
vimGlobalState.registerController.pushText(
|
1886
2005
|
operatorArgs.registerName, 'yank',
|
1887
|
-
|
2006
|
+
text, operatorArgs.linewise);
|
1888
2007
|
cm.setCursor(curOriginal);
|
1889
2008
|
}
|
1890
2009
|
};
|
@@ -2015,6 +2134,7 @@
|
|
2015
2134
|
var repeat = actionArgs.repeat;
|
2016
2135
|
var curStart = cm.getCursor();
|
2017
2136
|
var curEnd;
|
2137
|
+
var selections = cm.listSelections();
|
2018
2138
|
// TODO: The repeat should actually select number of characters/lines
|
2019
2139
|
// equal to the repeat times the size of the previous visual
|
2020
2140
|
// operation.
|
@@ -2022,6 +2142,7 @@
|
|
2022
2142
|
cm.on('mousedown', exitVisualMode);
|
2023
2143
|
vim.visualMode = true;
|
2024
2144
|
vim.visualLine = !!actionArgs.linewise;
|
2145
|
+
vim.visualBlock = !!actionArgs.blockwise;
|
2025
2146
|
if (vim.visualLine) {
|
2026
2147
|
curStart.ch = 0;
|
2027
2148
|
curEnd = clipCursorToContent(
|
@@ -2037,24 +2158,57 @@
|
|
2037
2158
|
} else {
|
2038
2159
|
curStart = cm.getCursor('anchor');
|
2039
2160
|
curEnd = cm.getCursor('head');
|
2040
|
-
if (
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2161
|
+
if (vim.visualLine) {
|
2162
|
+
if (actionArgs.blockwise) {
|
2163
|
+
// This means Ctrl-V pressed in linewise visual
|
2164
|
+
vim.visualBlock = true;
|
2165
|
+
selectBlock(cm, curEnd);
|
2166
|
+
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'blockwise'});
|
2167
|
+
} else if (!actionArgs.linewise) {
|
2168
|
+
// v pressed in linewise, switch to characterwise visual mode
|
2169
|
+
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual'});
|
2170
|
+
} else {
|
2171
|
+
exitVisualMode(cm);
|
2172
|
+
}
|
2173
|
+
vim.visualLine = false;
|
2174
|
+
} else if (vim.visualBlock) {
|
2175
|
+
if (actionArgs.linewise) {
|
2176
|
+
// Shift-V pressed in blockwise visual mode
|
2177
|
+
vim.visualLine = true;
|
2178
|
+
curStart = Pos(selections[0].anchor.line, 0);
|
2179
|
+
curEnd = Pos(selections[selections.length-1].anchor.line, lineLength(cm, selections[selections.length-1].anchor.line));
|
2180
|
+
cm.setSelection(curStart, curEnd);
|
2181
|
+
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'linewise'});
|
2182
|
+
} else if (!actionArgs.blockwise) {
|
2183
|
+
// v pressed in blockwise mode, Switch to characterwise
|
2184
|
+
if (curEnd != selections[0].head) {
|
2185
|
+
curStart = selections[0].anchor;
|
2186
|
+
} else {
|
2187
|
+
curStart = selections[selections.length-1].anchor;
|
2188
|
+
}
|
2189
|
+
cm.setSelection(curStart, curEnd);
|
2190
|
+
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual'});
|
2191
|
+
} else {
|
2192
|
+
exitVisualMode(cm);
|
2193
|
+
}
|
2194
|
+
vim.visualBlock = false;
|
2195
|
+
} else if (actionArgs.linewise) {
|
2196
|
+
// Shift-V pressed in characterwise visual mode. Switch to linewise
|
2197
|
+
// visual mode instead of exiting visual mode.
|
2198
|
+
vim.visualLine = true;
|
2199
|
+
curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
|
2045
2200
|
lineLength(cm, curStart.line);
|
2046
|
-
|
2201
|
+
curEnd.ch = cursorIsBefore(curStart, curEnd) ?
|
2047
2202
|
lineLength(cm, curEnd.line) : 0;
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
}
|
2203
|
+
cm.setSelection(curStart, curEnd);
|
2204
|
+
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: "linewise"});
|
2205
|
+
} else if (actionArgs.blockwise) {
|
2206
|
+
vim.visualBlock = true;
|
2207
|
+
selectBlock(cm, curEnd);
|
2208
|
+
CodeMirror.signal(cm, 'vim-mode-change', {mode: 'visual', subMode: 'blockwise'});
|
2209
|
+
} else {
|
2210
|
+
exitVisualMode(cm);
|
2211
|
+
}
|
2058
2212
|
}
|
2059
2213
|
updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
|
2060
2214
|
: curEnd);
|
@@ -2062,27 +2216,40 @@
|
|
2062
2216
|
: curStart);
|
2063
2217
|
},
|
2064
2218
|
reselectLastSelection: function(cm, _actionArgs, vim) {
|
2219
|
+
var curStart = vim.marks['<'].find();
|
2220
|
+
var curEnd = vim.marks['>'].find();
|
2065
2221
|
var lastSelection = vim.lastSelection;
|
2066
2222
|
if (lastSelection) {
|
2067
|
-
|
2068
|
-
var
|
2069
|
-
|
2223
|
+
// Set the selections as per last selection
|
2224
|
+
var selectionStart = lastSelection.curStartMark.find();
|
2225
|
+
var selectionEnd = lastSelection.curEndMark.find();
|
2226
|
+
var blockwise = lastSelection.visualBlock;
|
2227
|
+
// update last selection
|
2228
|
+
updateLastSelection(cm, vim, curStart, curEnd);
|
2229
|
+
if (blockwise) {
|
2230
|
+
cm.setCursor(selectionStart);
|
2231
|
+
selectionStart = selectBlock(cm, selectionEnd);
|
2232
|
+
} else {
|
2233
|
+
cm.setSelection(selectionStart, selectionEnd);
|
2234
|
+
selectionStart = cm.getCursor('anchor');
|
2235
|
+
selectionEnd = cm.getCursor('head');
|
2236
|
+
}
|
2070
2237
|
if (vim.visualMode) {
|
2071
|
-
updateLastSelection(cm, vim);
|
2072
|
-
var selectionStart = cm.getCursor('anchor');
|
2073
|
-
var selectionEnd = cm.getCursor('head');
|
2074
2238
|
updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
|
2075
|
-
|
2239
|
+
: selectionEnd);
|
2076
2240
|
updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
|
2077
|
-
|
2241
|
+
: selectionStart);
|
2078
2242
|
}
|
2243
|
+
// Last selection is updated now
|
2244
|
+
vim.visualMode = true;
|
2079
2245
|
if (lastSelection.visualLine) {
|
2080
|
-
vim.visualMode = true;
|
2081
2246
|
vim.visualLine = true;
|
2082
|
-
|
2083
|
-
else {
|
2084
|
-
vim.visualMode = true;
|
2247
|
+
vim.visualBlock = false;
|
2248
|
+
} else if (lastSelection.visualBlock) {
|
2085
2249
|
vim.visualLine = false;
|
2250
|
+
vim.visualBlock = true;
|
2251
|
+
} else {
|
2252
|
+
vim.visualBlock = vim.visualLine = false;
|
2086
2253
|
}
|
2087
2254
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
|
2088
2255
|
}
|
@@ -2224,6 +2391,9 @@
|
|
2224
2391
|
}
|
2225
2392
|
}
|
2226
2393
|
cm.setCursor(curPosFinal);
|
2394
|
+
if (vim.visualMode) {
|
2395
|
+
exitVisualMode(cm);
|
2396
|
+
}
|
2227
2397
|
},
|
2228
2398
|
undo: function(cm, actionArgs) {
|
2229
2399
|
cm.operation(function() {
|
@@ -2246,10 +2416,11 @@
|
|
2246
2416
|
var curStart = cm.getCursor();
|
2247
2417
|
var replaceTo;
|
2248
2418
|
var curEnd;
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2252
|
-
|
2419
|
+
var selections = cm.listSelections();
|
2420
|
+
if (vim.visualMode) {
|
2421
|
+
curStart = cm.getCursor('start');
|
2422
|
+
curEnd = cm.getCursor('end');
|
2423
|
+
} else {
|
2253
2424
|
var line = cm.getLine(curStart.line);
|
2254
2425
|
replaceTo = curStart.ch + actionArgs.repeat;
|
2255
2426
|
if (replaceTo > line.length) {
|
@@ -2257,19 +2428,29 @@
|
|
2257
2428
|
}
|
2258
2429
|
curEnd = Pos(curStart.line, replaceTo);
|
2259
2430
|
}
|
2260
|
-
if (replaceWith=='\n'){
|
2431
|
+
if (replaceWith=='\n') {
|
2261
2432
|
if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
|
2262
2433
|
// special case, where vim help says to replace by just one line-break
|
2263
2434
|
(CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
|
2264
|
-
}else {
|
2265
|
-
var replaceWithStr=cm.getRange(curStart, curEnd);
|
2435
|
+
} else {
|
2436
|
+
var replaceWithStr = cm.getRange(curStart, curEnd);
|
2266
2437
|
//replace all characters in range by selected, but keep linebreaks
|
2267
|
-
replaceWithStr=replaceWithStr.replace(/[^\n]/g,replaceWith);
|
2268
|
-
|
2269
|
-
|
2438
|
+
replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
|
2439
|
+
if (vim.visualBlock) {
|
2440
|
+
// Tabs are split in visua block before replacing
|
2441
|
+
var spaces = new Array(cm.options.tabSize+1).join(' ');
|
2442
|
+
replaceWithStr = cm.getSelection();
|
2443
|
+
replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
|
2444
|
+
cm.replaceSelections(replaceWithStr);
|
2445
|
+
} else {
|
2446
|
+
cm.replaceRange(replaceWithStr, curStart, curEnd);
|
2447
|
+
}
|
2448
|
+
if (vim.visualMode) {
|
2449
|
+
curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
|
2450
|
+
selections[0].anchor : selections[0].head;
|
2270
2451
|
cm.setCursor(curStart);
|
2271
2452
|
exitVisualMode(cm);
|
2272
|
-
}else{
|
2453
|
+
} else {
|
2273
2454
|
cm.setCursor(offsetCursor(curEnd, 0, -1));
|
2274
2455
|
}
|
2275
2456
|
}
|
@@ -2314,17 +2495,26 @@
|
|
2314
2495
|
repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
|
2315
2496
|
},
|
2316
2497
|
changeCase: function(cm, actionArgs, vim) {
|
2317
|
-
var
|
2318
|
-
var
|
2319
|
-
var
|
2498
|
+
var selectionStart = getSelectedAreaRange(cm, vim)[0];
|
2499
|
+
var text = cm.getSelection();
|
2500
|
+
var lastSelectionCurEnd;
|
2501
|
+
var blockSelection;
|
2502
|
+
if (vim.lastSelection) {
|
2320
2503
|
// save the curEnd marker to avoid its removal due to cm.replaceRange
|
2321
|
-
|
2504
|
+
lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
|
2505
|
+
blockSelection = vim.lastSelection.visualBlock;
|
2506
|
+
}
|
2322
2507
|
var toLower = actionArgs.toLower;
|
2323
|
-
|
2324
|
-
cm.
|
2508
|
+
text = toLower ? text.toLowerCase() : text.toUpperCase();
|
2509
|
+
cm.replaceSelections(vim.visualBlock || blockSelection ? text.split('\n') : [text]);
|
2325
2510
|
// restore the last selection curEnd marker
|
2326
|
-
|
2511
|
+
if (lastSelectionCurEnd) {
|
2512
|
+
vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
|
2513
|
+
}
|
2327
2514
|
cm.setCursor(selectionStart);
|
2515
|
+
if (vim.visualMode) {
|
2516
|
+
exitVisualMode(cm);
|
2517
|
+
}
|
2328
2518
|
}
|
2329
2519
|
};
|
2330
2520
|
|
@@ -2407,45 +2597,161 @@
|
|
2407
2597
|
function escapeRegex(s) {
|
2408
2598
|
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
|
2409
2599
|
}
|
2600
|
+
// This functions selects a rectangular block
|
2601
|
+
// of text with selectionEnd as any of its corner
|
2602
|
+
// Height of block:
|
2603
|
+
// Difference in selectionEnd.line and first/last selection.line
|
2604
|
+
// Width of the block:
|
2605
|
+
// Distance between selectionEnd.ch and any(first considered here) selection.ch
|
2606
|
+
function selectBlock(cm, selectionEnd) {
|
2607
|
+
var selections = [], ranges = cm.listSelections();
|
2608
|
+
var firstRange = ranges[0].anchor, lastRange = ranges[ranges.length-1].anchor;
|
2609
|
+
var start, end, selectionStart;
|
2610
|
+
var curEnd = cm.getCursor('head');
|
2611
|
+
var primIndex = getIndex(ranges, curEnd);
|
2612
|
+
// sets to true when selectionEnd already lies inside the existing selections
|
2613
|
+
var contains = getIndex(ranges, selectionEnd) < 0 ? false : true;
|
2614
|
+
selectionEnd = cm.clipPos(selectionEnd);
|
2615
|
+
// difference in distance of selectionEnd from each end of the block.
|
2616
|
+
var near = Math.abs(firstRange.line - selectionEnd.line) - Math.abs(lastRange.line - selectionEnd.line);
|
2617
|
+
if (near > 0) {
|
2618
|
+
end = selectionEnd.line;
|
2619
|
+
start = firstRange.line;
|
2620
|
+
if (lastRange.line == selectionEnd.line && contains) {
|
2621
|
+
start = end;
|
2622
|
+
}
|
2623
|
+
} else if (near < 0) {
|
2624
|
+
start = selectionEnd.line;
|
2625
|
+
end = lastRange.line;
|
2626
|
+
if (firstRange.line == selectionEnd.line && contains) {
|
2627
|
+
end = start;
|
2628
|
+
}
|
2629
|
+
} else {
|
2630
|
+
// Case where selectionEnd line is halfway between the 2 ends.
|
2631
|
+
// We remove the primary selection in this case
|
2632
|
+
if (primIndex == 0) {
|
2633
|
+
start = selectionEnd.line;
|
2634
|
+
end = lastRange.line;
|
2635
|
+
} else {
|
2636
|
+
start = firstRange.line;
|
2637
|
+
end = selectionEnd.line;
|
2638
|
+
}
|
2639
|
+
}
|
2640
|
+
if (start > end) {
|
2641
|
+
var tmp = start;
|
2642
|
+
start = end;
|
2643
|
+
end = tmp;
|
2644
|
+
}
|
2645
|
+
selectionStart = (near > 0) ? firstRange : lastRange;
|
2646
|
+
while (start <= end) {
|
2647
|
+
var anchor = {line: start, ch: selectionStart.ch};
|
2648
|
+
var head = {line: start, ch: selectionEnd.ch};
|
2649
|
+
// Shift the anchor right or left
|
2650
|
+
// as each selection crosses itself.
|
2651
|
+
if ((anchor.ch < curEnd.ch) && ((head.ch == anchor.ch) || (anchor.ch - head.ch == 1))) {
|
2652
|
+
anchor.ch++;
|
2653
|
+
head.ch--;
|
2654
|
+
} else if ((anchor.ch > curEnd.ch) && ((head.ch == anchor.ch) || (anchor.ch - head.ch == -1))) {
|
2655
|
+
anchor.ch--;
|
2656
|
+
head.ch++;
|
2657
|
+
}
|
2658
|
+
var range = {anchor: anchor, head: head};
|
2659
|
+
selections.push(range);
|
2660
|
+
if (cursorEqual(head, selectionEnd)) {
|
2661
|
+
primIndex = selections.indexOf(range);
|
2662
|
+
}
|
2663
|
+
start++;
|
2664
|
+
}
|
2665
|
+
// Update selectionEnd and selectionStart
|
2666
|
+
// after selection crossing
|
2667
|
+
selectionEnd.ch = selections[0].head.ch;
|
2668
|
+
selectionStart.ch = selections[0].anchor.ch;
|
2669
|
+
cm.setSelections(selections, primIndex);
|
2670
|
+
return selectionStart;
|
2671
|
+
}
|
2672
|
+
function getIndex(ranges, head) {
|
2673
|
+
for (var i = 0; i < ranges.length; i++) {
|
2674
|
+
if (cursorEqual(ranges[i].head, head)) {
|
2675
|
+
return i;
|
2676
|
+
}
|
2677
|
+
}
|
2678
|
+
return -1;
|
2679
|
+
}
|
2410
2680
|
function getSelectedAreaRange(cm, vim) {
|
2411
|
-
var selectionStart = cm.getCursor('anchor');
|
2412
|
-
var selectionEnd = cm.getCursor('head');
|
2413
2681
|
var lastSelection = vim.lastSelection;
|
2414
|
-
|
2415
|
-
var
|
2416
|
-
var
|
2417
|
-
var
|
2418
|
-
var
|
2419
|
-
selectionEnd =
|
2420
|
-
|
2421
|
-
|
2682
|
+
var getCurrentSelectedAreaRange = function() {
|
2683
|
+
var selections = cm.listSelections();
|
2684
|
+
var start = selections[0];
|
2685
|
+
var end = selections[selections.length-1];
|
2686
|
+
var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
|
2687
|
+
var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
|
2688
|
+
return [selectionStart, selectionEnd];
|
2689
|
+
};
|
2690
|
+
var getLastSelectedAreaRange = function() {
|
2691
|
+
var selectionStart = cm.getCursor();
|
2692
|
+
var selectionEnd = cm.getCursor();
|
2693
|
+
var block = lastSelection.visualBlock;
|
2694
|
+
if (block) {
|
2695
|
+
var width = block.width;
|
2696
|
+
var height = block.height;
|
2697
|
+
selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
|
2698
|
+
var selections = [];
|
2699
|
+
// selectBlock creates a 'proper' rectangular block.
|
2700
|
+
// We do not want that in all cases, so we manually set selections.
|
2701
|
+
for (var i = selectionStart.line; i < selectionEnd.line; i++) {
|
2702
|
+
var anchor = Pos(i, selectionStart.ch);
|
2703
|
+
var head = Pos(i, selectionEnd.ch);
|
2704
|
+
var range = {anchor: anchor, head: head};
|
2705
|
+
selections.push(range);
|
2706
|
+
}
|
2707
|
+
cm.setSelections(selections);
|
2708
|
+
} else {
|
2709
|
+
var start = lastSelection.curStartMark.find();
|
2710
|
+
var end = lastSelection.curEndMark.find();
|
2711
|
+
var line = end.line - start.line;
|
2712
|
+
var ch = end.ch - start.ch;
|
2713
|
+
selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
|
2714
|
+
if (lastSelection.visualLine) {
|
2715
|
+
selectionStart = Pos(selectionStart.line, 0);
|
2716
|
+
selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
|
2717
|
+
}
|
2718
|
+
cm.setSelection(selectionStart, selectionEnd);
|
2422
2719
|
}
|
2720
|
+
return [selectionStart, selectionEnd];
|
2721
|
+
};
|
2722
|
+
if (!vim.visualMode) {
|
2723
|
+
// In case of replaying the action.
|
2724
|
+
return getLastSelectedAreaRange();
|
2423
2725
|
} else {
|
2424
|
-
|
2425
|
-
var tmp = selectionStart;
|
2426
|
-
selectionStart = selectionEnd;
|
2427
|
-
selectionEnd = tmp;
|
2428
|
-
}
|
2429
|
-
exitVisualMode(cm);
|
2726
|
+
return getCurrentSelectedAreaRange();
|
2430
2727
|
}
|
2431
|
-
return [selectionStart, selectionEnd];
|
2432
2728
|
}
|
2433
|
-
function updateLastSelection(cm, vim) {
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2729
|
+
function updateLastSelection(cm, vim, selectionStart, selectionEnd) {
|
2730
|
+
if (!selectionStart || !selectionEnd) {
|
2731
|
+
selectionStart = vim.marks['<'].find() || cm.getCursor('anchor');
|
2732
|
+
selectionEnd = vim.marks['>'].find() || cm.getCursor('head');
|
2733
|
+
}
|
2734
|
+
// To accommodate the effect of lastPastedText in the last selection
|
2438
2735
|
if (vim.lastPastedText) {
|
2439
|
-
selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length
|
2736
|
+
selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length);
|
2440
2737
|
vim.lastPastedText = null;
|
2441
2738
|
}
|
2739
|
+
var ranges = cm.listSelections();
|
2740
|
+
// This check ensures to set the cursor
|
2741
|
+
// position where we left off in previous selection
|
2742
|
+
var swap = getIndex(ranges, selectionStart) > -1;
|
2743
|
+
if (vim.visualBlock) {
|
2744
|
+
var height = Math.abs(selectionStart.line - selectionEnd.line)+1;
|
2745
|
+
var width = Math.abs(selectionStart.ch - selectionEnd.ch);
|
2746
|
+
var block = {height: height, width: width};
|
2747
|
+
}
|
2442
2748
|
// can't use selection state here because yank has already reset its cursor
|
2443
2749
|
// Also, Bookmarks make the visual selections robust to edit operations
|
2444
|
-
vim.lastSelection = {'curStartMark': cm.setBookmark(
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2750
|
+
vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart),
|
2751
|
+
'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd),
|
2752
|
+
'visualMode': vim.visualMode,
|
2753
|
+
'visualLine': vim.visualLine,
|
2754
|
+
'visualBlock': block};
|
2449
2755
|
}
|
2450
2756
|
|
2451
2757
|
function exitVisualMode(cm) {
|
@@ -2456,6 +2762,7 @@
|
|
2456
2762
|
updateLastSelection(cm, vim);
|
2457
2763
|
vim.visualMode = false;
|
2458
2764
|
vim.visualLine = false;
|
2765
|
+
vim.visualBlock = false;
|
2459
2766
|
if (!cursorEqual(selectionStart, selectionEnd)) {
|
2460
2767
|
// Clear the selection and set the cursor only if the selection has not
|
2461
2768
|
// already been cleared. Otherwise we risk moving the cursor somewhere
|