aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/completion/complete_trait_impl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/completion/complete_trait_impl.rs')
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs488
1 files changed, 488 insertions, 0 deletions
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
new file mode 100644
index 000000000..1a2b1e8a5
--- /dev/null
+++ b/crates/ide/src/completion/complete_trait_impl.rs
@@ -0,0 +1,488 @@
1//! Completion for associated items in a trait implementation.
2//!
3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6//! and an direct child of an `IMPL`.
7//!
8//! # Examples
9//!
10//! Considering the following trait `impl`:
11//!
12//! ```ignore
13//! trait SomeTrait {
14//! fn foo();
15//! }
16//!
17//! impl SomeTrait for () {
18//! fn f<|>
19//! }
20//! ```
21//!
22//! may result in the completion of the following method:
23//!
24//! ```ignore
25//! # trait SomeTrait {
26//! # fn foo();
27//! # }
28//!
29//! impl SomeTrait for () {
30//! fn foo() {}<|>
31//! }
32//! ```
33
34use assists::utils::get_missing_assoc_items;
35use hir::{self, Docs, HasSource};
36use syntax::{
37 ast::{self, edit, Impl},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39};
40use text_edit::TextEdit;
41
42use crate::{
43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 },
46 display::function_declaration,
47};
48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() {
52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53 .into_iter()
54 .for_each(|item| match item {
55 hir::AssocItem::Function(fn_item) => {
56 add_function_impl(&trigger, acc, ctx, fn_item)
57 }
58 hir::AssocItem::TypeAlias(type_item) => {
59 add_type_alias_impl(&trigger, acc, ctx, type_item)
60 }
61 hir::AssocItem::Const(const_item) => {
62 add_const_impl(&trigger, acc, ctx, const_item)
63 }
64 }),
65
66 SyntaxKind::FN => {
67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 .into_iter()
69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None,
72 })
73 {
74 add_function_impl(&trigger, acc, ctx, missing_fn);
75 }
76 }
77
78 SyntaxKind::TYPE_ALIAS => {
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
80 .into_iter()
81 .filter_map(|item| match item {
82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83 _ => None,
84 })
85 {
86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
87 }
88 }
89
90 SyntaxKind::CONST => {
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
92 .into_iter()
93 .filter_map(|item| match item {
94 hir::AssocItem::Const(const_item) => Some(const_item),
95 _ => None,
96 })
97 {
98 add_const_impl(&trigger, acc, ctx, missing_fn);
99 }
100 }
101
102 _ => {}
103 }
104 }
105}
106
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => {
110 Some((p, 2))
111 }
112 SyntaxKind::NAME_REF => Some((p, 5)),
113 _ => None,
114 })?;
115 let impl_def = (0..impl_def_offset - 1)
116 .try_fold(trigger.parent()?, |t, _| t.parent())
117 .and_then(ast::Impl::cast)?;
118 Some((trigger, impl_def))
119}
120
121fn add_function_impl(
122 fn_def_node: &SyntaxNode,
123 acc: &mut Completions,
124 ctx: &CompletionContext,
125 func: hir::Function,
126) {
127 let fn_name = func.name(ctx.db).to_string();
128
129 let label = if func.params(ctx.db).is_empty() {
130 format!("fn {}()", fn_name)
131 } else {
132 format!("fn {}(..)", fn_name)
133 };
134
135 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
136 .lookup_by(fn_name)
137 .set_documentation(func.docs(ctx.db));
138
139 let completion_kind = if func.self_param(ctx.db).is_some() {
140 CompletionItemKind::Method
141 } else {
142 CompletionItemKind::Function
143 };
144 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
145
146 let function_decl = function_declaration(&func.source(ctx.db).value);
147 match ctx.config.snippet_cap {
148 Some(cap) => {
149 let snippet = format!("{} {{\n $0\n}}", function_decl);
150 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
151 }
152 None => {
153 let header = format!("{} {{", function_decl);
154 builder.text_edit(TextEdit::replace(range, header))
155 }
156 }
157 .kind(completion_kind)
158 .add_to(acc);
159}
160
161fn add_type_alias_impl(
162 type_def_node: &SyntaxNode,
163 acc: &mut Completions,
164 ctx: &CompletionContext,
165 type_alias: hir::TypeAlias,
166) {
167 let alias_name = type_alias.name(ctx.db).to_string();
168
169 let snippet = format!("type {} = ", alias_name);
170
171 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
172
173 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
174 .text_edit(TextEdit::replace(range, snippet))
175 .lookup_by(alias_name)
176 .kind(CompletionItemKind::TypeAlias)
177 .set_documentation(type_alias.docs(ctx.db))
178 .add_to(acc);
179}
180
181fn add_const_impl(
182 const_def_node: &SyntaxNode,
183 acc: &mut Completions,
184 ctx: &CompletionContext,
185 const_: hir::Const,
186) {
187 let const_name = const_.name(ctx.db).map(|n| n.to_string());
188
189 if let Some(const_name) = const_name {
190 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
191
192 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
193
194 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
195 .text_edit(TextEdit::replace(range, snippet))
196 .lookup_by(const_name)
197 .kind(CompletionItemKind::Const)
198 .set_documentation(const_.docs(ctx.db))
199 .add_to(acc);
200 }
201}
202
203fn make_const_compl_syntax(const_: &ast::Const) -> String {
204 let const_ = edit::remove_attrs_and_docs(const_);
205
206 let const_start = const_.syntax().text_range().start();
207 let const_end = const_.syntax().text_range().end();
208
209 let start =
210 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
211
212 let end = const_
213 .syntax()
214 .children_with_tokens()
215 .find(|s| s.kind() == T![;] || s.kind() == T![=])
216 .map_or(const_end, |f| f.text_range().start());
217
218 let len = end - start;
219 let range = TextRange::new(0.into(), len);
220
221 let syntax = const_.syntax().text().slice(range).to_string();
222
223 format!("{} = ", syntax.trim_end())
224}
225
226#[cfg(test)]
227mod tests {
228 use expect_test::{expect, Expect};
229
230 use crate::completion::{
231 test_utils::{check_edit, completion_list},
232 CompletionKind,
233 };
234
235 fn check(ra_fixture: &str, expect: Expect) {
236 let actual = completion_list(ra_fixture, CompletionKind::Magic);
237 expect.assert_eq(&actual)
238 }
239
240 #[test]
241 fn name_ref_function_type_const() {
242 check(
243 r#"
244trait Test {
245 type TestType;
246 const TEST_CONST: u16;
247 fn test();
248}
249struct T;
250
251impl Test for T {
252 t<|>
253}
254"#,
255 expect![["
256ct const TEST_CONST: u16 = \n\
257fn fn test()
258ta type TestType = \n\
259 "]],
260 );
261 }
262
263 #[test]
264 fn no_nested_fn_completions() {
265 check(
266 r"
267trait Test {
268 fn test();
269 fn test2();
270}
271struct T;
272
273impl Test for T {
274 fn test() {
275 t<|>
276 }
277}
278",
279 expect![[""]],
280 );
281 }
282
283 #[test]
284 fn name_ref_single_function() {
285 check_edit(
286 "test",
287 r#"
288trait Test {
289 fn test();
290}
291struct T;
292
293impl Test for T {
294 t<|>
295}
296"#,
297 r#"
298trait Test {
299 fn test();
300}
301struct T;
302
303impl Test for T {
304 fn test() {
305 $0
306}
307}
308"#,
309 );
310 }
311
312 #[test]
313 fn single_function() {
314 check_edit(
315 "test",
316 r#"
317trait Test {
318 fn test();
319}
320struct T;
321
322impl Test for T {
323 fn t<|>
324}
325"#,
326 r#"
327trait Test {
328 fn test();
329}
330struct T;
331
332impl Test for T {
333 fn test() {
334 $0
335}
336}
337"#,
338 );
339 }
340
341 #[test]
342 fn hide_implemented_fn() {
343 check(
344 r#"
345trait Test {
346 fn foo();
347 fn foo_bar();
348}
349struct T;
350
351impl Test for T {
352 fn foo() {}
353 fn f<|>
354}
355"#,
356 expect![[r#"
357 fn fn foo_bar()
358 "#]],
359 );
360 }
361
362 #[test]
363 fn generic_fn() {
364 check_edit(
365 "foo",
366 r#"
367trait Test {
368 fn foo<T>();
369}
370struct T;
371
372impl Test for T {
373 fn f<|>
374}
375"#,
376 r#"
377trait Test {
378 fn foo<T>();
379}
380struct T;
381
382impl Test for T {
383 fn foo<T>() {
384 $0
385}
386}
387"#,
388 );
389 check_edit(
390 "foo",
391 r#"
392trait Test {
393 fn foo<T>() where T: Into<String>;
394}
395struct T;
396
397impl Test for T {
398 fn f<|>
399}
400"#,
401 r#"
402trait Test {
403 fn foo<T>() where T: Into<String>;
404}
405struct T;
406
407impl Test for T {
408 fn foo<T>()
409where T: Into<String> {
410 $0
411}
412}
413"#,
414 );
415 }
416
417 #[test]
418 fn associated_type() {
419 check_edit(
420 "SomeType",
421 r#"
422trait Test {
423 type SomeType;
424}
425
426impl Test for () {
427 type S<|>
428}
429"#,
430 "
431trait Test {
432 type SomeType;
433}
434
435impl Test for () {
436 type SomeType = \n\
437}
438",
439 );
440 }
441
442 #[test]
443 fn associated_const() {
444 check_edit(
445 "SOME_CONST",
446 r#"
447trait Test {
448 const SOME_CONST: u16;
449}
450
451impl Test for () {
452 const S<|>
453}
454"#,
455 "
456trait Test {
457 const SOME_CONST: u16;
458}
459
460impl Test for () {
461 const SOME_CONST: u16 = \n\
462}
463",
464 );
465
466 check_edit(
467 "SOME_CONST",
468 r#"
469trait Test {
470 const SOME_CONST: u16 = 92;
471}
472
473impl Test for () {
474 const S<|>
475}
476"#,
477 "
478trait Test {
479 const SOME_CONST: u16 = 92;
480}
481
482impl Test for () {
483 const SOME_CONST: u16 = \n\
484}
485",
486 );
487 }
488}