aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_enum_match_method.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_enum_match_method.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_match_method.rs240
1 files changed, 240 insertions, 0 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
new file mode 100644
index 000000000..aeb887e71
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs
@@ -0,0 +1,240 @@
1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5
6use crate::{
7 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
8 AssistContext, AssistId, AssistKind, Assists,
9};
10
11// Assist: generate_enum_match_method
12//
13// Generate an `is_` method for an enum variant.
14//
15// ```
16// enum Version {
17// Undefined,
18// Minor$0,
19// Major,
20// }
21// ```
22// ->
23// ```
24// enum Version {
25// Undefined,
26// Minor,
27// Major,
28// }
29//
30// impl Version {
31// /// Returns `true` if the version is [`Minor`].
32// fn is_minor(&self) -> bool {
33// matches!(self, Self::Minor)
34// }
35// }
36// ```
37pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
39 let variant_name = variant.name()?;
40 let parent_enum = variant.parent_enum();
41 if !matches!(variant.kind(), ast::StructKind::Unit) {
42 mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented);
43 return None;
44 }
45
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());
48
49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl(
51 &ctx,
52 &ast::Adt::Enum(parent_enum.clone()),
53 format!("is_{}", fn_name).as_str(),
54 )?;
55
56 let target = variant.syntax().text_range();
57 acc.add(
58 AssistId("generate_enum_match_method", AssistKind::Generate),
59 "Generate an `is_` method for an enum variant",
60 target,
61 |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));
69 format_to!(
70 buf,
71 " /// Returns `true` if the {} is [`{}`].
72 {}fn is_{}(&self) -> bool {{
73 matches!(self, Self::{})
74 }}",
75 enum_lowercase_name,
76 variant_name,
77 vis,
78 fn_name,
79 variant_name
80 );
81
82 let start_offset = impl_def
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 },
91 )
92}
93
94#[cfg(test)]
95mod tests {
96 use test_utils::mark;
97
98 use crate::tests::{check_assist, check_assist_not_applicable};
99
100 use super::*;
101
102 fn check_not_applicable(ra_fixture: &str) {
103 check_assist_not_applicable(generate_enum_match_method, ra_fixture)
104 }
105
106 #[test]
107 fn test_generate_enum_match_from_variant() {
108 check_assist(
109 generate_enum_match_method,
110 r#"
111enum Variant {
112 Undefined,
113 Minor$0,
114 Major,
115}"#,
116 r#"enum Variant {
117 Undefined,
118 Minor,
119 Major,
120}
121
122impl Variant {
123 /// Returns `true` if the variant is [`Minor`].
124 fn is_minor(&self) -> bool {
125 matches!(self, Self::Minor)
126 }
127}"#,
128 );
129 }
130
131 #[test]
132 fn test_generate_enum_match_already_implemented() {
133 check_not_applicable(
134 r#"
135enum Variant {
136 Undefined,
137 Minor$0,
138 Major,
139}
140
141impl Variant {
142 fn is_minor(&self) -> bool {
143 matches!(self, Self::Minor)
144 }
145}"#,
146 );
147 }
148
149 #[test]
150 fn test_add_from_impl_no_element() {
151 mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented);
152 check_not_applicable(
153 r#"
154enum Variant {
155 Undefined,
156 Minor(u32)$0,
157 Major,
158}"#,
159 );
160 }
161
162 #[test]
163 fn test_generate_enum_match_from_variant_with_one_variant() {
164 check_assist(
165 generate_enum_match_method,
166 r#"enum Variant { Undefi$0ned }"#,
167 r#"
168enum Variant { Undefined }
169
170impl Variant {
171 /// Returns `true` if the variant is [`Undefined`].
172 fn is_undefined(&self) -> bool {
173 matches!(self, Self::Undefined)
174 }
175}"#,
176 );
177 }
178
179 #[test]
180 fn test_generate_enum_match_from_variant_with_visibility_marker() {
181 check_assist(
182 generate_enum_match_method,
183 r#"
184pub(crate) enum Variant {
185 Undefined,
186 Minor$0,
187 Major,
188}"#,
189 r#"pub(crate) enum Variant {
190 Undefined,
191 Minor,
192 Major,
193}
194
195impl Variant {
196 /// Returns `true` if the variant is [`Minor`].
197 pub(crate) fn is_minor(&self) -> bool {
198 matches!(self, Self::Minor)
199 }
200}"#,
201 );
202 }
203
204 #[test]
205 fn test_multiple_generate_enum_match_from_variant() {
206 check_assist(
207 generate_enum_match_method,
208 r#"
209enum Variant {
210 Undefined,
211 Minor,
212 Major$0,
213}
214
215impl Variant {
216 /// Returns `true` if the variant is [`Minor`].
217 fn is_minor(&self) -> bool {
218 matches!(self, Self::Minor)
219 }
220}"#,
221 r#"enum Variant {
222 Undefined,
223 Minor,
224 Major,
225}
226
227impl Variant {
228 /// Returns `true` if the variant is [`Minor`].
229 fn is_minor(&self) -> bool {
230 matches!(self, Self::Minor)
231 }
232
233 /// Returns `true` if the variant is [`Major`].
234 fn is_major(&self) -> bool {
235 matches!(self, Self::Major)
236 }
237}"#,
238 );
239 }
240}