Skip to content

Commit

Permalink
feat: LSP completion function detail (#5993)
Browse files Browse the repository at this point in the history
# Description

## Problem

Just a tiny thing: when functions are suggested, Rust Analyzer shows the
full function signature above the documentation (this is a completion
item's `detail` property). It's useful because in the list the signature
is usually trimmed.

## Summary

Before:


![image](https://github.com/user-attachments/assets/173acca4-74fe-40ff-9c8c-6552296c05e4)

After:


![image](https://github.com/user-attachments/assets/520fc7df-bf01-4ef2-887a-b4e651f4035c)

## Additional Context

Also includes a bunch of small refactors to make it easier to test that
something is a function completion item regardless of whether it ends up
being a snippet completion or not, whether it triggers a command, etc.

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Sep 10, 2024
1 parent 31f50c4 commit e84f7d2
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 60 deletions.
48 changes: 34 additions & 14 deletions tooling/lsp/src/requests/completion/completion_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,14 @@ impl<'a> NodeFinder<'a> {
let name = if self_prefix { format!("self.{}", name) } else { name.clone() };
let name = &name;
let description = func_meta_type_to_string(func_meta, func_self_type.is_some());
let mut has_arguments = false;

let completion_item = match function_completion_kind {
FunctionCompletionKind::Name => {
simple_completion_item(name, CompletionItemKind::FUNCTION, Some(description))
}
FunctionCompletionKind::Name => simple_completion_item(
name,
CompletionItemKind::FUNCTION,
Some(description.clone()),
),
FunctionCompletionKind::NameAndParameters => {
let kind = CompletionItemKind::FUNCTION;
let skip_first_argument = attribute_first_type.is_some();
Expand All @@ -227,20 +230,25 @@ impl<'a> NodeFinder<'a> {
function_kind,
skip_first_argument,
);
let (label, insert_text) = if insert_text.ends_with("()") {
if skip_first_argument {
(name.to_string(), insert_text.strip_suffix("()").unwrap().to_string())
} else {
(format!("{}()", name), insert_text)
}
} else {
(format!("{}(…)", name), insert_text)
};

snippet_completion_item(label, kind, insert_text, Some(description))
if insert_text.ends_with("()") {
let label =
if skip_first_argument { name.to_string() } else { format!("{}()", name) };
simple_completion_item(label, kind, Some(description.clone()))
} else {
has_arguments = true;
snippet_completion_item(
format!("{}(…)", name),
kind,
insert_text,
Some(description.clone()),
)
}
}
};

let completion_item = completion_item_with_detail(completion_item, description);

let completion_item = if is_operator {
completion_item_with_sort_text(completion_item, operator_sort_text())
} else if function_kind == FunctionKind::Any && name == "new" {
Expand All @@ -254,11 +262,16 @@ impl<'a> NodeFinder<'a> {
let completion_item = match function_completion_kind {
FunctionCompletionKind::Name => completion_item,
FunctionCompletionKind::NameAndParameters => {
completion_item_with_trigger_parameter_hints_command(completion_item)
if has_arguments {
completion_item_with_trigger_parameter_hints_command(completion_item)
} else {
completion_item
}
}
};
let completion_item =
self.completion_item_with_doc_comments(ReferenceId::Function(func_id), completion_item);

Some(completion_item)
}

Expand Down Expand Up @@ -494,3 +507,10 @@ pub(super) fn completion_item_with_trigger_parameter_hints_command(
..completion_item
}
}

pub(super) fn completion_item_with_detail(
completion_item: CompletionItem,
detail: String,
) -> CompletionItem {
CompletionItem { detail: Some(detail), ..completion_item }
}
71 changes: 25 additions & 46 deletions tooling/lsp/src/requests/completion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod completion_tests {
requests::{
completion::{
completion_items::{
completion_item_with_sort_text,
completion_item_with_detail, completion_item_with_sort_text,
completion_item_with_trigger_parameter_hints_command, module_completion_item,
simple_completion_item, snippet_completion_item,
},
Expand Down Expand Up @@ -107,12 +107,22 @@ mod completion_tests {
insert_text: impl Into<String>,
description: impl Into<String>,
) -> CompletionItem {
completion_item_with_trigger_parameter_hints_command(snippet_completion_item(
label,
CompletionItemKind::FUNCTION,
insert_text,
Some(description.into()),
))
let insert_text: String = insert_text.into();
let description: String = description.into();

let has_arguments = insert_text.ends_with(')') && !insert_text.ends_with("()");
let completion_item = if has_arguments {
completion_item_with_trigger_parameter_hints_command(snippet_completion_item(
label,
CompletionItemKind::FUNCTION,
insert_text,
Some(description.clone()),
))
} else {
simple_completion_item(label, CompletionItemKind::FUNCTION, Some(description.clone()))
};

completion_item_with_detail(completion_item, description)
}

fn field_completion_item(field: &str, typ: impl Into<String>) -> CompletionItem {
Expand Down Expand Up @@ -191,15 +201,8 @@ mod completion_tests {
use foo::>|<
"#;

assert_completion(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn(i32) -> u64".to_string()),
)],
)
.await;
assert_completion(src, vec![function_completion_item("bar", "bar()", "fn(i32) -> u64")])
.await;
}

#[test]
Expand Down Expand Up @@ -1672,11 +1675,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"hello_world",
CompletionItemKind::FUNCTION,
Some("fn()".to_string()),
)],
vec![function_completion_item("hello_world", "hello_world()", "fn()")],
)
.await;
}
Expand All @@ -1694,11 +1693,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn()".to_string()),
)],
vec![function_completion_item("bar", "bar()", "fn()")],
)
.await;
}
Expand All @@ -1716,11 +1711,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn()".to_string()),
)],
vec![function_completion_item("bar", "bar()", "fn()")],
)
.await;
}
Expand All @@ -1738,11 +1729,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn()".to_string()),
)],
vec![function_completion_item("bar", "bar()", "fn()")],
)
.await;
}
Expand All @@ -1762,11 +1749,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn(self)".to_string()),
)],
vec![function_completion_item("bar", "bar()", "fn(self)")],
)
.await;
}
Expand All @@ -1786,11 +1769,7 @@ mod completion_tests {
"#;
assert_completion_excluding_auto_import(
src,
vec![simple_completion_item(
"bar",
CompletionItemKind::FUNCTION,
Some("fn(self)".to_string()),
)],
vec![function_completion_item("bar", "bar()", "fn(self)")],
)
.await;
}
Expand Down

0 comments on commit e84f7d2

Please sign in to comment.