diff options
Diffstat (limited to 'crates/ide_assists')
-rw-r--r-- | crates/ide_assists/src/handlers/generate_enum_match_method.rs | 406 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_enum_projection_method.rs | 307 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 24 |
4 files changed, 340 insertions, 402 deletions
diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 670c82200..7e181a480 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs | |||
@@ -1,11 +1,9 @@ | |||
1 | use itertools::Itertools; | ||
2 | use stdx::to_lower_snake_case; | 1 | use stdx::to_lower_snake_case; |
3 | use syntax::ast::VisibilityOwner; | 2 | use syntax::ast::VisibilityOwner; |
4 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
5 | 4 | ||
6 | use crate::{ | 5 | use crate::{ |
7 | assist_context::AssistBuilder, | 6 | utils::{add_method_to_adt, find_struct_impl}, |
8 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, | ||
9 | AssistContext, AssistId, AssistKind, Assists, | 7 | AssistContext, AssistId, AssistKind, Assists, |
10 | }; | 8 | }; |
11 | 9 | ||
@@ -39,7 +37,11 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
39 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | 37 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
40 | let variant_name = variant.name()?; | 38 | let variant_name = variant.name()?; |
41 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); | 39 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); |
42 | let variant_kind = variant_kind(&variant); | 40 | let pattern_suffix = match variant.kind() { |
41 | ast::StructKind::Record(_) => " { .. }", | ||
42 | ast::StructKind::Tuple(_) => "(..)", | ||
43 | ast::StructKind::Unit => "", | ||
44 | }; | ||
43 | 45 | ||
44 | 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()); |
45 | let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); | 47 | let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); |
@@ -59,12 +61,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
59 | {}fn {}(&self) -> bool {{ | 61 | {}fn {}(&self) -> bool {{ |
60 | matches!(self, Self::{}{}) | 62 | matches!(self, Self::{}{}) |
61 | }}", | 63 | }}", |
62 | enum_lowercase_name, | 64 | enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix, |
63 | variant_name, | ||
64 | vis, | ||
65 | fn_name, | ||
66 | variant_name, | ||
67 | variant_kind.pattern_suffix(), | ||
68 | ); | 65 | ); |
69 | 66 | ||
70 | add_method_to_adt(builder, &parent_enum, impl_def, &method); | 67 | add_method_to_adt(builder, &parent_enum, impl_def, &method); |
@@ -72,237 +69,6 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
72 | ) | 69 | ) |
73 | } | 70 | } |
74 | 71 | ||
75 | // Assist: generate_enum_into_method | ||
76 | // | ||
77 | // Generate an `into_` method for an enum variant. | ||
78 | // | ||
79 | // ``` | ||
80 | // enum Value { | ||
81 | // Number(i32), | ||
82 | // Text(String)$0, | ||
83 | // } | ||
84 | // ``` | ||
85 | // -> | ||
86 | // ``` | ||
87 | // enum Value { | ||
88 | // Number(i32), | ||
89 | // Text(String), | ||
90 | // } | ||
91 | // | ||
92 | // impl Value { | ||
93 | // fn into_text(self) -> Option<String> { | ||
94 | // if let Self::Text(v) = self { | ||
95 | // Some(v) | ||
96 | // } else { | ||
97 | // None | ||
98 | // } | ||
99 | // } | ||
100 | // } | ||
101 | // ``` | ||
102 | pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
103 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
104 | let variant_name = variant.name()?; | ||
105 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); | ||
106 | let variant_kind = variant_kind(&variant); | ||
107 | |||
108 | let fn_name = format!("into_{}", &to_lower_snake_case(variant_name.text())); | ||
109 | |||
110 | // Return early if we've found an existing new fn | ||
111 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | ||
112 | |||
113 | let field_type = variant_kind.single_field_type()?; | ||
114 | let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; | ||
115 | |||
116 | let target = variant.syntax().text_range(); | ||
117 | acc.add( | ||
118 | AssistId("generate_enum_into_method", AssistKind::Generate), | ||
119 | "Generate an `into_` method for an enum variant", | ||
120 | target, | ||
121 | |builder| { | ||
122 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
123 | let method = format!( | ||
124 | " {}fn {}(self) -> Option<{}> {{ | ||
125 | if let Self::{}{} = self {{ | ||
126 | Some({}) | ||
127 | }} else {{ | ||
128 | None | ||
129 | }} | ||
130 | }}", | ||
131 | vis, | ||
132 | fn_name, | ||
133 | field_type.syntax(), | ||
134 | variant_name, | ||
135 | pattern_suffix, | ||
136 | bound_name, | ||
137 | ); | ||
138 | |||
139 | add_method_to_adt(builder, &parent_enum, impl_def, &method); | ||
140 | }, | ||
141 | ) | ||
142 | } | ||
143 | |||
144 | // Assist: generate_enum_as_method | ||
145 | // | ||
146 | // Generate an `as_` method for an enum variant. | ||
147 | // | ||
148 | // ``` | ||
149 | // enum Value { | ||
150 | // Number(i32), | ||
151 | // Text(String)$0, | ||
152 | // } | ||
153 | // ``` | ||
154 | // -> | ||
155 | // ``` | ||
156 | // enum Value { | ||
157 | // Number(i32), | ||
158 | // Text(String), | ||
159 | // } | ||
160 | // | ||
161 | // impl Value { | ||
162 | // fn as_text(&self) -> Option<&String> { | ||
163 | // if let Self::Text(v) = self { | ||
164 | // Some(v) | ||
165 | // } else { | ||
166 | // None | ||
167 | // } | ||
168 | // } | ||
169 | // } | ||
170 | // ``` | ||
171 | pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
172 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
173 | let variant_name = variant.name()?; | ||
174 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); | ||
175 | let variant_kind = variant_kind(&variant); | ||
176 | |||
177 | let fn_name = format!("as_{}", &to_lower_snake_case(variant_name.text())); | ||
178 | |||
179 | // Return early if we've found an existing new fn | ||
180 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | ||
181 | |||
182 | let field_type = variant_kind.single_field_type()?; | ||
183 | let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; | ||
184 | |||
185 | let target = variant.syntax().text_range(); | ||
186 | acc.add( | ||
187 | AssistId("generate_enum_as_method", AssistKind::Generate), | ||
188 | "Generate an `as_` method for an enum variant", | ||
189 | target, | ||
190 | |builder| { | ||
191 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
192 | let method = format!( | ||
193 | " {}fn {}(&self) -> Option<&{}> {{ | ||
194 | if let Self::{}{} = self {{ | ||
195 | Some({}) | ||
196 | }} else {{ | ||
197 | None | ||
198 | }} | ||
199 | }}", | ||
200 | vis, | ||
201 | fn_name, | ||
202 | field_type.syntax(), | ||
203 | variant_name, | ||
204 | pattern_suffix, | ||
205 | bound_name, | ||
206 | ); | ||
207 | |||
208 | add_method_to_adt(builder, &parent_enum, impl_def, &method); | ||
209 | }, | ||
210 | ) | ||
211 | } | ||
212 | |||
213 | fn add_method_to_adt( | ||
214 | builder: &mut AssistBuilder, | ||
215 | adt: &ast::Adt, | ||
216 | impl_def: Option<ast::Impl>, | ||
217 | method: &str, | ||
218 | ) { | ||
219 | let mut buf = String::with_capacity(method.len() + 2); | ||
220 | if impl_def.is_some() { | ||
221 | buf.push('\n'); | ||
222 | } | ||
223 | buf.push_str(method); | ||
224 | |||
225 | let start_offset = impl_def | ||
226 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | ||
227 | .unwrap_or_else(|| { | ||
228 | buf = generate_impl_text(&adt, &buf); | ||
229 | adt.syntax().text_range().end() | ||
230 | }); | ||
231 | |||
232 | builder.insert(start_offset, buf); | ||
233 | } | ||
234 | |||
235 | enum VariantKind { | ||
236 | Unit, | ||
237 | /// Tuple with a single field | ||
238 | NewtypeTuple { | ||
239 | ty: Option<ast::Type>, | ||
240 | }, | ||
241 | /// Tuple with 0 or more than 2 fields | ||
242 | Tuple, | ||
243 | /// Record with a single field | ||
244 | NewtypeRecord { | ||
245 | field_name: Option<ast::Name>, | ||
246 | field_type: Option<ast::Type>, | ||
247 | }, | ||
248 | /// Record with 0 or more than 2 fields | ||
249 | Record, | ||
250 | } | ||
251 | |||
252 | impl VariantKind { | ||
253 | fn pattern_suffix(&self) -> &'static str { | ||
254 | match self { | ||
255 | VariantKind::Unit => "", | ||
256 | VariantKind::NewtypeTuple { .. } | VariantKind::Tuple => "(..)", | ||
257 | VariantKind::NewtypeRecord { .. } | VariantKind::Record => " { .. }", | ||
258 | } | ||
259 | } | ||
260 | |||
261 | fn binding_pattern(&self) -> Option<(String, String)> { | ||
262 | match self { | ||
263 | VariantKind::Unit | ||
264 | | VariantKind::Tuple | ||
265 | | VariantKind::Record | ||
266 | | VariantKind::NewtypeRecord { field_name: None, .. } => None, | ||
267 | VariantKind::NewtypeTuple { .. } => Some(("(v)".to_owned(), "v".to_owned())), | ||
268 | VariantKind::NewtypeRecord { field_name: Some(name), .. } => { | ||
269 | Some((format!(" {{ {} }}", name.syntax()), name.syntax().to_string())) | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | fn single_field_type(&self) -> Option<&ast::Type> { | ||
275 | match self { | ||
276 | VariantKind::Unit | VariantKind::Tuple | VariantKind::Record => None, | ||
277 | VariantKind::NewtypeTuple { ty } => ty.as_ref(), | ||
278 | VariantKind::NewtypeRecord { field_type, .. } => field_type.as_ref(), | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | fn variant_kind(variant: &ast::Variant) -> VariantKind { | ||
284 | match variant.kind() { | ||
285 | ast::StructKind::Record(record) => { | ||
286 | if let Some((single_field,)) = record.fields().collect_tuple() { | ||
287 | let field_name = single_field.name(); | ||
288 | let field_type = single_field.ty(); | ||
289 | VariantKind::NewtypeRecord { field_name, field_type } | ||
290 | } else { | ||
291 | VariantKind::Record | ||
292 | } | ||
293 | } | ||
294 | ast::StructKind::Tuple(tuple) => { | ||
295 | if let Some((single_field,)) = tuple.fields().collect_tuple() { | ||
296 | let ty = single_field.ty(); | ||
297 | VariantKind::NewtypeTuple { ty } | ||
298 | } else { | ||
299 | VariantKind::Tuple | ||
300 | } | ||
301 | } | ||
302 | ast::StructKind::Unit => VariantKind::Unit, | ||
303 | } | ||
304 | } | ||
305 | |||
306 | #[cfg(test)] | 72 | #[cfg(test)] |
307 | mod tests { | 73 | mod tests { |
308 | use crate::tests::{check_assist, check_assist_not_applicable}; | 74 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -481,162 +247,4 @@ impl Variant { | |||
481 | }"#, | 247 | }"#, |
482 | ); | 248 | ); |
483 | } | 249 | } |
484 | |||
485 | #[test] | ||
486 | fn test_generate_enum_into_tuple_variant() { | ||
487 | check_assist( | ||
488 | generate_enum_into_method, | ||
489 | r#" | ||
490 | enum Value { | ||
491 | Number(i32), | ||
492 | Text(String)$0, | ||
493 | }"#, | ||
494 | r#"enum Value { | ||
495 | Number(i32), | ||
496 | Text(String), | ||
497 | } | ||
498 | |||
499 | impl Value { | ||
500 | fn into_text(self) -> Option<String> { | ||
501 | if let Self::Text(v) = self { | ||
502 | Some(v) | ||
503 | } else { | ||
504 | None | ||
505 | } | ||
506 | } | ||
507 | }"#, | ||
508 | ); | ||
509 | } | ||
510 | |||
511 | #[test] | ||
512 | fn test_generate_enum_into_already_implemented() { | ||
513 | check_assist_not_applicable( | ||
514 | generate_enum_into_method, | ||
515 | r#"enum Value { | ||
516 | Number(i32), | ||
517 | Text(String)$0, | ||
518 | } | ||
519 | |||
520 | impl Value { | ||
521 | fn into_text(self) -> Option<String> { | ||
522 | if let Self::Text(v) = self { | ||
523 | Some(v) | ||
524 | } else { | ||
525 | None | ||
526 | } | ||
527 | } | ||
528 | }"#, | ||
529 | ); | ||
530 | } | ||
531 | |||
532 | #[test] | ||
533 | fn test_generate_enum_into_unit_variant() { | ||
534 | check_assist_not_applicable( | ||
535 | generate_enum_into_method, | ||
536 | r#"enum Value { | ||
537 | Number(i32), | ||
538 | Text(String), | ||
539 | Unit$0, | ||
540 | }"#, | ||
541 | ); | ||
542 | } | ||
543 | |||
544 | #[test] | ||
545 | fn test_generate_enum_into_record_with_multiple_fields() { | ||
546 | check_assist_not_applicable( | ||
547 | generate_enum_into_method, | ||
548 | r#"enum Value { | ||
549 | Number(i32), | ||
550 | Text(String), | ||
551 | Both { first: i32, second: String }$0, | ||
552 | }"#, | ||
553 | ); | ||
554 | } | ||
555 | |||
556 | #[test] | ||
557 | fn test_generate_enum_into_tuple_with_multiple_fields() { | ||
558 | check_assist_not_applicable( | ||
559 | generate_enum_into_method, | ||
560 | r#"enum Value { | ||
561 | Number(i32), | ||
562 | Text(String, String)$0, | ||
563 | }"#, | ||
564 | ); | ||
565 | } | ||
566 | |||
567 | #[test] | ||
568 | fn test_generate_enum_into_record_variant() { | ||
569 | check_assist( | ||
570 | generate_enum_into_method, | ||
571 | r#"enum Value { | ||
572 | Number(i32), | ||
573 | Text { text: String }$0, | ||
574 | }"#, | ||
575 | r#"enum Value { | ||
576 | Number(i32), | ||
577 | Text { text: String }, | ||
578 | } | ||
579 | |||
580 | impl Value { | ||
581 | fn into_text(self) -> Option<String> { | ||
582 | if let Self::Text { text } = self { | ||
583 | Some(text) | ||
584 | } else { | ||
585 | None | ||
586 | } | ||
587 | } | ||
588 | }"#, | ||
589 | ); | ||
590 | } | ||
591 | |||
592 | #[test] | ||
593 | fn test_generate_enum_as_tuple_variant() { | ||
594 | check_assist( | ||
595 | generate_enum_as_method, | ||
596 | r#" | ||
597 | enum Value { | ||
598 | Number(i32), | ||
599 | Text(String)$0, | ||
600 | }"#, | ||
601 | r#"enum Value { | ||
602 | Number(i32), | ||
603 | Text(String), | ||
604 | } | ||
605 | |||
606 | impl Value { | ||
607 | fn as_text(&self) -> Option<&String> { | ||
608 | if let Self::Text(v) = self { | ||
609 | Some(v) | ||
610 | } else { | ||
611 | None | ||
612 | } | ||
613 | } | ||
614 | }"#, | ||
615 | ); | ||
616 | } | ||
617 | |||
618 | #[test] | ||
619 | fn test_generate_enum_as_record_variant() { | ||
620 | check_assist( | ||
621 | generate_enum_as_method, | ||
622 | r#"enum Value { | ||
623 | Number(i32), | ||
624 | Text { text: String }$0, | ||
625 | }"#, | ||
626 | r#"enum Value { | ||
627 | Number(i32), | ||
628 | Text { text: String }, | ||
629 | } | ||
630 | |||
631 | impl Value { | ||
632 | fn as_text(&self) -> Option<&String> { | ||
633 | if let Self::Text { text } = self { | ||
634 | Some(text) | ||
635 | } else { | ||
636 | None | ||
637 | } | ||
638 | } | ||
639 | }"#, | ||
640 | ); | ||
641 | } | ||
642 | } | 250 | } |
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..71447f310 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs | |||
@@ -0,0 +1,307 @@ | |||
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_into_method | ||
12 | // | ||
13 | // Generate an `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 into_text(self) -> Option<String> { | ||
30 | // if let Self::Text(v) = self { | ||
31 | // Some(v) | ||
32 | // } else { | ||
33 | // None | ||
34 | // } | ||
35 | // } | ||
36 | // } | ||
37 | // ``` | ||
38 | pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
39 | generate_enum_projection_method( | ||
40 | acc, | ||
41 | ctx, | ||
42 | "generate_enum_into_method", | ||
43 | "Generate an `into_` method for an enum variant", | ||
44 | "into", | ||
45 | "", | ||
46 | ) | ||
47 | } | ||
48 | |||
49 | // Assist: generate_enum_as_method | ||
50 | // | ||
51 | // Generate an `as_` method for an enum variant. | ||
52 | // | ||
53 | // ``` | ||
54 | // enum Value { | ||
55 | // Number(i32), | ||
56 | // Text(String)$0, | ||
57 | // } | ||
58 | // ``` | ||
59 | // -> | ||
60 | // ``` | ||
61 | // enum Value { | ||
62 | // Number(i32), | ||
63 | // Text(String), | ||
64 | // } | ||
65 | // | ||
66 | // impl Value { | ||
67 | // fn as_text(&self) -> Option<&String> { | ||
68 | // if let Self::Text(v) = self { | ||
69 | // Some(v) | ||
70 | // } else { | ||
71 | // None | ||
72 | // } | ||
73 | // } | ||
74 | // } | ||
75 | // ``` | ||
76 | pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
77 | generate_enum_projection_method( | ||
78 | acc, | ||
79 | ctx, | ||
80 | "generate_enum_as_method", | ||
81 | "Generate an `as_` method for an enum variant", | ||
82 | "as", | ||
83 | "&", | ||
84 | ) | ||
85 | } | ||
86 | |||
87 | pub(crate) fn generate_enum_projection_method( | ||
88 | acc: &mut Assists, | ||
89 | ctx: &AssistContext, | ||
90 | assist_id: &'static str, | ||
91 | assist_description: &str, | ||
92 | fn_name_prefix: &str, | ||
93 | ref_prefix: &str, | ||
94 | ) -> Option<()> { | ||
95 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
96 | let variant_name = variant.name()?; | ||
97 | let parent_enum = ast::Adt::Enum(variant.parent_enum()); | ||
98 | |||
99 | let (pattern_suffix, field_type, bound_name) = match variant.kind() { | ||
100 | ast::StructKind::Record(record) => { | ||
101 | let (field,) = record.fields().collect_tuple()?; | ||
102 | let name = field.name()?.to_string(); | ||
103 | let ty = field.ty()?; | ||
104 | let pattern_suffix = format!(" {{ {} }}", name); | ||
105 | (pattern_suffix, ty, name) | ||
106 | } | ||
107 | ast::StructKind::Tuple(tuple) => { | ||
108 | let (field,) = tuple.fields().collect_tuple()?; | ||
109 | let ty = field.ty()?; | ||
110 | ("(v)".to_owned(), ty, "v".to_owned()) | ||
111 | } | ||
112 | ast::StructKind::Unit => return None, | ||
113 | }; | ||
114 | |||
115 | let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(variant_name.text())); | ||
116 | |||
117 | // Return early if we've found an existing new fn | ||
118 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | ||
119 | |||
120 | let target = variant.syntax().text_range(); | ||
121 | acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { | ||
122 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
123 | let method = format!( | ||
124 | " {0}fn {1}({2}self) -> Option<{2}{3}> {{ | ||
125 | if let Self::{4}{5} = self {{ | ||
126 | Some({6}) | ||
127 | }} else {{ | ||
128 | None | ||
129 | }} | ||
130 | }}", | ||
131 | vis, | ||
132 | fn_name, | ||
133 | ref_prefix, | ||
134 | field_type.syntax(), | ||
135 | variant_name, | ||
136 | pattern_suffix, | ||
137 | bound_name, | ||
138 | ); | ||
139 | |||
140 | add_method_to_adt(builder, &parent_enum, impl_def, &method); | ||
141 | }) | ||
142 | } | ||
143 | |||
144 | #[cfg(test)] | ||
145 | mod tests { | ||
146 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
147 | |||
148 | use super::*; | ||
149 | |||
150 | #[test] | ||
151 | fn test_generate_enum_into_tuple_variant() { | ||
152 | check_assist( | ||
153 | generate_enum_into_method, | ||
154 | r#" | ||
155 | enum Value { | ||
156 | Number(i32), | ||
157 | Text(String)$0, | ||
158 | }"#, | ||
159 | r#"enum Value { | ||
160 | Number(i32), | ||
161 | Text(String), | ||
162 | } | ||
163 | |||
164 | impl Value { | ||
165 | fn into_text(self) -> Option<String> { | ||
166 | if let Self::Text(v) = self { | ||
167 | Some(v) | ||
168 | } else { | ||
169 | None | ||
170 | } | ||
171 | } | ||
172 | }"#, | ||
173 | ); | ||
174 | } | ||
175 | |||
176 | #[test] | ||
177 | fn test_generate_enum_into_already_implemented() { | ||
178 | check_assist_not_applicable( | ||
179 | generate_enum_into_method, | ||
180 | r#"enum Value { | ||
181 | Number(i32), | ||
182 | Text(String)$0, | ||
183 | } | ||
184 | |||
185 | impl Value { | ||
186 | fn into_text(self) -> Option<String> { | ||
187 | if let Self::Text(v) = self { | ||
188 | Some(v) | ||
189 | } else { | ||
190 | None | ||
191 | } | ||
192 | } | ||
193 | }"#, | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_generate_enum_into_unit_variant() { | ||
199 | check_assist_not_applicable( | ||
200 | generate_enum_into_method, | ||
201 | r#"enum Value { | ||
202 | Number(i32), | ||
203 | Text(String), | ||
204 | Unit$0, | ||
205 | }"#, | ||
206 | ); | ||
207 | } | ||
208 | |||
209 | #[test] | ||
210 | fn test_generate_enum_into_record_with_multiple_fields() { | ||
211 | check_assist_not_applicable( | ||
212 | generate_enum_into_method, | ||
213 | r#"enum Value { | ||
214 | Number(i32), | ||
215 | Text(String), | ||
216 | Both { first: i32, second: String }$0, | ||
217 | }"#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_generate_enum_into_tuple_with_multiple_fields() { | ||
223 | check_assist_not_applicable( | ||
224 | generate_enum_into_method, | ||
225 | r#"enum Value { | ||
226 | Number(i32), | ||
227 | Text(String, String)$0, | ||
228 | }"#, | ||
229 | ); | ||
230 | } | ||
231 | |||
232 | #[test] | ||
233 | fn test_generate_enum_into_record_variant() { | ||
234 | check_assist( | ||
235 | generate_enum_into_method, | ||
236 | r#"enum Value { | ||
237 | Number(i32), | ||
238 | Text { text: String }$0, | ||
239 | }"#, | ||
240 | r#"enum Value { | ||
241 | Number(i32), | ||
242 | Text { text: String }, | ||
243 | } | ||
244 | |||
245 | impl Value { | ||
246 | fn into_text(self) -> Option<String> { | ||
247 | if let Self::Text { text } = self { | ||
248 | Some(text) | ||
249 | } else { | ||
250 | None | ||
251 | } | ||
252 | } | ||
253 | }"#, | ||
254 | ); | ||
255 | } | ||
256 | |||
257 | #[test] | ||
258 | fn test_generate_enum_as_tuple_variant() { | ||
259 | check_assist( | ||
260 | generate_enum_as_method, | ||
261 | r#" | ||
262 | enum Value { | ||
263 | Number(i32), | ||
264 | Text(String)$0, | ||
265 | }"#, | ||
266 | r#"enum Value { | ||
267 | Number(i32), | ||
268 | Text(String), | ||
269 | } | ||
270 | |||
271 | impl Value { | ||
272 | fn as_text(&self) -> Option<&String> { | ||
273 | if let Self::Text(v) = self { | ||
274 | Some(v) | ||
275 | } else { | ||
276 | None | ||
277 | } | ||
278 | } | ||
279 | }"#, | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn test_generate_enum_as_record_variant() { | ||
285 | check_assist( | ||
286 | generate_enum_as_method, | ||
287 | r#"enum Value { | ||
288 | Number(i32), | ||
289 | Text { text: String }$0, | ||
290 | }"#, | ||
291 | r#"enum Value { | ||
292 | Number(i32), | ||
293 | Text { text: String }, | ||
294 | } | ||
295 | |||
296 | impl Value { | ||
297 | fn as_text(&self) -> Option<&String> { | ||
298 | if let Self::Text { text } = self { | ||
299 | Some(text) | ||
300 | } else { | ||
301 | None | ||
302 | } | ||
303 | } | ||
304 | }"#, | ||
305 | ); | ||
306 | } | ||
307 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 84ac928e9..4a7cd5848 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -129,6 +129,7 @@ mod handlers { | |||
129 | mod generate_default_from_enum_variant; | 129 | mod generate_default_from_enum_variant; |
130 | mod generate_derive; | 130 | mod generate_derive; |
131 | mod generate_enum_match_method; | 131 | mod generate_enum_match_method; |
132 | mod generate_enum_projection_method; | ||
132 | mod generate_from_impl_for_enum; | 133 | mod generate_from_impl_for_enum; |
133 | mod generate_function; | 134 | mod generate_function; |
134 | mod generate_getter; | 135 | mod generate_getter; |
@@ -190,8 +191,8 @@ mod handlers { | |||
190 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 191 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
191 | generate_derive::generate_derive, | 192 | generate_derive::generate_derive, |
192 | generate_enum_match_method::generate_enum_is_method, | 193 | generate_enum_match_method::generate_enum_is_method, |
193 | generate_enum_match_method::generate_enum_into_method, | 194 | generate_enum_projection_method::generate_enum_into_method, |
194 | generate_enum_match_method::generate_enum_as_method, | 195 | generate_enum_projection_method::generate_enum_as_method, |
195 | generate_from_impl_for_enum::generate_from_impl_for_enum, | 196 | generate_from_impl_for_enum::generate_from_impl_for_enum, |
196 | generate_function::generate_function, | 197 | generate_function::generate_function, |
197 | generate_getter::generate_getter, | 198 | generate_getter::generate_getter, |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 276792bc1..880ab6fe3 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -21,7 +21,7 @@ use syntax::{ | |||
21 | }; | 21 | }; |
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
24 | assist_context::AssistContext, | 24 | assist_context::{AssistBuilder, AssistContext}, |
25 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 25 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
26 | }; | 26 | }; |
27 | 27 | ||
@@ -464,3 +464,25 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str | |||
464 | 464 | ||
465 | buf | 465 | buf |
466 | } | 466 | } |
467 | |||
468 | pub(crate) fn add_method_to_adt( | ||
469 | builder: &mut AssistBuilder, | ||
470 | adt: &ast::Adt, | ||
471 | impl_def: Option<ast::Impl>, | ||
472 | method: &str, | ||
473 | ) { | ||
474 | let mut buf = String::with_capacity(method.len() + 2); | ||
475 | if impl_def.is_some() { | ||
476 | buf.push('\n'); | ||
477 | } | ||
478 | buf.push_str(method); | ||
479 | |||
480 | let start_offset = impl_def | ||
481 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | ||
482 | .unwrap_or_else(|| { | ||
483 | buf = generate_impl_text(&adt, &buf); | ||
484 | adt.syntax().text_range().end() | ||
485 | }); | ||
486 | |||
487 | builder.insert(start_offset, buf); | ||
488 | } | ||