ide_assists/handlers/
unmerge_match_arm.rs1use syntax::{
2 Direction, SyntaxKind, T,
3 ast::{self, AstNode, edit::IndentLevel},
4 syntax_editor::{Element, Position},
5};
6
7use crate::{AssistContext, AssistId, Assists};
8
9pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
34 let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
35 let or_pat = ast::OrPat::cast(pipe_token.parent()?)?;
36 if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
37 return None;
38 }
39 let match_arm = ast::MatchArm::cast(or_pat.syntax().parent()?)?;
40 let match_arm_body = match_arm.expr()?;
41 let pats_after = pipe_token
42 .siblings_with_tokens(Direction::Next)
43 .filter_map(|it| ast::Pat::cast(it.into_node()?))
44 .collect::<Vec<_>>();
45
46 let new_parent = match_arm.syntax().parent()?;
50 if pats_after.is_empty() {
51 return None;
52 }
53
54 acc.add(
55 AssistId::refactor_rewrite("unmerge_match_arm"),
56 "Unmerge match arm",
57 pipe_token.text_range(),
58 |edit| {
59 let editor = edit.make_editor(&new_parent);
60 let make = editor.make();
61 let new_pat = if pats_after.len() == 1 {
63 pats_after[0].clone()
64 } else {
65 make.or_pat(pats_after, or_pat.leading_pipe().is_some()).into()
66 };
67 let new_match_arm = make.match_arm(new_pat, match_arm.guard(), match_arm_body);
68 let mut pipe_index = pipe_token.index();
69 if pipe_token
70 .prev_sibling_or_token()
71 .is_some_and(|it| it.kind() == SyntaxKind::WHITESPACE)
72 {
73 pipe_index -= 1;
74 }
75 for child in or_pat
76 .syntax()
77 .children_with_tokens()
78 .skip_while(|child| child.index() < pipe_index)
79 {
80 editor.delete(child.syntax_element());
81 }
82
83 let mut insert_after_old_arm = Vec::new();
84
85 let has_comma_after = match_arm.comma_token().is_some();
94 if !has_comma_after && !match_arm.expr().unwrap().is_block_like() {
95 insert_after_old_arm.push(make.token(T![,]).into());
96 }
97
98 let indent = IndentLevel::from_node(match_arm.syntax());
99 insert_after_old_arm.push(make.whitespace(&format!("\n{indent}")).into());
100
101 insert_after_old_arm.push(new_match_arm.syntax().clone().into());
102
103 editor.insert_all(Position::after(match_arm.syntax()), insert_after_old_arm);
104 edit.add_file_edits(ctx.vfs_file_id(), editor);
105 },
106 )
107}
108
109#[cfg(test)]
110mod tests {
111 use crate::tests::{check_assist, check_assist_not_applicable};
112
113 use super::*;
114
115 #[test]
116 fn unmerge_match_arm_single_pipe() {
117 check_assist(
118 unmerge_match_arm,
119 r#"
120#[derive(Debug)]
121enum X { A, B, C }
122
123fn main() {
124 let x = X::A;
125 let y = match x {
126 X::A $0| X::B => { 1i32 }
127 X::C => { 2i32 }
128 };
129}
130"#,
131 r#"
132#[derive(Debug)]
133enum X { A, B, C }
134
135fn main() {
136 let x = X::A;
137 let y = match x {
138 X::A => { 1i32 }
139 X::B => { 1i32 }
140 X::C => { 2i32 }
141 };
142}
143"#,
144 );
145 }
146
147 #[test]
148 fn unmerge_match_arm_guard() {
149 check_assist(
150 unmerge_match_arm,
151 r#"
152#[derive(Debug)]
153enum X { A, B, C }
154
155fn main() {
156 let x = X::A;
157 let y = match x {
158 X::A $0| X::B if true => { 1i32 }
159 _ => { 2i32 }
160 };
161}
162"#,
163 r#"
164#[derive(Debug)]
165enum X { A, B, C }
166
167fn main() {
168 let x = X::A;
169 let y = match x {
170 X::A if true => { 1i32 }
171 X::B if true => { 1i32 }
172 _ => { 2i32 }
173 };
174}
175"#,
176 );
177 }
178
179 #[test]
180 fn unmerge_match_arm_leading_pipe() {
181 check_assist_not_applicable(
182 unmerge_match_arm,
183 r#"
184
185fn main() {
186 let y = match 0 {
187 |$0 0 => { 1i32 }
188 1 => { 2i32 }
189 };
190}
191"#,
192 );
193 }
194
195 #[test]
196 fn unmerge_match_arm_trailing_pipe() {
197 check_assist_not_applicable(
198 unmerge_match_arm,
199 r#"
200fn main() {
201 let y = match 0 {
202 0 |$0 => { 1i32 }
203 1 => { 2i32 }
204 };
205}
206"#,
207 );
208 }
209
210 #[test]
211 fn unmerge_match_arm_multiple_pipes() {
212 check_assist(
213 unmerge_match_arm,
214 r#"
215#[derive(Debug)]
216enum X { A, B, C, D, E }
217
218fn main() {
219 let x = X::A;
220 let y = match x {
221 X::A | X::B |$0 X::C | X::D => 1i32,
222 X::E => 2i32,
223 };
224}
225"#,
226 r#"
227#[derive(Debug)]
228enum X { A, B, C, D, E }
229
230fn main() {
231 let x = X::A;
232 let y = match x {
233 X::A | X::B => 1i32,
234 X::C | X::D => 1i32,
235 X::E => 2i32,
236 };
237}
238"#,
239 );
240 }
241
242 #[test]
243 fn unmerge_match_arm_inserts_comma_if_required() {
244 check_assist(
245 unmerge_match_arm,
246 r#"
247#[derive(Debug)]
248enum X { A, B }
249
250fn main() {
251 let x = X::A;
252 let y = match x {
253 X::A $0| X::B => 1i32
254 };
255}
256"#,
257 r#"
258#[derive(Debug)]
259enum X { A, B }
260
261fn main() {
262 let x = X::A;
263 let y = match x {
264 X::A => 1i32,
265 X::B => 1i32,
266 };
267}
268"#,
269 );
270 }
271
272 #[test]
273 fn unmerge_match_arm_inserts_comma_if_had_after() {
274 check_assist(
275 unmerge_match_arm,
276 r#"
277#[derive(Debug)]
278enum X { A, B }
279
280fn main() {
281 let x = X::A;
282 match x {
283 X::A $0| X::B => {}
284 }
285}
286"#,
287 r#"
288#[derive(Debug)]
289enum X { A, B }
290
291fn main() {
292 let x = X::A;
293 match x {
294 X::A => {}
295 X::B => {}
296 }
297}
298"#,
299 );
300 }
301}