codemirror-rails 3.22 → 3.23
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 +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 {
|