Do less resolves when showing the completion menu (#21286)
Closes https://github.com/zed-industries/zed/issues/21205 Zed does completion resolve on every menu item selection and when applying the edit, so resolving all completion menu list is excessive indeed. In addition to that, removes the documentation-centric approach of menu resolves, as we're actually resolving these for more than that, e.g. additionalTextEdits and have to do that always, even if we do not show the documentation. Potentially, we can omit the second resolve too, but that seems relatively dangerous, and many servers remove the `data` after the first resolve, so a 2nd one is not that harmful given that we used to do much more Release Notes: - Reduced the amount of `completionItem/resolve` calls done in the completion menu
This commit is contained in:
parent
6cba467a4e
commit
f30944543e
2 changed files with 130 additions and 173 deletions
|
@ -596,7 +596,6 @@ pub struct Editor {
|
|||
auto_signature_help: Option<bool>,
|
||||
find_all_references_task_sources: Vec<Anchor>,
|
||||
next_completion_id: CompletionId,
|
||||
completion_documentation_pre_resolve_debounce: DebouncedDelay,
|
||||
available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>,
|
||||
code_actions_task: Option<Task<Result<()>>>,
|
||||
document_highlights_task: Option<Task<()>>,
|
||||
|
@ -1006,7 +1005,7 @@ struct CompletionsMenu {
|
|||
matches: Arc<[StringMatch]>,
|
||||
selected_item: usize,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
selected_completion_documentation_resolve_debounce: Option<Arc<Mutex<DebouncedDelay>>>,
|
||||
selected_completion_resolve_debounce: Option<Arc<Mutex<DebouncedDelay>>>,
|
||||
}
|
||||
|
||||
impl CompletionsMenu {
|
||||
|
@ -1038,9 +1037,7 @@ impl CompletionsMenu {
|
|||
matches: Vec::new().into(),
|
||||
selected_item: 0,
|
||||
scroll_handle: UniformListScrollHandle::new(),
|
||||
selected_completion_documentation_resolve_debounce: Some(Arc::new(Mutex::new(
|
||||
DebouncedDelay::new(),
|
||||
))),
|
||||
selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1093,15 +1090,12 @@ impl CompletionsMenu {
|
|||
matches,
|
||||
selected_item: 0,
|
||||
scroll_handle: UniformListScrollHandle::new(),
|
||||
selected_completion_documentation_resolve_debounce: Some(Arc::new(Mutex::new(
|
||||
DebouncedDelay::new(),
|
||||
))),
|
||||
selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn suppress_documentation_resolution(mut self) -> Self {
|
||||
self.selected_completion_documentation_resolve_debounce
|
||||
.take();
|
||||
self.selected_completion_resolve_debounce.take();
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -1113,7 +1107,7 @@ impl CompletionsMenu {
|
|||
self.selected_item = 0;
|
||||
self.scroll_handle
|
||||
.scroll_to_item(self.selected_item, ScrollStrategy::Top);
|
||||
self.attempt_resolve_selected_completion_documentation(provider, cx);
|
||||
self.resolve_selected_completion(provider, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1129,7 +1123,7 @@ impl CompletionsMenu {
|
|||
}
|
||||
self.scroll_handle
|
||||
.scroll_to_item(self.selected_item, ScrollStrategy::Top);
|
||||
self.attempt_resolve_selected_completion_documentation(provider, cx);
|
||||
self.resolve_selected_completion(provider, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1139,7 @@ impl CompletionsMenu {
|
|||
}
|
||||
self.scroll_handle
|
||||
.scroll_to_item(self.selected_item, ScrollStrategy::Top);
|
||||
self.attempt_resolve_selected_completion_documentation(provider, cx);
|
||||
self.resolve_selected_completion(provider, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1157,58 +1151,20 @@ impl CompletionsMenu {
|
|||
self.selected_item = self.matches.len() - 1;
|
||||
self.scroll_handle
|
||||
.scroll_to_item(self.selected_item, ScrollStrategy::Top);
|
||||
self.attempt_resolve_selected_completion_documentation(provider, cx);
|
||||
self.resolve_selected_completion(provider, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn pre_resolve_completion_documentation(
|
||||
buffer: Model<Buffer>,
|
||||
completions: Arc<RwLock<Box<[Completion]>>>,
|
||||
matches: Arc<[StringMatch]>,
|
||||
editor: &Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Task<()> {
|
||||
let settings = EditorSettings::get_global(cx);
|
||||
if !settings.show_completion_documentation {
|
||||
return Task::ready(());
|
||||
}
|
||||
|
||||
let Some(provider) = editor.completion_provider.as_ref() else {
|
||||
return Task::ready(());
|
||||
};
|
||||
|
||||
let resolve_task = provider.resolve_completions(
|
||||
buffer,
|
||||
matches.iter().map(|m| m.candidate_id).collect(),
|
||||
completions.clone(),
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
if let Some(true) = resolve_task.await.log_err() {
|
||||
this.update(&mut cx, |_, cx| cx.notify()).ok();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn attempt_resolve_selected_completion_documentation(
|
||||
fn resolve_selected_completion(
|
||||
&mut self,
|
||||
provider: Option<&dyn CompletionProvider>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let settings = EditorSettings::get_global(cx);
|
||||
if !settings.show_completion_documentation {
|
||||
return;
|
||||
}
|
||||
|
||||
let completion_index = self.matches[self.selected_item].candidate_id;
|
||||
let Some(provider) = provider else {
|
||||
return;
|
||||
};
|
||||
let Some(documentation_resolve) = self
|
||||
.selected_completion_documentation_resolve_debounce
|
||||
.as_ref()
|
||||
else {
|
||||
let Some(completion_resolve) = self.selected_completion_resolve_debounce.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -1223,7 +1179,7 @@ impl CompletionsMenu {
|
|||
EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
|
||||
let delay = Duration::from_millis(delay_ms);
|
||||
|
||||
documentation_resolve.lock().fire_new(delay, cx, |_, cx| {
|
||||
completion_resolve.lock().fire_new(delay, cx, |_, cx| {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
if let Some(true) = resolve_task.await.log_err() {
|
||||
this.update(&mut cx, |_, cx| cx.notify()).ok();
|
||||
|
@ -2118,7 +2074,6 @@ impl Editor {
|
|||
auto_signature_help: None,
|
||||
find_all_references_task_sources: Vec::new(),
|
||||
next_completion_id: 0,
|
||||
completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
|
||||
next_inlay_id: 0,
|
||||
code_action_providers,
|
||||
available_code_actions: Default::default(),
|
||||
|
@ -4523,9 +4478,9 @@ impl Editor {
|
|||
let sort_completions = provider.sort_completions();
|
||||
|
||||
let id = post_inc(&mut self.next_completion_id);
|
||||
let task = cx.spawn(|this, mut cx| {
|
||||
let task = cx.spawn(|editor, mut cx| {
|
||||
async move {
|
||||
this.update(&mut cx, |this, _| {
|
||||
editor.update(&mut cx, |this, _| {
|
||||
this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
|
||||
})?;
|
||||
let completions = completions.await.log_err();
|
||||
|
@ -4543,34 +4498,14 @@ impl Editor {
|
|||
if menu.matches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
let completions = menu.completions.clone();
|
||||
let matches = menu.matches.clone();
|
||||
|
||||
let delay_ms = EditorSettings::get_global(cx)
|
||||
.completion_documentation_secondary_query_debounce;
|
||||
let delay = Duration::from_millis(delay_ms);
|
||||
editor
|
||||
.completion_documentation_pre_resolve_debounce
|
||||
.fire_new(delay, cx, |editor, cx| {
|
||||
CompletionsMenu::pre_resolve_completion_documentation(
|
||||
buffer,
|
||||
completions,
|
||||
matches,
|
||||
editor,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
Some(menu)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let mut context_menu = this.context_menu.write();
|
||||
editor.update(&mut cx, |editor, cx| {
|
||||
let mut context_menu = editor.context_menu.write();
|
||||
match context_menu.as_ref() {
|
||||
None => {}
|
||||
|
||||
|
@ -4583,19 +4518,20 @@ impl Editor {
|
|||
_ => return,
|
||||
}
|
||||
|
||||
if this.focus_handle.is_focused(cx) && menu.is_some() {
|
||||
let menu = menu.unwrap();
|
||||
if editor.focus_handle.is_focused(cx) && menu.is_some() {
|
||||
let mut menu = menu.unwrap();
|
||||
menu.resolve_selected_completion(editor.completion_provider.as_deref(), cx);
|
||||
*context_menu = Some(ContextMenu::Completions(menu));
|
||||
drop(context_menu);
|
||||
this.discard_inline_completion(false, cx);
|
||||
editor.discard_inline_completion(false, cx);
|
||||
cx.notify();
|
||||
} else if this.completion_tasks.len() <= 1 {
|
||||
} else if editor.completion_tasks.len() <= 1 {
|
||||
// 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.
|
||||
drop(context_menu);
|
||||
if this.hide_context_menu(cx).is_none() {
|
||||
this.update_visible_inline_completion(cx);
|
||||
if editor.hide_context_menu(cx).is_none() {
|
||||
editor.update_visible_inline_completion(cx);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue