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