From fc46ed81ee54d342d83e428ca74cbb0083547251 Mon Sep 17 00:00:00 2001 From: Kevin DeLorey <2295721+kdelorey@users.noreply.github.com> Date: Tue, 4 Feb 2020 22:04:57 -0600 Subject: Add detection for a user already starting a fn impl and still providing completion. --- .../ra_ide/src/completion/complete_trait_impl.rs | 162 ++++++++++++++++++++- crates/ra_ide/src/completion/presentation.rs | 33 ----- 2 files changed, 157 insertions(+), 38 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 52ad7dd9d..c94ebee82 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -1,9 +1,9 @@ -use crate::completion::{CompletionContext, Completions}; +use crate::completion::{CompletionContext, Completions, CompletionItem, CompletionKind, CompletionItemKind}; use ast::{ NameOwner }; use hir::{ self, db::HirDatabase }; -use ra_syntax::{ ast, ast::AstNode }; +use ra_syntax::{ SyntaxKind, ast, ast::AstNode, TextRange }; pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { let item_list = ast::ItemList::cast(ctx.token.parent()); @@ -24,6 +24,37 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext return; } + // for cases where the user has already started writing the function def, navigate + // the previous tokens in order to find the location of that token so that we may + // replace it with our completion. + let start_position = { + let mut prev_token = ctx.token + .prev_token() + .clone(); + + while let Some(token) = &prev_token { + match token.kind() { + SyntaxKind::FN_KW => break, + + // todo: attempt to find a better way of determining when to stop as + // the following feels sketchy. + SyntaxKind::IMPL_KW | + SyntaxKind::L_CURLY | + SyntaxKind::R_CURLY => { + prev_token = None; + break; + } + _ => {} + } + + prev_token = token.prev_token().clone(); + } + + prev_token + .map(|t| t.text_range()) + .unwrap_or(ctx.source_range()) + }; + let trait_ = target_trait.unwrap(); let trait_items = trait_.items(ctx.db); @@ -97,7 +128,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext for item in missing_items { match item { - hir::AssocItem::Function(f) => acc.add_function_impl(ctx, f), + hir::AssocItem::Function(f) => add_function_impl(acc, ctx, f, start_position), _ => {} } } @@ -122,6 +153,40 @@ fn resolve_target_trait( } } +pub(crate) fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir::Function, start: TextRange) { + use crate::display::FunctionSignature; + + let display = FunctionSignature::from_hir(ctx.db, func.clone()); + + let func_name = func.name(ctx.db); + + let label = if func.params(ctx.db).len() > 0 { + format!("fn {}(..)", func_name.to_string()) + } else { + format!("fn {}()", func_name.to_string()) + }; + + let builder = CompletionItem::new(CompletionKind::Reference, start, label); + + let completion_kind = if func.has_self_param(ctx.db) { + CompletionItemKind::Method + } else { + CompletionItemKind::Function + }; + + let snippet = { + let mut s = format!("{}", display); + s.push_str(" {}"); + s + }; + + builder + .insert_text(snippet) + .kind(completion_kind) + .lookup_by(func_name.to_string()) + .add_to(acc); +} + #[cfg(test)] mod tests { use crate::completion::{do_completion, CompletionItem, CompletionKind}; @@ -152,7 +217,7 @@ mod tests { label: "fn foo()", source_range: [138; 138), delete: [138; 138), - insert: "fn foo() { $0}", + insert: "fn foo() {}", kind: Function, lookup: "foo", }, @@ -184,11 +249,98 @@ mod tests { label: "fn bar()", source_range: [193; 193), delete: [193; 193), - insert: "fn bar() { $0}", + insert: "fn bar() {}", kind: Function, lookup: "bar", }, ] "###); } + + #[test] + fn generic_fn() { + let completions = complete( + r" + trait Test { + fn foo(); + } + + struct T1; + + impl Test for T1 { + <|> + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "fn foo()", + source_range: [141; 141), + delete: [141; 141), + insert: "fn foo() {}", + kind: Function, + lookup: "foo", + }, + ] + "###); + } + + #[test] + fn generic_constrait_fn() { + let completions = complete( + r" + trait Test { + fn foo() where T: Into; + } + + struct T1; + + impl Test for T1 { + <|> + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "fn foo()", + source_range: [163; 163), + delete: [163; 163), + insert: "fn foo()\nwhere T: Into {}", + kind: Function, + lookup: "foo", + }, + ] + "###); + } + + #[test] + fn start_from_fn_kw() { + let completions = complete( + r" + trait Test { + fn foo(); + } + + struct T1; + + impl Test for T1 { + fn <|> + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "fn foo()", + source_range: [138; 140), + delete: [138; 140), + insert: "fn foo() {}", + kind: Function, + lookup: "foo", + }, + ] + "###); + } } \ No newline at end of file diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 0689013ba..97475fc0b 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -129,39 +129,6 @@ impl Completions { self.add_function_with_name(ctx, None, func) } - pub(crate) fn add_function_impl(&mut self, ctx: &CompletionContext, func: &hir::Function) { - use crate::display::FunctionSignature; - - let display = FunctionSignature::from_hir(ctx.db, func.clone()); - - let func_name = func.name(ctx.db); - - let mut builder = CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - format!("fn {}()", func_name.to_string())) - .set_documentation(func.docs(ctx.db)); - - let completion_kind = if func.has_self_param(ctx.db) { - CompletionItemKind::Method - } else { - CompletionItemKind::Function - }; - - let snippet = { - let mut s = format!("{}", display); - s.push_str(" { $0}"); - s - }; - - builder = builder - .insert_text(snippet) - .kind(completion_kind) - .lookup_by(func_name.to_string()); - - self.add(builder.build()); - } - fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { let mut votes = [0, 0, 0]; for (idx, s) in docs.match_indices(¯o_name) { -- cgit v1.2.3