diff options
Diffstat (limited to 'crates/ide_assists')
-rw-r--r-- | crates/ide_assists/src/handlers/generate_default_from_new.rs | 221 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 2 |
2 files changed, 223 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs new file mode 100644 index 000000000..a1174d315 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -0,0 +1,221 @@ | |||
1 | use crate::{AssistId, assist_context::{AssistContext, Assists}}; | ||
2 | use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxText, ast::{self, NameOwner}}; | ||
3 | use test_utils::mark; | ||
4 | |||
5 | // Assist: generate_default_from_new | ||
6 | // | ||
7 | // Generates default implementation from new method | ||
8 | // | ||
9 | // ``` | ||
10 | // struct Example { _inner: () } | ||
11 | // | ||
12 | // impl Example { | ||
13 | // pu|b fn new() -> Self { | ||
14 | // Self { _inner: () } | ||
15 | // } | ||
16 | // } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // struct Example { _inner: () } | ||
21 | |||
22 | // impl Example { | ||
23 | // pub fn new() -> Self { | ||
24 | // Self { _inner: () } | ||
25 | // } | ||
26 | // } | ||
27 | |||
28 | // impl Default for Example { | ||
29 | // fn default() -> Self { | ||
30 | // Self::new() | ||
31 | // } | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
35 | let fn_node: ast::Fn = ctx.find_node_at_offset()?; | ||
36 | let fn_name = fn_node.name()?.to_string(); | ||
37 | |||
38 | if !fn_name.eq("new") { | ||
39 | mark::hit!(other_function_than_new); | ||
40 | return None; | ||
41 | } | ||
42 | |||
43 | if fn_node.param_list()?.params().count() != 0 { | ||
44 | mark::hit!(new_function_with_parameters); | ||
45 | return None; | ||
46 | } | ||
47 | |||
48 | let insert_after = scope_for_fn_insertion_node(&fn_node.syntax())?; | ||
49 | let impl_obj = ast::Impl::cast(insert_after)?; | ||
50 | let struct_name = impl_obj.self_ty()?.syntax().text(); | ||
51 | |||
52 | let default_fn_syntax = default_fn_node_for_new(struct_name); | ||
53 | |||
54 | |||
55 | acc.add( | ||
56 | AssistId("generate_default_from_new", crate::AssistKind::Generate), | ||
57 | "Generate a Default impl from a new fn", | ||
58 | impl_obj.syntax().text_range(), | ||
59 | move |builder| { | ||
60 | // TODO: indentation logic can also go here. | ||
61 | // let new_indent = IndentLevel::from_node(&insert_after); | ||
62 | let insert_location = impl_obj.syntax().text_range().end(); | ||
63 | builder.insert(insert_location, default_fn_syntax); | ||
64 | }, | ||
65 | ) | ||
66 | } | ||
67 | |||
68 | fn scope_for_fn_insertion_node(node: &SyntaxNode) -> Option<SyntaxNode> { | ||
69 | node.ancestors().into_iter().find(|node| node.kind() == SyntaxKind::IMPL) | ||
70 | } | ||
71 | |||
72 | fn default_fn_node_for_new(struct_name: SyntaxText) -> String { | ||
73 | // TODO: Update the implementation to consider the code indentation. | ||
74 | format!( | ||
75 | r#" | ||
76 | |||
77 | impl Default for {} {{ | ||
78 | fn default() -> Self {{ | ||
79 | Self::new() | ||
80 | }} | ||
81 | }}"# | ||
82 | ,struct_name) | ||
83 | } | ||
84 | |||
85 | #[cfg(test)] | ||
86 | mod tests { | ||
87 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
88 | |||
89 | use super::*; | ||
90 | |||
91 | #[test] | ||
92 | fn generate_default() { | ||
93 | check_assist( | ||
94 | generate_default_from_new, | ||
95 | r#" | ||
96 | struct Example { _inner: () } | ||
97 | |||
98 | impl Example { | ||
99 | pub fn ne$0w() -> Self { | ||
100 | Self { _inner: () } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn main() {} | ||
105 | "#, | ||
106 | r#" | ||
107 | struct Example { _inner: () } | ||
108 | |||
109 | impl Example { | ||
110 | pub fn new() -> Self { | ||
111 | Self { _inner: () } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | impl Default for Example { | ||
116 | fn default() -> Self { | ||
117 | Self::new() | ||
118 | } | ||
119 | } | ||
120 | |||
121 | fn main() {} | ||
122 | "#, | ||
123 | ); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn generate_default2() { | ||
128 | check_assist( | ||
129 | generate_default_from_new, | ||
130 | r#" | ||
131 | struct Test { value: u32 } | ||
132 | |||
133 | impl Test { | ||
134 | pub fn ne$0w() -> Self { | ||
135 | Self { value: 0 } | ||
136 | } | ||
137 | } | ||
138 | "#, | ||
139 | r#" | ||
140 | struct Test { value: u32 } | ||
141 | |||
142 | impl Test { | ||
143 | pub fn new() -> Self { | ||
144 | Self { value: 0 } | ||
145 | } | ||
146 | } | ||
147 | |||
148 | impl Default for Test { | ||
149 | fn default() -> Self { | ||
150 | Self::new() | ||
151 | } | ||
152 | } | ||
153 | "#, | ||
154 | ); | ||
155 | } | ||
156 | |||
157 | #[test] | ||
158 | fn new_function_with_parameters() { | ||
159 | mark::check!(new_function_with_parameters); | ||
160 | check_assist_not_applicable(generate_default_from_new, | ||
161 | r#" | ||
162 | struct Example { _inner: () } | ||
163 | |||
164 | impl Example { | ||
165 | pub fn $0new(value: ()) -> Self { | ||
166 | Self { _inner: value } | ||
167 | } | ||
168 | } | ||
169 | "# | ||
170 | ); | ||
171 | } | ||
172 | |||
173 | #[test] | ||
174 | fn other_function_than_new() { | ||
175 | mark::check!(other_function_than_new); | ||
176 | check_assist_not_applicable(generate_default_from_new, | ||
177 | r#" | ||
178 | struct Example { _inner: () } | ||
179 | |||
180 | impl Exmaple { | ||
181 | pub fn a$0dd() -> Self { | ||
182 | Self { _inner: () } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | "# | ||
187 | ); | ||
188 | } | ||
189 | |||
190 | // #[test] | ||
191 | // fn default_block_is_already_present() { | ||
192 | // check_assist_not_applicable(generate_default_from_new, | ||
193 | // r#" | ||
194 | // struct Example { _inner: () } | ||
195 | |||
196 | // impl Exmaple { | ||
197 | // pub fn n$0ew() -> Self { | ||
198 | // Self { _inner: () } | ||
199 | // } | ||
200 | // } | ||
201 | |||
202 | // impl Default for Example { | ||
203 | // fn default() -> Self { | ||
204 | // Self::new() | ||
205 | // } | ||
206 | // } | ||
207 | // "#, | ||
208 | // ); | ||
209 | // } | ||
210 | |||
211 | #[test] | ||
212 | fn standalone_new_function() { | ||
213 | check_assist_not_applicable(generate_default_from_new, | ||
214 | r#" | ||
215 | fn n$0ew() -> u32 { | ||
216 | 0 | ||
217 | } | ||
218 | "# | ||
219 | ); | ||
220 | } | ||
221 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 9c8148462..ea62d5f5d 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -127,6 +127,7 @@ mod handlers { | |||
127 | mod flip_comma; | 127 | mod flip_comma; |
128 | mod flip_trait_bound; | 128 | mod flip_trait_bound; |
129 | mod generate_default_from_enum_variant; | 129 | mod generate_default_from_enum_variant; |
130 | mod generate_default_from_new; | ||
130 | mod generate_derive; | 131 | mod generate_derive; |
131 | mod generate_enum_is_method; | 132 | mod generate_enum_is_method; |
132 | mod generate_enum_projection_method; | 133 | mod generate_enum_projection_method; |
@@ -189,6 +190,7 @@ mod handlers { | |||
189 | flip_comma::flip_comma, | 190 | flip_comma::flip_comma, |
190 | flip_trait_bound::flip_trait_bound, | 191 | flip_trait_bound::flip_trait_bound, |
191 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 192 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
193 | generate_default_from_new::generate_default_from_new, | ||
192 | generate_derive::generate_derive, | 194 | generate_derive::generate_derive, |
193 | generate_enum_is_method::generate_enum_is_method, | 195 | generate_enum_is_method::generate_enum_is_method, |
194 | generate_enum_projection_method::generate_enum_as_method, | 196 | generate_enum_projection_method::generate_enum_as_method, |