diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 815ce5145dbe..86d3ef047e11 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -42,6 +42,11 @@ pub(crate) fn complete_pattern( } } + if pattern_ctx.after_if_expr { + add_keyword("else", "else {\n $0\n}"); + add_keyword("else if", "else if $1 {\n $0\n}"); + } + if pattern_ctx.record_pat.is_some() { return; } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 007475688d20..01df8e11dcd1 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -276,6 +276,7 @@ pub(crate) struct PatternContext { pub(crate) param_ctx: Option, pub(crate) has_type_ascription: bool, pub(crate) should_suggest_name: bool, + pub(crate) after_if_expr: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 33b98a33cabf..9bc42fbfc8b5 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -963,10 +963,6 @@ fn classify_name_ref<'db>( } } }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = prev_expr(node); - matches!(prev_expr, Some(ast::Expr::IfExpr(_))) - }; let after_incomplete_let = |node: SyntaxNode| { prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) }; @@ -1214,7 +1210,7 @@ fn classify_name_ref<'db>( let it = expr.syntax(); let in_block_expr = is_in_block(it); let in_loop_body = is_in_breakable(it); - let after_if_expr = after_if_expr(it.clone()); + let after_if_expr = is_after_if_expr(it.clone()); let ref_expr_parent = path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast); let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev) @@ -1726,6 +1722,7 @@ fn pattern_context_for( param_ctx, has_type_ascription, should_suggest_name, + after_if_expr: is_after_if_expr(pat.syntax().clone()), parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), mut_token, ref_token, @@ -1870,6 +1867,18 @@ fn is_in_block(node: &SyntaxNode) -> bool { .unwrap_or(false) } +fn is_after_if_expr(node: SyntaxNode) -> bool { + let node = match node.parent().and_then(Either::::cast) { + Some(stmt) => stmt.syntax().clone(), + None => node, + }; + let prev_sibling = + non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node); + iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node()) + .find_map(ast::IfExpr::cast) + .is_some() +} + fn next_non_trivia_token(e: impl Into) -> Option { let mut token = match e.into() { SyntaxElement::Node(n) => n.last_token()?, diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 79137104b568..a8124d5786ca 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -1706,6 +1706,298 @@ fn foo() { let x = if foo {} $0 else {}; } sn ppd "#]], ); + check( + r#" +fn foo() { [if foo {} $0]} +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { [if foo {} el$0]} +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { 2 + if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { -if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { &mut if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { return if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { match () { () => if foo {} $0 } } +"#, + expect![[r#" + kw else + kw else if + kw mut + kw ref + "#]], + ); + check( + r#" +fn foo() { match () { () => if foo {} $0, } } +"#, + expect![[r#" + kw else + kw else if + kw mut + kw ref + "#]], + ); + check( + r#" +fn foo() { match () { () => if foo {} $0, _ => (), } } +"#, + expect![[r#" + kw else + kw else if + kw mut + kw ref + "#]], + ); + // FIXME: support else completion after ast::RecordExprField } #[test]