aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/commands/lsp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/commands/lsp.rs')
-rw-r--r--helix-term/src/commands/lsp.rs124
1 files changed, 105 insertions, 19 deletions
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index a91e3792..1785a50c 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -6,18 +6,19 @@ use helix_lsp::{
};
use tui::text::{Span, Spans};
-use super::{align_view, push_jump, Align, Context, Editor};
+use super::{align_view, push_jump, Align, Context, Editor, Open};
use helix_core::{path, Selection};
use helix_view::{editor::Action, theme::Style};
use crate::{
compositor::{self, Compositor},
- ui::{self, overlay::overlayed, FileLocation, FilePicker, Popup, PromptEvent},
+ ui::{
+ self, lsp::SignatureHelp, overlay::overlayed, FileLocation, FilePicker, Popup, PromptEvent,
+ },
};
-use std::collections::BTreeMap;
-use std::{borrow::Cow, path::PathBuf};
+use std::{borrow::Cow, collections::BTreeMap, path::PathBuf, sync::Arc};
/// Gets the language server that is attached to a document, and
/// if it's not active displays a status message. Using this macro
@@ -805,31 +806,116 @@ pub fn goto_reference(cx: &mut Context) {
);
}
+#[derive(PartialEq)]
+pub enum SignatureHelpInvoked {
+ Manual,
+ Automatic,
+}
+
pub fn signature_help(cx: &mut Context) {
+ signature_help_impl(cx, SignatureHelpInvoked::Manual)
+}
+
+pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
let (view, doc) = current!(cx.editor);
- let language_server = language_server!(cx.editor, doc);
+ let was_manually_invoked = invoked == SignatureHelpInvoked::Manual;
+
+ let language_server = match doc.language_server() {
+ Some(language_server) => language_server,
+ None => {
+ // Do not show the message if signature help was invoked
+ // automatically on backspace, trigger characters, etc.
+ if was_manually_invoked {
+ cx.editor
+ .set_status("Language server not active for current buffer");
+ }
+ return;
+ }
+ };
let offset_encoding = language_server.offset_encoding();
let pos = doc.position(view.id, offset_encoding);
- let future = language_server.text_document_signature_help(doc.identifier(), pos, None);
+ let future = match language_server.text_document_signature_help(doc.identifier(), pos, None) {
+ Some(f) => f,
+ None => return,
+ };
cx.callback(
future,
- move |_editor, _compositor, response: Option<lsp::SignatureHelp>| {
- if let Some(signature_help) = response {
- log::info!("{:?}", signature_help);
- // signatures
- // active_signature
- // active_parameter
- // render as:
-
- // signature
- // ----------
- // doc
-
- // with active param highlighted
+ move |editor, compositor, response: Option<lsp::SignatureHelp>| {
+ let config = &editor.config();
+
+ if !(config.lsp.auto_signature_help
+ || SignatureHelp::visible_popup(compositor).is_some()
+ || was_manually_invoked)
+ {
+ return;
}
+
+ let response = match response {
+ // According to the spec the response should be None if there
+ // are no signatures, but some servers don't follow this.
+ Some(s) if !s.signatures.is_empty() => s,
+ _ => {
+ compositor.remove(SignatureHelp::ID);
+ return;
+ }
+ };
+ let doc = doc!(editor);
+ let language = doc
+ .language()
+ .and_then(|scope| scope.strip_prefix("source."))
+ .unwrap_or("");
+
+ let signature = match response
+ .signatures
+ .get(response.active_signature.unwrap_or(0) as usize)
+ {
+ Some(s) => s,
+ None => return,
+ };
+ let mut contents = SignatureHelp::new(
+ signature.label.clone(),
+ language.to_string(),
+ Arc::clone(&editor.syn_loader),
+ );
+
+ let signature_doc = if config.lsp.display_signature_help_docs {
+ signature.documentation.as_ref().map(|doc| match doc {
+ lsp::Documentation::String(s) => s.clone(),
+ lsp::Documentation::MarkupContent(markup) => markup.value.clone(),
+ })
+ } else {
+ None
+ };
+
+ contents.set_signature_doc(signature_doc);
+
+ let active_param_range = || -> Option<(usize, usize)> {
+ let param_idx = signature
+ .active_parameter
+ .or(response.active_parameter)
+ .unwrap_or(0) as usize;
+ let param = signature.parameters.as_ref()?.get(param_idx)?;
+ match &param.label {
+ lsp::ParameterLabel::Simple(string) => {
+ let start = signature.label.find(string.as_str())?;
+ Some((start, start + string.len()))
+ }
+ lsp::ParameterLabel::LabelOffsets([start, end]) => {
+ Some((*start as usize, *end as usize))
+ }
+ }
+ };
+ contents.set_active_param_range(active_param_range());
+
+ let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID);
+ let popup = Popup::new(SignatureHelp::ID, contents)
+ .position(old_popup.and_then(|p| p.get_position()))
+ .position_bias(Open::Above)
+ .ignore_escape_key(true);
+ compositor.replace_or_push(SignatureHelp::ID, popup);
},
);
}