Skip to content

Commit

Permalink
hover request to multiple LSPs
Browse files Browse the repository at this point in the history
  • Loading branch information
kyfanc committed Apr 16, 2024
1 parent 203ec1c commit 65d7105
Showing 1 changed file with 49 additions and 42 deletions.
91 changes: 49 additions & 42 deletions helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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<lsp::Hover>| {
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::<Option<lsp::Hover>>(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::<Vec<_>>()
.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) {
Expand Down

0 comments on commit 65d7105

Please sign in to comment.