Differentiate between explicit rejection and ignored in ep acceptance tracking (#48409)

Ben Kunkle created

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/cloud_llm_client/src/cloud_llm_client.rs              |  2 
crates/codestral/src/codestral.rs                            |  4 
crates/copilot/src/copilot_edit_prediction_delegate.rs       |  4 
crates/edit_prediction/src/edit_prediction.rs                | 23 ---
crates/edit_prediction/src/edit_prediction_tests.rs          | 12 --
crates/edit_prediction/src/mercury.rs                        | 11 +
crates/edit_prediction/src/zed_edit_prediction_delegate.rs   | 16 +-
crates/edit_prediction_types/src/edit_prediction_types.rs    |  8 
crates/editor/src/edit_prediction_tests.rs                   |  4 
crates/editor/src/editor.rs                                  | 33 ++---
crates/supermaven/src/supermaven_edit_prediction_delegate.rs |  4 
11 files changed, 46 insertions(+), 75 deletions(-)

Detailed changes

crates/cloud_llm_client/src/cloud_llm_client.rs 🔗

@@ -173,6 +173,8 @@ pub enum EditPredictionRejectReason {
     /// The current prediction was discarded
     #[default]
     Discarded,
+    /// The current prediction was explicitly rejected by the user
+    Rejected,
 }
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]

crates/codestral/src/codestral.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::Result;
 use edit_prediction::cursor_excerpt;
 use edit_prediction_types::{
-    EditPrediction, EditPredictionDelegate, EditPredictionDismissReason, EditPredictionIconSet,
+    EditPrediction, EditPredictionDelegate, EditPredictionDiscardReason, EditPredictionIconSet,
 };
 use futures::AsyncReadExt;
 use gpui::{App, Context, Entity, Task};
@@ -315,7 +315,7 @@ impl EditPredictionDelegate for CodestralEditPredictionDelegate {
         self.current_completion = None;
     }
 
-    fn discard(&mut self, _reason: EditPredictionDismissReason, _cx: &mut Context<Self>) {
+    fn discard(&mut self, _reason: EditPredictionDiscardReason, _cx: &mut Context<Self>) {
         log::debug!("Codestral: Completion discarded");
         self.pending_request = None;
         self.current_completion = None;

crates/copilot/src/copilot_edit_prediction_delegate.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
 };
 use anyhow::Result;
 use edit_prediction_types::{
-    EditPrediction, EditPredictionDelegate, EditPredictionDismissReason, EditPredictionIconSet,
+    EditPrediction, EditPredictionDelegate, EditPredictionDiscardReason, EditPredictionIconSet,
     interpolate_edits,
 };
 use gpui::{App, Context, Entity, Task};
@@ -129,7 +129,7 @@ impl EditPredictionDelegate for CopilotEditPredictionDelegate {
         }
     }
 
-    fn discard(&mut self, _reason: EditPredictionDismissReason, _: &mut Context<Self>) {}
+    fn discard(&mut self, _reason: EditPredictionDiscardReason, _: &mut Context<Self>) {}
 
     fn suggest(
         &mut self,

crates/edit_prediction/src/edit_prediction.rs 🔗

@@ -312,7 +312,6 @@ impl ProjectState {
                         prediction_id,
                         EditPredictionRejectReason::Canceled,
                         false,
-                        edit_prediction_types::EditPredictionDismissReason::Ignored,
                         cx,
                     );
                 })
@@ -1293,19 +1292,12 @@ impl EditPredictionStore {
         &mut self,
         reason: EditPredictionRejectReason,
         project: &Entity<Project>,
-        dismiss_reason: edit_prediction_types::EditPredictionDismissReason,
         cx: &App,
     ) {
         if let Some(project_state) = self.projects.get_mut(&project.entity_id()) {
             project_state.pending_predictions.clear();
             if let Some(prediction) = project_state.current_prediction.take() {
-                self.reject_prediction(
-                    prediction.prediction.id,
-                    reason,
-                    prediction.was_shown,
-                    dismiss_reason,
-                    cx,
-                );
+                self.reject_prediction(prediction.prediction.id, reason, prediction.was_shown, cx);
             }
         };
     }
