aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions/pattern.rs13
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs4
-rw-r--r--crates/ide_completion/src/context.rs152
-rw-r--r--crates/ide_completion/src/render.rs4
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
3use crate::{CompletionContext, Completions}; 3use crate::{context::IsPatOrConst, CompletionContext, Completions};
4 4
5/// Completes constants and paths in patterns. 5/// Completes constants and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(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)]
29pub(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
107impl<'a> CompletionContext<'a> { 116impl<'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::{
20use syntax::TextRange; 20use syntax::TextRange;
21 21
22use crate::{ 22use 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 }