aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/completion.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs293
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs9
3 files changed, 305 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index fedc02e14..4f24cd1f9 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -15,6 +15,7 @@ mod complete_path;
15mod complete_scope; 15mod complete_scope;
16mod complete_postfix; 16mod complete_postfix;
17mod complete_macro_in_item_position; 17mod complete_macro_in_item_position;
18mod complete_trait_impl;
18 19
19use ra_db::SourceDatabase; 20use ra_db::SourceDatabase;
20use ra_ide_db::RootDatabase; 21use ra_ide_db::RootDatabase;
@@ -74,5 +75,7 @@ pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<C
74 complete_pattern::complete_pattern(&mut acc, &ctx); 75 complete_pattern::complete_pattern(&mut acc, &ctx);
75 complete_postfix::complete_postfix(&mut acc, &ctx); 76 complete_postfix::complete_postfix(&mut acc, &ctx);
76 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 77 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
78 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
79
77 Some(acc) 80 Some(acc)
78} 81}
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
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 5a0407fd7..8678a3234 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -25,6 +25,7 @@ pub(crate) struct CompletionContext<'a> {
25 pub(super) use_item_syntax: Option<ast::UseItem>, 25 pub(super) use_item_syntax: Option<ast::UseItem>,
26 pub(super) record_lit_syntax: Option<ast::RecordLit>, 26 pub(super) record_lit_syntax: Option<ast::RecordLit>,
27 pub(super) record_lit_pat: Option<ast::RecordPat>, 27 pub(super) record_lit_pat: Option<ast::RecordPat>,
28 pub(super) impl_block: Option<ast::ImplBlock>,
28 pub(super) is_param: bool, 29 pub(super) is_param: bool,
29 /// If a name-binding or reference to a const in a pattern. 30 /// If a name-binding or reference to a const in a pattern.
30 /// Irrefutable patterns (like let) are excluded. 31 /// Irrefutable patterns (like let) are excluded.
@@ -72,6 +73,7 @@ impl<'a> CompletionContext<'a> {
72 use_item_syntax: None, 73 use_item_syntax: None,
73 record_lit_syntax: None, 74 record_lit_syntax: None,
74 record_lit_pat: None, 75 record_lit_pat: None,
76 impl_block: None,
75 is_param: false, 77 is_param: false,
76 is_pat_binding: false, 78 is_pat_binding: false,
77 is_trivial_path: false, 79 is_trivial_path: false,
@@ -148,6 +150,13 @@ impl<'a> CompletionContext<'a> {
148 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); 150 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
149 } 151 }
150 152
153 self.impl_block = self
154 .token
155 .parent()
156 .ancestors()
157 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
158 .find_map(ast::ImplBlock::cast);
159
151 let top_node = name_ref 160 let top_node = name_ref
152 .syntax() 161 .syntax()
153 .ancestors() 162 .ancestors()