Improve rate predictions modal (#48630)

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle 2026-02-06 15:46:27 -06:00 committed by GitHub
parent 263d8e58d8
commit d8b2c03c2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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 == '@')
}
}