Skip to content

Commit 28acd01

Browse files
committed
Merge match arms works with many arms
1 parent 45dd90b commit 28acd01

File tree

1 file changed

+70
-29
lines changed

1 file changed

+70
-29
lines changed

crates/ra_assists/src/assists/merge_match_arms.rs

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
use std::iter::successors;
2+
13
use hir::db::HirDatabase;
2-
use ra_syntax::ast::{self, AstNode};
4+
use ra_syntax::{
5+
ast::{self, AstNode},
6+
Direction, TextUnit,
7+
};
38

4-
use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit};
9+
use crate::{Assist, AssistCtx, AssistId, TextRange};
510

611
// Assist: merge_match_arms
712
//
@@ -29,51 +34,52 @@ use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit};
2934
// ```
3035
pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3136
let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
32-
33-
// We check if the following match arm matches this one. We could, but don't,
34-
// compare to the previous match arm as well.
35-
let next = current_arm.syntax().next_sibling();
36-
let next_arm = ast::MatchArm::cast(next?)?;
37-
3837
// Don't try to handle arms with guards for now - can add support for this later
39-
if current_arm.guard().is_some() || next_arm.guard().is_some() {
38+
if current_arm.guard().is_some() {
4039
return None;
4140
}
42-
4341
let current_expr = current_arm.expr()?;
44-
let next_expr = next_arm.expr()?;
42+
let current_text_range = current_arm.syntax().text_range();
43+
let cursor_offset_back = current_text_range.end() - ctx.frange.range.start();
4544

46-
// Check for match arm equality by comparing lengths and then string contents
47-
if current_expr.syntax().text_range().len() != next_expr.syntax().text_range().len() {
48-
return None;
49-
}
50-
if current_expr.syntax().text() != next_expr.syntax().text() {
45+
// We check if the following match arms match this one. We could, but don't,
46+
// compare to the previous match arm as well.
47+
let arms_to_merge = successors(Some(current_arm), next_arm)
48+
.take_while(|arm| {
49+
if arm.guard().is_some() {
50+
return false;
51+
}
52+
match arm.expr() {
53+
Some(expr) => expr.syntax().text() == current_expr.syntax().text(),
54+
None => false,
55+
}
56+
})
57+
.collect::<Vec<_>>();
58+
59+
if arms_to_merge.len() <= 1 {
5160
return None;
5261
}
5362

54-
let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start();
55-
5663
ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| {
57-
let pats = if contains_placeholder(&current_arm) || contains_placeholder(&next_arm) {
64+
let pats = if arms_to_merge.iter().any(contains_placeholder) {
5865
"_".into()
5966
} else {
60-
let ps: Vec<String> = current_arm
61-
.pats()
67+
arms_to_merge
68+
.iter()
69+
.flat_map(ast::MatchArm::pats)
6270
.map(|x| x.syntax().to_string())
63-
.chain(next_arm.pats().map(|x| x.syntax().to_string()))
64-
.collect();
65-
ps.join(" | ")
71+
.collect::<Vec<String>>()
72+
.join(" | ")
6673
};
6774

6875
let arm = format!("{} => {}", pats, current_expr.syntax().text());
69-
let offset = TextUnit::from_usize(arm.len()) - cursor_to_end;
7076

71-
let start = current_arm.syntax().text_range().start();
72-
let end = next_arm.syntax().text_range().end();
77+
let start = arms_to_merge.first().unwrap().syntax().text_range().start();
78+
let end = arms_to_merge.last().unwrap().syntax().text_range().end();
7379

74-
edit.target(current_arm.syntax().text_range());
80+
edit.target(current_text_range);
81+
edit.set_cursor(start + TextUnit::from_usize(arm.len()) - cursor_offset_back);
7582
edit.replace(TextRange::from_to(start, end), arm);
76-
edit.set_cursor(start + offset);
7783
})
7884
}
7985

@@ -84,6 +90,10 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
8490
})
8591
}
8692

93+
fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> {
94+
arm.syntax().siblings(Direction::Next).skip(1).find_map(ast::MatchArm::cast)
95+
}
96+
8797
#[cfg(test)]
8898
mod tests {
8999
use super::merge_match_arms;
@@ -185,6 +195,37 @@ mod tests {
185195
);
186196
}
187197

198+
#[test]
199+
fn merges_all_subsequent_arms() {
200+
check_assist(
201+
merge_match_arms,
202+
r#"
203+
enum X { A, B, C, D, E }
204+
205+
fn main() {
206+
match X::A {
207+
X::A =><|> 92,
208+
X::B => 92,
209+
X::C => 92,
210+
X::D => 62,
211+
_ => panic!(),
212+
}
213+
}
214+
"#,
215+
r#"
216+
enum X { A, B, C, D, E }
217+
218+
fn main() {
219+
match X::A {
220+
X::A | X::B | X::C =><|> 92,
221+
X::D => 62,
222+
_ => panic!(),
223+
}
224+
}
225+
"#,
226+
)
227+
}
228+
188229
#[test]
189230
fn merge_match_arms_rejects_guards() {
190231
check_assist_not_applicable(

0 commit comments

Comments
 (0)