ide_assists/handlers/
add_braces.rs1use either::Either;
2use syntax::{
3 AstNode, T,
4 ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
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 make = SyntaxFactory::with_mappings();
60 let mut editor = builder.make_editor(expr.syntax());
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
67 editor.add_mappings(make.finish_with_mappings());
68 builder.add_file_edits(ctx.vfs_file_id(), editor);
69 },
70 )
71}
72
73enum ParentType {
74 MatchArmExpr,
75 ClosureExpr,
76 Assignment,
77}
78
79fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> {
80 let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>();
81 let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) {
82 let parent = eq_token.parent()?;
83 let body = match_ast! {
84 match parent {
85 ast::LetStmt(it) => it.initializer()?,
86 ast::LetExpr(it) => it.expr()?,
87 ast::Static(it) => it.body()?,
88 ast::Const(it) => it.body()?,
89 _ => return None,
90 }
91 };
92 (ParentType::Assignment, body)
93 } else if let Some(Either::Left(match_arm)) = &node {
94 let match_arm_expr = match_arm.expr()?;
95 (ParentType::MatchArmExpr, match_arm_expr)
96 } else if let Some(Either::Right(closure_expr)) = &node {
97 let body = closure_expr.body()?;
98 (ParentType::ClosureExpr, body)
99 } else {
100 return None;
101 };
102
103 if matches!(body, ast::Expr::BlockExpr(_)) {
104 return None;
105 }
106
107 Some((parent_type, body))
108}
109
110#[cfg(test)]
111mod tests {
112 use crate::tests::{check_assist, check_assist_not_applicable};
113
114 use super::*;
115
116 #[test]
117 fn suggest_add_braces_for_closure() {
118 check_assist(
119 add_braces,
120 r#"
121fn foo() {
122 t(|n|$0 n + 100);
123}
124"#,
125 r#"
126fn foo() {
127 t(|n| {
128 n + 100
129 });
130}
131"#,
132 );
133 }
134
135 #[test]
136 fn suggest_add_braces_for_closure_in_match() {
137 check_assist(
138 add_braces,
139 r#"
140fn foo() {
141 match () {
142 () => {
143 t(|n|$0 n + 100);
144 }
145 }
146}
147"#,
148 r#"
149fn foo() {
150 match () {
151 () => {
152 t(|n| {
153 n + 100
154 });
155 }
156 }
157}
158"#,
159 );
160 }
161
162 #[test]
163 fn suggest_add_braces_for_assignment() {
164 check_assist(
165 add_braces,
166 r#"
167fn foo() {
168 let x =$0 n + 100;
169}
170"#,
171 r#"
172fn foo() {
173 let x = {
174 n + 100
175 };
176}
177"#,
178 );
179 }
180
181 #[test]
182 fn no_assist_for_closures_with_braces() {
183 check_assist_not_applicable(
184 add_braces,
185 r#"
186fn foo() {
187 t(|n|$0 { n + 100 });
188}
189"#,
190 );
191 }
192
193 #[test]
194 fn suggest_add_braces_for_match() {
195 check_assist(
196 add_braces,
197 r#"
198fn foo() {
199 match n {
200 Some(n) $0=> 29,
201 _ => ()
202 };
203}
204"#,
205 r#"
206fn foo() {
207 match n {
208 Some(n) => {
209 29
210 },
211 _ => ()
212 };
213}
214"#,
215 );
216 }
217
218 #[test]
219 fn multiple_indent() {
220 check_assist(
221 add_braces,
222 r#"
223fn foo() {
224 {
225 match n {
226 Some(n) $0=> foo(
227 29,
228 30,
229 ),
230 _ => ()
231 };
232 }
233}
234"#,
235 r#"
236fn foo() {
237 {
238 match n {
239 Some(n) => {
240 foo(
241 29,
242 30,
243 )
244 },
245 _ => ()
246 };
247 }
248}
249"#,
250 );
251 }
252
253 #[test]
254 fn no_assist_for_match_with_braces() {
255 check_assist_not_applicable(
256 add_braces,
257 r#"
258fn foo() {
259 match n {
260 Some(n) $0=> { return 29; },
261 _ => ()
262 };
263}
264"#,
265 );
266 }
267}