edit predictions: Reset onboarding action (#24387)

Agus Zubiaga created

https://github.com/user-attachments/assets/bb597b93-a616-4f8a-8608-013b8202799c


Release Notes:

- N/A

Change summary

crates/language/src/language_settings.rs | 11 ++++++
crates/zeta/src/init.rs                  | 19 +++++++++++
crates/zeta/src/onboarding_banner.rs     | 44 +++++++++++--------------
3 files changed, 50 insertions(+), 24 deletions(-)

Detailed changes

crates/language/src/language_settings.rs 🔗

@@ -206,6 +206,17 @@ pub enum InlineCompletionProvider {
     Zed,
 }
 
+impl InlineCompletionProvider {
+    pub fn is_zed(&self) -> bool {
+        match self {
+            InlineCompletionProvider::Zed => true,
+            InlineCompletionProvider::None
+            | InlineCompletionProvider::Copilot
+            | InlineCompletionProvider::Supermaven => false,
+        }
+    }
+}
+
 /// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot)
 /// or [Supermaven](https://supermaven.com).
 #[derive(Clone, Debug, Default)]

crates/zeta/src/init.rs 🔗

@@ -4,11 +4,16 @@ use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::{
     FeatureFlagAppExt as _, PredictEditsFeatureFlag, PredictEditsRateCompletionsFeatureFlag,
 };
+use gpui::actions;
+use language::language_settings::{AllLanguageSettings, InlineCompletionProvider};
+use settings::update_settings_file;
 use ui::App;
 use workspace::Workspace;
 
 use crate::{onboarding_modal::ZedPredictModal, RateCompletionModal, RateCompletions};
 
+actions!(edit_predictions, [ResetOnboarding]);
+
 pub fn init(cx: &mut App) {
     cx.observe_new(move |workspace: &mut Workspace, _, _cx| {
         workspace.register_action(|workspace, _: &RateCompletions, window, cx| {
@@ -31,6 +36,20 @@ pub fn init(cx: &mut App) {
                 }
             },
         );
+
+        workspace.register_action(|workspace, _: &ResetOnboarding, _window, cx| {
+            update_settings_file::<AllLanguageSettings>(
+                workspace.app_state().fs.clone(),
+                cx,
+                move |file, _| {
+                    file.features
+                        .get_or_insert(Default::default())
+                        .inline_completion_provider = Some(InlineCompletionProvider::None)
+                },
+            );
+
+            crate::onboarding_banner::clear_dismissed(cx);
+        });
     })
     .detach();
 

crates/zeta/src/onboarding_banner.rs 🔗

@@ -11,6 +11,7 @@ use crate::onboarding_event;
 /// Prompts the user to try Zed's Edit Prediction feature
 pub struct ZedPredictBanner {
     dismissed: bool,
+    provider: InlineCompletionProvider,
     _subscription: Subscription,
 }
 
@@ -18,40 +19,30 @@ impl ZedPredictBanner {
     pub fn new(cx: &mut Context<Self>) -> Self {
         Self {
             dismissed: get_dismissed(),
+            provider: all_language_settings(None, cx).inline_completions.provider,
             _subscription: cx.observe_global::<SettingsStore>(Self::handle_settings_changed),
         }
     }
 
     fn should_show(&self, cx: &mut App) -> bool {
-        if !cx.has_flag::<PredictEditsFeatureFlag>() || self.dismissed {
-            return false;
-        }
-
-        let provider = all_language_settings(None, cx).inline_completions.provider;
-
-        match provider {
-            InlineCompletionProvider::None
-            | InlineCompletionProvider::Copilot
-            | InlineCompletionProvider::Supermaven => true,
-            InlineCompletionProvider::Zed => false,
-        }
+        cx.has_flag::<PredictEditsFeatureFlag>() && !self.dismissed && !self.provider.is_zed()
     }
 
     fn handle_settings_changed(&mut self, cx: &mut Context<Self>) {
-        if self.dismissed {
+        let new_provider = all_language_settings(None, cx).inline_completions.provider;
+
+        if new_provider == self.provider {
             return;
         }
 
-        let provider = all_language_settings(None, cx).inline_completions.provider;
-
-        match provider {
-            InlineCompletionProvider::None
-            | InlineCompletionProvider::Copilot
-            | InlineCompletionProvider::Supermaven => {}
-            InlineCompletionProvider::Zed => {
-                self.dismiss(cx);
-            }
+        if new_provider.is_zed() {
+            self.dismiss(cx);
+        } else {
+            self.dismissed = get_dismissed();
         }
+
+        self.provider = new_provider;
+        cx.notify();
     }
 
     fn dismiss(&mut self, cx: &mut Context<Self>) {
@@ -64,14 +55,14 @@ impl ZedPredictBanner {
 
 const DISMISSED_AT_KEY: &str = "zed_predict_banner_dismissed_at";
 
-pub(crate) fn get_dismissed() -> bool {
+fn get_dismissed() -> bool {
     db::kvp::KEY_VALUE_STORE
         .read_kvp(DISMISSED_AT_KEY)
         .log_err()
         .map_or(false, |dismissed| dismissed.is_some())
 }
 
-pub(crate) fn persist_dismissed(cx: &mut App) {
+fn persist_dismissed(cx: &mut App) {
     cx.spawn(|_| {
         let time = Utc::now().to_rfc3339();
         db::kvp::KEY_VALUE_STORE.write_kvp(DISMISSED_AT_KEY.into(), time)
@@ -79,6 +70,11 @@ pub(crate) fn persist_dismissed(cx: &mut App) {
     .detach_and_log_err(cx);
 }
 
+pub(crate) fn clear_dismissed(cx: &mut App) {
+    cx.spawn(|_| db::kvp::KEY_VALUE_STORE.delete_kvp(DISMISSED_AT_KEY.into()))
+        .detach_and_log_err(cx);
+}
+
 impl Render for ZedPredictBanner {
     fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if !self.should_show(cx) {