| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | /** |
| 9 | * Replace text in a text area. |
| 10 | * Re-adjust cursor & selection to be the same as before the replacement. |
| 11 | */ |
| 12 | export function replaceInTextArea(textArea: HTMLTextAreaElement, oldText: string, newText: string) { |
| 13 | const {selectionStart, selectionEnd} = textArea; |
| 14 | const insertionSpot = textArea.value.indexOf(oldText); |
| 15 | textArea.value = textArea.value.replace(oldText, newText); |
| 16 | // re-select whatever we had selected before. |
| 17 | // if the new text is longer, we may need to add additional length |
| 18 | if (selectionStart) { |
| 19 | textArea.selectionStart = |
| 20 | selectionStart > insertionSpot |
| 21 | ? selectionStart + (newText.length - oldText.length) |
| 22 | : selectionStart; |
| 23 | } |
| 24 | if (selectionEnd) { |
| 25 | textArea.selectionEnd = |
| 26 | selectionEnd > insertionSpot |
| 27 | ? selectionEnd + (newText.length - oldText.length) |
| 28 | : selectionEnd; |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Insert text into a text area at the cursor location. |
| 34 | * Add spaces before/after as necessary so the new text does not neighbor the existing content. |
| 35 | * If text is selected, replace the selected text with the new text. |
| 36 | * If nothing is selected, append to the end. |
| 37 | */ |
| 38 | export function insertAtCursor(textArea: HTMLTextAreaElement, text: string) { |
| 39 | if (textArea.selectionStart != null) { |
| 40 | const startPos = textArea.selectionStart; |
| 41 | const endPos = textArea.selectionEnd; |
| 42 | const nextCharPos = endPos ?? startPos; |
| 43 | const previousChar = textArea.value[startPos - 1]; |
| 44 | const nextChar = textArea.value[nextCharPos]; |
| 45 | const isWhitespace = (s: string | undefined) => !s || /[ \n\t]/.test(s); |
| 46 | // if inserting next to whitespace, no need to add more. |
| 47 | // if inserting next to text, add a space before to avoid the link becoming invalid. |
| 48 | const prefix = isWhitespace(previousChar) ? '' : ' '; |
| 49 | // similarly for suffix |
| 50 | const suffix = isWhitespace(nextChar) ? '' : ' '; |
| 51 | const totalAddedLength = text.length + prefix.length + suffix.length; |
| 52 | textArea.value = |
| 53 | textArea.value.substring(0, startPos) + |
| 54 | prefix + |
| 55 | text + |
| 56 | suffix + |
| 57 | (endPos != null ? textArea.value.substring(endPos, textArea.value.length) : ''); |
| 58 | const newPos = startPos + totalAddedLength; |
| 59 | textArea.selectionStart = newPos; |
| 60 | textArea.selectionEnd = newPos; |
| 61 | } else { |
| 62 | // no selection => append to the end |
| 63 | const prefix = /\s/.test(textArea.value[textArea.value.length - 1]) ? '' : ' '; |
| 64 | textArea.value += prefix + text; |
| 65 | } |
| 66 | } |
| 67 | |