copilot: Fix panic in Copilot edit prediction due to anchor/snapshot mismatch (#46022)
When `trim_completion()` creates new anchors from the current buffer state, `completion.snapshot` was not being updated, leaving it with the older snapshot from when the prediction was initially fetched. This caused a panic in `interpolate_edits()` when trying to resolve anchors with Lamport timestamps newer than what the old snapshot had observed. The fix ensures that `completion.snapshot` is updated whenever new anchors are created in `trim_completion()`, keeping the snapshot and anchors consistent. Closes #45956 Release Notes: - Fixed a panic in Copilot edit predictions caused by anchor/snapshot version mismatch
This commit is contained in:
1 parent
3566d73669
commit
e7353c848b
1 file changed
+14
-9
@@ -2,7 +2,7 @@ use crate::{Copilot, CopilotEditPrediction};
|
||||
use anyhow::Result;
|
||||
use edit_prediction_types::{EditPrediction, EditPredictionDelegate, interpolate_edits};
|
||||
use gpui::{App, Context, Entity, Task};
|
||||
use language::{Anchor, Buffer, EditPreview, OffsetRangeExt};
|
||||
use language::{Anchor, Buffer, BufferSnapshot, EditPreview, OffsetRangeExt};
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
|
||||
pub const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
|
||||
@@ -79,20 +79,24 @@ impl EditPredictionDelegate for CopilotEditPredictionDelegate {
|
||||
.await?;
|
||||
|
||||
if let Some(mut completion) = completions.into_iter().next()
|
||||
&& let Some(trimmed_completion) = cx
|
||||
&& let Some((trimmed_range, trimmed_text, snapshot)) = cx
|
||||
.update(|cx| trim_completion(&completion, cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
{
|
||||
let preview = buffer
|
||||
.update(cx, |this, cx| {
|
||||
this.preview_edits(Arc::from(std::slice::from_ref(&trimmed_completion)), cx)
|
||||
this.preview_edits(
|
||||
Arc::from([(trimmed_range.clone(), trimmed_text.clone())].as_slice()),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await;
|
||||
this.update(cx, |this, cx| {
|
||||
this.pending_refresh = None;
|
||||
completion.range = trimmed_completion.0;
|
||||
completion.text = trimmed_completion.1.to_string();
|
||||
completion.range = trimmed_range;
|
||||
completion.text = trimmed_text.to_string();
|
||||
completion.snapshot = snapshot;
|
||||
this.completion = Some((completion, preview));
|
||||
|
||||
cx.notify();
|
||||
@@ -147,7 +151,7 @@ impl EditPredictionDelegate for CopilotEditPredictionDelegate {
|
||||
fn trim_completion(
|
||||
completion: &CopilotEditPrediction,
|
||||
cx: &mut App,
|
||||
) -> Option<(Range<Anchor>, Arc<str>)> {
|
||||
) -> Option<(Range<Anchor>, Arc<str>, BufferSnapshot)> {
|
||||
let buffer = completion.buffer.read(cx);
|
||||
let mut completion_range = completion.range.to_offset(buffer);
|
||||
let prefix_len = common_prefix(
|
||||
@@ -164,10 +168,11 @@ fn trim_completion(
|
||||
if completion_text.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
let completion_range =
|
||||
buffer.anchor_after(completion_range.start)..buffer.anchor_after(completion_range.end);
|
||||
let snapshot = buffer.snapshot();
|
||||
let completion_range = snapshot.anchor_after(completion_range.start)
|
||||
..snapshot.anchor_after(completion_range.end);
|
||||
|
||||
Some((completion_range, Arc::from(completion_text)))
|
||||
Some((completion_range, Arc::from(completion_text), snapshot))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user