@@ -305,66 +305,63 @@ impl LspAdapter for RustLspAdapter {
completion: &lsp::CompletionItem,
language: &Arc<Language>,
) -> Option<CodeLabel> {
- let detail = completion
+ // rust-analyzer calls these detail left and detail right in terms of where it expects things to be rendered
+ // this usually contains signatures of the thing to be completed
+ let detail_right = completion
.label_details
.as_ref()
- .and_then(|detail| detail.detail.as_ref())
+ .and_then(|detail| detail.description.as_ref())
.or(completion.detail.as_ref())
.map(|detail| detail.trim());
- let function_signature = completion
+ // this tends to contain alias and import information
+ let detail_left = completion
.label_details
.as_ref()
- .and_then(|detail| detail.description.as_deref())
- .or(completion.detail.as_deref());
- match (detail, completion.kind) {
- (Some(detail), Some(lsp::CompletionItemKind::FIELD)) => {
+ .and_then(|detail| detail.detail.as_deref());
+ let mk_label = |text: String, runs| {
+ let filter_range = completion
+ .filter_text
+ .as_deref()
+ .and_then(|filter| {
+ completion
+ .label
+ .find(filter)
+ .map(|ix| ix..ix + filter.len())
+ })
+ .unwrap_or(0..completion.label.len());
+
+ CodeLabel {
+ text,
+ runs,
+ filter_range,
+ }
+ };
+ let mut label = match (detail_right, completion.kind) {
+ (Some(signature), Some(lsp::CompletionItemKind::FIELD)) => {
let name = &completion.label;
- let text = format!("{name}: {detail}");
+ let text = format!("{name}: {signature}");
let prefix = "struct S { ";
let source = Rope::from(format!("{prefix}{text} }}"));
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
- let filter_range = completion
- .filter_text
- .as_deref()
- .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
- .unwrap_or(0..name.len());
- return Some(CodeLabel {
- text,
- runs,
- filter_range,
- });
+ mk_label(text, runs)
}
(
- Some(detail),
+ Some(signature),
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE),
) if completion.insert_text_format != Some(lsp::InsertTextFormat::SNIPPET) => {
let name = &completion.label;
- let text = format!(
- "{}: {}",
- name,
- completion.detail.as_deref().unwrap_or(detail)
- );
+ let text = format!("{name}: {signature}",);
let prefix = "let ";
let source = Rope::from(format!("{prefix}{text} = ();"));
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
- let filter_range = completion
- .filter_text
- .as_deref()
- .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
- .unwrap_or(0..name.len());
- return Some(CodeLabel {
- text,
- runs,
- filter_range,
- });
+ mk_label(text, runs)
}
(
- Some(detail),
+ function_signature,
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD),
) => {
- static REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new("\\(…?\\)").unwrap());
const FUNCTION_PREFIXES: [&str; 6] = [
"async fn",
"async unsafe fn",
@@ -373,34 +370,27 @@ impl LspAdapter for RustLspAdapter {
"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))
- })
+ let fn_prefixed = FUNCTION_PREFIXES.iter().find_map(|&prefix| {
+ function_signature?
+ .strip_prefix(prefix)
+ .map(|suffix| (prefix, suffix))
});
// fn keyword should be followed by opening parenthesis.
- if let Some((prefix, suffix)) = fn_keyword {
- let mut text = REGEX.replace(&completion.label, suffix).to_string();
+ 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 text = format!("{label}{suffix}");
let source = Rope::from(format!("{prefix} {text} {{}}"));
let run_start = prefix.len() + 1;
let runs = language.highlight_text(&source, run_start..run_start + text.len());
- if detail.starts_with("(") {
- text.push(' ');
- text.push_str(&detail);
- }
- let filter_range = completion
- .filter_text
- .as_deref()
- .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
- .unwrap_or(0..completion.label.find('(').unwrap_or(text.len()));
- return Some(CodeLabel {
- filter_range,
- text,
- runs,
- });
+ mk_label(text, runs)
} else if completion
.detail
.as_ref()
@@ -410,20 +400,13 @@ impl LspAdapter for RustLspAdapter {
let len = text.len();
let source = Rope::from(text.as_str());
let runs = language.highlight_text(&source, 0..len);
- let filter_range = completion
- .filter_text
- .as_deref()
- .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
- .unwrap_or(0..len);
- return Some(CodeLabel {
- filter_range,
- text,
- runs,
- });
+ mk_label(text, runs)
+ } else {
+ mk_label(completion.label.clone(), vec![])
}
}
- (_, Some(kind)) => {
- let highlight_name = match kind {
+ (_, kind) => {
+ let highlight_name = kind.and_then(|kind| match kind {
lsp::CompletionItemKind::STRUCT
| lsp::CompletionItemKind::INTERFACE
| lsp::CompletionItemKind::ENUM => Some("type"),
@@ -433,27 +416,32 @@ impl LspAdapter for RustLspAdapter {
Some("constant")
}
_ => None,
- };
+ });
- let mut label = completion.label.clone();
- if let Some(detail) = detail.filter(|detail| detail.starts_with("(")) {
- label.push(' ');
- label.push_str(detail);
- }
- let mut label = CodeLabel::plain(label, completion.filter_text.as_deref());
+ let label = completion.label.clone();
+ let mut runs = vec![];
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()),
+ runs.push((
+ 0..label.rfind('(').unwrap_or(completion.label.len()),
highlight_id,
));
}
+ mk_label(label, runs)
+ }
+ };
- return Some(label);
+ if let Some(detail_left) = detail_left {
+ label.text.push(' ');
+ if !detail_left.starts_with('(') {
+ label.text.push('(');
+ }
+ label.text.push_str(detail_left);
+ if !detail_left.ends_with(')') {
+ label.text.push(')');
}
- _ => {}
}
- None
+ Some(label)
}
async fn label_for_symbol(
@@ -1169,7 +1157,7 @@ mod tests {
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
- filter_range: 0..5,
+ filter_range: 0..10,
runs: vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
@@ -1187,7 +1175,7 @@ mod tests {
kind: Some(lsp::CompletionItemKind::FUNCTION),
label: "hello(…)".to_string(),
label_details: Some(CompletionItemLabelDetails {
- detail: Some(" (use crate::foo)".into()),
+ detail: Some("(use crate::foo)".into()),
description: Some("async fn(&mut Option<T>) -> Vec<T>".to_string()),
}),
..Default::default()
@@ -1197,7 +1185,7 @@ mod tests {
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
- filter_range: 0..5,
+ filter_range: 0..10,
runs: vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
@@ -1234,7 +1222,7 @@ mod tests {
kind: Some(lsp::CompletionItemKind::FUNCTION),
label: "hello(…)".to_string(),
label_details: Some(CompletionItemLabelDetails {
- detail: Some(" (use crate::foo)".to_string()),
+ detail: Some("(use crate::foo)".to_string()),
description: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
}),
@@ -1243,6 +1231,35 @@ mod tests {
&language
)
.await,
+ Some(CodeLabel {
+ text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
+ filter_range: 0..10,
+ runs: vec![
+ (0..5, highlight_function),
+ (7..10, highlight_keyword),
+ (11..17, highlight_type),
+ (18..19, highlight_type),
+ (25..28, highlight_type),
+ (29..30, highlight_type),
+ ],
+ })
+ );
+
+ assert_eq!(
+ adapter
+ .label_for_completion(
+ &lsp::CompletionItem {
+ kind: Some(lsp::CompletionItemKind::FUNCTION),
+ label: "hello".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> (use crate::foo)".to_string(),
filter_range: 0..5,
@@ -1274,9 +1291,14 @@ mod tests {
)
.await,
Some(CodeLabel {
- text: "await.as_deref_mut()".to_string(),
+ text: "await.as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(),
filter_range: 6..18,
- runs: vec![],
+ runs: vec![
+ (6..18, HighlightId(2)),
+ (20..23, HighlightId(1)),
+ (33..40, HighlightId(0)),
+ (45..46, HighlightId(0))
+ ],
})
);