codemirror-rails 3.00 → 3.02
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -2
- data/lib/codemirror/rails/version.rb +2 -2
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/db/.gitkeep +0 -0
- data/test/integration/codemirror_rails_integration_test.rb +1 -1
- data/vendor/assets/javascripts/codemirror.js +385 -152
- data/vendor/assets/javascripts/codemirror/keymaps/vim.js +279 -66
- data/vendor/assets/javascripts/codemirror/modes/apl.js +160 -0
- data/vendor/assets/javascripts/codemirror/modes/asterisk.js +183 -0
- data/vendor/assets/javascripts/codemirror/modes/clike.js +2 -0
- data/vendor/assets/javascripts/codemirror/modes/clojure.js +3 -3
- data/vendor/assets/javascripts/codemirror/modes/css.js +2 -2
- data/vendor/assets/javascripts/codemirror/modes/d.js +205 -0
- data/vendor/assets/javascripts/codemirror/modes/gfm.js +2 -1
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +13 -2
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +8 -7
- data/vendor/assets/javascripts/codemirror/modes/properties.js +0 -0
- data/vendor/assets/javascripts/codemirror/modes/sass.js +349 -0
- data/vendor/assets/javascripts/codemirror/modes/sieve.js +37 -10
- data/vendor/assets/javascripts/codemirror/modes/sql.js +268 -0
- data/vendor/assets/javascripts/codemirror/modes/xquery.js +8 -8
- data/vendor/assets/javascripts/codemirror/utils/collapserange.js +68 -0
- data/vendor/assets/javascripts/codemirror/utils/dialog.js +1 -0
- data/vendor/assets/javascripts/codemirror/utils/foldcode.js +2 -1
- data/vendor/assets/javascripts/codemirror/utils/formatting.js +9 -3
- data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +5 -4
- data/vendor/assets/javascripts/codemirror/utils/python-hint.js +93 -0
- data/vendor/assets/javascripts/codemirror/utils/runmode-standalone.js +51 -10
- data/vendor/assets/javascripts/codemirror/utils/search.js +20 -8
- data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +17 -10
- data/vendor/assets/stylesheets/codemirror.css +10 -9
- metadata +47 -15
- data/vendor/assets/javascripts/codemirror/modes/xmlpure.js +0 -490
- data/vendor/assets/stylesheets/codemirror/modes/diff.css +0 -3
@@ -115,6 +115,10 @@
|
|
115
115
|
motion: 'moveByWords',
|
116
116
|
motionArgs: { forward: false, wordEnd: true, bigWord: true,
|
117
117
|
inclusive: true }},
|
118
|
+
{ keys: ['{'], type: 'motion', motion: 'moveByParagraph',
|
119
|
+
motionArgs: { forward: false }},
|
120
|
+
{ keys: ['}'], type: 'motion', motion: 'moveByParagraph',
|
121
|
+
motionArgs: { forward: true }},
|
118
122
|
{ keys: ['Ctrl-f'], type: 'motion',
|
119
123
|
motion: 'moveByPage', motionArgs: { forward: true }},
|
120
124
|
{ keys: ['Ctrl-b'], type: 'motion',
|
@@ -247,7 +251,7 @@
|
|
247
251
|
var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
|
248
252
|
'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
|
249
253
|
var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
|
250
|
-
numbers);
|
254
|
+
numbers).concat(['<', '>']);
|
251
255
|
var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
|
252
256
|
numbers).concat('-\"'.split(''));
|
253
257
|
|
@@ -720,7 +724,11 @@
|
|
720
724
|
// Handle user defined Ex to Ex mappings
|
721
725
|
exCommandDispatcher.processCommand(cm, command.exArgs.input);
|
722
726
|
} else {
|
723
|
-
|
727
|
+
if (vim.visualMode) {
|
728
|
+
showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>');
|
729
|
+
} else {
|
730
|
+
showPrompt(cm, onPromptClose, ':');
|
731
|
+
}
|
724
732
|
}
|
725
733
|
},
|
726
734
|
evalInput: function(cm, vim) {
|
@@ -806,6 +814,12 @@
|
|
806
814
|
// CodeMirror can't figure out that we changed directions...
|
807
815
|
cm.setCursor(selectionStart);
|
808
816
|
cm.setSelection(selectionStart, selectionEnd);
|
817
|
+
updateMark(cm, vim, '<',
|
818
|
+
cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
|
819
|
+
: selectionEnd);
|
820
|
+
updateMark(cm, vim, '>',
|
821
|
+
cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
|
822
|
+
: selectionStart);
|
809
823
|
} else if (!operator) {
|
810
824
|
curEnd = clipCursorToContent(cm, curEnd);
|
811
825
|
cm.setCursor(curEnd.line, curEnd.ch);
|
@@ -923,6 +937,22 @@
|
|
923
937
|
cm.setCursor(curStart);
|
924
938
|
return curEnd;
|
925
939
|
},
|
940
|
+
moveByParagraph: function(cm, motionArgs) {
|
941
|
+
var line = cm.getCursor().line;
|
942
|
+
var repeat = motionArgs.repeat;
|
943
|
+
var inc = motionArgs.forward ? 1 : -1;
|
944
|
+
for (var i = 0; i < repeat; i++) {
|
945
|
+
if ((!motionArgs.forward && line === 0) ||
|
946
|
+
(motionArgs.forward && line == cm.lineCount() - 1)) {
|
947
|
+
break;
|
948
|
+
}
|
949
|
+
line += inc;
|
950
|
+
while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
|
951
|
+
line += inc;
|
952
|
+
}
|
953
|
+
}
|
954
|
+
return { line: line, ch: 0 };
|
955
|
+
},
|
926
956
|
moveByWords: function(cm, motionArgs) {
|
927
957
|
return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
|
928
958
|
!!motionArgs.wordEnd, !!motionArgs.bigWord);
|
@@ -1119,21 +1149,29 @@
|
|
1119
1149
|
cm.setSelection(curStart, curEnd);
|
1120
1150
|
}
|
1121
1151
|
} else {
|
1152
|
+
curStart = cm.getCursor('anchor');
|
1153
|
+
curEnd = cm.getCursor('head');
|
1122
1154
|
if (!vim.visualLine && actionArgs.linewise) {
|
1123
1155
|
// Shift-V pressed in characterwise visual mode. Switch to linewise
|
1124
1156
|
// visual mode instead of exiting visual mode.
|
1125
1157
|
vim.visualLine = true;
|
1126
|
-
curStart = cm.getCursor('anchor');
|
1127
|
-
curEnd = cm.getCursor('head');
|
1128
1158
|
curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
|
1129
1159
|
lineLength(cm, curStart.line);
|
1130
1160
|
curEnd.ch = cursorIsBefore(curStart, curEnd) ?
|
1131
1161
|
lineLength(cm, curEnd.line) : 0;
|
1132
1162
|
cm.setSelection(curStart, curEnd);
|
1163
|
+
} else if (vim.visualLine && !actionArgs.linewise) {
|
1164
|
+
// v pressed in linewise visual mode. Switch to characterwise visual
|
1165
|
+
// mode instead of exiting visual mode.
|
1166
|
+
vim.visualLine = false;
|
1133
1167
|
} else {
|
1134
1168
|
exitVisualMode(cm, vim);
|
1135
1169
|
}
|
1136
1170
|
}
|
1171
|
+
updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
|
1172
|
+
: curEnd);
|
1173
|
+
updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
|
1174
|
+
: curStart);
|
1137
1175
|
},
|
1138
1176
|
joinLines: function(cm, actionArgs, vim) {
|
1139
1177
|
var curStart, curEnd;
|
@@ -1232,13 +1270,7 @@
|
|
1232
1270
|
},
|
1233
1271
|
setMark: function(cm, actionArgs, vim) {
|
1234
1272
|
var markName = actionArgs.selectedCharacter;
|
1235
|
-
|
1236
|
-
return;
|
1237
|
-
}
|
1238
|
-
if (vim.marks[markName]) {
|
1239
|
-
vim.marks[markName].clear();
|
1240
|
-
}
|
1241
|
-
vim.marks[markName] = cm.setBookmark(cm.getCursor());
|
1273
|
+
updateMark(cm, vim, markName, cm.getCursor());
|
1242
1274
|
},
|
1243
1275
|
replace: function(cm, actionArgs) {
|
1244
1276
|
var replaceWith = actionArgs.selectedCharacter;
|
@@ -1636,6 +1668,16 @@
|
|
1636
1668
|
return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
|
1637
1669
|
}
|
1638
1670
|
|
1671
|
+
function updateMark(cm, vim, markName, pos) {
|
1672
|
+
if (!inArray(markName, validMarks)) {
|
1673
|
+
return;
|
1674
|
+
}
|
1675
|
+
if (vim.marks[markName]) {
|
1676
|
+
vim.marks[markName].clear();
|
1677
|
+
}
|
1678
|
+
vim.marks[markName] = cm.setBookmark(pos);
|
1679
|
+
}
|
1680
|
+
|
1639
1681
|
function charIdxInLine(start, line, character, forward, includeChar) {
|
1640
1682
|
// Search for char in line.
|
1641
1683
|
// motion_options: {forward, includeChar}
|
@@ -1804,6 +1846,12 @@
|
|
1804
1846
|
setMarked: function(marked) {
|
1805
1847
|
this.marked = marked;
|
1806
1848
|
},
|
1849
|
+
getOverlay: function() {
|
1850
|
+
return this.searchOverlay;
|
1851
|
+
},
|
1852
|
+
setOverlay: function(overlay) {
|
1853
|
+
this.searchOverlay = overlay;
|
1854
|
+
},
|
1807
1855
|
isReversed: function() {
|
1808
1856
|
return getVimGlobalState().isReversed;
|
1809
1857
|
},
|
@@ -1815,9 +1863,9 @@
|
|
1815
1863
|
var vim = getVimState(cm);
|
1816
1864
|
return vim.searchState_ || (vim.searchState_ = new SearchState());
|
1817
1865
|
}
|
1818
|
-
function dialog(cm, text, shortText, callback) {
|
1866
|
+
function dialog(cm, text, shortText, callback, initialValue) {
|
1819
1867
|
if (cm.openDialog) {
|
1820
|
-
cm.openDialog(text, callback, {bottom: true});
|
1868
|
+
cm.openDialog(text, callback, { bottom: true, value: initialValue });
|
1821
1869
|
}
|
1822
1870
|
else {
|
1823
1871
|
callback(prompt(shortText, ""));
|
@@ -1899,9 +1947,10 @@
|
|
1899
1947
|
return raw;
|
1900
1948
|
}
|
1901
1949
|
var searchPromptDesc = '(Javascript regexp)';
|
1902
|
-
function showPrompt(cm, onPromptClose, prefix, desc) {
|
1950
|
+
function showPrompt(cm, onPromptClose, prefix, desc, initialValue) {
|
1903
1951
|
var shortText = (prefix || '') + ' ' + (desc || '');
|
1904
|
-
dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose
|
1952
|
+
dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose,
|
1953
|
+
initialValue);
|
1905
1954
|
}
|
1906
1955
|
function regexEqual(r1, r2) {
|
1907
1956
|
if (r1 instanceof RegExp && r2 instanceof RegExp) {
|
@@ -1934,17 +1983,53 @@
|
|
1934
1983
|
state.setQuery(query);
|
1935
1984
|
});
|
1936
1985
|
}
|
1986
|
+
function searchOverlay(query) {
|
1987
|
+
return {
|
1988
|
+
token: function(stream) {
|
1989
|
+
var match = stream.match(query, false);
|
1990
|
+
if (match) {
|
1991
|
+
if (!stream.sol()) {
|
1992
|
+
// Backtrack 1 to match \b
|
1993
|
+
stream.backUp(1);
|
1994
|
+
if (!query.exec(stream.next() + match[0])) {
|
1995
|
+
stream.next();
|
1996
|
+
return null;
|
1997
|
+
}
|
1998
|
+
}
|
1999
|
+
stream.match(query);
|
2000
|
+
return "searching";
|
2001
|
+
}
|
2002
|
+
while (!stream.eol()) {
|
2003
|
+
stream.next();
|
2004
|
+
if (stream.match(query, false)) break;
|
2005
|
+
}
|
2006
|
+
},
|
2007
|
+
query: query
|
2008
|
+
};
|
2009
|
+
}
|
1937
2010
|
function highlightSearchMatches(cm, query) {
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
|
2011
|
+
if (cm.addOverlay) {
|
2012
|
+
var overlay = getSearchState(cm).getOverlay();
|
2013
|
+
if (!overlay || query != overlay.query) {
|
2014
|
+
if (overlay) {
|
2015
|
+
cm.removeOverlay(overlay);
|
2016
|
+
}
|
2017
|
+
overlay = searchOverlay(query);
|
2018
|
+
cm.addOverlay(overlay);
|
2019
|
+
getSearchState(cm).setOverlay(overlay);
|
2020
|
+
}
|
2021
|
+
} else {
|
2022
|
+
// TODO: Highlight only text inside the viewport. Highlighting everything
|
2023
|
+
// is inefficient and expensive.
|
2024
|
+
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
2025
|
+
var marked = [];
|
2026
|
+
for (var cursor = cm.getSearchCursor(query);
|
2027
|
+
cursor.findNext();) {
|
2028
|
+
marked.push(cm.markText(cursor.from(), cursor.to(),
|
2029
|
+
{ className: 'cm-searching' }));
|
2030
|
+
}
|
2031
|
+
getSearchState(cm).setMarked(marked);
|
1946
2032
|
}
|
1947
|
-
getSearchState(cm).setMarked(marked);
|
1948
2033
|
}
|
1949
2034
|
}
|
1950
2035
|
function findNext(cm, prev, repeat) {
|
@@ -1978,20 +2063,52 @@
|
|
1978
2063
|
return cursor.from();
|
1979
2064
|
});}
|
1980
2065
|
function clearSearchHighlight(cm) {
|
1981
|
-
cm.
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
marked
|
2066
|
+
if (cm.addOverlay) {
|
2067
|
+
cm.removeOverlay(getSearchState(cm).getOverlay());
|
2068
|
+
getSearchState(cm).setOverlay(null);
|
2069
|
+
} else {
|
2070
|
+
cm.operation(function() {
|
2071
|
+
var state = getSearchState(cm);
|
2072
|
+
if (!state.getQuery()) {
|
2073
|
+
return;
|
2074
|
+
}
|
2075
|
+
var marked = state.getMarked();
|
2076
|
+
if (!marked) {
|
2077
|
+
return;
|
2078
|
+
}
|
2079
|
+
for (var i = 0; i < marked.length; ++i) {
|
2080
|
+
marked[i].clear();
|
2081
|
+
}
|
2082
|
+
state.setMarked(null);
|
2083
|
+
});
|
2084
|
+
}
|
2085
|
+
}
|
2086
|
+
/**
|
2087
|
+
* Check if pos is in the specified range, INCLUSIVE.
|
2088
|
+
* Range can be specified with 1 or 2 arguments.
|
2089
|
+
* If the first range argument is an array, treat it as an array of line
|
2090
|
+
* numbers. Match pos against any of the lines.
|
2091
|
+
* If the first range argument is a number,
|
2092
|
+
* if there is only 1 range argument, check if pos has the same line
|
2093
|
+
* number
|
2094
|
+
* if there are 2 range arguments, then check if pos is in between the two
|
2095
|
+
* range arguments.
|
2096
|
+
*/
|
2097
|
+
function isInRange(pos, start, end) {
|
2098
|
+
if (typeof pos != 'number') {
|
2099
|
+
// Assume it is a cursor position. Get the line number.
|
2100
|
+
pos = pos.line;
|
2101
|
+
}
|
2102
|
+
if (start instanceof Array) {
|
2103
|
+
return inArray(pos, start);
|
2104
|
+
} else {
|
2105
|
+
if (end) {
|
2106
|
+
return (pos >= start && pos <= end);
|
2107
|
+
} else {
|
2108
|
+
return pos == start;
|
1992
2109
|
}
|
1993
|
-
|
1994
|
-
|
2110
|
+
}
|
2111
|
+
}
|
1995
2112
|
|
1996
2113
|
// Ex command handling
|
1997
2114
|
// Care must be taken when adding to the default Ex command map. For any
|
@@ -2001,14 +2118,23 @@
|
|
2001
2118
|
{ name: 'map', type: 'builtIn' },
|
2002
2119
|
{ name: 'write', shortName: 'w', type: 'builtIn' },
|
2003
2120
|
{ name: 'undo', shortName: 'u', type: 'builtIn' },
|
2004
|
-
{ name: 'redo', shortName: 'red', type: 'builtIn' }
|
2121
|
+
{ name: 'redo', shortName: 'red', type: 'builtIn' },
|
2122
|
+
{ name: 'substitute', shortName: 's', type: 'builtIn'}
|
2005
2123
|
];
|
2006
|
-
|
2124
|
+
Vim.ExCommandDispatcher = function() {
|
2007
2125
|
this.buildCommandMap_();
|
2008
2126
|
};
|
2009
|
-
ExCommandDispatcher.prototype = {
|
2127
|
+
Vim.ExCommandDispatcher.prototype = {
|
2010
2128
|
processCommand: function(cm, input) {
|
2011
|
-
var
|
2129
|
+
var inputStream = new CodeMirror.StringStream(input);
|
2130
|
+
var params = {};
|
2131
|
+
params.input = input;
|
2132
|
+
try {
|
2133
|
+
this.parseInput_(cm, inputStream, params);
|
2134
|
+
} catch(e) {
|
2135
|
+
showConfirm(cm, e);
|
2136
|
+
return;
|
2137
|
+
}
|
2012
2138
|
var commandName;
|
2013
2139
|
if (!params.commandName) {
|
2014
2140
|
// If only a line range is defined, move to the line.
|
@@ -2019,6 +2145,7 @@
|
|
2019
2145
|
var command = this.matchCommand_(params.commandName);
|
2020
2146
|
if (command) {
|
2021
2147
|
commandName = command.name;
|
2148
|
+
this.parseCommandArgs_(inputStream, params, command);
|
2022
2149
|
if (command.type == 'exToKey') {
|
2023
2150
|
// Handle Ex to Key mapping.
|
2024
2151
|
for (var i = 0; i < command.toKeys.length; i++) {
|
@@ -2038,37 +2165,63 @@
|
|
2038
2165
|
}
|
2039
2166
|
exCommands[commandName](cm, params);
|
2040
2167
|
},
|
2041
|
-
parseInput_: function(
|
2042
|
-
|
2043
|
-
result.input = input;
|
2044
|
-
var idx = 0;
|
2045
|
-
// Trim preceding ':'.
|
2046
|
-
var colons = (/^:+/).exec(input);
|
2047
|
-
if (colons) {
|
2048
|
-
idx += colons[0].length;
|
2049
|
-
}
|
2050
|
-
|
2168
|
+
parseInput_: function(cm, inputStream, result) {
|
2169
|
+
inputStream.eatWhile(':');
|
2051
2170
|
// Parse range.
|
2052
|
-
|
2053
|
-
|
2054
|
-
result.
|
2055
|
-
|
2171
|
+
if (inputStream.eat('%')) {
|
2172
|
+
result.line = 0;
|
2173
|
+
result.lineEnd = cm.lineCount() - 1;
|
2174
|
+
} else {
|
2175
|
+
result.line = this.parseLineSpec_(cm, inputStream);
|
2176
|
+
if (result.line !== undefined && inputStream.eat(',')) {
|
2177
|
+
result.lineEnd = this.parseLineSpec_(cm, inputStream);
|
2178
|
+
}
|
2056
2179
|
}
|
2057
2180
|
|
2058
2181
|
// Parse command name.
|
2059
|
-
var commandMatch = (/^(\w+)/)
|
2182
|
+
var commandMatch = inputStream.match(/^(\w+)/);
|
2060
2183
|
if (commandMatch) {
|
2061
2184
|
result.commandName = commandMatch[1];
|
2062
|
-
|
2185
|
+
} else {
|
2186
|
+
result.commandName = inputStream.match(/.*/)[0];
|
2063
2187
|
}
|
2064
2188
|
|
2189
|
+
return result;
|
2190
|
+
},
|
2191
|
+
parseLineSpec_: function(cm, inputStream) {
|
2192
|
+
var numberMatch = inputStream.match(/^(\d+)/);
|
2193
|
+
if (numberMatch) {
|
2194
|
+
return parseInt(numberMatch[1], 10) - 1;
|
2195
|
+
}
|
2196
|
+
switch (inputStream.next()) {
|
2197
|
+
case '.':
|
2198
|
+
return cm.getCursor().line;
|
2199
|
+
case '$':
|
2200
|
+
return cm.lineCount() - 1;
|
2201
|
+
case '\'':
|
2202
|
+
var mark = getVimState(cm).marks[inputStream.next()];
|
2203
|
+
if (mark && mark.find()) {
|
2204
|
+
return mark.find().line;
|
2205
|
+
} else {
|
2206
|
+
throw "Mark not set";
|
2207
|
+
}
|
2208
|
+
break;
|
2209
|
+
default:
|
2210
|
+
inputStream.backUp(1);
|
2211
|
+
return cm.getCursor().line;
|
2212
|
+
}
|
2213
|
+
},
|
2214
|
+
parseCommandArgs_: function(inputStream, params, command) {
|
2215
|
+
if (inputStream.eol()) {
|
2216
|
+
return;
|
2217
|
+
}
|
2218
|
+
params.argString = inputStream.match(/.*/)[0];
|
2065
2219
|
// Parse command-line arguments
|
2066
|
-
var
|
2220
|
+
var delim = command.argDelimiter || /\s+/;
|
2221
|
+
var args = trim(params.argString).split(delim);
|
2067
2222
|
if (args.length && args[0]) {
|
2068
|
-
|
2223
|
+
params.args = args;
|
2069
2224
|
}
|
2070
|
-
|
2071
|
-
return result;
|
2072
2225
|
},
|
2073
2226
|
matchCommand_: function(commandName) {
|
2074
2227
|
// Return the command in the command map that matches the shortest
|
@@ -2095,9 +2248,9 @@
|
|
2095
2248
|
}
|
2096
2249
|
},
|
2097
2250
|
map: function(lhs, rhs) {
|
2098
|
-
if (lhs.charAt(0) == ':') {
|
2251
|
+
if (lhs != ':' && lhs.charAt(0) == ':') {
|
2099
2252
|
var commandName = lhs.substring(1);
|
2100
|
-
if (rhs.charAt(0) == ':') {
|
2253
|
+
if (rhs != ':' && rhs.charAt(0) == ':') {
|
2101
2254
|
// Ex to Ex mapping
|
2102
2255
|
this.commandMap_[commandName] = {
|
2103
2256
|
name: commandName,
|
@@ -2113,7 +2266,7 @@
|
|
2113
2266
|
};
|
2114
2267
|
}
|
2115
2268
|
} else {
|
2116
|
-
if (rhs.charAt(0) == ':') {
|
2269
|
+
if (rhs != ':' && rhs.charAt(0) == ':') {
|
2117
2270
|
// Key to Ex mapping.
|
2118
2271
|
defaultKeymap.unshift({
|
2119
2272
|
keys: parseKeyString(lhs),
|
@@ -2172,7 +2325,7 @@
|
|
2172
2325
|
|
2173
2326
|
var exCommands = {
|
2174
2327
|
map: function(cm, params) {
|
2175
|
-
var mapArgs = params.
|
2328
|
+
var mapArgs = params.args;
|
2176
2329
|
if (!mapArgs || mapArgs.length < 2) {
|
2177
2330
|
if (cm) {
|
2178
2331
|
showConfirm(cm, 'Invalid mapping: ' + params.input);
|
@@ -2187,6 +2340,66 @@
|
|
2187
2340
|
motionArgs: { forward: false, explicitRepeat: true,
|
2188
2341
|
linewise: true, repeat: params.line }});
|
2189
2342
|
},
|
2343
|
+
substitute: function(cm, params) {
|
2344
|
+
var argString = params.argString;
|
2345
|
+
var slashes = findUnescapedSlashes(argString);
|
2346
|
+
if (slashes[0] !== 0) {
|
2347
|
+
showConfirm(cm, 'Substitutions should be of the form ' +
|
2348
|
+
':s/pattern/replace/');
|
2349
|
+
return;
|
2350
|
+
}
|
2351
|
+
var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
|
2352
|
+
var replacePart = '';
|
2353
|
+
var flagsPart;
|
2354
|
+
var count;
|
2355
|
+
if (slashes[1]) {
|
2356
|
+
replacePart = argString.substring(slashes[1] + 1, slashes[2]);
|
2357
|
+
}
|
2358
|
+
if (slashes[2]) {
|
2359
|
+
// After the 3rd slash, we can have flags followed by a space followed
|
2360
|
+
// by count.
|
2361
|
+
var trailing = argString.substring(slashes[2] + 1).split(' ');
|
2362
|
+
flagsPart = trailing[0];
|
2363
|
+
count = parseInt(trailing[1]);
|
2364
|
+
}
|
2365
|
+
if (flagsPart) {
|
2366
|
+
regexPart = regexPart + '/' + flagsPart;
|
2367
|
+
}
|
2368
|
+
if (regexPart) {
|
2369
|
+
// If regex part is empty, then use the previous query. Otherwise use
|
2370
|
+
// the regex part as the new query.
|
2371
|
+
updateSearchQuery(cm, regexPart, true /** ignoreCase */,
|
2372
|
+
true /** smartCase */);
|
2373
|
+
}
|
2374
|
+
var state = getSearchState(cm);
|
2375
|
+
var query = state.getQuery();
|
2376
|
+
var lineStart = params.line || 0;
|
2377
|
+
var lineEnd = params.lineEnd || lineStart;
|
2378
|
+
if (count) {
|
2379
|
+
lineStart = lineEnd;
|
2380
|
+
lineEnd = lineStart + count - 1;
|
2381
|
+
}
|
2382
|
+
var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
|
2383
|
+
function doReplace() {
|
2384
|
+
for (var cursor = cm.getSearchCursor(query, startPos);
|
2385
|
+
cursor.findNext() &&
|
2386
|
+
isInRange(cursor.from(), lineStart, lineEnd);) {
|
2387
|
+
var text = cm.getRange(cursor.from(), cursor.to());
|
2388
|
+
var newText = text.replace(query, replacePart);
|
2389
|
+
cursor.replace(newText);
|
2390
|
+
}
|
2391
|
+
var vim = getVimState(cm);
|
2392
|
+
if (vim.visualMode) {
|
2393
|
+
exitVisualMode(cm, vim);
|
2394
|
+
}
|
2395
|
+
}
|
2396
|
+
if (cm.compoundChange) {
|
2397
|
+
// Only exists in v2
|
2398
|
+
cm.compoundChange(doReplace);
|
2399
|
+
} else {
|
2400
|
+
cm.operation(doReplace);
|
2401
|
+
}
|
2402
|
+
},
|
2190
2403
|
redo: CodeMirror.commands.redo,
|
2191
2404
|
undo: CodeMirror.commands.undo,
|
2192
2405
|
write: function(cm) {
|
@@ -2200,7 +2413,7 @@
|
|
2200
2413
|
}
|
2201
2414
|
};
|
2202
2415
|
|
2203
|
-
var exCommandDispatcher = new ExCommandDispatcher();
|
2416
|
+
var exCommandDispatcher = new Vim.ExCommandDispatcher();
|
2204
2417
|
|
2205
2418
|
// Register Vim with CodeMirror
|
2206
2419
|
function buildVimKeyMap() {
|