@@ -1364,7 +1356,6 @@ impl EditPredictionStore {
         prediction_id: EditPredictionId,
         reason: EditPredictionRejectReason,
         was_shown: bool,
-        dismiss_reason: edit_prediction_types::EditPredictionDismissReason,
         cx: &App,
     ) {
         match self.edit_prediction_model {
@@ -1384,7 +1375,7 @@ impl EditPredictionStore {
                 mercury::edit_prediction_rejected(
                     prediction_id,
                     was_shown,
-                    dismiss_reason,
+                    reason,
                     self.client.http_client(),
                     cx,
                 );
@@ -1638,7 +1629,6 @@ impl EditPredictionStore {
                                     this.reject_current_prediction(
                                         EditPredictionRejectReason::Replaced,
                                         &project,
-                                        edit_prediction_types::EditPredictionDismissReason::Ignored,
                                         cx,
                                     );
 
@@ -1648,7 +1638,6 @@ impl EditPredictionStore {
                                         new_prediction.prediction.id,
                                         EditPredictionRejectReason::CurrentPreferred,
                                         false,
-                                        edit_prediction_types::EditPredictionDismissReason::Ignored,
                                         cx,
                                     );
                                     None
@@ -1658,13 +1647,7 @@ impl EditPredictionStore {
                             }
                         }
                         Err(reject_reason) => {
-                            this.reject_prediction(
-                                prediction_result.id,
-                                reject_reason,
-                                false,
-                                edit_prediction_types::EditPredictionDismissReason::Ignored,
-                                cx,
-                            );
+                            this.reject_prediction(prediction_result.id, reject_reason, false, cx);
                             None
                         }
                     }

crates/edit_prediction/src/edit_prediction_tests.rs 🔗

@@ -94,12 +94,7 @@ async fn test_current_state(cx: &mut TestAppContext) {
     });
 
     ep_store.update(cx, |ep_store, cx| {
-        ep_store.reject_current_prediction(
-            EditPredictionRejectReason::Discarded,
-            &project,
-            edit_prediction_types::EditPredictionDismissReason::Ignored,
-            cx,
-        );
+        ep_store.reject_current_prediction(EditPredictionRejectReason::Discarded, &project, cx);
     });
 
     // Prediction for diagnostic in another file
@@ -1135,14 +1130,12 @@ async fn test_rejections_flushing(cx: &mut TestAppContext) {
             EditPredictionId("test-1".into()),
             EditPredictionRejectReason::Discarded,
             false,
-            edit_prediction_types::EditPredictionDismissReason::Ignored,
             cx,
         );
         ep_store.reject_prediction(
             EditPredictionId("test-2".into()),
             EditPredictionRejectReason::Canceled,
             true,
-            edit_prediction_types::EditPredictionDismissReason::Ignored,
             cx,
         );
     });
@@ -1179,7 +1172,6 @@ async fn test_rejections_flushing(cx: &mut TestAppContext) {
                 EditPredictionId(format!("batch-{}", i).into()),
                 EditPredictionRejectReason::Discarded,
                 false,
-                edit_prediction_types::EditPredictionDismissReason::Ignored,
                 cx,
             );
         }
@@ -1211,7 +1203,6 @@ async fn test_rejections_flushing(cx: &mut TestAppContext) {
             EditPredictionId("retry-1".into()),
             EditPredictionRejectReason::Discarded,
             false,
-            edit_prediction_types::EditPredictionDismissReason::Ignored,
             cx,
         );
     });
@@ -1231,7 +1222,6 @@ async fn test_rejections_flushing(cx: &mut TestAppContext) {
             EditPredictionId("retry-2".into()),
             EditPredictionRejectReason::Discarded,
             false,
-            edit_prediction_types::EditPredictionDismissReason::Ignored,
             cx,
         );
     });

crates/edit_prediction/src/mercury.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
     prediction::EditPredictionResult, zeta1::compute_edits,
 };
 use anyhow::{Context as _, Result};
