Skip to content

Commit c5049bd

Browse files
bors[bot]dbofmmbt
andauthored
Merge #11190
11190: fix(completions): improve fn_param r=dbofmmbt a=dbofmmbt - insert commas around when necessary - only suggest `self` completions when param list is empty - stop suggesting completions for identifiers which are already on the param list Closes #11085 Co-authored-by: Eduardo Canellas <[email protected]>
2 parents ac3ea3e + a973e5a commit c5049bd

File tree

2 files changed

+91
-24
lines changed

2 files changed

+91
-24
lines changed

crates/ide_completion/src/completions/fn_param.rs

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use rustc_hash::FxHashMap;
44
use syntax::{
55
ast::{self, HasModuleItem},
6-
match_ast, AstNode,
6+
match_ast, AstNode, SyntaxKind,
77
};
88

99
use crate::{
@@ -16,24 +16,22 @@ use crate::{
1616
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
1717
/// suggested.
1818
pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
19-
if !matches!(ctx.pattern_ctx, Some(PatternContext { is_param: Some(ParamKind::Function), .. }))
20-
{
19+
let param_of_fn =
20+
matches!(ctx.pattern_ctx, Some(PatternContext { is_param: Some(ParamKind::Function), .. }));
21+
22+
if !param_of_fn {
2123
return None;
2224
}
2325

24-
let mut params = FxHashMap::default();
26+
let mut file_params = FxHashMap::default();
2527

26-
let me = ctx.token.ancestors().find_map(ast::Fn::cast);
27-
let mut process_fn = |func: ast::Fn| {
28-
if Some(&func) == me.as_ref() {
29-
return;
30-
}
31-
func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28+
let mut extract_params = |f: ast::Fn| {
29+
f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
3230
if let Some(pat) = param.pat() {
3331
// FIXME: We should be able to turn these into SmolStr without having to allocate a String
34-
let text = param.syntax().text().to_string();
35-
let lookup = pat.syntax().text().to_string();
36-
params.entry(text).or_insert(lookup);
32+
let whole_param = param.syntax().text().to_string();
33+
let binding = pat.syntax().text().to_string();
34+
file_params.entry(whole_param).or_insert(binding);
3735
}
3836
});
3937
};
@@ -44,32 +42,93 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
4442
ast::SourceFile(it) => it.items().filter_map(|item| match item {
4543
ast::Item::Fn(it) => Some(it),
4644
_ => None,
47-
}).for_each(&mut process_fn),
45+
}).for_each(&mut extract_params),
4846
ast::ItemList(it) => it.items().filter_map(|item| match item {
4947
ast::Item::Fn(it) => Some(it),
5048
_ => None,
51-
}).for_each(&mut process_fn),
49+
}).for_each(&mut extract_params),
5250
ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
5351
ast::AssocItem::Fn(it) => Some(it),
5452
_ => None,
55-
}).for_each(&mut process_fn),
53+
}).for_each(&mut extract_params),
5654
_ => continue,
5755
}
5856
};
5957
}
6058

59+
let function = ctx.token.ancestors().find_map(ast::Fn::cast)?;
60+
let param_list = function.param_list()?;
61+
62+
remove_duplicated(&mut file_params, param_list.params());
63+
6164
let self_completion_items = ["self", "&self", "mut self", "&mut self"];
62-
if ctx.impl_def.is_some() && me?.param_list()?.params().next().is_none() {
65+
if should_add_self_completions(ctx, param_list) {
6366
self_completion_items.into_iter().for_each(|self_item| {
6467
add_new_item_to_acc(ctx, acc, self_item.to_string(), self_item.to_string())
6568
});
6669
}
6770

68-
params.into_iter().for_each(|(label, lookup)| add_new_item_to_acc(ctx, acc, label, lookup));
71+
file_params.into_iter().try_for_each(|(whole_param, binding)| {
72+
Some(add_new_item_to_acc(ctx, acc, surround_with_commas(ctx, whole_param), binding))
73+
})?;
6974

7075
Some(())
7176
}
7277

78+
fn remove_duplicated(
79+
file_params: &mut FxHashMap<String, String>,
80+
fn_params: ast::AstChildren<ast::Param>,
81+
) {
82+
fn_params.for_each(|param| {
83+
let whole_param = param.syntax().text().to_string();
84+
file_params.remove(&whole_param);
85+
86+
if let Some(pattern) = param.pat() {
87+
let binding = pattern.syntax().text().to_string();
88+
file_params.retain(|_, v| v != &binding);
89+
}
90+
})
91+
}
92+
93+
fn should_add_self_completions(ctx: &CompletionContext, param_list: ast::ParamList) -> bool {
94+
let inside_impl = ctx.impl_def.is_some();
95+
let no_params = param_list.params().next().is_none() && param_list.self_param().is_none();
96+
97+
inside_impl && no_params
98+
}
99+
100+
fn surround_with_commas(ctx: &CompletionContext, param: String) -> String {
101+
match fallible_surround_with_commas(ctx, &param) {
102+
Some(surrounded) => surrounded,
103+
// fallback to the original parameter
104+
None => param,
105+
}
106+
}
107+
108+
fn fallible_surround_with_commas(ctx: &CompletionContext, param: &str) -> Option<String> {
109+
let next_token = {
110+
let t = ctx.token.next_token()?;
111+
match t.kind() {
112+
SyntaxKind::WHITESPACE => t.next_token()?,
113+
_ => t,
114+
}
115+
};
116+
117+
let trailing_comma_missing = matches!(next_token.kind(), SyntaxKind::IDENT);
118+
let trailing = if trailing_comma_missing { "," } else { "" };
119+
120+
let previous_token = if matches!(ctx.token.kind(), SyntaxKind::IDENT | SyntaxKind::WHITESPACE) {
121+
ctx.previous_token.as_ref()?
122+
} else {
123+
&ctx.token
124+
};
125+
126+
let needs_leading = !matches!(previous_token.kind(), SyntaxKind::L_PAREN | SyntaxKind::COMMA);
127+
let leading = if needs_leading { ", " } else { "" };
128+
129+
Some(format!("{}{}{}", leading, param, trailing))
130+
}
131+
73132
fn add_new_item_to_acc(
74133
ctx: &CompletionContext,
75134
acc: &mut Completions,

crates/ide_completion/src/tests/fn_param.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,20 @@ fn bar(file_id: usize) {}
4646
fn baz(file$0 id: u32) {}
4747
"#,
4848
expect![[r#"
49-
bn file_id: usize
49+
bn file_id: usize,
50+
kw mut
51+
"#]],
52+
);
53+
}
54+
55+
#[test]
56+
fn repeated_param_name() {
57+
check(
58+
r#"
59+
fn foo(file_id: usize) {}
60+
fn bar(file_id: u32, $0) {}
61+
"#,
62+
expect![[r#"
5063
kw mut
5164
"#]],
5265
);
@@ -126,7 +139,6 @@ impl A {
126139

127140
#[test]
128141
fn in_impl_after_self() {
129-
// FIXME: self completions should not be here
130142
check(
131143
r#"
132144
struct A {}
@@ -137,10 +149,6 @@ impl A {
137149
}
138150
"#,
139151
expect![[r#"
140-
bn self
141-
bn &self
142-
bn mut self
143-
bn &mut self
144152
bn file_id: usize
145153
kw mut
146154
sp Self

0 commit comments

Comments
 (0)