From 65d7105bfa8b545e878b9cb80dc923eb9b9d257e Mon Sep 17 00:00:00 2001 From: kyfanc Date: Mon, 15 Apr 2024 16:53:53 +0800 Subject: [PATCH] hover request to multiple LSPs --- helix-term/src/commands/lsp.rs | 91 ++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 6a5ceae6f3546..6f04fec5feb1d 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -2,7 +2,7 @@ use futures_util::{stream::FuturesOrdered, FutureExt}; use helix_lsp::{ block_on, lsp::{ - self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, DiagnosticSeverity, + self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, DiagnosticSeverity, Hover, NumberOrString, }, util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range}, @@ -943,53 +943,60 @@ pub fn signature_help(cx: &mut Context) { pub fn hover(cx: &mut Context) { let (view, doc) = current!(cx.editor); + if doc + .language_servers_with_feature(LanguageServerFeature::Hover) + .count() + == 0 + { + cx.editor.set_status(format!( + "No configured language server supports {}", + LanguageServerFeature::Hover + )); + return; + } - // TODO support multiple language servers (merge UI somehow) - let language_server = - language_server_with_feature!(cx.editor, doc, LanguageServerFeature::Hover); - // TODO: factor out a doc.position_identifier() that returns lsp::TextDocumentPositionIdentifier - let pos = doc.position(view.id, language_server.offset_encoding()); - let future = language_server - .text_document_hover(doc.identifier(), pos, None) - .unwrap(); + let mut seen_language_servers = HashSet::new(); + let mut futures: FuturesOrdered<_> = doc + .language_servers_with_feature(LanguageServerFeature::Hover) + .filter(|ls| seen_language_servers.insert(ls.id())) + .map(|language_server| { + let lsp_name = language_server.name().to_string(); + // TODO: factor out a doc.position_identifier() that returns lsp::TextDocumentPositionIdentifier + let pos = doc.position(view.id, language_server.offset_encoding()); + let request = language_server + .text_document_hover(doc.identifier(), pos, None) + .unwrap(); - cx.callback( - future, - move |editor, compositor, response: Option| { - if let Some(hover) = response { - // hover.contents / .range <- used for visualizing - - fn marked_string_to_markdown(contents: lsp::MarkedString) -> String { - match contents { - lsp::MarkedString::String(contents) => contents, - lsp::MarkedString::LanguageString(string) => { - if string.language == "markdown" { - string.value - } else { - format!("```{}\n{}\n```", string.language, string.value) - } - } - } - } + async move { + let json = request.await?; + let response = serde_json::from_value::>(json)?; + anyhow::Ok((lsp_name, response)) + } + }) + .collect(); - let contents = match hover.contents { - lsp::HoverContents::Scalar(contents) => marked_string_to_markdown(contents), - lsp::HoverContents::Array(contents) => contents - .into_iter() - .map(marked_string_to_markdown) - .collect::>() - .join("\n\n"), - lsp::HoverContents::Markup(contents) => contents.value, - }; + cx.jobs.callback(async move { + let mut hovers: Vec<(String, Hover)> = Vec::new(); - // skip if contents empty + while let Some((lsp_name, hover)) = futures.try_next().await? { + if let Some(hover) = hover { + hovers.push((lsp_name, hover)); + } + } - let contents = ui::Markdown::new(contents, editor.syn_loader.clone()); - let popup = Popup::new("hover", contents).auto_close(true); - compositor.replace_or_push("hover", popup); + let call = move |editor: &mut Editor, compositor: &mut Compositor| { + if hovers.is_empty() { + editor.set_status("No hover results available."); + return; } - }, - ); + + // create new popup + let contents = ui::lsp::hover::Hover::new(hovers, editor.syn_loader.clone()); + let popup = Popup::new(ui::lsp::hover::Hover::ID, contents).auto_close(true); + compositor.replace_or_push(ui::lsp::hover::Hover::ID, popup); + }; + Ok(Callback::EditorCompositor(Box::new(call))) + }); } pub fn rename_symbol(cx: &mut Context) {