diff options
-rw-r--r-- | crates/ide_assists/src/handlers/generate_enum_match_method.rs | 113 |
1 files changed, 95 insertions, 18 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 3a7177dbf..38aca0c88 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | 1 | use stdx::{format_to, 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::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
@@ -37,20 +36,17 @@ use crate::{ | |||
37 | pub(crate) fn generate_enum_is_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 variant_kind = variant_kind(&variant); |
42 | mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented); | ||
43 | return None; | ||
44 | } | ||
45 | 41 | ||
46 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); | 42 | 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()); | 43 | let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); |
48 | 44 | ||
49 | // Return early if we've found an existing new fn | 45 | // Return early if we've found an existing new fn |
50 | let impl_def = find_struct_impl( | 46 | let impl_def = find_struct_impl( |
51 | &ctx, | 47 | &ctx, |
52 | &ast::Adt::Enum(parent_enum.clone()), | 48 | &parent_enum, |
53 | format!("is_{}", fn_name).as_str(), | 49 | &fn_name, |
54 | )?; | 50 | )?; |
55 | 51 | ||
56 | let target = variant.syntax().text_range(); | 52 | let target = variant.syntax().text_range(); |
@@ -69,20 +65,21 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
69 | format_to!( | 65 | format_to!( |
70 | buf, | 66 | buf, |
71 | " /// Returns `true` if the {} is [`{}`]. | 67 | " /// Returns `true` if the {} is [`{}`]. |
72 | {}fn is_{}(&self) -> bool {{ | 68 | {}fn {}(&self) -> bool {{ |
73 | matches!(self, Self::{}) | 69 | matches!(self, Self::{}{}) |
74 | }}", | 70 | }}", |
75 | enum_lowercase_name, | 71 | enum_lowercase_name, |
76 | variant_name, | 72 | variant_name, |
77 | vis, | 73 | vis, |
78 | fn_name, | 74 | fn_name, |
79 | variant_name | 75 | variant_name, |
76 | variant_kind.pattern_suffix(), | ||
80 | ); | 77 | ); |
81 | 78 | ||
82 | let start_offset = impl_def | 79 | let start_offset = impl_def |
83 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | 80 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
84 | .unwrap_or_else(|| { | 81 | .unwrap_or_else(|| { |
85 | buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); | 82 | buf = generate_impl_text(&parent_enum, &buf); |
86 | parent_enum.syntax().text_range().end() | 83 | parent_enum.syntax().text_range().end() |
87 | }); | 84 | }); |
88 | 85 | ||
@@ -91,10 +88,53 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
91 | ) | 88 | ) |
92 | } | 89 | } |
93 | 90 | ||
91 | enum VariantKind { | ||
92 | Unit, | ||
93 | /// Tuple with a single field | ||
94 | NewtypeTuple, | ||
95 | /// Tuple with 0 or more than 2 fields | ||
96 | Tuple, | ||
97 | /// Record with a single field | ||
98 | NewtypeRecord { field_name: Option<ast::Name> }, | ||
99 | /// Record with 0 or more than 2 fields | ||
100 | Record, | ||
101 | } | ||
102 | |||
103 | impl VariantKind { | ||
104 | fn pattern_suffix(&self) -> &'static str { | ||
105 | match self { | ||
106 | VariantKind::Unit => "", | ||
107 | VariantKind::NewtypeTuple | | ||
108 | VariantKind::Tuple => "(..)", | ||
109 | VariantKind::NewtypeRecord { .. } | | ||
110 | VariantKind::Record => " { .. }", | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn variant_kind(variant: &ast::Variant) -> VariantKind { | ||
116 | match variant.kind() { | ||
117 | ast::StructKind::Record(record) => { | ||
118 | if record.fields().count() == 1 { | ||
119 | let field_name = record.fields().nth(0).unwrap().name(); | ||
120 | VariantKind::NewtypeRecord { field_name } | ||
121 | } else { | ||
122 | VariantKind::Record | ||
123 | } | ||
124 | } | ||
125 | ast::StructKind::Tuple(tuple) => { | ||
126 | if tuple.fields().count() == 1 { | ||
127 | VariantKind::NewtypeTuple | ||
128 | } else { | ||
129 | VariantKind::Tuple | ||
130 | } | ||
131 | } | ||
132 | ast::StructKind::Unit => VariantKind::Unit, | ||
133 | } | ||
134 | } | ||
135 | |||
94 | #[cfg(test)] | 136 | #[cfg(test)] |
95 | mod tests { | 137 | mod tests { |
96 | use test_utils::mark; | ||
97 | |||
98 | use crate::tests::{check_assist, check_assist_not_applicable}; | 138 | use crate::tests::{check_assist, check_assist_not_applicable}; |
99 | 139 | ||
100 | use super::*; | 140 | use super::*; |
@@ -147,15 +187,52 @@ impl Variant { | |||
147 | } | 187 | } |
148 | 188 | ||
149 | #[test] | 189 | #[test] |
150 | fn test_add_from_impl_no_element() { | 190 | fn test_generate_enum_match_from_tuple_variant() { |
151 | mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented); | 191 | check_assist( |
152 | check_not_applicable( | 192 | generate_enum_is_method, |
153 | r#" | 193 | r#" |
154 | enum Variant { | 194 | enum Variant { |
155 | Undefined, | 195 | Undefined, |
156 | Minor(u32)$0, | 196 | Minor(u32)$0, |
157 | Major, | 197 | Major, |
158 | }"#, | 198 | }"#, |
199 | r#"enum Variant { | ||
200 | Undefined, | ||
201 | Minor(u32), | ||
202 | Major, | ||
203 | } | ||
204 | |||
205 | impl Variant { | ||
206 | /// Returns `true` if the variant is [`Minor`]. | ||
207 | fn is_minor(&self) -> bool { | ||
208 | matches!(self, Self::Minor(..)) | ||
209 | } | ||
210 | }"#, | ||
211 | ); | ||
212 | } | ||
213 | |||
214 | #[test] | ||
215 | fn test_generate_enum_match_from_record_variant() { | ||
216 | check_assist( | ||
217 | generate_enum_is_method, | ||
218 | r#" | ||
219 | enum Variant { | ||
220 | Undefined, | ||
221 | Minor { foo: i32 }$0, | ||
222 | Major, | ||
223 | }"#, | ||
224 | r#"enum Variant { | ||
225 | Undefined, | ||
226 | Minor { foo: i32 }, | ||
227 | Major, | ||
228 | } | ||
229 | |||
230 | impl Variant { | ||
231 | /// Returns `true` if the variant is [`Minor`]. | ||
232 | fn is_minor(&self) -> bool { | ||
233 | matches!(self, Self::Minor { .. }) | ||
234 | } | ||
235 | }"#, | ||
159 | ); | 236 | ); |
160 | } | 237 | } |
161 | 238 | ||