From 0097d896729ac4d5b3c215f61836bb10c8e7b41b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Aug 2025 09:43:49 +0200 Subject: [PATCH] language: Fix rust completion labels with `fullFunctionSignature` config (#35823) Release Notes: - N/A --- crates/languages/src/rust.rs | 108 ++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index b6567c6e33ff93a8bc00fdbb8ed1674cc31ddaa0..b52b1e7d551429de5968ab4b1c7535f98f910d8f 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -15,6 +15,7 @@ use serde_json::json; use settings::Settings as _; use smol::fs::{self}; use std::fmt::Display; +use std::ops::Range; use std::{ any::Any, borrow::Cow, @@ -318,17 +319,16 @@ impl LspAdapter for RustLspAdapter { .label_details .as_ref() .and_then(|detail| detail.detail.as_deref()); - let mk_label = |text: String, runs| { + let mk_label = |text: String, filter_range: &dyn Fn() -> Range, runs| { let filter_range = completion .filter_text .as_deref() - .and_then(|filter| { - completion - .label - .find(filter) - .map(|ix| ix..ix + filter.len()) + .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len())) + .or_else(|| { + text.find(&completion.label) + .map(|ix| ix..ix + completion.label.len()) }) - .unwrap_or(0..completion.label.len()); + .unwrap_or_else(filter_range); CodeLabel { text, @@ -344,7 +344,7 @@ impl LspAdapter for RustLspAdapter { let source = Rope::from_iter([prefix, &text, " }"]); let runs = language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); - mk_label(text, runs) + mk_label(text, &|| 0..completion.label.len(), runs) } ( Some(signature), @@ -356,7 +356,7 @@ impl LspAdapter for RustLspAdapter { let source = Rope::from_iter([prefix, &text, " = ();"]); let runs = language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); - mk_label(text, runs) + mk_label(text, &|| 0..completion.label.len(), runs) } ( function_signature, @@ -375,22 +375,35 @@ impl LspAdapter for RustLspAdapter { .strip_prefix(prefix) .map(|suffix| (prefix, suffix)) }); - // fn keyword should be followed by opening parenthesis. - if let Some((prefix, suffix)) = fn_prefixed { - let label = if let Some(label) = completion - .label - .strip_suffix("(…)") - .or_else(|| completion.label.strip_suffix("()")) - { - label - } else { - &completion.label - }; + let label = if let Some(label) = completion + .label + .strip_suffix("(…)") + .or_else(|| completion.label.strip_suffix("()")) + { + label + } else { + &completion.label + }; + + static FULL_SIGNATURE_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"fn (.?+)\(").expect("Failed to create REGEX")); + if let Some((function_signature, match_)) = function_signature + .filter(|it| it.contains(&label)) + .and_then(|it| Some((it, FULL_SIGNATURE_REGEX.find(it)?))) + { + let source = Rope::from(function_signature); + let runs = language.highlight_text(&source, 0..function_signature.len()); + mk_label( + function_signature.to_owned(), + &|| match_.range().start - 3..match_.range().end - 1, + runs, + ) + } else if let Some((prefix, suffix)) = fn_prefixed { let text = format!("{label}{suffix}"); let source = Rope::from_iter([prefix, " ", &text, " {}"]); let run_start = prefix.len() + 1; let runs = language.highlight_text(&source, run_start..run_start + text.len()); - mk_label(text, runs) + mk_label(text, &|| 0..label.len(), runs) } else if completion .detail .as_ref() @@ -400,9 +413,15 @@ impl LspAdapter for RustLspAdapter { let len = text.len(); let source = Rope::from(text.as_str()); let runs = language.highlight_text(&source, 0..len); - mk_label(text, runs) + mk_label(text, &|| 0..completion.label.len(), runs) + } else if detail_left.is_none() { + return None; } else { - mk_label(completion.label.clone(), vec![]) + mk_label( + completion.label.clone(), + &|| 0..completion.label.len(), + vec![], + ) } } (_, kind) => { @@ -426,8 +445,11 @@ impl LspAdapter for RustLspAdapter { 0..label.rfind('(').unwrap_or(completion.label.len()), highlight_id, )); + } else if detail_left.is_none() { + return None; } - mk_label(label, runs) + + mk_label(label, &|| 0..completion.label.len(), runs) } }; @@ -1124,7 +1146,7 @@ mod tests { .await, Some(CodeLabel { text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), - filter_range: 0..10, + filter_range: 0..5, runs: vec![ (0..5, highlight_function), (7..10, highlight_keyword), @@ -1152,7 +1174,7 @@ mod tests { .await, Some(CodeLabel { text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), - filter_range: 0..10, + filter_range: 0..5, runs: vec![ (0..5, highlight_function), (7..10, highlight_keyword), @@ -1200,7 +1222,7 @@ mod tests { .await, Some(CodeLabel { text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), - filter_range: 0..10, + filter_range: 0..5, runs: vec![ (0..5, highlight_function), (7..10, highlight_keyword), @@ -1269,6 +1291,38 @@ mod tests { }) ); + assert_eq!( + adapter + .label_for_completion( + &lsp::CompletionItem { + kind: Some(lsp::CompletionItemKind::METHOD), + label: "as_deref_mut()".to_string(), + filter_text: Some("as_deref_mut".to_string()), + label_details: Some(CompletionItemLabelDetails { + detail: None, + description: Some( + "pub fn as_deref_mut(&mut self) -> IterMut<'_, T>".to_string() + ), + }), + ..Default::default() + }, + &language + ) + .await, + Some(CodeLabel { + text: "pub fn as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(), + filter_range: 7..19, + runs: vec![ + (0..3, HighlightId(1)), + (4..6, HighlightId(1)), + (7..19, HighlightId(2)), + (21..24, HighlightId(1)), + (34..41, HighlightId(0)), + (46..47, HighlightId(0)) + ], + }) + ); + assert_eq!( adapter .label_for_completion(