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 ...
This commit is contained in:
1 parent
53367edf64
commit
1575dbe27c
8 files changed
+263
-166
No files matched your search
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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| {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ impl Mercury {
|
||||
events,
|
||||
related_files,
|
||||
debug_tx,
|
||||
trigger,
|
||||
..
|
||||
}: EditPredictionModelInput,
|
||||
credentials_provider: Arc<dyn CredentialsProvider>,
|
||||
@@ -255,6 +256,7 @@ impl Mercury {
|
||||
Some(editable_range),
|
||||
inputs,
|
||||
None,
|
||||
trigger,
|
||||
cx.background_executor().now() - request_start,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -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<EditPrediction, EditPredictionRejectReason>,
|
||||
pub display_prediction: Option<EditPrediction>,
|
||||
pub model_version: Option<String>,
|
||||
pub e2e_latency: std::time::Duration,
|
||||
}
|
||||
@@ -39,36 +40,65 @@ impl EditPredictionResult {
|
||||
editable_range: Option<Range<Anchor>>,
|
||||
inputs: ZetaPromptInput,
|
||||
model_version: Option<String>,
|
||||
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<[(Range<Anchor>, Arc<str>)]>> =
|
||||
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<Anchor>, Arc<str>)]> = 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<Buffer>,
|
||||
pub inputs: zeta_prompt::ZetaPromptInput,
|
||||
pub model_version: Option<String>,
|
||||
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![]),
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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<Editor>,
|
||||
expected_buffer: Entity<Buffer>,
|
||||
expected_editable_range: Option<Range<Anchor>>,
|
||||
expected_editor: Entity<Editor>,
|
||||
expected_diff_editor: Entity<Editor>,
|
||||
expected_patch_preview: bool,
|
||||
_expected_buffer_subscription: gpui::Subscription,
|
||||
formatted_inputs: Entity<Markdown>,
|
||||
}
|
||||
|
||||
@@ -304,56 +303,36 @@ impl RatePredictionsModal {
|
||||
self.select_completion(completion, true, window, cx);
|
||||
}
|
||||
|
||||
fn update_diff_editor(
|
||||
diff_editor: &Entity<Editor>,
|
||||
new_buffer: Entity<Buffer>,
|
||||
fn update_buffer_diff(
|
||||
diff: &Entity<BufferDiff>,
|
||||
new_buffer_snapshot: BufferSnapshot,
|
||||
old_buffer_snapshot: BufferSnapshot,
|
||||
visible_range: Range<Point>,
|
||||
cx: &mut Context<Self>,
|
||||
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<Range<Anchor>> {
|
||||
prediction
|
||||
.editable_range
|
||||
.clone()
|
||||
.or_else(|| Some(prediction.edits.first()?.0.start..prediction.edits.last()?.0.end))
|
||||
}
|
||||
|
||||
fn insert_editable_region_markers(
|
||||
editor: &Entity<Editor>,
|
||||
buffer: &Entity<Buffer>,
|
||||
@@ -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<Self>) {
|
||||
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<Self>) -> Option<gpui::Stateful<Div>> {
|
||||
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);
|
||||
}))
|
||||
|
||||
@@ -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<dyn File>>,
|
||||
|
||||
Reference in new issue
Block a user