Skip to content

Commit a84d92f

Browse files
Merge pull request #21508 from A4-Tacks/move-guard-else-if
Support else-branch for move_guard
2 parents 631e3ab + 786b30d commit a84d92f

1 file changed

Lines changed: 154 additions & 9 deletions

File tree

crates/ide-assists/src/handlers/move_guard.rs

Lines changed: 154 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use itertools::Itertools;
1+
use itertools::{Itertools, chain};
22
use syntax::{
33
SyntaxKind::WHITESPACE,
4+
TextRange,
45
ast::{
56
AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make,
67
prec::ExprPrecedence, syntax_factory::SyntaxFactory,
@@ -44,13 +45,26 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
4445
cov_mark::hit!(move_guard_inapplicable_in_arm_body);
4546
return None;
4647
}
47-
let space_before_guard = guard.syntax().prev_sibling_or_token();
48+
let rest_arms = rest_arms(&match_arm, ctx.selection_trimmed())?;
49+
let space_before_delete = chain(
50+
guard.syntax().prev_sibling_or_token(),
51+
rest_arms.iter().filter_map(|it| it.syntax().prev_sibling_or_token()),
52+
);
4853
let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token();
4954

50-
let guard_condition = guard.condition()?.reset_indent();
5155
let arm_expr = match_arm.expr()?;
52-
let then_branch = crate::utils::wrap_block(&arm_expr);
53-
let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level());
56+
let if_branch = chain([&match_arm], &rest_arms)
57+
.rfold(None, |else_branch, arm| {
58+
if let Some(guard) = arm.guard() {
59+
let then_branch = crate::utils::wrap_block(&arm.expr()?);
60+
let guard_condition = guard.condition()?.reset_indent();
61+
Some(make::expr_if(guard_condition, then_branch, else_branch).into())
62+
} else {
63+
arm.expr().map(|it| crate::utils::wrap_block(&it).into())
64+
}
65+
})?
66+
.indent(arm_expr.indent_level());
67+
let ElseBranch::IfExpr(if_expr) = if_branch else { return None };
5468

5569
let target = guard.syntax().text_range();
5670
acc.add(
@@ -59,10 +73,13 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
5973
target,
6074
|builder| {
6175
let mut edit = builder.make_editor(match_arm.syntax());
62-
if let Some(element) = space_before_guard
63-
&& element.kind() == WHITESPACE
64-
{
65-
edit.delete(element);
76+
for element in space_before_delete {
77+
if element.kind() == WHITESPACE {
78+
edit.delete(element);
79+
}
80+
}
81+
for rest_arm in &rest_arms {
82+
edit.delete(rest_arm.syntax());
6683
}
6784
if let Some(element) = space_after_arrow
6885
&& element.kind() == WHITESPACE
@@ -221,6 +238,25 @@ pub(crate) fn move_arm_cond_to_match_guard(
221238
)
222239
}
223240

241+
fn rest_arms(match_arm: &MatchArm, selection: TextRange) -> Option<Vec<MatchArm>> {
242+
match_arm
243+
.parent_match()
244+
.match_arm_list()?
245+
.arms()
246+
.skip_while(|it| it != match_arm)
247+
.skip(1)
248+
.take_while(move |it| {
249+
selection.is_empty() || crate::utils::is_selected(it, selection, false)
250+
})
251+
.take_while(move |it| {
252+
it.pat()
253+
.zip(match_arm.pat())
254+
.is_some_and(|(a, b)| a.syntax().text() == b.syntax().text())
255+
})
256+
.collect::<Vec<_>>()
257+
.into()
258+
}
259+
224260
// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
225261
// branch or the end.
226262
fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
@@ -344,6 +380,115 @@ fn main() {
344380
);
345381
}
346382

383+
#[test]
384+
fn move_multiple_guard_to_arm_body_works() {
385+
check_assist(
386+
move_guard_to_arm_body,
387+
r#"
388+
fn main() {
389+
match 92 {
390+
x @ 0..30 $0if x % 3 == 0 => false,
391+
x @ 0..30 if x % 2 == 0 => true,
392+
_ => false
393+
}
394+
}
395+
"#,
396+
r#"
397+
fn main() {
398+
match 92 {
399+
x @ 0..30 => if x % 3 == 0 {
400+
false
401+
} else if x % 2 == 0 {
402+
true
403+
},
404+
_ => false
405+
}
406+
}
407+
"#,
408+
);
409+
410+
check_assist(
411+
move_guard_to_arm_body,
412+
r#"
413+
fn main() {
414+
match 92 {
415+
x @ 0..30 $0if x % 3 == 0 => false,
416+
x @ 0..30 if x % 2 == 0 => true,
417+
x @ 0..30 => false,
418+
_ => true
419+
}
420+
}
421+
"#,
422+
r#"
423+
fn main() {
424+
match 92 {
425+
x @ 0..30 => if x % 3 == 0 {
426+
false
427+
} else if x % 2 == 0 {
428+
true
429+
} else {
430+
false
431+
},
432+
_ => true
433+
}
434+
}
435+
"#,
436+
);
437+
438+
check_assist(
439+
move_guard_to_arm_body,
440+
r#"
441+
fn main() {
442+
match 92 {
443+
x @ 0..30 if x % 3 == 0 => false,
444+
x @ 0..30 $0if x % 2 == 0$0 => true,
445+
x @ 0..30 => false,
446+
_ => true
447+
}
448+
}
449+
"#,
450+
r#"
451+
fn main() {
452+
match 92 {
453+
x @ 0..30 if x % 3 == 0 => false,
454+
x @ 0..30 => if x % 2 == 0 {
455+
true
456+
},
457+
x @ 0..30 => false,
458+
_ => true
459+
}
460+
}
461+
"#,
462+
);
463+
464+
check_assist(
465+
move_guard_to_arm_body,
466+
r#"
467+
fn main() {
468+
match 92 {
469+
x @ 0..30 $0if x % 3 == 0 => false,
470+
x @ 0..30 $0if x % 2 == 0 => true,
471+
x @ 0..30 => false,
472+
_ => true
473+
}
474+
}
475+
"#,
476+
r#"
477+
fn main() {
478+
match 92 {
479+
x @ 0..30 => if x % 3 == 0 {
480+
false
481+
} else if x % 2 == 0 {
482+
true
483+
},
484+
x @ 0..30 => false,
485+
_ => true
486+
}
487+
}
488+
"#,
489+
);
490+
}
491+
347492
#[test]
348493
fn move_guard_to_block_arm_body_works() {
349494
check_assist(

0 commit comments

Comments
 (0)