rust-lang / rust-lang/rust-analyzer
Avoid expanding arguments in function completion depending on bounds
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 16.9k
- Forks
- 2.2k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 72
Description
Currently when completing function items, whether arguments are expanded is controlled globally by rust-analyzer.completion.callable.snippets. Instead it should be based on the excepted type in the context the completion:
- If the return type of the expanded function could be applicable and the function item cannot, i.e. there exists a combination of generics given to the function that will satisfy the bounds of the function it is used in, the expanded form is used
- Otherwise, If the function item itself could be applicable the unexpanded form is used
- Otherwise or if the bounds cannot be solved, it should fallback to the existing setting
The main motivation is adding systems in bevy (i.e. App::add_systems),
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, set|)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d::default());
}
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup([commands]);)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d::default());
}
(where [] represents the selection)
I would expect it to complete:
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup|)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d::default());
}
This case is simpler because the return type is () so we know it's never applicable.
As another example, completing arguments of std::convert::identity and std::hint::black_box it should be based on rust-analyzer.completion.callable.snippets, as both forms are applicable to the trait bounds.
Alternative: Attribute hints
If solving bounds isn't feasible in terms of complexity and performance, attributes that hint the intent could be used instead.
There are a couple of ways this could be done (terribly bikesheding syntax):
#[rust_analyzer::completion::callable::snippets(None)]to apply therust-analyzer.completion.callable.snippetssetting to an individual block#[rust_analyzer::completion::callable::arguments("functions")]to hint the function commonly expects functions as arguments#[rust_analyzer::completion::callable::expand(false)]to hint the function itself is commonly used as an argument
This won't work properly for higher order functions but it would still provide a much better experience for the bevy use case.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing rust-analyzer's function completion handling and the rust-analyzer.completion.callable.snippets setting. Compare completion for the Bevy add_systems example with std::convert::identity and std::hint::black_box, then define done as selecting expanded or unexpanded arguments according to applicable bounds, with the existing setting as fallback.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100