]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_api/src/completion/presentation.rs
move the rest of presentation to presentation
[rust.git] / crates / ra_ide_api / src / completion / presentation.rs
1 //! This modules takes care of rendering various defenitions as completion items.
2 use join_to_string::join;
3 use test_utils::tested_by;
4 use hir::{Docs, PerNs, Resolution};
5 use ra_syntax::ast::NameOwner;
6
7 use crate::completion::{
8     Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem,
9     function_label, const_label, type_label,
10 };
11
12 impl Completions {
13     pub(crate) fn add_field(
14         &mut self,
15         ctx: &CompletionContext,
16         field: hir::StructField,
17         substs: &hir::Substs,
18     ) {
19         CompletionItem::new(
20             CompletionKind::Reference,
21             ctx.source_range(),
22             field.name(ctx.db).to_string(),
23         )
24         .kind(CompletionItemKind::Field)
25         .detail(field.ty(ctx.db).subst(substs).to_string())
26         .set_documentation(field.docs(ctx.db))
27         .add_to(self);
28     }
29
30     pub(crate) fn add_pos_field(&mut self, ctx: &CompletionContext, field: usize, ty: &hir::Ty) {
31         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
32             .kind(CompletionItemKind::Field)
33             .detail(ty.to_string())
34             .add_to(self);
35     }
36
37     pub(crate) fn add_resolution(
38         &mut self,
39         ctx: &CompletionContext,
40         local_name: String,
41         resolution: &PerNs<Resolution>,
42     ) {
43         use hir::ModuleDef::*;
44
45         let def = resolution.as_ref().take_types().or_else(|| resolution.as_ref().take_values());
46         let def = match def {
47             None => {
48                 self.add(CompletionItem::new(
49                     CompletionKind::Reference,
50                     ctx.source_range(),
51                     local_name,
52                 ));
53                 return;
54             }
55             Some(it) => it,
56         };
57         let (kind, docs) = match def {
58             Resolution::Def(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)),
59             Resolution::Def(Function(func)) => {
60                 return self.add_function_with_name(ctx, Some(local_name), *func);
61             }
62             Resolution::Def(Struct(it)) => (CompletionItemKind::Struct, it.docs(ctx.db)),
63             Resolution::Def(Enum(it)) => (CompletionItemKind::Enum, it.docs(ctx.db)),
64             Resolution::Def(EnumVariant(it)) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)),
65             Resolution::Def(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)),
66             Resolution::Def(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)),
67             Resolution::Def(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)),
68             Resolution::Def(Type(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)),
69             Resolution::GenericParam(..) => (CompletionItemKind::TypeParam, None),
70             Resolution::LocalBinding(..) => (CompletionItemKind::Binding, None),
71             Resolution::SelfType(..) => (
72                 CompletionItemKind::TypeParam, // (does this need its own kind?)
73                 None,
74             ),
75         };
76         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
77             .kind(kind)
78             .set_documentation(docs)
79             .add_to(self)
80     }
81
82     pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) {
83         self.add_function_with_name(ctx, None, func)
84     }
85
86     fn add_function_with_name(
87         &mut self,
88         ctx: &CompletionContext,
89         name: Option<String>,
90         func: hir::Function,
91     ) {
92         let sig = func.signature(ctx.db);
93         let name = name.unwrap_or_else(|| sig.name().to_string());
94         let (_, ast_node) = func.source(ctx.db);
95         let detail = function_label(&ast_node);
96
97         let mut builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name)
98             .kind(if sig.has_self_param() {
99                 CompletionItemKind::Method
100             } else {
101                 CompletionItemKind::Function
102             })
103             .set_documentation(func.docs(ctx.db))
104             .set_detail(detail);
105         // If not an import, add parenthesis automatically.
106         if ctx.use_item_syntax.is_none() && !ctx.is_call {
107             tested_by!(inserts_parens_for_function_calls);
108             let snippet =
109                 if sig.params().is_empty() || sig.has_self_param() && sig.params().len() == 1 {
110                     format!("{}()$0", sig.name())
111                 } else {
112                     format!("{}($0)", sig.name())
113                 };
114             builder = builder.insert_snippet(snippet);
115         }
116         self.add(builder)
117     }
118
119     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
120         let (_file_id, ast_node) = constant.source(ctx.db);
121         let name = match ast_node.name() {
122             Some(name) => name,
123             _ => return,
124         };
125         let (_, ast_node) = constant.source(ctx.db);
126         let detail = const_label(&ast_node);
127
128         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
129             .kind(CompletionItemKind::Const)
130             .set_documentation(constant.docs(ctx.db))
131             .detail(detail)
132             .add_to(self);
133     }
134
135     pub(crate) fn add_type(&mut self, ctx: &CompletionContext, type_alias: hir::Type) {
136         let (_file_id, type_def) = type_alias.source(ctx.db);
137         let name = match type_def.name() {
138             Some(name) => name,
139             _ => return,
140         };
141         let (_, ast_node) = type_alias.source(ctx.db);
142         let detail = type_label(&ast_node);
143
144         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
145             .kind(CompletionItemKind::TypeAlias)
146             .set_documentation(type_alias.docs(ctx.db))
147             .detail(detail)
148             .add_to(self);
149     }
150
151     pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
152         let name = match variant.name(ctx.db) {
153             Some(it) => it,
154             None => return,
155         };
156         let detail_types = variant.fields(ctx.db).into_iter().map(|field| field.ty(ctx.db));
157         let detail = join(detail_types).separator(", ").surround_with("(", ")").to_string();
158
159         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
160             .kind(CompletionItemKind::EnumVariant)
161             .set_documentation(variant.docs(ctx.db))
162             .detail(detail)
163             .add_to(self);
164     }
165 }
166
167 #[cfg(test)]
168 mod tests {
169     use test_utils::covers;
170
171     use crate::completion::{CompletionKind, completion_item::check_completion};
172
173     fn check_reference_completion(code: &str, expected_completions: &str) {
174         check_completion(code, expected_completions, CompletionKind::Reference);
175     }
176
177     #[test]
178     fn inserts_parens_for_function_calls() {
179         covers!(inserts_parens_for_function_calls);
180         check_reference_completion(
181             "inserts_parens_for_function_calls1",
182             r"
183             fn no_args() {}
184             fn main() { no_<|> }
185             ",
186         );
187         check_reference_completion(
188             "inserts_parens_for_function_calls2",
189             r"
190             fn with_args(x: i32, y: String) {}
191             fn main() { with_<|> }
192             ",
193         );
194         check_reference_completion(
195             "inserts_parens_for_function_calls3",
196             r"
197             struct S {}
198             impl S {
199                 fn foo(&self) {}
200             }
201             fn bar(s: &S) {
202                 s.f<|>
203             }
204             ",
205         )
206     }
207
208     #[test]
209     fn dont_render_function_parens_in_use_item() {
210         check_reference_completion(
211             "dont_render_function_parens_in_use_item",
212             "
213             //- /lib.rs
214             mod m { pub fn foo() {} }
215             use crate::m::f<|>;
216             ",
217         )
218     }
219
220     #[test]
221     fn dont_render_function_parens_if_already_call() {
222         check_reference_completion(
223             "dont_render_function_parens_if_already_call",
224             "
225             //- /lib.rs
226             fn frobnicate() {}
227             fn main() {
228                 frob<|>();
229             }
230             ",
231         );
232         check_reference_completion(
233             "dont_render_function_parens_if_already_call_assoc_fn",
234             "
235             //- /lib.rs
236             struct Foo {}
237             impl Foo { fn new() -> Foo {} }
238             fn main() {
239                 Foo::ne<|>();
240             }
241             ",
242         )
243     }
244
245 }