]> git.lizzy.rs Git - rust.git/blob - crates/completion/src/render/function.rs
Align config's API with usage
[rust.git] / crates / completion / src / render / function.rs
1 //! Renderer for function calls.
2
3 use hir::{HasSource, Type};
4 use syntax::{ast::Fn, display::function_declaration};
5 use test_utils::mark;
6
7 use crate::{
8     item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9     render::{builder_ext::Params, RenderContext},
10 };
11
12 pub(crate) fn render_fn<'a>(
13     ctx: RenderContext<'a>,
14     import_to_add: Option<ImportEdit>,
15     local_name: Option<String>,
16     fn_: hir::Function,
17 ) -> Option<CompletionItem> {
18     let _p = profile::span("render_fn");
19     Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
20 }
21
22 #[derive(Debug)]
23 struct FunctionRender<'a> {
24     ctx: RenderContext<'a>,
25     name: String,
26     func: hir::Function,
27     ast_node: Fn,
28 }
29
30 impl<'a> FunctionRender<'a> {
31     fn new(
32         ctx: RenderContext<'a>,
33         local_name: Option<String>,
34         fn_: hir::Function,
35     ) -> Option<FunctionRender<'a>> {
36         let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
37         let ast_node = fn_.source(ctx.db())?.value;
38
39         Some(FunctionRender { ctx, name, func: fn_, ast_node })
40     }
41
42     fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43         let params = self.params();
44         CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45             .kind(self.kind())
46             .set_documentation(self.ctx.docs(self.func))
47             .set_deprecated(self.ctx.is_deprecated(self.func))
48             .detail(self.detail())
49             .add_call_parens(self.ctx.completion, self.name, params)
50             .add_import(import_to_add)
51             .build()
52     }
53
54     fn detail(&self) -> String {
55         function_declaration(&self.ast_node)
56     }
57
58     fn add_arg(&self, arg: &str, ty: &Type) -> String {
59         if let Some(derefed_ty) = ty.remove_ref() {
60             for (name, local) in self.ctx.completion.locals.iter() {
61                 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
62                     let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" };
63                     return format!("{}{}", mutability, arg);
64                 }
65             }
66         }
67         arg.to_string()
68     }
69
70     fn params(&self) -> Params {
71         let ast_params = match self.ast_node.param_list() {
72             Some(it) => it,
73             None => return Params::Named(Vec::new()),
74         };
75
76         let mut params_pats = Vec::new();
77         let params_ty = if self.ctx.completion.dot_receiver.is_some() {
78             self.func.method_params(self.ctx.db()).unwrap_or_default()
79         } else {
80             if let Some(s) = ast_params.self_param() {
81                 mark::hit!(parens_for_method_call_as_assoc_fn);
82                 params_pats.push(Some(s.to_string()));
83             }
84             self.func.assoc_fn_params(self.ctx.db())
85         };
86         params_pats
87             .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
88
89         let params = params_pats
90             .into_iter()
91             .zip(params_ty)
92             .flat_map(|(pat, param_ty)| {
93                 let pat = pat?;
94                 let name = pat;
95                 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
96                 Some(self.add_arg(arg, param_ty.ty()))
97             })
98             .collect();
99         Params::Named(params)
100     }
101
102     fn kind(&self) -> CompletionItemKind {
103         if self.func.self_param(self.ctx.db()).is_some() {
104             CompletionItemKind::Method
105         } else {
106             CompletionItemKind::Function
107         }
108     }
109 }
110
111 #[cfg(test)]
112 mod tests {
113     use test_utils::mark;
114
115     use crate::{
116         test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
117         CompletionConfig,
118     };
119
120     #[test]
121     fn inserts_parens_for_function_calls() {
122         mark::check!(inserts_parens_for_function_calls);
123         check_edit(
124             "no_args",
125             r#"
126 fn no_args() {}
127 fn main() { no_<|> }
128 "#,
129             r#"
130 fn no_args() {}
131 fn main() { no_args()$0 }
132 "#,
133         );
134
135         check_edit(
136             "with_args",
137             r#"
138 fn with_args(x: i32, y: String) {}
139 fn main() { with_<|> }
140 "#,
141             r#"
142 fn with_args(x: i32, y: String) {}
143 fn main() { with_args(${1:x}, ${2:y})$0 }
144 "#,
145         );
146
147         check_edit(
148             "foo",
149             r#"
150 struct S;
151 impl S {
152     fn foo(&self) {}
153 }
154 fn bar(s: &S) { s.f<|> }
155 "#,
156             r#"
157 struct S;
158 impl S {
159     fn foo(&self) {}
160 }
161 fn bar(s: &S) { s.foo()$0 }
162 "#,
163         );
164
165         check_edit(
166             "foo",
167             r#"
168 struct S {}
169 impl S {
170     fn foo(&self, x: i32) {}
171 }
172 fn bar(s: &S) {
173     s.f<|>
174 }
175 "#,
176             r#"
177 struct S {}
178 impl S {
179     fn foo(&self, x: i32) {}
180 }
181 fn bar(s: &S) {
182     s.foo(${1:x})$0
183 }
184 "#,
185         );
186     }
187
188     #[test]
189     fn parens_for_method_call_as_assoc_fn() {
190         mark::check!(parens_for_method_call_as_assoc_fn);
191         check_edit(
192             "foo",
193             r#"
194 struct S;
195 impl S {
196     fn foo(&self) {}
197 }
198 fn main() { S::f<|> }
199 "#,
200             r#"
201 struct S;
202 impl S {
203     fn foo(&self) {}
204 }
205 fn main() { S::foo(${1:&self})$0 }
206 "#,
207         );
208     }
209
210     #[test]
211     fn suppress_arg_snippets() {
212         mark::check!(suppress_arg_snippets);
213         check_edit_with_config(
214             CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
215             "with_args",
216             r#"
217 fn with_args(x: i32, y: String) {}
218 fn main() { with_<|> }
219 "#,
220             r#"
221 fn with_args(x: i32, y: String) {}
222 fn main() { with_args($0) }
223 "#,
224         );
225     }
226
227     #[test]
228     fn strips_underscores_from_args() {
229         check_edit(
230             "foo",
231             r#"
232 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
233 fn main() { f<|> }
234 "#,
235             r#"
236 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
237 fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
238 "#,
239         );
240     }
241
242     #[test]
243     fn insert_ref_when_matching_local_in_scope() {
244         check_edit(
245             "ref_arg",
246             r#"
247 struct Foo {}
248 fn ref_arg(x: &Foo) {}
249 fn main() {
250     let x = Foo {};
251     ref_ar<|>
252 }
253 "#,
254             r#"
255 struct Foo {}
256 fn ref_arg(x: &Foo) {}
257 fn main() {
258     let x = Foo {};
259     ref_arg(${1:&x})$0
260 }
261 "#,
262         );
263     }
264
265     #[test]
266     fn insert_mut_ref_when_matching_local_in_scope() {
267         check_edit(
268             "ref_arg",
269             r#"
270 struct Foo {}
271 fn ref_arg(x: &mut Foo) {}
272 fn main() {
273     let x = Foo {};
274     ref_ar<|>
275 }
276 "#,
277             r#"
278 struct Foo {}
279 fn ref_arg(x: &mut Foo) {}
280 fn main() {
281     let x = Foo {};
282     ref_arg(${1:&mut x})$0
283 }
284 "#,
285         );
286     }
287
288     #[test]
289     fn insert_ref_when_matching_local_in_scope_for_method() {
290         check_edit(
291             "apply_foo",
292             r#"
293 struct Foo {}
294 struct Bar {}
295 impl Bar {
296     fn apply_foo(&self, x: &Foo) {}
297 }
298
299 fn main() {
300     let x = Foo {};
301     let y = Bar {};
302     y.<|>
303 }
304 "#,
305             r#"
306 struct Foo {}
307 struct Bar {}
308 impl Bar {
309     fn apply_foo(&self, x: &Foo) {}
310 }
311
312 fn main() {
313     let x = Foo {};
314     let y = Bar {};
315     y.apply_foo(${1:&x})$0
316 }
317 "#,
318         );
319     }
320
321     #[test]
322     fn trim_mut_keyword_in_func_completion() {
323         check_edit(
324             "take_mutably",
325             r#"
326 fn take_mutably(mut x: &i32) {}
327
328 fn main() {
329     take_m<|>
330 }
331 "#,
332             r#"
333 fn take_mutably(mut x: &i32) {}
334
335 fn main() {
336     take_mutably(${1:x})$0
337 }
338 "#,
339         );
340     }
341 }