diff options
Diffstat (limited to 'crates/assists/src/handlers/generate_setter.rs')
-rw-r--r-- | crates/assists/src/handlers/generate_setter.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/generate_setter.rs b/crates/assists/src/handlers/generate_setter.rs new file mode 100644 index 000000000..c9043a162 --- /dev/null +++ b/crates/assists/src/handlers/generate_setter.rs | |||
@@ -0,0 +1,162 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | ||
2 | use syntax::ast::VisibilityOwner; | ||
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | |||
5 | use crate::{ | ||
6 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | ||
7 | AssistContext, AssistId, AssistKind, Assists, | ||
8 | }; | ||
9 | |||
10 | // Assist: generate_setter | ||
11 | // | ||
12 | // Generate a setter method. | ||
13 | // | ||
14 | // ``` | ||
15 | // struct Person { | ||
16 | // nam$0e: String, | ||
17 | // } | ||
18 | // ``` | ||
19 | // -> | ||
20 | // ``` | ||
21 | // struct Person { | ||
22 | // name: String, | ||
23 | // } | ||
24 | // | ||
25 | // impl Person { | ||
26 | // /// Set the person's name. | ||
27 | // fn set_name(&mut self, name: String) { | ||
28 | // self.name = name; | ||
29 | // } | ||
30 | // } | ||
31 | // ``` | ||
32 | pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
33 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
34 | let field = ctx.find_node_at_offset::<ast::RecordField>()?; | ||
35 | |||
36 | let strukt_name = strukt.name()?; | ||
37 | let field_name = field.name()?; | ||
38 | let field_ty = field.ty()?; | ||
39 | |||
40 | // Return early if we've found an existing fn | ||
41 | let fn_name = to_lower_snake_case(&field_name.to_string()); | ||
42 | let impl_def = find_struct_impl( | ||
43 | &ctx, | ||
44 | &ast::Adt::Struct(strukt.clone()), | ||
45 | format!("set_{}", fn_name).as_str(), | ||
46 | )?; | ||
47 | |||
48 | let target = field.syntax().text_range(); | ||
49 | acc.add( | ||
50 | AssistId("generate_setter", AssistKind::Generate), | ||
51 | "Generate a setter method", | ||
52 | target, | ||
53 | |builder| { | ||
54 | let mut buf = String::with_capacity(512); | ||
55 | |||
56 | let fn_name_spaced = fn_name.replace('_', " "); | ||
57 | let strukt_name_spaced = | ||
58 | to_lower_snake_case(&strukt_name.to_string()).replace('_', " "); | ||
59 | |||
60 | if impl_def.is_some() { | ||
61 | buf.push('\n'); | ||
62 | } | ||
63 | |||
64 | let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
65 | format_to!( | ||
66 | buf, | ||
67 | " /// Set the {}'s {}. | ||
68 | {}fn set_{}(&mut self, {}: {}) {{ | ||
69 | self.{} = {}; | ||
70 | }}", | ||
71 | strukt_name_spaced, | ||
72 | fn_name_spaced, | ||
73 | vis, | ||
74 | fn_name, | ||
75 | fn_name, | ||
76 | field_ty, | ||
77 | fn_name, | ||
78 | fn_name, | ||
79 | ); | ||
80 | |||
81 | let start_offset = impl_def | ||
82 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | ||
83 | .unwrap_or_else(|| { | ||
84 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); | ||
85 | strukt.syntax().text_range().end() | ||
86 | }); | ||
87 | |||
88 | builder.insert(start_offset, buf); | ||
89 | }, | ||
90 | ) | ||
91 | } | ||
92 | |||
93 | #[cfg(test)] | ||
94 | mod tests { | ||
95 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
96 | |||
97 | use super::*; | ||
98 | |||
99 | fn check_not_applicable(ra_fixture: &str) { | ||
100 | check_assist_not_applicable(generate_setter, ra_fixture) | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn test_generate_setter_from_field() { | ||
105 | check_assist( | ||
106 | generate_setter, | ||
107 | r#" | ||
108 | struct Person<T: Clone> { | ||
109 | dat$0a: T, | ||
110 | }"#, | ||
111 | r#" | ||
112 | struct Person<T: Clone> { | ||
113 | data: T, | ||
114 | } | ||
115 | |||
116 | impl<T: Clone> Person<T> { | ||
117 | /// Set the person's data. | ||
118 | fn set_data(&mut self, data: T) { | ||
119 | self.data = data; | ||
120 | } | ||
121 | }"#, | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn test_generate_setter_already_implemented() { | ||
127 | check_not_applicable( | ||
128 | r#" | ||
129 | struct Person<T: Clone> { | ||
130 | dat$0a: T, | ||
131 | } | ||
132 | |||
133 | impl<T: Clone> Person<T> { | ||
134 | fn set_data(&mut self, data: T) { | ||
135 | self.data = data; | ||
136 | } | ||
137 | }"#, | ||
138 | ); | ||
139 | } | ||
140 | |||
141 | #[test] | ||
142 | fn test_generate_setter_from_field_with_visibility_marker() { | ||
143 | check_assist( | ||
144 | generate_setter, | ||
145 | r#" | ||
146 | pub(crate) struct Person<T: Clone> { | ||
147 | dat$0a: T, | ||
148 | }"#, | ||
149 | r#" | ||
150 | pub(crate) struct Person<T: Clone> { | ||
151 | data: T, | ||
152 | } | ||
153 | |||
154 | impl<T: Clone> Person<T> { | ||
155 | /// Set the person's data. | ||
156 | pub(crate) fn set_data(&mut self, data: T) { | ||
157 | self.data = data; | ||
158 | } | ||
159 | }"#, | ||
160 | ); | ||
161 | } | ||
162 | } | ||