diff options
Diffstat (limited to 'ext/js/general/regex-util.js')
| -rw-r--r-- | ext/js/general/regex-util.js | 130 | 
1 files changed, 62 insertions, 68 deletions
| diff --git a/ext/js/general/regex-util.js b/ext/js/general/regex-util.js index 301b1fcf..f6eca3b6 100644 --- a/ext/js/general/regex-util.js +++ b/ext/js/general/regex-util.js @@ -16,82 +16,76 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +/** @type {RegExp} @readonly */ +const matchReplacementPattern = /\$(?:\$|&|`|'|(\d\d?)|<([^>]*)>)/g;  /** - * This class provides some general utility functions for regular expressions. + * Applies string.replace using a regular expression and replacement string as arguments. + * A source map of the changes is also maintained. + * @param {string} text A string of the text to replace. + * @param {import('./text-source-map.js').TextSourceMap} sourceMap An instance of `TextSourceMap` which corresponds to `text`. + * @param {RegExp} pattern A regular expression to use as the replacement. + * @param {string} replacement A replacement string that follows the format of the standard + *   JavaScript regular expression replacement string. + * @returns {string} A new string with the pattern replacements applied and the source map updated.   */ -export class RegexUtil { -    /** @type {RegExp} @readonly */ -    static _matchReplacementPattern = /\$(?:\$|&|`|'|(\d\d?)|<([^>]*)>)/g; +export function applyTextReplacement(text, sourceMap, pattern, replacement) { +    const isGlobal = pattern.global; +    if (isGlobal) { pattern.lastIndex = 0; } +    for (let loop = true; loop; loop = isGlobal) { +        const match = pattern.exec(text); +        if (match === null) { break; } -    /** -     * Applies string.replace using a regular expression and replacement string as arguments. -     * A source map of the changes is also maintained. -     * @param {string} text A string of the text to replace. -     * @param {import('./text-source-map.js').TextSourceMap} sourceMap An instance of `TextSourceMap` which corresponds to `text`. -     * @param {RegExp} pattern A regular expression to use as the replacement. -     * @param {string} replacement A replacement string that follows the format of the standard -     *   JavaScript regular expression replacement string. -     * @returns {string} A new string with the pattern replacements applied and the source map updated. -     */ -    static applyTextReplacement(text, sourceMap, pattern, replacement) { -        const isGlobal = pattern.global; -        if (isGlobal) { pattern.lastIndex = 0; } -        for (let loop = true; loop; loop = isGlobal) { -            const match = pattern.exec(text); -            if (match === null) { break; } +        const matchText = match[0]; +        const index = match.index; +        const actualReplacement = applyMatchReplacement(replacement, match); +        const actualReplacementLength = actualReplacement.length; +        const delta = actualReplacementLength - (matchText.length > 0 ? matchText.length : -1); -            const matchText = match[0]; -            const index = match.index; -            const actualReplacement = this.applyMatchReplacement(replacement, match); -            const actualReplacementLength = actualReplacement.length; -            const delta = actualReplacementLength - (matchText.length > 0 ? matchText.length : -1); +        text = `${text.substring(0, index)}${actualReplacement}${text.substring(index + matchText.length)}`; +        pattern.lastIndex += delta; -            text = `${text.substring(0, index)}${actualReplacement}${text.substring(index + matchText.length)}`; -            pattern.lastIndex += delta; - -            if (actualReplacementLength > 0) { -                sourceMap.insert(index, ...(new Array(actualReplacementLength).fill(0))); -                sourceMap.combine(index - 1 + actualReplacementLength, matchText.length); -            } else { -                sourceMap.combine(index, matchText.length); -            } +        if (actualReplacementLength > 0) { +            sourceMap.insert(index, ...(new Array(actualReplacementLength).fill(0))); +            sourceMap.combine(index - 1 + actualReplacementLength, matchText.length); +        } else { +            sourceMap.combine(index, matchText.length);          } -        return text;      } +    return text; +} -    /** -     * Applies the replacement string for a given regular expression match. -     * @param {string} replacement The replacement string that follows the format of the standard -     *   JavaScript regular expression replacement string. -     * @param {RegExpMatchArray} match A match object returned from RegExp.match. -     * @returns {string} A new string with the pattern replacement applied. -     */ -    static applyMatchReplacement(replacement, match) { -        const pattern = this._matchReplacementPattern; -        pattern.lastIndex = 0; -        return replacement.replace(pattern, (g0, g1, g2) => { -            if (typeof g1 !== 'undefined') { -                const matchIndex = Number.parseInt(g1, 10); -                if (matchIndex >= 1 && matchIndex <= match.length) { -                    return match[matchIndex]; -                } -            } else if (typeof g2 !== 'undefined') { -                const {groups} = match; -                if (typeof groups === 'object' && groups !== null && Object.prototype.hasOwnProperty.call(groups, g2)) { -                    return groups[g2]; -                } -            } else { -                let {index} = match; -                if (typeof index !== 'number') { index = 0; } -                switch (g0) { -                    case '$': return '$'; -                    case '&': return match[0]; -                    case '`': return replacement.substring(0, index); -                    case '\'': return replacement.substring(index + g0.length); -                } +/** + * Applies the replacement string for a given regular expression match. + * @param {string} replacement The replacement string that follows the format of the standard + *   JavaScript regular expression replacement string. + * @param {RegExpMatchArray} match A match object returned from RegExp.match. + * @returns {string} A new string with the pattern replacement applied. + */ +export function applyMatchReplacement(replacement, match) { +    const pattern = matchReplacementPattern; +    pattern.lastIndex = 0; +    return replacement.replace(pattern, (g0, g1, g2) => { +        if (typeof g1 !== 'undefined') { +            const matchIndex = Number.parseInt(g1, 10); +            if (matchIndex >= 1 && matchIndex <= match.length) { +                return match[matchIndex];              } -            return g0; -        }); -    } +        } else if (typeof g2 !== 'undefined') { +            const {groups} = match; +            if (typeof groups === 'object' && groups !== null && Object.prototype.hasOwnProperty.call(groups, g2)) { +                return groups[g2]; +            } +        } else { +            let {index} = match; +            if (typeof index !== 'number') { index = 0; } +            switch (g0) { +                case '$': return '$'; +                case '&': return match[0]; +                case '`': return replacement.substring(0, index); +                case '\'': return replacement.substring(index + g0.length); +            } +        } +        return g0; +    });  } |