aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers
diff options
context:
space:
mode:
authorChetan Khilosiya <[email protected]>2021-03-11 18:55:22 +0000
committerChetan Khilosiya <[email protected]>2021-03-15 16:01:52 +0000
commit2bf3802f21de48b1c284cdd5e0e9f7f168787d9b (patch)
tree3469e855a5df8e98d565955ab35d41d21315029b /crates/ide_assists/src/handlers
parenteec64ec01b0553aca855df8146965ed6c6746e7d (diff)
7709: Added the assist to generate is_empty function
the assist will be shown when the len function is implemented. is_empty internally uses len function.
Diffstat (limited to 'crates/ide_assists/src/handlers')
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
new file mode 100644
index 000000000..bd29dddb3
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -0,0 +1,225 @@
1use hir::{AssocItem, HasSource, Impl};
2use syntax::{
3 ast::{self, NameOwner},
4 AstNode, TextRange,
5};
6
7use crate::{
8 assist_context::{AssistContext, Assists},
9 AssistId, AssistKind,
10};
11
12// Assist: generate_is_empty_from_len
13//
14// Generates is_empty implementation from the len method.
15//
16// ```
17// impl MyStruct {
18// p$0ub fn len(&self) -> usize {
19// self.data.len()
20// }
21// }
22// ```
23// ->
24// ```
25// impl MyStruct {
26// pub fn len(&self) -> usize {
27// self.data.len()
28// }
29//
30// pub fn is_empty(&self) -> bool {
31// self.len() == 0
32// }
33// }
34// ```
35pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
37 let fn_name = fn_node.name()?;
38
39 if fn_name.text() != "len" {
40 cov_mark::hit!(len_function_not_present);
41 return None;
42 }
43
44 if fn_node.param_list()?.params().next().is_some() {
45 cov_mark::hit!(len_function_with_parameters);
46 return None;
47 }
48
49 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
50 let impl_def = ctx.sema.to_def(&impl_)?;
51 if is_empty_implemented(ctx, &impl_def) {
52 cov_mark::hit!(is_empty_already_implemented);
53 return None;
54 }
55
56 let range = get_text_range_of_len_function(ctx, &impl_def)?;
57
58 acc.add(
59 AssistId("generate_is_empty_from_len", AssistKind::Generate),
60 "Generate a is_empty impl from a len function",
61 range,
62 |builder| {
63 let code = get_is_empty_code();
64 builder.insert(range.end(), code)
65 },
66 )
67}
68
69fn get_function_from_impl(ctx: &AssistContext, impl_def: &Impl, name: &str) -> Option<AssocItem> {
70 let db = ctx.sema.db;
71 impl_def.items(db).into_iter().filter(|item| matches!(item, AssocItem::Function(_value))).find(
72 |func| match func.name(db) {
73 Some(fn_name) => fn_name.to_string() == name,
74 None => false,
75 },
76 )
77}
78
79fn is_empty_implemented(ctx: &AssistContext, impl_def: &Impl) -> bool {
80 get_function_from_impl(ctx, impl_def, "is_empty").is_some()
81}
82
83fn get_text_range_of_len_function(ctx: &AssistContext, impl_def: &Impl) -> Option<TextRange> {
84 let db = ctx.sema.db;
85 let len_fn = get_function_from_impl(ctx, impl_def, "len")?;
86
87 let mut range = None;
88 if let AssocItem::Function(node) = len_fn {
89 let node = node.source(db)?;
90 range = Some(node.syntax().value.text_range());
91 }
92
93 range
94}
95
96fn get_is_empty_code() -> String {
97 r#"
98
99 pub fn is_empty(&self) -> bool {
100 self.len() == 0
101 }"#
102 .to_string()
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::tests::{check_assist, check_assist_not_applicable};
108
109 use super::*;
110
111 #[test]
112 fn len_function_not_present() {
113 cov_mark::check!(len_function_not_present);
114 check_assist_not_applicable(
115 generate_is_empty_from_len,
116 r#"
117impl MyStruct {
118 p$0ub fn test(&self) -> usize {
119 self.data.len()
120 }
121 }
122"#,
123 );
124 }
125
126 #[test]
127 fn len_function_with_parameters() {
128 cov_mark::check!(len_function_with_parameters);
129 check_assist_not_applicable(
130 generate_is_empty_from_len,
131 r#"
132impl MyStruct {
133 p$0ub fn len(&self, _i: bool) -> usize {
134 self.data.len()
135 }
136}
137"#,
138 );
139 }
140
141 #[test]
142 fn is_empty_already_implemented() {
143 cov_mark::check!(is_empty_already_implemented);
144 check_assist_not_applicable(
145 generate_is_empty_from_len,
146 r#"
147impl MyStruct {
148 p$0ub fn len(&self) -> usize {
149 self.data.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
153 self.len() == 0
154 }
155}
156"#,
157 );
158 }
159
160 #[test]
161 fn generate_is_empty() {
162 check_assist(
163 generate_is_empty_from_len,
164 r#"
165impl MyStruct {
166 p$0ub fn len(&self) -> usize {
167 self.data.len()
168 }
169}
170"#,
171 r#"
172impl MyStruct {
173 pub fn len(&self) -> usize {
174 self.data.len()
175 }
176
177 pub fn is_empty(&self) -> bool {
178 self.len() == 0
179 }
180}
181"#,
182 );
183 }
184
185 #[test]
186 fn multiple_functions_in_impl() {
187 check_assist(
188 generate_is_empty_from_len,
189 r#"
190impl MyStruct {
191 pub fn new() -> Self {
192 Self { data: 0 }
193 }
194
195 p$0ub fn len(&self) -> usize {
196 self.data.len()
197 }
198
199 pub fn work(&self) -> Option<usize> {
200 // do some work
201 }
202}
203"#,
204 r#"
205impl MyStruct {
206 pub fn new() -> Self {
207 Self { data: 0 }
208 }
209
210 pub fn len(&self) -> usize {
211 self.data.len()
212 }
213
214 pub fn is_empty(&self) -> bool {
215 self.len() == 0
216 }
217
218 pub fn work(&self) -> Option<usize> {
219 // do some work
220 }
221}
222"#,
223 );
224 }
225}