compose: Always preserve cursor position post replacement in a textarea.

Refactored (moved) the code for preserving the cursor's initial logical
position from `quote_and_reply()` in `compose_actions.js` which calls
`replace_syntax()` directly into `replace_syntax()` in `compose_ui.js`.
This ensures that anytime text in a textarea is replaced, the original
cursor position is always restored.

Earlier, this was needed to be done separately, and missing that would
lead to bugs with the cursor unexpectedly jumping on replacement.

Fixes: #23863.
This commit is contained in:
N-Shar-ma
2022-12-14 10:24:01 +05:30
committed by Tim Abbott
parent 8d539cff8f
commit 38587b79b9
3 changed files with 37 additions and 24 deletions

View File

@@ -75,6 +75,11 @@ export function insert_syntax_and_focus(syntax, $textarea = $("#compose-textarea
}
export function replace_syntax(old_syntax, new_syntax, $textarea = $("#compose-textarea")) {
// The following couple lines are needed to later restore the initial
// logical position of the cursor after the replacement
const prev_caret = $textarea.caret();
const replacement_offset = $textarea.val().indexOf(old_syntax);
// Replaces `old_syntax` with `new_syntax` text in the compose box. Due to
// the way that JavaScript handles string replacements, if `old_syntax` is
// a string it will only replace the first instance. If `old_syntax` is
@@ -84,7 +89,25 @@ export function replace_syntax(old_syntax, new_syntax, $textarea = $("#compose-t
// replace() function treating `$`s in new_syntax as special syntax. See
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Description
// for details.
replace($textarea[0], old_syntax, () => new_syntax, "after-replacement");
// When replacing content in a textarea, we need to move the cursor
// to preserve its logical position if and only if the content we
// just added was before the current cursor position. If it was,
// we need to move the cursor forward by the increase in the
// length of the content after the replacement.
if (prev_caret >= replacement_offset + old_syntax.length) {
$textarea.caret(prev_caret + new_syntax.length - old_syntax.length);
} else if (prev_caret > replacement_offset) {
// In the rare case that our cursor was inside the
// placeholder, we treat that as though the cursor was
// just after the placeholder.
$textarea.caret(replacement_offset + new_syntax.length + 1);
} else {
// Otherwise we simply restore it to it's original position
$textarea.caret(prev_caret);
}
}
export function compute_placeholder_text(opts) {