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