diff options
Diffstat (limited to 'crates/ra_ide_api/src/completion/presentation.rs')
-rw-r--r-- | crates/ra_ide_api/src/completion/presentation.rs | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs new file mode 100644 index 000000000..6454436c9 --- /dev/null +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -0,0 +1,245 @@ | |||
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, 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 | } | ||