diff options
Diffstat (limited to 'crates/ide_assists/src/handlers')
-rw-r--r-- | crates/ide_assists/src/handlers/generate_enum_is_method.rs (renamed from crates/ide_assists/src/handlers/generate_enum_match_method.rs) | 128 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_enum_projection_method.rs | 331 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs (renamed from crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs) | 38 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/replace_let_with_if_let.rs | 2 |
4 files changed, 430 insertions, 69 deletions
diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs index aeb887e71..7e181a480 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs | |||
@@ -1,14 +1,13 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | 1 | use stdx::to_lower_snake_case; |
2 | use syntax::ast::VisibilityOwner; | 2 | use syntax::ast::VisibilityOwner; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{ | 5 | use crate::{ |
7 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, | 6 | utils::{add_method_to_adt, find_struct_impl}, |
8 | AssistContext, AssistId, AssistKind, Assists, | 7 | AssistContext, AssistId, AssistKind, Assists, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | // Assist: generate_enum_match_method | 10 | // Assist: generate_enum_is_method |
12 | // | 11 | // |
13 | // Generate an `is_` method for an enum variant. | 12 | // Generate an `is_` method for an enum variant. |
14 | // | 13 | // |
@@ -34,79 +33,52 @@ use crate::{ | |||
34 | // } | 33 | // } |
35 | // } | 34 | // } |
36 | // ``` | 35 | // ``` |
37 | pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 36 | pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
38 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | 37 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
39 | let variant_name = variant.name()?; | 38 | let variant_name = variant.name()?; |
40 | let parent_enum = variant.parent_enum(); | 39 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); |
41 | if !matches!(variant.kind(), ast::StructKind::Unit) { | 40 | let pattern_suffix = match variant.kind() { |
42 | mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented); | 41 | ast::StructKind::Record(_) => " { .. }", |
43 | return None; | 42 | ast::StructKind::Tuple(_) => "(..)", |
44 | } | 43 | ast::StructKind::Unit => "", |
44 | }; | ||
45 | 45 | ||
46 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); | 46 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); |
47 | let fn_name = to_lower_snake_case(&variant_name.to_string()); | 47 | let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); |
48 | 48 | ||
49 | // Return early if we've found an existing new fn | 49 | // Return early if we've found an existing new fn |
50 | let impl_def = find_struct_impl( | 50 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; |
51 | &ctx, | ||
52 | &ast::Adt::Enum(parent_enum.clone()), | ||
53 | format!("is_{}", fn_name).as_str(), | ||
54 | )?; | ||
55 | 51 | ||
56 | let target = variant.syntax().text_range(); | 52 | let target = variant.syntax().text_range(); |
57 | acc.add( | 53 | acc.add( |
58 | AssistId("generate_enum_match_method", AssistKind::Generate), | 54 | AssistId("generate_enum_is_method", AssistKind::Generate), |
59 | "Generate an `is_` method for an enum variant", | 55 | "Generate an `is_` method for an enum variant", |
60 | target, | 56 | target, |
61 | |builder| { | 57 | |builder| { |
62 | let mut buf = String::with_capacity(512); | ||
63 | |||
64 | if impl_def.is_some() { | ||
65 | buf.push('\n'); | ||
66 | } | ||
67 | |||
68 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | 58 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); |
69 | format_to!( | 59 | let method = format!( |
70 | buf, | ||
71 | " /// Returns `true` if the {} is [`{}`]. | 60 | " /// Returns `true` if the {} is [`{}`]. |
72 | {}fn is_{}(&self) -> bool {{ | 61 | {}fn {}(&self) -> bool {{ |
73 | matches!(self, Self::{}) | 62 | matches!(self, Self::{}{}) |
74 | }}", | 63 | }}", |
75 | enum_lowercase_name, | 64 | enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix, |
76 | variant_name, | ||
77 | vis, | ||
78 | fn_name, | ||
79 | variant_name | ||
80 | ); | 65 | ); |
81 | 66 | ||
82 | let start_offset = impl_def | 67 | add_method_to_adt(builder, &parent_enum, impl_def, &method); |
83 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | ||
84 | .unwrap_or_else(|| { | ||
85 | buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); | ||
86 | parent_enum.syntax().text_range().end() | ||
87 | }); | ||
88 | |||
89 | builder.insert(start_offset, buf); | ||
90 | }, | 68 | }, |
91 | ) | 69 | ) |
92 | } | 70 | } |
93 | 71 | ||
94 | #[cfg(test)] | 72 | #[cfg(test)] |
95 | mod tests { | 73 | mod tests { |
96 | use test_utils::mark; | ||
97 | |||
98 | use crate::tests::{check_assist, check_assist_not_applicable}; | 74 | use crate::tests::{check_assist, check_assist_not_applicable}; |
99 | 75 | ||
100 | use super::*; | 76 | use super::*; |
101 | 77 | ||
102 | fn check_not_applicable(ra_fixture: &str) { | ||
103 | check_assist_not_applicable(generate_enum_match_method, ra_fixture) | ||
104 | } | ||
105 | |||
106 | #[test] | 78 | #[test] |
107 | fn test_generate_enum_match_from_variant() { | 79 | fn test_generate_enum_is_from_variant() { |
108 | check_assist( | 80 | check_assist( |
109 | generate_enum_match_method, | 81 | generate_enum_is_method, |
110 | r#" | 82 | r#" |
111 | enum Variant { | 83 | enum Variant { |
112 | Undefined, | 84 | Undefined, |
@@ -129,8 +101,9 @@ impl Variant { | |||
129 | } | 101 | } |
130 | 102 | ||
131 | #[test] | 103 | #[test] |
132 | fn test_generate_enum_match_already_implemented() { | 104 | fn test_generate_enum_is_already_implemented() { |
133 | check_not_applicable( | 105 | check_assist_not_applicable( |
106 | generate_enum_is_method, | ||
134 | r#" | 107 | r#" |
135 | enum Variant { | 108 | enum Variant { |
136 | Undefined, | 109 | Undefined, |
@@ -147,22 +120,59 @@ impl Variant { | |||
147 | } | 120 | } |
148 | 121 | ||
149 | #[test] | 122 | #[test] |
150 | fn test_add_from_impl_no_element() { | 123 | fn test_generate_enum_is_from_tuple_variant() { |
151 | mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented); | 124 | check_assist( |
152 | check_not_applicable( | 125 | generate_enum_is_method, |
153 | r#" | 126 | r#" |
154 | enum Variant { | 127 | enum Variant { |
155 | Undefined, | 128 | Undefined, |
156 | Minor(u32)$0, | 129 | Minor(u32)$0, |
157 | Major, | 130 | Major, |
158 | }"#, | 131 | }"#, |
132 | r#"enum Variant { | ||
133 | Undefined, | ||
134 | Minor(u32), | ||
135 | Major, | ||
136 | } | ||
137 | |||
138 | impl Variant { | ||
139 | /// Returns `true` if the variant is [`Minor`]. | ||
140 | fn is_minor(&self) -> bool { | ||
141 | matches!(self, Self::Minor(..)) | ||
142 | } | ||
143 | }"#, | ||
144 | ); | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn test_generate_enum_is_from_record_variant() { | ||
149 | check_assist( | ||
150 | generate_enum_is_method, | ||
151 | r#" | ||
152 | enum Variant { | ||
153 | Undefined, | ||
154 | Minor { foo: i32 }$0, | ||
155 | Major, | ||
156 | }"#, | ||
157 | r#"enum Variant { | ||
158 | Undefined, | ||
159 | Minor { foo: i32 }, | ||
160 | Major, | ||
161 | } | ||
162 | |||
163 | impl Variant { | ||
164 | /// Returns `true` if the variant is [`Minor`]. | ||
165 | fn is_minor(&self) -> bool { | ||
166 | matches!(self, Self::Minor { .. }) | ||
167 | } | ||
168 | }"#, | ||
159 | ); | 169 | ); |
160 | } | 170 | } |
161 | 171 | ||
162 | #[test] | 172 | #[test] |
163 | fn test_generate_enum_match_from_variant_with_one_variant() { | 173 | fn test_generate_enum_is_from_variant_with_one_variant() { |
164 | check_assist( | 174 | check_assist( |
165 | generate_enum_match_method, | 175 | generate_enum_is_method, |
166 | r#"enum Variant { Undefi$0ned }"#, | 176 | r#"enum Variant { Undefi$0ned }"#, |
167 | r#" | 177 | r#" |
168 | enum Variant { Undefined } | 178 | enum Variant { Undefined } |
@@ -177,9 +187,9 @@ impl Variant { | |||
177 | } | 187 | } |
178 | 188 | ||
179 | #[test] | 189 | #[test] |
180 | fn test_generate_enum_match_from_variant_with_visibility_marker() { | 190 | fn test_generate_enum_is_from_variant_with_visibility_marker() { |
181 | check_assist( | 191 | check_assist( |
182 | generate_enum_match_method, | 192 | generate_enum_is_method, |
183 | r#" | 193 | r#" |
184 | pub(crate) enum Variant { | 194 | pub(crate) enum Variant { |
185 | Undefined, | 195 | Undefined, |
@@ -202,9 +212,9 @@ impl Variant { | |||
202 | } | 212 | } |
203 | 213 | ||
204 | #[test] | 214 | #[test] |
205 | fn test_multiple_generate_enum_match_from_variant() { | 215 | fn test_multiple_generate_enum_is_from_variant() { |
206 | check_assist( | 216 | check_assist( |
207 | generate_enum_match_method, | 217 | generate_enum_is_method, |
208 | r#" | 218 | r#" |
209 | enum Variant { | 219 | enum Variant { |
210 | Undefined, | 220 | Undefined, |
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs new file mode 100644 index 000000000..871bcab50 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs | |||
@@ -0,0 +1,331 @@ | |||
1 | use itertools::Itertools; | ||
2 | use stdx::to_lower_snake_case; | ||
3 | use syntax::ast::VisibilityOwner; | ||
4 | use syntax::ast::{self, AstNode, NameOwner}; | ||
5 | |||
6 | use crate::{ | ||
7 | utils::{add_method_to_adt, find_struct_impl}, | ||
8 | AssistContext, AssistId, AssistKind, Assists, | ||
9 | }; | ||
10 | |||
11 | // Assist: generate_enum_try_into_method | ||
12 | // | ||
13 | // Generate an `try_into_` method for an enum variant. | ||
14 | // | ||
15 | // ``` | ||
16 | // enum Value { | ||
17 | // Number(i32), | ||
18 | // Text(String)$0, | ||
19 | // } | ||
20 | // ``` | ||
21 | // -> | ||
22 | // ``` | ||
23 | // enum Value { | ||
24 | // Number(i32), | ||
25 | // Text(String), | ||
26 | // } | ||
27 | // | ||
28 | // impl Value { | ||
29 | // fn try_into_text(self) -> Result<String, Self> { | ||
30 | // if let Self::Text(v) = self { | ||
31 | // Ok(v) | ||
32 | // } else { | ||
33 | // Err(self) | ||
34 | // } | ||
35 | // } | ||
36 | // } | ||
37 | // ``` | ||
38 | pub(crate) fn generate_enum_try_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
39 | generate_enum_projection_method( | ||
40 | acc, | ||
41 | ctx, | ||
42 | "generate_enum_try_into_method", | ||
43 | "Generate an `try_into_` method for an enum variant", | ||
44 | ProjectionProps { | ||
45 | fn_name_prefix: "try_into", | ||
46 | self_param: "self", | ||
47 | return_prefix: "Result<", | ||
48 | return_suffix: ", Self>", | ||
49 | happy_case: "Ok", | ||
50 | sad_case: "Err(self)", | ||
51 | }, | ||
52 | ) | ||
53 | } | ||
54 | |||
55 | // Assist: generate_enum_as_method | ||
56 | // | ||
57 | // Generate an `as_` method for an enum variant. | ||
58 | // | ||
59 | // ``` | ||
60 | // enum Value { | ||
61 | // Number(i32), | ||
62 | // Text(String)$0, | ||
63 | // } | ||
64 | // ``` | ||
65 | // -> | ||
66 | // ``` | ||
67 | // enum Value { | ||
68 | // Number(i32), | ||
69 | // Text(String), | ||
70 | // } | ||
71 | // | ||
72 | // impl Value { | ||
73 | // fn as_text(&self) -> Option<&String> { | ||
74 | // if let Self::Text(v) = self { | ||
75 | // Some(v) | ||
76 | // } else { | ||
77 | // None | ||
78 | // } | ||
79 | // } | ||
80 | // } | ||
81 | // ``` | ||
82 | pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
83 | generate_enum_projection_method( | ||
84 | acc, | ||
85 | ctx, | ||
86 | "generate_enum_as_method", | ||
87 | "Generate an `as_` method for an enum variant", | ||
88 | ProjectionProps { | ||
89 | fn_name_prefix: "as", | ||
90 | self_param: "&self", | ||
91 | return_prefix: "Option<&", | ||
92 | return_suffix: ">", | ||
93 | happy_case: "Some", | ||
94 | sad_case: "None", | ||
95 | }, | ||
96 | ) | ||
97 | } | ||
98 | |||
99 | struct ProjectionProps { | ||
100 | fn_name_prefix: &'static str, | ||
101 | self_param: &'static str, | ||
102 | return_prefix: &'static str, | ||
103 | return_suffix: &'static str, | ||
104 | happy_case: &'static str, | ||
105 | sad_case: &'static str, | ||
106 | } | ||
107 | |||
108 | fn generate_enum_projection_method( | ||
109 | acc: &mut Assists, | ||
110 | ctx: &AssistContext, | ||
111 | assist_id: &'static str, | ||
112 | assist_description: &str, | ||
113 | props: ProjectionProps, | ||
114 | ) -> Option<()> { | ||
115 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
116 | let variant_name = variant.name()?; | ||
117 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); | ||
118 | |||
119 | let (pattern_suffix, field_type, bound_name) = match variant.kind() { | ||
120 | ast::StructKind::Record(record) => { | ||
121 | let (field,) = record.fields().collect_tuple()?; | ||
122 | let name = field.name()?.to_string(); | ||
123 | let ty = field.ty()?; | ||
124 | let pattern_suffix = format!(" {{ {} }}", name); | ||
125 | (pattern_suffix, ty, name) | ||
126 | } | ||
127 | ast::StructKind::Tuple(tuple) => { | ||
128 | let (field,) = tuple.fields().collect_tuple()?; | ||
129 | let ty = field.ty()?; | ||
130 | ("(v)".to_owned(), ty, "v".to_owned()) | ||
131 | } | ||
132 | ast::StructKind::Unit => return None, | ||
133 | }; | ||
134 | |||
135 | let fn_name = format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(variant_name.text())); | ||
136 | |||
137 | // Return early if we've found an existing new fn | ||
138 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | ||
139 | |||
140 | let target = variant.syntax().text_range(); | ||
141 | acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { | ||
142 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
143 | let method = format!( | ||
144 | " {0}fn {1}({2}) -> {3}{4}{5} {{ | ||
145 | if let Self::{6}{7} = self {{ | ||
146 | {8}({9}) | ||
147 | }} else {{ | ||
148 | {10} | ||
149 | }} | ||
150 | }}", | ||
151 | vis, | ||
152 | fn_name, | ||
153 | props.self_param, | ||
154 | props.return_prefix, | ||
155 | field_type.syntax(), | ||
156 | props.return_suffix, | ||
157 | variant_name, | ||
158 | pattern_suffix, | ||
159 | props.happy_case, | ||
160 | bound_name, | ||
161 | props.sad_case, | ||
162 | ); | ||
163 | |||
164 | add_method_to_adt(builder, &parent_enum, impl_def, &method); | ||
165 | }) | ||
166 | } | ||
167 | |||
168 | #[cfg(test)] | ||
169 | mod tests { | ||
170 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
171 | |||
172 | use super::*; | ||
173 | |||
174 | #[test] | ||
175 | fn test_generate_enum_try_into_tuple_variant() { | ||
176 | check_assist( | ||
177 | generate_enum_try_into_method, | ||
178 | r#" | ||
179 | enum Value { | ||
180 | Number(i32), | ||
181 | Text(String)$0, | ||
182 | }"#, | ||
183 | r#"enum Value { | ||
184 | Number(i32), | ||
185 | Text(String), | ||
186 | } | ||
187 | |||
188 | impl Value { | ||
189 | fn try_into_text(self) -> Result<String, Self> { | ||
190 | if let Self::Text(v) = self { | ||
191 | Ok(v) | ||
192 | } else { | ||
193 | Err(self) | ||
194 | } | ||
195 | } | ||
196 | }"#, | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn test_generate_enum_try_into_already_implemented() { | ||
202 | check_assist_not_applicable( | ||
203 | generate_enum_try_into_method, | ||
204 | r#"enum Value { | ||
205 | Number(i32), | ||
206 | Text(String)$0, | ||
207 | } | ||
208 | |||
209 | impl Value { | ||
210 | fn try_into_text(self) -> Result<String, Self> { | ||
211 | if let Self::Text(v) = self { | ||
212 | Ok(v) | ||
213 | } else { | ||
214 | Err(self) | ||
215 | } | ||
216 | } | ||
217 | }"#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_generate_enum_try_into_unit_variant() { | ||
223 | check_assist_not_applicable( | ||
224 | generate_enum_try_into_method, | ||
225 | r#"enum Value { | ||
226 | Number(i32), | ||
227 | Text(String), | ||
228 | Unit$0, | ||
229 | }"#, | ||
230 | ); | ||
231 | } | ||
232 | |||
233 | #[test] | ||
234 | fn test_generate_enum_try_into_record_with_multiple_fields() { | ||
235 | check_assist_not_applicable( | ||
236 | generate_enum_try_into_method, | ||
237 | r#"enum Value { | ||
238 | Number(i32), | ||
239 | Text(String), | ||
240 | Both { first: i32, second: String }$0, | ||
241 | }"#, | ||
242 | ); | ||
243 | } | ||
244 | |||
245 | #[test] | ||
246 | fn test_generate_enum_try_into_tuple_with_multiple_fields() { | ||
247 | check_assist_not_applicable( | ||
248 | generate_enum_try_into_method, | ||
249 | r#"enum Value { | ||
250 | Number(i32), | ||
251 | Text(String, String)$0, | ||
252 | }"#, | ||
253 | ); | ||
254 | } | ||
255 | |||
256 | #[test] | ||
257 | fn test_generate_enum_try_into_record_variant() { | ||
258 | check_assist( | ||
259 | generate_enum_try_into_method, | ||
260 | r#"enum Value { | ||
261 | Number(i32), | ||
262 | Text { text: String }$0, | ||
263 | }"#, | ||
264 | r#"enum Value { | ||
265 | Number(i32), | ||
266 | Text { text: String }, | ||
267 | } | ||
268 | |||
269 | impl Value { | ||
270 | fn try_into_text(self) -> Result<String, Self> { | ||
271 | if let Self::Text { text } = self { | ||
272 | Ok(text) | ||
273 | } else { | ||
274 | Err(self) | ||
275 | } | ||
276 | } | ||
277 | }"#, | ||
278 | ); | ||
279 | } | ||
280 | |||
281 | #[test] | ||
282 | fn test_generate_enum_as_tuple_variant() { | ||
283 | check_assist( | ||
284 | generate_enum_as_method, | ||
285 | r#" | ||
286 | enum Value { | ||
287 | Number(i32), | ||
288 | Text(String)$0, | ||
289 | }"#, | ||
290 | r#"enum Value { | ||
291 | Number(i32), | ||
292 | Text(String), | ||
293 | } | ||
294 | |||
295 | impl Value { | ||
296 | fn as_text(&self) -> Option<&String> { | ||
297 | if let Self::Text(v) = self { | ||
298 | Some(v) | ||
299 | } else { | ||
300 | None | ||
301 | } | ||
302 | } | ||
303 | }"#, | ||
304 | ); | ||
305 | } | ||
306 | |||
307 | #[test] | ||
308 | fn test_generate_enum_as_record_variant() { | ||
309 | check_assist( | ||
310 | generate_enum_as_method, | ||
311 | r#"enum Value { | ||
312 | Number(i32), | ||
313 | Text { text: String }$0, | ||
314 | }"#, | ||
315 | r#"enum Value { | ||
316 | Number(i32), | ||
317 | Text { text: String }, | ||
318 | } | ||
319 | |||
320 | impl Value { | ||
321 | fn as_text(&self) -> Option<&String> { | ||
322 | if let Self::Text { text } = self { | ||
323 | Some(text) | ||
324 | } else { | ||
325 | None | ||
326 | } | ||
327 | } | ||
328 | }"#, | ||
329 | ); | ||
330 | } | ||
331 | } | ||
diff --git a/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs index 9fddf889c..27da28bc0 100644 --- a/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs +++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs | |||
@@ -3,17 +3,18 @@ use hir::known; | |||
3 | use ide_db::helpers::FamousDefs; | 3 | use ide_db::helpers::FamousDefs; |
4 | use stdx::format_to; | 4 | use stdx::format_to; |
5 | use syntax::{ast, AstNode}; | 5 | use syntax::{ast, AstNode}; |
6 | use test_utils::mark; | ||
6 | 7 | ||
7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
8 | 9 | ||
9 | // Assist: convert_for_to_iter_for_each | 10 | // Assist: replace_for_loop_with_for_each |
10 | // | 11 | // |
11 | // Converts a for loop into a for_each loop on the Iterator. | 12 | // Converts a for loop into a for_each loop on the Iterator. |
12 | // | 13 | // |
13 | // ``` | 14 | // ``` |
14 | // fn main() { | 15 | // fn main() { |
15 | // let x = vec![1, 2, 3]; | 16 | // let x = vec![1, 2, 3]; |
16 | // for $0v in x { | 17 | // for$0 v in x { |
17 | // let y = v * 2; | 18 | // let y = v * 2; |
18 | // } | 19 | // } |
19 | // } | 20 | // } |
@@ -27,15 +28,19 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
27 | // }); | 28 | // }); |
28 | // } | 29 | // } |
29 | // ``` | 30 | // ``` |
30 | pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
31 | let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; | 32 | let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; |
32 | let iterable = for_loop.iterable()?; | 33 | let iterable = for_loop.iterable()?; |
33 | let pat = for_loop.pat()?; | 34 | let pat = for_loop.pat()?; |
34 | let body = for_loop.loop_body()?; | 35 | let body = for_loop.loop_body()?; |
36 | if body.syntax().text_range().start() < ctx.offset() { | ||
37 | mark::hit!(not_available_in_body); | ||
38 | return None; | ||
39 | } | ||
35 | 40 | ||
36 | acc.add( | 41 | acc.add( |
37 | AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite), | 42 | AssistId("replace_for_loop_with_for_each", AssistKind::RefactorRewrite), |
38 | "Convert a for loop into an Iterator::for_each", | 43 | "Replace this for loop with `Iterator::for_each`", |
39 | for_loop.syntax().text_range(), | 44 | for_loop.syntax().text_range(), |
40 | |builder| { | 45 | |builder| { |
41 | let mut buf = String::new(); | 46 | let mut buf = String::new(); |
@@ -145,13 +150,13 @@ pub struct NoIterMethod; | |||
145 | FamousDefs::FIXTURE, | 150 | FamousDefs::FIXTURE, |
146 | EMPTY_ITER_FIXTURE | 151 | EMPTY_ITER_FIXTURE |
147 | ); | 152 | ); |
148 | check_assist(convert_for_to_iter_for_each, before, after); | 153 | check_assist(replace_for_loop_with_for_each, before, after); |
149 | } | 154 | } |
150 | 155 | ||
151 | #[test] | 156 | #[test] |
152 | fn test_not_for() { | 157 | fn test_not_for() { |
153 | check_assist_not_applicable( | 158 | check_assist_not_applicable( |
154 | convert_for_to_iter_for_each, | 159 | replace_for_loop_with_for_each, |
155 | r" | 160 | r" |
156 | let mut x = vec![1, 2, 3]; | 161 | let mut x = vec![1, 2, 3]; |
157 | x.iter_mut().$0for_each(|v| *v *= 2); | 162 | x.iter_mut().$0for_each(|v| *v *= 2); |
@@ -162,7 +167,7 @@ x.iter_mut().$0for_each(|v| *v *= 2); | |||
162 | #[test] | 167 | #[test] |
163 | fn test_simple_for() { | 168 | fn test_simple_for() { |
164 | check_assist( | 169 | check_assist( |
165 | convert_for_to_iter_for_each, | 170 | replace_for_loop_with_for_each, |
166 | r" | 171 | r" |
167 | fn main() { | 172 | fn main() { |
168 | let x = vec![1, 2, 3]; | 173 | let x = vec![1, 2, 3]; |
@@ -181,6 +186,21 @@ fn main() { | |||
181 | } | 186 | } |
182 | 187 | ||
183 | #[test] | 188 | #[test] |
189 | fn not_available_in_body() { | ||
190 | mark::check!(not_available_in_body); | ||
191 | check_assist_not_applicable( | ||
192 | replace_for_loop_with_for_each, | ||
193 | r" | ||
194 | fn main() { | ||
195 | let x = vec![1, 2, 3]; | ||
196 | for v in x { | ||
197 | $0v *= 2; | ||
198 | } | ||
199 | }", | ||
200 | ) | ||
201 | } | ||
202 | |||
203 | #[test] | ||
184 | fn test_for_borrowed() { | 204 | fn test_for_borrowed() { |
185 | check_assist_with_fixtures( | 205 | check_assist_with_fixtures( |
186 | r" | 206 | r" |
@@ -255,7 +275,7 @@ fn main() { | |||
255 | #[test] | 275 | #[test] |
256 | fn test_for_borrowed_mut_behind_var() { | 276 | fn test_for_borrowed_mut_behind_var() { |
257 | check_assist( | 277 | check_assist( |
258 | convert_for_to_iter_for_each, | 278 | replace_for_loop_with_for_each, |
259 | r" | 279 | r" |
260 | fn main() { | 280 | fn main() { |
261 | let x = vec![1, 2, 3]; | 281 | let x = vec![1, 2, 3]; |
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs index 5a27ada6b..be7e724b5 100644 --- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::iter::once; | 1 | use std::iter::once; |
2 | 2 | ||
3 | use ide_db::ty_filter::TryEnum; | ||
3 | use syntax::{ | 4 | use syntax::{ |
4 | ast::{ | 5 | ast::{ |
5 | self, | 6 | self, |
@@ -10,7 +11,6 @@ use syntax::{ | |||
10 | }; | 11 | }; |
11 | 12 | ||
12 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 13 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
13 | use ide_db::ty_filter::TryEnum; | ||
14 | 14 | ||
15 | // Assist: replace_let_with_if_let | 15 | // Assist: replace_let_with_if_let |
16 | // | 16 | // |