]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/fn_param.rs
Merge #7966
[rust.git] / crates / ide_completion / src / completions / fn_param.rs
1 //! See `complete_fn_param`.
2
3 use rustc_hash::FxHashMap;
4 use syntax::{
5     ast::{self, ModuleItemOwner},
6     match_ast, AstNode,
7 };
8
9 use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
10
11 /// Complete repeated parameters, both name and type. For example, if all
12 /// functions in a file have a `spam: &mut Spam` parameter, a completion with
13 /// `spam: &mut Spam` insert text/label and `spam` lookup string will be
14 /// suggested.
15 pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
16     if !ctx.is_param {
17         return;
18     }
19
20     let mut params = FxHashMap::default();
21
22     let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23     let mut process_fn = |func: ast::Fn| {
24         if Some(&func) == me.as_ref() {
25             return;
26         }
27         func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28             if let Some(pat) = param.pat() {
29                 let text = param.syntax().text().to_string();
30                 let lookup = pat.syntax().text().to_string();
31                 params.entry(text).or_insert(lookup);
32             }
33         });
34     };
35
36     for node in ctx.token.parent().ancestors() {
37         match_ast! {
38             match node {
39                 ast::SourceFile(it) => it.items().filter_map(|item| match item {
40                     ast::Item::Fn(it) => Some(it),
41                     _ => None,
42                 }).for_each(&mut process_fn),
43                 ast::ItemList(it) => it.items().filter_map(|item| match item {
44                     ast::Item::Fn(it) => Some(it),
45                     _ => None,
46                 }).for_each(&mut process_fn),
47                 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
48                     ast::AssocItem::Fn(it) => Some(it),
49                     _ => None,
50                 }).for_each(&mut process_fn),
51                 _ => continue,
52             }
53         };
54     }
55
56     params.into_iter().for_each(|(label, lookup)| {
57         let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
58         item.kind(CompletionItemKind::Binding).lookup_by(lookup);
59         item.add_to(acc)
60     });
61 }
62
63 #[cfg(test)]
64 mod tests {
65     use expect_test::{expect, Expect};
66
67     use crate::{test_utils::completion_list, CompletionKind};
68
69     fn check(ra_fixture: &str, expect: Expect) {
70         let actual = completion_list(ra_fixture, CompletionKind::Magic);
71         expect.assert_eq(&actual);
72     }
73
74     #[test]
75     fn test_param_completion_last_param() {
76         check(
77             r#"
78 fn foo(file_id: FileId) {}
79 fn bar(file_id: FileId) {}
80 fn baz(file$0) {}
81 "#,
82             expect![[r#"
83                 bn file_id: FileId
84             "#]],
85         );
86     }
87
88     #[test]
89     fn test_param_completion_nth_param() {
90         check(
91             r#"
92 fn foo(file_id: FileId) {}
93 fn baz(file$0, x: i32) {}
94 "#,
95             expect![[r#"
96                 bn file_id: FileId
97             "#]],
98         );
99     }
100
101     #[test]
102     fn test_param_completion_trait_param() {
103         check(
104             r#"
105 pub(crate) trait SourceRoot {
106     pub fn contains(&self, file_id: FileId) -> bool;
107     pub fn module_map(&self) -> &ModuleMap;
108     pub fn lines(&self, file_id: FileId) -> &LineIndex;
109     pub fn syntax(&self, file$0)
110 }
111 "#,
112             expect![[r#"
113                 bn file_id: FileId
114             "#]],
115         );
116     }
117
118     #[test]
119     fn completes_param_in_inner_function() {
120         check(
121             r#"
122 fn outer(text: String) {
123     fn inner($0)
124 }
125 "#,
126             expect![[r#"
127                 bn text: String
128             "#]],
129         )
130     }
131 }