ide_assists/handlers/
add_braces.rs1use either::Either;
2use syntax::{
3 AstNode, T,
4 ast::{self, edit::AstNodeEdit},
5 match_ast,
6};
7
8use crate::{AssistContext, AssistId, Assists};
9
10pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
48 let (expr_type, expr) = get_replacement_node(ctx)?;
49
50 acc.add(
51 AssistId::refactor_rewrite("add_braces"),
52 match expr_type {
53 ParentType::ClosureExpr => "Add braces to this closure body",
54 ParentType::MatchArmExpr => "Add braces to this match arm expression",
55 ParentType::Assignment => "Add braces to this assignment expression",
56 },
57 expr.syntax().text_range(),
58 |builder| {
59 let editor = builder.make_editor(expr.syntax());
60 let make = editor.make();
61
62 let new_expr = expr.reset_indent().indent(1.into());
63 let block_expr = make.block_expr(None, Some(new_expr));
64
65 editor.replace(expr.syntax(), block_expr.indent(expr.indent_level()).syntax());
66 builder.add_file_edits(ctx.vfs_file_id(), editor);
67 },
68 )
69}
70
71enum ParentType {
72 MatchArmExpr,
73 ClosureExpr,
74 Assignment,
75}
76
77fn get_replacement_node(ctx: &AssistContext<'_, '_>) -> Option<(ParentType, ast::Expr)> {
78 let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>();
79 let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) {
80 let parent = eq_token.parent()?;
81 let body = match_ast! {
82 match parent {
83 ast::LetStmt(it) => it.initializer()?,
84 ast::LetExpr(it) => it.expr()?,
85 ast::BinExpr(it) => it.rhs()?,
86 ast::Static(it) => it.body()?,
87 ast::Const(it) => it.body()?,
88 _ => return None,
89 }
90 };
91 (ParentType::Assignment, body)
92 } else if let Some(Either::Left(match_arm)) = &node {
93 let match_arm_expr = match_arm.expr()?;
94 (ParentType::MatchArmExpr, match_arm_expr)
95 } else if let Some(Either::Right(closure_expr)) = &node {
96 let body = closure_expr.body()?;
97 (ParentType::ClosureExpr, body)
98 } else {
99 return None;
100 };
101
102 if matches!(body, ast::Expr::BlockExpr(_)) {
103 return None;
104 }
105
106 Some((parent_type, body))
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 suggest_add_braces_for_closure() {
117 check_assist(
118 add_braces,
119 r#"
120fn foo() {
121 t(|n|$0 n + 100);
122}
123"#,
124 r#"
125fn foo() {
126 t(|n| {
127 n + 100
128 });
129}
130"#,
131 );
132 }
133
134 #[test]
135 fn suggest_add_braces_for_closure_in_match() {
136 check_assist(
137 add_braces,
138 r#"
139fn foo() {
140 match () {
141 () => {
142 t(|n|$0 n + 100);
143 }
144 }
145}
146"#,
147 r#"
148fn foo() {
149 match () {
150 () => {
151 t(|n| {
152 n + 100
153 });
154 }
155 }
156}
157"#,
158 );
159 }
160
161 #[test]
162 fn suggest_add_braces_for_assignment() {
163 check_assist(
164 add_braces,
165 r#"
166fn foo() {
167 let x =$0 n + 100;
168}
169"#,
170 r#"
171fn foo() {
172 let x = {
173 n + 100
174 };
175}
176"#,
177 );
178
179 check_assist(
180 add_braces,
181 r#"
182fn foo() {
183 let x;
184 x =$0 n + 100;
185}
186"#,
187 r#"
188fn foo() {
189 let x;
190 x = {
191 n + 100
192 };
193}
194"#,
195 );
196
197 check_assist(
198 add_braces,
199 r#"
200fn foo() {
201 if let x =$0 n + 100 {}
202}
203"#,
204 r#"
205fn foo() {
206 if let x = {
207 n + 100
208 } {}
209}
210"#,
211 );
212 }
213
214 #[test]
215 fn suggest_add_braces_for_const_initializer() {
216 check_assist(
217 add_braces,
218 r#"
219const X: i32 =$0 1 + 2;
220"#,
221 r#"
222const X: i32 = {
223 1 + 2
224};
225"#,
226 );
227 }
228
229 #[test]
230 fn suggest_add_braces_for_static_initializer() {
231 check_assist(
232 add_braces,
233 r#"
234static X: i32 $0= 1 + 2;
235"#,
236 r#"
237static X: i32 = {
238 1 + 2
239};
240"#,
241 );
242 }
243
244 #[test]
245 fn no_assist_for_closures_with_braces() {
246 check_assist_not_applicable(
247 add_braces,
248 r#"
249fn foo() {
250 t(|n|$0 { n + 100 });
251}
252"#,
253 );
254 }
255
256 #[test]
257 fn suggest_add_braces_for_match() {
258 check_assist(
259 add_braces,
260 r#"
261fn foo() {
262 match n {
263 Some(n) $0=> 29,
264 _ => ()
265 };
266}
267"#,
268 r#"
269fn foo() {
270 match n {
271 Some(n) => {
272 29
273 },
274 _ => ()
275 };
276}
277"#,
278 );
279 }
280
281 #[test]
282 fn multiple_indent() {
283 check_assist(
284 add_braces,
285 r#"
286fn foo() {
287 {
288 match n {
289 Some(n) $0=> foo(
290 29,
291 30,
292 ),
293 _ => ()
294 };
295 }
296}
297"#,
298 r#"
299fn foo() {
300 {
301 match n {
302 Some(n) => {
303 foo(
304 29,
305 30,
306 )
307 },
308 _ => ()
309 };
310 }
311}
312"#,
313 );
314 }
315
316 #[test]
317 fn no_assist_for_match_with_braces() {
318 check_assist_not_applicable(
319 add_braces,
320 r#"
321fn foo() {
322 match n {
323 Some(n) $0=> { return 29; },
324 _ => ()
325 };
326}
327"#,
328 );
329 }
330}