1 use std::iter::successors;
9 use crate::{Assist, AssistCtx, AssistId, TextRange};
11 // Assist: merge_match_arms
13 // Merges identical match arms.
16 // enum Action { Move { distance: u32 }, Stop }
18 // fn handle(action: Action) {
20 // <|>Action::Move(..) => foo(),
21 // Action::Stop => foo(),
27 // enum Action { Move { distance: u32 }, Stop }
29 // fn handle(action: Action) {
31 // Action::Move(..) | Action::Stop => foo(),
35 pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
36 let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
37 // Don't try to handle arms with guards for now - can add support for this later
38 if current_arm.guard().is_some() {
41 let current_expr = current_arm.expr()?;
42 let current_text_range = current_arm.syntax().text_range();
48 let cursor_pos = ctx.frange.range.start();
49 let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) {
50 CursorPos::InExpr(current_text_range.end() - cursor_pos)
52 CursorPos::InPat(cursor_pos)
55 // We check if the following match arms match this one. We could, but don't,
56 // compare to the previous match arm as well.
57 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
59 if arm.guard().is_some() {
63 Some(expr) => expr.syntax().text() == current_expr.syntax().text(),
69 if arms_to_merge.len() <= 1 {
73 ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| {
74 let pats = if arms_to_merge.iter().any(contains_placeholder) {
79 .filter_map(ast::MatchArm::pat)
80 .map(|x| x.syntax().to_string())
81 .collect::<Vec<String>>()
85 let arm = format!("{} => {}", pats, current_expr.syntax().text());
87 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
88 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
90 edit.set_cursor(match cursor_pos {
91 CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset,
92 CursorPos::InPat(offset) => offset,
94 edit.replace(TextRange::new(start, end), arm);
98 fn contains_placeholder(a: &ast::MatchArm) -> bool {
100 Some(ra_syntax::ast::Pat::PlaceholderPat(..)) => true,
107 use crate::tests::{check_assist, check_assist_not_applicable};
112 fn merge_match_arms_single_patterns() {
135 X::A | X::B => { 1i32<|> }
144 fn merge_match_arms_multiple_patterns() {
149 enum X { A, B, C, D, E }
154 X::A | X::B => {<|> 1i32 },
155 X::C | X::D => { 1i32 },
162 enum X { A, B, C, D, E }
167 X::A | X::B | X::C | X::D => {<|> 1i32 },
176 fn merge_match_arms_placeholder_pattern() {
181 enum X { A, B, C, D, E }
194 enum X { A, B, C, D, E }
208 fn merges_all_subsequent_arms() {
212 enum X { A, B, C, D, E }
225 enum X { A, B, C, D, E }
229 X::A<|> | X::B | X::C => 92,
239 fn merge_match_arms_rejects_guards() {
240 check_assist_not_applicable(
253 X::A(a) if a > 5 => { <|>1i32 },