From 70157f07d9a33e4b8ed71e162218a793f44a7691 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Mon, 12 Oct 2020 10:59:15 +0300 Subject: Remove redundant completions --- crates/ide/src/completion/completion_context.rs | 22 ++++++++++++++++++---- crates/ide/src/completion/patterns.rs | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'crates/ide/src/completion') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 8dea8a4bf..c9473cca6 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -16,10 +16,10 @@ use crate::{ call_info::ActiveParameter, completion::{ patterns::{ - has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, - has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, - has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, - is_in_loop_body, is_match_arm, unsafe_is_prev, + fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, + has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, + has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, + has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev, }, CompletionConfig, }, @@ -91,6 +91,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) impl_as_prev_sibling: bool, pub(super) is_match_arm: bool, pub(super) has_item_list_or_source_file_parent: bool, + pub(super) for_is_prev2: bool, + pub(super) fn_is_prev: bool, pub(super) locals: Vec<(String, Local)>, } @@ -174,6 +176,8 @@ impl<'a> CompletionContext<'a> { if_is_prev: false, is_match_arm: false, has_item_list_or_source_file_parent: false, + for_is_prev2: false, + fn_is_prev: false, locals, }; @@ -221,6 +225,14 @@ impl<'a> CompletionContext<'a> { Some(ctx) } + /// Checks whether completions in that particular case don't make much sense. + /// Examples: + /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful. + /// - `for _ i<|>` -- obviously, it'll be "in" keyword. + pub(crate) fn no_completion_required(&self) -> bool { + self.fn_is_prev || self.for_is_prev2 + } + /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { // check kind of macro-expanded token, but use range of original token @@ -253,6 +265,8 @@ impl<'a> CompletionContext<'a> { self.mod_declaration_under_caret = find_node_at_offset::(&file_with_fake_ident, offset) .filter(|module| module.item_list().is_none()); + self.for_is_prev2 = for_is_prev2(syntax_element.clone()); + self.fn_is_prev = fn_is_prev(syntax_element.clone()); } fn fill( diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index b17ddf133..76fcad631 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -116,6 +116,25 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { .is_some() } +pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { + element + .into_token() + .and_then(|it| previous_non_trivia_token(it)) + .filter(|it| it.kind() == FN_KW) + .is_some() +} + +/// Check if the token previous to the previous one is `for`. +/// For example, `for _ i<|>` => true. +pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { + element + .into_token() + .and_then(|it| previous_non_trivia_token(it)) + .and_then(|it| previous_non_trivia_token(it)) + .filter(|it| it.kind() == FOR_KW) + .is_some() +} + #[test] fn test_if_is_prev() { check_pattern_is_applicable(r"if l<|>", if_is_prev); -- cgit v1.2.3 From 8f303daf458ae798b678d7e908ce5b2f27504111 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 17 Oct 2020 10:56:00 +0300 Subject: Add test for new pattern functions --- crates/ide/src/completion/patterns.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'crates/ide/src/completion') diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index 76fcad631..f00ddeed7 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -123,6 +123,10 @@ pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { .filter(|it| it.kind() == FN_KW) .is_some() } +#[test] +fn test_fn_is_prev() { + check_pattern_is_applicable(r"fn l<|>", fn_is_prev); +} /// Check if the token previous to the previous one is `for`. /// For example, `for _ i<|>` => true. @@ -134,6 +138,10 @@ pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { .filter(|it| it.kind() == FOR_KW) .is_some() } +#[test] +fn test_for_is_prev2() { + check_pattern_is_applicable(r"for i i<|>", for_is_prev2); +} #[test] fn test_if_is_prev() { -- cgit v1.2.3 From 6f573bd84f4564f11b08db720401ae16a0f42f2f Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 17 Oct 2020 11:03:07 +0300 Subject: Allow hints after 'fn' keyword if it's an impl trait block --- crates/ide/src/completion/completion_context.rs | 11 ++++++++--- crates/ide/src/completion/patterns.rs | 18 +++++++++++++++++- crates/ide/src/completion/test_utils.rs | 12 ++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) (limited to 'crates/ide/src/completion') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index c9473cca6..cff0afae9 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -18,8 +18,9 @@ use crate::{ patterns::{ fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, - has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, - has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev, + has_impl_trait_parent, has_item_list_or_source_file_parent, has_ref_parent, + has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, + unsafe_is_prev, }, CompletionConfig, }, @@ -86,6 +87,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) in_loop_body: bool, pub(super) has_trait_parent: bool, pub(super) has_impl_parent: bool, + pub(super) has_impl_trait_parent: bool, pub(super) has_field_list_parent: bool, pub(super) trait_as_prev_sibling: bool, pub(super) impl_as_prev_sibling: bool, @@ -170,6 +172,7 @@ impl<'a> CompletionContext<'a> { block_expr_parent: false, has_trait_parent: false, has_impl_parent: false, + has_impl_trait_parent: false, has_field_list_parent: false, trait_as_prev_sibling: false, impl_as_prev_sibling: false, @@ -228,9 +231,10 @@ impl<'a> CompletionContext<'a> { /// Checks whether completions in that particular case don't make much sense. /// Examples: /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful. + /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. /// - `for _ i<|>` -- obviously, it'll be "in" keyword. pub(crate) fn no_completion_required(&self) -> bool { - self.fn_is_prev || self.for_is_prev2 + (self.fn_is_prev && !self.has_impl_trait_parent) || self.for_is_prev2 } /// The range of the identifier that is being completed. @@ -256,6 +260,7 @@ impl<'a> CompletionContext<'a> { self.in_loop_body = is_in_loop_body(syntax_element.clone()); self.has_trait_parent = has_trait_parent(syntax_element.clone()); self.has_impl_parent = has_impl_parent(syntax_element.clone()); + self.has_impl_trait_parent = has_impl_trait_parent(syntax_element.clone()); self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index f00ddeed7..bdce7a6e7 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -9,7 +9,7 @@ use syntax::{ }; #[cfg(test)] -use crate::completion::test_utils::check_pattern_is_applicable; +use crate::completion::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element) @@ -34,6 +34,22 @@ pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { fn test_has_impl_parent() { check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); } + +pub(crate) fn has_impl_trait_parent(element: SyntaxElement) -> bool { + not_same_range_ancestor(element) + .filter(|it| it.kind() == ASSOC_ITEM_LIST) + .and_then(|it| it.parent()) + .filter(|it| it.kind() == IMPL) + .map(|it| ast::Impl::cast(it).unwrap()) + .map(|it| it.trait_().is_some()) + .unwrap_or(false) +} +#[test] +fn test_has_impl_trait_parent() { + check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", has_impl_trait_parent); + check_pattern_is_not_applicable(r"impl A { f<|> }", has_impl_trait_parent); +} + pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() } diff --git a/crates/ide/src/completion/test_utils.rs b/crates/ide/src/completion/test_utils.rs index feb8cd2a6..dabbef888 100644 --- a/crates/ide/src/completion/test_utils.rs +++ b/crates/ide/src/completion/test_utils.rs @@ -104,6 +104,18 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) - .unwrap(); } +pub(crate) fn check_pattern_is_not_applicable(code: &str, check: fn(SyntaxElement) -> bool) { + let (analysis, pos) = fixture::position(code); + analysis + .with_db(|db| { + let sema = Semantics::new(db); + let original_file = sema.parse(pos.file_id); + let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap(); + assert!(!check(NodeOrToken::Token(token))); + }) + .unwrap(); +} + pub(crate) fn get_all_completion_items( config: CompletionConfig, code: &str, -- cgit v1.2.3 From 99c435939c941fe5216c39bc136769098abbbfea Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 17 Oct 2020 11:17:58 +0300 Subject: Scan all ancestors for the impl trait block check --- crates/ide/src/completion/completion_context.rs | 12 ++++++------ crates/ide/src/completion/patterns.rs | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'crates/ide/src/completion') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index cff0afae9..d9f90477c 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -18,8 +18,8 @@ use crate::{ patterns::{ fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, - has_impl_trait_parent, has_item_list_or_source_file_parent, has_ref_parent, - has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, + has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, + has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm, unsafe_is_prev, }, CompletionConfig, @@ -87,7 +87,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) in_loop_body: bool, pub(super) has_trait_parent: bool, pub(super) has_impl_parent: bool, - pub(super) has_impl_trait_parent: bool, + pub(super) inside_impl_trait_block: bool, pub(super) has_field_list_parent: bool, pub(super) trait_as_prev_sibling: bool, pub(super) impl_as_prev_sibling: bool, @@ -172,7 +172,7 @@ impl<'a> CompletionContext<'a> { block_expr_parent: false, has_trait_parent: false, has_impl_parent: false, - has_impl_trait_parent: false, + inside_impl_trait_block: false, has_field_list_parent: false, trait_as_prev_sibling: false, impl_as_prev_sibling: false, @@ -234,7 +234,7 @@ impl<'a> CompletionContext<'a> { /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. /// - `for _ i<|>` -- obviously, it'll be "in" keyword. pub(crate) fn no_completion_required(&self) -> bool { - (self.fn_is_prev && !self.has_impl_trait_parent) || self.for_is_prev2 + (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 } /// The range of the identifier that is being completed. @@ -260,7 +260,7 @@ impl<'a> CompletionContext<'a> { self.in_loop_body = is_in_loop_body(syntax_element.clone()); self.has_trait_parent = has_trait_parent(syntax_element.clone()); self.has_impl_parent = has_impl_parent(syntax_element.clone()); - self.has_impl_trait_parent = has_impl_trait_parent(syntax_element.clone()); + self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index bdce7a6e7..cf6d5947d 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -35,19 +35,22 @@ fn test_has_impl_parent() { check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); } -pub(crate) fn has_impl_trait_parent(element: SyntaxElement) -> bool { - not_same_range_ancestor(element) - .filter(|it| it.kind() == ASSOC_ITEM_LIST) - .and_then(|it| it.parent()) - .filter(|it| it.kind() == IMPL) +pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { + // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, + // where we only check the first parent with different text range. + element + .ancestors() + .find(|it| it.kind() == IMPL) .map(|it| ast::Impl::cast(it).unwrap()) .map(|it| it.trait_().is_some()) .unwrap_or(false) } #[test] -fn test_has_impl_trait_parent() { - check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", has_impl_trait_parent); - check_pattern_is_not_applicable(r"impl A { f<|> }", has_impl_trait_parent); +fn test_inside_impl_trait_block() { + check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", inside_impl_trait_block); + check_pattern_is_applicable(r"impl Foo for Bar { fn f<|> }", inside_impl_trait_block); + check_pattern_is_not_applicable(r"impl A { f<|> }", inside_impl_trait_block); + check_pattern_is_not_applicable(r"impl A { fn f<|> }", inside_impl_trait_block); } pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { -- cgit v1.2.3