mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
supermaven: Improve completion caching and position validation (#37047)
Closes #36981 - Add completion text and position caching to reduce redundant API calls - Only trigger new completion requests on text changes, not cursor movement - Validate cursor position to ensure completions show at correct location - Improve end-of-line range calculation for more accurate deletions - Extract reset_completion_cache helper for cleaner code organization - Update completion diff algorithm documentation for clarity Edit: Sorry this is the 2nd PR, I forgot that the forks history was messy; I cherrypicked and cleaned it properly with this PR Release Notes: - supermaven: Improved caching of predictions - supermaven: Fixed an issue where changing cursor position would incorrectly trigger new completions
This commit is contained in:
parent
a790e514af
commit
fcc3d1092f
1 changed files with 77 additions and 16 deletions
|
|
@ -19,8 +19,10 @@ pub struct SupermavenCompletionProvider {
|
|||
supermaven: Entity<Supermaven>,
|
||||
buffer_id: Option<EntityId>,
|
||||
completion_id: Option<SupermavenCompletionStateId>,
|
||||
completion_text: Option<String>,
|
||||
file_extension: Option<String>,
|
||||
pending_refresh: Option<Task<Result<()>>>,
|
||||
completion_position: Option<language::Anchor>,
|
||||
}
|
||||
|
||||
impl SupermavenCompletionProvider {
|
||||
|
|
@ -29,16 +31,19 @@ impl SupermavenCompletionProvider {
|
|||
supermaven,
|
||||
buffer_id: None,
|
||||
completion_id: None,
|
||||
completion_text: None,
|
||||
file_extension: None,
|
||||
pending_refresh: None,
|
||||
completion_position: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the edit prediction from the difference between the completion text.
|
||||
// this is defined by greedily matching the buffer text against the completion text, with any leftover buffer placed at the end.
|
||||
// for example, given the completion text "moo cows are cool" and the buffer text "cowsre pool", the completion state would be
|
||||
// the inlays "moo ", " a", and "cool" which will render as "[moo ]cows[ a]re [cool]pool" in the editor.
|
||||
// This is defined by greedily matching the buffer text against the completion text.
|
||||
// Inlays are inserted for parts of the completion text that are not present in the buffer text.
|
||||
// For example, given the completion text "axbyc" and the buffer text "xy", the rendered output in the editor would be "[a]x[b]y[c]".
|
||||
// The parts in brackets are the inlays.
|
||||
fn completion_from_diff(
|
||||
snapshot: BufferSnapshot,
|
||||
completion_text: &str,
|
||||
|
|
@ -133,6 +138,14 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
debounce: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
// Only make new completion requests when debounce is true (i.e., when text is typed)
|
||||
// When debounce is false (i.e., cursor movement), we should not make new requests
|
||||
if !debounce {
|
||||
return;
|
||||
}
|
||||
|
||||
reset_completion_cache(self, cx);
|
||||
|
||||
let Some(mut completion) = self.supermaven.update(cx, |supermaven, cx| {
|
||||
supermaven.complete(&buffer_handle, cursor_position, cx)
|
||||
}) else {
|
||||
|
|
@ -146,6 +159,17 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
|
||||
while let Some(()) = completion.updates.next().await {
|
||||
this.update(cx, |this, cx| {
|
||||
// Get the completion text and cache it
|
||||
if let Some(text) =
|
||||
this.supermaven
|
||||
.read(cx)
|
||||
.completion(&buffer_handle, cursor_position, cx)
|
||||
{
|
||||
this.completion_text = Some(text.to_string());
|
||||
|
||||
this.completion_position = Some(cursor_position);
|
||||
}
|
||||
|
||||
this.completion_id = Some(completion.id);
|
||||
this.buffer_id = Some(buffer_handle.entity_id());
|
||||
this.file_extension = buffer_handle.read(cx).file().and_then(|file| {
|
||||
|
|
@ -156,7 +180,6 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
.to_string(),
|
||||
)
|
||||
});
|
||||
this.pending_refresh = None;
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
|
|
@ -174,13 +197,11 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
}
|
||||
|
||||
fn accept(&mut self, _cx: &mut Context<Self>) {
|
||||
self.pending_refresh = None;
|
||||
self.completion_id = None;
|
||||
reset_completion_cache(self, _cx);
|
||||
}
|
||||
|
||||
fn discard(&mut self, _cx: &mut Context<Self>) {
|
||||
self.pending_refresh = None;
|
||||
self.completion_id = None;
|
||||
reset_completion_cache(self, _cx);
|
||||
}
|
||||
|
||||
fn suggest(
|
||||
|
|
@ -189,10 +210,34 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
cursor_position: Anchor,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<EditPrediction> {
|
||||
let completion_text = self
|
||||
.supermaven
|
||||
.read(cx)
|
||||
.completion(buffer, cursor_position, cx)?;
|
||||
if self.buffer_id != Some(buffer.entity_id()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.completion_id.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let completion_text = if let Some(cached_text) = &self.completion_text {
|
||||
cached_text.as_str()
|
||||
} else {
|
||||
let text = self
|
||||
.supermaven
|
||||
.read(cx)
|
||||
.completion(buffer, cursor_position, cx)?;
|
||||
self.completion_text = Some(text.to_string());
|
||||
text
|
||||
};
|
||||
|
||||
// Check if the cursor is still at the same position as the completion request
|
||||
// If we don't have a completion position stored, don't show the completion
|
||||
if let Some(completion_position) = self.completion_position {
|
||||
if cursor_position != completion_position {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
|
||||
let completion_text = trim_to_end_of_line_unless_leading_newline(completion_text);
|
||||
|
||||
|
|
@ -200,15 +245,20 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
|
||||
if !completion_text.trim().is_empty() {
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let mut point = cursor_position.to_point(&snapshot);
|
||||
point.column = snapshot.line_len(point.row);
|
||||
let range = cursor_position..snapshot.anchor_after(point);
|
||||
|
||||
// Calculate the range from cursor to end of line correctly
|
||||
let cursor_point = cursor_position.to_point(&snapshot);
|
||||
let end_of_line = snapshot.anchor_after(language::Point::new(
|
||||
cursor_point.row,
|
||||
snapshot.line_len(cursor_point.row),
|
||||
));
|
||||
let delete_range = cursor_position..end_of_line;
|
||||
|
||||
Some(completion_from_diff(
|
||||
snapshot,
|
||||
completion_text,
|
||||
cursor_position,
|
||||
range,
|
||||
delete_range,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -216,6 +266,17 @@ impl EditPredictionProvider for SupermavenCompletionProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn reset_completion_cache(
|
||||
provider: &mut SupermavenCompletionProvider,
|
||||
_cx: &mut Context<SupermavenCompletionProvider>,
|
||||
) {
|
||||
provider.pending_refresh = None;
|
||||
provider.completion_id = None;
|
||||
provider.completion_text = None;
|
||||
provider.completion_position = None;
|
||||
provider.buffer_id = None;
|
||||
}
|
||||
|
||||
fn trim_to_end_of_line_unless_leading_newline(text: &str) -> &str {
|
||||
if has_leading_newline(text) {
|
||||
text
|
||||
|
|
|
|||
Loading…
Reference in a new issue