codemirror-rails 4.3 → 4.4
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 +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
|