rust: Expose import names in completions for modules and functions (#14490)

Release Notes:

- Improved accuracy of completion lists for Rust functions and modules.
This commit is contained in:
Piotr Osiewicz 2024-07-15 14:26:39 +02:00 committed by GitHub
parent e8d674dc04
commit c1aa4d939c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 19 deletions

View file

@ -58,6 +58,6 @@ util.workspace = true
[dev-dependencies] [dev-dependencies]
text.workspace = true text.workspace = true
theme.workspace = true theme = { workspace = true, features = ["test-support"] }
unindent.workspace = true unindent.workspace = true
workspace = { workspace = true, features = ["test-support"] } workspace = { workspace = true, features = ["test-support"] }

View file

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