From d8b2c03c2e5c062291efcab54b09efbebd3961e2 Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Fri, 6 Feb 2026 15:46:27 -0600 Subject: [PATCH] Improve rate predictions modal (#48630) Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ... --- .../src/rate_prediction_modal.rs | 96 ++++++++----------- 1 file changed, 38 insertions(+), 58 deletions(-) diff --git a/crates/edit_prediction_ui/src/rate_prediction_modal.rs b/crates/edit_prediction_ui/src/rate_prediction_modal.rs index 79be2dfed296dc733836f97bcae876c03abf6bce..c6c6b824832a022b4537a53ecfb04dd851285a53 100644 --- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs +++ b/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, } 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 == '@') } }