Clean up completion tasks, even if they fail or return no results

This fixes a bug where leaving the completion task in `completion_tasks`
could cause the Copilot suggestion to not be shown due to the LSP not
successfully return a completion.
This commit is contained in:
Antonio Scandurra 2023-04-18 10:34:18 +02:00
parent 75d6b6360f
commit d26d0ac56f

View file

@ -20,7 +20,7 @@ mod editor_tests;
pub mod test; pub mod test;
use aho_corasick::AhoCorasick; use aho_corasick::AhoCorasick;
use anyhow::Result; use anyhow::{anyhow, Result};
use blink_manager::BlinkManager; use blink_manager::BlinkManager;
use clock::ReplicaId; use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
@ -2352,53 +2352,66 @@ impl Editor {
let id = post_inc(&mut self.next_completion_id); let id = post_inc(&mut self.next_completion_id);
let task = cx.spawn_weak(|this, mut cx| { let task = cx.spawn_weak(|this, mut cx| {
async move { async move {
let completions = completions.await?; let menu = if let Some(completions) = completions.await.log_err() {
if completions.is_empty() { let mut menu = CompletionsMenu {
return Ok(()); id,
} initial_position: position,
match_candidates: completions
let mut menu = CompletionsMenu { .iter()
id, .enumerate()
initial_position: position, .map(|(id, completion)| {
match_candidates: completions StringMatchCandidate::new(
.iter() id,
.enumerate() completion.label.text[completion.label.filter_range.clone()]
.map(|(id, completion)| { .into(),
StringMatchCandidate::new( )
id, })
completion.label.text[completion.label.filter_range.clone()].into(), .collect(),
) buffer,
}) completions: completions.into(),
.collect(), matches: Vec::new().into(),
buffer, selected_item: 0,
completions: completions.into(), list: Default::default(),
matches: Vec::new().into(), };
selected_item: 0, menu.filter(query.as_deref(), cx.background()).await;
list: Default::default(), if menu.matches.is_empty() {
None
} else {
Some(menu)
}
} else {
None
}; };
menu.filter(query.as_deref(), cx.background()).await; let this = this
.upgrade(&cx)
.ok_or_else(|| anyhow!("editor was dropped"))?;
this.update(&mut cx, |this, cx| {
this.completion_tasks.retain(|(task_id, _)| *task_id > id);
if let Some(this) = this.upgrade(&cx) { match this.context_menu.as_ref() {
this.update(&mut cx, |this, cx| { None => {}
match this.context_menu.as_ref() { Some(ContextMenu::Completions(prev_menu)) => {
None => {} if prev_menu.id > id {
Some(ContextMenu::Completions(prev_menu)) => { return;
if prev_menu.id > menu.id {
return;
}
} }
_ => return,
} }
_ => return,
}
this.completion_tasks.retain(|(id, _)| *id > menu.id); if this.focused && menu.is_some() {
if this.focused && !menu.matches.is_empty() { let menu = menu.unwrap();
this.show_context_menu(ContextMenu::Completions(menu), cx); this.show_context_menu(ContextMenu::Completions(menu), cx);
} else if this.hide_context_menu(cx).is_none() { } else if this.completion_tasks.is_empty() {
// 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 suggestion when available.
if this.hide_context_menu(cx).is_none() {
this.update_visible_copilot_suggestion(cx); this.update_visible_copilot_suggestion(cx);
} }
}); }
} });
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
} }
.log_err() .log_err()