Introduce staff-only inline completion provider (#21739)

Release Notes:

- N/A

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-12-09 14:26:36 +01:00 committed by GitHub
parent 39e8944dcc
commit 77b8296fbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 2890 additions and 356 deletions

View file

@ -1,14 +1,12 @@
use crate::{Supermaven, SupermavenCompletionStateId};
use anyhow::Result;
use client::telemetry::Telemetry;
use futures::StreamExt as _;
use gpui::{AppContext, EntityId, Model, ModelContext, Task};
use inline_completion::{CompletionProposal, Direction, InlayProposal, InlineCompletionProvider};
use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
use language::{language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot};
use std::{
ops::{AddAssign, Range},
path::Path,
sync::Arc,
time::Duration,
};
use text::{ToOffset, ToPoint};
@ -22,7 +20,6 @@ pub struct SupermavenCompletionProvider {
completion_id: Option<SupermavenCompletionStateId>,
file_extension: Option<String>,
pending_refresh: Task<Result<()>>,
telemetry: Option<Arc<Telemetry>>,
}
impl SupermavenCompletionProvider {
@ -33,31 +30,25 @@ impl SupermavenCompletionProvider {
completion_id: None,
file_extension: None,
pending_refresh: Task::ready(Ok(())),
telemetry: None,
}
}
pub fn with_telemetry(mut self, telemetry: Arc<Telemetry>) -> Self {
self.telemetry = Some(telemetry);
self
}
}
// Computes the completion state from the difference between the completion text.
// Computes the inline completion 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.
fn completion_state_from_diff(
fn completion_from_diff(
snapshot: BufferSnapshot,
completion_text: &str,
position: Anchor,
delete_range: Range<Anchor>,
) -> CompletionProposal {
) -> InlineCompletion {
let buffer_text = snapshot
.text_for_range(delete_range.clone())
.collect::<String>();
let mut inlays: Vec<InlayProposal> = Vec::new();
let mut edits: Vec<(Range<language::Anchor>, String)> = Vec::new();
let completion_graphemes: Vec<&str> = completion_text.graphemes(true).collect();
let buffer_graphemes: Vec<&str> = buffer_text.graphemes(true).collect();
@ -74,11 +65,10 @@ fn completion_state_from_diff(
match k {
Some(k) => {
if k != 0 {
let offset = snapshot.anchor_after(offset);
// the range from the current position to item is an inlay.
inlays.push(InlayProposal::Suggestion(
snapshot.anchor_after(offset),
completion_graphemes[i..i + k].join("").into(),
));
let edit = (offset..offset, completion_graphemes[i..i + k].join(""));
edits.push(edit);
}
i += k + 1;
j += 1;
@ -93,18 +83,14 @@ fn completion_state_from_diff(
}
if j == buffer_graphemes.len() && i < completion_graphemes.len() {
let offset = snapshot.anchor_after(offset);
// there is leftover completion text, so drop it as an inlay.
inlays.push(InlayProposal::Suggestion(
snapshot.anchor_after(offset),
completion_graphemes[i..].join("").into(),
));
let edit_range = offset..offset;
let edit_text = completion_graphemes[i..].join("");
edits.push((edit_range, edit_text));
}
CompletionProposal {
inlays,
text: completion_text.into(),
delete_range: Some(delete_range),
}
InlineCompletion { edits }
}
impl InlineCompletionProvider for SupermavenCompletionProvider {
@ -171,44 +157,21 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
}
fn accept(&mut self, _cx: &mut ModelContext<Self>) {
if self.completion_id.is_some() {
if let Some(telemetry) = self.telemetry.as_ref() {
telemetry.report_inline_completion_event(
Self::name().to_string(),
true,
self.file_extension.clone(),
);
}
}
self.pending_refresh = Task::ready(Ok(()));
self.completion_id = None;
}
fn discard(
fn discard(&mut self, _cx: &mut ModelContext<Self>) {
self.pending_refresh = Task::ready(Ok(()));
self.completion_id = None;
}
fn suggest(
&mut self,
should_report_inline_completion_event: bool,
_cx: &mut ModelContext<Self>,
) {
if should_report_inline_completion_event && self.completion_id.is_some() {
if let Some(telemetry) = self.telemetry.as_ref() {
telemetry.report_inline_completion_event(
Self::name().to_string(),
false,
self.file_extension.clone(),
);
}
}
self.pending_refresh = Task::ready(Ok(()));
self.completion_id = None;
}
fn active_completion_text<'a>(
&'a self,
buffer: &Model<Buffer>,
cursor_position: Anchor,
cx: &'a AppContext,
) -> Option<CompletionProposal> {
cx: &mut ModelContext<Self>,
) -> Option<InlineCompletion> {
let completion_text = self
.supermaven
.read(cx)
@ -223,7 +186,7 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
let mut point = cursor_position.to_point(&snapshot);
point.column = snapshot.line_len(point.row);
let range = cursor_position..snapshot.anchor_after(point);
Some(completion_state_from_diff(
Some(completion_from_diff(
snapshot,
completion_text,
cursor_position,