codemirror-rails 3.22 → 3.23
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 +35 -33
- data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +1 -0
- data/vendor/assets/javascripts/codemirror/addons/hint/sql-hint.js +24 -16
- data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +18 -10
- data/vendor/assets/javascripts/codemirror/addons/lint/css-lint.js +1 -0
- data/vendor/assets/javascripts/codemirror/addons/lint/javascript-lint.js +1 -0
- data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +19 -0
- data/vendor/assets/javascripts/codemirror/addons/search/search.js +2 -7
- data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +1 -1
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +493 -186
- data/vendor/assets/javascripts/codemirror/modes/clike.js +46 -2
- data/vendor/assets/javascripts/codemirror/modes/css.js +20 -3
- data/vendor/assets/javascripts/codemirror/modes/dylan.js +284 -0
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +3 -2
- data/vendor/assets/javascripts/codemirror/modes/pig.js +2 -0
- data/vendor/assets/javascripts/codemirror/modes/sql.js +11 -0
- data/vendor/assets/javascripts/codemirror/modes/toml.js +2 -0
- data/vendor/assets/javascripts/codemirror/modes/xml.js +64 -32
- data/vendor/assets/stylesheets/codemirror/themes/solarized.css +3 -16
- metadata +3 -3
- data/vendor/assets/javascripts/codemirror/addons/hint/pig-hint.js +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a43600e7cc5c0826859fd737519ab1a93720bfd5
|
4
|
+
data.tar.gz: 7bb9599e53f755a14682493259634e7bf3ef485e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62bdf9e6c06fc7e21e468c8f1d346eb0b8bcf6ef4bca06795ec60c61ce0bbec3e503be8443a5e5a9f3a124ec7c96bbbbfe39c986307b335d8c0e0d4e3e3dcde2
|
7
|
+
data.tar.gz: 1bc95b49a2b8c34126bed096693b0a7b231d78d0ca361522ab1dc6be1b9f55200ea76a5ab2327ec86b4d8ee5bb1adb162c8315b33e8e19a98f87f55a358a2355
|
@@ -1,5 +1,3 @@
|
|
1
|
-
// CodeMirror version 3.22
|
2
|
-
//
|
3
1
|
// CodeMirror is the only global var we claim
|
4
2
|
window.CodeMirror = (function() {
|
5
3
|
"use strict";
|
@@ -112,8 +110,8 @@ window.CodeMirror = (function() {
|
|
112
110
|
// Wraps and hides input textarea
|
113
111
|
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
114
112
|
// The actual fake scrollbars.
|
115
|
-
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
|
116
|
-
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
|
113
|
+
d.scrollbarH = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
|
114
|
+
d.scrollbarV = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
|
117
115
|
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
|
118
116
|
d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
|
119
117
|
// DIVs containing the selection and the actual code
|
@@ -232,12 +230,17 @@ window.CodeMirror = (function() {
|
|
232
230
|
var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
|
233
231
|
var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
|
234
232
|
return function(line) {
|
235
|
-
if (lineIsHidden(cm.doc, line))
|
236
|
-
|
237
|
-
|
238
|
-
|
233
|
+
if (lineIsHidden(cm.doc, line)) return 0;
|
234
|
+
|
235
|
+
var widgetsHeight = 0;
|
236
|
+
if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
|
237
|
+
if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
|
238
|
+
}
|
239
|
+
|
240
|
+
if (wrapping)
|
241
|
+
return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
|
239
242
|
else
|
240
|
-
return th;
|
243
|
+
return widgetsHeight + th;
|
241
244
|
};
|
242
245
|
}
|
243
246
|
|
@@ -335,8 +338,8 @@ window.CodeMirror = (function() {
|
|
335
338
|
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
336
339
|
d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
|
337
340
|
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
338
|
-
var needsH = d.scroller.scrollWidth >
|
339
|
-
var needsV = scrollHeight >
|
341
|
+
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
|
342
|
+
var needsV = scrollHeight > d.scroller.clientHeight;
|
340
343
|
if (needsV) {
|
341
344
|
d.scrollbarV.style.display = "block";
|
342
345
|
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
@@ -368,7 +371,12 @@ window.CodeMirror = (function() {
|
|
368
371
|
|
369
372
|
if (mac_geLion && scrollbarWidth(d.measure) === 0) {
|
370
373
|
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
|
371
|
-
|
374
|
+
var barMouseDown = function(e) {
|
375
|
+
if (e_target(e) != d.scrollbarV && e_target(e) != d.scrollbarH)
|
376
|
+
operation(cm, onMouseDown)(e);
|
377
|
+
};
|
378
|
+
on(d.scrollbarV, "mousedown", barMouseDown);
|
379
|
+
on(d.scrollbarH, "mousedown", barMouseDown);
|
372
380
|
}
|
373
381
|
}
|
374
382
|
|
@@ -1160,7 +1168,12 @@ window.CodeMirror = (function() {
|
|
1160
1168
|
var pre = buildLineContent(cm, line, null, true).pre;
|
1161
1169
|
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
|
1162
1170
|
removeChildrenAndAdd(cm.display.measure, pre);
|
1163
|
-
|
1171
|
+
var rect = getRect(end);
|
1172
|
+
if (rect.right == 0 && rect.bottom == 0) {
|
1173
|
+
end = pre.appendChild(elt("span", "\u00a0"));
|
1174
|
+
rect = getRect(end);
|
1175
|
+
}
|
1176
|
+
return rect.left - getRect(cm.display.lineDiv).left;
|
1164
1177
|
}
|
1165
1178
|
|
1166
1179
|
function clearCaches(cm) {
|
@@ -1495,10 +1508,6 @@ window.CodeMirror = (function() {
|
|
1495
1508
|
function readInput(cm) {
|
1496
1509
|
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
1497
1510
|
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false;
|
1498
|
-
if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
|
1499
|
-
input.value = input.value.substring(0, input.value.length - 1);
|
1500
|
-
cm.state.fakedLastChar = false;
|
1501
|
-
}
|
1502
1511
|
var text = input.value;
|
1503
1512
|
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1504
1513
|
if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
|
@@ -1665,16 +1674,6 @@ window.CodeMirror = (function() {
|
|
1665
1674
|
fastPoll(cm);
|
1666
1675
|
});
|
1667
1676
|
on(d.input, "paste", function() {
|
1668
|
-
// Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
|
1669
|
-
// Add a char to the end of textarea before paste occur so that
|
1670
|
-
// selection doesn't span to the end of textarea.
|
1671
|
-
if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
|
1672
|
-
var start = d.input.selectionStart, end = d.input.selectionEnd;
|
1673
|
-
d.input.value += "$";
|
1674
|
-
d.input.selectionStart = start;
|
1675
|
-
d.input.selectionEnd = end;
|
1676
|
-
cm.state.fakedLastChar = true;
|
1677
|
-
}
|
1678
1677
|
cm.state.pasteIncoming = true;
|
1679
1678
|
fastPoll(cm);
|
1680
1679
|
});
|
@@ -1708,8 +1707,7 @@ window.CodeMirror = (function() {
|
|
1708
1707
|
var display = cm.display;
|
1709
1708
|
if (!liberal) {
|
1710
1709
|
var target = e_target(e);
|
1711
|
-
if (target == display.scrollbarH || target == display.
|
1712
|
-
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1710
|
+
if (target == display.scrollbarH || target == display.scrollbarV ||
|
1713
1711
|
target == display.scrollbarFiller || target == display.gutterFiller) return null;
|
1714
1712
|
}
|
1715
1713
|
var x, y, space = getRect(display.lineSpace);
|
@@ -2230,8 +2228,9 @@ window.CodeMirror = (function() {
|
|
2230
2228
|
var oldCSS = display.input.style.cssText;
|
2231
2229
|
display.inputDiv.style.position = "absolute";
|
2232
2230
|
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
2233
|
-
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background:
|
2234
|
-
|
2231
|
+
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
|
2232
|
+
(ie ? "rgba(255, 255, 255, .05)" : "transparent") +
|
2233
|
+
"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
2235
2234
|
focusInput(cm);
|
2236
2235
|
resetInput(cm, true);
|
2237
2236
|
// Adds "Select all" to context menu in FF
|
@@ -3064,7 +3063,7 @@ window.CodeMirror = (function() {
|
|
3064
3063
|
else if (line > last) { line = last; end = true; }
|
3065
3064
|
var lineObj = getLine(this.doc, line);
|
3066
3065
|
return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
|
3067
|
-
(end ?
|
3066
|
+
(end ? this.doc.height - heightAtLine(this, lineObj) : 0);
|
3068
3067
|
},
|
3069
3068
|
|
3070
3069
|
defaultTextHeight: function() { return textHeight(this.display); },
|
@@ -3905,6 +3904,7 @@ window.CodeMirror = (function() {
|
|
3905
3904
|
this.doc.cantEdit = false;
|
3906
3905
|
if (cm) reCheckSelection(cm);
|
3907
3906
|
}
|
3907
|
+
if (cm) signalLater(cm, "markerCleared", cm, this);
|
3908
3908
|
if (withOp) endOperation(cm);
|
3909
3909
|
};
|
3910
3910
|
|
@@ -4013,6 +4013,7 @@ window.CodeMirror = (function() {
|
|
4013
4013
|
if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
|
4014
4014
|
regChange(cm, from.line, to.line + 1);
|
4015
4015
|
if (marker.atomic) reCheckSelection(cm);
|
4016
|
+
signalLater(cm, "markerAdded", cm, marker);
|
4016
4017
|
}
|
4017
4018
|
return marker;
|
4018
4019
|
}
|
@@ -5024,6 +5025,7 @@ window.CodeMirror = (function() {
|
|
5024
5025
|
redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
|
5025
5026
|
|
5026
5027
|
setExtending: function(val) {this.sel.extend = val;},
|
5028
|
+
getExtending: function() {return this.sel.extend;},
|
5027
5029
|
|
5028
5030
|
historySize: function() {
|
5029
5031
|
var hist = this.history;
|
@@ -6087,7 +6089,7 @@ window.CodeMirror = (function() {
|
|
6087
6089
|
|
6088
6090
|
// THE END
|
6089
6091
|
|
6090
|
-
CodeMirror.version = "3.
|
6092
|
+
CodeMirror.version = "3.23.0";
|
6091
6093
|
|
6092
6094
|
return CodeMirror;
|
6093
6095
|
})();
|
@@ -7,6 +7,7 @@
|
|
7
7
|
QUERY_DIV: ";",
|
8
8
|
ALIAS_KEYWORD: "AS"
|
9
9
|
};
|
10
|
+
var Pos = CodeMirror.Pos;
|
10
11
|
|
11
12
|
function getKeywords(editor) {
|
12
13
|
var mode = editor.doc.modeOption;
|
@@ -36,7 +37,7 @@
|
|
36
37
|
var cur = editor.getCursor();
|
37
38
|
var token = editor.getTokenAt(cur);
|
38
39
|
var string = token.string.substr(1);
|
39
|
-
var prevCur =
|
40
|
+
var prevCur = Pos(cur.line, token.start);
|
40
41
|
var table = editor.getTokenAt(prevCur).string;
|
41
42
|
if( !tables.hasOwnProperty( table ) ){
|
42
43
|
table = findTableByAlias(table, editor);
|
@@ -64,7 +65,7 @@
|
|
64
65
|
}
|
65
66
|
|
66
67
|
function convertNumberToCur( num ){
|
67
|
-
return
|
68
|
+
return Pos(Math.floor( num ), +num.toString().split( '.' ).pop());
|
68
69
|
}
|
69
70
|
|
70
71
|
function findTableByAlias(alias, editor) {
|
@@ -75,8 +76,8 @@
|
|
75
76
|
var table = "";
|
76
77
|
var separator = [];
|
77
78
|
var validRange = {
|
78
|
-
start:
|
79
|
-
end:
|
79
|
+
start: Pos( 0, 0 ),
|
80
|
+
end: Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length )
|
80
81
|
};
|
81
82
|
|
82
83
|
//add separator
|
@@ -85,8 +86,8 @@
|
|
85
86
|
separator.push( doc.posFromIndex(indexOfSeparator));
|
86
87
|
indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1);
|
87
88
|
}
|
88
|
-
separator.unshift(
|
89
|
-
separator.push(
|
89
|
+
separator.unshift( Pos( 0, 0 ) );
|
90
|
+
separator.push( Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) );
|
90
91
|
|
91
92
|
//find valieRange
|
92
93
|
var prevItem = 0;
|
@@ -122,24 +123,31 @@
|
|
122
123
|
tables = (options && options.tables) || {};
|
123
124
|
keywords = keywords || getKeywords(editor);
|
124
125
|
var cur = editor.getCursor();
|
125
|
-
var token = editor.getTokenAt(cur);
|
126
|
+
var token = editor.getTokenAt(cur), end = token.end;
|
126
127
|
var result = [];
|
127
128
|
var search = token.string.trim();
|
128
129
|
|
129
|
-
|
130
|
-
function(w) {return w.toUpperCase();});
|
131
|
-
|
132
|
-
addMatches(result, search, tables,
|
133
|
-
function(w) {return w;});
|
134
|
-
|
135
|
-
if(search.lastIndexOf('.') === 0) {
|
130
|
+
if (search.charAt(0) == ".") {
|
136
131
|
columnCompletion(result, editor);
|
132
|
+
if (!result.length) {
|
133
|
+
while (token.start && search.charAt(0) == ".") {
|
134
|
+
token = editor.getTokenAt(Pos(cur.line, token.start - 1));
|
135
|
+
search = token.string + search;
|
136
|
+
}
|
137
|
+
addMatches(result, search, tables,
|
138
|
+
function(w) {return w;});
|
139
|
+
}
|
140
|
+
} else {
|
141
|
+
addMatches(result, search, keywords,
|
142
|
+
function(w) {return w.toUpperCase();});
|
143
|
+
addMatches(result, search, tables,
|
144
|
+
function(w) {return w;});
|
137
145
|
}
|
138
146
|
|
139
147
|
return {
|
140
148
|
list: result,
|
141
|
-
from:
|
142
|
-
to:
|
149
|
+
from: Pos(cur.line, token.start),
|
150
|
+
to: Pos(cur.line, end)
|
143
151
|
};
|
144
152
|
}
|
145
153
|
CodeMirror.registerHelper("hint", "sql", sqlHint);
|
@@ -11,22 +11,30 @@
|
|
11
11
|
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
12
12
|
if (inner.mode.name != "xml") return;
|
13
13
|
var result = [], replaceToken = false, prefix;
|
14
|
-
var
|
15
|
-
if (
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
var tag = /\btag\b/.test(token.type), tagName = tag && /^\w/.test(token.string), tagStart;
|
15
|
+
if (tagName) {
|
16
|
+
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
|
17
|
+
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
|
18
|
+
if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
|
19
|
+
} else if (tag && token.string == "<") {
|
20
|
+
tagType = "open";
|
21
|
+
} else if (tag && token.string == "</") {
|
22
|
+
tagType = "close";
|
23
|
+
}
|
24
|
+
if (!tag && !inner.state.tagName || tagType) {
|
25
|
+
if (tagName)
|
26
|
+
prefix = token.string;
|
27
|
+
replaceToken = tagType;
|
20
28
|
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
|
21
29
|
var childList = cx ? curTag && curTag.children : tags["!top"];
|
22
|
-
if (childList) {
|
30
|
+
if (childList && tagType != "close") {
|
23
31
|
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0)
|
24
32
|
result.push("<" + childList[i]);
|
25
|
-
} else {
|
33
|
+
} else if (tagType != "close") {
|
26
34
|
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
|
27
35
|
result.push("<" + name);
|
28
36
|
}
|
29
|
-
if (cx && (!prefix ||
|
37
|
+
if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0))
|
30
38
|
result.push("</" + cx.tagName + ">");
|
31
39
|
} else {
|
32
40
|
// Attribute completion
|
@@ -59,7 +67,7 @@
|
|
59
67
|
}
|
60
68
|
return {
|
61
69
|
list: result,
|
62
|
-
from: replaceToken ? Pos(cur.line, token.start) : cur,
|
70
|
+
from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
|
63
71
|
to: replaceToken ? Pos(cur.line, token.end) : cur
|
64
72
|
};
|
65
73
|
}
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
CodeMirror.registerHelper("lint", "css", function(text) {
|
6
6
|
var found = [];
|
7
|
+
if (!window.CSSLint) return found;
|
7
8
|
var results = CSSLint.verify(text), messages = results.messages, message = null;
|
8
9
|
for ( var i = 0; i < messages.length; i++) {
|
9
10
|
message = messages[i];
|
@@ -82,6 +82,10 @@
|
|
82
82
|
}
|
83
83
|
dv.edit.on("change", change);
|
84
84
|
dv.orig.on("change", change);
|
85
|
+
dv.edit.on("markerAdded", set);
|
86
|
+
dv.edit.on("markerCleared", set);
|
87
|
+
dv.orig.on("markerAdded", set);
|
88
|
+
dv.orig.on("markerCleared", set);
|
85
89
|
dv.edit.on("viewportChange", set);
|
86
90
|
dv.orig.on("viewportChange", set);
|
87
91
|
update();
|
@@ -349,6 +353,12 @@
|
|
349
353
|
setShowDifferences: function(val) {
|
350
354
|
if (this.right) this.right.setShowDifferences(val);
|
351
355
|
if (this.left) this.left.setShowDifferences(val);
|
356
|
+
},
|
357
|
+
rightChunks: function() {
|
358
|
+
return this.right && getChunks(this.right.diff);
|
359
|
+
},
|
360
|
+
leftChunks: function() {
|
361
|
+
return this.left && getChunks(this.left.diff);
|
352
362
|
}
|
353
363
|
};
|
354
364
|
|
@@ -399,6 +409,15 @@
|
|
399
409
|
f(startOrig, orig.line + 1, startEdit, edit.line + 1);
|
400
410
|
}
|
401
411
|
|
412
|
+
function getChunks(diff) {
|
413
|
+
var collect = [];
|
414
|
+
iterateChunks(diff, function(topOrig, botOrig, topEdit, botEdit) {
|
415
|
+
collect.push({origFrom: topOrig, origTo: botOrig,
|
416
|
+
editFrom: topEdit, editTo: botEdit});
|
417
|
+
});
|
418
|
+
return collect;
|
419
|
+
}
|
420
|
+
|
402
421
|
function endOfLineClean(diff, i) {
|
403
422
|
if (i == diff.length - 1) return true;
|
404
423
|
var next = diff[i + 1][1];
|
@@ -16,16 +16,11 @@
|
|
16
16
|
} else {
|
17
17
|
query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
|
18
18
|
}
|
19
|
-
if (typeof query == "string") return {token: function(stream) {
|
20
|
-
if (stream.match(query)) return "searching";
|
21
|
-
stream.next();
|
22
|
-
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
23
|
-
}};
|
24
19
|
return {token: function(stream) {
|
25
20
|
if (stream.match(query)) return "searching";
|
26
21
|
while (!stream.eol()) {
|
27
22
|
stream.next();
|
28
|
-
if (startChar)
|
23
|
+
if (startChar && !caseInsensitive)
|
29
24
|
stream.skipTo(startChar) || stream.skipToEnd();
|
30
25
|
if (stream.match(query, false)) break;
|
31
26
|
}
|
@@ -74,7 +69,7 @@
|
|
74
69
|
if (!query || state.query) return;
|
75
70
|
state.query = parseQuery(query);
|
76
71
|
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
|
77
|
-
state.overlay = searchOverlay(state.query);
|
72
|
+
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
|
78
73
|
cm.addOverlay(state.overlay);
|
79
74
|
state.posFrom = state.posTo = cm.getCursor();
|
80
75
|
findNext(cm, rev);
|
@@ -32,7 +32,7 @@
|
|
32
32
|
* ESC - leave insert mode, visual mode, and clear input state.
|
33
33
|
* Ctrl-[, Ctrl-c - same as ESC.
|
34
34
|
*
|
35
|
-
* Registers:
|
35
|
+
* Registers: unnamed, -, a-z, A-Z, 0-9
|
36
36
|
* (Does not respect the special case for number registers when delete
|
37
37
|
* operator is made with these commands: %, (, ), , /, ?, n, N, {, } )
|
38
38
|
* TODO: Implement the remaining registers.
|
@@ -194,7 +194,7 @@
|
|
194
194
|
{ keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch',
|
195
195
|
motionArgs: { forward: false }},
|
196
196
|
{ keys: ['\'', 'character'], type: 'motion', motion: 'goToMark',
|
197
|
-
motionArgs: {toJumplist: true}},
|
197
|
+
motionArgs: {toJumplist: true, linewise: true}},
|
198
198
|
{ keys: ['`', 'character'], type: 'motion', motion: 'goToMark',
|
199
199
|
motionArgs: {toJumplist: true}},
|
200
200
|
{ keys: [']', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
|
@@ -210,6 +210,7 @@
|
|
210
210
|
{ keys: ['|'], type: 'motion',
|
211
211
|
motion: 'moveToColumn',
|
212
212
|
motionArgs: { }},
|
213
|
+
{ keys: ['o'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { },context:'visual'},
|
213
214
|
// Operators
|
214
215
|
{ keys: ['d'], type: 'operator', operator: 'delete' },
|
215
216
|
{ keys: ['y'], type: 'operator', operator: 'yank' },
|
@@ -271,6 +272,7 @@
|
|
271
272
|
{ keys: ['v'], type: 'action', action: 'toggleVisualMode' },
|
272
273
|
{ keys: ['V'], type: 'action', action: 'toggleVisualMode',
|
273
274
|
actionArgs: { linewise: true }},
|
275
|
+
{ keys: ['g', 'v'], type: 'action', action: 'reselectLastSelection' },
|
274
276
|
{ keys: ['J'], type: 'action', action: 'joinLines', isEdit: true },
|
275
277
|
{ keys: ['p'], type: 'action', action: 'paste', isEdit: true,
|
276
278
|
actionArgs: { after: true, isEdit: true }},
|
@@ -320,9 +322,11 @@
|
|
320
322
|
{ keys: ['?'], type: 'search',
|
321
323
|
searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
|
322
324
|
{ keys: ['*'], type: 'search',
|
323
|
-
searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
|
325
|
+
searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
|
324
326
|
{ keys: ['#'], type: 'search',
|
325
|
-
searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
|
327
|
+
searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
|
328
|
+
{ keys: ['g', '*'], type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
|
329
|
+
{ keys: ['g', '#'], type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
|
326
330
|
// Ex command
|
327
331
|
{ keys: [':'], type: 'ex' }
|
328
332
|
];
|
@@ -382,7 +386,7 @@
|
|
382
386
|
var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
|
383
387
|
'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
|
384
388
|
var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
|
385
|
-
var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"']);
|
389
|
+
var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':']);
|
386
390
|
|
387
391
|
function isLine(cm, line) {
|
388
392
|
return line >= cm.firstLine() && line <= cm.lastLine();
|
@@ -411,6 +415,41 @@
|
|
411
415
|
return false;
|
412
416
|
}
|
413
417
|
|
418
|
+
var options = {};
|
419
|
+
function defineOption(name, defaultValue, type) {
|
420
|
+
if (defaultValue === undefined) { throw Error('defaultValue is required'); }
|
421
|
+
if (!type) { type = 'string'; }
|
422
|
+
options[name] = {
|
423
|
+
type: type,
|
424
|
+
defaultValue: defaultValue
|
425
|
+
};
|
426
|
+
setOption(name, defaultValue);
|
427
|
+
}
|
428
|
+
|
429
|
+
function setOption(name, value) {
|
430
|
+
var option = options[name];
|
431
|
+
if (!option) {
|
432
|
+
throw Error('Unknown option: ' + name);
|
433
|
+
}
|
434
|
+
if (option.type == 'boolean') {
|
435
|
+
if (value && value !== true) {
|
436
|
+
throw Error('Invalid argument: ' + name + '=' + value);
|
437
|
+
} else if (value !== false) {
|
438
|
+
// Boolean options are set to true if value is not defined.
|
439
|
+
value = true;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
option.value = option.type == 'boolean' ? !!value : value;
|
443
|
+
}
|
444
|
+
|
445
|
+
function getOption(name) {
|
446
|
+
var option = options[name];
|
447
|
+
if (!option) {
|
448
|
+
throw Error('Unknown option: ' + name);
|
449
|
+
}
|
450
|
+
return option.value;
|
451
|
+
}
|
452
|
+
|
414
453
|
var createCircularJumpList = function() {
|
415
454
|
var size = 100;
|
416
455
|
var pointer = -1;
|
@@ -477,30 +516,52 @@
|
|
477
516
|
};
|
478
517
|
};
|
479
518
|
|
480
|
-
|
519
|
+
// Returns an object to track the changes associated insert mode. It
|
520
|
+
// clones the object that is passed in, or creates an empty object one if
|
521
|
+
// none is provided.
|
522
|
+
var createInsertModeChanges = function(c) {
|
523
|
+
if (c) {
|
524
|
+
// Copy construction
|
525
|
+
return {
|
526
|
+
changes: c.changes,
|
527
|
+
expectCursorActivityForChange: c.expectCursorActivityForChange
|
528
|
+
};
|
529
|
+
}
|
481
530
|
return {
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
changes: [], // Change list
|
487
|
-
expectCursorActivityForChange: false // Set to true on change, false on cursorActivity.
|
488
|
-
},
|
489
|
-
enteredMacroMode: undefined,
|
490
|
-
isMacroPlaying: false,
|
491
|
-
toggle: function(cm, registerName) {
|
492
|
-
if (this.enteredMacroMode) { //onExit
|
493
|
-
this.enteredMacroMode(); // close dialog
|
494
|
-
this.enteredMacroMode = undefined;
|
495
|
-
} else { //onEnter
|
496
|
-
this.latestRegister = registerName;
|
497
|
-
this.enteredMacroMode = cm.openDialog(
|
498
|
-
'(recording)['+registerName+']', null, {bottom:true});
|
499
|
-
}
|
500
|
-
}
|
531
|
+
// Change list
|
532
|
+
changes: [],
|
533
|
+
// Set to true on change, false on cursorActivity.
|
534
|
+
expectCursorActivityForChange: false
|
501
535
|
};
|
502
536
|
};
|
503
537
|
|
538
|
+
function MacroModeState() {
|
539
|
+
this.latestRegister = undefined;
|
540
|
+
this.isPlaying = false;
|
541
|
+
this.isRecording = false;
|
542
|
+
this.replaySearchQueries = [];
|
543
|
+
this.onRecordingDone = undefined;
|
544
|
+
this.lastInsertModeChanges = createInsertModeChanges();
|
545
|
+
}
|
546
|
+
MacroModeState.prototype = {
|
547
|
+
exitMacroRecordMode: function() {
|
548
|
+
var macroModeState = vimGlobalState.macroModeState;
|
549
|
+
macroModeState.onRecordingDone(); // close dialog
|
550
|
+
macroModeState.onRecordingDone = undefined;
|
551
|
+
macroModeState.isRecording = false;
|
552
|
+
},
|
553
|
+
enterMacroRecordMode: function(cm, registerName) {
|
554
|
+
var register =
|
555
|
+
vimGlobalState.registerController.getRegister(registerName);
|
556
|
+
if (register) {
|
557
|
+
register.clear();
|
558
|
+
this.latestRegister = registerName;
|
559
|
+
this.onRecordingDone = cm.openDialog(
|
560
|
+
'(recording)['+registerName+']', null, {bottom:true});
|
561
|
+
this.isRecording = true;
|
562
|
+
}
|
563
|
+
}
|
564
|
+
};
|
504
565
|
|
505
566
|
function maybeInitVimState(cm) {
|
506
567
|
if (!cm.state.vim) {
|
@@ -531,7 +592,8 @@
|
|
531
592
|
insertModeRepeat: undefined,
|
532
593
|
visualMode: false,
|
533
594
|
// If we are in visual line mode. No effect if visualMode is false.
|
534
|
-
visualLine: false
|
595
|
+
visualLine: false,
|
596
|
+
lastSelection: null
|
535
597
|
};
|
536
598
|
}
|
537
599
|
return cm.state.vim;
|
@@ -543,12 +605,18 @@
|
|
543
605
|
searchQuery: null,
|
544
606
|
// Whether we are searching backwards.
|
545
607
|
searchIsReversed: false,
|
608
|
+
// Replace part of the last substituted pattern
|
609
|
+
lastSubstituteReplacePart: undefined,
|
546
610
|
jumpList: createCircularJumpList(),
|
547
|
-
macroModeState:
|
611
|
+
macroModeState: new MacroModeState,
|
548
612
|
// Recording latest f, t, F or T motion command.
|
549
613
|
lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
|
550
614
|
registerController: new RegisterController({})
|
551
615
|
};
|
616
|
+
for (var optionName in options) {
|
617
|
+
var option = options[optionName];
|
618
|
+
option.value = option.defaultValue;
|
619
|
+
}
|
552
620
|
}
|
553
621
|
|
554
622
|
var vimApi= {
|
@@ -576,6 +644,9 @@
|
|
576
644
|
// Add user defined key bindings.
|
577
645
|
exCommandDispatcher.map(lhs, rhs, ctx);
|
578
646
|
},
|
647
|
+
setOption: setOption,
|
648
|
+
getOption: getOption,
|
649
|
+
defineOption: defineOption,
|
579
650
|
defineEx: function(name, prefix, func){
|
580
651
|
if (name.indexOf(prefix) !== 0) {
|
581
652
|
throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');
|
@@ -589,9 +660,9 @@
|
|
589
660
|
var command;
|
590
661
|
var vim = maybeInitVimState(cm);
|
591
662
|
var macroModeState = vimGlobalState.macroModeState;
|
592
|
-
if (macroModeState.
|
663
|
+
if (macroModeState.isRecording) {
|
593
664
|
if (key == 'q') {
|
594
|
-
|
665
|
+
macroModeState.exitMacroRecordMode();
|
595
666
|
vim.inputState = new InputState();
|
596
667
|
return;
|
597
668
|
}
|
@@ -621,6 +692,9 @@
|
|
621
692
|
// Increment count unless count is 0 and key is 0.
|
622
693
|
vim.inputState.pushRepeatDigit(key);
|
623
694
|
}
|
695
|
+
if (macroModeState.isRecording) {
|
696
|
+
logKey(macroModeState, key);
|
697
|
+
}
|
624
698
|
return;
|
625
699
|
}
|
626
700
|
if (command.type == 'keyToKey') {
|
@@ -629,7 +703,7 @@
|
|
629
703
|
this.handleKey(cm, command.toKeys[i]);
|
630
704
|
}
|
631
705
|
} else {
|
632
|
-
if (macroModeState.
|
706
|
+
if (macroModeState.isRecording) {
|
633
707
|
logKey(macroModeState, key);
|
634
708
|
}
|
635
709
|
commandDispatcher.processCommand(cm, vim, command);
|
@@ -650,7 +724,7 @@
|
|
650
724
|
this.motion = null;
|
651
725
|
this.motionArgs = null;
|
652
726
|
this.keyBuffer = []; // For matching multi-key commands.
|
653
|
-
this.registerName = null; // Defaults to the
|
727
|
+
this.registerName = null; // Defaults to the unnamed register.
|
654
728
|
}
|
655
729
|
InputState.prototype.pushRepeatDigit = function(n) {
|
656
730
|
if (!this.operator) {
|
@@ -681,29 +755,39 @@
|
|
681
755
|
*/
|
682
756
|
function Register(text, linewise) {
|
683
757
|
this.clear();
|
684
|
-
|
685
|
-
|
686
|
-
|
758
|
+
this.keyBuffer = [text || ''];
|
759
|
+
this.insertModeChanges = [];
|
760
|
+
this.searchQueries = [];
|
761
|
+
this.linewise = !!linewise;
|
687
762
|
}
|
688
763
|
Register.prototype = {
|
689
|
-
|
690
|
-
this.
|
764
|
+
setText: function(text, linewise) {
|
765
|
+
this.keyBuffer = [text || ''];
|
691
766
|
this.linewise = !!linewise;
|
692
767
|
},
|
693
|
-
|
768
|
+
pushText: function(text, linewise) {
|
694
769
|
// if this register has ever been set to linewise, use linewise.
|
695
770
|
if (linewise || this.linewise) {
|
696
|
-
this.
|
771
|
+
this.keyBuffer.push('\n');
|
697
772
|
this.linewise = true;
|
698
|
-
} else {
|
699
|
-
this.text += text;
|
700
773
|
}
|
774
|
+
this.keyBuffer.push(text);
|
775
|
+
},
|
776
|
+
pushInsertModeChanges: function(changes) {
|
777
|
+
this.insertModeChanges.push(createInsertModeChanges(changes));
|
778
|
+
},
|
779
|
+
pushSearchQuery: function(query) {
|
780
|
+
this.searchQueries.push(query);
|
701
781
|
},
|
702
782
|
clear: function() {
|
703
|
-
this.
|
783
|
+
this.keyBuffer = [];
|
784
|
+
this.insertModeChanges = [];
|
785
|
+
this.searchQueries = [];
|
704
786
|
this.linewise = false;
|
705
787
|
},
|
706
|
-
toString: function() {
|
788
|
+
toString: function() {
|
789
|
+
return this.keyBuffer.join('');
|
790
|
+
}
|
707
791
|
};
|
708
792
|
|
709
793
|
/*
|
@@ -716,14 +800,16 @@
|
|
716
800
|
*/
|
717
801
|
function RegisterController(registers) {
|
718
802
|
this.registers = registers;
|
719
|
-
this.
|
803
|
+
this.unnamedRegister = registers['"'] = new Register();
|
804
|
+
registers['.'] = new Register();
|
805
|
+
registers[':'] = new Register();
|
720
806
|
}
|
721
807
|
RegisterController.prototype = {
|
722
808
|
pushText: function(registerName, operator, text, linewise) {
|
723
809
|
if (linewise && text.charAt(0) == '\n') {
|
724
810
|
text = text.slice(1) + '\n';
|
725
811
|
}
|
726
|
-
if(linewise && text.charAt(text.length - 1) !== '\n'){
|
812
|
+
if (linewise && text.charAt(text.length - 1) !== '\n'){
|
727
813
|
text += '\n';
|
728
814
|
}
|
729
815
|
// Lowercase and uppercase registers refer to the same register.
|
@@ -752,7 +838,7 @@
|
|
752
838
|
break;
|
753
839
|
}
|
754
840
|
// Make sure the unnamed register is set to what just happened
|
755
|
-
this.
|
841
|
+
this.unnamedRegister.setText(text, linewise);
|
756
842
|
return;
|
757
843
|
}
|
758
844
|
|
@@ -760,22 +846,19 @@
|
|
760
846
|
var append = isUpperCase(registerName);
|
761
847
|
if (append) {
|
762
848
|
register.append(text, linewise);
|
763
|
-
// The
|
849
|
+
// The unnamed register always has the same value as the last used
|
764
850
|
// register.
|
765
|
-
this.
|
851
|
+
this.unnamedRegister.append(text, linewise);
|
766
852
|
} else {
|
767
|
-
register.
|
768
|
-
this.
|
853
|
+
register.setText(text, linewise);
|
854
|
+
this.unnamedRegister.setText(text, linewise);
|
769
855
|
}
|
770
856
|
},
|
771
|
-
setRegisterText: function(name, text, linewise) {
|
772
|
-
this.getRegister(name).set(text, linewise);
|
773
|
-
},
|
774
857
|
// Gets the register named @name. If one of @name doesn't already exist,
|
775
|
-
// create it. If @name is invalid, return the
|
858
|
+
// create it. If @name is invalid, return the unnamedRegister.
|
776
859
|
getRegister: function(name) {
|
777
860
|
if (!this.isValidRegister(name)) {
|
778
|
-
return this.
|
861
|
+
return this.unnamedRegister;
|
779
862
|
}
|
780
863
|
name = name.toLowerCase();
|
781
864
|
if (!this.registers[name]) {
|
@@ -811,7 +894,7 @@
|
|
811
894
|
// Match commands that take <character> as an argument.
|
812
895
|
if (command.keys[keys.length - 1] == 'character') {
|
813
896
|
selectedCharacter = keys[keys.length - 1];
|
814
|
-
if(selectedCharacter.length>1){
|
897
|
+
if (selectedCharacter.length>1){
|
815
898
|
switch(selectedCharacter){
|
816
899
|
case '<CR>':
|
817
900
|
selectedCharacter='\n';
|
@@ -973,6 +1056,7 @@
|
|
973
1056
|
return;
|
974
1057
|
}
|
975
1058
|
var forward = command.searchArgs.forward;
|
1059
|
+
var wholeWordOnly = command.searchArgs.wholeWordOnly;
|
976
1060
|
getSearchState(cm).setReversed(!forward);
|
977
1061
|
var promptPrefix = (forward) ? '/' : '?';
|
978
1062
|
var originalQuery = getSearchState(cm).getQuery();
|
@@ -993,6 +1077,10 @@
|
|
993
1077
|
function onPromptClose(query) {
|
994
1078
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
995
1079
|
handleQuery(query, true /** ignoreCase */, true /** smartCase */);
|
1080
|
+
var macroModeState = vimGlobalState.macroModeState;
|
1081
|
+
if (macroModeState.isRecording) {
|
1082
|
+
logSearchQuery(macroModeState, query);
|
1083
|
+
}
|
996
1084
|
}
|
997
1085
|
function onPromptKeyUp(_e, query) {
|
998
1086
|
var parsedQuery;
|
@@ -1023,13 +1111,19 @@
|
|
1023
1111
|
}
|
1024
1112
|
switch (command.searchArgs.querySrc) {
|
1025
1113
|
case 'prompt':
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1114
|
+
var macroModeState = vimGlobalState.macroModeState;
|
1115
|
+
if (macroModeState.isPlaying) {
|
1116
|
+
var query = macroModeState.replaySearchQueries.shift();
|
1117
|
+
handleQuery(query, true /** ignoreCase */, false /** smartCase */);
|
1118
|
+
} else {
|
1119
|
+
showPrompt(cm, {
|
1120
|
+
onClose: onPromptClose,
|
1121
|
+
prefix: promptPrefix,
|
1122
|
+
desc: searchPromptDesc,
|
1123
|
+
onKeyUp: onPromptKeyUp,
|
1124
|
+
onKeyDown: onPromptKeyDown
|
1125
|
+
});
|
1126
|
+
}
|
1033
1127
|
break;
|
1034
1128
|
case 'wordUnderCursor':
|
1035
1129
|
var word = expandWordUnderCursor(cm, false /** inclusive */,
|
@@ -1047,8 +1141,8 @@
|
|
1047
1141
|
}
|
1048
1142
|
var query = cm.getLine(word.start.line).substring(word.start.ch,
|
1049
1143
|
word.end.ch);
|
1050
|
-
if (isKeyword) {
|
1051
|
-
|
1144
|
+
if (isKeyword && wholeWordOnly) {
|
1145
|
+
query = '\\b' + query + '\\b';
|
1052
1146
|
} else {
|
1053
1147
|
query = escapeRegex(query);
|
1054
1148
|
}
|
@@ -1174,6 +1268,7 @@
|
|
1174
1268
|
selectionStart.ch -= 1;
|
1175
1269
|
}
|
1176
1270
|
selectionEnd = curEnd;
|
1271
|
+
selectionStart = (motionResult instanceof Array) ? curStart : selectionStart;
|
1177
1272
|
if (vim.visualLine) {
|
1178
1273
|
if (cursorIsBefore(selectionStart, selectionEnd)) {
|
1179
1274
|
selectionStart.ch = 0;
|
@@ -1243,7 +1338,7 @@
|
|
1243
1338
|
},
|
1244
1339
|
recordLastEdit: function(vim, inputState, actionCommand) {
|
1245
1340
|
var macroModeState = vimGlobalState.macroModeState;
|
1246
|
-
if (macroModeState.
|
1341
|
+
if (macroModeState.isPlaying) { return; }
|
1247
1342
|
vim.lastEditInputState = inputState;
|
1248
1343
|
vim.lastEditActionCommand = actionCommand;
|
1249
1344
|
macroModeState.lastInsertModeChanges.changes = [];
|
@@ -1288,13 +1383,24 @@
|
|
1288
1383
|
highlightSearchMatches(cm, query);
|
1289
1384
|
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
1290
1385
|
},
|
1291
|
-
goToMark: function(
|
1386
|
+
goToMark: function(cm, motionArgs, vim) {
|
1292
1387
|
var mark = vim.marks[motionArgs.selectedCharacter];
|
1293
1388
|
if (mark) {
|
1294
|
-
|
1389
|
+
var pos = mark.find();
|
1390
|
+
return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
|
1295
1391
|
}
|
1296
1392
|
return null;
|
1297
1393
|
},
|
1394
|
+
moveToOtherHighlightedEnd: function(cm) {
|
1395
|
+
var curEnd = copyCursor(cm.getCursor('head'));
|
1396
|
+
var curStart = copyCursor(cm.getCursor('anchor'));
|
1397
|
+
if (cursorIsBefore(curStart, curEnd)) {
|
1398
|
+
curEnd.ch += 1;
|
1399
|
+
} else if (cursorIsBefore(curEnd, curStart)) {
|
1400
|
+
curStart.ch -= 1;
|
1401
|
+
}
|
1402
|
+
return ([curEnd,curStart]);
|
1403
|
+
},
|
1298
1404
|
jumpToMark: function(cm, motionArgs, vim) {
|
1299
1405
|
var best = cm.getCursor();
|
1300
1406
|
for (var i = 0; i < motionArgs.repeat; i++) {
|
@@ -1368,7 +1474,7 @@
|
|
1368
1474
|
(line > last && cur.line == last)) {
|
1369
1475
|
return;
|
1370
1476
|
}
|
1371
|
-
if(motionArgs.toFirstChar){
|
1477
|
+
if (motionArgs.toFirstChar){
|
1372
1478
|
endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
1373
1479
|
vim.lastHPos = endCh;
|
1374
1480
|
}
|
@@ -1740,29 +1846,21 @@
|
|
1740
1846
|
}
|
1741
1847
|
cm.scrollTo(null, y);
|
1742
1848
|
},
|
1743
|
-
replayMacro: function(cm, actionArgs) {
|
1849
|
+
replayMacro: function(cm, actionArgs, vim) {
|
1744
1850
|
var registerName = actionArgs.selectedCharacter;
|
1745
1851
|
var repeat = actionArgs.repeat;
|
1746
1852
|
var macroModeState = vimGlobalState.macroModeState;
|
1747
1853
|
if (registerName == '@') {
|
1748
1854
|
registerName = macroModeState.latestRegister;
|
1749
1855
|
}
|
1750
|
-
var keyBuffer = parseRegisterToKeyBuffer(macroModeState, registerName);
|
1751
1856
|
while(repeat--){
|
1752
|
-
|
1857
|
+
executeMacroRegister(cm, vim, macroModeState, registerName);
|
1753
1858
|
}
|
1754
1859
|
},
|
1755
|
-
exitMacroRecordMode: function() {
|
1756
|
-
var macroModeState = vimGlobalState.macroModeState;
|
1757
|
-
macroModeState.toggle();
|
1758
|
-
parseKeyBufferToRegister(macroModeState.latestRegister,
|
1759
|
-
macroModeState.macroKeyBuffer);
|
1760
|
-
},
|
1761
1860
|
enterMacroRecordMode: function(cm, actionArgs) {
|
1762
1861
|
var macroModeState = vimGlobalState.macroModeState;
|
1763
1862
|
var registerName = actionArgs.selectedCharacter;
|
1764
|
-
macroModeState.
|
1765
|
-
emptyMacroKeyBuffer(macroModeState);
|
1863
|
+
macroModeState.enterMacroRecordMode(cm, registerName);
|
1766
1864
|
},
|
1767
1865
|
enterInsertMode: function(cm, actionArgs, vim) {
|
1768
1866
|
if (cm.getOption('readOnly')) { return; }
|
@@ -1789,7 +1887,7 @@
|
|
1789
1887
|
cm.setOption('keyMap', 'vim-insert');
|
1790
1888
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
|
1791
1889
|
}
|
1792
|
-
if (!vimGlobalState.macroModeState.
|
1890
|
+
if (!vimGlobalState.macroModeState.isPlaying) {
|
1793
1891
|
// Only record if not replaying.
|
1794
1892
|
cm.on('change', onChange);
|
1795
1893
|
cm.on('cursorActivity', onCursorActivity);
|
@@ -1857,6 +1955,21 @@
|
|
1857
1955
|
updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
|
1858
1956
|
: curStart);
|
1859
1957
|
},
|
1958
|
+
reselectLastSelection: function(cm, _actionArgs, vim) {
|
1959
|
+
if (vim.lastSelection) {
|
1960
|
+
var lastSelection = vim.lastSelection;
|
1961
|
+
cm.setSelection(lastSelection.curStart, lastSelection.curEnd);
|
1962
|
+
if (lastSelection.visualLine) {
|
1963
|
+
vim.visualMode = true;
|
1964
|
+
vim.visualLine = true;
|
1965
|
+
}
|
1966
|
+
else {
|
1967
|
+
vim.visualMode = true;
|
1968
|
+
vim.visualLine = false;
|
1969
|
+
}
|
1970
|
+
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
|
1971
|
+
}
|
1972
|
+
},
|
1860
1973
|
joinLines: function(cm, actionArgs, vim) {
|
1861
1974
|
var curStart, curEnd;
|
1862
1975
|
if (vim.visualMode) {
|
@@ -1906,11 +2019,12 @@
|
|
1906
2019
|
var cur = cm.getCursor();
|
1907
2020
|
var register = vimGlobalState.registerController.getRegister(
|
1908
2021
|
actionArgs.registerName);
|
1909
|
-
|
2022
|
+
var text = register.toString();
|
2023
|
+
if (!text) {
|
1910
2024
|
return;
|
1911
2025
|
}
|
1912
|
-
|
1913
|
-
text
|
2026
|
+
if (actionArgs.repeat > 1) {
|
2027
|
+
var text = Array(actionArgs.repeat + 1).join(text);
|
1914
2028
|
}
|
1915
2029
|
var linewise = register.linewise;
|
1916
2030
|
if (linewise) {
|
@@ -1965,7 +2079,7 @@
|
|
1965
2079
|
var curStart = cm.getCursor();
|
1966
2080
|
var replaceTo;
|
1967
2081
|
var curEnd;
|
1968
|
-
if(vim.visualMode){
|
2082
|
+
if (vim.visualMode){
|
1969
2083
|
curStart=cm.getCursor('start');
|
1970
2084
|
curEnd=cm.getCursor('end');
|
1971
2085
|
// workaround to catch the character under the cursor
|
@@ -1979,8 +2093,8 @@
|
|
1979
2093
|
}
|
1980
2094
|
curEnd = { line: curStart.line, ch: replaceTo };
|
1981
2095
|
}
|
1982
|
-
if(replaceWith=='\n'){
|
1983
|
-
if(!vim.visualMode) cm.replaceRange('', curStart, curEnd);
|
2096
|
+
if (replaceWith=='\n'){
|
2097
|
+
if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
|
1984
2098
|
// special case, where vim help says to replace by just one line-break
|
1985
2099
|
(CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
|
1986
2100
|
}else {
|
@@ -1988,7 +2102,7 @@
|
|
1988
2102
|
//replace all characters in range by selected, but keep linebreaks
|
1989
2103
|
replaceWithStr=replaceWithStr.replace(/[^\n]/g,replaceWith);
|
1990
2104
|
cm.replaceRange(replaceWithStr, curStart, curEnd);
|
1991
|
-
if(vim.visualMode){
|
2105
|
+
if (vim.visualMode){
|
1992
2106
|
cm.setCursor(curStart);
|
1993
2107
|
exitVisualMode(cm);
|
1994
2108
|
}else{
|
@@ -2009,9 +2123,9 @@
|
|
2009
2123
|
token = match[0];
|
2010
2124
|
start = match.index;
|
2011
2125
|
end = start + token.length;
|
2012
|
-
if(cur.ch < end)break;
|
2126
|
+
if (cur.ch < end)break;
|
2013
2127
|
}
|
2014
|
-
if(!actionArgs.backtrack && (end <= cur.ch))return;
|
2128
|
+
if (!actionArgs.backtrack && (end <= cur.ch))return;
|
2015
2129
|
if (token) {
|
2016
2130
|
var increment = actionArgs.increase ? 1 : -1;
|
2017
2131
|
var number = parseInt(token) + (increment * actionArgs.repeat);
|
@@ -2120,6 +2234,10 @@
|
|
2120
2234
|
function exitVisualMode(cm) {
|
2121
2235
|
cm.off('mousedown', exitVisualMode);
|
2122
2236
|
var vim = cm.state.vim;
|
2237
|
+
// can't use selection state here because yank has already reset its cursor
|
2238
|
+
vim.lastSelection = {'curStart': vim.marks['<'].find(),
|
2239
|
+
'curEnd': vim.marks['>'].find(), 'visualMode': vim.visualMode,
|
2240
|
+
'visualLine': vim.visualLine};
|
2123
2241
|
vim.visualMode = false;
|
2124
2242
|
vim.visualLine = false;
|
2125
2243
|
var selectionStart = cm.getCursor('anchor');
|
@@ -2243,7 +2361,7 @@
|
|
2243
2361
|
}
|
2244
2362
|
|
2245
2363
|
function recordJumpPosition(cm, oldCur, newCur) {
|
2246
|
-
if(!cursorEqual(oldCur, newCur)) {
|
2364
|
+
if (!cursorEqual(oldCur, newCur)) {
|
2247
2365
|
vimGlobalState.jumpList.add(cm, oldCur, newCur);
|
2248
2366
|
}
|
2249
2367
|
}
|
@@ -2266,7 +2384,7 @@
|
|
2266
2384
|
isComplete: function(state) {
|
2267
2385
|
if (state.nextCh === state.symb) {
|
2268
2386
|
state.depth++;
|
2269
|
-
if(state.depth >= 1)return true;
|
2387
|
+
if (state.depth >= 1)return true;
|
2270
2388
|
} else if (state.nextCh === state.reverseSymb) {
|
2271
2389
|
state.depth--;
|
2272
2390
|
}
|
@@ -2298,7 +2416,7 @@
|
|
2298
2416
|
state.reverseSymb = state.symb === '{' ? '}' : '{';
|
2299
2417
|
},
|
2300
2418
|
isComplete: function(state) {
|
2301
|
-
if(state.nextCh === state.symb)return true;
|
2419
|
+
if (state.nextCh === state.symb)return true;
|
2302
2420
|
return false;
|
2303
2421
|
}
|
2304
2422
|
},
|
@@ -2320,7 +2438,7 @@
|
|
2320
2438
|
}
|
2321
2439
|
state.depth--;
|
2322
2440
|
}
|
2323
|
-
if(token === 'else' && state.depth === 0)return true;
|
2441
|
+
if (token === 'else' && state.depth === 0)return true;
|
2324
2442
|
}
|
2325
2443
|
return false;
|
2326
2444
|
}
|
@@ -2345,10 +2463,10 @@
|
|
2345
2463
|
curMoveThrough: false
|
2346
2464
|
};
|
2347
2465
|
var mode = symbolToMode[symb];
|
2348
|
-
if(!mode)return cur;
|
2466
|
+
if (!mode)return cur;
|
2349
2467
|
var init = findSymbolModes[mode].init;
|
2350
2468
|
var isComplete = findSymbolModes[mode].isComplete;
|
2351
|
-
if(init)init(state);
|
2469
|
+
if (init) { init(state); }
|
2352
2470
|
while (line !== endLine && repeat) {
|
2353
2471
|
state.index += increment;
|
2354
2472
|
state.nextCh = state.lineText.charAt(state.index);
|
@@ -2633,14 +2751,14 @@
|
|
2633
2751
|
var end = findMatchedSymbol(cm, cur, revSymb);
|
2634
2752
|
var start = findMatchedSymbol(cm, end);
|
2635
2753
|
|
2636
|
-
if((start.line == end.line && start.ch > end.ch)
|
2754
|
+
if ((start.line == end.line && start.ch > end.ch)
|
2637
2755
|
|| (start.line > end.line)) {
|
2638
2756
|
var tmp = start;
|
2639
2757
|
start = end;
|
2640
2758
|
end = tmp;
|
2641
2759
|
}
|
2642
2760
|
|
2643
|
-
if(inclusive) {
|
2761
|
+
if (inclusive) {
|
2644
2762
|
end.ch += 1;
|
2645
2763
|
} else {
|
2646
2764
|
start.ch += 1;
|
@@ -2711,6 +2829,7 @@
|
|
2711
2829
|
}
|
2712
2830
|
|
2713
2831
|
// Search functions
|
2832
|
+
defineOption('pcre', true, 'boolean');
|
2714
2833
|
function SearchState() {}
|
2715
2834
|
SearchState.prototype = {
|
2716
2835
|
getQuery: function() {
|
@@ -2760,7 +2879,7 @@
|
|
2760
2879
|
}
|
2761
2880
|
|
2762
2881
|
// Translates a search string from ex (vim) syntax into javascript form.
|
2763
|
-
function
|
2882
|
+
function translateRegex(str) {
|
2764
2883
|
// When these match, add a '\' if unescaped or remove one if escaped.
|
2765
2884
|
var specials = ['|', '(', ')', '{'];
|
2766
2885
|
// Remove, but never add, a '\' for these.
|
@@ -2799,15 +2918,17 @@
|
|
2799
2918
|
}
|
2800
2919
|
|
2801
2920
|
// Translates the replace part of a search and replace from ex (vim) syntax into
|
2802
|
-
// javascript form. Similar to
|
2921
|
+
// javascript form. Similar to translateRegex, but additionally fixes back references
|
2803
2922
|
// (translates '\[0..9]' to '$[0..9]') and follows different rules for escaping '$'.
|
2804
|
-
function
|
2923
|
+
function translateRegexReplace(str) {
|
2805
2924
|
var escapeNextChar = false;
|
2806
2925
|
var out = [];
|
2807
2926
|
for (var i = -1; i < str.length; i++) {
|
2808
2927
|
var c = str.charAt(i) || '';
|
2809
2928
|
var n = str.charAt(i+1) || '';
|
2810
2929
|
if (escapeNextChar) {
|
2930
|
+
// At any point in the loop, escapeNextChar is true if the previous
|
2931
|
+
// character was a '\' and was not escaped.
|
2811
2932
|
out.push(c);
|
2812
2933
|
escapeNextChar = false;
|
2813
2934
|
} else {
|
@@ -2832,6 +2953,29 @@
|
|
2832
2953
|
return out.join('');
|
2833
2954
|
}
|
2834
2955
|
|
2956
|
+
// Unescape \ and / in the replace part, for PCRE mode.
|
2957
|
+
function unescapeRegexReplace(str) {
|
2958
|
+
var stream = new CodeMirror.StringStream(str);
|
2959
|
+
var output = [];
|
2960
|
+
while (!stream.eol()) {
|
2961
|
+
// Search for \.
|
2962
|
+
while (stream.peek() && stream.peek() != '\\') {
|
2963
|
+
output.push(stream.next());
|
2964
|
+
}
|
2965
|
+
if (stream.match('\\/', true)) {
|
2966
|
+
// \/ => /
|
2967
|
+
output.push('/');
|
2968
|
+
} else if (stream.match('\\\\', true)) {
|
2969
|
+
// \\ => \
|
2970
|
+
output.push('\\');
|
2971
|
+
} else {
|
2972
|
+
// Don't change anything
|
2973
|
+
output.push(stream.next());
|
2974
|
+
}
|
2975
|
+
}
|
2976
|
+
return output.join('');
|
2977
|
+
}
|
2978
|
+
|
2835
2979
|
/**
|
2836
2980
|
* Extract the regular expression from the query and return a Regexp object.
|
2837
2981
|
* Returns null if the query is blank.
|
@@ -2863,7 +3007,9 @@
|
|
2863
3007
|
if (!regexPart) {
|
2864
3008
|
return null;
|
2865
3009
|
}
|
2866
|
-
|
3010
|
+
if (!getOption('pcre')) {
|
3011
|
+
regexPart = translateRegex(regexPart);
|
3012
|
+
}
|
2867
3013
|
if (smartCase) {
|
2868
3014
|
ignoreCase = (/^[^A-Z]*$/).test(regexPart);
|
2869
3015
|
}
|
@@ -3045,13 +3191,16 @@
|
|
3045
3191
|
{ name: 'map' },
|
3046
3192
|
{ name: 'nmap', shortName: 'nm' },
|
3047
3193
|
{ name: 'vmap', shortName: 'vm' },
|
3194
|
+
{ name: 'unmap' },
|
3048
3195
|
{ name: 'write', shortName: 'w' },
|
3049
3196
|
{ name: 'undo', shortName: 'u' },
|
3050
3197
|
{ name: 'redo', shortName: 'red' },
|
3198
|
+
{ name: 'set', shortName: 'set' },
|
3051
3199
|
{ name: 'sort', shortName: 'sor' },
|
3052
3200
|
{ name: 'substitute', shortName: 's' },
|
3053
3201
|
{ name: 'nohlsearch', shortName: 'noh' },
|
3054
|
-
{ name: 'delmarks', shortName: 'delm' }
|
3202
|
+
{ name: 'delmarks', shortName: 'delm' },
|
3203
|
+
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true }
|
3055
3204
|
];
|
3056
3205
|
Vim.ExCommandDispatcher = function() {
|
3057
3206
|
this.buildCommandMap_();
|
@@ -3059,17 +3208,21 @@
|
|
3059
3208
|
Vim.ExCommandDispatcher.prototype = {
|
3060
3209
|
processCommand: function(cm, input) {
|
3061
3210
|
var vim = cm.state.vim;
|
3211
|
+
var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
|
3212
|
+
var previousCommand = commandHistoryRegister.toString();
|
3062
3213
|
if (vim.visualMode) {
|
3063
3214
|
exitVisualMode(cm);
|
3064
3215
|
}
|
3065
3216
|
var inputStream = new CodeMirror.StringStream(input);
|
3217
|
+
// update ": with the latest command whether valid or invalid
|
3218
|
+
commandHistoryRegister.setText(input);
|
3066
3219
|
var params = {};
|
3067
3220
|
params.input = input;
|
3068
3221
|
try {
|
3069
3222
|
this.parseInput_(cm, inputStream, params);
|
3070
3223
|
} catch(e) {
|
3071
3224
|
showConfirm(cm, e);
|
3072
|
-
|
3225
|
+
throw e;
|
3073
3226
|
}
|
3074
3227
|
var commandName;
|
3075
3228
|
if (!params.commandName) {
|
@@ -3081,6 +3234,9 @@
|
|
3081
3234
|
var command = this.matchCommand_(params.commandName);
|
3082
3235
|
if (command) {
|
3083
3236
|
commandName = command.name;
|
3237
|
+
if (command.excludeFromCommandHistory) {
|
3238
|
+
commandHistoryRegister.setText(previousCommand);
|
3239
|
+
}
|
3084
3240
|
this.parseCommandArgs_(inputStream, params, command);
|
3085
3241
|
if (command.type == 'exToKey') {
|
3086
3242
|
// Handle Ex to Key mapping.
|
@@ -3195,14 +3351,16 @@
|
|
3195
3351
|
this.commandMap_[commandName] = {
|
3196
3352
|
name: commandName,
|
3197
3353
|
type: 'exToEx',
|
3198
|
-
toInput: rhs.substring(1)
|
3354
|
+
toInput: rhs.substring(1),
|
3355
|
+
user: true
|
3199
3356
|
};
|
3200
3357
|
} else {
|
3201
3358
|
// Ex to key mapping
|
3202
3359
|
this.commandMap_[commandName] = {
|
3203
3360
|
name: commandName,
|
3204
3361
|
type: 'exToKey',
|
3205
|
-
toKeys: parseKeyString(rhs)
|
3362
|
+
toKeys: parseKeyString(rhs),
|
3363
|
+
user: true
|
3206
3364
|
};
|
3207
3365
|
}
|
3208
3366
|
} else {
|
@@ -3211,7 +3369,8 @@
|
|
3211
3369
|
var mapping = {
|
3212
3370
|
keys: parseKeyString(lhs),
|
3213
3371
|
type: 'keyToEx',
|
3214
|
-
exArgs: { input: rhs.substring(1) }
|
3372
|
+
exArgs: { input: rhs.substring(1) },
|
3373
|
+
user: true};
|
3215
3374
|
if (ctx) { mapping.context = ctx; }
|
3216
3375
|
defaultKeymap.unshift(mapping);
|
3217
3376
|
} else {
|
@@ -3219,12 +3378,45 @@
|
|
3219
3378
|
var mapping = {
|
3220
3379
|
keys: parseKeyString(lhs),
|
3221
3380
|
type: 'keyToKey',
|
3222
|
-
toKeys: parseKeyString(rhs)
|
3381
|
+
toKeys: parseKeyString(rhs),
|
3382
|
+
user: true
|
3223
3383
|
};
|
3224
3384
|
if (ctx) { mapping.context = ctx; }
|
3225
3385
|
defaultKeymap.unshift(mapping);
|
3226
3386
|
}
|
3227
3387
|
}
|
3388
|
+
},
|
3389
|
+
unmap: function(lhs, ctx) {
|
3390
|
+
var arrayEquals = function(a, b) {
|
3391
|
+
if (a === b) return true;
|
3392
|
+
if (a == null || b == null) return true;
|
3393
|
+
if (a.length != b.length) return false;
|
3394
|
+
for (var i = 0; i < a.length; i++) {
|
3395
|
+
if (a[i] !== b[i]) return false;
|
3396
|
+
}
|
3397
|
+
return true;
|
3398
|
+
};
|
3399
|
+
if (lhs != ':' && lhs.charAt(0) == ':') {
|
3400
|
+
// Ex to Ex or Ex to key mapping
|
3401
|
+
if (ctx) { throw Error('Mode not supported for ex mappings'); }
|
3402
|
+
var commandName = lhs.substring(1);
|
3403
|
+
if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
|
3404
|
+
delete this.commandMap_[commandName];
|
3405
|
+
return;
|
3406
|
+
}
|
3407
|
+
} else {
|
3408
|
+
// Key to Ex or key to key mapping
|
3409
|
+
var keys = parseKeyString(lhs);
|
3410
|
+
for (var i = 0; i < defaultKeymap.length; i++) {
|
3411
|
+
if (arrayEquals(keys, defaultKeymap[i].keys)
|
3412
|
+
&& defaultKeymap[i].context === ctx
|
3413
|
+
&& defaultKeymap[i].user) {
|
3414
|
+
defaultKeymap.splice(i, 1);
|
3415
|
+
return;
|
3416
|
+
}
|
3417
|
+
}
|
3418
|
+
}
|
3419
|
+
throw Error('No such mapping.');
|
3228
3420
|
}
|
3229
3421
|
};
|
3230
3422
|
|
@@ -3235,7 +3427,7 @@
|
|
3235
3427
|
var keys = [];
|
3236
3428
|
while (str) {
|
3237
3429
|
match = (/<\w+-.+?>|<\w+>|./).exec(str);
|
3238
|
-
if(match === null)break;
|
3430
|
+
if (match === null)break;
|
3239
3431
|
key = match[0];
|
3240
3432
|
str = str.substring(match.index + key.length);
|
3241
3433
|
keys.push(key);
|
@@ -3256,6 +3448,16 @@
|
|
3256
3448
|
},
|
3257
3449
|
nmap: function(cm, params) { this.map(cm, params, 'normal'); },
|
3258
3450
|
vmap: function(cm, params) { this.map(cm, params, 'visual'); },
|
3451
|
+
unmap: function(cm, params, ctx) {
|
3452
|
+
var mapArgs = params.args;
|
3453
|
+
if (!mapArgs || mapArgs.length < 1) {
|
3454
|
+
if (cm) {
|
3455
|
+
showConfirm(cm, 'No such mapping: ' + params.input);
|
3456
|
+
}
|
3457
|
+
return;
|
3458
|
+
}
|
3459
|
+
exCommandDispatcher.unmap(mapArgs[0], ctx);
|
3460
|
+
},
|
3259
3461
|
move: function(cm, params) {
|
3260
3462
|
commandDispatcher.processCommand(cm, cm.state.vim, {
|
3261
3463
|
type: 'motion',
|
@@ -3264,6 +3466,73 @@
|
|
3264
3466
|
linewise: true },
|
3265
3467
|
repeatOverride: params.line+1});
|
3266
3468
|
},
|
3469
|
+
set: function(cm, params) {
|
3470
|
+
var setArgs = params.args;
|
3471
|
+
if (!setArgs || setArgs.length < 1) {
|
3472
|
+
if (cm) {
|
3473
|
+
showConfirm(cm, 'Invalid mapping: ' + params.input);
|
3474
|
+
}
|
3475
|
+
return;
|
3476
|
+
}
|
3477
|
+
var expr = setArgs[0].split('=');
|
3478
|
+
var optionName = expr[0];
|
3479
|
+
var value = expr[1];
|
3480
|
+
var forceGet = false;
|
3481
|
+
|
3482
|
+
if (optionName.charAt(optionName.length - 1) == '?') {
|
3483
|
+
// If post-fixed with ?, then the set is actually a get.
|
3484
|
+
if (value) { throw Error('Trailing characters: ' + params.argString); }
|
3485
|
+
optionName = optionName.substring(0, optionName.length - 1);
|
3486
|
+
forceGet = true;
|
3487
|
+
}
|
3488
|
+
if (value === undefined && optionName.substring(0, 2) == 'no') {
|
3489
|
+
// To set boolean options to false, the option name is prefixed with
|
3490
|
+
// 'no'.
|
3491
|
+
optionName = optionName.substring(2);
|
3492
|
+
value = false;
|
3493
|
+
}
|
3494
|
+
var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
|
3495
|
+
if (optionIsBoolean && value == undefined) {
|
3496
|
+
// Calling set with a boolean option sets it to true.
|
3497
|
+
value = true;
|
3498
|
+
}
|
3499
|
+
if (!optionIsBoolean && !value || forceGet) {
|
3500
|
+
var oldValue = getOption(optionName);
|
3501
|
+
// If no value is provided, then we assume this is a get.
|
3502
|
+
if (oldValue === true || oldValue === false) {
|
3503
|
+
showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
|
3504
|
+
} else {
|
3505
|
+
showConfirm(cm, ' ' + optionName + '=' + oldValue);
|
3506
|
+
}
|
3507
|
+
} else {
|
3508
|
+
setOption(optionName, value);
|
3509
|
+
}
|
3510
|
+
},
|
3511
|
+
registers: function(cm,params) {
|
3512
|
+
var regArgs = params.args;
|
3513
|
+
var registers = vimGlobalState.registerController.registers;
|
3514
|
+
var regInfo = '----------Registers----------<br><br>';
|
3515
|
+
if (!regArgs) {
|
3516
|
+
for (var registerName in registers) {
|
3517
|
+
var text = registers[registerName].toString();
|
3518
|
+
if (text.length) {
|
3519
|
+
regInfo += '"' + registerName + ' ' + text + '<br>';
|
3520
|
+
}
|
3521
|
+
}
|
3522
|
+
} else {
|
3523
|
+
var registerName;
|
3524
|
+
regArgs = regArgs.join('');
|
3525
|
+
for (var i = 0; i < regArgs.length; i++) {
|
3526
|
+
registerName = regArgs.charAt(i);
|
3527
|
+
if (!vimGlobalState.registerController.isValidRegister(registerName)) {
|
3528
|
+
continue;
|
3529
|
+
}
|
3530
|
+
var register = registers[registerName] || new Register();
|
3531
|
+
regInfo += '"' + registerName + ' ' + register.toString() + '<br>';
|
3532
|
+
}
|
3533
|
+
}
|
3534
|
+
showConfirm(cm, regInfo);
|
3535
|
+
},
|
3267
3536
|
sort: function(cm, params) {
|
3268
3537
|
var reverse, ignoreCase, unique, number;
|
3269
3538
|
function parseArgs() {
|
@@ -3345,34 +3614,41 @@
|
|
3345
3614
|
'any other getSearchCursor implementation.');
|
3346
3615
|
}
|
3347
3616
|
var argString = params.argString;
|
3348
|
-
var slashes = findUnescapedSlashes(argString);
|
3349
|
-
if (slashes[0] !== 0) {
|
3350
|
-
showConfirm(cm, 'Substitutions should be of the form ' +
|
3351
|
-
':s/pattern/replace/');
|
3352
|
-
return;
|
3353
|
-
}
|
3354
|
-
var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
|
3617
|
+
var slashes = argString ? findUnescapedSlashes(argString) : [];
|
3355
3618
|
var replacePart = '';
|
3356
|
-
|
3357
|
-
|
3358
|
-
|
3359
|
-
|
3360
|
-
|
3361
|
-
|
3362
|
-
|
3363
|
-
|
3364
|
-
|
3365
|
-
//
|
3366
|
-
|
3367
|
-
|
3368
|
-
|
3369
|
-
|
3370
|
-
|
3371
|
-
|
3372
|
-
|
3373
|
-
|
3374
|
-
}
|
3375
|
-
|
3619
|
+
if (slashes.length) {
|
3620
|
+
if (slashes[0] !== 0) {
|
3621
|
+
showConfirm(cm, 'Substitutions should be of the form ' +
|
3622
|
+
':s/pattern/replace/');
|
3623
|
+
return;
|
3624
|
+
}
|
3625
|
+
var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
|
3626
|
+
var flagsPart;
|
3627
|
+
var count;
|
3628
|
+
var confirm = false; // Whether to confirm each replace.
|
3629
|
+
if (slashes[1]) {
|
3630
|
+
replacePart = argString.substring(slashes[1] + 1, slashes[2]);
|
3631
|
+
if (getOption('pcre')) {
|
3632
|
+
replacePart = unescapeRegexReplace(replacePart);
|
3633
|
+
} else {
|
3634
|
+
replacePart = translateRegexReplace(replacePart);
|
3635
|
+
}
|
3636
|
+
vimGlobalState.lastSubstituteReplacePart = replacePart;
|
3637
|
+
}
|
3638
|
+
if (slashes[2]) {
|
3639
|
+
// After the 3rd slash, we can have flags followed by a space followed
|
3640
|
+
// by count.
|
3641
|
+
var trailing = argString.substring(slashes[2] + 1).split(' ');
|
3642
|
+
flagsPart = trailing[0];
|
3643
|
+
count = parseInt(trailing[1]);
|
3644
|
+
}
|
3645
|
+
if (flagsPart) {
|
3646
|
+
if (flagsPart.indexOf('c') != -1) {
|
3647
|
+
confirm = true;
|
3648
|
+
flagsPart.replace('c', '');
|
3649
|
+
}
|
3650
|
+
regexPart = regexPart + '/' + flagsPart;
|
3651
|
+
}
|
3376
3652
|
}
|
3377
3653
|
if (regexPart) {
|
3378
3654
|
// If regex part is empty, then use the previous query. Otherwise use
|
@@ -3385,6 +3661,11 @@
|
|
3385
3661
|
return;
|
3386
3662
|
}
|
3387
3663
|
}
|
3664
|
+
replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
|
3665
|
+
if (replacePart === undefined) {
|
3666
|
+
showConfirm(cm, 'No previous substitute regular expression');
|
3667
|
+
return;
|
3668
|
+
}
|
3388
3669
|
var state = getSearchState(cm);
|
3389
3670
|
var query = state.getQuery();
|
3390
3671
|
var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
|
@@ -3629,13 +3910,15 @@
|
|
3629
3910
|
|
3630
3911
|
function exitInsertMode(cm) {
|
3631
3912
|
var vim = cm.state.vim;
|
3632
|
-
var
|
3633
|
-
|
3913
|
+
var macroModeState = vimGlobalState.macroModeState;
|
3914
|
+
var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
|
3915
|
+
var isPlaying = macroModeState.isPlaying;
|
3916
|
+
if (!isPlaying) {
|
3634
3917
|
cm.off('change', onChange);
|
3635
3918
|
cm.off('cursorActivity', onCursorActivity);
|
3636
3919
|
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
|
3637
3920
|
}
|
3638
|
-
if (!
|
3921
|
+
if (!isPlaying && vim.insertModeRepeat > 1) {
|
3639
3922
|
// Perform insert mode repeat for commands like 3,a and 3,o.
|
3640
3923
|
repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,
|
3641
3924
|
true /** repeatForInsert */);
|
@@ -3647,7 +3930,12 @@
|
|
3647
3930
|
cm.setOption('keyMap', 'vim');
|
3648
3931
|
cm.setOption('disableInput', true);
|
3649
3932
|
cm.toggleOverwrite(false); // exit replace mode if we were in it.
|
3933
|
+
// update the ". register before exiting insert mode
|
3934
|
+
insertModeChangeRegister.setText(macroModeState.lastInsertModeChanges.changes.join(''));
|
3650
3935
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
3936
|
+
if (macroModeState.isRecording) {
|
3937
|
+
logInsertModeChange(macroModeState);
|
3938
|
+
}
|
3651
3939
|
}
|
3652
3940
|
|
3653
3941
|
CodeMirror.keyMap['vim-insert'] = {
|
@@ -3671,45 +3959,57 @@
|
|
3671
3959
|
fallthrough: ['vim-insert']
|
3672
3960
|
};
|
3673
3961
|
|
3674
|
-
function
|
3675
|
-
var match, key;
|
3962
|
+
function executeMacroRegister(cm, vim, macroModeState, registerName) {
|
3676
3963
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
3677
|
-
var
|
3678
|
-
var
|
3679
|
-
|
3680
|
-
|
3681
|
-
|
3682
|
-
|
3683
|
-
|
3684
|
-
|
3685
|
-
|
3686
|
-
|
3687
|
-
|
3688
|
-
|
3689
|
-
|
3690
|
-
|
3691
|
-
|
3692
|
-
|
3964
|
+
var keyBuffer = register.keyBuffer;
|
3965
|
+
var imc = 0;
|
3966
|
+
macroModeState.isPlaying = true;
|
3967
|
+
macroModeState.replaySearchQueries = register.searchQueries.slice(0);
|
3968
|
+
for (var i = 0; i < keyBuffer.length; i++) {
|
3969
|
+
var text = keyBuffer[i];
|
3970
|
+
var match, key;
|
3971
|
+
while (text) {
|
3972
|
+
// Pull off one command key, which is either a single character
|
3973
|
+
// or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
|
3974
|
+
match = (/<\w+-.+?>|<\w+>|./).exec(text);
|
3975
|
+
key = match[0];
|
3976
|
+
text = text.substring(match.index + key.length);
|
3977
|
+
CodeMirror.Vim.handleKey(cm, key);
|
3978
|
+
if (vim.insertMode) {
|
3979
|
+
repeatInsertModeChanges(
|
3980
|
+
cm, register.insertModeChanges[imc++].changes, 1);
|
3981
|
+
exitInsertMode(cm);
|
3982
|
+
}
|
3983
|
+
}
|
3984
|
+
};
|
3985
|
+
macroModeState.isPlaying = false;
|
3693
3986
|
}
|
3694
3987
|
|
3695
|
-
function
|
3696
|
-
if(macroModeState.
|
3697
|
-
var
|
3698
|
-
|
3988
|
+
function logKey(macroModeState, key) {
|
3989
|
+
if (macroModeState.isPlaying) { return; }
|
3990
|
+
var registerName = macroModeState.latestRegister;
|
3991
|
+
var register = vimGlobalState.registerController.getRegister(registerName);
|
3992
|
+
if (register) {
|
3993
|
+
register.pushText(key);
|
3994
|
+
}
|
3699
3995
|
}
|
3700
3996
|
|
3701
|
-
function
|
3702
|
-
macroModeState.
|
3703
|
-
|
3704
|
-
|
3705
|
-
|
3706
|
-
|
3997
|
+
function logInsertModeChange(macroModeState) {
|
3998
|
+
if (macroModeState.isPlaying) { return; }
|
3999
|
+
var registerName = macroModeState.latestRegister;
|
4000
|
+
var register = vimGlobalState.registerController.getRegister(registerName);
|
4001
|
+
if (register) {
|
4002
|
+
register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
|
4003
|
+
}
|
3707
4004
|
}
|
3708
4005
|
|
3709
|
-
function
|
3710
|
-
if(macroModeState.
|
3711
|
-
var
|
3712
|
-
|
4006
|
+
function logSearchQuery(macroModeState, query) {
|
4007
|
+
if (macroModeState.isPlaying) { return; }
|
4008
|
+
var registerName = macroModeState.latestRegister;
|
4009
|
+
var register = vimGlobalState.registerController.getRegister(registerName);
|
4010
|
+
if (register) {
|
4011
|
+
register.pushSearchQuery(query);
|
4012
|
+
}
|
3713
4013
|
}
|
3714
4014
|
|
3715
4015
|
/**
|
@@ -3719,15 +4019,17 @@
|
|
3719
4019
|
function onChange(_cm, changeObj) {
|
3720
4020
|
var macroModeState = vimGlobalState.macroModeState;
|
3721
4021
|
var lastChange = macroModeState.lastInsertModeChanges;
|
3722
|
-
|
3723
|
-
|
3724
|
-
|
3725
|
-
|
3726
|
-
|
3727
|
-
|
4022
|
+
if (!macroModeState.isPlaying) {
|
4023
|
+
while(changeObj) {
|
4024
|
+
lastChange.expectCursorActivityForChange = true;
|
4025
|
+
if (changeObj.origin == '+input' || changeObj.origin == 'paste'
|
4026
|
+
|| changeObj.origin === undefined /* only in testing */) {
|
4027
|
+
var text = changeObj.text.join('\n');
|
4028
|
+
lastChange.changes.push(text);
|
4029
|
+
}
|
4030
|
+
// Change objects may be chained with next.
|
4031
|
+
changeObj = changeObj.next;
|
3728
4032
|
}
|
3729
|
-
// Change objects may be chained with next.
|
3730
|
-
changeObj = changeObj.next;
|
3731
4033
|
}
|
3732
4034
|
}
|
3733
4035
|
|
@@ -3738,6 +4040,7 @@
|
|
3738
4040
|
*/
|
3739
4041
|
function onCursorActivity() {
|
3740
4042
|
var macroModeState = vimGlobalState.macroModeState;
|
4043
|
+
if (macroModeState.isPlaying) { return; }
|
3741
4044
|
var lastChange = macroModeState.lastInsertModeChanges;
|
3742
4045
|
if (lastChange.expectCursorActivityForChange) {
|
3743
4046
|
lastChange.expectCursorActivityForChange = false;
|
@@ -3781,7 +4084,7 @@
|
|
3781
4084
|
*/
|
3782
4085
|
function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
|
3783
4086
|
var macroModeState = vimGlobalState.macroModeState;
|
3784
|
-
macroModeState.
|
4087
|
+
macroModeState.isPlaying = true;
|
3785
4088
|
var isAction = !!vim.lastEditActionCommand;
|
3786
4089
|
var cachedInputState = vim.inputState;
|
3787
4090
|
function repeatCommand() {
|
@@ -3793,10 +4096,15 @@
|
|
3793
4096
|
}
|
3794
4097
|
function repeatInsert(repeat) {
|
3795
4098
|
if (macroModeState.lastInsertModeChanges.changes.length > 0) {
|
3796
|
-
// For some reason, repeat cw in desktop VIM
|
4099
|
+
// For some reason, repeat cw in desktop VIM does not repeat
|
3797
4100
|
// insert mode changes. Will conform to that behavior.
|
3798
4101
|
repeat = !vim.lastEditActionCommand ? 1 : repeat;
|
3799
|
-
|
4102
|
+
var changeObject = macroModeState.lastInsertModeChanges;
|
4103
|
+
// This isn't strictly necessary, but since lastInsertModeChanges is
|
4104
|
+
// supposed to be immutable during replay, this helps catch bugs.
|
4105
|
+
macroModeState.lastInsertModeChanges = {};
|
4106
|
+
repeatInsertModeChanges(cm, changeObject.changes, repeat);
|
4107
|
+
macroModeState.lastInsertModeChanges = changeObject;
|
3800
4108
|
}
|
3801
4109
|
}
|
3802
4110
|
vim.inputState = vim.lastEditInputState;
|
@@ -3822,11 +4130,10 @@
|
|
3822
4130
|
// were called by an exitInsertMode call lower on the stack.
|
3823
4131
|
exitInsertMode(cm);
|
3824
4132
|
}
|
3825
|
-
macroModeState.
|
4133
|
+
macroModeState.isPlaying = false;
|
3826
4134
|
};
|
3827
4135
|
|
3828
|
-
function
|
3829
|
-
var lastChange = macroModeState.lastInsertModeChanges;
|
4136
|
+
function repeatInsertModeChanges(cm, changes, repeat) {
|
3830
4137
|
function keyHandler(binding) {
|
3831
4138
|
if (typeof binding == 'string') {
|
3832
4139
|
CodeMirror.commands[binding](cm);
|
@@ -3836,8 +4143,8 @@
|
|
3836
4143
|
return true;
|
3837
4144
|
}
|
3838
4145
|
for (var i = 0; i < repeat; i++) {
|
3839
|
-
for (var j = 0; j <
|
3840
|
-
var change =
|
4146
|
+
for (var j = 0; j < changes.length; j++) {
|
4147
|
+
var change = changes[j];
|
3841
4148
|
if (change instanceof InsertModeKey) {
|
3842
4149
|
CodeMirror.lookupKey(change.keyName, ['vim-insert'], keyHandler);
|
3843
4150
|
} else {
|