Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster completion support for remotes #17993

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 87 additions & 22 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,10 @@ pub struct Editor {
nav_history: Option<ItemNavHistory>,
context_menu: RwLock<Option<ContextMenu>>,
mouse_context_menu: Option<MouseContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
signature_help_state: SignatureHelpState,
auto_signature_help: Option<bool>,
find_all_references_task_sources: Vec<Anchor>,
completion_tasks: Option<CompletionTasks>,
next_completion_id: CompletionId,
completion_documentation_pre_resolve_debounce: DebouncedDelay,
available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
Expand Down Expand Up @@ -853,6 +853,14 @@ enum ContextMenu {
CodeActions(CodeActionsMenu),
}

#[derive(Debug)]
struct CompletionTasks {
id: CompletionId,
task: Task<()>,
backup_id: Option<CompletionId>,
backup_task: Option<Task<()>>,
}

impl ContextMenu {
fn select_first(
&mut self,
Expand Down Expand Up @@ -1891,7 +1899,7 @@ impl Editor {
nav_history: None,
context_menu: RwLock::new(None),
mouse_context_menu: None,
completion_tasks: Default::default(),
completion_tasks: None,
signature_help_state: SignatureHelpState::default(),
auto_signature_help: None,
find_all_references_task_sources: Vec::new(),
Expand Down Expand Up @@ -2471,7 +2479,7 @@ impl Editor {
let mut completion_menu = completion_menu.clone();
drop(context_menu);

let query = Self::completion_query(buffer, cursor_position);
let (_, query) = Self::completion_query(buffer, cursor_position);
cx.spawn(move |this, mut cx| async move {
completion_menu
.filter(query.as_deref(), cx.background_executor().clone())
Expand Down Expand Up @@ -3934,18 +3942,22 @@ impl Editor {
});
}

fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
fn completion_query(
buffer: &MultiBufferSnapshot,
position: impl ToOffset,
) -> (multi_buffer::Anchor, Option<String>) {
let offset = position.to_offset(buffer);
let (word_range, kind) = buffer.surrounding_word(offset, true);
if offset > word_range.start && kind == Some(CharKind::Word) {
let query = if offset > word_range.start && kind == Some(CharKind::Word) {
Some(
buffer
.text_for_range(word_range.start..offset)
.collect::<String>(),
)
} else {
None
}
};
(buffer.anchor_before(word_range.start), query)
}

pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
Expand Down Expand Up @@ -4192,7 +4204,8 @@ impl Editor {
return;
};

let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
let (initial_position, query) =
Self::completion_query(&self.buffer.read(cx).read(cx), position);
let is_followup_invoke = {
let context_menu_state = self.context_menu.read();
matches!(
Expand All @@ -4218,19 +4231,26 @@ impl Editor {
}),
trigger_kind,
};
let snapshot = buffer.read(cx).snapshot();
let classifier = snapshot.char_classifier_at(&buffer_position);
let first_char = options
.trigger
.as_ref()
.and_then(|trigger| trigger.chars().next());
let should_cancel_previous = first_char.is_some_and(|char| !classifier.is_word(char));
let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
let sort_completions = provider.sort_completions();

let id = post_inc(&mut self.next_completion_id);
let completion_id = post_inc(&mut self.next_completion_id);
if should_cancel_previous {
self.completion_tasks = None;
}
let task = cx.spawn(|this, mut cx| {
async move {
this.update(&mut cx, |this, _| {
this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
})?;
let run = async move {
let completions = completions.await.log_err();
let menu = if let Some(completions) = completions {
let mut menu = CompletionsMenu {
id,
id: completion_id,
sort_completions,
initial_position: position,
match_candidates: completions
Expand All @@ -4253,10 +4273,25 @@ impl Editor {
DebouncedDelay::new(),
)),
};
let (cursor_moved, query) = this.update(&mut cx, |this, cx| {
let position = this.selections.newest_anchor().head();
let (new_position, new_query) =
Self::completion_query(&this.buffer.read(cx).read(cx), position);
if initial_position != new_position
&& this
.completion_tasks
.as_ref()
.is_some_and(|tasks| tasks.backup_id == Some(completion_id))
{
return (true, None);
}
(false, new_query)
})?;

menu.filter(query.as_deref(), cx.background_executor().clone())
.await;

if menu.matches.is_empty() {
if menu.matches.is_empty() || cursor_moved {
None
} else {
this.update(&mut cx, |editor, cx| {
Expand Down Expand Up @@ -4291,21 +4326,36 @@ impl Editor {
None => {}

Some(ContextMenu::Completions(prev_menu)) => {
if prev_menu.id > id {
if prev_menu.id > completion_id {
return;
}
}

_ => return,
}

let is_latest = this
.completion_tasks
.as_ref()
.is_some_and(|tasks| tasks.id == completion_id);
if let Some(tasks) = this.completion_tasks.take() {
if Some(completion_id) == tasks.backup_id {
this.completion_tasks = Some(CompletionTasks {
id: tasks.id,
task: tasks.task,
backup_id: None,
backup_task: None,
});
}
}

if this.focus_handle.is_focused(cx) && menu.is_some() {
let menu = menu.unwrap();
*context_menu = Some(ContextMenu::Completions(menu));
drop(context_menu);
this.discard_inline_completion(false, cx);
cx.notify();
} else if this.completion_tasks.len() <= 1 {
} else if is_latest {
// If there are no more completion tasks and the last menu was
// empty, we should hide it. If it was already hidden, we should
// also show the copilot completion when available.
Expand All @@ -4318,10 +4368,25 @@ impl Editor {

Ok::<_, anyhow::Error>(())
}
.log_err()
.log_err();
async move {
run.await;
}
});
let (backup_id, backup_task) = if let Some(tasks) = self.completion_tasks.take() {
(
Some(tasks.backup_id.unwrap_or(tasks.id)),
Some(tasks.backup_task.unwrap_or(tasks.task)),
)
} else {
(None, None)
};
self.completion_tasks = Some(CompletionTasks {
id: completion_id,
task,
backup_id,
backup_task,
});

self.completion_tasks.push((id, task));
}

pub fn confirm_completion(
Expand Down Expand Up @@ -4583,7 +4648,7 @@ impl Editor {
return None;
}

editor.completion_tasks.clear();
editor.completion_tasks = None;
editor.discard_inline_completion(false, cx);
let task_context =
tasks
Expand Down Expand Up @@ -5191,7 +5256,7 @@ impl Editor {
let excerpt_id = cursor.excerpt_id;

if self.context_menu.read().is_none()
&& self.completion_tasks.is_empty()
&& matches!(self.completion_tasks, None)
&& selection.start == selection.end
{
if let Some(provider) = self.inline_completion_provider() {
Expand Down Expand Up @@ -5363,7 +5428,7 @@ impl Editor {

fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
cx.notify();
self.completion_tasks.clear();
self.completion_tasks = None;
let context_menu = self.context_menu.write().take();
if context_menu.is_some() {
self.update_visible_inline_completion(cx);
Expand Down
Loading