From a7cd2207048bfac3e76fafdf47e259cd1b391352 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Thu, 12 Sep 2024 16:11:39 +0100 Subject: [PATCH] PERF: Use insertText more efficiently in `replaceText` (#28880) Followup to e25578d702791bac80b431acd450cc53b6c39a3c Using execCommand to replace the entire contents of the textarea is very slow for larger posts (it seems the browser does a reflow after every 'virtual keypress'. This commit updates the `replaceText` function to be more surgical with its `insertAt` calls. Now it only selects & replaces the characters which are actually being replaced. --- .../app/mixins/textarea-text-manipulation.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js b/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js index fe1122aa7f5..2914a049b9f 100644 --- a/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js +++ b/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js @@ -149,16 +149,21 @@ export default Mixin.create({ }); if (opts.index && opts.regex) { - let i = -1; - const newValue = val.replace(opts.regex, (match) => { - i++; - return i === opts.index ? newVal : match; - }); + if (!opts.regex.global) { + throw new Error("Regex must be global"); + } - this._insertAt(0, val.length, newValue); + const regex = new RegExp(opts.regex); + let match; + for (let i = 0; i <= opts.index; i++) { + match = regex.exec(val); + } + + if (match) { + this._insertAt(match.index, match.index + match[0].length, newVal); + } } else { - const replacedValue = val.replace(oldVal, newVal); - this._insertAt(0, val.length, replacedValue); + this._insertAt(needleStart, needleStart + oldVal.length, newVal); } if ( @@ -332,7 +337,11 @@ export default Mixin.create({ _insertAt(start, end, text) { this._textarea.setSelectionRange(start, end); this._textarea.focus(); - document.execCommand("insertText", false, text); + if (start !== end && text === "") { + document.execCommand("delete", false); + } else { + document.execCommand("insertText", false, text); + } }, extractTable(text) {