From f32081fa185b3a9df021f277c2c27fbd123d0951 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 25 Sep 2019 14:29:41 +0300 Subject: move assists to subdir --- crates/ra_assists/src/assists/merge_match_arms.rs | 188 ++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 crates/ra_assists/src/assists/merge_match_arms.rs (limited to 'crates/ra_assists/src/assists/merge_match_arms.rs') diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs new file mode 100644 index 000000000..3b6a99895 --- /dev/null +++ b/crates/ra_assists/src/assists/merge_match_arms.rs @@ -0,0 +1,188 @@ +use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit}; +use hir::db::HirDatabase; +use ra_syntax::ast::{AstNode, MatchArm}; + +pub(crate) fn merge_match_arms(mut ctx: AssistCtx) -> Option { + let current_arm = ctx.node_at_offset::()?; + + // We check if the following match arm matches this one. We could, but don't, + // compare to the previous match arm as well. + let next = current_arm.syntax().next_sibling(); + let next_arm = MatchArm::cast(next?)?; + + // Don't try to handle arms with guards for now - can add support for this later + if current_arm.guard().is_some() || next_arm.guard().is_some() { + return None; + } + + let current_expr = current_arm.expr()?; + let next_expr = next_arm.expr()?; + + // Check for match arm equality by comparing lengths and then string contents + if current_expr.syntax().text_range().len() != next_expr.syntax().text_range().len() { + return None; + } + if current_expr.syntax().text() != next_expr.syntax().text() { + return None; + } + + let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); + + ctx.add_action(AssistId("merge_match_arms"), "merge match arms", |edit| { + fn contains_placeholder(a: &MatchArm) -> bool { + a.pats().any(|x| match x { + ra_syntax::ast::Pat::PlaceholderPat(..) => true, + _ => false, + }) + } + + let pats = if contains_placeholder(¤t_arm) || contains_placeholder(&next_arm) { + "_".into() + } else { + let ps: Vec = current_arm + .pats() + .map(|x| x.syntax().to_string()) + .chain(next_arm.pats().map(|x| x.syntax().to_string())) + .collect(); + ps.join(" | ") + }; + + let arm = format!("{} => {}", pats, current_expr.syntax().text()); + let offset = TextUnit::from_usize(arm.len()) - cursor_to_end; + + let start = current_arm.syntax().text_range().start(); + let end = next_arm.syntax().text_range().end(); + + edit.target(current_arm.syntax().text_range()); + edit.replace(TextRange::from_to(start, end), arm); + edit.set_cursor(start + offset); + }); + + ctx.build() +} + +#[cfg(test)] +mod tests { + use super::merge_match_arms; + use crate::helpers::{check_assist, check_assist_not_applicable}; + + #[test] + fn merge_match_arms_single_patterns() { + check_assist( + merge_match_arms, + r#" + #[derive(Debug)] + enum X { A, B, C } + + fn main() { + let x = X::A; + let y = match x { + X::A => { 1i32<|> } + X::B => { 1i32 } + X::C => { 2i32 } + } + } + "#, + r#" + #[derive(Debug)] + enum X { A, B, C } + + fn main() { + let x = X::A; + let y = match x { + X::A | X::B => { 1i32<|> } + X::C => { 2i32 } + } + } + "#, + ); + } + + #[test] + fn merge_match_arms_multiple_patterns() { + check_assist( + merge_match_arms, + r#" + #[derive(Debug)] + enum X { A, B, C, D, E } + + fn main() { + let x = X::A; + let y = match x { + X::A | X::B => {<|> 1i32 }, + X::C | X::D => { 1i32 }, + X::E => { 2i32 }, + } + } + "#, + r#" + #[derive(Debug)] + enum X { A, B, C, D, E } + + fn main() { + let x = X::A; + let y = match x { + X::A | X::B | X::C | X::D => {<|> 1i32 }, + X::E => { 2i32 }, + } + } + "#, + ); + } + + #[test] + fn merge_match_arms_placeholder_pattern() { + check_assist( + merge_match_arms, + r#" + #[derive(Debug)] + enum X { A, B, C, D, E } + + fn main() { + let x = X::A; + let y = match x { + X::A => { 1i32 }, + X::B => { 2i<|>32 }, + _ => { 2i32 } + } + } + "#, + r#" + #[derive(Debug)] + enum X { A, B, C, D, E } + + fn main() { + let x = X::A; + let y = match x { + X::A => { 1i32 }, + _ => { 2i<|>32 } + } + } + "#, + ); + } + + #[test] + fn merge_match_arms_rejects_guards() { + check_assist_not_applicable( + merge_match_arms, + r#" + #[derive(Debug)] + enum X { + A(i32), + B, + C + } + + fn main() { + let x = X::A; + let y = match x { + X::A(a) if a > 5 => { <|>1i32 }, + X::B => { 1i32 }, + X::C => { 2i32 } + } + } + "#, + ); + } +} -- cgit v1.2.3