aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-23 21:48:35 +0100
committerGitHub <[email protected]>2021-05-23 21:48:35 +0100
commit951c0e95f44bf7947c2a46ef9e8ff2616823faba (patch)
treef202726bcd9676b919d04d638f9fafa6f9b801e9 /crates/ide_assists/src/handlers
parente0864c9c153c88a4bc3b75d0cc5501678a21183a (diff)
parent8696c82777f9992fceadc531536bf90b64cf753d (diff)
Merge #8948
8948: feat: generate getter assist places the cursor at the generated function r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ide_assists/src/handlers')
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs189
-rw-r--r--crates/ide_assists/src/handlers/generate_getter_mut.rs195
2 files changed, 137 insertions, 247 deletions
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index df7d1bb95..9faaaf284 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) -> &String {
27// &self.name 27// &self.name
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,22 +71,26 @@ 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 }
@@ -60,16 +98,18 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
60 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); 98 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
61 format_to!( 99 format_to!(
62 buf, 100 buf,
63 " /// Get a reference to the {}'s {}. 101 " /// Get a {}reference to the {}'s {}.
64 {}fn {}(&self) -> &{} {{ 102 {}fn {}(&{mut_}self) -> &{mut_}{} {{
65 &self.{} 103 &{mut_}self.{}
66 }}", 104 }}",
67 strukt_name_spaced, 105 mutable.then(|| "mutable ").unwrap_or_default(),
68 fn_name_spaced, 106 to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
107 fn_name.trim_end_matches("_mut").replace('_', " "),
69 vis, 108 vis,
70 fn_name, 109 fn_name,
71 field_ty, 110 field_ty,
72 fn_name, 111 field_name,
112 mut_ = mutable.then(|| "mut ").unwrap_or_default(),
73 ); 113 );
74 114
75 let start_offset = impl_def 115 let start_offset = impl_def
@@ -79,7 +119,12 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
79 strukt.syntax().text_range().end() 119 strukt.syntax().text_range().end()
80 }); 120 });
81 121
82 builder.insert(start_offset, buf); 122 match ctx.config.snippet_cap {
123 Some(cap) => {
124 builder.insert_snippet(cap, start_offset, buf.replacen("fn ", "fn $0", 1))
125 }
126 None => builder.insert(start_offset, buf),
127 }
83 }, 128 },
84 ) 129 )
85} 130}
@@ -90,45 +135,81 @@ mod tests {
90 135
91 use super::*; 136 use super::*;
92 137
93 fn check_not_applicable(ra_fixture: &str) {
94 check_assist_not_applicable(generate_getter, ra_fixture)
95 }
96
97 #[test] 138 #[test]
98 fn test_generate_getter_from_field() { 139 fn test_generate_getter_from_field() {
99 check_assist( 140 check_assist(
100 generate_getter, 141 generate_getter,
101 r#" 142 r#"
102struct Context<T: Clone> { 143struct Context {
103 dat$0a: T, 144 dat$0a: Data,
104}"#, 145}
146"#,
105 r#" 147 r#"
106struct Context<T: Clone> { 148struct Context {
107 data: T, 149 data: Data,
108} 150}
109 151
110impl<T: Clone> Context<T> { 152impl Context {
111 /// Get a reference to the context's data. 153 /// Get a reference to the context's data.
112 fn data(&self) -> &T { 154 fn $0data(&self) -> &Data {
113 &self.data 155 &self.data
114 } 156 }
115}"#, 157}
158"#,
159 );
160
161 check_assist(
162 generate_getter_mut,
163 r#"
164struct Context {
165 dat$0a: Data,
166}
167"#,
168 r#"
169struct Context {
170 data: Data,
171}
172
173impl Context {
174 /// Get a mutable reference to the context's data.
175 fn $0data_mut(&mut self) -> &mut Data {
176 &mut self.data
177 }
178}
179"#,
116 ); 180 );
117 } 181 }
118 182
119 #[test] 183 #[test]
120 fn test_generate_getter_already_implemented() { 184 fn test_generate_getter_already_implemented() {
121 check_not_applicable( 185 check_assist_not_applicable(
186 generate_getter,
122 r#" 187 r#"
123struct Context<T: Clone> { 188struct Context {
124 dat$0a: T, 189 dat$0a: Data,
125} 190}
126 191
127impl<T: Clone> Context<T> { 192impl Context {
128 fn data(&self) -> &T { 193 fn data(&self) -> &Data {
129 &self.data 194 &self.data
130 } 195 }
131}"#, 196}
197"#,
198 );
199
200 check_assist_not_applicable(
201 generate_getter_mut,
202 r#"
203struct Context {
204 dat$0a: Data,
205}
206
207impl Context {
208 fn data_mut(&mut self) -> &mut Data {
209 &mut self.data
210 }
211}
212"#,
132 ); 213 );
133 } 214 }
134 215
@@ -137,20 +218,22 @@ impl<T: Clone> Context<T> {
137 check_assist( 218 check_assist(
138 generate_getter, 219 generate_getter,
139 r#" 220 r#"
140pub(crate) struct Context<T: Clone> { 221pub(crate) struct Context {
141 dat$0a: T, 222 dat$0a: Data,
142}"#, 223}
224"#,
143 r#" 225 r#"
144pub(crate) struct Context<T: Clone> { 226pub(crate) struct Context {
145 data: T, 227 data: Data,
146} 228}
147 229
148impl<T: Clone> Context<T> { 230impl Context {
149 /// Get a reference to the context's data. 231 /// Get a reference to the context's data.
150 pub(crate) fn data(&self) -> &T { 232 pub(crate) fn $0data(&self) -> &Data {
151 &self.data 233 &self.data
152 } 234 }
153}"#, 235}
236"#,
154 ); 237 );
155 } 238 }
156 239
@@ -159,34 +242,36 @@ impl<T: Clone> Context<T> {
159 check_assist( 242 check_assist(
160 generate_getter, 243 generate_getter,
161 r#" 244 r#"
162struct Context<T: Clone> { 245struct Context {
163 data: T, 246 data: Data,
164 cou$0nt: usize, 247 cou$0nt: usize,
165} 248}
166 249
167impl<T: Clone> Context<T> { 250impl Context {
168 /// Get a reference to the context's data. 251 /// Get a reference to the context's data.
169 fn data(&self) -> &T { 252 fn data(&self) -> &Data {
170 &self.data 253 &self.data
171 } 254 }
172}"#, 255}
256"#,
173 r#" 257 r#"
174struct Context<T: Clone> { 258struct Context {
175 data: T, 259 data: Data,
176 count: usize, 260 count: usize,
177} 261}
178 262
179impl<T: Clone> Context<T> { 263impl Context {
180 /// Get a reference to the context's data. 264 /// Get a reference to the context's data.
181 fn data(&self) -> &T { 265 fn data(&self) -> &Data {
182 &self.data 266 &self.data
183 } 267 }
184 268
185 /// Get a reference to the context's count. 269 /// Get a reference to the context's count.
186 fn count(&self) -> &usize { 270 fn $0count(&self) -> &usize {
187 &self.count 271 &self.count
188 } 272 }
189}"#, 273}
274"#,
190 ); 275 );
191 } 276 }
192} 277}
diff --git a/crates/ide_assists/src/handlers/generate_getter_mut.rs b/crates/ide_assists/src/handlers/generate_getter_mut.rs
deleted file mode 100644
index 821c2eed5..000000000
--- a/crates/ide_assists/src/handlers/generate_getter_mut.rs
+++ /dev/null
@@ -1,195 +0,0 @@
1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3
4use 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_mut
10//
11// Generate a mut 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 mutable reference to the person's name.
26// fn name_mut(&mut self) -> &mut String {
27// &mut self.name
28// }
29// }
30// ```
31pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
32 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
33 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
34
35 let strukt_name = strukt.name()?;
36 let field_name = field.name()?;
37 let field_ty = field.ty()?;
38
39 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string());
41 let impl_def = find_struct_impl(
42 &ctx,
43 &ast::Adt::Struct(strukt.clone()),
44 format!("{}_mut", fn_name).as_str(),
45 )?;
46
47 let target = field.syntax().text_range();
48 acc.add_group(
49 &GroupLabel("Generate getter/setter".to_owned()),
50 AssistId("generate_getter_mut", AssistKind::Generate),
51 "Generate a mut getter method",
52 target,
53 |builder| {
54 let mut buf = String::with_capacity(512);
55 let fn_name_spaced = fn_name.replace('_', " ");
56 let strukt_name_spaced =
57 to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
58
59 if impl_def.is_some() {
60 buf.push('\n');
61 }
62
63 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
64 format_to!(
65 buf,
66 " /// Get a mutable reference to the {}'s {}.
67 {}fn {}_mut(&mut self) -> &mut {} {{
68 &mut self.{}
69 }}",
70 strukt_name_spaced,
71 fn_name_spaced,
72 vis,
73 fn_name,
74 field_ty,
75 fn_name,
76 );
77
78 let start_offset = impl_def
79 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
80 .unwrap_or_else(|| {
81 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
82 strukt.syntax().text_range().end()
83 });
84
85 builder.insert(start_offset, buf);
86 },
87 )
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::tests::{check_assist, check_assist_not_applicable};
93
94 use super::*;
95
96 fn check_not_applicable(ra_fixture: &str) {
97 check_assist_not_applicable(generate_getter_mut, ra_fixture)
98 }
99
100 #[test]
101 fn test_generate_getter_mut_from_field() {
102 check_assist(
103 generate_getter_mut,
104 r#"
105struct Context<T: Clone> {
106 dat$0a: T,
107}"#,
108 r#"
109struct Context<T: Clone> {
110 data: T,
111}
112
113impl<T: Clone> Context<T> {
114 /// Get a mutable reference to the context's data.
115 fn data_mut(&mut self) -> &mut T {
116 &mut self.data
117 }
118}"#,
119 );
120 }
121
122 #[test]
123 fn test_generate_getter_mut_already_implemented() {
124 check_not_applicable(
125 r#"
126struct Context<T: Clone> {
127 dat$0a: T,
128}
129
130impl<T: Clone> Context<T> {
131 fn data_mut(&mut self) -> &mut T {
132 &mut self.data
133 }
134}"#,
135 );
136 }
137
138 #[test]
139 fn test_generate_getter_mut_from_field_with_visibility_marker() {
140 check_assist(
141 generate_getter_mut,
142 r#"
143pub(crate) struct Context<T: Clone> {
144 dat$0a: T,
145}"#,
146 r#"
147pub(crate) struct Context<T: Clone> {
148 data: T,
149}
150
151impl<T: Clone> Context<T> {
152 /// Get a mutable reference to the context's data.
153 pub(crate) fn data_mut(&mut self) -> &mut T {
154 &mut self.data
155 }
156}"#,
157 );
158 }
159
160 #[test]
161 fn test_multiple_generate_getter_mut() {
162 check_assist(
163 generate_getter_mut,
164 r#"
165struct Context<T: Clone> {
166 data: T,
167 cou$0nt: usize,
168}
169
170impl<T: Clone> Context<T> {
171 /// Get a mutable reference to the context's data.
172 fn data_mut(&mut self) -> &mut T {
173 &mut self.data
174 }
175}"#,
176 r#"
177struct Context<T: Clone> {
178 data: T,
179 count: usize,
180}
181
182impl<T: Clone> Context<T> {
183 /// Get a mutable reference to the context's data.
184 fn data_mut(&mut self) -> &mut T {
185 &mut self.data
186 }
187
188 /// Get a mutable reference to the context's count.
189 fn count_mut(&mut self) -> &mut usize {
190 &mut self.count
191 }
192}"#,
193 );
194 }
195}