From 1575dbe27c79aeb9e8b277a514a93cfa679a2337 Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Mon, 8 Jun 2026 09:08:35 -0400 Subject: [PATCH] ep: Show empty predictions in rate predictions modal (#58829) Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... --- crates/edit_prediction/src/edit_prediction.rs | 43 ++- .../src/edit_prediction_tests.rs | 22 +- crates/edit_prediction/src/fim.rs | 2 + crates/edit_prediction/src/mercury.rs | 2 + crates/edit_prediction/src/prediction.rs | 58 +++- crates/edit_prediction/src/zeta.rs | 27 +- .../src/rate_prediction_modal.rs | 267 ++++++++---------- crates/language/src/buffer.rs | 8 + 8 files changed, 263 insertions(+), 166 deletions(-) diff --git a/crates/edit_prediction/src/edit_prediction.rs b/crates/edit_prediction/src/edit_prediction.rs index e71d9119cd..c177d23844 100644 --- a/crates/edit_prediction/src/edit_prediction.rs +++ b/crates/edit_prediction/src/edit_prediction.rs @@ -2386,6 +2386,7 @@ impl EditPredictionStore { EditPredictionResult { id: prediction_result.id, prediction: Err(EditPredictionRejectReason::CurrentPreferred), + display_prediction: None, model_version: prediction_result.model_version, e2e_latency: prediction_result.e2e_latency, } @@ -2575,14 +2576,18 @@ impl EditPredictionStore { let new_current_prediction = if !is_cancelled && let Some((prediction_result, requested_by)) = new_prediction_result { - match prediction_result.prediction { - Ok(prediction) => { + match prediction_result { + EditPredictionResult { + prediction: Ok(prediction), + e2e_latency, + .. + } => { let new_prediction = CurrentEditPrediction { requested_by, prediction, was_shown: false, shown_with: None, - e2e_latency: prediction_result.e2e_latency, + e2e_latency, }; if let Some(current_prediction) = @@ -2612,15 +2617,39 @@ impl EditPredictionStore { Some(new_prediction) } } - Err(reject_reason) => { + EditPredictionResult { + id, + prediction: Err(reject_reason), + display_prediction, + model_version, + e2e_latency, + } => { + let should_show_rejected_prediction = matches!( + reject_reason, + EditPredictionRejectReason::Empty + | EditPredictionRejectReason::InterpolatedEmpty + ); + this.reject_prediction( - prediction_result.id, + id, reject_reason, false, - prediction_result.model_version, - Some(prediction_result.e2e_latency), + model_version, + Some(e2e_latency), cx, ); + + if should_show_rejected_prediction + && let Some(display_prediction) = display_prediction + { + this.shown_predictions.push_front(display_prediction); + if this.shown_predictions.len() > 50 + && let Some(completion) = this.shown_predictions.pop_back() + { + this.rated_predictions.remove(&completion.id); + } + } + None } } diff --git a/crates/edit_prediction/src/edit_prediction_tests.rs b/crates/edit_prediction/src/edit_prediction_tests.rs index 3648bdc8f4..93427c5f31 100644 --- a/crates/edit_prediction/src/edit_prediction_tests.rs +++ b/crates/edit_prediction/src/edit_prediction_tests.rs @@ -1471,7 +1471,7 @@ async fn test_empty_prediction(cx: &mut TestAppContext) { project.clone(), buffer.clone(), position, - EditPredictionRequestTrigger::Other, + EditPredictionRequestTrigger::Explicit, cx, ); }); @@ -1490,6 +1490,15 @@ async fn test_empty_prediction(cx: &mut TestAppContext) { .prediction_at(&buffer, None, &project, cx) .is_none() ); + let shown_predictions = ep_store.shown_predictions().collect::>(); + assert_eq!(shown_predictions.len(), 1); + assert_eq!(shown_predictions[0].id.to_string(), id); + assert!(shown_predictions[0].edits.is_empty()); + assert!(shown_predictions[0].editable_range.is_some()); + assert!(matches!( + shown_predictions[0].trigger, + PredictEditsRequestTrigger::Explicit + )); }); // prediction is reported as rejected @@ -1559,6 +1568,11 @@ async fn test_interpolated_empty(cx: &mut TestAppContext) { .prediction_at(&buffer, None, &project, cx) .is_none() ); + let shown_predictions = ep_store.shown_predictions().collect::>(); + assert_eq!(shown_predictions.len(), 1); + assert_eq!(shown_predictions[0].id.to_string(), id); + assert!(shown_predictions[0].edits.is_empty()); + assert!(shown_predictions[0].editable_range.is_some()); }); // prediction is reported as rejected @@ -1774,6 +1788,11 @@ async fn test_current_preferred(cx: &mut TestAppContext) { .0, first_id ); + let shown_prediction_ids = ep_store + .shown_predictions() + .map(|prediction| prediction.id.to_string()) + .collect::>(); + assert!(shown_prediction_ids.is_empty()); }); // second is reported as rejected @@ -2962,6 +2981,7 @@ async fn test_edit_prediction_basic_interpolation(cx: &mut TestAppContext) { repo_url: None, }, model_version: None, + trigger: PredictEditsRequestTrigger::Other, }; cx.update(|cx| { diff --git a/crates/edit_prediction/src/fim.rs b/crates/edit_prediction/src/fim.rs index d0c47bfbe1..a28480a114 100644 --- a/crates/edit_prediction/src/fim.rs +++ b/crates/edit_prediction/src/fim.rs @@ -29,6 +29,7 @@ pub fn request_prediction( snapshot, position, events, + trigger, .. }: EditPredictionModelInput, prompt_format: EditPredictionPromptFormat, @@ -155,6 +156,7 @@ pub fn request_prediction( Some(output.editable_range), output.inputs, None, + trigger, cx.background_executor().now() - request_start, cx, ) diff --git a/crates/edit_prediction/src/mercury.rs b/crates/edit_prediction/src/mercury.rs index ddbe899313..db41d896fe 100644 --- a/crates/edit_prediction/src/mercury.rs +++ b/crates/edit_prediction/src/mercury.rs @@ -50,6 +50,7 @@ impl Mercury { events, related_files, debug_tx, + trigger, .. }: EditPredictionModelInput, credentials_provider: Arc, @@ -255,6 +256,7 @@ impl Mercury { Some(editable_range), inputs, None, + trigger, cx.background_executor().now() - request_start, cx, ) diff --git a/crates/edit_prediction/src/prediction.rs b/crates/edit_prediction/src/prediction.rs index f9f7e548e7..c01c52e2bd 100644 --- a/crates/edit_prediction/src/prediction.rs +++ b/crates/edit_prediction/src/prediction.rs @@ -1,6 +1,6 @@ use std::{ops::Range, sync::Arc}; -use cloud_llm_client::EditPredictionRejectReason; +use cloud_llm_client::{EditPredictionRejectReason, PredictEditsRequestTrigger}; use edit_prediction_types::{PredictedCursorPosition, interpolate_edits}; use gpui::{AsyncApp, Entity, SharedString}; use language::{Anchor, Buffer, BufferSnapshot, EditPreview, TextBufferSnapshot}; @@ -25,6 +25,7 @@ impl std::fmt::Display for EditPredictionId { pub struct EditPredictionResult { pub id: EditPredictionId, pub prediction: Result, + pub display_prediction: Option, pub model_version: Option, pub e2e_latency: std::time::Duration, } @@ -39,36 +40,65 @@ impl EditPredictionResult { editable_range: Option>, inputs: ZetaPromptInput, model_version: Option, + trigger: PredictEditsRequestTrigger, e2e_latency: std::time::Duration, cx: &mut AsyncApp, ) -> Self { if edits.is_empty() { + let empty_edits = Arc::new([]); return Self { - id, + id: id.clone(), prediction: Err(EditPredictionRejectReason::Empty), + display_prediction: Some(EditPrediction { + id, + edits: empty_edits, + cursor_position: None, + editable_range, + snapshot: edited_buffer_snapshot.clone(), + edit_preview: EditPreview::unchanged(edited_buffer_snapshot), + buffer: edited_buffer.clone(), + inputs, + model_version: model_version.clone(), + trigger, + }), model_version, e2e_latency, }; } - let Some((edits, snapshot, edit_preview_task)) = - edited_buffer.read_with(cx, |buffer, cx| { - let new_snapshot = buffer.snapshot(); - let edits: Arc<[_]> = - interpolate_edits(&edited_buffer_snapshot, &new_snapshot, &edits)?.into(); + let (edits, snapshot) = edited_buffer.read_with(cx, |buffer, _cx| { + let new_snapshot = buffer.snapshot(); + let edits: Option, Arc)]>> = + interpolate_edits(&edited_buffer_snapshot, &new_snapshot, &edits).map(Arc::from); - Some((edits.clone(), new_snapshot, buffer.preview_edits(edits, cx))) - }) - else { + (edits, new_snapshot) + }); + + let Some(edits) = edits else { + let empty_edits: Arc<[(Range, Arc)]> = Vec::new().into(); return Self { - id, + id: id.clone(), prediction: Err(EditPredictionRejectReason::InterpolatedEmpty), + display_prediction: Some(EditPrediction { + id, + edits: empty_edits, + cursor_position: None, + editable_range, + snapshot: edited_buffer_snapshot.clone(), + edit_preview: EditPreview::unchanged(edited_buffer_snapshot), + inputs, + buffer: edited_buffer.clone(), + model_version: model_version.clone(), + trigger, + }), model_version, e2e_latency, }; }; - let edit_preview = edit_preview_task.await; + let edit_preview = edited_buffer + .read_with(cx, |buffer, cx| buffer.preview_edits(edits.clone(), cx)) + .await; Self { id: id.clone(), @@ -82,7 +112,9 @@ impl EditPredictionResult { inputs, buffer: edited_buffer.clone(), model_version: model_version.clone(), + trigger, }), + display_prediction: None, model_version, e2e_latency, } @@ -100,6 +132,7 @@ pub struct EditPrediction { pub buffer: Entity, pub inputs: zeta_prompt::ZetaPromptInput, pub model_version: Option, + pub trigger: PredictEditsRequestTrigger, } impl EditPrediction { @@ -153,6 +186,7 @@ mod tests { buffer: buffer.clone(), edit_preview, model_version: None, + trigger: PredictEditsRequestTrigger::Other, inputs: ZetaPromptInput { events: vec![], related_files: Some(vec![]), diff --git a/crates/edit_prediction/src/zeta.rs b/crates/edit_prediction/src/zeta.rs index 9b9217ebec..172726a1d5 100644 --- a/crates/edit_prediction/src/zeta.rs +++ b/crates/edit_prediction/src/zeta.rs @@ -27,7 +27,8 @@ use zeta_prompt::{ParsedOutput, ZetaPromptInput}; use std::{ops::Range, path::Path, sync::Arc}; use zeta_prompt::{ - ZetaFormat, format_zeta_prompt, get_prefill, parse_zeta2_model_output, stop_tokens_for_format, + ZetaFormat, excerpt_range_for_format, format_zeta_prompt, get_prefill, + parse_zeta2_model_output, stop_tokens_for_format, zeta1::{self, EDITABLE_REGION_END_MARKER}, }; @@ -309,7 +310,27 @@ pub fn request_prediction_with_zeta( cursor_offset_in_new_editable_region: cursor_offset_in_output, }) = output else { - return Ok((Some((request_id, None, model_version)), None)); + let editable_range_in_excerpt = + excerpt_range_for_format(zeta_format, &prompt_input.excerpt_ranges).0; + let editable_range_in_buffer = editable_range_in_excerpt.start + + full_context_offset_range.start + ..editable_range_in_excerpt.end + full_context_offset_range.start; + + return Ok(( + Some(( + request_id, + Some(Prediction { + prompt_input, + buffer, + snapshot: snapshot.clone(), + edits: Vec::new(), + cursor_position: None, + editable_range_in_buffer, + }), + model_version, + )), + usage, + )); }; let editable_range_in_buffer = editable_range_in_excerpt.start @@ -381,6 +402,7 @@ pub fn request_prediction_with_zeta( return Ok(Some(EditPredictionResult { id, prediction: Err(EditPredictionRejectReason::Empty), + display_prediction: None, model_version, e2e_latency: request_duration, })); @@ -395,6 +417,7 @@ pub fn request_prediction_with_zeta( Some(edited_buffer_snapshot.anchor_range_inside(editable_range_in_buffer.clone())), inputs, model_version, + trigger, request_duration, cx, ) diff --git a/crates/edit_prediction_ui/src/rate_prediction_modal.rs b/crates/edit_prediction_ui/src/rate_prediction_modal.rs index 8299f86054..3b9a80ea37 100644 --- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs +++ b/crates/edit_prediction_ui/src/rate_prediction_modal.rs @@ -1,4 +1,5 @@ use buffer_diff::BufferDiff; +use cloud_llm_client::PredictEditsRequestTrigger; use edit_prediction::{EditPrediction, EditPredictionRating, EditPredictionStore}; use editor::{Editor, Inlay, MultiBuffer}; use feature_flags::{FeatureFlag, PresenceFlag, register_feature_flag}; @@ -7,7 +8,7 @@ use gpui::{ Length, StyleRefinement, TextStyleRefinement, Window, actions, prelude::*, }; use language::{ - Anchor, Bias, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry, Point, ToOffset, ToPoint, + Bias, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry, Point, ToOffset, ToPoint, language_settings::{self, InlayHintKind}, }; use markdown::{Markdown, MarkdownStyle}; @@ -67,10 +68,8 @@ struct ActivePrediction { prediction: EditPrediction, feedback_editor: Entity, expected_buffer: Entity, - expected_editable_range: Option>, expected_editor: Entity, - expected_diff_editor: Entity, - expected_patch_preview: bool, + _expected_buffer_subscription: gpui::Subscription, formatted_inputs: Entity, } @@ -304,56 +303,36 @@ impl RatePredictionsModal { self.select_completion(completion, true, window, cx); } - fn update_diff_editor( - diff_editor: &Entity, - new_buffer: Entity, + fn update_buffer_diff( + diff: &Entity, + new_buffer_snapshot: BufferSnapshot, old_buffer_snapshot: BufferSnapshot, - visible_range: Range, - cx: &mut Context, + cx: &mut App, ) { - diff_editor.update(cx, |editor, cx| { - let new_buffer_snapshot = new_buffer.read(cx).snapshot(); - let new_buffer_id = new_buffer_snapshot.remote_id(); - let language = new_buffer_snapshot.language().cloned(); - let diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot.text, cx)); - diff.update(cx, |diff, cx| { - let update = diff.update_diff( - new_buffer_snapshot.text.clone(), - Some(old_buffer_snapshot.text().into()), - Some(true), - language, - cx, - ); - cx.spawn(async move |diff, cx| { - let update = update.await; - if let Some(task) = diff - .update(cx, |diff, cx| { - diff.set_snapshot(update, &new_buffer_snapshot.text, cx) - }) - .ok() - { - task.await; - } - }) - .detach(); - }); - - editor.disable_header_for_buffer(new_buffer_id, cx); - editor.buffer().update(cx, |multibuffer, cx| { - multibuffer.clear(cx); - multibuffer.set_excerpts_for_buffer(new_buffer, [visible_range], 0, cx); - multibuffer.add_diff(diff, cx); - }); + let language = new_buffer_snapshot.language().cloned(); + diff.update(cx, |diff, cx| { + let update = diff.update_diff( + new_buffer_snapshot.text.clone(), + Some(old_buffer_snapshot.text().into()), + Some(true), + language, + cx, + ); + cx.spawn(async move |diff, cx| { + let update = update.await; + if let Some(task) = diff + .update(cx, |diff, cx| { + diff.set_snapshot(update, &new_buffer_snapshot.text, cx) + }) + .ok() + { + task.await; + } + }) + .detach(); }); } - fn editable_range_for_prediction(prediction: &EditPrediction) -> Option> { - prediction - .editable_range - .clone() - .or_else(|| Some(prediction.edits.first()?.0.start..prediction.edits.last()?.0.end)) - } - fn insert_editable_region_markers( editor: &Entity, buffer: &Entity, @@ -471,23 +450,47 @@ impl RatePredictionsModal { return; } - let editable_range = Self::editable_range_for_prediction(&prediction); + let editable_range = prediction.editable_range.clone().or_else(|| { + Some(prediction.edits.first()?.0.start..prediction.edits.last()?.0.end) + }); let predicted_buffer = prediction.edit_preview.build_result_buffer(cx); let predicted_buffer_snapshot = predicted_buffer.read(cx).snapshot(); let visible_range = prediction .edit_preview .compute_visible_range(&prediction.edits) + .or_else(|| { + editable_range.as_ref().map(|range| { + range.start.to_point(&prediction.snapshot) + ..range.end.to_point(&prediction.snapshot) + }) + }) .unwrap_or(Point::zero()..Point::zero()); - let start = Point::new(visible_range.start.row.saturating_sub(5), 0); - let end = - Point::new(visible_range.end.row + 5, 0).min(predicted_buffer_snapshot.max_point()); - Self::update_diff_editor( - &self.diff_editor, - predicted_buffer.clone(), - prediction.snapshot.clone(), - start..end, - cx, - ); + let visible_range_with_context = + Point::new(visible_range.start.row.saturating_sub(5), 0) + ..Point::new(visible_range.end.row.saturating_add(5), 0) + .min(predicted_buffer_snapshot.max_point()); + self.diff_editor.update(cx, |editor, cx| { + let predicted_buffer_id = predicted_buffer_snapshot.remote_id(); + let diff = cx.new(|cx| BufferDiff::new(&predicted_buffer_snapshot.text, cx)); + Self::update_buffer_diff( + &diff, + predicted_buffer_snapshot.clone(), + prediction.snapshot.clone(), + cx, + ); + + editor.disable_header_for_buffer(predicted_buffer_id, cx); + editor.buffer().update(cx, |multibuffer, cx| { + multibuffer.clear(cx); + multibuffer.set_excerpts_for_buffer( + predicted_buffer.clone(), + [visible_range_with_context], + 0, + cx, + ); + multibuffer.add_diff(diff, cx); + }); + }); if let Some(editable_range) = editable_range.as_ref() { Self::insert_editable_region_markers( @@ -621,7 +624,14 @@ impl RatePredictionsModal { range.start.to_point(&expected_buffer_snapshot) ..range.end.to_point(&expected_buffer_snapshot) }) - .unwrap_or_else(|| visible_range.clone()); + .unwrap_or(visible_range); + let expected_diff = cx.new(|cx| BufferDiff::new(&expected_buffer_snapshot.text, cx)); + Self::update_buffer_diff( + &expected_diff, + expected_buffer_snapshot.clone(), + prediction.snapshot.clone(), + cx, + ); let expected_editor = cx.new(|cx| { let multibuffer = cx.new(|cx| { let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite); @@ -631,12 +641,14 @@ impl RatePredictionsModal { 0, cx, ); + multibuffer.add_diff(expected_diff.clone(), cx); multibuffer }); let mut editor = Editor::for_multibuffer(multibuffer, None, window, cx); let expected_buffer_id = expected_buffer.read(cx).remote_id(); editor.disable_header_for_buffer(expected_buffer_id, cx); editor.disable_inline_diagnostics(); + editor.set_expand_all_diff_hunks(cx); editor.set_show_git_diff_gutter(false, cx); editor.set_show_code_actions(false, cx); editor.set_show_runnables(false, cx); @@ -646,14 +658,6 @@ impl RatePredictionsModal { editor.set_show_edit_predictions(Some(false), window, cx); editor }); - let expected_diff_editor = cx.new(|cx| { - let multibuffer = cx.new(|_| MultiBuffer::new(language::Capability::ReadOnly)); - let mut editor = Editor::for_multibuffer(multibuffer, None, window, cx); - editor.disable_inline_diagnostics(); - editor.set_expand_all_diff_hunks(cx); - editor.set_show_git_diff_gutter(false, cx); - editor - }); if let Some(expected_editable_range) = expected_editable_range.as_ref() { let expected_buffer_snapshot = expected_buffer.read(cx).snapshot(); Self::insert_editable_region_markers( @@ -669,6 +673,24 @@ impl RatePredictionsModal { ); } + let expected_buffer_subscription = cx.subscribe(&expected_buffer, { + let expected_diff = expected_diff.clone(); + let original_snapshot = prediction.snapshot.clone(); + move |_this, buffer, event, cx| match event { + language::BufferEvent::Edited { .. } + | language::BufferEvent::LanguageChanged(_) + | language::BufferEvent::Reparsed => { + Self::update_buffer_diff( + &expected_diff, + buffer.read(cx).snapshot(), + original_snapshot.clone(), + cx, + ); + } + _ => {} + } + }); + self.active_prediction = Some(ActivePrediction { prediction, feedback_editor: cx.new(|cx| { @@ -692,10 +714,8 @@ impl RatePredictionsModal { editor }), expected_buffer, - expected_editable_range, expected_editor, - expected_diff_editor, - expected_patch_preview: false, + _expected_buffer_subscription: expected_buffer_subscription, formatted_inputs: cx.new(|cx| { Markdown::new( formatted_inputs.into(), @@ -746,57 +766,10 @@ impl RatePredictionsModal { ) } - fn toggle_expected_patch_preview(&mut self, cx: &mut Context) { - if let Some(active_prediction) = &mut self.active_prediction { - if active_prediction.expected_patch_preview { - active_prediction.expected_patch_preview = false; - } else { - let expected_buffer_snapshot = - active_prediction.expected_buffer.read(cx).snapshot(); - let visible_range = active_prediction - .prediction - .edit_preview - .compute_visible_range(&active_prediction.prediction.edits) - .unwrap_or(Point::zero()..Point::zero()); - let start = Point::new(visible_range.start.row.saturating_sub(5), 0); - let end = Point::new(visible_range.end.row + 5, 0) - .min(expected_buffer_snapshot.max_point()); - - Self::update_diff_editor( - &active_prediction.expected_diff_editor, - active_prediction.expected_buffer.clone(), - active_prediction.prediction.snapshot.clone(), - start..end, - cx, - ); - if let Some(expected_editable_range) = - active_prediction.expected_editable_range.as_ref() - { - let expected_buffer_snapshot = - active_prediction.expected_buffer.read(cx).snapshot(); - Self::insert_editable_region_markers( - &active_prediction.expected_diff_editor, - &active_prediction.expected_buffer, - expected_editable_range - .start - .to_offset(&expected_buffer_snapshot) - ..expected_editable_range - .end - .to_offset(&expected_buffer_snapshot), - cx, - ); - } - active_prediction.expected_patch_preview = true; - } - cx.notify(); - } - } - fn render_suggested_edits(&self, cx: &mut Context) -> Option> { let bg_color = cx.theme().colors().editor_background; let border_color = cx.theme().colors().border; let active_prediction = self.active_prediction.as_ref()?; - let expected_patch_preview = active_prediction.expected_patch_preview; Some( v_flex() @@ -840,22 +813,6 @@ impl RatePredictionsModal { .gap_2() .border_b_1() .border_color(border_color) - .child( - Button::new( - "expected-patch-preview", - if expected_patch_preview { - "Edit" - } else { - "Preview" - }, - ) - .label_size(LabelSize::Small) - .on_click(cx.listener( - |this, _, _window, cx| { - this.toggle_expected_patch_preview(cx); - }, - )), - ) .child(Label::new("Expected Patch").size(LabelSize::Small)), ) .child( @@ -866,14 +823,7 @@ impl RatePredictionsModal { .min_h_0() .overflow_scroll() .whitespace_nowrap() - .child(if expected_patch_preview { - active_prediction - .expected_diff_editor - .clone() - .into_any_element() - } else { - active_prediction.expected_editor.clone().into_any_element() - }), + .child(active_prediction.expected_editor.clone()), ), ), ) @@ -1186,6 +1136,28 @@ impl RatePredictionsModal { (false, true) => (IconName::File, Color::Muted, "No Edits Produced"), (false, false) => (IconName::FileDiff, Color::Accent, "Edits Available"), }; + let (trigger_icon, trigger_tooltip) = match completion.trigger { + PredictEditsRequestTrigger::Testing => (IconName::Debug, "Testing"), + PredictEditsRequestTrigger::Diagnostics => { + (IconName::ToolDiagnostics, "Diagnostics") + } + PredictEditsRequestTrigger::DiagnosticNavigation => { + (IconName::ArrowRight, "Diagnostic Navigation") + } + PredictEditsRequestTrigger::Cli => (IconName::Terminal, "CLI"), + PredictEditsRequestTrigger::Explicit => (IconName::Person, "Explicit"), + PredictEditsRequestTrigger::BufferEdit => (IconName::Pencil, "Buffer Edit"), + PredictEditsRequestTrigger::LSPCompletionAccepted => { + (IconName::Code, "LSP Completion Accepted") + } + PredictEditsRequestTrigger::PredictionAccepted => { + (IconName::ZedPredict, "Prediction Accepted") + } + PredictEditsRequestTrigger::PredictionPartiallyAccepted => { + (IconName::CheckDouble, "Prediction Partially Accepted") + } + PredictEditsRequestTrigger::Other => (IconName::CircleHelp, "Other"), + }; let file = completion.buffer.read(cx).file(); let file_name = file @@ -1205,6 +1177,11 @@ impl RatePredictionsModal { .id("completion-content") .gap_3() .child(Icon::new(icon_name).color(icon_color).size(IconSize::Small)) + .child( + Icon::new(trigger_icon) + .color(Color::Muted) + .size(IconSize::XSmall), + ) .child( v_flex().child( h_flex() @@ -1220,7 +1197,9 @@ impl RatePredictionsModal { ), ), ) - .tooltip(Tooltip::text(tooltip_text)) + .tooltip(Tooltip::text(format!( + "{tooltip_text} • Trigger: {trigger_tooltip}" + ))) .on_click(cx.listener(move |this, _, window, cx| { this.select_completion(Some(completion.clone()), true, window, cx); })) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 633436f4bd..5551a4b5ea 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -756,6 +756,14 @@ pub struct EditPreview { } impl EditPreview { + pub fn unchanged(snapshot: &BufferSnapshot) -> Self { + Self { + old_snapshot: snapshot.text.clone(), + applied_edits_snapshot: snapshot.text.clone(), + syntax_snapshot: snapshot.syntax.clone(), + } + } + pub fn as_unified_diff( &self, file: Option<&Arc>,