From 7de925b8abcf0d26869dc96f346510af61663edf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 27 May 2021 02:54:49 +0200 Subject: Collapse more CompletionContext booleans into enums --- crates/ide_completion/src/completions/flyimport.rs | 3 +- crates/ide_completion/src/completions/keyword.rs | 39 +++--- crates/ide_completion/src/completions/snippet.rs | 2 +- .../src/completions/unqualified_path.rs | 3 +- crates/ide_completion/src/context.rs | 143 ++++++++++++++------- crates/ide_completion/src/patterns.rs | 41 +++--- 6 files changed, 133 insertions(+), 98 deletions(-) (limited to 'crates/ide_completion') diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 9d5b61562..b7d3ee8ce 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -114,8 +114,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) || ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() || ctx.record_lit_syntax.is_some() - || ctx.has_trait_parent - || ctx.has_impl_parent + || ctx.has_impl_or_trait_parent() { return None; } diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index fa6bcc955..58e35bad9 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -49,34 +49,35 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte return; } - let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; - if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { + let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent(); + let has_block_expr_parent = ctx.has_block_expr_parent(); + let has_item_list_parent = ctx.has_item_list_parent(); + if ctx.has_impl_or_trait_prev_sibling() { add_keyword(ctx, acc, "where", "where "); return; } if ctx.previous_token_is(T![unsafe]) { - if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") } - if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); } return; } - if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent - { + if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); } - if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "use", "use "); add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); } - if ctx.has_item_list_or_source_file_parent { + if has_item_list_parent { add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); add_keyword(ctx, acc, "struct", "struct $0"); add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); @@ -92,7 +93,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); } - if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || ctx.block_expr_parent { + if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { add_keyword(ctx, acc, "let", "let "); } @@ -100,27 +101,23 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "else", "else {\n $0\n}"); add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); } - if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "mod", "mod $0"); } - if ctx.bind_pat_parent || ctx.ref_pat_parent { + if ctx.has_ident_or_ref_pat_parent() { add_keyword(ctx, acc, "mut", "mut "); } - if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent - { + if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { add_keyword(ctx, acc, "const", "const "); add_keyword(ctx, acc, "type", "type "); } - if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "static", "static "); }; - if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { + if has_item_list_parent || has_block_expr_parent { add_keyword(ctx, acc, "extern", "extern "); } - if ctx.has_item_list_or_source_file_parent - || has_trait_or_impl_parent - || ctx.block_expr_parent - || ctx.is_match_arm + if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm { add_keyword(ctx, acc, "unsafe", "unsafe "); } @@ -133,7 +130,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "break", "break"); } } - if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent { + if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() { add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); add_keyword(ctx, acc, "pub", "pub "); } @@ -141,7 +138,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte if !ctx.is_trivial_path { return; } - let fn_def = match &ctx.function_syntax { + let fn_def = match &ctx.function_def { Some(it) => it, None => return, }; diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index 14cfb61de..defc25b00 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs @@ -14,7 +14,7 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) } pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { - if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { + if !(ctx.is_trivial_path && ctx.function_def.is_some()) { return; } let cap = match ctx.config.snippet_cap { diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index b8f8ef25f..7496d26c4 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -13,8 +13,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC || ctx.record_pat_syntax.is_some() || ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() - || ctx.has_impl_parent - || ctx.has_trait_parent + || ctx.has_impl_or_trait_parent() { return; } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 6dc6769df..dfac8f29f 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -31,6 +31,24 @@ pub(crate) enum PatternRefutability { Irrefutable, } +/// Direct parent container of the cursor position +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum ImmediateLocation { + Impl, + Trait, + RecordFieldList, + RefPatOrExpr, + IdentPat, + BlockExpr, + ItemList, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PrevSibling { + Trait, + Impl, +} + /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -48,14 +66,19 @@ pub(crate) struct CompletionContext<'a> { pub(super) expected_name: Option, pub(super) expected_type: Option, pub(super) name_ref_syntax: Option, - pub(super) function_syntax: Option, + pub(super) use_item_syntax: Option, - pub(super) record_lit_syntax: Option, - pub(super) record_pat_syntax: Option, - pub(super) record_field_syntax: Option, + + /// The parent function of the cursor position if it exists. + pub(super) function_def: Option, /// The parent impl of the cursor position if it exists. pub(super) impl_def: Option, + /// RecordExpr the token is a field of + pub(super) record_lit_syntax: Option, + /// RecordPat the token is a field of + pub(super) record_pat_syntax: Option, + // potentially set if we are completing a lifetime pub(super) lifetime_syntax: Option, pub(super) lifetime_param_syntax: Option, @@ -66,6 +89,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_pat_or_const: Option, pub(super) is_param: bool, + pub(super) completion_location: Option, + /// FIXME: `ActiveParameter` is string-based, which is very very wrong pub(super) active_parameter: Option, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. @@ -94,20 +119,12 @@ pub(crate) struct CompletionContext<'a> { pub(super) locals: Vec<(String, Local)>, pub(super) mod_declaration_under_caret: Option, - pub(super) has_trait_parent: bool, - pub(super) has_impl_parent: bool, // keyword patterns pub(super) previous_token: Option, - pub(super) block_expr_parent: bool, - pub(super) bind_pat_parent: bool, - pub(super) ref_pat_parent: bool, pub(super) in_loop_body: bool, - pub(super) has_field_list_parent: bool, - pub(super) trait_as_prev_sibling: bool, - pub(super) impl_as_prev_sibling: bool, + pub(super) prev_sibling: Option, pub(super) is_match_arm: bool, - pub(super) has_item_list_or_source_file_parent: bool, pub(super) incomplete_let: bool, no_completion_required: bool, @@ -159,11 +176,10 @@ impl<'a> CompletionContext<'a> { name_ref_syntax: None, lifetime_syntax: None, lifetime_param_syntax: None, - function_syntax: None, + function_def: None, use_item_syntax: None, record_lit_syntax: None, record_pat_syntax: None, - record_field_syntax: None, impl_def: None, active_parameter: ActiveParameter::at(db, position), is_label_ref: false, @@ -185,17 +201,10 @@ impl<'a> CompletionContext<'a> { attribute_under_caret: None, mod_declaration_under_caret: None, previous_token: None, - block_expr_parent: false, - bind_pat_parent: false, - ref_pat_parent: false, in_loop_body: false, - has_trait_parent: false, - has_impl_parent: false, - has_field_list_parent: false, - trait_as_prev_sibling: false, - impl_as_prev_sibling: false, + completion_location: None, + prev_sibling: None, is_match_arm: false, - has_item_list_or_source_file_parent: false, no_completion_required: false, incomplete_let: false, locals, @@ -274,23 +283,68 @@ impl<'a> CompletionContext<'a> { self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) } + pub(crate) fn has_impl_or_trait_parent(&self) -> bool { + matches!( + self.completion_location, + Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) + ) + } + + pub(crate) fn has_block_expr_parent(&self) -> bool { + matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) + } + + pub(crate) fn has_item_list_parent(&self) -> bool { + matches!(self.completion_location, Some(ImmediateLocation::ItemList)) + } + + pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { + matches!( + self.completion_location, + Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) + ) + } + + pub(crate) fn has_impl_parent(&self) -> bool { + matches!(self.completion_location, Some(ImmediateLocation::Impl)) + } + + pub(crate) fn has_field_list_parent(&self) -> bool { + matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList)) + } + + pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { + self.prev_sibling.is_some() + } + fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); let syntax_element = NodeOrToken::Token(fake_ident_token); self.previous_token = previous_token(syntax_element.clone()); - self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); - self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); - self.ref_pat_parent = has_ref_parent(syntax_element.clone()); 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_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()); self.is_match_arm = is_match_arm(syntax_element.clone()); + if has_impl_as_prev_sibling(syntax_element.clone()) { + self.prev_sibling = Some(PrevSibling::Impl) + } else if has_trait_as_prev_sibling(syntax_element.clone()) { + self.prev_sibling = Some(PrevSibling::Trait) + } + + if has_block_expr_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::BlockExpr); + } else if has_bind_pat_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::IdentPat); + } else if has_ref_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::RefPatOrExpr); + } else if has_impl_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::Impl); + } else if has_field_list_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::RecordFieldList); + } else if has_trait_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::Trait); + } else if has_item_list_or_source_file_parent(syntax_element.clone()) { + self.completion_location = Some(ImmediateLocation::ItemList); + } - self.has_item_list_or_source_file_parent = - has_item_list_or_source_file_parent(syntax_element.clone()); self.mod_declaration_under_caret = find_node_at_offset::(&file_with_fake_ident, offset) .filter(|module| module.item_list().is_none()); @@ -542,31 +596,20 @@ impl<'a> CompletionContext<'a> { .last() .unwrap(); - match top_node.parent().map(|it| it.kind()) { - Some(SOURCE_FILE) | Some(ITEM_LIST) => { - self.is_new_item = true; - return; - } - _ => (), + if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) { + self.is_new_item = true; + return; } self.use_item_syntax = self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); - self.function_syntax = self + self.function_def = self .sema .token_ancestors_with_macros(self.token.clone()) .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) .find_map(ast::Fn::cast); - self.record_field_syntax = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| { - it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR - }) - .find_map(ast::RecordExprField::cast); - let parent = match name_ref.syntax().parent() { Some(it) => it, None => return, @@ -639,6 +682,7 @@ impl<'a> CompletionContext<'a> { } } } + if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { // The receiver comes before the point of insertion of the fake // ident, so it should have the same range in the non-modified file @@ -656,6 +700,7 @@ impl<'a> CompletionContext<'a> { false }; } + if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { // As above self.dot_receiver = method_call_expr diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 3d8a83ed8..8f0d76661 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -91,11 +91,10 @@ fn test_has_ref_parent() { } pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { - let ancestor = not_same_range_ancestor(element); - if !ancestor.is_some() { - return true; + match not_same_range_ancestor(element) { + Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST, + None => true, } - ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some() } #[test] fn test_has_item_list_or_source_file_parent() { @@ -151,25 +150,21 @@ fn test_has_impl_as_prev_sibling() { } pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { - for node in element.ancestors() { - if node.kind() == FN || node.kind() == CLOSURE_EXPR { - break; - } - let loop_body = match_ast! { - match node { - ast::ForExpr(it) => it.loop_body(), - ast::WhileExpr(it) => it.loop_body(), - ast::LoopExpr(it) => it.loop_body(), - _ => None, - } - }; - if let Some(body) = loop_body { - if body.syntax().text_range().contains_range(element.text_range()) { - return true; - } - } - } - false + element + .ancestors() + .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) + .find_map(|it| { + let loop_body = match_ast! { + match it { + ast::ForExpr(it) => it.loop_body(), + ast::WhileExpr(it) => it.loop_body(), + ast::LoopExpr(it) => it.loop_body(), + _ => None, + } + }; + loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range())) + }) + .is_some() } fn not_same_range_ancestor(element: SyntaxElement) -> Option { -- cgit v1.2.3