From 15ed114a4935684ded93c717f6a3240888b58985 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Fri, 6 Mar 2020 17:35:39 -0800 Subject: Next steps in assoc item completion #3183 Allow trait autocompletions for unimplemented associated fn's, types, and consts without using explicit keywords before hand (fn, type, const). The sequel to #3108. --- .../ra_ide/src/completion/complete_trait_impl.rs | 105 +++++++++++++++++++-- 1 file changed, 99 insertions(+), 6 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 18a1d2995..8cb0c72bb 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -47,22 +47,39 @@ use crate::{ }; pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { - let trigger = ctx.token.ancestors().find(|p| match p.kind() { + let mut tokens = ctx.token.ancestors(); + let trigger = tokens.find(|p| match p.kind() { SyntaxKind::FN_DEF | SyntaxKind::TYPE_ALIAS_DEF | SyntaxKind::CONST_DEF + | SyntaxKind::NAME_REF | SyntaxKind::BLOCK_EXPR => true, _ => false, }); - let impl_def = trigger - .as_ref() - .and_then(|node| node.parent()) - .and_then(|node| node.parent()) - .and_then(ast::ImplDef::cast); + let impl_def = tokens + .find(|p| match p.kind() { + SyntaxKind::IMPL_DEF => true, + _ => false, + }) + .and_then(|n| ast::ImplDef::cast(n)); if let (Some(trigger), Some(impl_def)) = (trigger, impl_def) { match trigger.kind() { + SyntaxKind::NAME_REF => { + get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { + hir::AssocItem::Function(fn_item) => { + add_function_impl(&trigger, acc, ctx, &fn_item) + } + hir::AssocItem::TypeAlias(type_item) => { + add_type_alias_impl(&trigger, acc, ctx, &type_item) + } + hir::AssocItem::Const(const_item) => { + add_const_impl(&trigger, acc, ctx, &const_item) + } + }) + } + SyntaxKind::FN_DEF => { for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( |item| match item { @@ -209,6 +226,82 @@ mod tests { do_completion(code, CompletionKind::Magic) } + #[test] + fn name_ref_function_type_const() { + let completions = complete( + r" + trait Test { + type TestType; + const TEST_CONST: u16; + fn test(); + } + + struct T1; + + impl Test for T1 { + t<|> + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "const TEST_CONST: u16 = ", + source_range: [209; 210), + delete: [209; 210), + insert: "const TEST_CONST: u16 = ", + kind: Const, + lookup: "TEST_CONST", + }, + CompletionItem { + label: "fn test()", + source_range: [209; 210), + delete: [209; 210), + insert: "fn test() {}", + kind: Function, + lookup: "test", + }, + CompletionItem { + label: "type TestType = ", + source_range: [209; 210), + delete: [209; 210), + insert: "type TestType = ", + kind: TypeAlias, + lookup: "TestType", + }, + ] + "###); + } + + #[test] + fn name_ref_single_function() { + let completions = complete( + r" + trait Test { + fn test(); + } + + struct T1; + + impl Test for T1 { + t<|> + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "fn test()", + source_range: [139; 140), + delete: [139; 140), + insert: "fn test() {}", + kind: Function, + lookup: "test", + }, + ] + "###); + } + #[test] fn single_function() { let completions = complete( -- cgit v1.2.3 From 7c439355ce5337002fe83d940329596f75400696 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Sun, 8 Mar 2020 03:24:34 -0700 Subject: Don't allow nested completions --- .../ra_ide/src/completion/complete_trait_impl.rs | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 8cb0c72bb..a4e1045fc 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -48,23 +48,28 @@ use crate::{ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { let mut tokens = ctx.token.ancestors(); - let trigger = tokens.find(|p| match p.kind() { - SyntaxKind::FN_DEF - | SyntaxKind::TYPE_ALIAS_DEF - | SyntaxKind::CONST_DEF - | SyntaxKind::NAME_REF - | SyntaxKind::BLOCK_EXPR => true, - _ => false, - }); - - let impl_def = tokens + let completion_match = tokens .find(|p| match p.kind() { - SyntaxKind::IMPL_DEF => true, + SyntaxKind::FN_DEF + | SyntaxKind::TYPE_ALIAS_DEF + | SyntaxKind::CONST_DEF + | SyntaxKind::NAME_REF + | SyntaxKind::BLOCK_EXPR => true, _ => false, }) - .and_then(|n| ast::ImplDef::cast(n)); + .and_then(|trigger| { + for p in tokens { + match p.kind() { + // No nested completions + SyntaxKind::FN_DEF | SyntaxKind::BLOCK => return None, + SyntaxKind::IMPL_DEF => return ast::ImplDef::cast(p).map(|p| (trigger, p)), + _ => {} + } + } + None + }); - if let (Some(trigger), Some(impl_def)) = (trigger, impl_def) { + if let Some((trigger, impl_def)) = completion_match { match trigger.kind() { SyntaxKind::NAME_REF => { get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { -- cgit v1.2.3 From 9138d39947c3009a1bdbf420b29e6bd06e6d6376 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Sun, 8 Mar 2020 17:50:41 -0700 Subject: Clean up completion matching. - Add test to ensure nested completions don't happen --- .../ra_ide/src/completion/complete_trait_impl.rs | 77 +++++++++++++++------- 1 file changed, 53 insertions(+), 24 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index a4e1045fc..ba7d43f1e 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -34,7 +34,7 @@ use hir::{self, Docs, HasSource}; use ra_assists::utils::get_missing_impl_items; use ra_syntax::{ - ast::{self, edit}, + ast::{self, edit, ImplDef}, AstNode, SyntaxKind, SyntaxNode, TextRange, }; use ra_text_edit::TextEdit; @@ -47,29 +47,7 @@ use crate::{ }; pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { - let mut tokens = ctx.token.ancestors(); - let completion_match = tokens - .find(|p| match p.kind() { - SyntaxKind::FN_DEF - | SyntaxKind::TYPE_ALIAS_DEF - | SyntaxKind::CONST_DEF - | SyntaxKind::NAME_REF - | SyntaxKind::BLOCK_EXPR => true, - _ => false, - }) - .and_then(|trigger| { - for p in tokens { - match p.kind() { - // No nested completions - SyntaxKind::FN_DEF | SyntaxKind::BLOCK => return None, - SyntaxKind::IMPL_DEF => return ast::ImplDef::cast(p).map(|p| (trigger, p)), - _ => {} - } - } - None - }); - - if let Some((trigger, impl_def)) = completion_match { + if let Some((trigger, impl_def)) = completion_match(ctx) { match trigger.kind() { SyntaxKind::NAME_REF => { get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { @@ -123,6 +101,36 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext } } +fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, ImplDef)> { + let (trigger_idx, trigger) = + ctx.token.ancestors().enumerate().find(|(_idx, p)| match p.kind() { + SyntaxKind::FN_DEF + | SyntaxKind::TYPE_ALIAS_DEF + | SyntaxKind::CONST_DEF + | SyntaxKind::NAME_REF + | SyntaxKind::BLOCK_EXPR => true, + _ => false, + })?; + let (impl_def_idx, impl_def) = + ctx.token.ancestors().enumerate().skip(trigger_idx + 1).find_map(|(idx, p)| { + match p.kind() { + SyntaxKind::IMPL_DEF => ast::ImplDef::cast(p).map(|p| (idx, p)), + _ => None, + } + })?; + let _is_nested = ctx + .token + .ancestors() + .skip(trigger_idx + 1) + .take(impl_def_idx - trigger_idx - 1) + .find_map(|p| match p.kind() { + SyntaxKind::FN_DEF | SyntaxKind::BLOCK => Some(()), + _ => None, + }) + .xor(Some(()))?; + Some((trigger, impl_def)) +} + fn add_function_impl( fn_def_node: &SyntaxNode, acc: &mut Completions, @@ -278,6 +286,27 @@ mod tests { "###); } + #[test] + fn no_nested_fn_completions() { + let completions = complete( + r" + trait Test { + fn test(); + fn test2(); + } + + struct T1; + + impl Test for T1 { + fn test() { + t<|> + } + } + ", + ); + assert_debug_snapshot!(completions, @r###"[]"###); + } + #[test] fn name_ref_single_function() { let completions = complete( -- cgit v1.2.3 From f67e6a850ec4c2cbfd183e45802ea4c2805af911 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Mon, 9 Mar 2020 13:01:40 -0700 Subject: Switch to explicit offsets for impl_def Blacklists are prone to more errors --- .../ra_ide/src/completion/complete_trait_impl.rs | 37 +++++++--------------- 1 file changed, 11 insertions(+), 26 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ba7d43f1e..2bf654a57 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -102,32 +102,17 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext } fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, ImplDef)> { - let (trigger_idx, trigger) = - ctx.token.ancestors().enumerate().find(|(_idx, p)| match p.kind() { - SyntaxKind::FN_DEF - | SyntaxKind::TYPE_ALIAS_DEF - | SyntaxKind::CONST_DEF - | SyntaxKind::NAME_REF - | SyntaxKind::BLOCK_EXPR => true, - _ => false, - })?; - let (impl_def_idx, impl_def) = - ctx.token.ancestors().enumerate().skip(trigger_idx + 1).find_map(|(idx, p)| { - match p.kind() { - SyntaxKind::IMPL_DEF => ast::ImplDef::cast(p).map(|p| (idx, p)), - _ => None, - } - })?; - let _is_nested = ctx - .token - .ancestors() - .skip(trigger_idx + 1) - .take(impl_def_idx - trigger_idx - 1) - .find_map(|p| match p.kind() { - SyntaxKind::FN_DEF | SyntaxKind::BLOCK => Some(()), - _ => None, - }) - .xor(Some(()))?; + let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() { + SyntaxKind::FN_DEF + | SyntaxKind::TYPE_ALIAS_DEF + | SyntaxKind::CONST_DEF + | SyntaxKind::BLOCK_EXPR => Some((p, 2)), + SyntaxKind::NAME_REF => Some((p, 5)), + _ => None, + })?; + let impl_def = (0..impl_def_offset - 1) + .try_fold(trigger.parent()?, |t, _| t.parent()) + .and_then(ast::ImplDef::cast)?; Some((trigger, impl_def)) } -- cgit v1.2.3