@@ -936,6 +936,8 @@ pub struct Editor {
select_next_state: Option<SelectNextState>,
select_prev_state: Option<SelectNextState>,
selection_history: SelectionHistory,
+ defer_selection_effects: bool,
+ deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
autoclose_regions: Vec<AutocloseRegion>,
snippet_stack: InvalidationStack<SnippetState>,
select_syntax_node_history: SelectSyntaxNodeHistory,
@@ -1195,6 +1197,14 @@ impl Default for SelectionHistoryMode {
}
}
+struct DeferredSelectionEffectsState {
+ changed: bool,
+ show_completions: bool,
+ autoscroll: Option<Autoscroll>,
+ old_cursor_position: Anchor,
+ history_entry: SelectionHistoryEntry,
+}
+
#[derive(Default)]
struct SelectionHistory {
#[allow(clippy::type_complexity)]
@@ -1791,6 +1801,8 @@ impl Editor {
select_next_state: None,
select_prev_state: None,
selection_history: SelectionHistory::default(),
+ defer_selection_effects: false,
+ deferred_selection_effects_state: None,
autoclose_regions: Vec::new(),
snippet_stack: InvalidationStack::default(),
select_syntax_node_history: SelectSyntaxNodeHistory::default(),
@@ -2954,6 +2966,9 @@ impl Editor {
Subscription::join(other_subscription, this_subscription)
}
+ /// Changes selections using the provided mutation function. Changes to `self.selections` occur
+ /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
+ /// effects of selection change occur at the end of the transaction.
pub fn change_selections<R>(
&mut self,
autoscroll: Option<Autoscroll>,
@@ -2961,39 +2976,105 @@ impl Editor {
cx: &mut Context<Self>,
change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
) -> R {
- self.change_selections_inner(autoscroll, true, window, cx, change)
+ self.change_selections_inner(true, autoscroll, window, cx, change)
}
- fn change_selections_inner<R>(
+ pub(crate) fn change_selections_without_showing_completions<R>(
&mut self,
autoscroll: Option<Autoscroll>,
- request_completions: bool,
window: &mut Window,
cx: &mut Context<Self>,
change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
) -> R {
- let old_cursor_position = self.selections.newest_anchor().head();
- self.push_to_selection_history();
+ self.change_selections_inner(false, autoscroll, window, cx, change)
+ }
+ fn change_selections_inner<R>(
+ &mut self,
+ show_completions: bool,
+ autoscroll: Option<Autoscroll>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
+ ) -> R {
+ if let Some(state) = &mut self.deferred_selection_effects_state {
+ state.autoscroll = autoscroll.or(state.autoscroll);
+ state.show_completions = show_completions;
+ let (changed, result) = self.selections.change_with(cx, change);
+ state.changed |= changed;
+ return result;
+ }
+ let mut state = DeferredSelectionEffectsState {
+ changed: false,
+ show_completions,
+ autoscroll,
+ old_cursor_position: self.selections.newest_anchor().head(),
+ history_entry: SelectionHistoryEntry {
+ selections: self.selections.disjoint_anchors(),
+ select_next_state: self.select_next_state.clone(),
+ select_prev_state: self.select_prev_state.clone(),
+ add_selections_state: self.add_selections_state.clone(),
+ },
+ };
let (changed, result) = self.selections.change_with(cx, change);
+ state.changed = state.changed || changed;
+ if self.defer_selection_effects {
+ self.deferred_selection_effects_state = Some(state);
+ } else {
+ self.apply_selection_effects(state, window, cx);
+ }
+ result
+ }
+
+ /// Defers the effects of selection change, so that the effects of multiple calls to
+ /// `change_selections` are applied at the end. This way these intermediate states aren't added
+ /// to selection history and the state of popovers based on selection position aren't
+ /// erroneously updated.
+ pub fn with_selection_effects_deferred<R>(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
+ ) -> R {
+ let already_deferred = self.defer_selection_effects;
+ self.defer_selection_effects = true;
+ let result = update(self, window, cx);
+ if !already_deferred {
+ self.defer_selection_effects = false;
+ if let Some(state) = self.deferred_selection_effects_state.take() {
+ self.apply_selection_effects(state, window, cx);
+ }
+ }
+ result
+ }
- if changed {
- if let Some(autoscroll) = autoscroll {
+ fn apply_selection_effects(
+ &mut self,
+ state: DeferredSelectionEffectsState,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if state.changed {
+ self.selection_history.push(state.history_entry);
+
+ if let Some(autoscroll) = state.autoscroll {
self.request_autoscroll(autoscroll, cx);
}
- self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
- if self.should_open_signature_help_automatically(
+ let old_cursor_position = &state.old_cursor_position;
+
+ self.selections_did_change(
+ true,
&old_cursor_position,
- self.signature_help_state.backspace_pressed(),
+ state.show_completions,
+ window,
cx,
- ) {
+ );
+
+ if self.should_open_signature_help_automatically(&old_cursor_position, cx) {
self.show_signature_help(&ShowSignatureHelp, window, cx);
}
- self.signature_help_state.set_backspace_pressed(false);
}
-
- result
}
pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
@@ -3877,9 +3958,12 @@ impl Editor {
}
let had_active_inline_completion = this.has_active_inline_completion();
- this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
- s.select(new_selections)
- });
+ this.change_selections_without_showing_completions(
+ Some(Autoscroll::fit()),
+ window,
+ cx,
+ |s| s.select(new_selections),
+ );
if !bracket_inserted {
if let Some(on_type_format_task) =
@@ -9033,7 +9117,6 @@ impl Editor {
}
}
- this.signature_help_state.set_backspace_pressed(true);
this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select(selections)
});
@@ -12755,7 +12838,6 @@ impl Editor {
) -> Result<()> {
self.hide_mouse_cursor(&HideMouseCursorOrigin::MovementAction);
- self.push_to_selection_history();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.select_next_match_internal(&display_map, false, None, window, cx)?;
@@ -12808,7 +12890,6 @@ impl Editor {
cx: &mut Context<Self>,
) -> Result<()> {
self.hide_mouse_cursor(&HideMouseCursorOrigin::MovementAction);
- self.push_to_selection_history();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.select_next_match_internal(
&display_map,
@@ -12827,7 +12908,6 @@ impl Editor {
cx: &mut Context<Self>,
) -> Result<()> {
self.hide_mouse_cursor(&HideMouseCursorOrigin::MovementAction);
- self.push_to_selection_history();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot;
let mut selections = self.selections.all::<usize>(cx);
@@ -15697,24 +15777,17 @@ impl Editor {
self.selections_did_change(false, &old_cursor_position, true, window, cx);
}
- fn push_to_selection_history(&mut self) {
- self.selection_history.push(SelectionHistoryEntry {
- selections: self.selections.disjoint_anchors(),
- select_next_state: self.select_next_state.clone(),
- select_prev_state: self.select_prev_state.clone(),
- add_selections_state: self.add_selections_state.clone(),
- });
- }
-
pub fn transact(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
) -> Option<TransactionId> {
- self.start_transaction_at(Instant::now(), window, cx);
- update(self, window, cx);
- self.end_transaction_at(Instant::now(), cx)
+ self.with_selection_effects_deferred(window, cx, |this, window, cx| {
+ this.start_transaction_at(Instant::now(), window, cx);
+ update(this, window, cx);
+ this.end_transaction_at(Instant::now(), cx)
+ })
}
pub fn start_transaction_at(