diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 56 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 13 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 215 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 37 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 4 |
6 files changed, 163 insertions, 168 deletions
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 61b667104..fa6bcc955 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use syntax::SyntaxKind; | 5 | use syntax::{SyntaxKind, T}; |
6 | 6 | ||
7 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; |
8 | 8 | ||
@@ -54,51 +54,51 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
54 | add_keyword(ctx, acc, "where", "where "); | 54 | add_keyword(ctx, acc, "where", "where "); |
55 | return; | 55 | return; |
56 | } | 56 | } |
57 | if ctx.unsafe_is_prev { | 57 | if ctx.previous_token_is(T![unsafe]) { |
58 | if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { | 58 | if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { |
59 | add_keyword(ctx, acc, "fn", "fn $0() {}") | 59 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") |
60 | } | 60 | } |
61 | 61 | ||
62 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { | 62 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { |
63 | add_keyword(ctx, acc, "trait", "trait $0 {}"); | 63 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); |
64 | add_keyword(ctx, acc, "impl", "impl $0 {}"); | 64 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); |
65 | } | 65 | } |
66 | 66 | ||
67 | return; | 67 | return; |
68 | } | 68 | } |
69 | if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent | 69 | if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent |
70 | { | 70 | { |
71 | add_keyword(ctx, acc, "fn", "fn $0() {}"); | 71 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); |
72 | } | 72 | } |
73 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { | 73 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { |
74 | add_keyword(ctx, acc, "use", "use "); | 74 | add_keyword(ctx, acc, "use", "use "); |
75 | add_keyword(ctx, acc, "impl", "impl $0 {}"); | 75 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); |
76 | add_keyword(ctx, acc, "trait", "trait $0 {}"); | 76 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); |
77 | } | 77 | } |
78 | 78 | ||
79 | if ctx.has_item_list_or_source_file_parent { | 79 | if ctx.has_item_list_or_source_file_parent { |
80 | add_keyword(ctx, acc, "enum", "enum $0 {}"); | 80 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); |
81 | add_keyword(ctx, acc, "struct", "struct $0"); | 81 | add_keyword(ctx, acc, "struct", "struct $0"); |
82 | add_keyword(ctx, acc, "union", "union $0 {}"); | 82 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); |
83 | } | 83 | } |
84 | 84 | ||
85 | if ctx.is_expr { | 85 | if ctx.is_expr { |
86 | add_keyword(ctx, acc, "match", "match $0 {}"); | 86 | add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); |
87 | add_keyword(ctx, acc, "while", "while $0 {}"); | 87 | add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); |
88 | add_keyword(ctx, acc, "while let", "while let $1 = $0 {}"); | 88 | add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); |
89 | add_keyword(ctx, acc, "loop", "loop {$0}"); | 89 | add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); |
90 | add_keyword(ctx, acc, "if", "if $0 {}"); | 90 | add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); |
91 | add_keyword(ctx, acc, "if let", "if let $1 = $0 {}"); | 91 | add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); |
92 | add_keyword(ctx, acc, "for", "for $1 in $0 {}"); | 92 | add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); |
93 | } | 93 | } |
94 | 94 | ||
95 | if ctx.if_is_prev || ctx.block_expr_parent { | 95 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || ctx.block_expr_parent { |
96 | add_keyword(ctx, acc, "let", "let "); | 96 | add_keyword(ctx, acc, "let", "let "); |
97 | } | 97 | } |
98 | 98 | ||
99 | if ctx.after_if { | 99 | if ctx.after_if { |
100 | add_keyword(ctx, acc, "else", "else {$0}"); | 100 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); |
101 | add_keyword(ctx, acc, "else if", "else if $0 {}"); | 101 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); |
102 | } | 102 | } |
103 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { | 103 | if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { |
104 | add_keyword(ctx, acc, "mod", "mod $0"); | 104 | add_keyword(ctx, acc, "mod", "mod $0"); |
@@ -342,7 +342,9 @@ mod tests { | |||
342 | check_edit( | 342 | check_edit( |
343 | "else", | 343 | "else", |
344 | r#"fn quux() { if true { () } $0 }"#, | 344 | r#"fn quux() { if true { () } $0 }"#, |
345 | r#"fn quux() { if true { () } else {$0} }"#, | 345 | r#"fn quux() { if true { () } else { |
346 | $0 | ||
347 | } }"#, | ||
346 | ); | 348 | ); |
347 | } | 349 | } |
348 | 350 | ||
@@ -646,7 +648,9 @@ fn foo() { | |||
646 | fn main() { let x = $0 } | 648 | fn main() { let x = $0 } |
647 | "#, | 649 | "#, |
648 | r#" | 650 | r#" |
649 | fn main() { let x = match $0 {}; } | 651 | fn main() { let x = match $1 { |
652 | $0 | ||
653 | }; } | ||
650 | "#, | 654 | "#, |
651 | ); | 655 | ); |
652 | 656 | ||
@@ -660,7 +664,9 @@ fn main() { | |||
660 | "#, | 664 | "#, |
661 | r#" | 665 | r#" |
662 | fn main() { | 666 | fn main() { |
663 | let x = if $0 {}; | 667 | let x = if $1 { |
668 | $0 | ||
669 | }; | ||
664 | let y = 92; | 670 | let y = 92; |
665 | } | 671 | } |
666 | "#, | 672 | "#, |
@@ -676,7 +682,9 @@ fn main() { | |||
676 | "#, | 682 | "#, |
677 | r#" | 683 | r#" |
678 | fn main() { | 684 | fn main() { |
679 | let x = loop {$0}; | 685 | let x = loop { |
686 | $0 | ||
687 | }; | ||
680 | bar(); | 688 | bar(); |
681 | } | 689 | } |
682 | "#, | 690 | "#, |
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 8dc9ab73c..3329a4844 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -1,17 +1,18 @@ | |||
1 | //! Completes constants and paths in patterns. | 1 | //! Completes constants and paths in patterns. |
2 | 2 | ||
3 | use crate::{CompletionContext, Completions}; | 3 | use crate::{context::IsPatOrConst, CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Completes constants and paths in patterns. | 5 | /// Completes constants and paths in patterns. |
6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) { | 7 | if ctx.is_pat_or_const == IsPatOrConst::No { |
8 | return; | 8 | return; |
9 | } | 9 | } |
10 | if ctx.record_pat_syntax.is_some() { | 10 | if ctx.record_pat_syntax.is_some() { |
11 | return; | 11 | return; |
12 | } | 12 | } |
13 | 13 | ||
14 | if !ctx.is_irrefutable_pat_binding { | 14 | let refutable = ctx.is_pat_or_const == IsPatOrConst::Refutable; |
15 | if refutable { | ||
15 | if let Some(hir::Adt::Enum(e)) = | 16 | if let Some(hir::Adt::Enum(e)) = |
16 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 17 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
17 | { | 18 | { |
@@ -31,14 +32,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
31 | acc.add_struct_pat(ctx, *strukt, Some(name.clone())); | 32 | acc.add_struct_pat(ctx, *strukt, Some(name.clone())); |
32 | true | 33 | true |
33 | } | 34 | } |
34 | hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => { | 35 | hir::ModuleDef::Variant(variant) if refutable => { |
35 | acc.add_variant_pat(ctx, *variant, Some(name.clone())); | 36 | acc.add_variant_pat(ctx, *variant, Some(name.clone())); |
36 | true | 37 | true |
37 | } | 38 | } |
38 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | 39 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) |
39 | | hir::ModuleDef::Variant(..) | 40 | | hir::ModuleDef::Variant(..) |
40 | | hir::ModuleDef::Const(..) | 41 | | hir::ModuleDef::Const(..) |
41 | | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding, | 42 | | hir::ModuleDef::Module(..) => refutable, |
42 | _ => false, | 43 | _ => false, |
43 | }, | 44 | }, |
44 | hir::ScopeDef::MacroDef(_) => true, | 45 | hir::ScopeDef::MacroDef(_) => true, |
@@ -47,7 +48,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
47 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); | 48 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); |
48 | true | 49 | true |
49 | } | 50 | } |
50 | Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding, | 51 | Some(hir::Adt::Enum(_)) => refutable, |
51 | _ => true, | 52 | _ => true, |
52 | }, | 53 | }, |
53 | _ => false, | 54 | _ => false, |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 7875500c1..b8f8ef25f 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -13,6 +13,8 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
13 | || ctx.record_pat_syntax.is_some() | 13 | || ctx.record_pat_syntax.is_some() |
14 | || ctx.attribute_under_caret.is_some() | 14 | || ctx.attribute_under_caret.is_some() |
15 | || ctx.mod_declaration_under_caret.is_some() | 15 | || ctx.mod_declaration_under_caret.is_some() |
16 | || ctx.has_impl_parent | ||
17 | || ctx.has_trait_parent | ||
16 | { | 18 | { |
17 | return; | 19 | return; |
18 | } | 20 | } |
@@ -86,7 +88,7 @@ fn quux(x: Option<Enum>) { | |||
86 | } | 88 | } |
87 | } | 89 | } |
88 | "#, | 90 | "#, |
89 | expect![[""]], | 91 | expect![[r#""#]], |
90 | ); | 92 | ); |
91 | } | 93 | } |
92 | 94 | ||
@@ -102,7 +104,7 @@ fn quux(x: Option<Enum>) { | |||
102 | } | 104 | } |
103 | } | 105 | } |
104 | "#, | 106 | "#, |
105 | expect![[""]], | 107 | expect![[r#""#]], |
106 | ); | 108 | ); |
107 | } | 109 | } |
108 | 110 | ||
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 2f3fb1710..9d761aa43 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -1,29 +1,37 @@ | |||
1 | //! See `CompletionContext` structure. | 1 | //! See `CompletionContext` structure. |
2 | 2 | ||
3 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; | 3 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; |
4 | use ide_db::base_db::{FilePosition, SourceDatabase}; | 4 | use ide_db::{ |
5 | use ide_db::{call_info::ActiveParameter, RootDatabase}; | 5 | base_db::{FilePosition, SourceDatabase}, |
6 | call_info::ActiveParameter, | ||
7 | RootDatabase, | ||
8 | }; | ||
6 | use syntax::{ | 9 | use syntax::{ |
7 | algo::find_node_at_offset, | 10 | algo::find_node_at_offset, |
8 | ast::{self, NameOrNameRef, NameOwner}, | 11 | ast::{self, NameOrNameRef, NameOwner}, |
9 | match_ast, AstNode, NodeOrToken, | 12 | match_ast, AstNode, NodeOrToken, |
10 | SyntaxKind::*, | 13 | SyntaxKind::{self, *}, |
11 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 14 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
12 | }; | 15 | }; |
13 | |||
14 | use text_edit::Indel; | 16 | use text_edit::Indel; |
15 | 17 | ||
16 | use crate::{ | 18 | use crate::{ |
17 | patterns::{ | 19 | patterns::{ |
18 | fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, | 20 | for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, |
19 | has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, | 21 | has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, |
20 | has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, | 22 | has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, inside_impl_trait_block, |
21 | has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm, | 23 | is_in_loop_body, is_match_arm, previous_token, |
22 | unsafe_is_prev, | ||
23 | }, | 24 | }, |
24 | CompletionConfig, | 25 | CompletionConfig, |
25 | }; | 26 | }; |
26 | 27 | ||
28 | #[derive(Debug, PartialEq, Eq)] | ||
29 | pub(crate) enum IsPatOrConst { | ||
30 | No, | ||
31 | Refutable, | ||
32 | Irrefutable, | ||
33 | } | ||
34 | |||
27 | /// `CompletionContext` is created early during completion to figure out, where | 35 | /// `CompletionContext` is created early during completion to figure out, where |
28 | /// exactly is the cursor, syntax-wise. | 36 | /// exactly is the cursor, syntax-wise. |
29 | #[derive(Debug)] | 37 | #[derive(Debug)] |
@@ -41,23 +49,26 @@ pub(crate) struct CompletionContext<'a> { | |||
41 | pub(super) expected_name: Option<NameOrNameRef>, | 49 | pub(super) expected_name: Option<NameOrNameRef>, |
42 | pub(super) expected_type: Option<Type>, | 50 | pub(super) expected_type: Option<Type>, |
43 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 51 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
44 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | ||
45 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | ||
46 | pub(super) function_syntax: Option<ast::Fn>, | 52 | pub(super) function_syntax: Option<ast::Fn>, |
47 | pub(super) use_item_syntax: Option<ast::Use>, | 53 | pub(super) use_item_syntax: Option<ast::Use>, |
48 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | 54 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, |
49 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 55 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
50 | pub(super) record_field_syntax: Option<ast::RecordExprField>, | 56 | pub(super) record_field_syntax: Option<ast::RecordExprField>, |
57 | /// The parent impl of the cursor position if it exists. | ||
51 | pub(super) impl_def: Option<ast::Impl>, | 58 | pub(super) impl_def: Option<ast::Impl>, |
59 | |||
60 | // potentially set if we are completing a lifetime | ||
61 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | ||
62 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | ||
52 | pub(super) lifetime_allowed: bool, | 63 | pub(super) lifetime_allowed: bool, |
64 | pub(super) is_label_ref: bool, | ||
65 | |||
66 | // potentially set if we are completing a name | ||
67 | pub(super) is_pat_or_const: IsPatOrConst, | ||
68 | pub(super) is_param: bool, | ||
69 | |||
53 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 70 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
54 | pub(super) active_parameter: Option<ActiveParameter>, | 71 | pub(super) active_parameter: Option<ActiveParameter>, |
55 | pub(super) is_param: bool, | ||
56 | pub(super) is_label_ref: bool, | ||
57 | /// If a name-binding or reference to a const in a pattern. | ||
58 | /// Irrefutable patterns (like let) are excluded. | ||
59 | pub(super) is_pat_binding_or_const: bool, | ||
60 | pub(super) is_irrefutable_pat_binding: bool, | ||
61 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 72 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
62 | pub(super) is_trivial_path: bool, | 73 | pub(super) is_trivial_path: bool, |
63 | /// If not a trivial path, the prefix (qualifier). | 74 | /// If not a trivial path, the prefix (qualifier). |
@@ -81,27 +92,27 @@ pub(crate) struct CompletionContext<'a> { | |||
81 | pub(super) is_path_type: bool, | 92 | pub(super) is_path_type: bool, |
82 | pub(super) has_type_args: bool, | 93 | pub(super) has_type_args: bool, |
83 | pub(super) attribute_under_caret: Option<ast::Attr>, | 94 | pub(super) attribute_under_caret: Option<ast::Attr>, |
95 | pub(super) locals: Vec<(String, Local)>, | ||
96 | |||
84 | pub(super) mod_declaration_under_caret: Option<ast::Module>, | 97 | pub(super) mod_declaration_under_caret: Option<ast::Module>, |
85 | pub(super) unsafe_is_prev: bool, | 98 | pub(super) has_trait_parent: bool, |
86 | pub(super) if_is_prev: bool, | 99 | pub(super) has_impl_parent: bool, |
100 | |||
101 | // keyword patterns | ||
102 | pub(super) previous_token: Option<SyntaxToken>, | ||
87 | pub(super) block_expr_parent: bool, | 103 | pub(super) block_expr_parent: bool, |
88 | pub(super) bind_pat_parent: bool, | 104 | pub(super) bind_pat_parent: bool, |
89 | pub(super) ref_pat_parent: bool, | 105 | pub(super) ref_pat_parent: bool, |
90 | pub(super) in_loop_body: bool, | 106 | pub(super) in_loop_body: bool, |
91 | pub(super) has_trait_parent: bool, | ||
92 | pub(super) has_impl_parent: bool, | ||
93 | pub(super) inside_impl_trait_block: bool, | ||
94 | pub(super) has_field_list_parent: bool, | 107 | pub(super) has_field_list_parent: bool, |
95 | pub(super) trait_as_prev_sibling: bool, | 108 | pub(super) trait_as_prev_sibling: bool, |
96 | pub(super) impl_as_prev_sibling: bool, | 109 | pub(super) impl_as_prev_sibling: bool, |
97 | pub(super) is_match_arm: bool, | 110 | pub(super) is_match_arm: bool, |
98 | pub(super) has_item_list_or_source_file_parent: bool, | 111 | pub(super) has_item_list_or_source_file_parent: bool, |
99 | pub(super) for_is_prev2: bool, | ||
100 | pub(super) fn_is_prev: bool, | ||
101 | pub(super) incomplete_let: bool, | 112 | pub(super) incomplete_let: bool, |
102 | pub(super) locals: Vec<(String, Local)>, | ||
103 | } | ||
104 | 113 | ||
114 | no_completion_required: bool, | ||
115 | } | ||
105 | impl<'a> CompletionContext<'a> { | 116 | impl<'a> CompletionContext<'a> { |
106 | pub(super) fn new( | 117 | pub(super) fn new( |
107 | db: &'a RootDatabase, | 118 | db: &'a RootDatabase, |
@@ -158,8 +169,7 @@ impl<'a> CompletionContext<'a> { | |||
158 | active_parameter: ActiveParameter::at(db, position), | 169 | active_parameter: ActiveParameter::at(db, position), |
159 | is_label_ref: false, | 170 | is_label_ref: false, |
160 | is_param: false, | 171 | is_param: false, |
161 | is_pat_binding_or_const: false, | 172 | is_pat_or_const: IsPatOrConst::No, |
162 | is_irrefutable_pat_binding: false, | ||
163 | is_trivial_path: false, | 173 | is_trivial_path: false, |
164 | path_qual: None, | 174 | path_qual: None, |
165 | after_if: false, | 175 | after_if: false, |
@@ -175,22 +185,19 @@ impl<'a> CompletionContext<'a> { | |||
175 | has_type_args: false, | 185 | has_type_args: false, |
176 | attribute_under_caret: None, | 186 | attribute_under_caret: None, |
177 | mod_declaration_under_caret: None, | 187 | mod_declaration_under_caret: None, |
178 | unsafe_is_prev: false, | 188 | previous_token: None, |
179 | if_is_prev: false, | ||
180 | block_expr_parent: false, | 189 | block_expr_parent: false, |
181 | bind_pat_parent: false, | 190 | bind_pat_parent: false, |
182 | ref_pat_parent: false, | 191 | ref_pat_parent: false, |
183 | in_loop_body: false, | 192 | in_loop_body: false, |
184 | has_trait_parent: false, | 193 | has_trait_parent: false, |
185 | has_impl_parent: false, | 194 | has_impl_parent: false, |
186 | inside_impl_trait_block: false, | ||
187 | has_field_list_parent: false, | 195 | has_field_list_parent: false, |
188 | trait_as_prev_sibling: false, | 196 | trait_as_prev_sibling: false, |
189 | impl_as_prev_sibling: false, | 197 | impl_as_prev_sibling: false, |
190 | is_match_arm: false, | 198 | is_match_arm: false, |
191 | has_item_list_or_source_file_parent: false, | 199 | has_item_list_or_source_file_parent: false, |
192 | for_is_prev2: false, | 200 | no_completion_required: false, |
193 | fn_is_prev: false, | ||
194 | incomplete_let: false, | 201 | incomplete_let: false, |
195 | locals, | 202 | locals, |
196 | }; | 203 | }; |
@@ -245,7 +252,7 @@ impl<'a> CompletionContext<'a> { | |||
245 | /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. | 252 | /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. |
246 | /// - `for _ i$0` -- obviously, it'll be "in" keyword. | 253 | /// - `for _ i$0` -- obviously, it'll be "in" keyword. |
247 | pub(crate) fn no_completion_required(&self) -> bool { | 254 | pub(crate) fn no_completion_required(&self) -> bool { |
248 | (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 | 255 | self.no_completion_required |
249 | } | 256 | } |
250 | 257 | ||
251 | /// The range of the identifier that is being completed. | 258 | /// The range of the identifier that is being completed. |
@@ -264,33 +271,39 @@ impl<'a> CompletionContext<'a> { | |||
264 | } | 271 | } |
265 | } | 272 | } |
266 | 273 | ||
274 | pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool { | ||
275 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) | ||
276 | } | ||
277 | |||
267 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 278 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
268 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 279 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
269 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 280 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
281 | self.previous_token = previous_token(syntax_element.clone()); | ||
270 | self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); | 282 | self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); |
271 | self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); | ||
272 | self.if_is_prev = if_is_prev(syntax_element.clone()); | ||
273 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); | 283 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); |
274 | self.ref_pat_parent = has_ref_parent(syntax_element.clone()); | 284 | self.ref_pat_parent = has_ref_parent(syntax_element.clone()); |
275 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 285 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
276 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); | 286 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); |
277 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); | 287 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); |
278 | self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
279 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); | 288 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); |
280 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); | 289 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); |
281 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | 290 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); |
282 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 291 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
292 | |||
283 | self.has_item_list_or_source_file_parent = | 293 | self.has_item_list_or_source_file_parent = |
284 | has_item_list_or_source_file_parent(syntax_element.clone()); | 294 | has_item_list_or_source_file_parent(syntax_element.clone()); |
285 | self.mod_declaration_under_caret = | 295 | self.mod_declaration_under_caret = |
286 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 296 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
287 | .filter(|module| module.item_list().is_none()); | 297 | .filter(|module| module.item_list().is_none()); |
288 | self.for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
289 | self.fn_is_prev = fn_is_prev(syntax_element.clone()); | ||
290 | self.incomplete_let = | 298 | self.incomplete_let = |
291 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | 299 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { |
292 | it.syntax().text_range().end() == syntax_element.text_range().end() | 300 | it.syntax().text_range().end() == syntax_element.text_range().end() |
293 | }); | 301 | }); |
302 | |||
303 | let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
304 | let fn_is_prev = self.previous_token_is(T![fn]); | ||
305 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
306 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; | ||
294 | } | 307 | } |
295 | 308 | ||
296 | fn fill_impl_def(&mut self) { | 309 | fn fill_impl_def(&mut self) { |
@@ -412,67 +425,19 @@ impl<'a> CompletionContext<'a> { | |||
412 | self.expected_type = expected_type; | 425 | self.expected_type = expected_type; |
413 | self.expected_name = expected_name; | 426 | self.expected_name = expected_name; |
414 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 427 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
415 | 428 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | |
416 | if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset) | 429 | Some(it) => it, |
417 | { | 430 | None => return, |
418 | self.classify_lifetime(original_file, lifetime, offset); | 431 | }; |
419 | } | 432 | match name_like { |
420 | 433 | ast::NameLike::Lifetime(lifetime) => { | |
421 | // First, let's try to complete a reference to some declaration. | 434 | self.classify_lifetime(original_file, lifetime, offset); |
422 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | ||
423 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | ||
424 | // See RFC#1685. | ||
425 | if is_node::<ast::Param>(name_ref.syntax()) { | ||
426 | self.is_param = true; | ||
427 | return; | ||
428 | } | ||
429 | // FIXME: remove this (V) duplication and make the check more precise | ||
430 | if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
431 | self.record_pat_syntax = | ||
432 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
433 | } | ||
434 | self.classify_name_ref(original_file, name_ref, offset); | ||
435 | } | ||
436 | |||
437 | // Otherwise, see if this is a declaration. We can use heuristics to | ||
438 | // suggest declaration names, see `CompletionKind::Magic`. | ||
439 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { | ||
440 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) { | ||
441 | self.is_pat_binding_or_const = true; | ||
442 | if bind_pat.at_token().is_some() | ||
443 | || bind_pat.ref_token().is_some() | ||
444 | || bind_pat.mut_token().is_some() | ||
445 | { | ||
446 | self.is_pat_binding_or_const = false; | ||
447 | } | ||
448 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { | ||
449 | self.is_pat_binding_or_const = false; | ||
450 | } | ||
451 | if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { | ||
452 | match_ast! { | ||
453 | match node { | ||
454 | ast::LetStmt(it) => Some(it.pat()), | ||
455 | ast::Param(it) => Some(it.pat()), | ||
456 | _ => None, | ||
457 | } | ||
458 | } | ||
459 | }) { | ||
460 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
461 | self.is_pat_binding_or_const = false; | ||
462 | self.is_irrefutable_pat_binding = true; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | self.fill_impl_def(); | ||
467 | } | 435 | } |
468 | if is_node::<ast::Param>(name.syntax()) { | 436 | ast::NameLike::NameRef(name_ref) => { |
469 | self.is_param = true; | 437 | self.classify_name_ref(original_file, name_ref, offset); |
470 | return; | ||
471 | } | 438 | } |
472 | // FIXME: remove this (^) duplication and make the check more precise | 439 | ast::NameLike::Name(name) => { |
473 | if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | 440 | self.classify_name(original_file, name, offset); |
474 | self.record_pat_syntax = | ||
475 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
476 | } | 441 | } |
477 | } | 442 | } |
478 | } | 443 | } |
@@ -506,12 +471,64 @@ impl<'a> CompletionContext<'a> { | |||
506 | } | 471 | } |
507 | } | 472 | } |
508 | 473 | ||
474 | fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { | ||
475 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
476 | self.is_pat_or_const = IsPatOrConst::Refutable; | ||
477 | // if any of these is here our bind pat can't be a const pat anymore | ||
478 | let complex_ident_pat = bind_pat.at_token().is_some() | ||
479 | || bind_pat.ref_token().is_some() | ||
480 | || bind_pat.mut_token().is_some(); | ||
481 | if complex_ident_pat { | ||
482 | self.is_pat_or_const = IsPatOrConst::No; | ||
483 | } else if let Some(pat_field) = | ||
484 | bind_pat.syntax().parent().and_then(ast::RecordPatField::cast) | ||
485 | { | ||
486 | if pat_field.name_ref().is_none() { | ||
487 | self.is_pat_or_const = IsPatOrConst::No; | ||
488 | } | ||
489 | } else { | ||
490 | let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| { | ||
491 | match_ast! { | ||
492 | match node { | ||
493 | ast::LetStmt(it) => Some(it.pat()), | ||
494 | ast::Param(it) => Some(it.pat()), | ||
495 | _ => None, | ||
496 | } | ||
497 | } | ||
498 | }); | ||
499 | if let Some(Some(pat)) = irrefutable_pat { | ||
500 | // This check is here since we could be inside a pattern in the initializer expression of the let statement. | ||
501 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
502 | self.is_pat_or_const = IsPatOrConst::Irrefutable; | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | self.fill_impl_def(); | ||
508 | } | ||
509 | if is_node::<ast::Param>(name.syntax()) { | ||
510 | self.is_param = true; | ||
511 | return; | ||
512 | } | ||
513 | // FIXME: remove this (V) duplication and make the check more precise | ||
514 | if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
515 | self.record_pat_syntax = | ||
516 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
517 | } | ||
518 | } | ||
519 | |||
509 | fn classify_name_ref( | 520 | fn classify_name_ref( |
510 | &mut self, | 521 | &mut self, |
511 | original_file: &SyntaxNode, | 522 | original_file: &SyntaxNode, |
512 | name_ref: ast::NameRef, | 523 | name_ref: ast::NameRef, |
513 | offset: TextSize, | 524 | offset: TextSize, |
514 | ) { | 525 | ) { |
526 | // FIXME: remove this (^) duplication and make the check more precise | ||
527 | if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
528 | self.record_pat_syntax = | ||
529 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
530 | } | ||
531 | |||
515 | self.name_ref_syntax = | 532 | self.name_ref_syntax = |
516 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 533 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
517 | let name_range = name_ref.syntax().text_range(); | 534 | let name_range = name_ref.syntax().text_range(); |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index d82564381..3d8a83ed8 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -115,36 +115,8 @@ fn test_is_match_arm() { | |||
115 | check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm); | 115 | check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm); |
116 | } | 116 | } |
117 | 117 | ||
118 | pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { | 118 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { |
119 | element | 119 | element.into_token().and_then(|it| previous_non_trivia_token(it)) |
120 | .into_token() | ||
121 | .and_then(|it| previous_non_trivia_token(it)) | ||
122 | .filter(|it| it.kind() == T![unsafe]) | ||
123 | .is_some() | ||
124 | } | ||
125 | #[test] | ||
126 | fn test_unsafe_is_prev() { | ||
127 | check_pattern_is_applicable(r"unsafe i$0", unsafe_is_prev); | ||
128 | } | ||
129 | |||
130 | pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | ||
131 | element | ||
132 | .into_token() | ||
133 | .and_then(|it| previous_non_trivia_token(it)) | ||
134 | .filter(|it| it.kind() == T![if]) | ||
135 | .is_some() | ||
136 | } | ||
137 | |||
138 | pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { | ||
139 | element | ||
140 | .into_token() | ||
141 | .and_then(|it| previous_non_trivia_token(it)) | ||
142 | .filter(|it| it.kind() == T![fn]) | ||
143 | .is_some() | ||
144 | } | ||
145 | #[test] | ||
146 | fn test_fn_is_prev() { | ||
147 | check_pattern_is_applicable(r"fn l$0", fn_is_prev); | ||
148 | } | 120 | } |
149 | 121 | ||
150 | /// Check if the token previous to the previous one is `for`. | 122 | /// Check if the token previous to the previous one is `for`. |
@@ -162,11 +134,6 @@ fn test_for_is_prev2() { | |||
162 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); | 134 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); |
163 | } | 135 | } |
164 | 136 | ||
165 | #[test] | ||
166 | fn test_if_is_prev() { | ||
167 | check_pattern_is_applicable(r"if l$0", if_is_prev); | ||
168 | } | ||
169 | |||
170 | pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { | 137 | pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { |
171 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some() | 138 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some() |
172 | } | 139 | } |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index d7f96b864..21f0da5f8 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -20,6 +20,7 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | context::IsPatOrConst, | ||
23 | item::{CompletionRelevanceTypeMatch, ImportEdit}, | 24 | item::{CompletionRelevanceTypeMatch, ImportEdit}, |
24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 25 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
25 | }; | 26 | }; |
@@ -188,8 +189,7 @@ impl<'a> Render<'a> { | |||
188 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); | 189 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); |
189 | } | 190 | } |
190 | ScopeDef::ModuleDef(Variant(_)) | 191 | ScopeDef::ModuleDef(Variant(_)) |
191 | if self.ctx.completion.is_pat_binding_or_const | 192 | if self.ctx.completion.is_pat_or_const != IsPatOrConst::No => |
192 | | self.ctx.completion.is_irrefutable_pat_binding => | ||
193 | { | 193 | { |
194 | CompletionItemKind::SymbolKind(SymbolKind::Variant) | 194 | CompletionItemKind::SymbolKind(SymbolKind::Variant) |
195 | } | 195 | } |