in lib/ace/keyboard/vim.js [5683:5773]
substitute: function(cm, params) {
if (!cm.getSearchCursor) {
throw new Error('Search feature not available. Requires searchcursor.js or ' +
'any other getSearchCursor implementation.');
}
var argString = params.argString;
var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
var regexPart, replacePart = '', trailing, flagsPart, count;
var confirm = false; // Whether to confirm each replace.
var global = false; // True to replace all instances on a line, false to replace only 1.
if (tokens.length) {
regexPart = tokens[0];
if (getOption('pcre') && regexPart !== '') {
regexPart = new RegExp(regexPart).source; //normalize not escaped characters
}
replacePart = tokens[1];
if (regexPart && regexPart[regexPart.length - 1] === '$') {
regexPart = regexPart.slice(0, regexPart.length - 1) + '\\n';
replacePart = replacePart ? replacePart + '\n' : '\n';
}
if (replacePart !== undefined) {
if (getOption('pcre')) {
replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g,"$1$$&"));
} else {
replacePart = translateRegexReplace(replacePart);
}
vimGlobalState.lastSubstituteReplacePart = replacePart;
}
trailing = tokens[2] ? tokens[2].split(' ') : [];
} else {
// either the argString is empty or its of the form ' hello/world'
// actually splitBySlash returns a list of tokens
// only if the string starts with a '/'
if (argString && argString.length) {
showConfirm(cm, 'Substitutions should be of the form ' +
':s/pattern/replace/');
return;
}
}
// After the 3rd slash, we can have flags followed by a space followed
// by count.
if (trailing) {
flagsPart = trailing[0];
count = parseInt(trailing[1]);
if (flagsPart) {
if (flagsPart.indexOf('c') != -1) {
confirm = true;
flagsPart.replace('c', '');
}
if (flagsPart.indexOf('g') != -1) {
global = true;
flagsPart.replace('g', '');
}
if (getOption('pcre')) {
regexPart = regexPart + '/' + flagsPart;
} else {
regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;
}
}
}
if (regexPart) {
// If regex part is empty, then use the previous query. Otherwise use
// the regex part as the new query.
try {
updateSearchQuery(cm, regexPart, true /** ignoreCase */,
true /** smartCase */);
} catch (e) {
showConfirm(cm, 'Invalid regex: ' + regexPart);
return;
}
}
replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
if (replacePart === undefined) {
showConfirm(cm, 'No previous substitute regular expression');
return;
}
var state = getSearchState(cm);
var query = state.getQuery();
var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
var lineEnd = params.lineEnd || lineStart;
if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {
lineEnd = Infinity;
}
if (count) {
lineStart = lineEnd;
lineEnd = lineStart + count - 1;
}
var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
var cursor = cm.getSearchCursor(query, startPos);
doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
},