]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_api/src/completion/presentation.rs
move more code 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,
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
95         let mut builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name)
96             .kind(if sig.has_self_param() {
97                 CompletionItemKind::Method
98             } else {
99                 CompletionItemKind::Function
100             })
101             .set_documentation(func.docs(ctx.db))
102             .set_detail(function_item_label(ctx, func));
103         // If not an import, add parenthesis automatically.
104         if ctx.use_item_syntax.is_none() && !ctx.is_call {
105             tested_by!(inserts_parens_for_function_calls);
106             let snippet =
107                 if sig.params().is_empty() || sig.has_self_param() && sig.params().len() == 1 {
108                     format!("{}()$0", sig.name())
109                 } else {
110                     format!("{}($0)", sig.name())
111                 };
112             builder = builder.insert_snippet(snippet);
113         }
114         self.add(builder)
115     }
116
117     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
118         let (_file_id, cosnt_def) = constant.source(ctx.db);
119         let name = match cosnt_def.name() {
120             Some(name) => name,
121             _ => return,
122         };
123         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
124             .from_const(ctx, constant)
125             .add_to(self);
126     }
127
128     pub(crate) fn add_type(&mut self, ctx: &CompletionContext, type_alias: hir::Type) {
129         let (_file_id, type_def) = type_alias.source(ctx.db);
130         let name = match type_def.name() {
131             Some(name) => name,
132             _ => return,
133         };
134         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
135             .from_type(ctx, type_alias)
136             .add_to(self);
137     }
138
139     pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
140         let name = match variant.name(ctx.db) {
141             Some(it) => it,
142             None => return,
143         };
144         let detail_types = variant.fields(ctx.db).into_iter().map(|field| field.ty(ctx.db));
145         let detail = join(detail_types).separator(", ").surround_with("(", ")").to_string();
146
147         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
148             .kind(CompletionItemKind::EnumVariant)
149             .set_documentation(variant.docs(ctx.db))
150             .detail(detail)
151             .add_to(self);
152     }
153 }
154
155 fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> {
156     let node = function.source(ctx.db).1;
157     function_label(&node)
158 }
159
160 #[cfg(test)]
161 mod tests {
162     use test_utils::covers;
163
164     use crate::completion::{CompletionKind, completion_item::check_completion};
165
166     fn check_reference_completion(code: &str, expected_completions: &str) {
167         check_completion(code, expected_completions, CompletionKind::Reference);
168     }
169
170     #[test]
171     fn inserts_parens_for_function_calls() {
172         covers!(inserts_parens_for_function_calls);
173         check_reference_completion(
174             "inserts_parens_for_function_calls1",
175             r"
176             fn no_args() {}
177             fn main() { no_<|> }
178             ",
179         );
180         check_reference_completion(
181             "inserts_parens_for_function_calls2",
182             r"
183             fn with_args(x: i32, y: String) {}
184             fn main() { with_<|> }
185             ",
186         );
187         check_reference_completion(
188             "inserts_parens_for_function_calls3",
189             r"
190             struct S {}
191             impl S {
192                 fn foo(&self) {}
193             }
194             fn bar(s: &S) {
195                 s.f<|>
196             }
197             ",
198         )
199     }
200
201     #[test]
202     fn dont_render_function_parens_in_use_item() {
203         check_reference_completion(
204             "dont_render_function_parens_in_use_item",
205             "
206             //- /lib.rs
207             mod m { pub fn foo() {} }
208             use crate::m::f<|>;
209             ",
210         )
211     }
212
213     #[test]
214     fn dont_render_function_parens_if_already_call() {
215         check_reference_completion(
216             "dont_render_function_parens_if_already_call",
217             "
218             //- /lib.rs
219             fn frobnicate() {}
220             fn main() {
221                 frob<|>();
222             }
223             ",
224         );
225         check_reference_completion(
226             "dont_render_function_parens_if_already_call_assoc_fn",
227             "
228             //- /lib.rs
229             struct Foo {}
230             impl Foo { fn new() -> Foo {} }
231             fn main() {
232                 Foo::ne<|>();
233             }
234             ",
235         )
236     }
237
238 }