]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_getter.rs
reduce duplication
[rust.git] / crates / ide_assists / src / handlers / generate_getter.rs
1 use stdx::{format_to, to_lower_snake_case};
2 use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3
4 use crate::{
5     utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
6     AssistContext, AssistId, AssistKind, Assists, GroupLabel,
7 };
8
9 // Assist: generate_getter
10 //
11 // Generate a getter method.
12 //
13 // ```
14 // struct Person {
15 //     nam$0e: String,
16 // }
17 // ```
18 // ->
19 // ```
20 // struct Person {
21 //     name: String,
22 // }
23 //
24 // impl Person {
25 //     /// Get a reference to the person's name.
26 //     fn name(&self) -> &String {
27 //         &self.name
28 //     }
29 // }
30 // ```
31 pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
32     generate_getter_impl(acc, ctx, false)
33 }
34
35 // Assist: generate_getter_mut
36 //
37 // Generate a mut getter method.
38 //
39 // ```
40 // struct Person {
41 //     nam$0e: String,
42 // }
43 // ```
44 // ->
45 // ```
46 // struct Person {
47 //     name: String,
48 // }
49 //
50 // impl Person {
51 //     /// Get a mutable reference to the person's name.
52 //     fn name_mut(&mut self) -> &mut String {
53 //         &mut self.name
54 //     }
55 // }
56 // ```
57 pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
58     generate_getter_impl(acc, ctx, true)
59 }
60
61 pub(crate) fn generate_getter_impl(
62     acc: &mut Assists,
63     ctx: &AssistContext,
64     mutable: bool,
65 ) -> Option<()> {
66     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
67     let field = ctx.find_node_at_offset::<ast::RecordField>()?;
68
69     let strukt_name = strukt.name()?;
70     let field_name = field.name()?;
71     let field_ty = field.ty()?;
72
73     // Return early if we've found an existing fn
74     let mut fn_name = to_lower_snake_case(&field_name.to_string());
75     if mutable {
76         format_to!(fn_name, "_mut");
77     }
78     let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
79
80     let (id, label) = if mutable {
81         ("generate_getter_mut", "Generate a mut getter method")
82     } else {
83         ("generate_getter", "Generate a getter method")
84     };
85     let target = field.syntax().text_range();
86     acc.add_group(
87         &GroupLabel("Generate getter/setter".to_owned()),
88         AssistId(id, AssistKind::Generate),
89         label,
90         target,
91         |builder| {
92             let mut buf = String::with_capacity(512);
93
94             if impl_def.is_some() {
95                 buf.push('\n');
96             }
97
98             let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
99             format_to!(
100                 buf,
101                 "    /// Get a {}reference to the {}'s {}.
102     {}fn {}(&{mut_}self) -> &{mut_}{} {{
103         &{mut_}self.{}
104     }}",
105                 mutable.then(|| "mutable ").unwrap_or_default(),
106                 to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
107                 fn_name.trim_end_matches("_mut").replace('_', " "),
108                 vis,
109                 fn_name,
110                 field_ty,
111                 field_name,
112                 mut_ = mutable.then(|| "mut ").unwrap_or_default(),
113             );
114
115             let start_offset = impl_def
116                 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
117                 .unwrap_or_else(|| {
118                     buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
119                     strukt.syntax().text_range().end()
120                 });
121
122             builder.insert(start_offset, buf);
123         },
124     )
125 }
126
127 #[cfg(test)]
128 mod tests {
129     use crate::tests::{check_assist, check_assist_not_applicable};
130
131     use super::*;
132
133     fn check_not_applicable(ra_fixture: &str) {
134         check_assist_not_applicable(generate_getter, ra_fixture)
135     }
136
137     #[test]
138     fn test_generate_getter_from_field() {
139         check_assist(
140             generate_getter,
141             r#"
142 struct Context<T: Clone> {
143     dat$0a: T,
144 }"#,
145             r#"
146 struct Context<T: Clone> {
147     data: T,
148 }
149
150 impl<T: Clone> Context<T> {
151     /// Get a reference to the context's data.
152     fn data(&self) -> &T {
153         &self.data
154     }
155 }"#,
156         );
157     }
158
159     #[test]
160     fn test_generate_getter_already_implemented() {
161         check_not_applicable(
162             r#"
163 struct Context<T: Clone> {
164     dat$0a: T,
165 }
166
167 impl<T: Clone> Context<T> {
168     fn data(&self) -> &T {
169         &self.data
170     }
171 }"#,
172         );
173     }
174
175     #[test]
176     fn test_generate_getter_from_field_with_visibility_marker() {
177         check_assist(
178             generate_getter,
179             r#"
180 pub(crate) struct Context<T: Clone> {
181     dat$0a: T,
182 }"#,
183             r#"
184 pub(crate) struct Context<T: Clone> {
185     data: T,
186 }
187
188 impl<T: Clone> Context<T> {
189     /// Get a reference to the context's data.
190     pub(crate) fn data(&self) -> &T {
191         &self.data
192     }
193 }"#,
194         );
195     }
196
197     #[test]
198     fn test_multiple_generate_getter() {
199         check_assist(
200             generate_getter,
201             r#"
202 struct Context<T: Clone> {
203     data: T,
204     cou$0nt: usize,
205 }
206
207 impl<T: Clone> Context<T> {
208     /// Get a reference to the context's data.
209     fn data(&self) -> &T {
210         &self.data
211     }
212 }"#,
213             r#"
214 struct Context<T: Clone> {
215     data: T,
216     count: usize,
217 }
218
219 impl<T: Clone> Context<T> {
220     /// Get a reference to the context's data.
221     fn data(&self) -> &T {
222         &self.data
223     }
224
225     /// Get a reference to the context's count.
226     fn count(&self) -> &usize {
227         &self.count
228     }
229 }"#,
230         );
231     }
232 }
233
234 #[cfg(test)]
235 mod tests_mut {
236     use crate::tests::{check_assist, check_assist_not_applicable};
237
238     use super::*;
239
240     fn check_not_applicable(ra_fixture: &str) {
241         check_assist_not_applicable(generate_getter_mut, ra_fixture)
242     }
243
244     #[test]
245     fn test_generate_getter_mut_from_field() {
246         check_assist(
247             generate_getter_mut,
248             r#"
249 struct Context<T: Clone> {
250     dat$0a: T,
251 }"#,
252             r#"
253 struct Context<T: Clone> {
254     data: T,
255 }
256
257 impl<T: Clone> Context<T> {
258     /// Get a mutable reference to the context's data.
259     fn data_mut(&mut self) -> &mut T {
260         &mut self.data
261     }
262 }"#,
263         );
264     }
265
266     #[test]
267     fn test_generate_getter_mut_already_implemented() {
268         check_not_applicable(
269             r#"
270 struct Context<T: Clone> {
271     dat$0a: T,
272 }
273
274 impl<T: Clone> Context<T> {
275     fn data_mut(&mut self) -> &mut T {
276         &mut self.data
277     }
278 }"#,
279         );
280     }
281
282     #[test]
283     fn test_generate_getter_mut_from_field_with_visibility_marker() {
284         check_assist(
285             generate_getter_mut,
286             r#"
287 pub(crate) struct Context<T: Clone> {
288     dat$0a: T,
289 }"#,
290             r#"
291 pub(crate) struct Context<T: Clone> {
292     data: T,
293 }
294
295 impl<T: Clone> Context<T> {
296     /// Get a mutable reference to the context's data.
297     pub(crate) fn data_mut(&mut self) -> &mut T {
298         &mut self.data
299     }
300 }"#,
301         );
302     }
303
304     #[test]
305     fn test_multiple_generate_getter_mut() {
306         check_assist(
307             generate_getter_mut,
308             r#"
309 struct Context<T: Clone> {
310     data: T,
311     cou$0nt: usize,
312 }
313
314 impl<T: Clone> Context<T> {
315     /// Get a mutable reference to the context's data.
316     fn data_mut(&mut self) -> &mut T {
317         &mut self.data
318     }
319 }"#,
320             r#"
321 struct Context<T: Clone> {
322     data: T,
323     count: usize,
324 }
325
326 impl<T: Clone> Context<T> {
327     /// Get a mutable reference to the context's data.
328     fn data_mut(&mut self) -> &mut T {
329         &mut self.data
330     }
331
332     /// Get a mutable reference to the context's count.
333     fn count_mut(&mut self) -> &mut usize {
334         &mut self.count
335     }
336 }"#,
337         );
338     }
339 }