diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 13 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 152 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 4 |
4 files changed, 93 insertions, 80 deletions
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 6ea0efe9f..b8f8ef25f 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -88,7 +88,7 @@ fn quux(x: Option<Enum>) { | |||
88 | } | 88 | } |
89 | } | 89 | } |
90 | "#, | 90 | "#, |
91 | expect![[""]], | 91 | expect![[r#""#]], |
92 | ); | 92 | ); |
93 | } | 93 | } |
94 | 94 | ||
@@ -104,7 +104,7 @@ fn quux(x: Option<Enum>) { | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | "#, | 106 | "#, |
107 | expect![[""]], | 107 | expect![[r#""#]], |
108 | ); | 108 | ); |
109 | } | 109 | } |
110 | 110 | ||
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 85c7edabb..9d761aa43 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -25,6 +25,13 @@ use crate::{ | |||
25 | CompletionConfig, | 25 | CompletionConfig, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | #[derive(Debug, PartialEq, Eq)] | ||
29 | pub(crate) enum IsPatOrConst { | ||
30 | No, | ||
31 | Refutable, | ||
32 | Irrefutable, | ||
33 | } | ||
34 | |||
28 | /// `CompletionContext` is created early during completion to figure out, where | 35 | /// `CompletionContext` is created early during completion to figure out, where |
29 | /// exactly is the cursor, syntax-wise. | 36 | /// exactly is the cursor, syntax-wise. |
30 | #[derive(Debug)] | 37 | #[derive(Debug)] |
@@ -42,23 +49,26 @@ pub(crate) struct CompletionContext<'a> { | |||
42 | pub(super) expected_name: Option<NameOrNameRef>, | 49 | pub(super) expected_name: Option<NameOrNameRef>, |
43 | pub(super) expected_type: Option<Type>, | 50 | pub(super) expected_type: Option<Type>, |
44 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 51 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
45 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | ||
46 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | ||
47 | pub(super) function_syntax: Option<ast::Fn>, | 52 | pub(super) function_syntax: Option<ast::Fn>, |
48 | pub(super) use_item_syntax: Option<ast::Use>, | 53 | pub(super) use_item_syntax: Option<ast::Use>, |
49 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | 54 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, |
50 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 55 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
51 | 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. | ||
52 | 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>, | ||
53 | 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 | |||
54 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 70 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
55 | pub(super) active_parameter: Option<ActiveParameter>, | 71 | pub(super) active_parameter: Option<ActiveParameter>, |
56 | pub(super) is_param: bool, | ||
57 | pub(super) is_label_ref: bool, | ||
58 | /// If a name-binding or reference to a const in a pattern. | ||
59 | /// Irrefutable patterns (like let) are excluded. | ||
60 | pub(super) is_pat_binding_or_const: bool, | ||
61 | pub(super) is_irrefutable_pat_binding: bool, | ||
62 | /// 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. |
63 | pub(super) is_trivial_path: bool, | 73 | pub(super) is_trivial_path: bool, |
64 | /// If not a trivial path, the prefix (qualifier). | 74 | /// If not a trivial path, the prefix (qualifier). |
@@ -103,7 +113,6 @@ pub(crate) struct CompletionContext<'a> { | |||
103 | 113 | ||
104 | no_completion_required: bool, | 114 | no_completion_required: bool, |
105 | } | 115 | } |
106 | |||
107 | impl<'a> CompletionContext<'a> { | 116 | impl<'a> CompletionContext<'a> { |
108 | pub(super) fn new( | 117 | pub(super) fn new( |
109 | db: &'a RootDatabase, | 118 | db: &'a RootDatabase, |
@@ -160,8 +169,7 @@ impl<'a> CompletionContext<'a> { | |||
160 | active_parameter: ActiveParameter::at(db, position), | 169 | active_parameter: ActiveParameter::at(db, position), |
161 | is_label_ref: false, | 170 | is_label_ref: false, |
162 | is_param: false, | 171 | is_param: false, |
163 | is_pat_binding_or_const: false, | 172 | is_pat_or_const: IsPatOrConst::No, |
164 | is_irrefutable_pat_binding: false, | ||
165 | is_trivial_path: false, | 173 | is_trivial_path: false, |
166 | path_qual: None, | 174 | path_qual: None, |
167 | after_if: false, | 175 | after_if: false, |
@@ -417,67 +425,19 @@ impl<'a> CompletionContext<'a> { | |||
417 | self.expected_type = expected_type; | 425 | self.expected_type = expected_type; |
418 | self.expected_name = expected_name; | 426 | self.expected_name = expected_name; |
419 | 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); |
420 | 428 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | |
421 | if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset) | 429 | Some(it) => it, |
422 | { | 430 | None => return, |
423 | self.classify_lifetime(original_file, lifetime, offset); | 431 | }; |
424 | } | 432 | match name_like { |
425 | 433 | ast::NameLike::Lifetime(lifetime) => { | |
426 | // First, let's try to complete a reference to some declaration. | 434 | self.classify_lifetime(original_file, lifetime, offset); |
427 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | ||
428 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | ||
429 | // See RFC#1685. | ||
430 | if is_node::<ast::Param>(name_ref.syntax()) { | ||
431 | self.is_param = true; | ||
432 | return; | ||
433 | } | ||
434 | // FIXME: remove this (V) duplication and make the check more precise | ||
435 | if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
436 | self.record_pat_syntax = | ||
437 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
438 | } | ||
439 | self.classify_name_ref(original_file, name_ref, offset); | ||
440 | } | ||
441 | |||
442 | // Otherwise, see if this is a declaration. We can use heuristics to | ||
443 | // suggest declaration names, see `CompletionKind::Magic`. | ||
444 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { | ||
445 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) { | ||
446 | self.is_pat_binding_or_const = true; | ||
447 | if bind_pat.at_token().is_some() | ||
448 | || bind_pat.ref_token().is_some() | ||
449 | || bind_pat.mut_token().is_some() | ||
450 | { | ||
451 | self.is_pat_binding_or_const = false; | ||
452 | } | ||
453 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { | ||
454 | self.is_pat_binding_or_const = false; | ||
455 | } | ||
456 | if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { | ||
457 | match_ast! { | ||
458 | match node { | ||
459 | ast::LetStmt(it) => Some(it.pat()), | ||
460 | ast::Param(it) => Some(it.pat()), | ||
461 | _ => None, | ||
462 | } | ||
463 | } | ||
464 | }) { | ||
465 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
466 | self.is_pat_binding_or_const = false; | ||
467 | self.is_irrefutable_pat_binding = true; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | self.fill_impl_def(); | ||
472 | } | 435 | } |
473 | if is_node::<ast::Param>(name.syntax()) { | 436 | ast::NameLike::NameRef(name_ref) => { |
474 | self.is_param = true; | 437 | self.classify_name_ref(original_file, name_ref, offset); |
475 | return; | ||
476 | } | 438 | } |
477 | // FIXME: remove this (^) duplication and make the check more precise | 439 | ast::NameLike::Name(name) => { |
478 | if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | 440 | self.classify_name(original_file, name, offset); |
479 | self.record_pat_syntax = | ||
480 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
481 | } | 441 | } |
482 | } | 442 | } |
483 | } | 443 | } |
@@ -511,12 +471,64 @@ impl<'a> CompletionContext<'a> { | |||
511 | } | 471 | } |
512 | } | 472 | } |
513 | 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 | |||
514 | fn classify_name_ref( | 520 | fn classify_name_ref( |
515 | &mut self, | 521 | &mut self, |
516 | original_file: &SyntaxNode, | 522 | original_file: &SyntaxNode, |
517 | name_ref: ast::NameRef, | 523 | name_ref: ast::NameRef, |
518 | offset: TextSize, | 524 | offset: TextSize, |
519 | ) { | 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 | |||
520 | self.name_ref_syntax = | 532 | self.name_ref_syntax = |
521 | 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()); |
522 | let name_range = name_ref.syntax().text_range(); | 534 | let name_range = name_ref.syntax().text_range(); |
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 | } |