From fb69397ac1a4cbcc4b80fbf547f3bad582aad166 Mon Sep 17 00:00:00 2001 From: kingbri Date: Thu, 6 Jul 2023 00:36:48 -0400 Subject: [PATCH] Regex: Add overlay replacement strategy As opposed to making the match variable include the entire regex match, overlay the replacement string over the regex match and splice out whatever's already in the replacement string from the regex match. This new strategy helps save time when editing messages since match prefix and suffix phrases have a lower chance of being repeated on every edit. The overlay strategy also preserves uniqueness if the user decides to change something in the edited text. However, overlay can cause issues especially with punctiation, so the strategy isn't chosen by default when creating a new regex. Signed-off-by: kingbri --- public/scripts/extensions/regex/editor.html | 7 +++ public/scripts/extensions/regex/engine.js | 54 +++++++++++++++++++-- public/scripts/extensions/regex/index.js | 10 +++- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/public/scripts/extensions/regex/editor.html b/public/scripts/extensions/regex/editor.html index e10ffe678..7448e474e 100644 --- a/public/scripts/extensions/regex/editor.html +++ b/public/scripts/extensions/regex/editor.html @@ -103,6 +103,13 @@ Substitute Regex +
+ Replacement Strategy + +
diff --git a/public/scripts/extensions/regex/engine.js b/public/scripts/extensions/regex/engine.js index 65ea7981c..0a8554a1f 100644 --- a/public/scripts/extensions/regex/engine.js +++ b/public/scripts/extensions/regex/engine.js @@ -14,6 +14,11 @@ const regex_placement = { SENDAS: 4 } +const regex_replace_strategy = { + REPLACE: 0, + OVERLAY: 1 +} + // Originally from: https://github.com/IonicaBizau/regex-parser.js/blob/master/lib/index.js function regexFromString(input) { try { @@ -32,6 +37,7 @@ function regexFromString(input) { } } +// Parent function to fetch a regexed version of a raw string function getRegexedString(rawString, placement, { characterOverride } = {}) { if (extension_settings.disabledExtensions.includes("regex") || !rawString || placement === undefined) { return; @@ -78,7 +84,14 @@ function runRegexScript(regexScript, rawString, { characterOverride } = {}) { // TODO: Use substrings for replacement. But not necessary at this time. // A substring is from match.index to match.index + match[0].length or fencedMatch.length - const subReplaceString = substituteRegexParams(regexScript.replaceString, trimCapturedMatch ?? trimFencedMatch, { characterOverride }); + const subReplaceString = substituteRegexParams( + regexScript.replaceString, + trimCapturedMatch ?? trimFencedMatch, + { + characterOverride, + replaceStrategy: regexScript.replaceStrategy ?? regex_replace_strategy.REPLACE + } + ); if (!newString) { newString = rawString.replace(fencedMatch, subReplaceString); } else { @@ -106,10 +119,45 @@ function filterString(rawString, trimStrings, { characterOverride } = {}) { } // Substitutes regex-specific and normal parameters -function substituteRegexParams(rawString, regexMatch, { characterOverride } = {}) { +function substituteRegexParams(rawString, regexMatch, { characterOverride, replaceStrategy } = {}) { let finalString = rawString; - finalString = finalString.replace("{{match}}", regexMatch); finalString = substituteParams(finalString, undefined, characterOverride); + let overlaidMatch = regexMatch; + if (replaceStrategy === regex_replace_strategy.OVERLAY) { + const splitReplace = finalString.split("{{match}}"); + + // There's a prefix + if (splitReplace[0]) { + const splicedPrefix = spliceSymbols(splitReplace[0], false); + overlaidMatch = overlaidMatch.replace(splicedPrefix, "").trim(); + } + + // There's a suffix + if (splitReplace[1]) { + const splicedSuffix = spliceSymbols(splitReplace[1], true); + overlaidMatch = overlaidMatch.replace(new RegExp(`${splicedSuffix}$`), "").trim(); + } + } + + // Only one match is replaced. This is by design + finalString = finalString.replace("{{match}}", overlaidMatch) || finalString.replace("{{match}}", regexMatch); + return finalString; } + +// Splices symbols and whitespace from the beginning and end of a string +// Using a for loop due to sequential ordering +function spliceSymbols(rawString, isSuffix) { + let offset = 0; + + for (const ch of isSuffix ? rawString.split('').reverse() : rawString) { + if (ch.match(/[^\w.,?'!]/)) { + offset++; + } else { + break; + } + } + + return isSuffix ? rawString.substring(0, rawString.length - offset) : rawString.substring(offset);; +} diff --git a/public/scripts/extensions/regex/index.js b/public/scripts/extensions/regex/index.js index 2920386e9..9ad9a2318 100644 --- a/public/scripts/extensions/regex/index.js +++ b/public/scripts/extensions/regex/index.js @@ -119,6 +119,9 @@ async function onRegexEditorOpenClick(existingId) { editorHtml .find(`input[name="substitute_regex"]`) .prop("checked", existingScript.substituteRegex ?? false); + editorHtml + .find(`select[name="replace_strategy_select"]`) + .val(existingScript.replaceStrategy ?? 0); existingScript.placement.forEach((element) => { editorHtml @@ -161,7 +164,12 @@ async function onRegexEditorOpenClick(existingId) { substituteRegex: editorHtml .find(`input[name="substitute_regex"]`) - .prop("checked") + .prop("checked"), + replaceStrategy: + parseInt(editorHtml + .find(`select[name="replace_strategy_select"]`) + .find(`:selected`) + .val()) ?? 0 }; saveRegexScript(newRegexScript, existingScriptIndex);