2598 lines · 95.6 KB · Rust
RawDownload
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.