@@ -257,7 +257,7 @@ impl Completion {
257 let future = language_server.resolve_completion_item(completion_item);
257 let future = language_server.resolve_completion_item(completion_item);
258 let response = helix_lsp::block_on(future);
258 let response = helix_lsp::block_on(future);
259 match response {
259 match response {
260− Ok(completion_item) => Some(completion_item),
260+ Ok(value) => serde_json::from_value(value).ok(),
261 Err(err) => {
261 Err(err) => {
262 log::error!("execute LSP command: {}", err);
262 log::error!("execute LSP command: {}", err);
@@ -304,6 +304,12 @@ impl Completion {
304 self.popup.contents().is_empty()
304 self.popup.contents().is_empty()
307+ fn replace_item(&mut self, old_item: lsp::CompletionItem, new_item: lsp::CompletionItem) {
308+ self.popup.contents_mut().replace_option(old_item, new_item);
311+ /// Asynchronously requests that the currently selection completion item is
312+ /// resolved through LSP `completionItem/resolve`.
307 pub fn ensure_item_resolved(&mut self, cx: &mut commands::Context) -> bool {
313 pub fn ensure_item_resolved(&mut self, cx: &mut commands::Context) -> bool {
308 // > If computing full completion items is expensive, servers can additionally provide a
314 // > If computing full completion items is expensive, servers can additionally provide a
309 // > handler for the completion item resolve request. ...
315 // > handler for the completion item resolve request. ...
@@ -313,16 +319,38 @@ impl Completion {
313 // > 'completionItem/resolve' request is sent with the selected completion item as a parameter.
319 // > 'completionItem/resolve' request is sent with the selected completion item as a parameter.
314 // > The returned completion item should have the documentation property filled in.
320 // > The returned completion item should have the documentation property filled in.
315 // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
321 // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
316− match self.popup.contents_mut().selection_mut() {
322+ let current_item = match self.popup.contents().selection() {
317− Some(item) if item.documentation.is_none() => {
323+ Some(item) if item.documentation.is_none() => item.clone(),
318− let doc = doc!(cx.editor);
327+ let language_server = match doc!(cx.editor).language_server() {
328+ Some(language_server) => language_server,
329+ None => return false,
332+ // This method should not block the compositor so we handle the response asynchronously.
319− if let Some(resolved_item) = Self::resolve_completion_item(doc, item.clone()) {
333+ let future = language_server.resolve_completion_item(current_item.clone());
337+ move |_editor, compositor, response: Option<lsp::CompletionItem>| {
338+ let resolved_item = match response {
343+ if let Some(completion) = &mut compositor
344+ .find::<crate::ui::EditorView>()
320− *item = resolved_item;
348+ completion.replace_item(current_item, resolved_item);