aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_getter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_getter.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs292
1 files changed, 238 insertions, 54 deletions
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index df7d1bb95..09971226e 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -23,12 +23,46 @@ use crate::{
23// 23//
24// impl Person { 24// impl Person {
25// /// Get a reference to the person's name. 25// /// Get a reference to the person's name.
26// fn name(&self) -> &String { 26// fn $0name(&self) -> &str {
27// &self.name 27// self.name.as_str()
28// } 28// }
29// } 29// }
30// ``` 30// ```
31pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(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 $0name_mut(&mut self) -> &mut String {
53// &mut self.name
54// }
55// }
56// ```
57pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
58 generate_getter_impl(acc, ctx, true)
59}
60
61pub(crate) fn generate_getter_impl(
62 acc: &mut Assists,
63 ctx: &AssistContext,
64 mutable: bool,
65) -> Option<()> {
32 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 66 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
33 let field = ctx.find_node_at_offset::<ast::RecordField>()?; 67 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
34 68
@@ -37,39 +71,52 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
37 let field_ty = field.ty()?; 71 let field_ty = field.ty()?;
38 72
39 // Return early if we've found an existing fn 73 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string()); 74 let mut fn_name = to_lower_snake_case(&field_name.to_string());
75 if mutable {
76 format_to!(fn_name, "_mut");
77 }
41 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 78 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
42 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 };
43 let target = field.syntax().text_range(); 85 let target = field.syntax().text_range();
44 acc.add_group( 86 acc.add_group(
45 &GroupLabel("Generate getter/setter".to_owned()), 87 &GroupLabel("Generate getter/setter".to_owned()),
46 AssistId("generate_getter", AssistKind::Generate), 88 AssistId(id, AssistKind::Generate),
47 "Generate a getter method", 89 label,
48 target, 90 target,
49 |builder| { 91 |builder| {
50 let mut buf = String::with_capacity(512); 92 let mut buf = String::with_capacity(512);
51 93
52 let fn_name_spaced = fn_name.replace('_', " ");
53 let strukt_name_spaced =
54 to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
55
56 if impl_def.is_some() { 94 if impl_def.is_some() {
57 buf.push('\n'); 95 buf.push('\n');
58 } 96 }
59 97
60 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); 98 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
99 let (ty, body) = if mutable {
100 (format!("&mut {}", field_ty), format!("&mut self.{}", field_name))
101 } else {
102 useless_type_special_case(&field_name.to_string(), &field_ty)
103 .unwrap_or_else(|| (format!("&{}", field_ty), format!("&self.{}", field_name)))
104 };
105
61 format_to!( 106 format_to!(
62 buf, 107 buf,
63 " /// Get a reference to the {}'s {}. 108 " /// Get a {}reference to the {}'s {}.
64 {}fn {}(&self) -> &{} {{ 109 {}fn {}(&{}self) -> {} {{
65 &self.{} 110 {}
66 }}", 111 }}",
67 strukt_name_spaced, 112 mutable.then(|| "mutable ").unwrap_or_default(),
68 fn_name_spaced, 113 to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
114 fn_name.trim_end_matches("_mut").replace('_', " "),
69 vis, 115 vis,
70 fn_name, 116 fn_name,
71 field_ty, 117 mutable.then(|| "mut ").unwrap_or_default(),
72 fn_name, 118 ty,
119 body,
73 ); 120 );
74 121
75 let start_offset = impl_def 122 let start_offset = impl_def
@@ -79,56 +126,120 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
79 strukt.syntax().text_range().end() 126 strukt.syntax().text_range().end()
80 }); 127 });
81 128
82 builder.insert(start_offset, buf); 129 match ctx.config.snippet_cap {
130 Some(cap) => {
131 builder.insert_snippet(cap, start_offset, buf.replacen("fn ", "fn $0", 1))
132 }
133 None => builder.insert(start_offset, buf),
134 }
83 }, 135 },
84 ) 136 )
85} 137}
86 138
139fn useless_type_special_case(field_name: &str, field_ty: &ast::Type) -> Option<(String, String)> {
140 if field_ty.to_string() == "String" {
141 cov_mark::hit!(useless_type_special_case);
142 return Some(("&str".to_string(), format!("self.{}.as_str()", field_name)));
143 }
144 if let Some(arg) = ty_ctor(field_ty, "Vec") {
145 return Some((format!("&[{}]", arg), format!("self.{}.as_slice()", field_name)));
146 }
147 if let Some(arg) = ty_ctor(field_ty, "Box") {
148 return Some((format!("&{}", arg), format!("self.{}.as_ref()", field_name)));
149 }
150 if let Some(arg) = ty_ctor(field_ty, "Option") {
151 return Some((format!("Option<&{}>", arg), format!("self.{}.as_ref()", field_name)));
152 }
153 None
154}
155
156// FIXME: This should rely on semantic info.
157fn ty_ctor(ty: &ast::Type, ctor: &str) -> Option<String> {
158 let res = ty.to_string().strip_prefix(ctor)?.strip_prefix('<')?.strip_suffix('>')?.to_string();
159 Some(res)
160}
161
87#[cfg(test)] 162#[cfg(test)]
88mod tests { 163mod tests {
89 use crate::tests::{check_assist, check_assist_not_applicable}; 164 use crate::tests::{check_assist, check_assist_not_applicable};
90 165
91 use super::*; 166 use super::*;
92 167
93 fn check_not_applicable(ra_fixture: &str) {
94 check_assist_not_applicable(generate_getter, ra_fixture)
95 }
96
97 #[test] 168 #[test]
98 fn test_generate_getter_from_field() { 169 fn test_generate_getter_from_field() {
99 check_assist( 170 check_assist(
100 generate_getter, 171 generate_getter,
101 r#" 172 r#"
102struct Context<T: Clone> { 173struct Context {
103 dat$0a: T, 174 dat$0a: Data,
104}"#, 175}
176"#,
105 r#" 177 r#"
106struct Context<T: Clone> { 178struct Context {
107 data: T, 179 data: Data,
108} 180}
109 181
110impl<T: Clone> Context<T> { 182impl Context {
111 /// Get a reference to the context's data. 183 /// Get a reference to the context's data.
112 fn data(&self) -> &T { 184 fn $0data(&self) -> &Data {
113 &self.data 185 &self.data
114 } 186 }
115}"#, 187}
188"#,
189 );
190
191 check_assist(
192 generate_getter_mut,
193 r#"
194struct Context {
195 dat$0a: Data,
196}
197"#,
198 r#"
199struct Context {
200 data: Data,
201}
202
203impl Context {
204 /// Get a mutable reference to the context's data.
205 fn $0data_mut(&mut self) -> &mut Data {
206 &mut self.data
207 }
208}
209"#,
116 ); 210 );
117 } 211 }
118 212
119 #[test] 213 #[test]
120 fn test_generate_getter_already_implemented() { 214 fn test_generate_getter_already_implemented() {
121 check_not_applicable( 215 check_assist_not_applicable(
216 generate_getter,
122 r#" 217 r#"
123struct Context<T: Clone> { 218struct Context {
124 dat$0a: T, 219 dat$0a: Data,
125} 220}
126 221
127impl<T: Clone> Context<T> { 222impl Context {
128 fn data(&self) -> &T { 223 fn data(&self) -> &Data {
129 &self.data 224 &self.data
130 } 225 }
131}"#, 226}
227"#,
228 );
229
230 check_assist_not_applicable(
231 generate_getter_mut,
232 r#"
233struct Context {
234 dat$0a: Data,
235}
236
237impl Context {
238 fn data_mut(&mut self) -> &mut Data {
239 &mut self.data
240 }
241}
242"#,
132 ); 243 );
133 } 244 }
134 245
@@ -137,20 +248,22 @@ impl<T: Clone> Context<T> {
137 check_assist( 248 check_assist(
138 generate_getter, 249 generate_getter,
139 r#" 250 r#"
140pub(crate) struct Context<T: Clone> { 251pub(crate) struct Context {
141 dat$0a: T, 252 dat$0a: Data,
142}"#, 253}
254"#,
143 r#" 255 r#"
144pub(crate) struct Context<T: Clone> { 256pub(crate) struct Context {
145 data: T, 257 data: Data,
146} 258}
147 259
148impl<T: Clone> Context<T> { 260impl Context {
149 /// Get a reference to the context's data. 261 /// Get a reference to the context's data.
150 pub(crate) fn data(&self) -> &T { 262 pub(crate) fn $0data(&self) -> &Data {
151 &self.data 263 &self.data
152 } 264 }
153}"#, 265}
266"#,
154 ); 267 );
155 } 268 }
156 269
@@ -159,34 +272,105 @@ impl<T: Clone> Context<T> {
159 check_assist( 272 check_assist(
160 generate_getter, 273 generate_getter,
161 r#" 274 r#"
162struct Context<T: Clone> { 275struct Context {
163 data: T, 276 data: Data,
164 cou$0nt: usize, 277 cou$0nt: usize,
165} 278}
166 279
167impl<T: Clone> Context<T> { 280impl Context {
168 /// Get a reference to the context's data. 281 /// Get a reference to the context's data.
169 fn data(&self) -> &T { 282 fn data(&self) -> &Data {
170 &self.data 283 &self.data
171 } 284 }
172}"#, 285}
286"#,
173 r#" 287 r#"
174struct Context<T: Clone> { 288struct Context {
175 data: T, 289 data: Data,
176 count: usize, 290 count: usize,
177} 291}
178 292
179impl<T: Clone> Context<T> { 293impl Context {
180 /// Get a reference to the context's data. 294 /// Get a reference to the context's data.
181 fn data(&self) -> &T { 295 fn data(&self) -> &Data {
182 &self.data 296 &self.data
183 } 297 }
184 298
185 /// Get a reference to the context's count. 299 /// Get a reference to the context's count.
186 fn count(&self) -> &usize { 300 fn $0count(&self) -> &usize {
187 &self.count 301 &self.count
188 } 302 }
189}"#, 303}
304"#,
305 );
306 }
307
308 #[test]
309 fn test_special_cases() {
310 cov_mark::check!(useless_type_special_case);
311 check_assist(
312 generate_getter,
313 r#"
314struct S { foo: $0String }
315"#,
316 r#"
317struct S { foo: String }
318
319impl S {
320 /// Get a reference to the s's foo.
321 fn $0foo(&self) -> &str {
322 self.foo.as_str()
323 }
324}
325"#,
326 );
327 check_assist(
328 generate_getter,
329 r#"
330struct S { foo: $0Box<Sweets> }
331"#,
332 r#"
333struct S { foo: Box<Sweets> }
334
335impl S {
336 /// Get a reference to the s's foo.
337 fn $0foo(&self) -> &Sweets {
338 self.foo.as_ref()
339 }
340}
341"#,
342 );
343 check_assist(
344 generate_getter,
345 r#"
346struct S { foo: $0Vec<()> }
347"#,
348 r#"
349struct S { foo: Vec<()> }
350
351impl S {
352 /// Get a reference to the s's foo.
353 fn $0foo(&self) -> &[()] {
354 self.foo.as_slice()
355 }
356}
357"#,
358 );
359 check_assist(
360 generate_getter,
361 r#"
362struct S { foo: $0Option<Failure> }
363"#,
364 r#"
365struct S { foo: Option<Failure> }
366
367impl S {
368 /// Get a reference to the s's foo.
369 fn $0foo(&self) -> Option<&Failure> {
370 self.foo.as_ref()
371 }
372}
373"#,
190 ); 374 );
191 } 375 }
192} 376}