@internal
(searchValue: RegExp, replacement: string | ReplacementFunction)
| 1315 | |
| 1316 | /** @internal */ |
| 1317 | _replaceRegexp(searchValue: RegExp, replacement: string | ReplacementFunction): this { |
| 1318 | function getReplacement(match: RegExpMatchArray, str: string): string { |
| 1319 | if (typeof replacement === 'string') { |
| 1320 | return expandReplacement( |
| 1321 | replacement, |
| 1322 | match[0], |
| 1323 | match.index, |
| 1324 | str, |
| 1325 | match.slice(1), |
| 1326 | match.groups, |
| 1327 | ) |
| 1328 | } |
| 1329 | else { |
| 1330 | // `String.prototype.replace` only passes the named-capture-groups object |
| 1331 | // when the pattern actually has named groups - passing an `undefined` |
| 1332 | // there unconditionally shifts the last argument a replacer sees |
| 1333 | return match.groups === undefined |
| 1334 | ? replacement(match[0], ...match.slice(1), match.index, str) |
| 1335 | : replacement(match[0], ...match.slice(1), match.index, str, match.groups) |
| 1336 | } |
| 1337 | } |
| 1338 | const replaceMatch = (match: RegExpMatchArray): void => { |
| 1339 | if (match.index == null) |
| 1340 | return |
| 1341 | |
| 1342 | const replacement = getReplacement(match, this.original) |
| 1343 | if (replacement === match[0]) |
| 1344 | return |
| 1345 | |
| 1346 | if (match[0].length === 0) { |
| 1347 | // a zero-length match spans no characters, so there is no range to |
| 1348 | // overwrite - the replacement is an insertion at the matched position, |
| 1349 | // which is what `String.prototype.replace` does for an empty match |
| 1350 | this.appendRight(match.index, replacement) |
| 1351 | } |
| 1352 | else { |
| 1353 | this.overwrite(match.index, match.index + match[0].length, replacement) |
| 1354 | } |
| 1355 | } |
| 1356 | |
| 1357 | if (searchValue.global) { |
| 1358 | // `String.prototype.replace` starts a global regexp from the beginning of |
| 1359 | // the string, so reset `lastIndex` - a regexp that has already been used |
| 1360 | // would otherwise resume from wherever it stopped and skip earlier matches. |
| 1361 | // `matchAll` also steps over a zero-length match, where `exec` in a loop |
| 1362 | // would keep rematching it at an unmoving `lastIndex` and never terminate. |
| 1363 | searchValue.lastIndex = 0 |
| 1364 | for (const match of this.original.matchAll(searchValue)) { |
| 1365 | replaceMatch(match) |
| 1366 | } |
| 1367 | } |
| 1368 | else { |
| 1369 | const match = this.original.match(searchValue) |
| 1370 | if (match) { |
| 1371 | replaceMatch(match) |
| 1372 | } |
| 1373 | } |
| 1374 | return this |
no test coverage detected