Improve rate predictions modal (#48630)

Ben Kunkle created

Closes #ISSUE

Release Notes:

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

Change summary

crates/edit_prediction_ui/src/rate_prediction_modal.rs | 96 ++++-------
1 file changed, 38 insertions(+), 58 deletions(-)

Detailed changes

crates/edit_prediction_ui/src/rate_prediction_modal.rs 🔗

@@ -16,7 +16,8 @@ use std::rc::Rc;
 use std::{fmt::Write, sync::Arc, time::Duration};
 use theme::ThemeSettings;
 use ui::{
-    ContextMenu, DropdownMenu, KeyBinding, List, ListItem, ListItemSpacing, Tooltip, prelude::*,
+    ContextMenu, DropdownMenu, KeyBinding, List, ListItem, ListItemSpacing, PopoverMenuHandle,
+    Tooltip, prelude::*,
 };
 use workspace::{ModalView, Workspace};
 
@@ -53,6 +54,7 @@ pub struct RatePredictionsModal {
     focus_handle: FocusHandle,
     _subscription: gpui::Subscription,
     current_view: RatePredictionView,
+    failure_mode_menu_handle: PopoverMenuHandle<ContextMenu>,
 }
 
 struct ActivePrediction {
@@ -112,6 +114,7 @@ impl RatePredictionsModal {
                 editor
             }),
             current_view: RatePredictionView::SuggestedEdits,
+            failure_mode_menu_handle: PopoverMenuHandle::default(),
         }
     }
 
