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]
text.workspace = true
theme.workspace = true
theme = { workspace = true, features = ["test-support"] }
unindent.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View file

@ -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<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()
},
&language
)
.await,
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,
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<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()
},
&language
)
.await,
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,
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<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()
},
&language
)
.await,
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,
runs: vec![
(0..5, highlight_function),