aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/merge_match_arms.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-09-25 12:35:50 +0100
committerGitHub <[email protected]>2019-09-25 12:35:50 +0100
commit8f92309dbc00a0f88563030921706f205c01f452 (patch)
tree3e3f08ada8011f300faae3966cbe61a4a7dd7377 /crates/ra_assists/src/assists/merge_match_arms.rs
parentd9b4835625ac96c7628e2ef66ef6e26cc48d231f (diff)
parent69689625ce4465f2d008d6543553d0d91d53dca4 (diff)
Merge #1909
1909: move ast builder to a separate file r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/assists/merge_match_arms.rs')
-rw-r--r--crates/ra_assists/src/assists/merge_match_arms.rs188
1 files changed, 188 insertions, 0 deletions
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 @@
1use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit};
2use hir::db::HirDatabase;
3use ra_syntax::ast::{AstNode, MatchArm};
4
5pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
6 let current_arm = ctx.node_at_offset::<MatchArm>()?;
7
8 // We check if the following match arm matches this one. We could, but don't,
9 // compare to the previous match arm as well.
10 let next = current_arm.syntax().next_sibling();
11 let next_arm = MatchArm::cast(next?)?;
12
13 // Don't try to handle arms with guards for now - can add support for this later
14 if current_arm.guard().is_some() || next_arm.guard().is_some() {
15 return None;
16 }
17
18 let current_expr = current_arm.expr()?;
19 let next_expr = next_arm.expr()?;
20
21 // Check for match arm equality by comparing lengths and then string contents
22 if current_expr.syntax().text_range().len() != next_expr.syntax().text_range().len() {
23 return None;
24 }
25 if current_expr.syntax().text() != next_expr.syntax().text() {
26 return None;
27 }
28
29 let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start();
30
31 ctx.add_action(AssistId("merge_match_arms"), "merge match arms", |edit| {
32 fn contains_placeholder(a: &MatchArm) -> bool {
33 a.pats().any(|x| match x {
34 ra_syntax::ast::Pat::PlaceholderPat(..) => true,
35 _ => false,
36 })
37 }
38
39 let pats = if contains_placeholder(&current_arm) || contains_placeholder(&next_arm) {
40 "_".into()
41 } else {
42 let ps: Vec<String> = current_arm
43 .pats()
44 .map(|x| x.syntax().to_string())
45 .chain(next_arm.pats().map(|x| x.syntax().to_string()))
46 .collect();
47 ps.join(" | ")
48 };
49
50 let arm = format!("{} => {}", pats, current_expr.syntax().text());
51 let offset = TextUnit::from_usize(arm.len()) - cursor_to_end;
52
53 let start = current_arm.syntax().text_range().start();
54 let end = next_arm.syntax().text_range().end();
55
56 edit.target(current_arm.syntax().text_range());
57 edit.replace(TextRange::from_to(start, end), arm);
58 edit.set_cursor(start + offset);
59 });
60
61 ctx.build()
62}
63
64#[cfg(test)]
65mod tests {
66 use super::merge_match_arms;
67 use crate::helpers::{check_assist, check_assist_not_applicable};
68
69 #[test]
70 fn merge_match_arms_single_patterns() {
71 check_assist(
72 merge_match_arms,
73 r#"
74 #[derive(Debug)]
75 enum X { A, B, C }
76
77 fn main() {
78 let x = X::A;
79 let y = match x {
80 X::A => { 1i32<|> }
81 X::B => { 1i32 }
82 X::C => { 2i32 }
83 }
84 }
85 "#,
86 r#"
87 #[derive(Debug)]
88 enum X { A, B, C }
89
90 fn main() {
91 let x = X::A;
92 let y = match x {
93 X::A | X::B => { 1i32<|> }
94 X::C => { 2i32 }
95 }
96 }
97 "#,
98 );
99 }
100
101 #[test]
102 fn merge_match_arms_multiple_patterns() {
103 check_assist(
104 merge_match_arms,
105 r#"
106 #[derive(Debug)]
107 enum X { A, B, C, D, E }
108
109 fn main() {
110 let x = X::A;
111 let y = match x {
112 X::A | X::B => {<|> 1i32 },
113 X::C | X::D => { 1i32 },
114 X::E => { 2i32 },
115 }
116 }
117 "#,
118 r#"
119 #[derive(Debug)]
120 enum X { A, B, C, D, E }
121
122 fn main() {
123 let x = X::A;
124 let y = match x {
125 X::A | X::B | X::C | X::D => {<|> 1i32 },
126 X::E => { 2i32 },
127 }
128 }
129 "#,
130 );
131 }
132
133 #[test]
134 fn merge_match_arms_placeholder_pattern() {
135 check_assist(
136 merge_match_arms,
137 r#"
138 #[derive(Debug)]
139 enum X { A, B, C, D, E }
140
141 fn main() {
142 let x = X::A;
143 let y = match x {
144 X::A => { 1i32 },
145 X::B => { 2i<|>32 },
146 _ => { 2i32 }
147 }
148 }
149 "#,
150 r#"
151 #[derive(Debug)]
152 enum X { A, B, C, D, E }
153
154 fn main() {
155 let x = X::A;
156 let y = match x {
157 X::A => { 1i32 },
158 _ => { 2i<|>32 }
159 }
160 }
161 "#,
162 );
163 }
164
165 #[test]
166 fn merge_match_arms_rejects_guards() {
167 check_assist_not_applicable(
168 merge_match_arms,
169 r#"
170 #[derive(Debug)]
171 enum X {
172 A(i32),
173 B,
174 C
175 }
176
177 fn main() {
178 let x = X::A;
179 let y = match x {
180 X::A(a) if a > 5 => { <|>1i32 },
181 X::B => { 1i32 },
182 X::C => { 2i32 }
183 }
184 }
185 "#,
186 );
187 }
188}