aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-11-03 07:54:45 +0000
committerGitHub <[email protected]>2020-11-03 07:54:45 +0000
commit07c7f35effe1a4602ba02baf9ff67a4eb214818f (patch)
tree1cf4253dc23366dfd20f36285036d73b168a526e /crates/completion/src
parent658e97a39e77bcb978697a66ddccd7e4b58990cf (diff)
parent8efe43245bc64ed27f88c333541b2cb7af2ce44c (diff)
Merge #6430
6430: Move completions rendering into a separate module r=popzxc a=popzxc This PR extracts rendering-related things from `Completions` structure to the new `render` module. `render` module declares a `Render` structure (which is a generic renderer interface), `RenderContext` (interface for data/methods not required for completions generating, but required for rendering), and a bunch of smaller `*Render` structures which encapsulate logic behind rendering a certain item. This is just a step in full separation direction, since the following this are still to be done: - Move some data from `CompletionContext` to the `RenderContext`; - Forbid any kind of rendering outside of `render` module; - Extract score computing into a separate module. This PR is already pretty big, so not to make it even harder to review I decided to split this process into several subsequent PRs. Co-authored-by: Igor Aleksanov <[email protected]>
Diffstat (limited to 'crates/completion/src')
-rw-r--r--crates/completion/src/completions.rs1371
-rw-r--r--crates/completion/src/context.rs13
-rw-r--r--crates/completion/src/lib.rs1
-rw-r--r--crates/completion/src/render.rs848
-rw-r--r--crates/completion/src/render/builder_ext.rs94
-rw-r--r--crates/completion/src/render/const_.rs55
-rw-r--r--crates/completion/src/render/enum_variant.rs180
-rw-r--r--crates/completion/src/render/function.rs303
-rw-r--r--crates/completion/src/render/macro_.rs216
-rw-r--r--crates/completion/src/render/type_alias.rs55
10 files changed, 1781 insertions, 1355 deletions
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index d5fb85b79..75dbb1a23 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -14,14 +14,16 @@ pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl; 14pub(crate) mod trait_impl;
15pub(crate) mod mod_; 15pub(crate) mod mod_;
16 16
17use hir::{HasAttrs, HasSource, HirDisplay, ModPath, Mutability, ScopeDef, StructKind, Type}; 17use hir::{ModPath, ScopeDef, Type};
18use itertools::Itertools;
19use syntax::{ast::NameOwner, display::*};
20use test_utils::mark;
21 18
22use crate::{ 19use crate::{
23 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 20 item::Builder,
24 CompletionScore, RootDatabase, 21 render::{
22 const_::render_const, enum_variant::render_enum_variant, function::render_fn,
23 macro_::render_macro, render_field, render_resolution, render_tuple_field,
24 type_alias::render_type_alias, RenderContext,
25 },
26 CompletionContext, CompletionItem,
25}; 27};
26 28
27/// Represents an in-progress set of completions being built. 29/// Represents an in-progress set of completions being built.
@@ -58,27 +60,13 @@ impl Completions {
58 } 60 }
59 61
60 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) { 62 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
61 let is_deprecated = is_deprecated(field, ctx.db); 63 let item = render_field(RenderContext::new(ctx), field, ty);
62 let name = field.name(ctx.db); 64 self.add(item);
63 let mut item =
64 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
65 .kind(CompletionItemKind::Field)
66 .detail(ty.display(ctx.db).to_string())
67 .set_documentation(field.docs(ctx.db))
68 .set_deprecated(is_deprecated);
69
70 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
71 item = item.set_score(score);
72 }
73
74 item.add_to(self);
75 } 65 }
76 66
77 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { 67 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
78 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string()) 68 let item = render_tuple_field(RenderContext::new(ctx), field, ty);
79 .kind(CompletionItemKind::Field) 69 self.add(item);
80 .detail(ty.display(ctx.db).to_string())
81 .add_to(self);
82 } 70 }
83 71
84 pub(crate) fn add_resolution( 72 pub(crate) fn add_resolution(
@@ -87,100 +75,9 @@ impl Completions {
87 local_name: String, 75 local_name: String,
88 resolution: &ScopeDef, 76 resolution: &ScopeDef,
89 ) { 77 ) {
90 use hir::ModuleDef::*; 78 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) {
91 79 self.add(item);
92 let completion_kind = match resolution {
93 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
94 _ => CompletionKind::Reference,
95 };
96
97 let kind = match resolution {
98 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
99 ScopeDef::ModuleDef(Function(func)) => {
100 self.add_function(ctx, *func, Some(local_name));
101 return;
102 }
103 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
104 // FIXME: add CompletionItemKind::Union
105 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
106 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
107
108 ScopeDef::ModuleDef(EnumVariant(var)) => {
109 self.add_enum_variant(ctx, *var, Some(local_name));
110 return;
111 }
112 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
113 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
114 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
115 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
116 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
117 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
118 ScopeDef::Local(..) => CompletionItemKind::Binding,
119 // (does this need its own kind?)
120 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
121 ScopeDef::MacroDef(mac) => {
122 self.add_macro(ctx, Some(local_name), *mac);
123 return;
124 }
125 ScopeDef::Unknown => {
126 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
127 .kind(CompletionItemKind::UnresolvedReference)
128 .add_to(self);
129 return;
130 }
131 };
132
133 let docs = match resolution {
134 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
135 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
136 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
137 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
138 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
139 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
140 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
141 _ => None,
142 };
143
144 let mut item = CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
145 if let ScopeDef::Local(local) = resolution {
146 let ty = local.ty(ctx.db);
147 if !ty.is_unknown() {
148 item = item.detail(ty.display(ctx.db).to_string());
149 }
150 };
151
152 let mut ref_match = None;
153 if let ScopeDef::Local(local) = resolution {
154 if let Some((active_name, active_type)) = ctx.active_name_and_type() {
155 let ty = local.ty(ctx.db);
156 if let Some(score) =
157 compute_score_from_active(&active_type, &active_name, &ty, &local_name)
158 {
159 item = item.set_score(score);
160 }
161 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
162 }
163 } 80 }
164
165 // Add `<>` for generic types
166 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
167 if let Some(cap) = ctx.config.snippet_cap {
168 let has_non_default_type_params = match resolution {
169 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
170 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
171 _ => false,
172 };
173 if has_non_default_type_params {
174 mark::hit!(inserts_angle_brackets_for_generics);
175 item = item
176 .lookup_by(local_name.clone())
177 .label(format!("{}<…>", local_name))
178 .insert_snippet(cap, format!("{}<$0>", local_name));
179 }
180 }
181 }
182
183 item.kind(kind).set_documentation(docs).set_ref_match(ref_match).add_to(self)
184 } 81 }
185 82
186 pub(crate) fn add_macro( 83 pub(crate) fn add_macro(
@@ -189,50 +86,13 @@ impl Completions {
189 name: Option<String>, 86 name: Option<String>,
190 macro_: hir::MacroDef, 87 macro_: hir::MacroDef,
191 ) { 88 ) {
192 // FIXME: Currently proc-macro do not have ast-node,
193 // such that it does not have source
194 if macro_.is_proc_macro() {
195 return;
196 }
197
198 let name = match name { 89 let name = match name {
199 Some(it) => it, 90 Some(it) => it,
200 None => return, 91 None => return,
201 }; 92 };
202 93 if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) {
203 let ast_node = macro_.source(ctx.db).value; 94 self.add(item);
204 let detail = macro_label(&ast_node); 95 }
205
206 let docs = macro_.docs(ctx.db);
207
208 let mut builder = CompletionItem::new(
209 CompletionKind::Reference,
210 ctx.source_range(),
211 &format!("{}!", name),
212 )
213 .kind(CompletionItemKind::Macro)
214 .set_documentation(docs.clone())
215 .set_deprecated(is_deprecated(macro_, ctx.db))
216 .detail(detail);
217
218 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
219 builder = match ctx.config.snippet_cap {
220 Some(cap) if needs_bang => {
221 let docs = docs.as_ref().map_or("", |s| s.as_str());
222 let (bra, ket) = guess_macro_braces(&name, docs);
223 builder
224 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
225 .label(format!("{}!{}…{}", name, bra, ket))
226 .lookup_by(format!("{}!", name))
227 }
228 None if needs_bang => builder.insert_text(format!("{}!", name)),
229 _ => {
230 mark::hit!(dont_insert_macro_call_parens_unncessary);
231 builder.insert_text(name)
232 }
233 };
234
235 self.add(builder.build());
236 } 96 }
237 97
238 pub(crate) fn add_function( 98 pub(crate) fn add_function(
@@ -241,82 +101,20 @@ impl Completions {
241 func: hir::Function, 101 func: hir::Function,
242 local_name: Option<String>, 102 local_name: Option<String>,
243 ) { 103 ) {
244 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String { 104 let item = render_fn(RenderContext::new(ctx), local_name, func);
245 if let Some(derefed_ty) = ty.remove_ref() { 105 self.add(item)
246 for (name, local) in ctx.locals.iter() {
247 if name == arg && local.ty(ctx.db) == derefed_ty {
248 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
249 + &arg.to_string();
250 }
251 }
252 }
253 arg.to_string()
254 };
255 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
256 let ast_node = func.source(ctx.db).value;
257
258 let mut builder =
259 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
260 .kind(if func.self_param(ctx.db).is_some() {
261 CompletionItemKind::Method
262 } else {
263 CompletionItemKind::Function
264 })
265 .set_documentation(func.docs(ctx.db))
266 .set_deprecated(is_deprecated(func, ctx.db))
267 .detail(function_declaration(&ast_node));
268
269 let params_ty = func.params(ctx.db);
270 let params = ast_node
271 .param_list()
272 .into_iter()
273 .flat_map(|it| it.params())
274 .zip(params_ty)
275 .flat_map(|(it, param_ty)| {
276 if let Some(pat) = it.pat() {
277 let name = pat.to_string();
278 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
279 return Some(add_arg(arg, param_ty.ty(), ctx));
280 }
281 None
282 })
283 .collect();
284
285 builder = builder.add_call_parens(ctx, name, Params::Named(params));
286
287 self.add(builder.build())
288 } 106 }
289 107
290 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 108 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
291 let ast_node = constant.source(ctx.db).value; 109 if let Some(item) = render_const(RenderContext::new(ctx), constant) {
292 let name = match ast_node.name() { 110 self.add(item);
293 Some(name) => name, 111 }
294 _ => return,
295 };
296 let detail = const_label(&ast_node);
297
298 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
299 .kind(CompletionItemKind::Const)
300 .set_documentation(constant.docs(ctx.db))
301 .set_deprecated(is_deprecated(constant, ctx.db))
302 .detail(detail)
303 .add_to(self);
304 } 112 }
305 113
306 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { 114 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
307 let type_def = type_alias.source(ctx.db).value; 115 if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) {
308 let name = match type_def.name() { 116 self.add(item)
309 Some(name) => name, 117 }
310 _ => return,
311 };
312 let detail = type_label(&type_def);
313
314 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
315 .kind(CompletionItemKind::TypeAlias)
316 .set_documentation(type_alias.docs(ctx.db))
317 .set_deprecated(is_deprecated(type_alias, ctx.db))
318 .detail(detail)
319 .add_to(self);
320 } 118 }
321 119
322 pub(crate) fn add_qualified_enum_variant( 120 pub(crate) fn add_qualified_enum_variant(
@@ -325,7 +123,8 @@ impl Completions {
325 variant: hir::EnumVariant, 123 variant: hir::EnumVariant,
326 path: ModPath, 124 path: ModPath,
327 ) { 125 ) {
328 self.add_enum_variant_impl(ctx, variant, None, Some(path)) 126 let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path));
127 self.add(item);
329 } 128 }
330 129
331 pub(crate) fn add_enum_variant( 130 pub(crate) fn add_enum_variant(
@@ -334,1119 +133,7 @@ impl Completions {
334 variant: hir::EnumVariant, 133 variant: hir::EnumVariant,
335 local_name: Option<String>, 134 local_name: Option<String>,
336 ) { 135 ) {
337 self.add_enum_variant_impl(ctx, variant, local_name, None) 136 let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None);
338 } 137 self.add(item);
339
340 fn add_enum_variant_impl(
341 &mut self,
342 ctx: &CompletionContext,
343 variant: hir::EnumVariant,
344 local_name: Option<String>,
345 path: Option<ModPath>,
346 ) {
347 let is_deprecated = is_deprecated(variant, ctx.db);
348 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
349 let (qualified_name, short_qualified_name) = match &path {
350 Some(path) => {
351 let full = path.to_string();
352 let short =
353 path.segments[path.segments.len().saturating_sub(2)..].iter().join("::");
354 (full, short)
355 }
356 None => (name.to_string(), name.to_string()),
357 };
358 let detail_types = variant
359 .fields(ctx.db)
360 .into_iter()
361 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
362 let variant_kind = variant.kind(ctx.db);
363 let detail = match variant_kind {
364 StructKind::Tuple | StructKind::Unit => format!(
365 "({})",
366 detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ")
367 ),
368 StructKind::Record => format!(
369 "{{ {} }}",
370 detail_types
371 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
372 .format(", ")
373 ),
374 };
375 let mut res = CompletionItem::new(
376 CompletionKind::Reference,
377 ctx.source_range(),
378 qualified_name.clone(),
379 )
380 .kind(CompletionItemKind::EnumVariant)
381 .set_documentation(variant.docs(ctx.db))
382 .set_deprecated(is_deprecated)
383 .detail(detail);
384
385 if variant_kind == StructKind::Tuple {
386 mark::hit!(inserts_parens_for_tuple_enums);
387 let params = Params::Anonymous(variant.fields(ctx.db).len());
388 res = res.add_call_parens(ctx, short_qualified_name, params)
389 } else if path.is_some() {
390 res = res.lookup_by(short_qualified_name);
391 }
392
393 res.add_to(self);
394 }
395}
396
397fn compute_score_from_active(
398 active_type: &Type,
399 active_name: &str,
400 ty: &Type,
401 name: &str,
402) -> Option<CompletionScore> {
403 // Compute score
404 // For the same type
405 if active_type != ty {
406 return None;
407 }
408
409 let mut res = CompletionScore::TypeMatch;
410
411 // If same type + same name then go top position
412 if active_name == name {
413 res = CompletionScore::TypeAndNameMatch
414 }
415
416 Some(res)
417}
418fn refed_type_matches(
419 active_type: &Type,
420 active_name: &str,
421 ty: &Type,
422 name: &str,
423) -> Option<(Mutability, CompletionScore)> {
424 let derefed_active = active_type.remove_ref()?;
425 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
426 Some((
427 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
428 score,
429 ))
430}
431
432fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<CompletionScore> {
433 let (active_name, active_type) = ctx.active_name_and_type()?;
434 compute_score_from_active(&active_type, &active_name, ty, name)
435}
436
437enum Params {
438 Named(Vec<String>),
439 Anonymous(usize),
440}
441
442impl Params {
443 fn len(&self) -> usize {
444 match self {
445 Params::Named(xs) => xs.len(),
446 Params::Anonymous(len) => *len,
447 }
448 }
449
450 fn is_empty(&self) -> bool {
451 self.len() == 0
452 }
453}
454
455impl Builder {
456 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
457 if !ctx.config.add_call_parenthesis {
458 return self;
459 }
460 if ctx.use_item_syntax.is_some() {
461 mark::hit!(no_parens_in_use_item);
462 return self;
463 }
464 if ctx.is_pattern_call {
465 mark::hit!(dont_duplicate_pattern_parens);
466 return self;
467 }
468 if ctx.is_call {
469 return self;
470 }
471
472 // Don't add parentheses if the expected type is some function reference.
473 if let Some(ty) = &ctx.expected_type {
474 if ty.is_fn() {
475 mark::hit!(no_call_parens_if_fn_ptr_needed);
476 return self;
477 }
478 }
479
480 let cap = match ctx.config.snippet_cap {
481 Some(it) => it,
482 None => return self,
483 };
484 // If not an import, add parenthesis automatically.
485 mark::hit!(inserts_parens_for_function_calls);
486
487 let (snippet, label) = if params.is_empty() {
488 (format!("{}()$0", name), format!("{}()", name))
489 } else {
490 self = self.trigger_call_info();
491 let snippet = match (ctx.config.add_call_argument_snippets, params) {
492 (true, Params::Named(params)) => {
493 let function_params_snippet =
494 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
495 f(&format_args!("${{{}:{}}}", index + 1, param_name))
496 });
497 format!("{}({})$0", name, function_params_snippet)
498 }
499 _ => {
500 mark::hit!(suppress_arg_snippets);
501 format!("{}($0)", name)
502 }
503 };
504
505 (snippet, format!("{}(…)", name))
506 };
507 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
508 }
509}
510
511fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
512 node.attrs(db).by_key("deprecated").exists()
513}
514
515fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
516 let mut votes = [0, 0, 0];
517 for (idx, s) in docs.match_indices(&macro_name) {
518 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
519 // Ensure to match the full word
520 if after.starts_with('!')
521 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
522 {
523 // It may have spaces before the braces like `foo! {}`
524 match after[1..].chars().find(|&c| !c.is_whitespace()) {
525 Some('{') => votes[0] += 1,
526 Some('[') => votes[1] += 1,
527 Some('(') => votes[2] += 1,
528 _ => {}
529 }
530 }
531 }
532
533 // Insert a space before `{}`.
534 // We prefer the last one when some votes equal.
535 let (_vote, (bra, ket)) = votes
536 .iter()
537 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
538 .max_by_key(|&(&vote, _)| vote)
539 .unwrap();
540 (*bra, *ket)
541}
542
543#[cfg(test)]
544mod tests {
545 use std::cmp::Reverse;
546
547 use expect_test::{expect, Expect};
548 use test_utils::mark;
549
550 use crate::{
551 test_utils::{check_edit, check_edit_with_config, do_completion, get_all_items},
552 CompletionConfig, CompletionKind, CompletionScore,
553 };
554
555 fn check(ra_fixture: &str, expect: Expect) {
556 let actual = do_completion(ra_fixture, CompletionKind::Reference);
557 expect.assert_debug_eq(&actual);
558 }
559
560 fn check_scores(ra_fixture: &str, expect: Expect) {
561 fn display_score(score: Option<CompletionScore>) -> &'static str {
562 match score {
563 Some(CompletionScore::TypeMatch) => "[type]",
564 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
565 None => "[]".into(),
566 }
567 }
568
569 let mut completions = get_all_items(CompletionConfig::default(), ra_fixture);
570 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
571 let actual = completions
572 .into_iter()
573 .filter(|it| it.completion_kind == CompletionKind::Reference)
574 .map(|it| {
575 let tag = it.kind().unwrap().tag();
576 let score = display_score(it.score());
577 format!("{} {} {}\n", tag, it.label(), score)
578 })
579 .collect::<String>();
580 expect.assert_eq(&actual);
581 }
582
583 #[test]
584 fn enum_detail_includes_record_fields() {
585 check(
586 r#"
587enum Foo { Foo { x: i32, y: i32 } }
588
589fn main() { Foo::Fo<|> }
590"#,
591 expect![[r#"
592 [
593 CompletionItem {
594 label: "Foo",
595 source_range: 54..56,
596 delete: 54..56,
597 insert: "Foo",
598 kind: EnumVariant,
599 detail: "{ x: i32, y: i32 }",
600 },
601 ]
602 "#]],
603 );
604 }
605
606 #[test]
607 fn enum_detail_doesnt_include_tuple_fields() {
608 check(
609 r#"
610enum Foo { Foo (i32, i32) }
611
612fn main() { Foo::Fo<|> }
613"#,
614 expect![[r#"
615 [
616 CompletionItem {
617 label: "Foo(…)",
618 source_range: 46..48,
619 delete: 46..48,
620 insert: "Foo($0)",
621 kind: EnumVariant,
622 lookup: "Foo",
623 detail: "(i32, i32)",
624 trigger_call_info: true,
625 },
626 ]
627 "#]],
628 );
629 }
630
631 #[test]
632 fn enum_detail_just_parentheses_for_unit() {
633 check(
634 r#"
635enum Foo { Foo }
636
637fn main() { Foo::Fo<|> }
638"#,
639 expect![[r#"
640 [
641 CompletionItem {
642 label: "Foo",
643 source_range: 35..37,
644 delete: 35..37,
645 insert: "Foo",
646 kind: EnumVariant,
647 detail: "()",
648 },
649 ]
650 "#]],
651 );
652 }
653
654 #[test]
655 fn lookup_enums_by_two_qualifiers() {
656 check(
657 r#"
658mod m {
659 pub enum Spam { Foo, Bar(i32) }
660}
661fn main() { let _: m::Spam = S<|> }
662"#,
663 expect![[r#"
664 [
665 CompletionItem {
666 label: "Spam::Bar(…)",
667 source_range: 75..76,
668 delete: 75..76,
669 insert: "Spam::Bar($0)",
670 kind: EnumVariant,
671 lookup: "Spam::Bar",
672 detail: "(i32)",
673 trigger_call_info: true,
674 },
675 CompletionItem {
676 label: "m",
677 source_range: 75..76,
678 delete: 75..76,
679 insert: "m",
680 kind: Module,
681 },
682 CompletionItem {
683 label: "m::Spam::Foo",
684 source_range: 75..76,
685 delete: 75..76,
686 insert: "m::Spam::Foo",
687 kind: EnumVariant,
688 lookup: "Spam::Foo",
689 detail: "()",
690 },
691 CompletionItem {
692 label: "main()",
693 source_range: 75..76,
694 delete: 75..76,
695 insert: "main()$0",
696 kind: Function,
697 lookup: "main",
698 detail: "fn main()",
699 },
700 ]
701 "#]],
702 )
703 }
704
705 #[test]
706 fn sets_deprecated_flag_in_items() {
707 check(
708 r#"
709#[deprecated]
710fn something_deprecated() {}
711#[deprecated(since = "1.0.0")]
712fn something_else_deprecated() {}
713
714fn main() { som<|> }
715"#,
716 expect![[r#"
717 [
718 CompletionItem {
719 label: "main()",
720 source_range: 121..124,
721 delete: 121..124,
722 insert: "main()$0",
723 kind: Function,
724 lookup: "main",
725 detail: "fn main()",
726 },
727 CompletionItem {
728 label: "something_deprecated()",
729 source_range: 121..124,
730 delete: 121..124,
731 insert: "something_deprecated()$0",
732 kind: Function,
733 lookup: "something_deprecated",
734 detail: "fn something_deprecated()",
735 deprecated: true,
736 },
737 CompletionItem {
738 label: "something_else_deprecated()",
739 source_range: 121..124,
740 delete: 121..124,
741 insert: "something_else_deprecated()$0",
742 kind: Function,
743 lookup: "something_else_deprecated",
744 detail: "fn something_else_deprecated()",
745 deprecated: true,
746 },
747 ]
748 "#]],
749 );
750
751 check(
752 r#"
753struct A { #[deprecated] the_field: u32 }
754fn foo() { A { the<|> } }
755"#,
756 expect![[r#"
757 [
758 CompletionItem {
759 label: "the_field",
760 source_range: 57..60,
761 delete: 57..60,
762 insert: "the_field",
763 kind: Field,
764 detail: "u32",
765 deprecated: true,
766 },
767 ]
768 "#]],
769 );
770 }
771
772 #[test]
773 fn renders_docs() {
774 check(
775 r#"
776struct S {
777 /// Field docs
778 foo:
779}
780impl S {
781 /// Method docs
782 fn bar(self) { self.<|> }
783}"#,
784 expect![[r#"
785 [
786 CompletionItem {
787 label: "bar()",
788 source_range: 94..94,
789 delete: 94..94,
790 insert: "bar()$0",
791 kind: Method,
792 lookup: "bar",
793 detail: "fn bar(self)",
794 documentation: Documentation(
795 "Method docs",
796 ),
797 },
798 CompletionItem {
799 label: "foo",
800 source_range: 94..94,
801 delete: 94..94,
802 insert: "foo",
803 kind: Field,
804 detail: "{unknown}",
805 documentation: Documentation(
806 "Field docs",
807 ),
808 },
809 ]
810 "#]],
811 );
812
813 check(
814 r#"
815use self::my<|>;
816
817/// mod docs
818mod my { }
819
820/// enum docs
821enum E {
822 /// variant docs
823 V
824}
825use self::E::*;
826"#,
827 expect![[r#"
828 [
829 CompletionItem {
830 label: "E",
831 source_range: 10..12,
832 delete: 10..12,
833 insert: "E",
834 kind: Enum,
835 documentation: Documentation(
836 "enum docs",
837 ),
838 },
839 CompletionItem {
840 label: "V",
841 source_range: 10..12,
842 delete: 10..12,
843 insert: "V",
844 kind: EnumVariant,
845 detail: "()",
846 documentation: Documentation(
847 "variant docs",
848 ),
849 },
850 CompletionItem {
851 label: "my",
852 source_range: 10..12,
853 delete: 10..12,
854 insert: "my",
855 kind: Module,
856 documentation: Documentation(
857 "mod docs",
858 ),
859 },
860 ]
861 "#]],
862 )
863 }
864
865 #[test]
866 fn dont_render_attrs() {
867 check(
868 r#"
869struct S;
870impl S {
871 #[inline]
872 fn the_method(&self) { }
873}
874fn foo(s: S) { s.<|> }
875"#,
876 expect![[r#"
877 [
878 CompletionItem {
879 label: "the_method()",
880 source_range: 81..81,
881 delete: 81..81,
882 insert: "the_method()$0",
883 kind: Method,
884 lookup: "the_method",
885 detail: "fn the_method(&self)",
886 },
887 ]
888 "#]],
889 )
890 }
891
892 #[test]
893 fn inserts_parens_for_function_calls() {
894 mark::check!(inserts_parens_for_function_calls);
895 check_edit(
896 "no_args",
897 r#"
898fn no_args() {}
899fn main() { no_<|> }
900"#,
901 r#"
902fn no_args() {}
903fn main() { no_args()$0 }
904"#,
905 );
906
907 check_edit(
908 "with_args",
909 r#"
910fn with_args(x: i32, y: String) {}
911fn main() { with_<|> }
912"#,
913 r#"
914fn with_args(x: i32, y: String) {}
915fn main() { with_args(${1:x}, ${2:y})$0 }
916"#,
917 );
918
919 check_edit(
920 "foo",
921 r#"
922struct S;
923impl S {
924 fn foo(&self) {}
925}
926fn bar(s: &S) { s.f<|> }
927"#,
928 r#"
929struct S;
930impl S {
931 fn foo(&self) {}
932}
933fn bar(s: &S) { s.foo()$0 }
934"#,
935 );
936
937 check_edit(
938 "foo",
939 r#"
940struct S {}
941impl S {
942 fn foo(&self, x: i32) {}
943}
944fn bar(s: &S) {
945 s.f<|>
946}
947"#,
948 r#"
949struct S {}
950impl S {
951 fn foo(&self, x: i32) {}
952}
953fn bar(s: &S) {
954 s.foo(${1:x})$0
955}
956"#,
957 );
958 }
959
960 #[test]
961 fn suppress_arg_snippets() {
962 mark::check!(suppress_arg_snippets);
963 check_edit_with_config(
964 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
965 "with_args",
966 r#"
967fn with_args(x: i32, y: String) {}
968fn main() { with_<|> }
969"#,
970 r#"
971fn with_args(x: i32, y: String) {}
972fn main() { with_args($0) }
973"#,
974 );
975 }
976
977 #[test]
978 fn strips_underscores_from_args() {
979 check_edit(
980 "foo",
981 r#"
982fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
983fn main() { f<|> }
984"#,
985 r#"
986fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
987fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
988"#,
989 );
990 }
991
992 #[test]
993 fn insert_ref_when_matching_local_in_scope() {
994 check_edit(
995 "ref_arg",
996 r#"
997struct Foo {}
998fn ref_arg(x: &Foo) {}
999fn main() {
1000 let x = Foo {};
1001 ref_ar<|>
1002}
1003"#,
1004 r#"
1005struct Foo {}
1006fn ref_arg(x: &Foo) {}
1007fn main() {
1008 let x = Foo {};
1009 ref_arg(${1:&x})$0
1010}
1011"#,
1012 );
1013 }
1014
1015 #[test]
1016 fn insert_mut_ref_when_matching_local_in_scope() {
1017 check_edit(
1018 "ref_arg",
1019 r#"
1020struct Foo {}
1021fn ref_arg(x: &mut Foo) {}
1022fn main() {
1023 let x = Foo {};
1024 ref_ar<|>
1025}
1026"#,
1027 r#"
1028struct Foo {}
1029fn ref_arg(x: &mut Foo) {}
1030fn main() {
1031 let x = Foo {};
1032 ref_arg(${1:&mut x})$0
1033}
1034"#,
1035 );
1036 }
1037
1038 #[test]
1039 fn insert_ref_when_matching_local_in_scope_for_method() {
1040 check_edit(
1041 "apply_foo",
1042 r#"
1043struct Foo {}
1044struct Bar {}
1045impl Bar {
1046 fn apply_foo(&self, x: &Foo) {}
1047}
1048
1049fn main() {
1050 let x = Foo {};
1051 let y = Bar {};
1052 y.<|>
1053}
1054"#,
1055 r#"
1056struct Foo {}
1057struct Bar {}
1058impl Bar {
1059 fn apply_foo(&self, x: &Foo) {}
1060}
1061
1062fn main() {
1063 let x = Foo {};
1064 let y = Bar {};
1065 y.apply_foo(${1:&x})$0
1066}
1067"#,
1068 );
1069 }
1070
1071 #[test]
1072 fn trim_mut_keyword_in_func_completion() {
1073 check_edit(
1074 "take_mutably",
1075 r#"
1076fn take_mutably(mut x: &i32) {}
1077
1078fn main() {
1079 take_m<|>
1080}
1081"#,
1082 r#"
1083fn take_mutably(mut x: &i32) {}
1084
1085fn main() {
1086 take_mutably(${1:x})$0
1087}
1088"#,
1089 );
1090 }
1091
1092 #[test]
1093 fn inserts_parens_for_tuple_enums() {
1094 mark::check!(inserts_parens_for_tuple_enums);
1095 check_edit(
1096 "Some",
1097 r#"
1098enum Option<T> { Some(T), None }
1099use Option::*;
1100fn main() -> Option<i32> {
1101 Som<|>
1102}
1103"#,
1104 r#"
1105enum Option<T> { Some(T), None }
1106use Option::*;
1107fn main() -> Option<i32> {
1108 Some($0)
1109}
1110"#,
1111 );
1112 check_edit(
1113 "Some",
1114 r#"
1115enum Option<T> { Some(T), None }
1116use Option::*;
1117fn main(value: Option<i32>) {
1118 match value {
1119 Som<|>
1120 }
1121}
1122"#,
1123 r#"
1124enum Option<T> { Some(T), None }
1125use Option::*;
1126fn main(value: Option<i32>) {
1127 match value {
1128 Some($0)
1129 }
1130}
1131"#,
1132 );
1133 }
1134
1135 #[test]
1136 fn dont_duplicate_pattern_parens() {
1137 mark::check!(dont_duplicate_pattern_parens);
1138 check_edit(
1139 "Var",
1140 r#"
1141enum E { Var(i32) }
1142fn main() {
1143 match E::Var(92) {
1144 E::<|>(92) => (),
1145 }
1146}
1147"#,
1148 r#"
1149enum E { Var(i32) }
1150fn main() {
1151 match E::Var(92) {
1152 E::Var(92) => (),
1153 }
1154}
1155"#,
1156 );
1157 }
1158
1159 #[test]
1160 fn no_call_parens_if_fn_ptr_needed() {
1161 mark::check!(no_call_parens_if_fn_ptr_needed);
1162 check_edit(
1163 "foo",
1164 r#"
1165fn foo(foo: u8, bar: u8) {}
1166struct ManualVtable { f: fn(u8, u8) }
1167
1168fn main() -> ManualVtable {
1169 ManualVtable { f: f<|> }
1170}
1171"#,
1172 r#"
1173fn foo(foo: u8, bar: u8) {}
1174struct ManualVtable { f: fn(u8, u8) }
1175
1176fn main() -> ManualVtable {
1177 ManualVtable { f: foo }
1178}
1179"#,
1180 );
1181 }
1182
1183 #[test]
1184 fn no_parens_in_use_item() {
1185 mark::check!(no_parens_in_use_item);
1186 check_edit(
1187 "foo",
1188 r#"
1189mod m { pub fn foo() {} }
1190use crate::m::f<|>;
1191"#,
1192 r#"
1193mod m { pub fn foo() {} }
1194use crate::m::foo;
1195"#,
1196 );
1197 }
1198
1199 #[test]
1200 fn no_parens_in_call() {
1201 check_edit(
1202 "foo",
1203 r#"
1204fn foo(x: i32) {}
1205fn main() { f<|>(); }
1206"#,
1207 r#"
1208fn foo(x: i32) {}
1209fn main() { foo(); }
1210"#,
1211 );
1212 check_edit(
1213 "foo",
1214 r#"
1215struct Foo;
1216impl Foo { fn foo(&self){} }
1217fn f(foo: &Foo) { foo.f<|>(); }
1218"#,
1219 r#"
1220struct Foo;
1221impl Foo { fn foo(&self){} }
1222fn f(foo: &Foo) { foo.foo(); }
1223"#,
1224 );
1225 }
1226
1227 #[test]
1228 fn inserts_angle_brackets_for_generics() {
1229 mark::check!(inserts_angle_brackets_for_generics);
1230 check_edit(
1231 "Vec",
1232 r#"
1233struct Vec<T> {}
1234fn foo(xs: Ve<|>)
1235"#,
1236 r#"
1237struct Vec<T> {}
1238fn foo(xs: Vec<$0>)
1239"#,
1240 );
1241 check_edit(
1242 "Vec",
1243 r#"
1244type Vec<T> = (T,);
1245fn foo(xs: Ve<|>)
1246"#,
1247 r#"
1248type Vec<T> = (T,);
1249fn foo(xs: Vec<$0>)
1250"#,
1251 );
1252 check_edit(
1253 "Vec",
1254 r#"
1255struct Vec<T = i128> {}
1256fn foo(xs: Ve<|>)
1257"#,
1258 r#"
1259struct Vec<T = i128> {}
1260fn foo(xs: Vec)
1261"#,
1262 );
1263 check_edit(
1264 "Vec",
1265 r#"
1266struct Vec<T> {}
1267fn foo(xs: Ve<|><i128>)
1268"#,
1269 r#"
1270struct Vec<T> {}
1271fn foo(xs: Vec<i128>)
1272"#,
1273 );
1274 }
1275
1276 #[test]
1277 fn dont_insert_macro_call_parens_unncessary() {
1278 mark::check!(dont_insert_macro_call_parens_unncessary);
1279 check_edit(
1280 "frobnicate!",
1281 r#"
1282//- /main.rs crate:main deps:foo
1283use foo::<|>;
1284//- /foo/lib.rs crate:foo
1285#[macro_export]
1286macro_rules frobnicate { () => () }
1287"#,
1288 r#"
1289use foo::frobnicate;
1290"#,
1291 );
1292
1293 check_edit(
1294 "frobnicate!",
1295 r#"
1296macro_rules frobnicate { () => () }
1297fn main() { frob<|>!(); }
1298"#,
1299 r#"
1300macro_rules frobnicate { () => () }
1301fn main() { frobnicate!(); }
1302"#,
1303 );
1304 }
1305
1306 #[test]
1307 fn active_param_score() {
1308 mark::check!(active_param_type_match);
1309 check_scores(
1310 r#"
1311struct S { foo: i64, bar: u32, baz: u32 }
1312fn test(bar: u32) { }
1313fn foo(s: S) { test(s.<|>) }
1314"#,
1315 expect![[r#"
1316 fd bar [type+name]
1317 fd baz [type]
1318 fd foo []
1319 "#]],
1320 );
1321 }
1322
1323 #[test]
1324 fn record_field_scores() {
1325 mark::check!(record_field_type_match);
1326 check_scores(
1327 r#"
1328struct A { foo: i64, bar: u32, baz: u32 }
1329struct B { x: (), y: f32, bar: u32 }
1330fn foo(a: A) { B { bar: a.<|> }; }
1331"#,
1332 expect![[r#"
1333 fd bar [type+name]
1334 fd baz [type]
1335 fd foo []
1336 "#]],
1337 )
1338 }
1339
1340 #[test]
1341 fn record_field_and_call_scores() {
1342 check_scores(
1343 r#"
1344struct A { foo: i64, bar: u32, baz: u32 }
1345struct B { x: (), y: f32, bar: u32 }
1346fn f(foo: i64) { }
1347fn foo(a: A) { B { bar: f(a.<|>) }; }
1348"#,
1349 expect![[r#"
1350 fd foo [type+name]
1351 fd bar []
1352 fd baz []
1353 "#]],
1354 );
1355 check_scores(
1356 r#"
1357struct A { foo: i64, bar: u32, baz: u32 }
1358struct B { x: (), y: f32, bar: u32 }
1359fn f(foo: i64) { }
1360fn foo(a: A) { f(B { bar: a.<|> }); }
1361"#,
1362 expect![[r#"
1363 fd bar [type+name]
1364 fd baz [type]
1365 fd foo []
1366 "#]],
1367 );
1368 }
1369
1370 #[test]
1371 fn prioritize_exact_ref_match() {
1372 check_scores(
1373 r#"
1374struct WorldSnapshot { _f: () };
1375fn go(world: &WorldSnapshot) { go(w<|>) }
1376"#,
1377 expect![[r#"
1378 bn world [type+name]
1379 st WorldSnapshot []
1380 fn go(…) []
1381 "#]],
1382 );
1383 }
1384
1385 #[test]
1386 fn too_many_arguments() {
1387 check_scores(
1388 r#"
1389struct Foo;
1390fn f(foo: &Foo) { f(foo, w<|>) }
1391"#,
1392 expect![[r#"
1393 st Foo []
1394 fn f(…) []
1395 bn foo []
1396 "#]],
1397 );
1398 }
1399
1400 #[test]
1401 fn guesses_macro_braces() {
1402 check_edit(
1403 "vec!",
1404 r#"
1405/// Creates a [`Vec`] containing the arguments.
1406///
1407/// ```
1408/// let v = vec![1, 2, 3];
1409/// assert_eq!(v[0], 1);
1410/// assert_eq!(v[1], 2);
1411/// assert_eq!(v[2], 3);
1412/// ```
1413macro_rules! vec { () => {} }
1414
1415fn fn main() { v<|> }
1416"#,
1417 r#"
1418/// Creates a [`Vec`] containing the arguments.
1419///
1420/// ```
1421/// let v = vec![1, 2, 3];
1422/// assert_eq!(v[0], 1);
1423/// assert_eq!(v[1], 2);
1424/// assert_eq!(v[2], 3);
1425/// ```
1426macro_rules! vec { () => {} }
1427
1428fn fn main() { vec![$0] }
1429"#,
1430 );
1431
1432 check_edit(
1433 "foo!",
1434 r#"
1435/// Foo
1436///
1437/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1438/// call as `let _=foo! { hello world };`
1439macro_rules! foo { () => {} }
1440fn main() { <|> }
1441"#,
1442 r#"
1443/// Foo
1444///
1445/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1446/// call as `let _=foo! { hello world };`
1447macro_rules! foo { () => {} }
1448fn main() { foo! {$0} }
1449"#,
1450 )
1451 } 138 }
1452} 139}
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index dca304a8f..bf70ee478 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -245,19 +245,6 @@ impl<'a> CompletionContext<'a> {
245 } 245 }
246 } 246 }
247 247
248 pub(crate) fn active_name_and_type(&self) -> Option<(String, Type)> {
249 if let Some(record_field) = &self.record_field_syntax {
250 mark::hit!(record_field_type_match);
251 let (struct_field, _local) = self.sema.resolve_record_field(record_field)?;
252 Some((struct_field.name(self.db).to_string(), struct_field.signature_ty(self.db)))
253 } else if let Some(active_parameter) = &self.active_parameter {
254 mark::hit!(active_param_type_match);
255 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
256 } else {
257 None
258 }
259 }
260
261 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 248 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
262 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 249 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
263 let syntax_element = NodeOrToken::Token(fake_ident_token); 250 let syntax_element = NodeOrToken::Token(fake_ident_token);
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 89c0a9978..cb6e0554e 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -7,6 +7,7 @@ mod patterns;
7mod generated_lint_completions; 7mod generated_lint_completions;
8#[cfg(test)] 8#[cfg(test)]
9mod test_utils; 9mod test_utils;
10mod render;
10 11
11mod completions; 12mod completions;
12 13
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
new file mode 100644
index 000000000..1fa02c375
--- /dev/null
+++ b/crates/completion/src/render.rs
@@ -0,0 +1,848 @@
1//! `render` module provides utilities for rendering completion suggestions
2//! into code pieces that will be presented to user.
3
4pub(crate) mod macro_;
5pub(crate) mod function;
6pub(crate) mod enum_variant;
7pub(crate) mod const_;
8pub(crate) mod type_alias;
9
10mod builder_ext;
11
12use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
13use ide_db::RootDatabase;
14use syntax::TextRange;
15use test_utils::mark;
16
17use crate::{
18 config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
19 CompletionScore,
20};
21
22use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro};
23
24pub(crate) fn render_field<'a>(
25 ctx: RenderContext<'a>,
26 field: hir::Field,
27 ty: &Type,
28) -> CompletionItem {
29 Render::new(ctx).add_field(field, ty)
30}
31
32pub(crate) fn render_tuple_field<'a>(
33 ctx: RenderContext<'a>,
34 field: usize,
35 ty: &Type,
36) -> CompletionItem {
37 Render::new(ctx).add_tuple_field(field, ty)
38}
39
40pub(crate) fn render_resolution<'a>(
41 ctx: RenderContext<'a>,
42 local_name: String,
43 resolution: &ScopeDef,
44) -> Option<CompletionItem> {
45 Render::new(ctx).render_resolution(local_name, resolution)
46}
47
48/// Interface for data and methods required for items rendering.
49#[derive(Debug)]
50pub(crate) struct RenderContext<'a> {
51 completion: &'a CompletionContext<'a>,
52}
53
54impl<'a> RenderContext<'a> {
55 pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
56 RenderContext { completion }
57 }
58
59 fn snippet_cap(&self) -> Option<SnippetCap> {
60 self.completion.config.snippet_cap.clone()
61 }
62
63 fn db(&self) -> &'a RootDatabase {
64 &self.completion.db
65 }
66
67 fn source_range(&self) -> TextRange {
68 self.completion.source_range()
69 }
70
71 fn is_deprecated(&self, node: impl HasAttrs) -> bool {
72 node.attrs(self.db()).by_key("deprecated").exists()
73 }
74
75 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
76 node.docs(self.db())
77 }
78
79 fn active_name_and_type(&self) -> Option<(String, Type)> {
80 if let Some(record_field) = &self.completion.record_field_syntax {
81 mark::hit!(record_field_type_match);
82 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
83 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
84 } else if let Some(active_parameter) = &self.completion.active_parameter {
85 mark::hit!(active_param_type_match);
86 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
87 } else {
88 None
89 }
90 }
91}
92
93/// Generic renderer for completion items.
94#[derive(Debug)]
95struct Render<'a> {
96 ctx: RenderContext<'a>,
97}
98
99impl<'a> Render<'a> {
100 fn new(ctx: RenderContext<'a>) -> Render<'a> {
101 Render { ctx }
102 }
103
104 fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem {
105 let is_deprecated = self.ctx.is_deprecated(field);
106 let name = field.name(self.ctx.db());
107 let mut item = CompletionItem::new(
108 CompletionKind::Reference,
109 self.ctx.source_range(),
110 name.to_string(),
111 )
112 .kind(CompletionItemKind::Field)
113 .detail(ty.display(self.ctx.db()).to_string())
114 .set_documentation(field.docs(self.ctx.db()))
115 .set_deprecated(is_deprecated);
116
117 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) {
118 item = item.set_score(score);
119 }
120
121 item.build()
122 }
123
124 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
125 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string())
126 .kind(CompletionItemKind::Field)
127 .detail(ty.display(self.ctx.db()).to_string())
128 .build()
129 }
130
131 fn render_resolution(
132 self,
133 local_name: String,
134 resolution: &ScopeDef,
135 ) -> Option<CompletionItem> {
136 use hir::ModuleDef::*;
137
138 let completion_kind = match resolution {
139 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
140 _ => CompletionKind::Reference,
141 };
142
143 let kind = match resolution {
144 ScopeDef::ModuleDef(Function(func)) => {
145 let item = render_fn(self.ctx, Some(local_name), *func);
146 return Some(item);
147 }
148 ScopeDef::ModuleDef(EnumVariant(var)) => {
149 let item = render_enum_variant(self.ctx, Some(local_name), *var, None);
150 return Some(item);
151 }
152 ScopeDef::MacroDef(mac) => {
153 let item = render_macro(self.ctx, local_name, *mac);
154 return item;
155 }
156
157 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
158 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
159 // FIXME: add CompletionItemKind::Union
160 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
161 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
162 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
163 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
164 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
165 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
166 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
167 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
168 ScopeDef::Local(..) => CompletionItemKind::Binding,
169 // (does this need its own kind?)
170 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
171 ScopeDef::Unknown => {
172 let item = CompletionItem::new(
173 CompletionKind::Reference,
174 self.ctx.source_range(),
175 local_name,
176 )
177 .kind(CompletionItemKind::UnresolvedReference)
178 .build();
179 return Some(item);
180 }
181 };
182
183 let docs = self.docs(resolution);
184
185 let mut item =
186 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
187 if let ScopeDef::Local(local) = resolution {
188 let ty = local.ty(self.ctx.db());
189 if !ty.is_unknown() {
190 item = item.detail(ty.display(self.ctx.db()).to_string());
191 }
192 };
193
194 let mut ref_match = None;
195 if let ScopeDef::Local(local) = resolution {
196 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() {
197 let ty = local.ty(self.ctx.db());
198 if let Some(score) =
199 compute_score_from_active(&active_type, &active_name, &ty, &local_name)
200 {
201 item = item.set_score(score);
202 }
203 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
204 }
205 }
206
207 // Add `<>` for generic types
208 if self.ctx.completion.is_path_type
209 && !self.ctx.completion.has_type_args
210 && self.ctx.completion.config.add_call_parenthesis
211 {
212 if let Some(cap) = self.ctx.snippet_cap() {
213 let has_non_default_type_params = match resolution {
214 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()),
215 ScopeDef::ModuleDef(TypeAlias(it)) => {
216 it.has_non_default_type_params(self.ctx.db())
217 }
218 _ => false,
219 };
220 if has_non_default_type_params {
221 mark::hit!(inserts_angle_brackets_for_generics);
222 item = item
223 .lookup_by(local_name.clone())
224 .label(format!("{}<…>", local_name))
225 .insert_snippet(cap, format!("{}<$0>", local_name));
226 }
227 }
228 }
229
230 let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build();
231 Some(item)
232 }
233
234 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
235 use hir::ModuleDef::*;
236 match resolution {
237 ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()),
238 ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()),
239 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(self.ctx.db()),
240 ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()),
241 ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()),
242 ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()),
243 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()),
244 _ => None,
245 }
246 }
247}
248
249fn compute_score_from_active(
250 active_type: &Type,
251 active_name: &str,
252 ty: &Type,
253 name: &str,
254) -> Option<CompletionScore> {
255 // Compute score
256 // For the same type
257 if active_type != ty {
258 return None;
259 }
260
261 let mut res = CompletionScore::TypeMatch;
262
263 // If same type + same name then go top position
264 if active_name == name {
265 res = CompletionScore::TypeAndNameMatch
266 }
267
268 Some(res)
269}
270fn refed_type_matches(
271 active_type: &Type,
272 active_name: &str,
273 ty: &Type,
274 name: &str,
275) -> Option<(Mutability, CompletionScore)> {
276 let derefed_active = active_type.remove_ref()?;
277 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
278 Some((
279 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
280 score,
281 ))
282}
283
284fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
285 let (active_name, active_type) = ctx.active_name_and_type()?;
286 compute_score_from_active(&active_type, &active_name, ty, name)
287}
288
289#[cfg(test)]
290mod tests {
291 use std::cmp::Reverse;
292
293 use expect_test::{expect, Expect};
294 use test_utils::mark;
295
296 use crate::{
297 test_utils::{check_edit, do_completion, get_all_items},
298 CompletionConfig, CompletionKind, CompletionScore,
299 };
300
301 fn check(ra_fixture: &str, expect: Expect) {
302 let actual = do_completion(ra_fixture, CompletionKind::Reference);
303 expect.assert_debug_eq(&actual);
304 }
305
306 fn check_scores(ra_fixture: &str, expect: Expect) {
307 fn display_score(score: Option<CompletionScore>) -> &'static str {
308 match score {
309 Some(CompletionScore::TypeMatch) => "[type]",
310 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
311 None => "[]".into(),
312 }
313 }
314
315 let mut completions = get_all_items(CompletionConfig::default(), ra_fixture);
316 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
317 let actual = completions
318 .into_iter()
319 .filter(|it| it.completion_kind == CompletionKind::Reference)
320 .map(|it| {
321 let tag = it.kind().unwrap().tag();
322 let score = display_score(it.score());
323 format!("{} {} {}\n", tag, it.label(), score)
324 })
325 .collect::<String>();
326 expect.assert_eq(&actual);
327 }
328
329 #[test]
330 fn enum_detail_includes_record_fields() {
331 check(
332 r#"
333enum Foo { Foo { x: i32, y: i32 } }
334
335fn main() { Foo::Fo<|> }
336"#,
337 expect![[r#"
338 [
339 CompletionItem {
340 label: "Foo",
341 source_range: 54..56,
342 delete: 54..56,
343 insert: "Foo",
344 kind: EnumVariant,
345 detail: "{ x: i32, y: i32 }",
346 },
347 ]
348 "#]],
349 );
350 }
351
352 #[test]
353 fn enum_detail_doesnt_include_tuple_fields() {
354 check(
355 r#"
356enum Foo { Foo (i32, i32) }
357
358fn main() { Foo::Fo<|> }
359"#,
360 expect![[r#"
361 [
362 CompletionItem {
363 label: "Foo(…)",
364 source_range: 46..48,
365 delete: 46..48,
366 insert: "Foo($0)",
367 kind: EnumVariant,
368 lookup: "Foo",
369 detail: "(i32, i32)",
370 trigger_call_info: true,
371 },
372 ]
373 "#]],
374 );
375 }
376
377 #[test]
378 fn enum_detail_just_parentheses_for_unit() {
379 check(
380 r#"
381enum Foo { Foo }
382
383fn main() { Foo::Fo<|> }
384"#,
385 expect![[r#"
386 [
387 CompletionItem {
388 label: "Foo",
389 source_range: 35..37,
390 delete: 35..37,
391 insert: "Foo",
392 kind: EnumVariant,
393 detail: "()",
394 },
395 ]
396 "#]],
397 );
398 }
399
400 #[test]
401 fn lookup_enums_by_two_qualifiers() {
402 check(
403 r#"
404mod m {
405 pub enum Spam { Foo, Bar(i32) }
406}
407fn main() { let _: m::Spam = S<|> }
408"#,
409 expect![[r#"
410 [
411 CompletionItem {
412 label: "Spam::Bar(…)",
413 source_range: 75..76,
414 delete: 75..76,
415 insert: "Spam::Bar($0)",
416 kind: EnumVariant,
417 lookup: "Spam::Bar",
418 detail: "(i32)",
419 trigger_call_info: true,
420 },
421 CompletionItem {
422 label: "m",
423 source_range: 75..76,
424 delete: 75..76,
425 insert: "m",
426 kind: Module,
427 },
428 CompletionItem {
429 label: "m::Spam::Foo",
430 source_range: 75..76,
431 delete: 75..76,
432 insert: "m::Spam::Foo",
433 kind: EnumVariant,
434 lookup: "Spam::Foo",
435 detail: "()",
436 },
437 CompletionItem {
438 label: "main()",
439 source_range: 75..76,
440 delete: 75..76,
441 insert: "main()$0",
442 kind: Function,
443 lookup: "main",
444 detail: "fn main()",
445 },
446 ]
447 "#]],
448 )
449 }
450
451 #[test]
452 fn sets_deprecated_flag_in_items() {
453 check(
454 r#"
455#[deprecated]
456fn something_deprecated() {}
457#[deprecated(since = "1.0.0")]
458fn something_else_deprecated() {}
459
460fn main() { som<|> }
461"#,
462 expect![[r#"
463 [
464 CompletionItem {
465 label: "main()",
466 source_range: 121..124,
467 delete: 121..124,
468 insert: "main()$0",
469 kind: Function,
470 lookup: "main",
471 detail: "fn main()",
472 },
473 CompletionItem {
474 label: "something_deprecated()",
475 source_range: 121..124,
476 delete: 121..124,
477 insert: "something_deprecated()$0",
478 kind: Function,
479 lookup: "something_deprecated",
480 detail: "fn something_deprecated()",
481 deprecated: true,
482 },
483 CompletionItem {
484 label: "something_else_deprecated()",
485 source_range: 121..124,
486 delete: 121..124,
487 insert: "something_else_deprecated()$0",
488 kind: Function,
489 lookup: "something_else_deprecated",
490 detail: "fn something_else_deprecated()",
491 deprecated: true,
492 },
493 ]
494 "#]],
495 );
496
497 check(
498 r#"
499struct A { #[deprecated] the_field: u32 }
500fn foo() { A { the<|> } }
501"#,
502 expect![[r#"
503 [
504 CompletionItem {
505 label: "the_field",
506 source_range: 57..60,
507 delete: 57..60,
508 insert: "the_field",
509 kind: Field,
510 detail: "u32",
511 deprecated: true,
512 },
513 ]
514 "#]],
515 );
516 }
517
518 #[test]
519 fn renders_docs() {
520 check(
521 r#"
522struct S {
523 /// Field docs
524 foo:
525}
526impl S {
527 /// Method docs
528 fn bar(self) { self.<|> }
529}"#,
530 expect![[r#"
531 [
532 CompletionItem {
533 label: "bar()",
534 source_range: 94..94,
535 delete: 94..94,
536 insert: "bar()$0",
537 kind: Method,
538 lookup: "bar",
539 detail: "fn bar(self)",
540 documentation: Documentation(
541 "Method docs",
542 ),
543 },
544 CompletionItem {
545 label: "foo",
546 source_range: 94..94,
547 delete: 94..94,
548 insert: "foo",
549 kind: Field,
550 detail: "{unknown}",
551 documentation: Documentation(
552 "Field docs",
553 ),
554 },
555 ]
556 "#]],
557 );
558
559 check(
560 r#"
561use self::my<|>;
562
563/// mod docs
564mod my { }
565
566/// enum docs
567enum E {
568 /// variant docs
569 V
570}
571use self::E::*;
572"#,
573 expect![[r#"
574 [
575 CompletionItem {
576 label: "E",
577 source_range: 10..12,
578 delete: 10..12,
579 insert: "E",
580 kind: Enum,
581 documentation: Documentation(
582 "enum docs",
583 ),
584 },
585 CompletionItem {
586 label: "V",
587 source_range: 10..12,
588 delete: 10..12,
589 insert: "V",
590 kind: EnumVariant,
591 detail: "()",
592 documentation: Documentation(
593 "variant docs",
594 ),
595 },
596 CompletionItem {
597 label: "my",
598 source_range: 10..12,
599 delete: 10..12,
600 insert: "my",
601 kind: Module,
602 documentation: Documentation(
603 "mod docs",
604 ),
605 },
606 ]
607 "#]],
608 )
609 }
610
611 #[test]
612 fn dont_render_attrs() {
613 check(
614 r#"
615struct S;
616impl S {
617 #[inline]
618 fn the_method(&self) { }
619}
620fn foo(s: S) { s.<|> }
621"#,
622 expect![[r#"
623 [
624 CompletionItem {
625 label: "the_method()",
626 source_range: 81..81,
627 delete: 81..81,
628 insert: "the_method()$0",
629 kind: Method,
630 lookup: "the_method",
631 detail: "fn the_method(&self)",
632 },
633 ]
634 "#]],
635 )
636 }
637
638 #[test]
639 fn no_call_parens_if_fn_ptr_needed() {
640 mark::check!(no_call_parens_if_fn_ptr_needed);
641 check_edit(
642 "foo",
643 r#"
644fn foo(foo: u8, bar: u8) {}
645struct ManualVtable { f: fn(u8, u8) }
646
647fn main() -> ManualVtable {
648 ManualVtable { f: f<|> }
649}
650"#,
651 r#"
652fn foo(foo: u8, bar: u8) {}
653struct ManualVtable { f: fn(u8, u8) }
654
655fn main() -> ManualVtable {
656 ManualVtable { f: foo }
657}
658"#,
659 );
660 }
661
662 #[test]
663 fn no_parens_in_use_item() {
664 mark::check!(no_parens_in_use_item);
665 check_edit(
666 "foo",
667 r#"
668mod m { pub fn foo() {} }
669use crate::m::f<|>;
670"#,
671 r#"
672mod m { pub fn foo() {} }
673use crate::m::foo;
674"#,
675 );
676 }
677
678 #[test]
679 fn no_parens_in_call() {
680 check_edit(
681 "foo",
682 r#"
683fn foo(x: i32) {}
684fn main() { f<|>(); }
685"#,
686 r#"
687fn foo(x: i32) {}
688fn main() { foo(); }
689"#,
690 );
691 check_edit(
692 "foo",
693 r#"
694struct Foo;
695impl Foo { fn foo(&self){} }
696fn f(foo: &Foo) { foo.f<|>(); }
697"#,
698 r#"
699struct Foo;
700impl Foo { fn foo(&self){} }
701fn f(foo: &Foo) { foo.foo(); }
702"#,
703 );
704 }
705
706 #[test]
707 fn inserts_angle_brackets_for_generics() {
708 mark::check!(inserts_angle_brackets_for_generics);
709 check_edit(
710 "Vec",
711 r#"
712struct Vec<T> {}
713fn foo(xs: Ve<|>)
714"#,
715 r#"
716struct Vec<T> {}
717fn foo(xs: Vec<$0>)
718"#,
719 );
720 check_edit(
721 "Vec",
722 r#"
723type Vec<T> = (T,);
724fn foo(xs: Ve<|>)
725"#,
726 r#"
727type Vec<T> = (T,);
728fn foo(xs: Vec<$0>)
729"#,
730 );
731 check_edit(
732 "Vec",
733 r#"
734struct Vec<T = i128> {}
735fn foo(xs: Ve<|>)
736"#,
737 r#"
738struct Vec<T = i128> {}
739fn foo(xs: Vec)
740"#,
741 );
742 check_edit(
743 "Vec",
744 r#"
745struct Vec<T> {}
746fn foo(xs: Ve<|><i128>)
747"#,
748 r#"
749struct Vec<T> {}
750fn foo(xs: Vec<i128>)
751"#,
752 );
753 }
754
755 #[test]
756 fn active_param_score() {
757 mark::check!(active_param_type_match);
758 check_scores(
759 r#"
760struct S { foo: i64, bar: u32, baz: u32 }
761fn test(bar: u32) { }
762fn foo(s: S) { test(s.<|>) }
763"#,
764 expect![[r#"
765 fd bar [type+name]
766 fd baz [type]
767 fd foo []
768 "#]],
769 );
770 }
771
772 #[test]
773 fn record_field_scores() {
774 mark::check!(record_field_type_match);
775 check_scores(
776 r#"
777struct A { foo: i64, bar: u32, baz: u32 }
778struct B { x: (), y: f32, bar: u32 }
779fn foo(a: A) { B { bar: a.<|> }; }
780"#,
781 expect![[r#"
782 fd bar [type+name]
783 fd baz [type]
784 fd foo []
785 "#]],
786 )
787 }
788
789 #[test]
790 fn record_field_and_call_scores() {
791 check_scores(
792 r#"
793struct A { foo: i64, bar: u32, baz: u32 }
794struct B { x: (), y: f32, bar: u32 }
795fn f(foo: i64) { }
796fn foo(a: A) { B { bar: f(a.<|>) }; }
797"#,
798 expect![[r#"
799 fd foo [type+name]
800 fd bar []
801 fd baz []
802 "#]],
803 );
804 check_scores(
805 r#"
806struct A { foo: i64, bar: u32, baz: u32 }
807struct B { x: (), y: f32, bar: u32 }
808fn f(foo: i64) { }
809fn foo(a: A) { f(B { bar: a.<|> }); }
810"#,
811 expect![[r#"
812 fd bar [type+name]
813 fd baz [type]
814 fd foo []
815 "#]],
816 );
817 }
818
819 #[test]
820 fn prioritize_exact_ref_match() {
821 check_scores(
822 r#"
823struct WorldSnapshot { _f: () };
824fn go(world: &WorldSnapshot) { go(w<|>) }
825"#,
826 expect![[r#"
827 bn world [type+name]
828 st WorldSnapshot []
829 fn go(…) []
830 "#]],
831 );
832 }
833
834 #[test]
835 fn too_many_arguments() {
836 check_scores(
837 r#"
838struct Foo;
839fn f(foo: &Foo) { f(foo, w<|>) }
840"#,
841 expect![[r#"
842 st Foo []
843 fn f(…) []
844 bn foo []
845 "#]],
846 );
847 }
848}
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
new file mode 100644
index 000000000..37b0d0459
--- /dev/null
+++ b/crates/completion/src/render/builder_ext.rs
@@ -0,0 +1,94 @@
1//! Extensions for `Builder` structure required for item rendering.
2
3use itertools::Itertools;
4use test_utils::mark;
5
6use crate::{item::Builder, CompletionContext};
7
8pub(super) enum Params {
9 Named(Vec<String>),
10 Anonymous(usize),
11}
12
13impl Params {
14 pub(super) fn len(&self) -> usize {
15 match self {
16 Params::Named(xs) => xs.len(),
17 Params::Anonymous(len) => *len,
18 }
19 }
20
21 pub(super) fn is_empty(&self) -> bool {
22 self.len() == 0
23 }
24}
25
26impl Builder {
27 pub(super) fn should_add_parems(&self, ctx: &CompletionContext) -> bool {
28 if !ctx.config.add_call_parenthesis {
29 return false;
30 }
31 if ctx.use_item_syntax.is_some() {
32 mark::hit!(no_parens_in_use_item);
33 return false;
34 }
35 if ctx.is_pattern_call {
36 mark::hit!(dont_duplicate_pattern_parens);
37 return false;
38 }
39 if ctx.is_call {
40 return false;
41 }
42
43 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false;
48 }
49 }
50
51 // Nothing prevents us from adding parentheses
52 true
53 }
54
55 pub(super) fn add_call_parens(
56 mut self,
57 ctx: &CompletionContext,
58 name: String,
59 params: Params,
60 ) -> Builder {
61 if !self.should_add_parems(ctx) {
62 return self;
63 }
64
65 let cap = match ctx.config.snippet_cap {
66 Some(it) => it,
67 None => return self,
68 };
69 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls);
71
72 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name))
74 } else {
75 self = self.trigger_call_info();
76 let snippet = match (ctx.config.add_call_argument_snippets, params) {
77 (true, Params::Named(params)) => {
78 let function_params_snippet =
79 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
80 f(&format_args!("${{{}:{}}}", index + 1, param_name))
81 });
82 format!("{}({})$0", name, function_params_snippet)
83 }
84 _ => {
85 mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name)
87 }
88 };
89
90 (snippet, format!("{}(…)", name))
91 };
92 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
93 }
94}
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
new file mode 100644
index 000000000..039bdabc0
--- /dev/null
+++ b/crates/completion/src/render/const_.rs
@@ -0,0 +1,55 @@
1//! Renderer for `const` fields.
2
3use hir::HasSource;
4use syntax::{
5 ast::{Const, NameOwner},
6 display::const_label,
7};
8
9use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind},
11 render::RenderContext,
12};
13
14pub(crate) fn render_const<'a>(
15 ctx: RenderContext<'a>,
16 const_: hir::Const,
17) -> Option<CompletionItem> {
18 ConstRender::new(ctx, const_).render()
19}
20
21#[derive(Debug)]
22struct ConstRender<'a> {
23 ctx: RenderContext<'a>,
24 const_: hir::Const,
25 ast_node: Const,
26}
27
28impl<'a> ConstRender<'a> {
29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> ConstRender<'a> {
30 let ast_node = const_.source(ctx.db()).value;
31 ConstRender { ctx, const_, ast_node }
32 }
33
34 fn render(self) -> Option<CompletionItem> {
35 let name = self.name()?;
36 let detail = self.detail();
37
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::Const)
40 .set_documentation(self.ctx.docs(self.const_))
41 .set_deprecated(self.ctx.is_deprecated(self.const_))
42 .detail(detail)
43 .build();
44
45 Some(item)
46 }
47
48 fn name(&self) -> Option<String> {
49 self.ast_node.name().map(|name| name.text().to_string())
50 }
51
52 fn detail(&self) -> String {
53 const_label(&self.ast_node)
54 }
55}
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
new file mode 100644
index 000000000..fd412ed0e
--- /dev/null
+++ b/crates/completion/src/render/enum_variant.rs
@@ -0,0 +1,180 @@
1//! Renderer for `enum` variants.
2
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use itertools::Itertools;
5use test_utils::mark;
6
7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 render::{builder_ext::Params, RenderContext},
10};
11
12pub(crate) fn render_enum_variant<'a>(
13 ctx: RenderContext<'a>,
14 local_name: Option<String>,
15 variant: hir::EnumVariant,
16 path: Option<ModPath>,
17) -> CompletionItem {
18 EnumVariantRender::new(ctx, local_name, variant, path).render()
19}
20
21#[derive(Debug)]
22struct EnumVariantRender<'a> {
23 ctx: RenderContext<'a>,
24 name: String,
25 variant: hir::EnumVariant,
26 path: Option<ModPath>,
27 qualified_name: String,
28 short_qualified_name: String,
29 variant_kind: StructKind,
30}
31
32impl<'a> EnumVariantRender<'a> {
33 fn new(
34 ctx: RenderContext<'a>,
35 local_name: Option<String>,
36 variant: hir::EnumVariant,
37 path: Option<ModPath>,
38 ) -> EnumVariantRender<'a> {
39 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string());
40 let variant_kind = variant.kind(ctx.db());
41
42 let (qualified_name, short_qualified_name) = match &path {
43 Some(path) => {
44 let full = path.to_string();
45 let short =
46 path.segments[path.segments.len().saturating_sub(2)..].iter().join("::");
47 (full, short)
48 }
49 None => (name.to_string(), name.to_string()),
50 };
51
52 EnumVariantRender {
53 ctx,
54 name,
55 variant,
56 path,
57 qualified_name,
58 short_qualified_name,
59 variant_kind,
60 }
61 }
62
63 fn render(self) -> CompletionItem {
64 let mut builder = CompletionItem::new(
65 CompletionKind::Reference,
66 self.ctx.source_range(),
67 self.qualified_name.clone(),
68 )
69 .kind(CompletionItemKind::EnumVariant)
70 .set_documentation(self.variant.docs(self.ctx.db()))
71 .set_deprecated(self.ctx.is_deprecated(self.variant))
72 .detail(self.detail());
73
74 if self.variant_kind == StructKind::Tuple {
75 mark::hit!(inserts_parens_for_tuple_enums);
76 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
77 builder =
78 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
79 } else if self.path.is_some() {
80 builder = builder.lookup_by(self.short_qualified_name);
81 }
82
83 builder.build()
84 }
85
86 fn detail(&self) -> String {
87 let detail_types = self
88 .variant
89 .fields(self.ctx.db())
90 .into_iter()
91 .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
92
93 match self.variant_kind {
94 StructKind::Tuple | StructKind::Unit => format!(
95 "({})",
96 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
97 ),
98 StructKind::Record => format!(
99 "{{ {} }}",
100 detail_types
101 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
102 .format(", ")
103 ),
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use test_utils::mark;
111
112 use crate::test_utils::check_edit;
113
114 #[test]
115 fn inserts_parens_for_tuple_enums() {
116 mark::check!(inserts_parens_for_tuple_enums);
117 check_edit(
118 "Some",
119 r#"
120enum Option<T> { Some(T), None }
121use Option::*;
122fn main() -> Option<i32> {
123 Som<|>
124}
125"#,
126 r#"
127enum Option<T> { Some(T), None }
128use Option::*;
129fn main() -> Option<i32> {
130 Some($0)
131}
132"#,
133 );
134 check_edit(
135 "Some",
136 r#"
137enum Option<T> { Some(T), None }
138use Option::*;
139fn main(value: Option<i32>) {
140 match value {
141 Som<|>
142 }
143}
144"#,
145 r#"
146enum Option<T> { Some(T), None }
147use Option::*;
148fn main(value: Option<i32>) {
149 match value {
150 Some($0)
151 }
152}
153"#,
154 );
155 }
156
157 #[test]
158 fn dont_duplicate_pattern_parens() {
159 mark::check!(dont_duplicate_pattern_parens);
160 check_edit(
161 "Var",
162 r#"
163enum E { Var(i32) }
164fn main() {
165 match E::Var(92) {
166 E::<|>(92) => (),
167 }
168}
169"#,
170 r#"
171enum E { Var(i32) }
172fn main() {
173 match E::Var(92) {
174 E::Var(92) => (),
175 }
176}
177"#,
178 );
179 }
180}
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}
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
new file mode 100644
index 000000000..96be59cc3
--- /dev/null
+++ b/crates/completion/src/render/macro_.rs
@@ -0,0 +1,216 @@
1//! Renderer for macro invocations.
2
3use hir::{Documentation, HasSource};
4use syntax::display::macro_label;
5use test_utils::mark;
6
7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 render::RenderContext,
10};
11
12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>,
14 name: String,
15 macro_: hir::MacroDef,
16) -> Option<CompletionItem> {
17 MacroRender::new(ctx, name, macro_).render()
18}
19
20#[derive(Debug)]
21struct MacroRender<'a> {
22 ctx: RenderContext<'a>,
23 name: String,
24 macro_: hir::MacroDef,
25 docs: Option<Documentation>,
26 bra: &'static str,
27 ket: &'static str,
28}
29
30impl<'a> MacroRender<'a> {
31 fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> {
32 let docs = ctx.docs(macro_);
33 let docs_str = docs.as_ref().map_or("", |s| s.as_str());
34 let (bra, ket) = guess_macro_braces(&name, docs_str);
35
36 MacroRender { ctx, name, macro_, docs, bra, ket }
37 }
38
39 fn render(&self) -> Option<CompletionItem> {
40 // FIXME: Currently proc-macro do not have ast-node,
41 // such that it does not have source
42 if self.macro_.is_proc_macro() {
43 return None;
44 }
45
46 let mut builder =
47 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
48 .kind(CompletionItemKind::Macro)
49 .set_documentation(self.docs.clone())
50 .set_deprecated(self.ctx.is_deprecated(self.macro_))
51 .detail(self.detail());
52
53 let needs_bang = self.needs_bang();
54 builder = match self.ctx.snippet_cap() {
55 Some(cap) if needs_bang => {
56 let snippet = self.snippet();
57 let lookup = self.lookup();
58 builder.insert_snippet(cap, snippet).lookup_by(lookup)
59 }
60 None if needs_bang => builder.insert_text(self.banged_name()),
61 _ => {
62 mark::hit!(dont_insert_macro_call_parens_unncessary);
63 builder.insert_text(&self.name)
64 }
65 };
66
67 Some(builder.build())
68 }
69
70 fn needs_bang(&self) -> bool {
71 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call
72 }
73
74 fn label(&self) -> String {
75 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
76 format!("{}!{}…{}", self.name, self.bra, self.ket)
77 } else {
78 self.banged_name()
79 }
80 }
81
82 fn snippet(&self) -> String {
83 format!("{}!{}$0{}", self.name, self.bra, self.ket)
84 }
85
86 fn lookup(&self) -> String {
87 self.banged_name()
88 }
89
90 fn banged_name(&self) -> String {
91 format!("{}!", self.name)
92 }
93
94 fn detail(&self) -> String {
95 let ast_node = self.macro_.source(self.ctx.db()).value;
96 macro_label(&ast_node)
97 }
98}
99
100fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
101 let mut votes = [0, 0, 0];
102 for (idx, s) in docs.match_indices(&macro_name) {
103 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
104 // Ensure to match the full word
105 if after.starts_with('!')
106 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
107 {
108 // It may have spaces before the braces like `foo! {}`
109 match after[1..].chars().find(|&c| !c.is_whitespace()) {
110 Some('{') => votes[0] += 1,
111 Some('[') => votes[1] += 1,
112 Some('(') => votes[2] += 1,
113 _ => {}
114 }
115 }
116 }
117
118 // Insert a space before `{}`.
119 // We prefer the last one when some votes equal.
120 let (_vote, (bra, ket)) = votes
121 .iter()
122 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
123 .max_by_key(|&(&vote, _)| vote)
124 .unwrap();
125 (*bra, *ket)
126}
127
128#[cfg(test)]
129mod tests {
130 use test_utils::mark;
131
132 use crate::test_utils::check_edit;
133
134 #[test]
135 fn dont_insert_macro_call_parens_unncessary() {
136 mark::check!(dont_insert_macro_call_parens_unncessary);
137 check_edit(
138 "frobnicate!",
139 r#"
140//- /main.rs crate:main deps:foo
141use foo::<|>;
142//- /foo/lib.rs crate:foo
143#[macro_export]
144macro_rules frobnicate { () => () }
145"#,
146 r#"
147use foo::frobnicate;
148"#,
149 );
150
151 check_edit(
152 "frobnicate!",
153 r#"
154macro_rules frobnicate { () => () }
155fn main() { frob<|>!(); }
156"#,
157 r#"
158macro_rules frobnicate { () => () }
159fn main() { frobnicate!(); }
160"#,
161 );
162 }
163
164 #[test]
165 fn guesses_macro_braces() {
166 check_edit(
167 "vec!",
168 r#"
169/// Creates a [`Vec`] containing the arguments.
170///
171/// ```
172/// let v = vec![1, 2, 3];
173/// assert_eq!(v[0], 1);
174/// assert_eq!(v[1], 2);
175/// assert_eq!(v[2], 3);
176/// ```
177macro_rules! vec { () => {} }
178
179fn fn main() { v<|> }
180"#,
181 r#"
182/// Creates a [`Vec`] containing the arguments.
183///
184/// ```
185/// let v = vec![1, 2, 3];
186/// assert_eq!(v[0], 1);
187/// assert_eq!(v[1], 2);
188/// assert_eq!(v[2], 3);
189/// ```
190macro_rules! vec { () => {} }
191
192fn fn main() { vec![$0] }
193"#,
194 );
195
196 check_edit(
197 "foo!",
198 r#"
199/// Foo
200///
201/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
202/// call as `let _=foo! { hello world };`
203macro_rules! foo { () => {} }
204fn main() { <|> }
205"#,
206 r#"
207/// Foo
208///
209/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
210/// call as `let _=foo! { hello world };`
211macro_rules! foo { () => {} }
212fn main() { foo! {$0} }
213"#,
214 )
215 }
216}
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
new file mode 100644
index 000000000..9605c7fa9
--- /dev/null
+++ b/crates/completion/src/render/type_alias.rs
@@ -0,0 +1,55 @@
1//! Renderer for type aliases.
2
3use hir::HasSource;
4use syntax::{
5 ast::{NameOwner, TypeAlias},
6 display::type_label,
7};
8
9use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind},
11 render::RenderContext,
12};
13
14pub(crate) fn render_type_alias<'a>(
15 ctx: RenderContext<'a>,
16 type_alias: hir::TypeAlias,
17) -> Option<CompletionItem> {
18 TypeAliasRender::new(ctx, type_alias).render()
19}
20
21#[derive(Debug)]
22struct TypeAliasRender<'a> {
23 ctx: RenderContext<'a>,
24 type_alias: hir::TypeAlias,
25 ast_node: TypeAlias,
26}
27
28impl<'a> TypeAliasRender<'a> {
29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> TypeAliasRender<'a> {
30 let ast_node = type_alias.source(ctx.db()).value;
31 TypeAliasRender { ctx, type_alias, ast_node }
32 }
33
34 fn render(self) -> Option<CompletionItem> {
35 let name = self.name()?;
36 let detail = self.detail();
37
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::TypeAlias)
40 .set_documentation(self.ctx.docs(self.type_alias))
41 .set_deprecated(self.ctx.is_deprecated(self.type_alias))
42 .detail(detail)
43 .build();
44
45 Some(item)
46 }
47
48 fn name(&self) -> Option<String> {
49 self.ast_node.name().map(|name| name.text().to_string())
50 }
51
52 fn detail(&self) -> String {
53 type_label(&self.ast_node)
54 }
55}