edit predictions: Invalidate cached settings and unset provider when set to `none` (#25505)

Agus Zubiaga , Danilo , and Danilo Leal created

Fixes a few state mismatches when changing providers and other settings

Release Notes:

- edit predictions: Fix mismatch between status bar settings and editor
control settings
- edit predictions: Turn off as soon as `edit_prediction_provider` is
set to `none`

---------

Co-authored-by: Danilo <danilo@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/editor/src/editor.rs                      |  29 ++++
crates/editor/src/element.rs                     |   2 
crates/zed/src/zed/inline_completion_registry.rs |   6 
crates/zed/src/zed/quick_action_bar.rs           | 107 +++++++----------
4 files changed, 71 insertions(+), 73 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1824,6 +1824,7 @@ impl Editor {
                 }),
                 provider: Arc::new(provider),
             });
+        self.update_edit_prediction_settings(cx);
         self.refresh_inline_completion(false, false, window, cx);
     }
 
@@ -1943,7 +1944,7 @@ impl Editor {
         self.auto_replace_emoji_shortcode = auto_replace;
     }
 
-    pub fn toggle_inline_completions(
+    pub fn toggle_edit_predictions(
         &mut self,
         _: &ToggleEditPrediction,
         window: &mut Window,
@@ -1964,6 +1965,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.show_inline_completions_override = show_edit_predictions;
+        self.update_edit_prediction_settings(cx);
 
         if let Some(false) = show_edit_predictions {
             self.discard_inline_completion(false, cx);
@@ -4822,7 +4824,7 @@ impl Editor {
         let (buffer, cursor_buffer_position) =
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 
-        if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
+        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
             self.discard_inline_completion(false, cx);
             return None;
         }
@@ -4871,6 +4873,22 @@ impl Editor {
         }
     }
 
+    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;
+        } else {
+            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)
+            {
+                self.edit_prediction_settings =
+                    self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
+            }
+        }
+    }
+
     fn edit_prediction_settings_at_position(
         &self,
         buffer: &Entity<Buffer>,
@@ -4925,18 +4943,18 @@ impl Editor {
         )
     }
 
-    pub fn inline_completions_enabled(&self, cx: &App) -> bool {
+    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.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
+            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
         } else {
             false
         }
     }
 
