aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/complete_trait_impl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/complete_trait_impl.rs')
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs293
1 files changed, 293 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
new file mode 100644
index 000000000..0175f5e55
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -0,0 +1,293 @@
1use crate::completion::{CompletionContext, Completions, CompletionItem, CompletionKind, CompletionItemKind};
2
3use ra_syntax::ast::{self, NameOwner, AstNode};
4
5use hir::{self, db::HirDatabase, Docs};
6
7
8pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
9 let impl_block = ctx.impl_block.as_ref();
10 let item_list = impl_block.and_then(|i| i.item_list());
11
12 if item_list.is_none()
13 || impl_block.is_none()
14 || ctx.function_syntax.is_some() {
15 return;
16 }
17
18 let item_list = item_list.unwrap();
19 let impl_block = impl_block.unwrap();
20
21 let target_trait = resolve_target_trait(ctx.db, &ctx.analyzer, &impl_block);
22 if target_trait.is_none() {
23 return;
24 }
25
26 let target_trait = target_trait.unwrap();
27
28 let trait_items = target_trait.items(ctx.db);
29 let missing_items = trait_items
30 .iter()
31 .filter(|i| {
32 match i {
33 hir::AssocItem::Function(f) => {
34 let f_name = f.name(ctx.db).to_string();
35
36 item_list
37 .impl_items()
38 .find(|impl_item| {
39 match impl_item {
40 ast::ImplItem::FnDef(impl_f) => {
41 if let Some(n) = impl_f.name() {
42 f_name == n.syntax().to_string()
43 } else {
44 false
45 }
46 },
47 _ => false
48 }
49 }).is_none()
50 },
51 hir::AssocItem::Const(c) => {
52 let c_name = c.name(ctx.db)
53 .map(|f| f.to_string());
54
55 if c_name.is_none() {
56 return false;
57 }
58
59 let c_name = c_name.unwrap();
60
61 item_list
62 .impl_items()
63 .find(|impl_item| {
64 match impl_item {
65 ast::ImplItem::ConstDef(c) => {
66 if let Some(n) = c.name() {
67 c_name == n.syntax().to_string()
68 } else {
69 false
70 }
71 },
72 _ => false
73 }
74 }).is_none()
75 },
76 hir::AssocItem::TypeAlias(t) => {
77 let t_name = t.name(ctx.db).to_string();
78
79 item_list
80 .impl_items()
81 .find(|impl_item| {
82 match impl_item {
83 ast::ImplItem::TypeAliasDef(t) => {
84 if let Some(n) = t.name() {
85 t_name == n.syntax().to_string()
86 } else {
87 false
88 }
89 },
90 _ => false
91 }
92 }).is_none()
93 }
94 }
95 });
96
97 for item in missing_items {
98 match item {
99 hir::AssocItem::Function(f) => add_function_impl(acc, ctx, f),
100 hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, t),
101 _ => {},
102 }
103 }
104}
105
106fn resolve_target_trait(
107 db: &impl HirDatabase,
108 analyzer: &hir::SourceAnalyzer,
109 impl_block: &ast::ImplBlock
110) -> Option<hir::Trait> {
111 let ast_path = impl_block
112 .target_trait()
113 .map(|it| it.syntax().clone())
114 .and_then(ast::PathType::cast)?
115 .path()?;
116
117 match analyzer.resolve_path(db, &ast_path) {
118 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => {
119 Some(def)
120 }
121 _ => None,
122 }
123}
124
125fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir::Function) {
126 use crate::display::FunctionSignature;
127
128 let display = FunctionSignature::from_hir(ctx.db, func.clone());
129
130 let func_name = func.name(ctx.db);
131
132 let label = if func.params(ctx.db).len() > 0 {
133 format!("fn {}(..)", func_name.to_string())
134 } else {
135 format!("fn {}()", func_name.to_string())
136 };
137
138 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label.clone())
139 .lookup_by(label)
140 .set_documentation(func.docs(ctx.db));
141
142 let completion_kind = if func.has_self_param(ctx.db) {
143 CompletionItemKind::Method
144 } else {
145 CompletionItemKind::Function
146 };
147
148 let snippet = {
149 let mut s = format!("{}", display);
150 s.push_str(" {}");
151 s
152 };
153
154 builder
155 .insert_text(snippet)
156 .kind(completion_kind)
157 .add_to(acc);
158}
159
160fn add_type_alias_impl(acc: &mut Completions, ctx: &CompletionContext, type_alias: &hir::TypeAlias) {
161 let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string());
162
163 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
164 .insert_text(snippet)
165 .kind(CompletionItemKind::TypeAlias)
166 .set_documentation(type_alias.docs(ctx.db))
167 .add_to(acc);
168}
169
170#[cfg(test)]
171mod tests {
172 use crate::completion::{do_completion, CompletionItem, CompletionKind};
173 use insta::assert_debug_snapshot;
174
175 fn complete(code: &str) -> Vec<CompletionItem> {
176 do_completion(code, CompletionKind::Magic)
177 }
178
179 #[test]
180 fn single_function() {
181 let completions = complete(
182 r"
183 trait Test {
184 fn foo();
185 }
186
187 struct T1;
188
189 impl Test for T1 {
190 <|>
191 }
192 ",
193 );
194 assert_debug_snapshot!(completions, @r###"
195 [
196 CompletionItem {
197 label: "fn foo()",
198 source_range: [138; 138),
199 delete: [138; 138),
200 insert: "fn foo() {}",
201 kind: Function,
202 },
203 ]
204 "###);
205 }
206
207 #[test]
208 fn hide_implemented_fn() {
209 let completions = complete(
210 r"
211 trait Test {
212 fn foo();
213 fn bar();
214 }
215
216 struct T1;
217
218 impl Test for T1 {
219 fn foo() {}
220
221 <|>
222 }
223 ",
224 );
225 assert_debug_snapshot!(completions, @r###"
226 [
227 CompletionItem {
228 label: "fn bar()",
229 source_range: [193; 193),
230 delete: [193; 193),
231 insert: "fn bar() {}",
232 kind: Function,
233 },
234 ]
235 "###);
236 }
237
238 #[test]
239 fn generic_fn() {
240 let completions = complete(
241 r"
242 trait Test {
243 fn foo<T>();
244 }
245
246 struct T1;
247
248 impl Test for T1 {
249 <|>
250 }
251 ",
252 );
253 assert_debug_snapshot!(completions, @r###"
254 [
255 CompletionItem {
256 label: "fn foo()",
257 source_range: [141; 141),
258 delete: [141; 141),
259 insert: "fn foo<T>() {}",
260 kind: Function,
261 },
262 ]
263 "###);
264 }
265
266 #[test]
267 fn generic_constrait_fn() {
268 let completions = complete(
269 r"
270 trait Test {
271 fn foo<T>() where T: Into<String>;
272 }
273
274 struct T1;
275
276 impl Test for T1 {
277 <|>
278 }
279 ",
280 );
281 assert_debug_snapshot!(completions, @r###"
282 [
283 CompletionItem {
284 label: "fn foo()",
285 source_range: [163; 163),
286 delete: [163; 163),
287 insert: "fn foo<T>()\nwhere T: Into<String> {}",
288 kind: Function,
289 },
290 ]
291 "###);
292 }
293} \ No newline at end of file