aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
authorGalilée 'Bill' Enguehard <[email protected]>2020-05-21 22:27:38 +0100
committerGalilée 'Bill' Enguehard <[email protected]>2020-05-21 22:27:38 +0100
commit7fece3bdd2450c0807f7dd742239cae95f0cc65e (patch)
tree866c4db826c959e79c63a6727bdb9f2c61e6fc4f /crates/ra_ide/src/completion
parentdb926218b2082077750291f8426ddd28b284cd08 (diff)
parent59732df8d40dfadc6dcf5951265416576399712a (diff)
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer into modname_spacing
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs300
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs256
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs63
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs46
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs29
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs14
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs7
-rw-r--r--crates/ra_ide/src/completion/presentation.rs82
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs2
10 files changed, 736 insertions, 89 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 8bf952798..f17266221 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -3,25 +3,29 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use super::completion_context::CompletionContext; 6use ra_syntax::{ast, AstNode, SyntaxKind};
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use rustc_hash::FxHashSet;
8use ra_syntax::{ 8
9 ast::{Attr, AttrKind}, 9use crate::completion::{
10 AstNode, 10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
11}; 12};
12 13
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 if !ctx.is_attribute { 15 let attribute = ctx.attribute_under_caret.as_ref()?;
15 return;
16 }
17 16
18 let is_inner = ctx 17 match (attribute.path(), attribute.input()) {
19 .original_token 18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
20 .ancestors() 19 if path.to_string() == "derive" =>
21 .find_map(Attr::cast) 20 {
22 .map(|attr| attr.kind() == AttrKind::Inner) 21 complete_derive(acc, ctx, token_tree)
23 .unwrap_or(false); 22 }
23 _ => complete_attribute_start(acc, ctx, attribute),
24 }
25 Some(())
26}
24 27
28fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
25 for attr_completion in ATTRIBUTES { 29 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new( 30 let mut item = CompletionItem::new(
27 CompletionKind::Attribute, 31 CompletionKind::Attribute,
@@ -37,7 +41,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
37 _ => {} 41 _ => {}
38 } 42 }
39 43
40 if is_inner || !attr_completion.should_be_inner { 44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner {
41 acc.add(item); 45 acc.add(item);
42 } 46 }
43 } 47 }
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126 }, 130 },
127]; 131];
128 132
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
134 if let Ok(existing_derives) = parse_derive_input(derive_input) {
135 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
136 .into_iter()
137 .filter(|completion| !existing_derives.contains(completion.label))
138 {
139 let mut label = derive_completion.label.to_owned();
140 for dependency in derive_completion
141 .dependencies
142 .into_iter()
143 .filter(|&&dependency| !existing_derives.contains(dependency))
144 {
145 label.push_str(", ");
146 label.push_str(dependency);
147 }
148 acc.add(
149 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
150 .kind(CompletionItemKind::Attribute),
151 );
152 }
153
154 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
155 acc.add(
156 CompletionItem::new(
157 CompletionKind::Attribute,
158 ctx.source_range(),
159 custom_derive_name,
160 )
161 .kind(CompletionItemKind::Attribute),
162 );
163 }
164 }
165}
166
167fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
168 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
169 (Some(left_paren), Some(right_paren))
170 if left_paren.kind() == SyntaxKind::L_PAREN
171 && right_paren.kind() == SyntaxKind::R_PAREN =>
172 {
173 let mut input_derives = FxHashSet::default();
174 let mut current_derive = String::new();
175 for token in derive_input
176 .syntax()
177 .children_with_tokens()
178 .filter_map(|token| token.into_token())
179 .skip_while(|token| token != &left_paren)
180 .skip(1)
181 .take_while(|token| token != &right_paren)
182 {
183 if SyntaxKind::COMMA == token.kind() {
184 if !current_derive.is_empty() {
185 input_derives.insert(current_derive);
186 current_derive = String::new();
187 }
188 } else {
189 current_derive.push_str(token.to_string().trim());
190 }
191 }
192
193 if !current_derive.is_empty() {
194 input_derives.insert(current_derive);
195 }
196 Ok(input_derives)
197 }
198 _ => Err(()),
199 }
200}
201
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() {
207 result.insert(name.to_string());
208 }
209 }
210 });
211 result
212}
213
214struct DeriveCompletion {
215 label: &'static str,
216 dependencies: &'static [&'static str],
217}
218
219/// Standard Rust derives and the information about their dependencies
220/// (the dependencies are needed so that the main derive don't break the compilation when added)
221const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
222 DeriveCompletion { label: "Clone", dependencies: &[] },
223 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
224 DeriveCompletion { label: "Debug", dependencies: &[] },
225 DeriveCompletion { label: "Default", dependencies: &[] },
226 DeriveCompletion { label: "Hash", dependencies: &[] },
227 DeriveCompletion { label: "PartialEq", dependencies: &[] },
228 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
229 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
230 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
231];
232
129#[cfg(test)] 233#[cfg(test)]
130mod tests { 234mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
@@ -136,6 +240,170 @@ mod tests {
136 } 240 }
137 241
138 #[test] 242 #[test]
243 fn empty_derive_completion() {
244 assert_debug_snapshot!(
245 do_attr_completion(
246 r"
247 #[derive(<|>)]
248 struct Test {}
249 ",
250 ),
251 @r###"
252 [
253 CompletionItem {
254 label: "Clone",
255 source_range: 30..30,
256 delete: 30..30,
257 insert: "Clone",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 );
319 }
320
321 #[test]
322 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!(
324 do_attr_completion(
325 r"
326 #[derive{<|>)]
327 struct Test {}
328 ",
329 ),
330 @"[]"
331 );
332 }
333
334 #[test]
335 fn derive_with_input_completion() {
336 assert_debug_snapshot!(
337 do_attr_completion(
338 r"
339 #[derive(serde::Serialize, PartialEq, <|>)]
340 struct Test {}
341 ",
342 ),
343 @r###"
344 [
345 CompletionItem {
346 label: "Clone",
347 source_range: 59..59,
348 delete: 59..59,
349 insert: "Clone",
350 kind: Attribute,
351 },
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 }
405
406 #[test]
139 fn test_attribute_completion() { 407 fn test_attribute_completion() {
140 assert_debug_snapshot!( 408 assert_debug_snapshot!(
141 do_attr_completion( 409 do_attr_completion(
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 6a0f0c72e..f2a52a407 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -14,6 +14,7 @@ use crate::{
14 }, 14 },
15 CompletionItem, 15 CompletionItem,
16}; 16};
17use ra_assists::utils::TryEnum;
17 18
18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
19 if !ctx.config.enable_postfix_completions { 20 if !ctx.config.enable_postfix_completions {
@@ -37,8 +38,53 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
37 Some(it) => it, 38 Some(it) => it,
38 None => return, 39 None => return,
39 }; 40 };
41 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
42 if let Some(try_enum) = &try_enum {
43 match try_enum {
44 TryEnum::Result => {
45 postfix_snippet(
46 ctx,
47 cap,
48 &dot_receiver,
49 "ifl",
50 "if let Ok {}",
51 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
52 )
53 .add_to(acc);
40 54
41 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 55 postfix_snippet(
56 ctx,
57 cap,
58 &dot_receiver,
59 "while",
60 "while let Ok {}",
61 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
62 )
63 .add_to(acc);
64 }
65 TryEnum::Option => {
66 postfix_snippet(
67 ctx,
68 cap,
69 &dot_receiver,
70 "ifl",
71 "if let Some {}",
72 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
73 )
74 .add_to(acc);
75
76 postfix_snippet(
77 ctx,
78 cap,
79 &dot_receiver,
80 "while",
81 "while let Some {}",
82 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
83 )
84 .add_to(acc);
85 }
86 }
87 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
42 postfix_snippet( 88 postfix_snippet(
43 ctx, 89 ctx,
44 cap, 90 cap,
@@ -58,7 +104,6 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
58 ) 104 )
59 .add_to(acc); 105 .add_to(acc);
60 } 106 }
61
62 // !&&&42 is a compiler error, ergo process it before considering the references 107 // !&&&42 is a compiler error, ergo process it before considering the references
63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) 108 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc); 109 .add_to(acc);
@@ -80,16 +125,45 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
80 let dot_receiver = include_references(dot_receiver); 125 let dot_receiver = include_references(dot_receiver);
81 let receiver_text = 126 let receiver_text =
82 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); 127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
83 128 match try_enum {
84 postfix_snippet( 129 Some(try_enum) => {
85 ctx, 130 match try_enum {
86 cap, 131 TryEnum::Result => {
87 &dot_receiver, 132 postfix_snippet(
88 "match", 133 ctx,
89 "match expr {}", 134 cap,
90 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 135 &dot_receiver,
91 ) 136 "match",
92 .add_to(acc); 137 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text),
139 )
140 .add_to(acc);
141 }
142 TryEnum::Option => {
143 postfix_snippet(
144 ctx,
145 cap,
146 &dot_receiver,
147 "match",
148 "match expr {}",
149 &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text),
150 )
151 .add_to(acc);
152 }
153 }
154 }
155 None => {
156 postfix_snippet(
157 ctx,
158 cap,
159 &dot_receiver,
160 "match",
161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
163 )
164 .add_to(acc);
165 }
166 }
93 167
94 postfix_snippet( 168 postfix_snippet(
95 ctx, 169 ctx,
@@ -236,6 +310,164 @@ mod tests {
236 } 310 }
237 311
238 #[test] 312 #[test]
313 fn postfix_completion_works_for_option() {
314 assert_debug_snapshot!(
315 do_postfix_completion(
316 r#"
317 enum Option<T> {
318 Some(T),
319 None,
320 }
321
322 fn main() {
323 let bar = Option::Some(true);
324 bar.<|>
325 }
326 "#,
327 ),
328 @r###"
329 [
330 CompletionItem {
331 label: "box",
332 source_range: 210..210,
333 delete: 206..210,
334 insert: "Box::new(bar)",
335 detail: "Box::new(expr)",
336 },
337 CompletionItem {
338 label: "dbg",
339 source_range: 210..210,
340 delete: 206..210,
341 insert: "dbg!(bar)",
342 detail: "dbg!(expr)",
343 },
344 CompletionItem {
345 label: "ifl",
346 source_range: 210..210,
347 delete: 206..210,
348 insert: "if let Some($1) = bar {\n $0\n}",
349 detail: "if let Some {}",
350 },
351 CompletionItem {
352 label: "match",
353 source_range: 210..210,
354 delete: 206..210,
355 insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}",
356 detail: "match expr {}",
357 },
358 CompletionItem {
359 label: "not",
360 source_range: 210..210,
361 delete: 206..210,
362 insert: "!bar",
363 detail: "!expr",
364 },
365 CompletionItem {
366 label: "ref",
367 source_range: 210..210,
368 delete: 206..210,
369 insert: "&bar",
370 detail: "&expr",
371 },
372 CompletionItem {
373 label: "refm",
374 source_range: 210..210,
375 delete: 206..210,
376 insert: "&mut bar",
377 detail: "&mut expr",
378 },
379 CompletionItem {
380 label: "while",
381 source_range: 210..210,
382 delete: 206..210,
383 insert: "while let Some($1) = bar {\n $0\n}",
384 detail: "while let Some {}",
385 },
386 ]
387 "###
388 );
389 }
390
391 #[test]
392 fn postfix_completion_works_for_result() {
393 assert_debug_snapshot!(
394 do_postfix_completion(
395 r#"
396 enum Result<T, E> {
397 Ok(T),
398 Err(E),
399 }
400
401 fn main() {
402 let bar = Result::Ok(true);
403 bar.<|>
404 }
405 "#,
406 ),
407 @r###"
408 [
409 CompletionItem {
410 label: "box",
411 source_range: 211..211,
412 delete: 207..211,
413 insert: "Box::new(bar)",
414 detail: "Box::new(expr)",
415 },
416 CompletionItem {
417 label: "dbg",
418 source_range: 211..211,
419 delete: 207..211,
420 insert: "dbg!(bar)",
421 detail: "dbg!(expr)",
422 },
423 CompletionItem {
424 label: "ifl",
425 source_range: 211..211,
426 delete: 207..211,
427 insert: "if let Ok($1) = bar {\n $0\n}",
428 detail: "if let Ok {}",
429 },
430 CompletionItem {
431 label: "match",
432 source_range: 211..211,
433 delete: 207..211,
434 insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}",
435 detail: "match expr {}",
436 },
437 CompletionItem {
438 label: "not",
439 source_range: 211..211,
440 delete: 207..211,
441 insert: "!bar",
442 detail: "!expr",
443 },
444 CompletionItem {
445 label: "ref",
446 source_range: 211..211,
447 delete: 207..211,
448 insert: "&bar",
449 detail: "&expr",
450 },
451 CompletionItem {
452 label: "refm",
453 source_range: 211..211,
454 delete: 207..211,
455 insert: "&mut bar",
456 detail: "&mut expr",
457 },
458 CompletionItem {
459 label: "while",
460 source_range: 211..211,
461 delete: 207..211,
462 insert: "while let Ok($1) = bar {\n $0\n}",
463 detail: "while let Ok {}",
464 },
465 ]
466 "###
467 );
468 }
469
470 #[test]
239 fn some_postfix_completions_ignored() { 471 fn some_postfix_completions_ignored() {
240 assert_debug_snapshot!( 472 assert_debug_snapshot!(
241 do_postfix_completion( 473 do_postfix_completion(
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index aa56a5cd8..02ac0166b 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -2,20 +2,25 @@
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use test_utils::tested_by; 5use rustc_hash::FxHashSet;
6use test_utils::mark;
6 7
7use crate::completion::{CompletionContext, Completions}; 8use crate::completion::{CompletionContext, Completions};
8use rustc_hash::FxHashSet;
9 9
10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_prefix { 11 let path = match &ctx.path_prefix {
12 Some(path) => path.clone(), 12 Some(path) => path.clone(),
13 _ => return, 13 None => return,
14 }; 14 };
15
16 if ctx.attribute_under_caret.is_some() {
17 return;
18 }
19
15 let scope = ctx.scope(); 20 let scope = ctx.scope();
16 let context_module = scope.module(); 21 let context_module = scope.module();
17 22
18 let res = match scope.resolve_hir_path(&path) { 23 let res = match scope.resolve_hir_path_qualifier(&path) {
19 Some(res) => res, 24 Some(res) => res,
20 None => return, 25 None => return,
21 }; 26 };
@@ -35,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
35 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 40 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
36 if name_ref.syntax().text() == name.to_string().as_str() { 41 if name_ref.syntax().text() == name.to_string().as_str() {
37 // for `use self::foo<|>`, don't suggest `foo` as a completion 42 // for `use self::foo<|>`, don't suggest `foo` as a completion
38 tested_by!(dont_complete_current_use); 43 mark::hit!(dont_complete_current_use);
39 continue; 44 continue;
40 } 45 }
41 } 46 }
@@ -79,7 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
79 }); 84 });
80 85
81 // Iterate assoc types separately 86 // Iterate assoc types separately
82 ty.iterate_impl_items(ctx.db, krate, |item| { 87 ty.iterate_assoc_items(ctx.db, krate, |item| {
83 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 88 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
84 return None; 89 return None;
85 } 90 }
@@ -142,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
142 147
143#[cfg(test)] 148#[cfg(test)]
144mod tests { 149mod tests {
145 use test_utils::covers; 150 use test_utils::mark;
146 151
147 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
148 use insta::assert_debug_snapshot; 153 use insta::assert_debug_snapshot;
@@ -153,7 +158,7 @@ mod tests {
153 158
154 #[test] 159 #[test]
155 fn dont_complete_current_use() { 160 fn dont_complete_current_use() {
156 covers!(dont_complete_current_use); 161 mark::check!(dont_complete_current_use);
157 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference);
158 assert!(completions.is_empty()); 163 assert!(completions.is_empty());
159 } 164 }
@@ -221,6 +226,34 @@ mod tests {
221 } 226 }
222 227
223 #[test] 228 #[test]
229 fn completes_mod_with_same_name_as_function() {
230 assert_debug_snapshot!(
231 do_reference_completion(
232 r"
233 use self::my::<|>;
234
235 mod my {
236 pub struct Bar;
237 }
238
239 fn my() {}
240 "
241 ),
242 @r###"
243 [
244 CompletionItem {
245 label: "Bar",
246 source_range: 31..31,
247 delete: 31..31,
248 insert: "Bar",
249 kind: Struct,
250 },
251 ]
252 "###
253 );
254 }
255
256 #[test]
224 fn path_visibility() { 257 fn path_visibility() {
225 assert_debug_snapshot!( 258 assert_debug_snapshot!(
226 do_reference_completion( 259 do_reference_completion(
@@ -1325,4 +1358,18 @@ mod tests {
1325 "### 1358 "###
1326 ); 1359 );
1327 } 1360 }
1361
1362 #[test]
1363 fn dont_complete_attr() {
1364 assert_debug_snapshot!(
1365 do_reference_completion(
1366 r"
1367 mod foo { pub struct Foo; }
1368 #[foo::<|>]
1369 fn f() {}
1370 "
1371 ),
1372 @r###"[]"###
1373 )
1374 }
1328} 1375}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index a3f5d1b6a..0568d9ccf 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -36,6 +36,24 @@ pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
36 snippet( 36 snippet(
37 ctx, 37 ctx,
38 cap, 38 cap,
39 "Test module",
40 "\
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn ${1:test_name}() {
47 $0
48 }
49}",
50 )
51 .lookup_by("tmod")
52 .add_to(acc);
53
54 snippet(
55 ctx,
56 cap,
39 "Test function", 57 "Test function",
40 "\ 58 "\
41#[test] 59#[test]
@@ -118,6 +136,14 @@ mod tests {
118 lookup: "tfn", 136 lookup: "tfn",
119 }, 137 },
120 CompletionItem { 138 CompletionItem {
139 label: "Test module",
140 source_range: 78..78,
141 delete: 78..78,
142 insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}",
143 kind: Snippet,
144 lookup: "tmod",
145 },
146 CompletionItem {
121 label: "macro_rules", 147 label: "macro_rules",
122 source_range: 78..78, 148 source_range: 78..78,
123 delete: 78..78, 149 delete: 78..78,
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index ee32d1ff6..039df03e0 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -32,7 +32,7 @@
32//! ``` 32//! ```
33 33
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit, ImplDef}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
@@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => { 52 SyntaxKind::NAME_REF => {
53 get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { 53 get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => { 54 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item) 55 add_function_impl(&trigger, acc, ctx, &fn_item)
56 } 56 }
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
64 } 64 }
65 65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 67 for missing_fn in
68 |item| match item { 68 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
69 hir::AssocItem::Function(fn_item) => Some(fn_item), 69 match item {
70 _ => None, 70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 }, 71 _ => None,
72 ) { 72 }
73 })
74 {
73 add_function_impl(&trigger, acc, ctx, &missing_fn); 75 add_function_impl(&trigger, acc, ctx, &missing_fn);
74 } 76 }
75 } 77 }
76 78
77 SyntaxKind::TYPE_ALIAS_DEF => { 79 SyntaxKind::TYPE_ALIAS_DEF => {
78 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 80 for missing_fn in
79 |item| match item { 81 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
80 hir::AssocItem::TypeAlias(type_item) => Some(type_item), 82 match item {
81 _ => None, 83 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
82 }, 84 _ => None,
83 ) { 85 }
86 })
87 {
84 add_type_alias_impl(&trigger, acc, ctx, &missing_fn); 88 add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
85 } 89 }
86 } 90 }
87 91
88 SyntaxKind::CONST_DEF => { 92 SyntaxKind::CONST_DEF => {
89 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 93 for missing_fn in
90 |item| match item { 94 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
91 hir::AssocItem::Const(const_item) => Some(const_item), 95 match item {
92 _ => None, 96 hir::AssocItem::Const(const_item) => Some(const_item),
93 }, 97 _ => None,
94 ) { 98 }
99 })
100 {
95 add_const_impl(&trigger, acc, ctx, &missing_fn); 101 add_const_impl(&trigger, acc, ctx, &missing_fn);
96 } 102 }
97 } 103 }
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index a6a5568de..db791660a 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,16 +1,19 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::mark;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type}; 7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const) 11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
12 || ctx.record_lit_syntax.is_some() 12 return;
13 }
14 if ctx.record_lit_syntax.is_some()
13 || ctx.record_pat_syntax.is_some() 15 || ctx.record_pat_syntax.is_some()
16 || ctx.attribute_under_caret.is_some()
14 { 17 {
15 return; 18 return;
16 } 19 }
@@ -27,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
27 if ctx.use_item_syntax.is_some() { 30 if ctx.use_item_syntax.is_some() {
28 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
29 if name_ref.syntax().text() == name.to_string().as_str() { 32 if name_ref.syntax().text() == name.to_string().as_str() {
30 tested_by!(self_fulfilling_completion); 33 mark::hit!(self_fulfilling_completion);
31 return; 34 return;
32 } 35 }
33 } 36 }
@@ -63,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
63#[cfg(test)] 66#[cfg(test)]
64mod tests { 67mod tests {
65 use insta::assert_debug_snapshot; 68 use insta::assert_debug_snapshot;
66 use test_utils::covers; 69 use test_utils::mark;
67 70
68 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
69 72
@@ -73,7 +76,7 @@ mod tests {
73 76
74 #[test] 77 #[test]
75 fn self_fulfilling_completion() { 78 fn self_fulfilling_completion() {
76 covers!(self_fulfilling_completion); 79 mark::check!(self_fulfilling_completion);
77 assert_debug_snapshot!( 80 assert_debug_snapshot!(
78 do_reference_completion( 81 do_reference_completion(
79 r#" 82 r#"
@@ -1369,4 +1372,18 @@ mod tests {
1369 "### 1372 "###
1370 ) 1373 )
1371 } 1374 }
1375
1376 #[test]
1377 fn dont_complete_attr() {
1378 assert_debug_snapshot!(
1379 do_reference_completion(
1380 r"
1381 struct Foo;
1382 #[<|>]
1383 fn f() {}
1384 "
1385 ),
1386 @r###"[]"###
1387 )
1388 }
1372} 1389}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 118fceb2e..da336973c 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15 15
@@ -34,7 +34,7 @@ pub(crate) struct CompletionContext<'a> {
34 pub(super) record_pat_syntax: Option<ast::RecordPat>, 34 pub(super) record_pat_syntax: Option<ast::RecordPat>,
35 pub(super) record_field_syntax: Option<ast::RecordField>, 35 pub(super) record_field_syntax: Option<ast::RecordField>,
36 pub(super) impl_def: Option<ast::ImplDef>, 36 pub(super) impl_def: Option<ast::ImplDef>,
37 /// FIXME: `ActiveParameter` is string-based, which is very wrong 37 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
38 pub(super) active_parameter: Option<ActiveParameter>, 38 pub(super) active_parameter: Option<ActiveParameter>,
39 pub(super) is_param: bool, 39 pub(super) is_param: bool,
40 /// If a name-binding or reference to a const in a pattern. 40 /// If a name-binding or reference to a const in a pattern.
@@ -58,7 +58,7 @@ pub(crate) struct CompletionContext<'a> {
58 pub(super) is_macro_call: bool, 58 pub(super) is_macro_call: bool,
59 pub(super) is_path_type: bool, 59 pub(super) is_path_type: bool,
60 pub(super) has_type_args: bool, 60 pub(super) has_type_args: bool,
61 pub(super) is_attribute: bool, 61 pub(super) attribute_under_caret: Option<ast::Attr>,
62} 62}
63 63
64impl<'a> CompletionContext<'a> { 64impl<'a> CompletionContext<'a> {
@@ -76,7 +76,7 @@ impl<'a> CompletionContext<'a> {
76 // actual completion. 76 // actual completion.
77 let file_with_fake_ident = { 77 let file_with_fake_ident = {
78 let parse = db.parse(position.file_id); 78 let parse = db.parse(position.file_id);
79 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); 79 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
80 parse.reparse(&edit).tree() 80 parse.reparse(&edit).tree()
81 }; 81 };
82 let fake_ident_token = 82 let fake_ident_token =
@@ -116,7 +116,7 @@ impl<'a> CompletionContext<'a> {
116 is_path_type: false, 116 is_path_type: false,
117 has_type_args: false, 117 has_type_args: false,
118 dot_receiver_is_ambiguous_float_literal: false, 118 dot_receiver_is_ambiguous_float_literal: false,
119 is_attribute: false, 119 attribute_under_caret: None,
120 }; 120 };
121 121
122 let mut original_file = original_file.syntax().clone(); 122 let mut original_file = original_file.syntax().clone();
@@ -200,6 +200,7 @@ impl<'a> CompletionContext<'a> {
200 Some(ty) 200 Some(ty)
201 }) 201 })
202 .flatten(); 202 .flatten();
203 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
203 204
204 // First, let's try to complete a reference to some declaration. 205 // First, let's try to complete a reference to some declaration.
205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 206 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
@@ -318,7 +319,6 @@ impl<'a> CompletionContext<'a> {
318 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 319 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
319 .is_some(); 320 .is_some();
320 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 321 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
321 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
322 322
323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
324 self.has_type_args = segment.type_arg_list().is_some(); 324 self.has_type_args = segment.type_arg_list().is_some();
@@ -344,7 +344,7 @@ impl<'a> CompletionContext<'a> {
344 stmt.syntax().text_range() == name_ref.syntax().text_range(), 344 stmt.syntax().text_range() == name_ref.syntax().text_range(),
345 ); 345 );
346 } 346 }
347 if let Some(block) = ast::Block::cast(node) { 347 if let Some(block) = ast::BlockExpr::cast(node) {
348 return Some( 348 return Some(
349 block.expr().map(|e| e.syntax().text_range()) 349 block.expr().map(|e| e.syntax().text_range())
350 == Some(name_ref.syntax().text_range()), 350 == Some(name_ref.syntax().text_range()),
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 5936fb8f7..cfb7c1e38 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -2,11 +2,12 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use super::completion_config::SnippetCap;
6use hir::Documentation; 5use hir::Documentation;
7use ra_syntax::TextRange; 6use ra_syntax::TextRange;
8use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
9 8
9use crate::completion::completion_config::SnippetCap;
10
10/// `CompletionItem` describes a single completion variant in the editor pop-up. 11/// `CompletionItem` describes a single completion variant in the editor pop-up.
11/// It is basically a POD with various properties. To construct a 12/// It is basically a POD with various properties. To construct a
12/// `CompletionItem`, use `new` method and the `Builder` struct. 13/// `CompletionItem`, use `new` method and the `Builder` struct.
@@ -62,8 +63,8 @@ impl fmt::Debug for CompletionItem {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
64 s.field("label", &self.label()).field("source_range", &self.source_range()); 65 s.field("label", &self.label()).field("source_range", &self.source_range());
65 if self.text_edit().as_atoms().len() == 1 { 66 if self.text_edit().len() == 1 {
66 let atom = &self.text_edit().as_atoms()[0]; 67 let atom = &self.text_edit().iter().next().unwrap();
67 s.field("delete", &atom.delete); 68 s.field("delete", &atom.delete);
68 s.field("insert", &atom.insert); 69 s.field("insert", &atom.insert);
69 } else { 70 } else {
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 2edb130cf..440ffa31d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -3,7 +3,7 @@
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 4use ra_syntax::ast::NameOwner;
5use stdx::SepBy; 5use stdx::SepBy;
6use test_utils::tested_by; 6use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 completion::{ 9 completion::{
@@ -17,12 +17,11 @@ use crate::{
17impl Completions { 17impl Completions {
18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) { 18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
19 let is_deprecated = is_deprecated(field, ctx.db); 19 let is_deprecated = is_deprecated(field, ctx.db);
20 let ty = ty.display(ctx.db).to_string();
21 let name = field.name(ctx.db); 20 let name = field.name(ctx.db);
22 let mut completion_item = 21 let mut completion_item =
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 22 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
24 .kind(CompletionItemKind::Field) 23 .kind(CompletionItemKind::Field)
25 .detail(ty.clone()) 24 .detail(ty.display(ctx.db).to_string())
26 .set_documentation(field.docs(ctx.db)) 25 .set_documentation(field.docs(ctx.db))
27 .set_deprecated(is_deprecated); 26 .set_deprecated(is_deprecated);
28 27
@@ -107,6 +106,12 @@ impl Completions {
107 } 106 }
108 }; 107 };
109 108
109 if let ScopeDef::Local(local) = resolution {
110 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
111 completion_item = completion_item.set_score(score);
112 }
113 }
114
110 // Add `<>` for generic types 115 // Add `<>` for generic types
111 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
112 if let Some(cap) = ctx.config.snippet_cap { 117 if let Some(cap) = ctx.config.snippet_cap {
@@ -116,7 +121,7 @@ impl Completions {
116 _ => false, 121 _ => false,
117 }; 122 };
118 if has_non_default_type_params { 123 if has_non_default_type_params {
119 tested_by!(inserts_angle_brackets_for_generics); 124 mark::hit!(inserts_angle_brackets_for_generics);
120 completion_item = completion_item 125 completion_item = completion_item
121 .lookup_by(local_name.clone()) 126 .lookup_by(local_name.clone())
122 .label(format!("{}<…>", local_name)) 127 .label(format!("{}<…>", local_name))
@@ -171,7 +176,7 @@ impl Completions {
171 } 176 }
172 None if needs_bang => builder.insert_text(format!("{}!", name)), 177 None if needs_bang => builder.insert_text(format!("{}!", name)),
173 _ => { 178 _ => {
174 tested_by!(dont_insert_macro_call_parens_unncessary); 179 mark::hit!(dont_insert_macro_call_parens_unncessary);
175 builder.insert_text(name) 180 builder.insert_text(name)
176 } 181 }
177 }; 182 };
@@ -319,19 +324,20 @@ impl Completions {
319 324
320pub(crate) fn compute_score( 325pub(crate) fn compute_score(
321 ctx: &CompletionContext, 326 ctx: &CompletionContext,
322 // FIXME: this definitely should be a `Type` 327 ty: &Type,
323 ty: &str,
324 name: &str, 328 name: &str,
325) -> Option<CompletionScore> { 329) -> Option<CompletionScore> {
330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string();
326 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
327 tested_by!(test_struct_field_completion_in_record_lit); 333 mark::hit!(test_struct_field_completion_in_record_lit);
328 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
329 ( 335 (
330 struct_field.name(ctx.db).to_string(), 336 struct_field.name(ctx.db).to_string(),
331 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
332 ) 338 )
333 } else if let Some(active_parameter) = &ctx.active_parameter { 339 } else if let Some(active_parameter) = &ctx.active_parameter {
334 tested_by!(test_struct_field_completion_in_func_call); 340 mark::hit!(test_struct_field_completion_in_func_call);
335 (active_parameter.name.clone(), active_parameter.ty.clone()) 341 (active_parameter.name.clone(), active_parameter.ty.clone())
336 } else { 342 } else {
337 return None; 343 return None;
@@ -392,7 +398,7 @@ impl Builder {
392 None => return self, 398 None => return self,
393 }; 399 };
394 // If not an import, add parenthesis automatically. 400 // If not an import, add parenthesis automatically.
395 tested_by!(inserts_parens_for_function_calls); 401 mark::hit!(inserts_parens_for_function_calls);
396 402
397 let (snippet, label) = if params.is_empty() { 403 let (snippet, label) = if params.is_empty() {
398 (format!("{}()$0", name), format!("{}()", name)) 404 (format!("{}()$0", name), format!("{}()", name))
@@ -451,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
451#[cfg(test)] 457#[cfg(test)]
452mod tests { 458mod tests {
453 use insta::assert_debug_snapshot; 459 use insta::assert_debug_snapshot;
454 use test_utils::covers; 460 use test_utils::mark;
455 461
456 use crate::completion::{ 462 use crate::completion::{
457 test_utils::{do_completion, do_completion_with_options}, 463 test_utils::{do_completion, do_completion_with_options},
@@ -601,7 +607,7 @@ mod tests {
601 607
602 #[test] 608 #[test]
603 fn inserts_parens_for_function_calls() { 609 fn inserts_parens_for_function_calls() {
604 covers!(inserts_parens_for_function_calls); 610 mark::check!(inserts_parens_for_function_calls);
605 assert_debug_snapshot!( 611 assert_debug_snapshot!(
606 do_reference_completion( 612 do_reference_completion(
607 r" 613 r"
@@ -986,7 +992,7 @@ mod tests {
986 992
987 #[test] 993 #[test]
988 fn inserts_angle_brackets_for_generics() { 994 fn inserts_angle_brackets_for_generics() {
989 covers!(inserts_angle_brackets_for_generics); 995 mark::check!(inserts_angle_brackets_for_generics);
990 assert_debug_snapshot!( 996 assert_debug_snapshot!(
991 do_reference_completion( 997 do_reference_completion(
992 r" 998 r"
@@ -1109,7 +1115,7 @@ mod tests {
1109 1115
1110 #[test] 1116 #[test]
1111 fn dont_insert_macro_call_parens_unncessary() { 1117 fn dont_insert_macro_call_parens_unncessary() {
1112 covers!(dont_insert_macro_call_parens_unncessary); 1118 mark::check!(dont_insert_macro_call_parens_unncessary);
1113 assert_debug_snapshot!( 1119 assert_debug_snapshot!(
1114 do_reference_completion( 1120 do_reference_completion(
1115 r" 1121 r"
@@ -1175,7 +1181,7 @@ mod tests {
1175 1181
1176 #[test] 1182 #[test]
1177 fn test_struct_field_completion_in_func_call() { 1183 fn test_struct_field_completion_in_func_call() {
1178 covers!(test_struct_field_completion_in_func_call); 1184 mark::check!(test_struct_field_completion_in_func_call);
1179 assert_debug_snapshot!( 1185 assert_debug_snapshot!(
1180 do_reference_completion( 1186 do_reference_completion(
1181 r" 1187 r"
@@ -1265,7 +1271,7 @@ mod tests {
1265 1271
1266 #[test] 1272 #[test]
1267 fn test_struct_field_completion_in_record_lit() { 1273 fn test_struct_field_completion_in_record_lit() {
1268 covers!(test_struct_field_completion_in_record_lit); 1274 mark::check!(test_struct_field_completion_in_record_lit);
1269 assert_debug_snapshot!( 1275 assert_debug_snapshot!(
1270 do_reference_completion( 1276 do_reference_completion(
1271 r" 1277 r"
@@ -1405,4 +1411,48 @@ mod tests {
1405 "### 1411 "###
1406 ); 1412 );
1407 } 1413 }
1414
1415 #[test]
1416 fn prioritize_exact_ref_match() {
1417 assert_debug_snapshot!(
1418 do_reference_completion(
1419 r"
1420 struct WorldSnapshot { _f: () };
1421 fn go(world: &WorldSnapshot) {
1422 go(w<|>)
1423 }
1424 ",
1425 ),
1426 @r###"
1427 [
1428 CompletionItem {
1429 label: "WorldSnapshot",
1430 source_range: 132..133,
1431 delete: 132..133,
1432 insert: "WorldSnapshot",
1433 kind: Struct,
1434 },
1435 CompletionItem {
1436 label: "go(…)",
1437 source_range: 132..133,
1438 delete: 132..133,
1439 insert: "go(${1:world})$0",
1440 kind: Function,
1441 lookup: "go",
1442 detail: "fn go(world: &WorldSnapshot)",
1443 trigger_call_info: true,
1444 },
1445 CompletionItem {
1446 label: "world",
1447 source_range: 132..133,
1448 delete: 132..133,
1449 insert: "world",
1450 kind: Binding,
1451 detail: "&WorldSnapshot",
1452 score: TypeAndNameMatch,
1453 },
1454 ]
1455 "###
1456 );
1457 }
1408} 1458}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index eb90b5279..bf22452a2 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options(
20 } else { 20 } else {
21 single_file_with_position(code) 21 single_file_with_position(code)
22 }; 22 };
23 let completions = analysis.completions(position, options).unwrap().unwrap(); 23 let completions = analysis.completions(options, position).unwrap().unwrap();
24 let completion_items: Vec<CompletionItem> = completions.into(); 24 let completion_items: Vec<CompletionItem> = completions.into();
25 let mut kind_completions: Vec<CompletionItem> = 25 let mut kind_completions: Vec<CompletionItem> =
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();