diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 3562c17a0826519e50a3fff9f3a6a4e517b9bc49..502440e10caf9a8956474f25cac240d33ed1a3b6 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -58,6 +58,6 @@ util.workspace = true [dev-dependencies] text.workspace = true -theme.workspace = true +theme = { workspace = true, features = ["test-support"] } unindent.workspace = true workspace = { workspace = true, features = ["test-support"] } diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 51349464b19d118f4792b1da4caac189e2b4481c..5fb4f8046fa3461fce46174fb9e94561c36ace10 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -208,6 +208,12 @@ impl LspAdapter for RustLspAdapter { .and_then(|detail| detail.detail.as_ref()) .or(completion.detail.as_ref()) .map(ToOwned::to_owned); + let function_signature = completion + .label_details + .as_ref() + .and_then(|detail| detail.description.as_ref()) + .or(completion.detail.as_ref()) + .map(ToOwned::to_owned); match completion.kind { Some(lsp::CompletionItemKind::FIELD) if detail.is_some() => { let name = &completion.label; @@ -241,18 +247,31 @@ impl LspAdapter for RustLspAdapter { static ref REGEX: Regex = Regex::new("\\(…?\\)").unwrap(); } let detail = detail.unwrap(); - const FUNCTION_PREFIXES: [&'static str; 2] = ["async fn", "fn"]; - let prefix = FUNCTION_PREFIXES - .iter() - .find_map(|prefix| detail.strip_prefix(*prefix).map(|suffix| (prefix, suffix))); + const FUNCTION_PREFIXES: [&'static str; 6] = [ + "async fn", + "async unsafe fn", + "const fn", + "const unsafe fn", + "unsafe fn", + "fn", + ]; + // Is it function `async`? + let fn_keyword = FUNCTION_PREFIXES.iter().find_map(|prefix| { + function_signature.as_ref().and_then(|signature| { + signature + .strip_prefix(*prefix) + .map(|suffix| (*prefix, suffix)) + }) + }); // fn keyword should be followed by opening parenthesis. - if let Some((prefix, suffix)) = prefix { - if suffix.starts_with('(') { - let text = REGEX.replace(&completion.label, suffix).to_string(); + if let Some((prefix, suffix)) = fn_keyword { + if detail.starts_with(" (") { + let mut text = REGEX.replace(&completion.label, suffix).to_string(); let source = Rope::from(format!("{prefix} {} {{}}", text).as_str()); let run_start = prefix.len() + 1; let runs = language.highlight_text(&source, run_start..run_start + text.len()); + text.push_str(&detail); return Some(CodeLabel { filter_range: 0..completion.label.find('(').unwrap_or(text.len()), text, @@ -273,17 +292,21 @@ impl LspAdapter for RustLspAdapter { } _ => None, }; - let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name?)?; + let mut label = completion.label.clone(); if let Some(detail) = detail.filter(|detail| detail.starts_with(" (")) { use std::fmt::Write; write!(label, "{detail}").ok()?; } let mut label = CodeLabel::plain(label, None); - label.runs.push(( - 0..label.text.rfind('(').unwrap_or(completion.label.len()), - highlight_id, - )); + if let Some(highlight_name) = highlight_name { + let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name)?; + label.runs.push(( + 0..label.text.rfind('(').unwrap_or(completion.label.len()), + highlight_id, + )); + } + return Some(label); } _ => {} @@ -660,6 +683,7 @@ mod tests { use crate::language; use gpui::{BorrowAppContext, Context, Hsla, TestAppContext}; use language::language_settings::AllLanguageSettings; + use lsp::CompletionItemLabelDetails; use settings::SettingsStore; use theme::SyntaxTheme; @@ -729,14 +753,17 @@ mod tests { &lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "hello(…)".to_string(), - detail: Some("fn(&mut Option) -> Vec".to_string()), + label_details: Some(CompletionItemLabelDetails { + detail: Some(" (use crate::foo)".into()), + description: Some("fn(&mut Option) -> Vec".to_string()) + }), ..Default::default() }, &language ) .await, Some(CodeLabel { - text: "hello(&mut Option) -> Vec".to_string(), + text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), filter_range: 0..5, runs: vec![ (0..5, highlight_function), @@ -754,14 +781,17 @@ mod tests { &lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "hello(…)".to_string(), - detail: Some("async fn(&mut Option) -> Vec".to_string()), + label_details: Some(CompletionItemLabelDetails { + detail: Some(" (use crate::foo)".into()), + description: Some("async fn(&mut Option) -> Vec".to_string()), + }), ..Default::default() }, &language ) .await, Some(CodeLabel { - text: "hello(&mut Option) -> Vec".to_string(), + text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), filter_range: 0..5, runs: vec![ (0..5, highlight_function), @@ -798,14 +828,18 @@ mod tests { &lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "hello(…)".to_string(), - detail: Some("fn(&mut Option) -> Vec".to_string()), + label_details: Some(CompletionItemLabelDetails { + detail: Some(" (use crate::foo)".to_string()), + description: Some("fn(&mut Option) -> Vec".to_string()), + }), + ..Default::default() }, &language ) .await, Some(CodeLabel { - text: "hello(&mut Option) -> Vec".to_string(), + text: "hello(&mut Option) -> Vec (use crate::foo)".to_string(), filter_range: 0..5, runs: vec![ (0..5, highlight_function),