editor: Defer the effects of change_selections to end of transact (#31731)

In quite a few places the selection is changed multiple times in a
transaction. For example, `backspace` might do it 3 times:

* `select_autoclose_pair`
* selection of the ranges to delete
* `insert` of empty string also updates selection

Before this change, each of these selection changes appended to
selection history and did a bunch of work that's only relevant to
selections the user actually sees. So for each backspace,
`editor::UndoSelection` would need to be invoked 3-4 times before the
cursor actually moves. It still needs to be run twice after this change,
but that is a separate issue.

Signature help even had a `backspace_pressed: bool` as an incomplete
workaround, to avoid it flickering due to the selection switching
between being a range and being cursor-like.

The original motivation for this change is work I'm doing on not
re-querying completions when the language server provides a response
that has `is_incomplete: false`. Whether the menu is still visible is
determined by the cursor position, and this was complicated by it seeing
`backspace` temporarily moving the head of the selection 1 character to
the left.

This change also removes some redundant uses of
`push_to_selection_history`.

Not super stoked with the name `DeferredSelectionEffectsState`. Naming
is hard.

Release Notes:

- N/A
This commit is contained in:
Michael Sloan 2025-05-30 01:53:02 -06:00 committed by GitHub
parent 1445af559b
commit d7f0241d7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 108 additions and 48 deletions

View file

@ -74,8 +74,6 @@ impl Editor {
pub(super) fn should_open_signature_help_automatically(
&mut self,
old_cursor_position: &Anchor,
backspace_pressed: bool,
cx: &mut Context<Self>,
) -> bool {
if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {
@ -84,9 +82,7 @@ impl Editor {
let newest_selection = self.selections.newest::<usize>(cx);
let head = newest_selection.head();
// There are two cases where the head and tail of a selection are different: selecting multiple ranges and using backspace.
// If we dont exclude the backspace case, signature_help will blink every time backspace is pressed, so we need to prevent this.
if !newest_selection.is_empty() && !backspace_pressed && head != newest_selection.tail() {
if !newest_selection.is_empty() && head != newest_selection.tail() {
self.signature_help_state
.hide(SignatureHelpHiddenBy::Selection);
return false;
@ -232,7 +228,6 @@ pub struct SignatureHelpState {
task: Option<Task<()>>,
popover: Option<SignatureHelpPopover>,
hidden_by: Option<SignatureHelpHiddenBy>,
backspace_pressed: bool,
}
impl SignatureHelpState {
@ -254,14 +249,6 @@ impl SignatureHelpState {
self.popover.as_mut()
}
pub fn backspace_pressed(&self) -> bool {
self.backspace_pressed
}
pub fn set_backspace_pressed(&mut self, backspace_pressed: bool) {
self.backspace_pressed = backspace_pressed;
}
pub fn set_popover(&mut self, popover: SignatureHelpPopover) {
self.popover = Some(popover);
self.hidden_by = None;