I have a function like this that\'s provided by a user:
function replace_function(string) {
return string.replace(/:smile:/g, \'⻇\')
.replace(/(foo|bar
To do this, you'll have to do the replace
operation yourself with a RegExp#exec
loop, and keep track of how the replacements affect the position, something along these lines (but this can probably be optimized):
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
Here's a snippet showing us testing that with various positions:
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
function show(str, pos) {
console.log(str.substring(0, pos) + "|" + str.substring(pos));
}
function test(rex, str, replacement, pos) {
show(str, pos);
var result = trackingReplace(rex, str, replacement, pos);
show(result[0], result[1]);
}
for (var n = 3; n < 22; ++n) {
if (n > 3) {
console.log("----");
}
test(/([f])([o])o/g, "test foo result foo x", "...$2...", n);
}
.as-console-wrapper {
max-height: 100% !important;
}
And here's your snippet updated to use it:
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
function replace_function(string, position) {
var result = trackingReplace(/:smile:/g, string, '⻇', position);
result = trackingReplace(/(foo|bar|baz)/g, result[0], 'text_$1', result[1]);
return result;
}
var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
var position = textarea.selectionStart;
var result = replace_function(textarea.value, position);
var string = result[0];
position = result[1];
var split = [
string.substring(0, position),
string.substring(position)
];
pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
<textarea>:smile: foo</textarea>
<pre></pre>