]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/fn_param.rs
Merge #7866
[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         CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
58             .kind(CompletionItemKind::Binding)
59             .lookup_by(lookup)
60             .add_to(acc)
61     });
62 }
63
64 #[cfg(test)]
65 mod tests {
66     use expect_test::{expect, Expect};
67
68     use crate::{test_utils::completion_list, CompletionKind};
69
70     fn check(ra_fixture: &str, expect: Expect) {
71         let actual = completion_list(ra_fixture, CompletionKind::Magic);
72         expect.assert_eq(&actual);
73     }
74
75     #[test]
76     fn test_param_completion_last_param() {
77         check(
78             r#"
79 fn foo(file_id: FileId) {}
80 fn bar(file_id: FileId) {}
81 fn baz(file$0) {}
82 "#,
83             expect![[r#"
84                 bn file_id: FileId
85             "#]],
86         );
87     }
88
89     #[test]
90     fn test_param_completion_nth_param() {
91         check(
92             r#"
93 fn foo(file_id: FileId) {}
94 fn baz(file$0, x: i32) {}
95 "#,
96             expect![[r#"
97                 bn file_id: FileId
98             "#]],
99         );
100     }
101
102     #[test]
103     fn test_param_completion_trait_param() {
104         check(
105             r#"
106 pub(crate) trait SourceRoot {
107     pub fn contains(&self, file_id: FileId) -> bool;
108     pub fn module_map(&self) -> &ModuleMap;
109     pub fn lines(&self, file_id: FileId) -> &LineIndex;
110     pub fn syntax(&self, file$0)
111 }
112 "#,
113             expect![[r#"
114                 bn file_id: FileId
115             "#]],
116         );
117     }
118
119     #[test]
120     fn completes_param_in_inner_function() {
121         check(
122             r#"
123 fn outer(text: String) {
124     fn inner($0)
125 }
126 "#,
127             expect![[r#"
128                 bn text: String
129             "#]],
130         )
131     }
132 }