-use edit_prediction_types::EditPredictionDismissReason;
+use cloud_llm_client::EditPredictionRejectReason;
 use futures::AsyncReadExt as _;
 use gpui::{
     App, AppContext as _, Entity, Global, SharedString, Task,
@@ -358,16 +358,17 @@ pub(crate) fn edit_prediction_accepted(
 pub(crate) fn edit_prediction_rejected(
     prediction_id: EditPredictionId,
     was_shown: bool,
-    dismiss_reason: EditPredictionDismissReason,
+    reason: EditPredictionRejectReason,
     http_client: Arc<dyn HttpClient>,
     cx: &App,
 ) {
     if !was_shown {
         return;
     }
-    let action = match dismiss_reason {
-        EditPredictionDismissReason::Rejected => MercuryUserAction::Reject,
-        EditPredictionDismissReason::Ignored => MercuryUserAction::Ignore,
+    let action = match reason {
+        EditPredictionRejectReason::Rejected => MercuryUserAction::Reject,
+        EditPredictionRejectReason::Discarded => MercuryUserAction::Ignore,
+        _ => return,
     };
     send_feedback(prediction_id, action, http_client, cx);
 }

crates/edit_prediction/src/zed_edit_prediction_delegate.rs 🔗

@@ -3,7 +3,7 @@ use std::{cmp, sync::Arc};
 use client::{Client, UserStore};
 use cloud_llm_client::EditPredictionRejectReason;
 use edit_prediction_types::{
-    DataCollectionState, EditPredictionDelegate, EditPredictionDismissReason,
+    DataCollectionState, EditPredictionDelegate, EditPredictionDiscardReason,
     EditPredictionIconSet, SuggestionDisplayType,
 };
 use gpui::{App, Entity, prelude::*};
@@ -168,14 +168,13 @@ impl EditPredictionDelegate for ZedEditPredictionDelegate {
         });
     }
 
-    fn discard(&mut self, reason: EditPredictionDismissReason, cx: &mut Context<Self>) {
+    fn discard(&mut self, reason: EditPredictionDiscardReason, cx: &mut Context<Self>) {
+        let reject_reason = match reason {
+            EditPredictionDiscardReason::Rejected => EditPredictionRejectReason::Rejected,
+            EditPredictionDiscardReason::Ignored => EditPredictionRejectReason::Discarded,
+        };
         self.store.update(cx, |store, cx| {
-            store.reject_current_prediction(
-                EditPredictionRejectReason::Discarded,
-                &self.project,
-                reason,
-                cx,
-            );
+            store.reject_current_prediction(reject_reason, &self.project, cx);
         });
     }
 
@@ -213,7 +212,6 @@ impl EditPredictionDelegate for ZedEditPredictionDelegate {
                 store.reject_current_prediction(
                     EditPredictionRejectReason::InterpolatedEmpty,
                     &self.project,
-                    EditPredictionDismissReason::Ignored,
                     cx,
                 );
                 return None;

crates/edit_prediction_types/src/edit_prediction_types.rs 🔗

@@ -4,7 +4,7 @@ use client::EditPredictionUsage;
 use gpui::{App, Context, Entity, SharedString};
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum EditPredictionDismissReason {
+pub enum EditPredictionDiscardReason {
     Rejected,
     Ignored,
 }
@@ -184,7 +184,7 @@ pub trait EditPredictionDelegate: 'static + Sized {
         cx: &mut Context<Self>,
     );
     fn accept(&mut self, cx: &mut Context<Self>);
-    fn discard(&mut self, reason: EditPredictionDismissReason, cx: &mut Context<Self>);
+    fn discard(&mut self, reason: EditPredictionDiscardReason, cx: &mut Context<Self>);
     fn did_show(&mut self, _display_type: SuggestionDisplayType, _cx: &mut Context<Self>) {}
     fn suggest(
         &mut self,
@@ -220,7 +220,7 @@ pub trait EditPredictionDelegateHandle {
     );
     fn did_show(&self, display_type: SuggestionDisplayType, cx: &mut App);
     fn accept(&self, cx: &mut App);
-    fn discard(&self, reason: EditPredictionDismissReason, cx: &mut App);
+    fn discard(&self, reason: EditPredictionDiscardReason, cx: &mut App);
     fn suggest(
         &self,
         buffer: &Entity<Buffer>,
@@ -298,7 +298,7 @@ where
         self.update(cx, |this, cx| this.accept(cx))
     }
 
-    fn discard(&self, reason: EditPredictionDismissReason, cx: &mut App) {
+    fn discard(&self, reason: EditPredictionDiscardReason, cx: &mut App) {
         self.update(cx, |this, cx| this.discard(reason, cx))
     }
 

crates/editor/src/edit_prediction_tests.rs 🔗

@@ -625,7 +625,7 @@ impl EditPredictionDelegate for FakeEditPredictionDelegate {
 
     fn discard(
         &mut self,
-        _reason: edit_prediction_types::EditPredictionDismissReason,
+        _reason: edit_prediction_types::EditPredictionDiscardReason,
         _cx: &mut gpui::Context<Self>,
     ) {
     }
@@ -701,7 +701,7 @@ impl EditPredictionDelegate for FakeNonZedEditPredictionDelegate {
 
     fn discard(
         &mut self,
-        _reason: edit_prediction_types::EditPredictionDismissReason,
+        _reason: edit_prediction_types::EditPredictionDiscardReason,
         _cx: &mut gpui::Context<Self>,
     ) {
     }

crates/editor/src/editor.rs 🔗

@@ -96,7 +96,7 @@ use convert_case::{Case, Casing};
 use dap::TelemetrySpawnLocation;
 use display_map::*;
 use edit_prediction_types::{
-    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDismissReason,
+    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
     EditPredictionGranularity, SuggestionDisplayType,
 };
 use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
@@ -3406,7 +3406,7 @@ impl Editor {
         self.update_edit_prediction_settings(cx);
 
         if let Some(false) = show_edit_predictions {
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
         } else {
             self.refresh_edit_prediction(false, true, window, cx);
         }
@@ -4438,7 +4438,8 @@ impl Editor {
         dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
         dismissed |= self.hide_context_menu(window, cx).is_some();
         dismissed |= self.mouse_context_menu.take().is_some();
-        dismissed |= is_user_requested && self.discard_edit_prediction(true, cx);
+        dismissed |= is_user_requested
+            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
         dismissed |= self.snippet_stack.pop().is_some();
         if self.diff_review_drag_state.is_some() {
             self.cancel_diff_review_drag(cx);
@@ -6190,7 +6191,8 @@ impl Editor {
                         if editor.show_edit_predictions_in_menu() {
                             editor.update_visible_edit_prediction(window, cx);
                         } else {
-                            editor.discard_edit_prediction(false, cx);
+                            editor
+                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
                         }
 
                         cx.notify();
@@ -6297,7 +6299,7 @@ impl Editor {
             let entries = completions_menu.entries.borrow();
             let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
             if self.show_edit_predictions_in_menu() {
-                self.discard_edit_prediction(true, cx);
+                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
             }
             mat.candidate_id
         };
@@ -6552,7 +6554,7 @@ impl Editor {
         let deployed_from = action.deployed_from.clone();
         let action = action.clone();
         self.completion_tasks.clear();
-        self.discard_edit_prediction(false, cx);
+        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 
         let multibuffer_point = match &action.deployed_from {
             Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
@@ -7653,7 +7655,7 @@ impl Editor {
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 
         if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
             return None;
         }
 
@@ -7664,7 +7666,7 @@ impl Editor {
                 || !self.is_focused(window)
                 || buffer.read(cx).is_empty())
         {
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
             return None;
         }
 
@@ -7699,7 +7701,7 @@ impl Editor {
     pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
         if self.edit_prediction_provider.is_none() || DisableAiSettings::get_global(cx).disable_ai {
             self.edit_prediction_settings = EditPredictionSettings::Disabled;
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
         } else {
             let selection = self.selections.newest_anchor();
             let cursor = selection.head();
@@ -8073,10 +8075,10 @@ impl Editor {
 
     fn discard_edit_prediction(
         &mut self,
-        should_report_edit_prediction_event: bool,
+        reason: EditPredictionDiscardReason,
         cx: &mut Context<Self>,
     ) -> bool {
-        if should_report_edit_prediction_event {
+        if reason == EditPredictionDiscardReason::Rejected {
             let completion_id = self
                 .active_edit_prediction
                 .as_ref()
@@ -8086,11 +8088,6 @@ impl Editor {
         }
 
         if let Some(provider) = self.edit_prediction_provider() {
-            let reason = if should_report_edit_prediction_event {
-                EditPredictionDismissReason::Rejected
-            } else {
-                EditPredictionDismissReason::Ignored
-            };
             provider.discard(reason, cx);
         }
 
@@ -8360,7 +8357,7 @@ impl Editor {
         }
 
         if self.ime_transaction.is_some() {
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
             return None;
         }
 
@@ -8389,7 +8386,7 @@ impl Editor {
                     !invalidation_range.contains(&offset_selection.head())
                 })
         {
-            self.discard_edit_prediction(false, cx);
+            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
             return None;
         }
 

crates/supermaven/src/supermaven_edit_prediction_delegate.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{Supermaven, SupermavenCompletionStateId};
 use anyhow::Result;
 use edit_prediction_types::{
-    EditPrediction, EditPredictionDelegate, EditPredictionDismissReason, EditPredictionIconSet,
+    EditPrediction, EditPredictionDelegate, EditPredictionDiscardReason, EditPredictionIconSet,
 };
 use futures::StreamExt as _;
 use gpui::{App, Context, Entity, EntityId, Task};
@@ -203,7 +203,7 @@ impl EditPredictionDelegate for SupermavenEditPredictionDelegate {
         reset_completion_cache(self, _cx);
     }
 
-    fn discard(&mut self, _reason: EditPredictionDismissReason, _cx: &mut Context<Self>) {
+    fn discard(&mut self, _reason: EditPredictionDiscardReason, _cx: &mut Context<Self>) {
         reset_completion_cache(self, _cx);
     }