diff options
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 256 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 38 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_snippet.rs | 26 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_unqualified_path.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 82 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/test_utils.rs | 2 |
8 files changed, 377 insertions, 41 deletions
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 | }; |
17 | use ra_assists::utils::TryEnum; | ||
17 | 18 | ||
18 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 19 | pub(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 7fcd22525..02ac0166b 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
6 | use test_utils::tested_by; | 6 | use test_utils::mark; |
7 | 7 | ||
8 | use crate::completion::{CompletionContext, Completions}; | 8 | use crate::completion::{CompletionContext, Completions}; |
9 | 9 | ||
@@ -20,7 +20,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
20 | let scope = ctx.scope(); | 20 | let scope = ctx.scope(); |
21 | let context_module = scope.module(); | 21 | let context_module = scope.module(); |
22 | 22 | ||
23 | let res = match scope.resolve_hir_path(&path) { | 23 | let res = match scope.resolve_hir_path_qualifier(&path) { |
24 | Some(res) => res, | 24 | Some(res) => res, |
25 | None => return, | 25 | None => return, |
26 | }; | 26 | }; |
@@ -40,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
40 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 40 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
41 | if name_ref.syntax().text() == name.to_string().as_str() { | 41 | if name_ref.syntax().text() == name.to_string().as_str() { |
42 | // for `use self::foo<|>`, don't suggest `foo` as a completion | 42 | // for `use self::foo<|>`, don't suggest `foo` as a completion |
43 | tested_by!(dont_complete_current_use); | 43 | mark::hit!(dont_complete_current_use); |
44 | continue; | 44 | continue; |
45 | } | 45 | } |
46 | } | 46 | } |
@@ -147,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
147 | 147 | ||
148 | #[cfg(test)] | 148 | #[cfg(test)] |
149 | mod tests { | 149 | mod tests { |
150 | use test_utils::covers; | 150 | use test_utils::mark; |
151 | 151 | ||
152 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 152 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
153 | use insta::assert_debug_snapshot; | 153 | use insta::assert_debug_snapshot; |
@@ -158,7 +158,7 @@ mod tests { | |||
158 | 158 | ||
159 | #[test] | 159 | #[test] |
160 | fn dont_complete_current_use() { | 160 | fn dont_complete_current_use() { |
161 | covers!(dont_complete_current_use); | 161 | mark::check!(dont_complete_current_use); |
162 | let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); | 162 | let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); |
163 | assert!(completions.is_empty()); | 163 | assert!(completions.is_empty()); |
164 | } | 164 | } |
@@ -226,6 +226,34 @@ mod tests { | |||
226 | } | 226 | } |
227 | 227 | ||
228 | #[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] | ||
229 | fn path_visibility() { | 257 | fn path_visibility() { |
230 | assert_debug_snapshot!( | 258 | assert_debug_snapshot!( |
231 | do_reference_completion( | 259 | do_reference_completion( |
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)] | ||
42 | mod 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_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index bd40af1cb..db791660a 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -1,7 +1,7 @@ | |||
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 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use test_utils::tested_by; | 4 | use test_utils::mark; |
5 | 5 | ||
6 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use hir::{Adt, ModuleDef, Type}; | 7 | use hir::{Adt, ModuleDef, Type}; |
@@ -30,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
30 | if ctx.use_item_syntax.is_some() { | 30 | if ctx.use_item_syntax.is_some() { |
31 | 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) { |
32 | if name_ref.syntax().text() == name.to_string().as_str() { | 32 | if name_ref.syntax().text() == name.to_string().as_str() { |
33 | tested_by!(self_fulfilling_completion); | 33 | mark::hit!(self_fulfilling_completion); |
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | } | 36 | } |
@@ -66,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
66 | #[cfg(test)] | 66 | #[cfg(test)] |
67 | mod tests { | 67 | mod tests { |
68 | use insta::assert_debug_snapshot; | 68 | use insta::assert_debug_snapshot; |
69 | use test_utils::covers; | 69 | use test_utils::mark; |
70 | 70 | ||
71 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 71 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
72 | 72 | ||
@@ -76,7 +76,7 @@ mod tests { | |||
76 | 76 | ||
77 | #[test] | 77 | #[test] |
78 | fn self_fulfilling_completion() { | 78 | fn self_fulfilling_completion() { |
79 | covers!(self_fulfilling_completion); | 79 | mark::check!(self_fulfilling_completion); |
80 | assert_debug_snapshot!( | 80 | assert_debug_snapshot!( |
81 | do_reference_completion( | 81 | do_reference_completion( |
82 | r#" | 82 | r#" |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index b6b9627de..da336973c 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -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. |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 6021f7279..cfb7c1e38 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -63,8 +63,8 @@ impl fmt::Debug for CompletionItem { | |||
63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
64 | let mut s = f.debug_struct("CompletionItem"); | 64 | let mut s = f.debug_struct("CompletionItem"); |
65 | s.field("label", &self.label()).field("source_range", &self.source_range()); | 65 | s.field("label", &self.label()).field("source_range", &self.source_range()); |
66 | if self.text_edit().as_indels().len() == 1 { | 66 | if self.text_edit().len() == 1 { |
67 | let atom = &self.text_edit().as_indels()[0]; | 67 | let atom = &self.text_edit().iter().next().unwrap(); |
68 | s.field("delete", &atom.delete); | 68 | s.field("delete", &atom.delete); |
69 | s.field("insert", &atom.insert); | 69 | s.field("insert", &atom.insert); |
70 | } 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 @@ | |||
3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; | 3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use stdx::SepBy; | 5 | use stdx::SepBy; |
6 | use test_utils::tested_by; | 6 | use test_utils::mark; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | completion::{ | 9 | completion::{ |
@@ -17,12 +17,11 @@ use crate::{ | |||
17 | impl Completions { | 17 | impl 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 | ||
320 | pub(crate) fn compute_score( | 325 | pub(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)] |
452 | mod tests { | 458 | mod 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(); |