diff options
Diffstat (limited to 'crates/completion/src/render/function.rs')
-rw-r--r-- | crates/completion/src/render/function.rs | 87 |
1 files changed, 87 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..16f15e22c --- /dev/null +++ b/crates/completion/src/render/function.rs | |||
@@ -0,0 +1,87 @@ | |||
1 | use hir::{Documentation, HasAttrs, HasSource, Type}; | ||
2 | use syntax::{ast::Fn, display::function_declaration}; | ||
3 | |||
4 | use crate::{ | ||
5 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
6 | render::{builder_ext::Params, RenderContext}, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug)] | ||
10 | pub(crate) struct FunctionRender<'a> { | ||
11 | ctx: RenderContext<'a>, | ||
12 | name: String, | ||
13 | fn_: hir::Function, | ||
14 | ast_node: Fn, | ||
15 | } | ||
16 | |||
17 | impl<'a> FunctionRender<'a> { | ||
18 | pub(crate) fn new( | ||
19 | ctx: RenderContext<'a>, | ||
20 | local_name: Option<String>, | ||
21 | fn_: hir::Function, | ||
22 | ) -> FunctionRender<'a> { | ||
23 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); | ||
24 | let ast_node = fn_.source(ctx.db()).value; | ||
25 | |||
26 | FunctionRender { ctx, name, fn_, ast_node } | ||
27 | } | ||
28 | |||
29 | pub(crate) fn render(self) -> CompletionItem { | ||
30 | let params = self.params(); | ||
31 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | ||
32 | .kind(self.kind()) | ||
33 | .set_documentation(self.docs()) | ||
34 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) | ||
35 | .detail(self.detail()) | ||
36 | .add_call_parens(self.ctx.completion, self.name, params) | ||
37 | .build() | ||
38 | } | ||
39 | |||
40 | fn detail(&self) -> String { | ||
41 | function_declaration(&self.ast_node) | ||
42 | } | ||
43 | |||
44 | fn add_arg(&self, arg: &str, ty: &Type) -> String { | ||
45 | if let Some(derefed_ty) = ty.remove_ref() { | ||
46 | for (name, local) in self.ctx.completion.locals.iter() { | ||
47 | if name == arg && local.ty(self.ctx.db()) == derefed_ty { | ||
48 | return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string() | ||
49 | + &arg.to_string(); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | arg.to_string() | ||
54 | } | ||
55 | |||
56 | fn params(&self) -> Params { | ||
57 | let params_ty = self.fn_.params(self.ctx.db()); | ||
58 | let params = self | ||
59 | .ast_node | ||
60 | .param_list() | ||
61 | .into_iter() | ||
62 | .flat_map(|it| it.params()) | ||
63 | .zip(params_ty) | ||
64 | .flat_map(|(it, param_ty)| { | ||
65 | if let Some(pat) = it.pat() { | ||
66 | let name = pat.to_string(); | ||
67 | let arg = name.trim_start_matches("mut ").trim_start_matches('_'); | ||
68 | return Some(self.add_arg(arg, param_ty.ty())); | ||
69 | } | ||
70 | None | ||
71 | }) | ||
72 | .collect(); | ||
73 | Params::Named(params) | ||
74 | } | ||
75 | |||
76 | fn kind(&self) -> CompletionItemKind { | ||
77 | if self.fn_.self_param(self.ctx.db()).is_some() { | ||
78 | CompletionItemKind::Method | ||
79 | } else { | ||
80 | CompletionItemKind::Function | ||
81 | } | ||
82 | } | ||
83 | |||
84 | fn docs(&self) -> Option<Documentation> { | ||
85 | self.fn_.docs(self.ctx.db()) | ||
86 | } | ||
87 | } | ||