use super::*; pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles { EditPredictionStyles { insertion: HighlightStyle { color: Some(cx.theme().status().predictive), ..HighlightStyle::default() }, whitespace: HighlightStyle { background_color: Some(cx.theme().status().created_background), ..HighlightStyle::default() }, }} pub(super) enum EditDisplayMode { TabAccept, DiffPopover, Inline,} pub(super) enum EditPrediction { Edit { // TODO could be a language::Anchor? edits: Vec<(Range<Anchor>, Arc<str>)>, /// Predicted cursor position as (anchor, offset_from_anchor). /// The anchor is in multibuffer coordinates; after applying edits, /// resolve the anchor and add the offset to get the final cursor position. cursor_position: Option<(Anchor, usize)>, edit_preview: Option<EditPreview>, display_mode: EditDisplayMode, snapshot: BufferSnapshot, }, /// Move to a specific location in the active editor MoveWithin { target: Anchor, snapshot: BufferSnapshot, }, /// Move to a specific location in a different editor (not the active one) MoveOutside { target: language::Anchor, snapshot: BufferSnapshot, },} pub(super) struct EditPredictionState { pub(super) inlay_ids: Vec<InlayId>, pub(super) completion: EditPrediction, pub(super) completion_id: Option<SharedString>, pub(super) invalidation_range: Option<Range<Anchor>>,} pub(super) enum EditPredictionSettings { Disabled, Enabled { show_in_menu: bool, preview_requires_modifier: bool, },} pub(super) enum MenuEditPredictionsPolicy { #[cfg(test)] Never, ByProvider,} pub(super) enum EditPredictionPreview { /// Modifier is not pressed Inactive { released_too_fast: bool }, /// Modifier pressed Active { since: Instant, previous_scroll_position: Option<SharedScrollAnchor>, },} #[derive(Copy, Clone, Eq, PartialEq)]pub(super) enum EditPredictionKeybindSurface { Inline, CursorPopoverCompact, CursorPopoverExpanded,} #[derive(Copy, Clone, Eq, PartialEq, Debug)]pub(super) enum EditPredictionKeybindAction { Accept, Preview,} pub(super) struct EditPredictionKeybindDisplay { #[cfg(test)] pub(super) accept_keystroke: Option<gpui::KeybindingKeystroke>, #[cfg(test)] pub(super) preview_keystroke: Option<gpui::KeybindingKeystroke>, pub(super) displayed_keystroke: Option<gpui::KeybindingKeystroke>, pub(super) action: EditPredictionKeybindAction, pub(super) missing_accept_keystroke: bool, pub(super) show_hold_label: bool,} impl EditPredictionPreview { pub(super) fn released_too_fast(&self) -> bool { match self { EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast, EditPredictionPreview::Active { .. } => false, } } pub(super) fn set_previous_scroll_position( &mut self, scroll_position: Option<SharedScrollAnchor>, ) { if let EditPredictionPreview::Active { previous_scroll_position, .. } = self { *previous_scroll_position = scroll_position; } }} pub(super) struct RegisteredEditPredictionDelegate { pub(super) provider: Arc<dyn EditPredictionDelegateHandle>, _subscription: Subscription,} pub(super) fn edit_prediction_edit_text( current_snapshot: &BufferSnapshot, edits: &[(Range<Anchor>, impl AsRef<str>)], edit_preview: &EditPreview, include_deletions: bool, multibuffer_snapshot: &MultiBufferSnapshot, cx: &App,) -> HighlightedText { let edits = edits .iter() .filter_map(|(anchor, text)| { Some(( multibuffer_snapshot .anchor_range_to_buffer_anchor_range(anchor.clone())? .1, text, )) }) .collect::<Vec<_>>(); edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)} impl Editor { pub fn set_edit_prediction_provider<T>( &mut self, provider: Option<Entity<T>>, trigger: EditPredictionRequestTrigger, window: &mut Window, cx: &mut Context<Self>, ) where T: EditPredictionDelegate, { self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate { _subscription: cx.observe_in(&provider, window, |this, _, window, cx| { if this.focus_handle.is_focused(window) { this.update_visible_edit_prediction(window, cx); } }), provider: Arc::new(provider), }); self.update_edit_prediction_settings(cx); self.refresh_edit_prediction(false, false, trigger, window, cx); } pub fn set_edit_predictions_hidden_for_vim_mode( &mut self, hidden: bool, window: &mut Window, cx: &mut Context<Self>, ) { if hidden != self.edit_predictions_hidden_for_vim_mode { self.edit_predictions_hidden_for_vim_mode = hidden; if hidden { self.update_visible_edit_prediction(window, cx); } else { self.refresh_edit_prediction( true, false, EditPredictionRequestTrigger::VimModeChanged, window, cx, ); } } } pub fn toggle_edit_predictions( &mut self, _: &ToggleEditPrediction, window: &mut Window, cx: &mut Context<Self>, ) { if self.show_edit_predictions_override.is_some() { self.set_show_edit_predictions(None, window, cx); } else { let show_edit_predictions = !self.edit_predictions_enabled(); self.set_show_edit_predictions(Some(show_edit_predictions), window, cx); } } pub fn set_show_edit_predictions( &mut self, show_edit_predictions: Option<bool>, window: &mut Window, cx: &mut Context<Self>, ) { self.show_edit_predictions_override = show_edit_predictions; self.update_edit_prediction_settings(cx); if let Some(false) = show_edit_predictions { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); } else { self.refresh_edit_prediction( false, true, EditPredictionRequestTrigger::Explicit, window, cx, ); } } pub fn refresh_edit_prediction( &mut self, debounce: bool, user_requested: bool, trigger: EditPredictionRequestTrigger, window: &mut Window, cx: &mut Context<Self>, ) -> Option<()> { if self.leader_id.is_some() { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } let cursor = self.selections.newest_anchor().head(); let (buffer, cursor_buffer_position) = self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) { return None; } if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } self.update_visible_edit_prediction(window, cx); if !user_requested && (!self.should_show_edit_predictions() || !self.is_focused(window) || buffer.read(cx).is_empty()) { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } self.edit_prediction_provider()?.refresh( buffer, cursor_buffer_position, debounce, trigger, cx, ); Some(()) } pub fn edit_predictions_enabled(&self) -> bool { match self.edit_prediction_settings { EditPredictionSettings::Disabled => false, EditPredictionSettings::Enabled { .. } => true, } } pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) { if self.edit_prediction_provider.is_none() { self.edit_prediction_settings = EditPredictionSettings::Disabled; self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return; } let selection = self.selections.newest_anchor(); let cursor = selection.head(); if let Some((buffer, cursor_buffer_position)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) { if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) { self.edit_prediction_settings = EditPredictionSettings::Disabled; self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return; } self.edit_prediction_settings = self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx); } } pub fn edit_prediction_preview_is_active(&self) -> bool { matches!( self.edit_prediction_preview, EditPredictionPreview::Active { .. } ) } pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool { let cursor = self.selections.newest_anchor().head(); if let Some((buffer, cursor_position)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) { self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx) } else { false } } pub fn show_edit_prediction( &mut self, _: &ShowEditPrediction, window: &mut Window, cx: &mut Context<Self>, ) { if !self.has_active_edit_prediction() { self.refresh_edit_prediction( false, true, EditPredictionRequestTrigger::Explicit, window, cx, ); return; } self.update_visible_edit_prediction(window, cx); } pub fn accept_partial_edit_prediction( &mut self, granularity: EditPredictionGranularity, window: &mut Window, cx: &mut Context<Self>, ) { if self.read_only(cx) { return; } if self.show_edit_predictions_in_menu() { self.hide_context_menu(window, cx); } let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else { return; }; if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 { return; } match &active_edit_prediction.completion { EditPrediction::MoveWithin { target, .. } => { let target = *target; if matches!(granularity, EditPredictionGranularity::Full) { if let Some(position_map) = &self.last_position_map { let target_row = target.to_display_point(&position_map.snapshot).row(); let is_visible = position_map.visible_row_range.contains(&target_row); if is_visible || !self.edit_prediction_requires_modifier() { self.unfold_ranges(&[target..target], true, false, cx); self.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_anchor_ranges([target..target]); }, ); self.clear_row_highlights::<EditPredictionPreview>(); self.edit_prediction_preview .set_previous_scroll_position(None); } else { // Highlight and request scroll self.edit_prediction_preview .set_previous_scroll_position(Some( position_map.snapshot.scroll_anchor, )); self.highlight_rows::<EditPredictionPreview>( target..target, |cx| cx.theme().colors().editor_highlighted_line_background, RowHighlightOptions { autoscroll: true, ..Default::default() }, cx, ); self.request_autoscroll(Autoscroll::fit(), cx); } } } else { self.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_anchor_ranges([target..target]); }, ); } } EditPrediction::MoveOutside { snapshot, target } => { if let Some(workspace) = self.workspace() { Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx) .detach_and_log_err(cx); } } EditPrediction::Edit { edits, cursor_position, .. } => { self.report_edit_prediction_event( active_edit_prediction.completion_id.clone(), true, cx, ); match granularity { EditPredictionGranularity::Full => { let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx); // Compute fallback cursor position BEFORE applying the edit, // so the anchor tracks through the edit correctly let fallback_cursor_target = { let snapshot = self.buffer.read(cx).snapshot(cx); let Some((last_edit_range, _)) = edits.last() else { return; }; last_edit_range.end.bias_right(&snapshot) }; self.buffer.update(cx, |buffer, cx| { buffer.edit(edits.iter().cloned(), None, cx) }); if let Some(provider) = self.edit_prediction_provider() { provider.accept(cx); } // Resolve cursor position after the edit is applied let cursor_target = if let Some((anchor, offset)) = cursor_position { // The anchor tracks through the edit, then we add the offset let snapshot = self.buffer.read(cx).snapshot(cx); let base_offset = anchor.to_offset(&snapshot).0; let target_offset = MultiBufferOffset((base_offset + offset).min(snapshot.len().0)); snapshot.anchor_after(target_offset) } else { fallback_cursor_target }; self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_anchor_ranges([cursor_target..cursor_target]); }); let selections = self.selections.disjoint_anchors_arc(); if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) { if transaction_id_prev != Some(transaction_id_now) { self.selection_history .insert_transaction(transaction_id_now, selections); } } self.update_visible_edit_prediction(window, cx); if self.active_edit_prediction.is_none() { self.refresh_edit_prediction( true, true, EditPredictionRequestTrigger::PredictionAccepted, window, cx, ); } cx.notify(); } _ => { let snapshot = self.buffer.read(cx).snapshot(cx); let cursor_offset = self .selections .newest::<MultiBufferOffset>(&self.display_snapshot(cx)) .head();Lines 1–500 of 2598. Load more
use super::*; pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles { EditPredictionStyles { insertion: HighlightStyle { color: Some(cx.theme().status().predictive), ..HighlightStyle::default() }, whitespace: HighlightStyle { background_color: Some(cx.theme().status().created_background), ..HighlightStyle::default() }, } } pub(super) enum EditDisplayMode { TabAccept, DiffPopover, Inline, } pub(super) enum EditPrediction { Edit { // TODO could be a language::Anchor? edits: Vec<(Range<Anchor>, Arc<str>)>, /// Predicted cursor position as (anchor, offset_from_anchor). /// The anchor is in multibuffer coordinates; after applying edits, /// resolve the anchor and add the offset to get the final cursor position. cursor_position: Option<(Anchor, usize)>, edit_preview: Option<EditPreview>, display_mode: EditDisplayMode, snapshot: BufferSnapshot, }, /// Move to a specific location in the active editor MoveWithin { target: Anchor, snapshot: BufferSnapshot, }, /// Move to a specific location in a different editor (not the active one) MoveOutside { target: language::Anchor, snapshot: BufferSnapshot, }, } pub(super) struct EditPredictionState { pub(super) inlay_ids: Vec<InlayId>, pub(super) completion: EditPrediction, pub(super) completion_id: Option<SharedString>, pub(super) invalidation_range: Option<Range<Anchor>>, } pub(super) enum EditPredictionSettings { Disabled, Enabled { show_in_menu: bool, preview_requires_modifier: bool, }, } pub(super) enum MenuEditPredictionsPolicy { #[cfg(test)] Never, ByProvider, } pub(super) enum EditPredictionPreview { /// Modifier is not pressed Inactive { released_too_fast: bool }, /// Modifier pressed Active { since: Instant, previous_scroll_position: Option<SharedScrollAnchor>, }, } #[derive(Copy, Clone, Eq, PartialEq)] pub(super) enum EditPredictionKeybindSurface { Inline, CursorPopoverCompact, CursorPopoverExpanded, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(super) enum EditPredictionKeybindAction { Accept, Preview, } pub(super) struct EditPredictionKeybindDisplay { #[cfg(test)] pub(super) accept_keystroke: Option<gpui::KeybindingKeystroke>, #[cfg(test)] pub(super) preview_keystroke: Option<gpui::KeybindingKeystroke>, pub(super) displayed_keystroke: Option<gpui::KeybindingKeystroke>, pub(super) action: EditPredictionKeybindAction, pub(super) missing_accept_keystroke: bool, pub(super) show_hold_label: bool, } impl EditPredictionPreview { pub(super) fn released_too_fast(&self) -> bool { match self { EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast, EditPredictionPreview::Active { .. } => false, } } pub(super) fn set_previous_scroll_position( &mut self, scroll_position: Option<SharedScrollAnchor>, ) { if let EditPredictionPreview::Active { previous_scroll_position, .. } = self { *previous_scroll_position = scroll_position; } } } pub(super) struct RegisteredEditPredictionDelegate { pub(super) provider: Arc<dyn EditPredictionDelegateHandle>, _subscription: Subscription, } pub(super) fn edit_prediction_edit_text( current_snapshot: &BufferSnapshot, edits: &[(Range<Anchor>, impl AsRef<str>)], edit_preview: &EditPreview, include_deletions: bool, multibuffer_snapshot: &MultiBufferSnapshot, cx: &App, ) -> HighlightedText { let edits = edits .iter() .filter_map(|(anchor, text)| { Some(( multibuffer_snapshot .anchor_range_to_buffer_anchor_range(anchor.clone())? .1, text, )) }) .collect::<Vec<_>>(); edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx) } impl Editor { pub fn set_edit_prediction_provider<T>( &mut self, provider: Option<Entity<T>>, trigger: EditPredictionRequestTrigger, window: &mut Window, cx: &mut Context<Self>, ) where T: EditPredictionDelegate, { self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate { _subscription: cx.observe_in(&provider, window, |this, _, window, cx| { if this.focus_handle.is_focused(window) { this.update_visible_edit_prediction(window, cx); } }), provider: Arc::new(provider), }); self.update_edit_prediction_settings(cx); self.refresh_edit_prediction(false, false, trigger, window, cx); } pub fn set_edit_predictions_hidden_for_vim_mode( &mut self, hidden: bool, window: &mut Window, cx: &mut Context<Self>, ) { if hidden != self.edit_predictions_hidden_for_vim_mode { self.edit_predictions_hidden_for_vim_mode = hidden; if hidden { self.update_visible_edit_prediction(window, cx); } else { self.refresh_edit_prediction( true, false, EditPredictionRequestTrigger::VimModeChanged, window, cx, ); } } } pub fn toggle_edit_predictions( &mut self, _: &ToggleEditPrediction, window: &mut Window, cx: &mut Context<Self>, ) { if self.show_edit_predictions_override.is_some() { self.set_show_edit_predictions(None, window, cx); } else { let show_edit_predictions = !self.edit_predictions_enabled(); self.set_show_edit_predictions(Some(show_edit_predictions), window, cx); } } pub fn set_show_edit_predictions( &mut self, show_edit_predictions: Option<bool>, window: &mut Window, cx: &mut Context<Self>, ) { self.show_edit_predictions_override = show_edit_predictions; self.update_edit_prediction_settings(cx); if let Some(false) = show_edit_predictions { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); } else { self.refresh_edit_prediction( false, true, EditPredictionRequestTrigger::Explicit, window, cx, ); } } pub fn refresh_edit_prediction( &mut self, debounce: bool, user_requested: bool, trigger: EditPredictionRequestTrigger, window: &mut Window, cx: &mut Context<Self>, ) -> Option<()> { if self.leader_id.is_some() { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } let cursor = self.selections.newest_anchor().head(); let (buffer, cursor_buffer_position) = self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) { return None; } if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } self.update_visible_edit_prediction(window, cx); if !user_requested && (!self.should_show_edit_predictions() || !self.is_focused(window) || buffer.read(cx).is_empty()) { self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return None; } self.edit_prediction_provider()?.refresh( buffer, cursor_buffer_position, debounce, trigger, cx, ); Some(()) } pub fn edit_predictions_enabled(&self) -> bool { match self.edit_prediction_settings { EditPredictionSettings::Disabled => false, EditPredictionSettings::Enabled { .. } => true, } } pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) { if self.edit_prediction_provider.is_none() { self.edit_prediction_settings = EditPredictionSettings::Disabled; self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return; } let selection = self.selections.newest_anchor(); let cursor = selection.head(); if let Some((buffer, cursor_buffer_position)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) { if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) { self.edit_prediction_settings = EditPredictionSettings::Disabled; self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx); return; } self.edit_prediction_settings = self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx); } } pub fn edit_prediction_preview_is_active(&self) -> bool { matches!( self.edit_prediction_preview, EditPredictionPreview::Active { .. } ) } pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool { let cursor = self.selections.newest_anchor().head(); if let Some((buffer, cursor_position)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) { self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx) } else { false } } pub fn show_edit_prediction( &mut self, _: &ShowEditPrediction, window: &mut Window, cx: &mut Context<Self>, ) { if !self.has_active_edit_prediction() { self.refresh_edit_prediction( false, true, EditPredictionRequestTrigger::Explicit, window, cx, ); return; } self.update_visible_edit_prediction(window, cx); } pub fn accept_partial_edit_prediction( &mut self, granularity: EditPredictionGranularity, window: &mut Window, cx: &mut Context<Self>, ) { if self.read_only(cx) { return; } if self.show_edit_predictions_in_menu() { self.hide_context_menu(window, cx); } let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else { return; }; if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 { return; } match &active_edit_prediction.completion { EditPrediction::MoveWithin { target, .. } => { let target = *target; if matches!(granularity, EditPredictionGranularity::Full) { if let Some(position_map) = &self.last_position_map { let target_row = target.to_display_point(&position_map.snapshot).row(); let is_visible = position_map.visible_row_range.contains(&target_row); if is_visible || !self.edit_prediction_requires_modifier() { self.unfold_ranges(&[target..target], true, false, cx); self.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_anchor_ranges([target..target]); }, ); self.clear_row_highlights::<EditPredictionPreview>(); self.edit_prediction_preview .set_previous_scroll_position(None); } else { // Highlight and request scroll self.edit_prediction_preview .set_previous_scroll_position(Some( position_map.snapshot.scroll_anchor, )); self.highlight_rows::<EditPredictionPreview>( target..target, |cx| cx.theme().colors().editor_highlighted_line_background, RowHighlightOptions { autoscroll: true, ..Default::default() }, cx, ); self.request_autoscroll(Autoscroll::fit(), cx); } } } else { self.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_anchor_ranges([target..target]); }, ); } } EditPrediction::MoveOutside { snapshot, target } => { if let Some(workspace) = self.workspace() { Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx) .detach_and_log_err(cx); } } EditPrediction::Edit { edits, cursor_position, .. } => { self.report_edit_prediction_event( active_edit_prediction.completion_id.clone(), true, cx, ); match granularity { EditPredictionGranularity::Full => { let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx); // Compute fallback cursor position BEFORE applying the edit, // so the anchor tracks through the edit correctly let fallback_cursor_target = { let snapshot = self.buffer.read(cx).snapshot(cx); let Some((last_edit_range, _)) = edits.last() else { return; }; last_edit_range.end.bias_right(&snapshot) }; self.buffer.update(cx, |buffer, cx| { buffer.edit(edits.iter().cloned(), None, cx) }); if let Some(provider) = self.edit_prediction_provider() { provider.accept(cx); } // Resolve cursor position after the edit is applied let cursor_target = if let Some((anchor, offset)) = cursor_position { // The anchor tracks through the edit, then we add the offset let snapshot = self.buffer.read(cx).snapshot(cx); let base_offset = anchor.to_offset(&snapshot).0; let target_offset = MultiBufferOffset((base_offset + offset).min(snapshot.len().0)); snapshot.anchor_after(target_offset) } else { fallback_cursor_target }; self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_anchor_ranges([cursor_target..cursor_target]); }); let selections = self.selections.disjoint_anchors_arc(); if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) { if transaction_id_prev != Some(transaction_id_now) { self.selection_history .insert_transaction(transaction_id_now, selections); } } self.update_visible_edit_prediction(window, cx); if self.active_edit_prediction.is_none() { self.refresh_edit_prediction( true, true, EditPredictionRequestTrigger::PredictionAccepted, window, cx, ); } cx.notify(); } _ => { let snapshot = self.buffer.read(cx).snapshot(cx); let cursor_offset = self .selections .newest::<MultiBufferOffset>(&self.display_snapshot(cx)) .head();