Skip to content

Commit a91fb2b

Browse files
authored
Merge pull request #20620 from A4-Tacks/let-else-completion
fix: add `else` keyword completion after `let` statements
2 parents e0aa8d4 + 56114fc commit a91fb2b

File tree

5 files changed

+220
-17
lines changed

5 files changed

+220
-17
lines changed

crates/ide-completion/src/completions/expr.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path(
6161
after_if_expr,
6262
in_condition,
6363
incomplete_let,
64+
after_incomplete_let,
6465
in_value,
6566
ref ref_expr_parent,
6667
after_amp,
@@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path(
385386
add_keyword("let", "let $1 = $0;");
386387
}
387388

388-
if after_if_expr {
389+
if after_if_expr || after_incomplete_let {
389390
add_keyword("else", "else {\n $0\n}");
391+
}
392+
393+
if after_if_expr {
390394
add_keyword("else if", "else if $1 {\n $0\n}");
391395
}
392396

crates/ide-completion/src/completions/keyword.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,46 @@ fn main() {
247247
"#,
248248
);
249249

250+
check_edit(
251+
"else",
252+
r#"
253+
fn main() {
254+
let x = if true {
255+
()
256+
} $0
257+
let y = 92;
258+
}
259+
"#,
260+
r#"
261+
fn main() {
262+
let x = if true {
263+
()
264+
} else {
265+
$0
266+
};
267+
let y = 92;
268+
}
269+
"#,
270+
);
271+
272+
check_edit(
273+
"else",
274+
r#"
275+
fn main() {
276+
let x = 2 $0
277+
let y = 92;
278+
}
279+
"#,
280+
r#"
281+
fn main() {
282+
let x = 2 else {
283+
$0
284+
};
285+
let y = 92;
286+
}
287+
"#,
288+
);
289+
250290
check_edit(
251291
"loop",
252292
r#"

crates/ide-completion/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> {
147147
/// Whether this expression is the direct condition of an if or while expression
148148
pub(crate) in_condition: bool,
149149
pub(crate) incomplete_let: bool,
150+
pub(crate) after_incomplete_let: bool,
150151
pub(crate) in_value: bool,
151152
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
152153
pub(crate) after_amp: bool,

crates/ide-completion/src/context/analysis.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -947,25 +947,29 @@ fn classify_name_ref<'db>(
947947
None
948948
}
949949
};
950-
let after_if_expr = |node: SyntaxNode| {
951-
let prev_expr = (|| {
952-
let node = match node.parent().and_then(ast::ExprStmt::cast) {
953-
Some(stmt) => stmt.syntax().clone(),
954-
None => node,
955-
};
956-
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
950+
let prev_expr = |node: SyntaxNode| {
951+
let node = match node.parent().and_then(ast::ExprStmt::cast) {
952+
Some(stmt) => stmt.syntax().clone(),
953+
None => node,
954+
};
955+
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
957956

958-
match_ast! {
959-
match prev_sibling {
960-
ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
961-
ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
962-
ast::Expr(expr) => Some(expr),
963-
_ => None,
964-
}
957+
match_ast! {
958+
match prev_sibling {
959+
ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
960+
ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
961+
ast::Expr(expr) => Some(expr),
962+
_ => None,
965963
}
966-
})();
964+
}
965+
};
966+
let after_if_expr = |node: SyntaxNode| {
967+
let prev_expr = prev_expr(node);
967968
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
968969
};
970+
let after_incomplete_let = |node: SyntaxNode| {
971+
prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
972+
};
969973

970974
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
971975
// ex. trait Foo $0 {}
@@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>(
12651269
};
12661270
let is_func_update = func_update_record(it);
12671271
let in_condition = is_in_condition(&expr);
1272+
let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
1273+
let incomplete_expr_stmt =
1274+
it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none());
12681275
let incomplete_let = it
12691276
.parent()
12701277
.and_then(ast::LetStmt::cast)
1271-
.is_some_and(|it| it.semicolon_token().is_none());
1278+
.is_some_and(|it| it.semicolon_token().is_none())
1279+
|| after_incomplete_let && incomplete_expr_stmt.unwrap_or(true);
12721280
let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
12731281
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
12741282

@@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>(
12921300
self_param,
12931301
in_value,
12941302
incomplete_let,
1303+
after_incomplete_let,
12951304
impl_,
12961305
in_match_guard,
12971306
},

crates/ide-completion/src/tests/expression.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,155 @@ fn completes_in_let_initializer() {
450450
)
451451
}
452452

453+
#[test]
454+
fn completes_let_else() {
455+
check(
456+
r#"fn main() { let _ = 2 $0 }"#,
457+
expect![[r#"
458+
fn main() fn()
459+
bt u32 u32
460+
kw async
461+
kw const
462+
kw crate::
463+
kw else
464+
kw enum
465+
kw extern
466+
kw false
467+
kw fn
468+
kw for
469+
kw if
470+
kw if let
471+
kw impl
472+
kw impl for
473+
kw let
474+
kw letm
475+
kw loop
476+
kw match
477+
kw mod
478+
kw return
479+
kw self::
480+
kw static
481+
kw struct
482+
kw trait
483+
kw true
484+
kw type
485+
kw union
486+
kw unsafe
487+
kw use
488+
kw while
489+
kw while let
490+
sn macro_rules
491+
sn pd
492+
sn ppd
493+
"#]],
494+
);
495+
496+
check(
497+
r#"fn main() { let _ = 2 el$0 }"#,
498+
expect![[r#"
499+
fn main() fn()
500+
bt u32 u32
501+
kw async
502+
kw const
503+
kw crate::
504+
kw else
505+
kw enum
506+
kw extern
507+
kw false
508+
kw fn
509+
kw for
510+
kw if
511+
kw if let
512+
kw impl
513+
kw impl for
514+
kw let
515+
kw letm
516+
kw loop
517+
kw match
518+
kw mod
519+
kw return
520+
kw self::
521+
kw static
522+
kw struct
523+
kw trait
524+
kw true
525+
kw type
526+
kw union
527+
kw unsafe
528+
kw use
529+
kw while
530+
kw while let
531+
sn macro_rules
532+
sn pd
533+
sn ppd
534+
"#]],
535+
);
536+
537+
check_edit(
538+
"else",
539+
r#"
540+
fn main() {
541+
let _ = 2 $0
542+
}
543+
"#,
544+
r#"
545+
fn main() {
546+
let _ = 2 else {
547+
$0
548+
};
549+
}
550+
"#,
551+
);
552+
553+
check_edit(
554+
"else",
555+
r#"
556+
fn main() {
557+
let _ = 2 el$0
558+
}
559+
"#,
560+
r#"
561+
fn main() {
562+
let _ = 2 else {
563+
$0
564+
};
565+
}
566+
"#,
567+
);
568+
569+
check_edit(
570+
"else",
571+
r#"
572+
fn main() {
573+
let _ = 2 $0;
574+
}
575+
"#,
576+
r#"
577+
fn main() {
578+
let _ = 2 else {
579+
$0
580+
};
581+
}
582+
"#,
583+
);
584+
585+
check_edit(
586+
"else",
587+
r#"
588+
fn main() {
589+
let _ = 2 el$0;
590+
}
591+
"#,
592+
r#"
593+
fn main() {
594+
let _ = 2 else {
595+
$0
596+
};
597+
}
598+
"#,
599+
);
600+
}
601+
453602
#[test]
454603
fn completes_after_ref_expr() {
455604
check(

0 commit comments

Comments
 (0)