-    fn inline_completions_enabled_in_buffer(
+    fn edit_predictions_enabled_in_buffer(
         &self,
         buffer: &Entity<Buffer>,
         buffer_position: language::Anchor,
@@ -15154,6 +15172,7 @@ impl Editor {
 
     fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.tasks_update_task = Some(self.refresh_runnables(window, cx));
+        self.update_edit_prediction_settings(cx);
         self.refresh_inline_completion(true, false, window, cx);
         self.refresh_inlay_hints(
             InlayHintRefreshReason::SettingsChange(inlay_hint_settings(

crates/editor/src/element.rs 🔗

@@ -406,7 +406,7 @@ impl EditorElement {
         register_action(editor, window, Editor::toggle_relative_line_numbers);
         register_action(editor, window, Editor::toggle_indent_guides);
         register_action(editor, window, Editor::toggle_inlay_hints);
-        register_action(editor, window, Editor::toggle_inline_completions);
+        register_action(editor, window, Editor::toggle_edit_predictions);
         register_action(editor, window, Editor::toggle_inline_diagnostics);
         register_action(editor, window, hover_popover::hover);
         register_action(editor, window, Editor::reveal_in_finder);

crates/zed/src/zed/inline_completion_registry.rs 🔗

@@ -9,7 +9,7 @@ use settings::SettingsStore;
 use std::{cell::RefCell, rc::Rc, sync::Arc};
 use supermaven::{Supermaven, SupermavenCompletionProvider};
 use ui::Window;
-use zeta::ProviderDataCollection;
+use zeta::{ProviderDataCollection, ZetaInlineCompletionProvider};
 
 pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
     let editors: Rc<RefCell<HashMap<WeakEntity<Editor>, AnyWindowHandle>>> = Rc::default();
@@ -225,7 +225,9 @@ fn assign_edit_prediction_provider(
     let singleton_buffer = editor.buffer().read(cx).as_singleton();
 
     match provider {
-        EditPredictionProvider::None => {}
+        EditPredictionProvider::None => {
+            editor.set_edit_prediction_provider::<ZetaInlineCompletionProvider>(None, window, cx);
+        }
         EditPredictionProvider::Copilot => {
             if let Some(copilot) = Copilot::global(cx) {
                 if let Some(buffer) = singleton_buffer {

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -87,46 +87,21 @@ impl Render for QuickActionBar {
             return div().id("empty quick action bar");
         };
 
-        let (
-            selection_menu_enabled,
-            inlay_hints_enabled,
-            supports_inlay_hints,
-            inline_diagnostics_enabled,
-            supports_inline_diagnostics,
-            git_blame_inline_enabled,
-            show_git_blame_gutter,
-            auto_signature_help_enabled,
-            show_inline_completions,
-            inline_completion_enabled,
-        ) = {
-            let supports_inlay_hints =
-                editor.update(cx, |editor, cx| editor.supports_inlay_hints(cx));
-            let editor = editor.read(cx);
-            let selection_menu_enabled = editor.selection_menu_enabled(cx);
-            let inlay_hints_enabled = editor.inlay_hints_enabled();
-            let show_inline_diagnostics = editor.show_inline_diagnostics();
-            let supports_inline_diagnostics = editor.inline_diagnostics_enabled();
-            let git_blame_inline_enabled = editor.git_blame_inline_enabled();
-            let show_git_blame_gutter = editor.show_git_blame_gutter();
-            let auto_signature_help_enabled = editor.auto_signature_help_enabled(cx);
-            let show_edit_predictions = editor.edit_predictions_enabled();
-            let inline_completion_enabled = editor.inline_completions_enabled(cx);
-
-            (
-                selection_menu_enabled,
-                inlay_hints_enabled,
-                supports_inlay_hints,
-                show_inline_diagnostics,
-                supports_inline_diagnostics,
-                git_blame_inline_enabled,
-                show_git_blame_gutter,
-                auto_signature_help_enabled,
-                show_edit_predictions,
-                inline_completion_enabled,
-            )
-        };
-
-        let focus_handle = editor.read(cx).focus_handle(cx);
+        let supports_inlay_hints = editor.update(cx, |editor, cx| editor.supports_inlay_hints(cx));
+        let editor_value = editor.read(cx);
+        let selection_menu_enabled = editor_value.selection_menu_enabled(cx);
+        let inlay_hints_enabled = editor_value.inlay_hints_enabled();
+        let inline_diagnostics_enabled = editor_value.show_inline_diagnostics();
+        let supports_inline_diagnostics = editor_value.inline_diagnostics_enabled();
+        let git_blame_inline_enabled = editor_value.git_blame_inline_enabled();
+        let show_git_blame_gutter = editor_value.show_git_blame_gutter();
+        let auto_signature_help_enabled = editor_value.auto_signature_help_enabled(cx);
+        let has_edit_prediction_provider = editor_value.edit_prediction_provider().is_some();
+        let show_edit_predictions = editor_value.edit_predictions_enabled();
+        let edit_predictions_enabled_at_cursor =
+            editor_value.edit_predictions_enabled_at_cursor(cx);
+
+        let focus_handle = editor_value.focus_handle(cx);
 
         let search_button = editor.is_singleton(cx).then(|| {
             QuickActionBarButton::new(
@@ -328,33 +303,35 @@ impl Render for QuickActionBar {
                                 },
                             );
 
-                            let mut inline_completion_entry = ContextMenuEntry::new("Edit Predictions")
-                                .toggleable(IconPosition::Start, inline_completion_enabled && show_inline_completions)
-                                .disabled(!inline_completion_enabled)
-                                .action(Some(
-                                    editor::actions::ToggleEditPrediction.boxed_clone(),
-                                )).handler({
-                                    let editor = editor.clone();
-                                    move |window, cx| {
-                                        editor
-                                            .update(cx, |editor, cx| {
-                                                editor.toggle_inline_completions(
-                                                    &editor::actions::ToggleEditPrediction,
-                                                    window,
-                                                    cx,
-                                                );
-                                            })
-                                            .ok();
-                                    }
-                                });
-                            if !inline_completion_enabled {
-                                inline_completion_entry = inline_completion_entry.documentation_aside(|_| {
-                                    Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element()
-                                });
+                            if has_edit_prediction_provider {
+                                let mut inline_completion_entry = ContextMenuEntry::new("Edit Predictions")
+                                    .toggleable(IconPosition::Start, edit_predictions_enabled_at_cursor && show_edit_predictions)
+                                    .disabled(!edit_predictions_enabled_at_cursor)
+                                    .action(Some(
+                                        editor::actions::ToggleEditPrediction.boxed_clone(),
+                                    )).handler({
+                                        let editor = editor.clone();
+                                        move |window, cx| {
+                                            editor
+                                                .update(cx, |editor, cx| {
+                                                    editor.toggle_edit_predictions(
+                                                        &editor::actions::ToggleEditPrediction,
+                                                        window,
+                                                        cx,
+                                                    );
+                                                })
+                                                .ok();
+                                        }
+                                    });
+                                if !edit_predictions_enabled_at_cursor {
+                                    inline_completion_entry = inline_completion_entry.documentation_aside(|_| {
+                                        Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element()
+                                    });
+                                }
+
+                                menu = menu.item(inline_completion_entry);
                             }
 
-                            menu = menu.item(inline_completion_entry);
-
                             menu = menu.separator();
 
                             menu = menu.toggleable_entry(