From b6d6277362366e7ddd2b355d83227041d8b6fa12 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Mon, 9 Mar 2020 18:10:25 -0700 Subject: [PATCH 1/4] Completition for type name? #3418 Iterate through TupleStructPat's until a MatchArm if one exists. Store in a new is_pat_bind_and_path bool and allow the `complete_scope` to find matches. Added some tests to ensure it works in simple and nested cases. --- .../ra_ide/src/completion/complete_scope.rs | 107 +++++++++++++++++- .../src/completion/completion_context.rs | 16 ++- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 81d3cc1b66..82842e7e30 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -3,7 +3,7 @@ use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { - if !ctx.is_trivial_path { + if !ctx.is_trivial_path && !ctx.is_pat_binding_and_path { return; } @@ -20,6 +20,111 @@ mod tests { do_completion(ra_fixture, CompletionKind::Reference) } + #[test] + fn nested_bind_pat_and_path() { + assert_debug_snapshot!( + do_reference_completion( + r" + enum First { + A, + B, + } + enum Second { + A(First), + B(First), + } + fn quux(x: Option>>) { + match x { + None => (), + Some(Some(Second(Fi<|>))) => (), + } + } + " + ), + @r###" + [ + CompletionItem { + label: "First", + source_range: [363; 365), + delete: [363; 365), + insert: "First", + kind: Enum, + }, + CompletionItem { + label: "Second", + source_range: [363; 365), + delete: [363; 365), + insert: "Second", + kind: Enum, + }, + CompletionItem { + label: "quux(…)", + source_range: [363; 365), + delete: [363; 365), + insert: "quux(${1:x})$0", + kind: Function, + lookup: "quux", + detail: "fn quux(x: Option>)", + }, + ] + "### + ); + } + + #[test] + fn bind_pat_and_path() { + assert_debug_snapshot!( + do_reference_completion( + r" + enum Enum { + A, + B, + } + fn quux(x: Option) { + match x { + None => (), + Some(en<|>) => (), + } + } + " + ), + @r###" + [ + CompletionItem { + label: "Enum", + source_range: [231; 233), + delete: [231; 233), + insert: "Enum", + kind: Enum, + }, + CompletionItem { + label: "None", + source_range: [231; 233), + delete: [231; 233), + insert: "None", + kind: Binding, + }, + CompletionItem { + label: "quux(…)", + source_range: [231; 233), + delete: [231; 233), + insert: "quux(${1:x})$0", + kind: Function, + lookup: "quux", + detail: "fn quux(x: Option)", + }, + CompletionItem { + label: "x", + source_range: [231; 233), + delete: [231; 233), + insert: "x", + kind: Binding, + }, + ] + "### + ); + } + #[test] fn completes_bindings_from_let() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 54589a2a83..d867ff6b26 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -36,6 +36,9 @@ pub(crate) struct CompletionContext<'a> { /// If a name-binding or reference to a const in a pattern. /// Irrefutable patterns (like let) are excluded. pub(super) is_pat_binding: bool, + // A bind battern which may also be part of a path. + // if let Some(En<|>) = Some(Enum::A) + pub(super) is_pat_binding_and_path: bool, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. pub(super) is_trivial_path: bool, /// If not a trivial path, the prefix (qualifier). @@ -95,6 +98,7 @@ impl<'a> CompletionContext<'a> { impl_def: None, is_param: false, is_pat_binding: false, + is_pat_binding_and_path: false, is_trivial_path: false, path_prefix: None, after_if: false, @@ -186,12 +190,20 @@ impl<'a> CompletionContext<'a> { // suggest declaration names, see `CompletionKind::Magic`. if let Some(name) = find_node_at_offset::(&file_with_fake_ident, offset) { if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { - let parent = bind_pat.syntax().parent(); + let mut parent = bind_pat.syntax().parent(); if parent.clone().and_then(ast::MatchArm::cast).is_some() - || parent.and_then(ast::Condition::cast).is_some() + || parent.clone().and_then(ast::Condition::cast).is_some() { self.is_pat_binding = true; } + + while let Some(_) = parent.clone().and_then(ast::TupleStructPat::cast) { + parent = parent.and_then(|p| p.parent()); + if parent.clone().and_then(ast::MatchArm::cast).is_some() { + self.is_pat_binding_and_path = true; + break; + } + } } if is_node::(name.syntax()) { self.is_param = true; From 6941a7faba49b3927df3106b70b780857d1bab17 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Wed, 18 Mar 2020 00:46:42 -0700 Subject: [PATCH 2/4] - Exclude Local Scope for BindPats - Exclude BindPats with @ or ref - Remove outdated test and add one testing for ref --- .../ra_ide/src/completion/complete_scope.rs | 63 ++++--------------- .../src/completion/completion_context.rs | 11 ++-- 2 files changed, 16 insertions(+), 58 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 82842e7e30..6b06210812 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -1,13 +1,17 @@ //! Completion of names from the current scope, e.g. locals and imported items. use crate::completion::{CompletionContext, Completions}; +use hir::ScopeDef; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path && !ctx.is_pat_binding_and_path { return; } - ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); + ctx.scope().process_all_names(&mut |name, res| match (ctx.is_pat_binding_and_path, &res) { + (true, ScopeDef::Local(..)) => {} + _ => acc.add_resolution(ctx, name.to_string(), &res), + }); } #[cfg(test)] @@ -21,53 +25,23 @@ mod tests { } #[test] - fn nested_bind_pat_and_path() { + fn bind_pat_and_path_ignore_ref() { assert_debug_snapshot!( do_reference_completion( r" - enum First { + enum Enum { A, B, } - enum Second { - A(First), - B(First), - } - fn quux(x: Option>>) { + fn quux(x: Option) { match x { None => (), - Some(Some(Second(Fi<|>))) => (), + Some(ref en<|>) => (), } } " ), - @r###" - [ - CompletionItem { - label: "First", - source_range: [363; 365), - delete: [363; 365), - insert: "First", - kind: Enum, - }, - CompletionItem { - label: "Second", - source_range: [363; 365), - delete: [363; 365), - insert: "Second", - kind: Enum, - }, - CompletionItem { - label: "quux(…)", - source_range: [363; 365), - delete: [363; 365), - insert: "quux(${1:x})$0", - kind: Function, - lookup: "quux", - detail: "fn quux(x: Option>)", - }, - ] - "### + @r###"[]"### ); } @@ -83,7 +57,7 @@ mod tests { fn quux(x: Option) { match x { None => (), - Some(en<|>) => (), + Some(En<|>) => (), } } " @@ -97,13 +71,6 @@ mod tests { insert: "Enum", kind: Enum, }, - CompletionItem { - label: "None", - source_range: [231; 233), - delete: [231; 233), - insert: "None", - kind: Binding, - }, CompletionItem { label: "quux(…)", source_range: [231; 233), @@ -112,13 +79,7 @@ mod tests { kind: Function, lookup: "quux", detail: "fn quux(x: Option)", - }, - CompletionItem { - label: "x", - source_range: [231; 233), - delete: [231; 233), - insert: "x", - kind: Binding, + trigger_call_info: true, }, ] "### diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index d867ff6b26..f9d4154d84 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -190,19 +190,16 @@ impl<'a> CompletionContext<'a> { // suggest declaration names, see `CompletionKind::Magic`. if let Some(name) = find_node_at_offset::(&file_with_fake_ident, offset) { if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { - let mut parent = bind_pat.syntax().parent(); + let parent = bind_pat.syntax().parent(); if parent.clone().and_then(ast::MatchArm::cast).is_some() || parent.clone().and_then(ast::Condition::cast).is_some() { self.is_pat_binding = true; } - while let Some(_) = parent.clone().and_then(ast::TupleStructPat::cast) { - parent = parent.and_then(|p| p.parent()); - if parent.clone().and_then(ast::MatchArm::cast).is_some() { - self.is_pat_binding_and_path = true; - break; - } + let bind_pat_string = bind_pat.syntax().to_string(); + if !bind_pat_string.contains("ref ") && !bind_pat_string.contains(" @ ") { + self.is_pat_binding_and_path = true; } } if is_node::(name.syntax()) { From eb51abdc646e11b8c23fee83b6f052d3dde87985 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Wed, 18 Mar 2020 16:15:32 -0700 Subject: [PATCH 3/4] Fixes to more accurately give complete_scope completions - Exclude const, static, functions form is_pat_binding_and_path (there might be more?) - Add a check to filter out Record Fields - Fix tests --- .../ra_ide/src/completion/complete_pattern.rs | 28 +++++++++++++++++++ .../ra_ide/src/completion/complete_scope.rs | 17 ++++------- .../src/completion/completion_context.rs | 8 ++++-- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index cb84bb9348..e8e676a4c3 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs @@ -55,6 +55,20 @@ mod tests { ); assert_debug_snapshot!(completions, @r###" [ + CompletionItem { + label: "Bar", + source_range: [246; 246), + delete: [246; 246), + insert: "Bar", + kind: Struct, + }, + CompletionItem { + label: "E", + source_range: [246; 246), + delete: [246; 246), + insert: "E", + kind: Enum, + }, CompletionItem { label: "E", source_range: [246; 246), @@ -69,6 +83,13 @@ mod tests { insert: "X", kind: EnumVariant, }, + CompletionItem { + label: "X", + source_range: [246; 246), + delete: [246; 246), + insert: "X", + kind: EnumVariant, + }, CompletionItem { label: "Z", source_range: [246; 246), @@ -83,6 +104,13 @@ mod tests { insert: "m", kind: Module, }, + CompletionItem { + label: "m", + source_range: [246; 246), + delete: [246; 246), + insert: "m", + kind: Module, + }, ] "###); } diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 6b06210812..1cb2ed0708 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope, e.g. locals and imported items. use crate::completion::{CompletionContext, Completions}; -use hir::ScopeDef; +use hir::{ModuleDef, ScopeDef}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path && !ctx.is_pat_binding_and_path { @@ -9,7 +9,10 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { } ctx.scope().process_all_names(&mut |name, res| match (ctx.is_pat_binding_and_path, &res) { - (true, ScopeDef::Local(..)) => {} + (true, ScopeDef::ModuleDef(ModuleDef::Function(..))) => (), + (true, ScopeDef::ModuleDef(ModuleDef::Static(..))) => (), + (true, ScopeDef::ModuleDef(ModuleDef::Const(..))) => (), + (true, ScopeDef::Local(..)) => (), _ => acc.add_resolution(ctx, name.to_string(), &res), }); } @@ -71,16 +74,6 @@ mod tests { insert: "Enum", kind: Enum, }, - CompletionItem { - label: "quux(…)", - source_range: [231; 233), - delete: [231; 233), - insert: "quux(${1:x})$0", - kind: Function, - lookup: "quux", - detail: "fn quux(x: Option)", - trigger_call_info: true, - }, ] "### ); diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index f9d4154d84..65bdac1826 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -197,9 +197,11 @@ impl<'a> CompletionContext<'a> { self.is_pat_binding = true; } - let bind_pat_string = bind_pat.syntax().to_string(); - if !bind_pat_string.contains("ref ") && !bind_pat_string.contains(" @ ") { - self.is_pat_binding_and_path = true; + if parent.and_then(ast::RecordFieldPatList::cast).is_none() { + let bind_pat_string = bind_pat.syntax().to_string(); + if !bind_pat_string.contains("ref ") && !bind_pat_string.contains(" @ ") { + self.is_pat_binding_and_path = true; + } } } if is_node::(name.syntax()) { From ec24c090063b3b86349f7812b716412d83dad069 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Thu, 19 Mar 2020 00:11:25 -0700 Subject: [PATCH 4/4] Remove const - Add test for @ matching - Address comments --- .../ra_ide/src/completion/complete_pattern.rs | 22 +++++++++++++++++++ .../ra_ide/src/completion/complete_scope.rs | 22 ++++++++++++++++++- .../src/completion/completion_context.rs | 10 ++++----- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index e8e676a4c3..bc8fade6f3 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs @@ -97,6 +97,13 @@ mod tests { insert: "Z", kind: Const, }, + CompletionItem { + label: "Z", + source_range: [246; 246), + delete: [246; 246), + insert: "Z", + kind: Const, + }, CompletionItem { label: "m", source_range: [246; 246), @@ -138,6 +145,21 @@ mod tests { insert: "E", kind: Enum, }, + CompletionItem { + label: "E", + source_range: [151; 151), + delete: [151; 151), + insert: "E", + kind: Enum, + }, + CompletionItem { + label: "m!", + source_range: [151; 151), + delete: [151; 151), + insert: "m!($0)", + kind: Macro, + detail: "macro_rules! m", + }, ] "###); } diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 1cb2ed0708..2ca5527331 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -11,7 +11,6 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { ctx.scope().process_all_names(&mut |name, res| match (ctx.is_pat_binding_and_path, &res) { (true, ScopeDef::ModuleDef(ModuleDef::Function(..))) => (), (true, ScopeDef::ModuleDef(ModuleDef::Static(..))) => (), - (true, ScopeDef::ModuleDef(ModuleDef::Const(..))) => (), (true, ScopeDef::Local(..)) => (), _ => acc.add_resolution(ctx, name.to_string(), &res), }); @@ -27,6 +26,27 @@ mod tests { do_completion(ra_fixture, CompletionKind::Reference) } + #[test] + fn bind_pat_and_path_ignore_at() { + assert_debug_snapshot!( + do_reference_completion( + r" + enum Enum { + A, + B, + } + fn quux(x: Option) { + match x { + None => (), + Some(en<|> @ Enum::A) => (), + } + } + " + ), + @r###"[]"### + ); + } + #[test] fn bind_pat_and_path_ignore_ref() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 65bdac1826..319e33b615 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -197,11 +197,11 @@ impl<'a> CompletionContext<'a> { self.is_pat_binding = true; } - if parent.and_then(ast::RecordFieldPatList::cast).is_none() { - let bind_pat_string = bind_pat.syntax().to_string(); - if !bind_pat_string.contains("ref ") && !bind_pat_string.contains(" @ ") { - self.is_pat_binding_and_path = true; - } + if parent.and_then(ast::RecordFieldPatList::cast).is_none() + && bind_pat.pat().is_none() + && !bind_pat.is_ref() + { + self.is_pat_binding_and_path = true; } } if is_node::(name.syntax()) {