]> git.lizzy.rs Git - rust.git/blob - crates/completion/src/completions/fn_param.rs
Merge #7706
[rust.git] / crates / 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             let text = param.syntax().text().to_string();
29             params.entry(text).or_insert(param);
30         })
31     };
32
33     for node in ctx.token.parent().ancestors() {
34         match_ast! {
35             match node {
36                 ast::SourceFile(it) => it.items().filter_map(|item| match item {
37                     ast::Item::Fn(it) => Some(it),
38                     _ => None,
39                 }).for_each(&mut process_fn),
40                 ast::ItemList(it) => it.items().filter_map(|item| match item {
41                     ast::Item::Fn(it) => Some(it),
42                     _ => None,
43                 }).for_each(&mut process_fn),
44                 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45                     ast::AssocItem::Fn(it) => Some(it),
46                     _ => None,
47                 }).for_each(&mut process_fn),
48                 _ => continue,
49             }
50         };
51     }
52
53     params
54         .into_iter()
55         .filter_map(|(label, param)| {
56             let lookup = param.pat()?.syntax().text().to_string();
57             Some((label, lookup))
58         })
59         .for_each(|(label, lookup)| {
60             CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61                 .kind(CompletionItemKind::Binding)
62                 .lookup_by(lookup)
63                 .add_to(acc)
64         });
65 }
66
67 #[cfg(test)]
68 mod tests {
69     use expect_test::{expect, Expect};
70
71     use crate::{test_utils::completion_list, CompletionKind};
72
73     fn check(ra_fixture: &str, expect: Expect) {
74         let actual = completion_list(ra_fixture, CompletionKind::Magic);
75         expect.assert_eq(&actual);
76     }
77
78     #[test]
79     fn test_param_completion_last_param() {
80         check(
81             r#"
82 fn foo(file_id: FileId) {}
83 fn bar(file_id: FileId) {}
84 fn baz(file$0) {}
85 "#,
86             expect![[r#"
87                 bn file_id: FileId
88             "#]],
89         );
90     }
91
92     #[test]
93     fn test_param_completion_nth_param() {
94         check(
95             r#"
96 fn foo(file_id: FileId) {}
97 fn baz(file$0, x: i32) {}
98 "#,
99             expect![[r#"
100                 bn file_id: FileId
101             "#]],
102         );
103     }
104
105     #[test]
106     fn test_param_completion_trait_param() {
107         check(
108             r#"
109 pub(crate) trait SourceRoot {
110     pub fn contains(&self, file_id: FileId) -> bool;
111     pub fn module_map(&self) -> &ModuleMap;
112     pub fn lines(&self, file_id: FileId) -> &LineIndex;
113     pub fn syntax(&self, file$0)
114 }
115 "#,
116             expect![[r#"
117                 bn file_id: FileId
118             "#]],
119         );
120     }
121
122     #[test]
123     fn completes_param_in_inner_function() {
124         check(
125             r#"
126 fn outer(text: String) {
127     fn inner($0)
128 }
129 "#,
130             expect![[r#"
131                 bn text: String
132             "#]],
133         )
134     }
135 }