Skip to content

Commit

Permalink
feat: multi-lsp for hover
Browse files Browse the repository at this point in the history
  • Loading branch information
Desdaemon committed Dec 2, 2023
1 parent 86023cf commit 19fa20f
Showing 1 changed file with 62 additions and 41 deletions.
103 changes: 62 additions & 41 deletions helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use futures_util::{future::BoxFuture, stream::FuturesUnordered, FutureExt};
use futures_util::{
future::{join_all, BoxFuture},
stream::FuturesUnordered,
FutureExt,
};
use helix_lsp::{
block_on,
lsp::{
Expand Down Expand Up @@ -1279,52 +1283,69 @@ pub fn signature_help_impl_with_future(
pub fn hover(cx: &mut Context) {
let (view, doc) = current!(cx.editor);

// 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 language_servers = doc.language_servers_with_feature(LanguageServerFeature::Hover);
let mut future = vec![];
for server in language_servers {
// TODO: factor out a doc.position_identifier() that returns lsp::TextDocumentPositionIdentifier
let pos = doc.position(view.id, server.offset_encoding());
if let Some(resp) = server.text_document_hover(doc.identifier(), pos, None) {
future.push(resp);
}
}

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)
}
}
let future = join_all(future).map(|data| {
Ok(data
.into_iter()
.flat_map(|resp| {
let resp = resp.ok()?;
(!resp.is_null()).then_some(resp)
})
.collect())
});

cx.callback(future, move |editor, compositor, hover: Vec<lsp::Hover>| {
// 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)
}
}
}
}

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,
};
let contents = hover
.into_iter()
.map(|hover| 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,
})
.fold(String::new(), |mut lhs, rhs| {
if lhs.is_empty() {
rhs
} else {
if !rhs.is_empty() {
_ = write!(&mut lhs, "\n---\n{rhs}");
}
lhs
}
});

// skip if contents empty
// skip if contents empty

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 contents = ui::Markdown::new(contents, editor.syn_loader.clone());
let popup = Popup::new("hover", contents).auto_close(true);
compositor.replace_or_push("hover", popup);
});
}

pub fn rename_symbol(cx: &mut Context) {
Expand Down

0 comments on commit 19fa20f

Please sign in to comment.