diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 1972f166134a..2c7d9e97c44e 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path( after_if_expr, in_condition, incomplete_let, + after_incomplete_let, in_value, ref ref_expr_parent, after_amp, @@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path( add_keyword("let", "let $1 = $0;"); } - if after_if_expr { + if after_if_expr || after_incomplete_let { add_keyword("else", "else {\n $0\n}"); + } + + if after_if_expr { add_keyword("else if", "else if $1 {\n $0\n}"); } diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index aea4e119f207..b3d770997ab0 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -247,6 +247,46 @@ fn main() { "#, ); + check_edit( + "else", + r#" +fn main() { + let x = if true { + () + } $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = if true { + () + } else { + $0 +}; + let y = 92; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let x = 2 $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = 2 else { + $0 +}; + let y = 92; +} +"#, + ); + check_edit( "loop", r#" diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 265462d22016..007475688d20 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, + pub(crate) after_incomplete_let: bool, pub(crate) in_value: bool, pub(crate) ref_expr_parent: Option, pub(crate) after_amp: bool, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 17978b4c1079..33b98a33cabf 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -947,25 +947,29 @@ fn classify_name_ref<'db>( None } }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = (|| { - let node = match node.parent().and_then(ast::ExprStmt::cast) { - Some(stmt) => stmt.syntax().clone(), - None => node, - }; - let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; + let prev_expr = |node: SyntaxNode| { + let node = match node.parent().and_then(ast::ExprStmt::cast) { + Some(stmt) => stmt.syntax().clone(), + None => node, + }; + let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - match_ast! { - match prev_sibling { - ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), - ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), - ast::Expr(expr) => Some(expr), - _ => None, - } + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, } - })(); + } + }; + 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) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>( }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let after_incomplete_let = after_incomplete_let(it.clone()).is_some(); + let incomplete_expr_stmt = + it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none()); let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .is_some_and(|it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()) + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true); let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); @@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>( self_param, in_value, incomplete_let, + after_incomplete_let, impl_, in_match_guard, }, diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 7a0d00444129..56fbd91a60fc 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -450,6 +450,155 @@ fn completes_in_let_initializer() { ) } +#[test] +fn completes_let_else() { + check( + r#"fn main() { let _ = 2 $0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + 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 main() { let _ = 2 el$0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + 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_edit( + "else", + r#" +fn main() { + let _ = 2 $0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 $0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); +} + #[test] fn completes_after_ref_expr() { check(