aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/complete_fn_param.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/complete_fn_param.rs')
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs136
1 files changed, 136 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
new file mode 100644
index 000000000..502458706
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -0,0 +1,136 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast, match_ast, AstNode};
4use rustc_hash::FxHashMap;
5
6use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
7
8/// Complete repeated parameters, both name and type. For example, if all
9/// functions in a file have a `spam: &mut Spam` parameter, a completion with
10/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
11/// suggested.
12pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
13 if !ctx.is_param {
14 return;
15 }
16
17 let mut params = FxHashMap::default();
18 for node in ctx.token.parent().ancestors() {
19 match_ast! {
20 match node {
21 ast::SourceFile(it) => { process(it, &mut params) },
22 ast::ItemList(it) => { process(it, &mut params) },
23 _ => (),
24 }
25 }
26 }
27 params
28 .into_iter()
29 .filter_map(|(label, (count, param))| {
30 let lookup = param.pat()?.syntax().text().to_string();
31 if count < 2 {
32 None
33 } else {
34 Some((label, lookup))
35 }
36 })
37 .for_each(|(label, lookup)| {
38 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
39 .lookup_by(lookup)
40 .add_to(acc)
41 });
42
43 fn process<N: ast::FnDefOwner>(node: N, params: &mut FxHashMap<String, (u32, ast::Param)>) {
44 node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each(
45 |param| {
46 let text = param.syntax().text().to_string();
47 params.entry(text).or_insert((0, param)).0 += 1;
48 },
49 )
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::completion::{do_completion, CompletionItem, CompletionKind};
56 use insta::assert_debug_snapshot;
57
58 fn do_magic_completion(code: &str) -> Vec<CompletionItem> {
59 do_completion(code, CompletionKind::Magic)
60 }
61
62 #[test]
63 fn test_param_completion_last_param() {
64 assert_debug_snapshot!(
65 do_magic_completion(
66 r"
67 fn foo(file_id: FileId) {}
68 fn bar(file_id: FileId) {}
69 fn baz(file<|>) {}
70 ",
71 ),
72 @r###"
73 [
74 CompletionItem {
75 label: "file_id: FileId",
76 source_range: [110; 114),
77 delete: [110; 114),
78 insert: "file_id: FileId",
79 lookup: "file_id",
80 },
81 ]
82 "###
83 );
84 }
85
86 #[test]
87 fn test_param_completion_nth_param() {
88 assert_debug_snapshot!(
89 do_magic_completion(
90 r"
91 fn foo(file_id: FileId) {}
92 fn bar(file_id: FileId) {}
93 fn baz(file<|>, x: i32) {}
94 ",
95 ),
96 @r###"
97 [
98 CompletionItem {
99 label: "file_id: FileId",
100 source_range: [110; 114),
101 delete: [110; 114),
102 insert: "file_id: FileId",
103 lookup: "file_id",
104 },
105 ]
106 "###
107 );
108 }
109
110 #[test]
111 fn test_param_completion_trait_param() {
112 assert_debug_snapshot!(
113 do_magic_completion(
114 r"
115 pub(crate) trait SourceRoot {
116 pub fn contains(&self, file_id: FileId) -> bool;
117 pub fn module_map(&self) -> &ModuleMap;
118 pub fn lines(&self, file_id: FileId) -> &LineIndex;
119 pub fn syntax(&self, file<|>)
120 }
121 ",
122 ),
123 @r###"
124 [
125 CompletionItem {
126 label: "file_id: FileId",
127 source_range: [289; 293),
128 delete: [289; 293),
129 insert: "file_id: FileId",
130 lookup: "file_id",
131 },
132 ]
133 "###
134 );
135 }
136}