Repository: helix-editor/helix
File: helix-term/src/handlers/signature_help.rs (Rust)

Code before:

    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]) => {
                // LS sends offsets based on utf-16 based string representation
                // but highlighting in helix is done using byte offset.
                use helix_core::str_utils::char_to_byte_idx;
                let from = char_to_byte_idx(&signature.label, *start as usize);
                let to = char_to_byte_idx(&signature.label, *end as usize);
                Some((from, to))
            }
        }
    };
    contents.set_active_param_range(active_param_range());

    let old_popup = compositor.find_id::<SignaturePopup>(SignatureHelp::ID);
    let popup = SignaturePopup::new(contents, old_popup.and_then(|p| p.get_position()));
    compositor.replace_or_push(SignatureHelp::ID, popup);
}

fn signature_help_post_insert_char_hook(
    tx: &Sender<SignatureHelpEvent>,
    PostInsertChar { cx, .. }: &mut PostInsertChar<'_, '_>,
) -> anyhow::Result<()> {
    if !cx.editor.config().lsp.auto_signature_help {
        return Ok(());
    }
    let (view, doc) = current!(cx.editor);
    // TODO support multiple language servers (not just the first that is found), likely by merging UI somehow
    let Some(language_server) = doc
            .language_servers_with_feature(LanguageServerFeature::SignatureHelp)
            .next()
        else {
            return Ok(());
        };

    let capabilities = language_server.capabilities();

    if let lsp::ServerCapabilities {
        signature_help_provider:
            Some(lsp::SignatureHelpOptions {
                trigger_characters: Some(triggers),

Review comment (suggestion):
I do prefer the `Above` bias for signature help. Why was that moved underneath the cursor? Particularly with autcompletions now being almost always below that just seems like the natural choice.

Code after:
    );

    let doc = doc!(editor);
    let language = doc.language_name().unwrap_or("");

    if response.signatures.is_empty() {
        return;
    }

    let signatures: Vec<Signature> = response
        .signatures
        .into_iter()
        .map(|s| {
            let active_param_range = active_param_range(&s, response.active_parameter);

            let signature_doc = if config.lsp.display_signature_help_docs {
                s.documentation.map(|doc| match doc {
                    lsp::Documentation::String(s) => s,
                    lsp::Documentation::MarkupContent(markup) => markup.value,
                })
            } else {
                None
            };

            Signature {
                signature: s.label,
                signature_doc,
                active_param_range,
            }
        })
        .collect();

    let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID);
    let mut active_signature = old_popup
        .as_ref()
        .map(|popup| popup.contents().active_signature())
        .unwrap_or_else(|| response.active_signature.unwrap_or_default() as usize);

    if active_signature >= signatures.len() {
        active_signature = signatures.len() - 1;
    }

    let contents = SignatureHelp::new(
        language.to_string(),
        Arc::clone(&editor.syn_loader),
        active_signature,
        signatures,
    );

    let mut popup = Popup::new(SignatureHelp::ID, contents)
        .position(old_popup.and_then(|p| p.get_position()))