diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-27 17:34:46 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-27 17:34:46 +0100 |
commit | a2940c42c0ab30e80e1a63494ca17fb2d81bdd1f (patch) | |
tree | 7d755c4edd86c0b031452eb45536eb42d802f65c | |
parent | cc5d8069219a0a52f9c98b6766d2421eaf4664d8 (diff) | |
parent | 3a16950fd919f46fd879c36423810a40105b2c10 (diff) |
Merge #9020
9020: fix: Don't complete non-macro item paths in impls and modules r=Veykril a=Veykril
Part of #8518
bors r+
Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 29 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 32 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 27 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 63 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 173 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 2 |
7 files changed, 198 insertions, 134 deletions
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index be9cfbded..df27e7a84 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -110,7 +110,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
110 | if !ctx.config.enable_imports_on_the_fly { | 110 | if !ctx.config.enable_imports_on_the_fly { |
111 | return None; | 111 | return None; |
112 | } | 112 | } |
113 | if ctx.use_item_syntax.is_some() || ctx.is_path_disallowed() { | 113 | if ctx.use_item_syntax.is_some() |
114 | || ctx.is_path_disallowed() | ||
115 | || ctx.expects_item() | ||
116 | || ctx.expects_assoc_item() | ||
117 | { | ||
114 | return None; | 118 | return None; |
115 | } | 119 | } |
116 | let potential_import_name = { | 120 | let potential_import_name = { |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 58e35bad9..96447a603 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -49,35 +49,35 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
49 | return; | 49 | return; |
50 | } | 50 | } |
51 | 51 | ||
52 | let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent(); | 52 | let expects_assoc_item = ctx.expects_assoc_item(); |
53 | let has_block_expr_parent = ctx.has_block_expr_parent(); | 53 | let has_block_expr_parent = ctx.has_block_expr_parent(); |
54 | let has_item_list_parent = ctx.has_item_list_parent(); | 54 | let expects_item = ctx.expects_item(); |
55 | if ctx.has_impl_or_trait_prev_sibling() { | 55 | if ctx.has_impl_or_trait_prev_sibling() { |
56 | add_keyword(ctx, acc, "where", "where "); | 56 | add_keyword(ctx, acc, "where", "where "); |
57 | return; | 57 | return; |
58 | } | 58 | } |
59 | if ctx.previous_token_is(T![unsafe]) { | 59 | if ctx.previous_token_is(T![unsafe]) { |
60 | if has_item_list_parent || has_block_expr_parent { | 60 | if expects_item || has_block_expr_parent { |
61 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") | 61 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") |
62 | } | 62 | } |
63 | 63 | ||
64 | if has_item_list_parent || has_block_expr_parent { | 64 | if expects_item || has_block_expr_parent { |
65 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 65 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); |
66 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 66 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); |
67 | } | 67 | } |
68 | 68 | ||
69 | return; | 69 | return; |
70 | } | 70 | } |
71 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { | 71 | if expects_item || expects_assoc_item || has_block_expr_parent { |
72 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); | 72 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); |
73 | } | 73 | } |
74 | if has_item_list_parent || has_block_expr_parent { | 74 | if expects_item || has_block_expr_parent { |
75 | add_keyword(ctx, acc, "use", "use "); | 75 | add_keyword(ctx, acc, "use", "use "); |
76 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 76 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); |
77 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 77 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); |
78 | } | 78 | } |
79 | 79 | ||
80 | if has_item_list_parent { | 80 | if expects_item { |
81 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); | 81 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); |
82 | add_keyword(ctx, acc, "struct", "struct $0"); | 82 | add_keyword(ctx, acc, "struct", "struct $0"); |
83 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); | 83 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); |
@@ -101,24 +101,23 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
101 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); | 101 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); |
102 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); | 102 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); |
103 | } | 103 | } |
104 | if has_item_list_parent || has_block_expr_parent { | 104 | if expects_item || has_block_expr_parent { |
105 | add_keyword(ctx, acc, "mod", "mod $0"); | 105 | add_keyword(ctx, acc, "mod", "mod $0"); |
106 | } | 106 | } |
107 | if ctx.has_ident_or_ref_pat_parent() { | 107 | if ctx.expects_ident_pat_or_ref_expr() { |
108 | add_keyword(ctx, acc, "mut", "mut "); | 108 | add_keyword(ctx, acc, "mut", "mut "); |
109 | } | 109 | } |
110 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { | 110 | if expects_item || expects_assoc_item || has_block_expr_parent { |
111 | add_keyword(ctx, acc, "const", "const "); | 111 | add_keyword(ctx, acc, "const", "const "); |
112 | add_keyword(ctx, acc, "type", "type "); | 112 | add_keyword(ctx, acc, "type", "type "); |
113 | } | 113 | } |
114 | if has_item_list_parent || has_block_expr_parent { | 114 | if expects_item || has_block_expr_parent { |
115 | add_keyword(ctx, acc, "static", "static "); | 115 | add_keyword(ctx, acc, "static", "static "); |
116 | }; | 116 | }; |
117 | if has_item_list_parent || has_block_expr_parent { | 117 | if expects_item || has_block_expr_parent { |
118 | add_keyword(ctx, acc, "extern", "extern "); | 118 | add_keyword(ctx, acc, "extern", "extern "); |
119 | } | 119 | } |
120 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm | 120 | if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm { |
121 | { | ||
122 | add_keyword(ctx, acc, "unsafe", "unsafe "); | 121 | add_keyword(ctx, acc, "unsafe", "unsafe "); |
123 | } | 122 | } |
124 | if ctx.in_loop_body { | 123 | if ctx.in_loop_body { |
@@ -130,7 +129,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
130 | add_keyword(ctx, acc, "break", "break"); | 129 | add_keyword(ctx, acc, "break", "break"); |
131 | } | 130 | } |
132 | } | 131 | } |
133 | if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() { | 132 | if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { |
134 | add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); | 133 | add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); |
135 | add_keyword(ctx, acc, "pub", "pub "); | 134 | add_keyword(ctx, acc, "pub", "pub "); |
136 | } | 135 | } |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index ed48f61af..c16bb215f 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -20,6 +20,17 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
20 | None => return, | 20 | None => return, |
21 | }; | 21 | }; |
22 | let context_module = ctx.scope.module(); | 22 | let context_module = ctx.scope.module(); |
23 | if ctx.expects_item() || ctx.expects_assoc_item() { | ||
24 | if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { | ||
25 | let module_scope = module.scope(ctx.db, context_module); | ||
26 | for (name, def) in module_scope { | ||
27 | if let ScopeDef::MacroDef(macro_def) = def { | ||
28 | acc.add_macro(ctx, Some(name.to_string()), macro_def); | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | return; | ||
33 | } | ||
23 | 34 | ||
24 | // Add associated types on type parameters and `Self`. | 35 | // Add associated types on type parameters and `Self`. |
25 | resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { | 36 | resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { |
@@ -594,7 +605,7 @@ fn main() { T::$0; } | |||
594 | macro_rules! foo { () => {} } | 605 | macro_rules! foo { () => {} } |
595 | 606 | ||
596 | fn main() { let _ = crate::$0 } | 607 | fn main() { let _ = crate::$0 } |
597 | "#, | 608 | "#, |
598 | expect![[r##" | 609 | expect![[r##" |
599 | fn main() fn() | 610 | fn main() fn() |
600 | ma foo!(…) #[macro_export] macro_rules! foo | 611 | ma foo!(…) #[macro_export] macro_rules! foo |
@@ -603,6 +614,25 @@ fn main() { let _ = crate::$0 } | |||
603 | } | 614 | } |
604 | 615 | ||
605 | #[test] | 616 | #[test] |
617 | fn completes_qualified_macros_in_impl() { | ||
618 | check( | ||
619 | r#" | ||
620 | #[macro_export] | ||
621 | macro_rules! foo { () => {} } | ||
622 | |||
623 | struct MyStruct {} | ||
624 | |||
625 | impl MyStruct { | ||
626 | crate::$0 | ||
627 | } | ||
628 | "#, | ||
629 | expect![[r##" | ||
630 | ma foo! #[macro_export] macro_rules! foo | ||
631 | "##]], | ||
632 | ); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
606 | fn test_super_super_completion() { | 636 | fn test_super_super_completion() { |
607 | check( | 637 | check( |
608 | r#" | 638 | r#" |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 046a393ae..cbac88240 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -12,6 +12,14 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
12 | if ctx.is_path_disallowed() { | 12 | if ctx.is_path_disallowed() { |
13 | return; | 13 | return; |
14 | } | 14 | } |
15 | if ctx.expects_item() || ctx.expects_assoc_item() { | ||
16 | ctx.scope.process_all_names(&mut |name, def| { | ||
17 | if let ScopeDef::MacroDef(macro_def) = def { | ||
18 | acc.add_macro(ctx, Some(name.to_string()), macro_def); | ||
19 | } | ||
20 | }); | ||
21 | return; | ||
22 | } | ||
15 | 23 | ||
16 | if let Some(hir::Adt::Enum(e)) = | 24 | if let Some(hir::Adt::Enum(e)) = |
17 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 25 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
@@ -647,7 +655,7 @@ fn f() {} | |||
647 | } | 655 | } |
648 | 656 | ||
649 | #[test] | 657 | #[test] |
650 | fn completes_type_or_trait_in_impl_block() { | 658 | fn completes_target_type_or_trait_in_impl_block() { |
651 | check( | 659 | check( |
652 | r#" | 660 | r#" |
653 | trait MyTrait {} | 661 | trait MyTrait {} |
@@ -662,4 +670,21 @@ impl My$0 | |||
662 | "#]], | 670 | "#]], |
663 | ) | 671 | ) |
664 | } | 672 | } |
673 | |||
674 | #[test] | ||
675 | fn only_completes_macros_in_assoc_item_list() { | ||
676 | check( | ||
677 | r#" | ||
678 | struct MyStruct {} | ||
679 | macro_rules! foo {} | ||
680 | |||
681 | impl MyStruct { | ||
682 | $0 | ||
683 | } | ||
684 | "#, | ||
685 | expect![[r#" | ||
686 | ma foo! macro_rules! foo | ||
687 | "#]], | ||
688 | ) | ||
689 | } | ||
665 | } | 690 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 66577df94..fbef54408 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -17,9 +17,8 @@ use text_edit::Indel; | |||
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | patterns::{ | 19 | patterns::{ |
20 | for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, | 20 | determine_location, for_is_prev2, has_prev_sibling, inside_impl_trait_block, |
21 | has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, | 21 | is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, |
22 | has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token, | ||
23 | }, | 22 | }, |
24 | CompletionConfig, | 23 | CompletionConfig, |
25 | }; | 24 | }; |
@@ -30,18 +29,6 @@ pub(crate) enum PatternRefutability { | |||
30 | Irrefutable, | 29 | Irrefutable, |
31 | } | 30 | } |
32 | 31 | ||
33 | /// Direct parent container of the cursor position | ||
34 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
35 | pub(crate) enum ImmediateLocation { | ||
36 | Impl, | ||
37 | Trait, | ||
38 | RecordFieldList, | ||
39 | RefPatOrExpr, | ||
40 | IdentPat, | ||
41 | BlockExpr, | ||
42 | ItemList, | ||
43 | } | ||
44 | |||
45 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 32 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
46 | pub(crate) enum PrevSibling { | 33 | pub(crate) enum PrevSibling { |
47 | Trait, | 34 | Trait, |
@@ -127,6 +114,7 @@ pub(crate) struct CompletionContext<'a> { | |||
127 | 114 | ||
128 | no_completion_required: bool, | 115 | no_completion_required: bool, |
129 | } | 116 | } |
117 | |||
130 | impl<'a> CompletionContext<'a> { | 118 | impl<'a> CompletionContext<'a> { |
131 | pub(super) fn new( | 119 | pub(super) fn new( |
132 | db: &'a RootDatabase, | 120 | db: &'a RootDatabase, |
@@ -281,34 +269,34 @@ impl<'a> CompletionContext<'a> { | |||
281 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) | 269 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) |
282 | } | 270 | } |
283 | 271 | ||
284 | pub(crate) fn has_impl_or_trait_parent(&self) -> bool { | 272 | pub(crate) fn expects_assoc_item(&self) -> bool { |
285 | matches!( | 273 | matches!( |
286 | self.completion_location, | 274 | self.completion_location, |
287 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) | 275 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) |
288 | ) | 276 | ) |
289 | } | 277 | } |
290 | 278 | ||
291 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 279 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { |
292 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 280 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) |
293 | } | 281 | } |
294 | 282 | ||
295 | pub(crate) fn has_item_list_parent(&self) -> bool { | 283 | pub(crate) fn expects_item(&self) -> bool { |
296 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 284 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
297 | } | 285 | } |
298 | 286 | ||
299 | pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { | 287 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
288 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | ||
289 | } | ||
290 | |||
291 | pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { | ||
300 | matches!( | 292 | matches!( |
301 | self.completion_location, | 293 | self.completion_location, |
302 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) | 294 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr) |
303 | ) | 295 | ) |
304 | } | 296 | } |
305 | 297 | ||
306 | pub(crate) fn has_impl_parent(&self) -> bool { | 298 | pub(crate) fn expect_record_field(&self) -> bool { |
307 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | 299 | matches!(self.completion_location, Some(ImmediateLocation::RecordField)) |
308 | } | ||
309 | |||
310 | pub(crate) fn has_field_list_parent(&self) -> bool { | ||
311 | matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList)) | ||
312 | } | 300 | } |
313 | 301 | ||
314 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | 302 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { |
@@ -320,12 +308,11 @@ impl<'a> CompletionContext<'a> { | |||
320 | || self.record_pat_syntax.is_some() | 308 | || self.record_pat_syntax.is_some() |
321 | || self.attribute_under_caret.is_some() | 309 | || self.attribute_under_caret.is_some() |
322 | || self.mod_declaration_under_caret.is_some() | 310 | || self.mod_declaration_under_caret.is_some() |
323 | || self.has_impl_or_trait_parent() | ||
324 | } | 311 | } |
325 | 312 | ||
326 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 313 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
327 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 314 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
328 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 315 | let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); |
329 | self.previous_token = previous_token(syntax_element.clone()); | 316 | self.previous_token = previous_token(syntax_element.clone()); |
330 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 317 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
331 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 318 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
@@ -335,22 +322,6 @@ impl<'a> CompletionContext<'a> { | |||
335 | self.prev_sibling = Some(PrevSibling::Trait) | 322 | self.prev_sibling = Some(PrevSibling::Trait) |
336 | } | 323 | } |
337 | 324 | ||
338 | if has_block_expr_parent(syntax_element.clone()) { | ||
339 | self.completion_location = Some(ImmediateLocation::BlockExpr); | ||
340 | } else if has_bind_pat_parent(syntax_element.clone()) { | ||
341 | self.completion_location = Some(ImmediateLocation::IdentPat); | ||
342 | } else if has_ref_parent(syntax_element.clone()) { | ||
343 | self.completion_location = Some(ImmediateLocation::RefPatOrExpr); | ||
344 | } else if has_impl_parent(syntax_element.clone()) { | ||
345 | self.completion_location = Some(ImmediateLocation::Impl); | ||
346 | } else if has_field_list_parent(syntax_element.clone()) { | ||
347 | self.completion_location = Some(ImmediateLocation::RecordFieldList); | ||
348 | } else if has_trait_parent(syntax_element.clone()) { | ||
349 | self.completion_location = Some(ImmediateLocation::Trait); | ||
350 | } else if has_item_list_or_source_file_parent(syntax_element.clone()) { | ||
351 | self.completion_location = Some(ImmediateLocation::ItemList); | ||
352 | } | ||
353 | |||
354 | self.mod_declaration_under_caret = | 325 | self.mod_declaration_under_caret = |
355 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 326 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
356 | .filter(|module| module.item_list().is_none()); | 327 | .filter(|module| module.item_list().is_none()); |
@@ -363,6 +334,8 @@ impl<'a> CompletionContext<'a> { | |||
363 | let fn_is_prev = self.previous_token_is(T![fn]); | 334 | let fn_is_prev = self.previous_token_is(T![fn]); |
364 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | 335 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); |
365 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; | 336 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; |
337 | |||
338 | self.completion_location = determine_location(fake_ident_token); | ||
366 | } | 339 | } |
367 | 340 | ||
368 | fn fill_impl_def(&mut self) { | 341 | fn fill_impl_def(&mut self) { |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 04f2c532b..ed289d561 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -11,96 +11,133 @@ use syntax::{ | |||
11 | #[cfg(test)] | 11 | #[cfg(test)] |
12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; | 12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; |
13 | 13 | ||
14 | pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { | 14 | /// Direct parent container of the cursor position |
15 | not_same_range_ancestor(element) | 15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
16 | .filter(|it| it.kind() == ASSOC_ITEM_LIST) | 16 | pub(crate) enum ImmediateLocation { |
17 | .and_then(|it| it.parent()) | 17 | Impl, |
18 | .filter(|it| it.kind() == TRAIT) | 18 | Trait, |
19 | .is_some() | 19 | RecordField, |
20 | } | 20 | RefExpr, |
21 | #[test] | 21 | IdentPat, |
22 | fn test_has_trait_parent() { | 22 | BlockExpr, |
23 | check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); | 23 | ItemList, |
24 | } | ||
25 | |||
26 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | ||
27 | // First "expand" the element we are completing to its maximum so that we can check in what | ||
28 | // context it immediately lies. This for example means if the token is a NameRef at the end of | ||
29 | // a path, we want to look at where the path is in the tree. | ||
30 | let node = match tok.parent().and_then(ast::NameLike::cast)? { | ||
31 | ast::NameLike::NameRef(name_ref) => { | ||
32 | if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { | ||
33 | let p = segment.parent_path(); | ||
34 | if p.parent_path().is_none() { | ||
35 | p.syntax() | ||
36 | .ancestors() | ||
37 | .take_while(|it| it.text_range() == p.syntax().text_range()) | ||
38 | .last()? | ||
39 | } else { | ||
40 | return None; | ||
41 | } | ||
42 | } else { | ||
43 | return None; | ||
44 | } | ||
45 | } | ||
46 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), | ||
47 | }; | ||
48 | let parent = match node.parent() { | ||
49 | Some(parent) => parent, | ||
50 | // SourceFile | ||
51 | None => { | ||
52 | return match node.kind() { | ||
53 | MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList), | ||
54 | _ => None, | ||
55 | } | ||
56 | } | ||
57 | }; | ||
58 | let res = match_ast! { | ||
59 | match parent { | ||
60 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, | ||
61 | ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, | ||
62 | ast::SourceFile(_it) => ImmediateLocation::ItemList, | ||
63 | ast::ItemList(_it) => ImmediateLocation::ItemList, | ||
64 | ast::RefExpr(_it) => ImmediateLocation::RefExpr, | ||
65 | ast::RefPat(_it) => ImmediateLocation::RefExpr, | ||
66 | ast::RecordField(_it) => ImmediateLocation::RecordField, | ||
67 | ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) { | ||
68 | Some(IMPL) => ImmediateLocation::Impl, | ||
69 | Some(TRAIT) => ImmediateLocation::Trait, | ||
70 | _ => return None, | ||
71 | }, | ||
72 | _ => return None, | ||
73 | } | ||
74 | }; | ||
75 | Some(res) | ||
24 | } | 76 | } |
25 | 77 | ||
26 | pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { | 78 | #[cfg(test)] |
27 | not_same_range_ancestor(element) | 79 | fn check_location(code: &str, loc: ImmediateLocation) { |
28 | .filter(|it| it.kind() == ASSOC_ITEM_LIST) | 80 | check_pattern_is_applicable(code, |e| { |
29 | .and_then(|it| it.parent()) | 81 | assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc)); |
30 | .filter(|it| it.kind() == IMPL) | 82 | true |
31 | .is_some() | 83 | }); |
32 | } | ||
33 | #[test] | ||
34 | fn test_has_impl_parent() { | ||
35 | check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent); | ||
36 | } | 84 | } |
37 | 85 | ||
38 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | ||
39 | // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, | ||
40 | // where we only check the first parent with different text range. | ||
41 | element | ||
42 | .ancestors() | ||
43 | .find(|it| it.kind() == IMPL) | ||
44 | .map(|it| ast::Impl::cast(it).unwrap()) | ||
45 | .map(|it| it.trait_().is_some()) | ||
46 | .unwrap_or(false) | ||
47 | } | ||
48 | #[test] | 86 | #[test] |
49 | fn test_inside_impl_trait_block() { | 87 | fn test_has_trait_parent() { |
50 | check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block); | 88 | check_location(r"trait A { f$0 }", ImmediateLocation::Trait); |
51 | check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block); | ||
52 | check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block); | ||
53 | check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); | ||
54 | } | 89 | } |
55 | 90 | ||
56 | pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { | 91 | #[test] |
57 | not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() | 92 | fn test_has_impl_parent() { |
93 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); | ||
58 | } | 94 | } |
59 | #[test] | 95 | #[test] |
60 | fn test_has_field_list_parent() { | 96 | fn test_has_field_list_parent() { |
61 | check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent); | 97 | check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); |
62 | check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent); | 98 | check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); |
63 | } | 99 | } |
64 | 100 | ||
65 | pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { | ||
66 | not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() | ||
67 | } | ||
68 | #[test] | 101 | #[test] |
69 | fn test_has_block_expr_parent() { | 102 | fn test_has_block_expr_parent() { |
70 | check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent); | 103 | check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr); |
71 | } | 104 | } |
72 | 105 | ||
73 | pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { | 106 | #[test] |
74 | element.ancestors().any(|it| it.kind() == IDENT_PAT) | 107 | fn test_has_ident_pat_parent() { |
108 | check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat); | ||
109 | check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat); | ||
110 | check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat); | ||
111 | check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat); | ||
75 | } | 112 | } |
76 | 113 | ||
77 | #[test] | 114 | #[test] |
78 | fn test_has_bind_pat_parent() { | 115 | fn test_has_ref_expr_parent() { |
79 | check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent); | 116 | check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); |
80 | check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent); | ||
81 | } | 117 | } |
82 | 118 | ||
83 | pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { | ||
84 | not_same_range_ancestor(element) | ||
85 | .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR) | ||
86 | .is_some() | ||
87 | } | ||
88 | #[test] | 119 | #[test] |
89 | fn test_has_ref_parent() { | 120 | fn test_has_item_list_or_source_file_parent() { |
90 | check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent); | 121 | check_location(r"i$0", ImmediateLocation::ItemList); |
91 | check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent); | 122 | check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList); |
92 | } | 123 | } |
93 | 124 | ||
94 | pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { | 125 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { |
95 | match not_same_range_ancestor(element) { | 126 | // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, |
96 | Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST, | 127 | // where we only check the first parent with different text range. |
97 | None => true, | 128 | element |
98 | } | 129 | .ancestors() |
130 | .find(|it| it.kind() == IMPL) | ||
131 | .map(|it| ast::Impl::cast(it).unwrap()) | ||
132 | .map(|it| it.trait_().is_some()) | ||
133 | .unwrap_or(false) | ||
99 | } | 134 | } |
100 | #[test] | 135 | #[test] |
101 | fn test_has_item_list_or_source_file_parent() { | 136 | fn test_inside_impl_trait_block() { |
102 | check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent); | 137 | check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block); |
103 | check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent); | 138 | check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block); |
139 | check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block); | ||
140 | check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); | ||
104 | } | 141 | } |
105 | 142 | ||
106 | pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { | 143 | pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { |
@@ -160,12 +197,8 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | |||
160 | .is_some() | 197 | .is_some() |
161 | } | 198 | } |
162 | 199 | ||
163 | fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> { | 200 | pub(crate) fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> { |
164 | element | 201 | element.ancestors().skip_while(|it| it.text_range() == element.text_range()).next() |
165 | .ancestors() | ||
166 | .take_while(|it| it.text_range() == element.text_range()) | ||
167 | .last() | ||
168 | .and_then(|it| it.parent()) | ||
169 | } | 202 | } |
170 | 203 | ||
171 | fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { | 204 | fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 37be575e5..6656fd725 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -132,7 +132,7 @@ pub(crate) fn check_edit_with_config( | |||
132 | assert_eq_text!(&ra_fixture_after, &actual) | 132 | assert_eq_text!(&ra_fixture_after, &actual) |
133 | } | 133 | } |
134 | 134 | ||
135 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { | 135 | pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) { |
136 | let (db, pos) = position(code); | 136 | let (db, pos) = position(code); |
137 | 137 | ||
138 | let sema = Semantics::new(&db); | 138 | let sema = Semantics::new(&db); |