@@ -651,11 +654,12 @@ impl RatePredictionsModal {
                         ContextMenu::build(window, cx, move |menu, _window, _cx| {
                             FeedbackCompletionProvider::FAILURE_MODES
                                 .iter()
-                                .fold(menu, |menu, (_key, description)| {
+                                .fold(menu, |menu, (key, description)| {
+                                    let key: SharedString = (*key).into();
                                     let description: SharedString = (*description).into();
                                     let modal = modal.clone();
                                     menu.entry(
-                                        description.clone(),
+                                        format!("{} {}", key, description),
                                         None,
                                         move |window, cx| {
                                             if let Some(modal) = modal.upgrade() {
@@ -665,18 +669,13 @@ impl RatePredictionsModal {
                                                             cx,
                                                             |editor, cx| {
                                                                 editor.set_text(
-                                                                    description.clone(),
+                                                                    format!("{} {}", key, description),
                                                                     window,
                                                                     cx,
                                                                 );
                                                             },
                                                         );
                                                     }
-                                                    this.thumbs_down_active(
-                                                        &ThumbsDownActivePrediction,
-                                                        window,
-                                                        cx,
-                                                    );
                                                 });
                                             }
                                         },
@@ -692,12 +691,13 @@ impl RatePredictionsModal {
                             .border_color(border_color)
                             .child(
                                 DropdownMenu::new(
-                                    "failure-mode-dropdown",
-                                    "Issue",
-                                    failure_mode_menu,
-                                )
-                                .style(ui::DropdownStyle::Outlined)
-                                .trigger_size(ButtonSize::Compact),
+                                        "failure-mode-dropdown",
+                                        "Issue",
+                                        failure_mode_menu,
+                                    )
+                                    .handle(self.failure_mode_menu_handle.clone())
+                                    .style(ui::DropdownStyle::Outlined)
+                                    .trigger_size(ButtonSize::Compact),
                             )
                             .child(
                                 h_flex()
@@ -964,7 +964,11 @@ impl Render for RatePredictionsModal {
                     ),
             )
             .children(self.render_active_completion(window, cx))
-            .on_mouse_down_out(cx.listener(|_, _, _, cx| cx.emit(DismissEvent)))
+            .on_mouse_down_out(cx.listener(|this, _, _, cx| {
+                if !this.failure_mode_menu_handle.is_deployed() {
+                    cx.emit(DismissEvent);
+                }
+            }))
     }
 }
 
@@ -999,46 +1003,22 @@ struct FeedbackCompletionProvider;
 
 impl FeedbackCompletionProvider {
     const FAILURE_MODES: &'static [(&'static str, &'static str)] = &[
+        ("@location", "Unexpected location"),
+        ("@malformed", "Incomplete, cut off, or syntax error"),
         (
-            "bad_location",
-            "Made a prediction somewhere other than expected",
-        ),
-        ("incomplete", "Prediction was incomplete or cut off"),
-        (
-            "deleted",
-            "Prediction deleted code that should have been kept. Prefer `reverted` if it reverted an edit",
-        ),
-        (
-            "bad_style",
-            "Prediction used wrong coding style or conventions",
-        ),
-        (
-            "repetitive",
-            "Prediction repeated existing code unnecessarily",
-        ),
-        (
-            "hallucinated",
-            "Prediction referenced non-existent variables/functions",
-        ),
-        ("wrong_indent", "Prediction had incorrect indentation"),
-        ("syntax_error", "Introduced a syntax error"),
-        (
-            "too_aggressive",
-            "Prediction made more changes than expected",
-        ),
-        (
-            "too_conservative",
-            "Prediction was overly cautious/conservative",
-        ),
-        (
-            "no_context",
-            "Misunderstood or did not use contextual information",
-        ),
-        ("reverted", "Reverted recent edits"),
-        (
-            "bad_cursor_position",
-            "The prediction moved the cursor to an unhelpful position",
+            "@deleted",
+            "Deleted code that should be kept (use `@reverted` if it undid a recent edit)",
         ),
+        ("@style", "Wrong coding style or conventions"),
+        ("@repetitive", "Repeated existing code"),
+        ("@hallucinated", "Referenced non-existent symbols"),
+        ("@formatting", "Wrong indentation or structure"),
+        ("@aggressive", "Changed more than expected"),
+        ("@conservative", "Too cautious, changed too little"),
+        ("@context", "Ignored or misunderstood context"),
+        ("@reverted", "Undid recent edits"),
+        ("@cursor_position", "Cursor placed in unhelpful position"),
+        ("@whitespace", "Unwanted whitespace or newline changes"),
     ];
 }
 
@@ -1056,7 +1036,7 @@ impl editor::CompletionProvider for FeedbackCompletionProvider {
         let mut count_back = 0;
 
         for char in buffer.reversed_chars_at(buffer_position) {
-            if char.is_ascii_alphanumeric() || char == '_' {
+            if char.is_ascii_alphanumeric() || char == '_' || char == '@' {
                 count_back += 1;
             } else {
                 break;
@@ -1073,7 +1053,7 @@ impl editor::CompletionProvider for FeedbackCompletionProvider {
         let snapshot = buffer.text_snapshot();
         let query: String = snapshot.text_for_range(replace_range.clone()).collect();
 
-        if query.len() < 3 {
+        if !query.starts_with('@') {
             return gpui::Task::ready(Ok(vec![CompletionResponse {
                 completions: vec![],
                 display_options: CompletionDisplayOptions {
@@ -1090,7 +1070,7 @@ impl editor::CompletionProvider for FeedbackCompletionProvider {
             .filter(|(key, _description)| key.starts_with(&query_lower))
             .map(|(key, description)| Completion {
                 replace_range: replace_range.clone(),
-                new_text: description.to_string(),
+                new_text: format!("{} {}", key, description),
                 label: CodeLabel::plain(format!("{}: {}", key, description), None),
                 documentation: None,
                 source: CompletionSource::Custom,
@@ -1121,6 +1101,6 @@ impl editor::CompletionProvider for FeedbackCompletionProvider {
     ) -> bool {
         text.chars()
             .last()
-            .is_some_and(|c| c.is_ascii_alphanumeric() || c == '_')
+            .is_some_and(|c| c.is_ascii_alphanumeric() || c == '_' || c == '@')
     }
 }