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