diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions.rs | 56 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix.rs | 5 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 13 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 69 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 2 |
7 files changed, 105 insertions, 52 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index e2994eed4..78154bf3e 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -203,41 +203,37 @@ impl Completions { | |||
203 | fn complete_enum_variants( | 203 | fn complete_enum_variants( |
204 | acc: &mut Completions, | 204 | acc: &mut Completions, |
205 | ctx: &CompletionContext, | 205 | ctx: &CompletionContext, |
206 | ty: &hir::Type, | 206 | enum_data: hir::Enum, |
207 | cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), | 207 | cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), |
208 | ) { | 208 | ) { |
209 | if let Some(hir::Adt::Enum(enum_data)) = | 209 | let variants = enum_data.variants(ctx.db); |
210 | iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) | 210 | |
211 | { | 211 | let module = if let Some(module) = ctx.scope.module() { |
212 | let variants = enum_data.variants(ctx.db); | 212 | // Compute path from the completion site if available. |
213 | 213 | module | |
214 | let module = if let Some(module) = ctx.scope.module() { | 214 | } else { |
215 | // Compute path from the completion site if available. | 215 | // Otherwise fall back to the enum's definition site. |
216 | module | 216 | enum_data.module(ctx.db) |
217 | } else { | 217 | }; |
218 | // Otherwise fall back to the enum's definition site. | 218 | |
219 | enum_data.module(ctx.db) | 219 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { |
220 | }; | 220 | if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_data)) { |
221 | 221 | for &variant in &variants { | |
222 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { | 222 | let self_path = hir::ModPath::from_segments( |
223 | if impl_.self_ty(ctx.db) == *ty { | 223 | hir::PathKind::Plain, |
224 | for &variant in &variants { | 224 | iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), |
225 | let self_path = hir::ModPath::from_segments( | 225 | ); |
226 | hir::PathKind::Plain, | 226 | cb(acc, ctx, variant, self_path); |
227 | iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), | ||
228 | ); | ||
229 | cb(acc, ctx, variant, self_path); | ||
230 | } | ||
231 | } | 227 | } |
232 | } | 228 | } |
229 | } | ||
233 | 230 | ||
234 | for variant in variants { | 231 | for variant in variants { |
235 | if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { | 232 | if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { |
236 | // Variants with trivial paths are already added by the existing completion logic, | 233 | // Variants with trivial paths are already added by the existing completion logic, |
237 | // so we should avoid adding these twice | 234 | // so we should avoid adding these twice |
238 | if path.segments().len() > 1 { | 235 | if path.segments().len() > 1 { |
239 | cb(acc, ctx, variant, path); | 236 | cb(acc, ctx, variant, path); |
240 | } | ||
241 | } | 237 | } |
242 | } | 238 | } |
243 | } | 239 | } |
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 808d7ff7e..8dc9ab73c 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -12,8 +12,10 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
12 | } | 12 | } |
13 | 13 | ||
14 | if !ctx.is_irrefutable_pat_binding { | 14 | if !ctx.is_irrefutable_pat_binding { |
15 | if let Some(ty) = ctx.expected_type.as_ref() { | 15 | if let Some(hir::Adt::Enum(e)) = |
16 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { | 16 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
17 | { | ||
18 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { | ||
17 | acc.add_qualified_variant_pat(ctx, variant, path.clone()); | 19 | acc.add_qualified_variant_pat(ctx, variant, path.clone()); |
18 | acc.add_qualified_enum_variant(ctx, variant, path); | 20 | acc.add_qualified_enum_variant(ctx, variant, path); |
19 | }); | 21 | }); |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index ac69b720a..962aaf0df 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -35,14 +35,11 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
35 | None => return, | 35 | None => return, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | let ref_removed_ty = | ||
39 | std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap(); | ||
40 | |||
41 | let cap = match ctx.config.snippet_cap { | 38 | let cap = match ctx.config.snippet_cap { |
42 | Some(it) => it, | 39 | Some(it) => it, |
43 | None => return, | 40 | None => return, |
44 | }; | 41 | }; |
45 | let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty); | 42 | let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); |
46 | if let Some(try_enum) = &try_enum { | 43 | if let Some(try_enum) = &try_enum { |
47 | match try_enum { | 44 | match try_enum { |
48 | TryEnum::Result => { | 45 | TryEnum::Result => { |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index d2ebba65f..eedb44873 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -59,6 +59,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
59 | hir::ModuleDef::TypeAlias(a) => { | 59 | hir::ModuleDef::TypeAlias(a) => { |
60 | let ty = a.ty(ctx.db); | 60 | let ty = a.ty(ctx.db); |
61 | if let Some(Adt::Enum(e)) = ty.as_adt() { | 61 | if let Some(Adt::Enum(e)) = ty.as_adt() { |
62 | cov_mark::hit!(completes_variant_through_alias); | ||
62 | add_enum_variants(ctx, acc, e); | 63 | add_enum_variants(ctx, acc, e); |
63 | } | 64 | } |
64 | ty | 65 | ty |
@@ -68,6 +69,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
68 | Some(it) => it, | 69 | Some(it) => it, |
69 | None => return, | 70 | None => return, |
70 | }; | 71 | }; |
72 | cov_mark::hit!(completes_primitive_assoc_const); | ||
71 | builtin.ty(ctx.db, module) | 73 | builtin.ty(ctx.db, module) |
72 | } | 74 | } |
73 | _ => unreachable!(), | 75 | _ => unreachable!(), |
@@ -96,9 +98,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
96 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 98 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
97 | return None; | 99 | return None; |
98 | } | 100 | } |
99 | match item { | 101 | if let hir::AssocItem::TypeAlias(ty) = item { |
100 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | 102 | acc.add_type_alias(ctx, ty) |
101 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
102 | } | 103 | } |
103 | None::<()> | 104 | None::<()> |
104 | }); | 105 | }); |
@@ -745,7 +746,7 @@ fn f() {} | |||
745 | } | 746 | } |
746 | 747 | ||
747 | #[test] | 748 | #[test] |
748 | fn completes_self_enum() { | 749 | fn completes_variant_through_self() { |
749 | check( | 750 | check( |
750 | r#" | 751 | r#" |
751 | enum Foo { | 752 | enum Foo { |
@@ -769,6 +770,7 @@ impl Foo { | |||
769 | 770 | ||
770 | #[test] | 771 | #[test] |
771 | fn completes_primitive_assoc_const() { | 772 | fn completes_primitive_assoc_const() { |
773 | cov_mark::check!(completes_primitive_assoc_const); | ||
772 | check( | 774 | check( |
773 | r#" | 775 | r#" |
774 | //- /lib.rs crate:lib deps:core | 776 | //- /lib.rs crate:lib deps:core |
@@ -792,7 +794,8 @@ impl u8 { | |||
792 | } | 794 | } |
793 | 795 | ||
794 | #[test] | 796 | #[test] |
795 | fn completes_through_alias() { | 797 | fn completes_variant_through_alias() { |
798 | cov_mark::check!(completes_variant_through_alias); | ||
796 | check( | 799 | check( |
797 | r#" | 800 | r#" |
798 | enum Foo { | 801 | enum Foo { |
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs index a26fe7c6c..968c0254d 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -36,7 +36,7 @@ use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | |||
36 | use syntax::{ | 36 | use syntax::{ |
37 | ast::{self, edit, Impl}, | 37 | ast::{self, edit, Impl}, |
38 | display::function_declaration, | 38 | display::function_declaration, |
39 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 39 | AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, |
40 | }; | 40 | }; |
41 | use text_edit::TextEdit; | 41 | use text_edit::TextEdit; |
42 | 42 | ||
@@ -154,8 +154,7 @@ fn add_function_impl( | |||
154 | } else { | 154 | } else { |
155 | CompletionItemKind::SymbolKind(SymbolKind::Function) | 155 | CompletionItemKind::SymbolKind(SymbolKind::Function) |
156 | }; | 156 | }; |
157 | let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); | 157 | let range = replacement_range(ctx, fn_def_node); |
158 | |||
159 | if let Some(src) = func.source(ctx.db) { | 158 | if let Some(src) = func.source(ctx.db) { |
160 | let function_decl = function_declaration(&src.value); | 159 | let function_decl = function_declaration(&src.value); |
161 | match ctx.config.snippet_cap { | 160 | match ctx.config.snippet_cap { |
@@ -183,8 +182,7 @@ fn add_type_alias_impl( | |||
183 | 182 | ||
184 | let snippet = format!("type {} = ", alias_name); | 183 | let snippet = format!("type {} = ", alias_name); |
185 | 184 | ||
186 | let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); | 185 | let range = replacement_range(ctx, type_def_node); |
187 | |||
188 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | 186 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
189 | item.text_edit(TextEdit::replace(range, snippet)) | 187 | item.text_edit(TextEdit::replace(range, snippet)) |
190 | .lookup_by(alias_name) | 188 | .lookup_by(alias_name) |
@@ -205,9 +203,7 @@ fn add_const_impl( | |||
205 | if let Some(source) = const_.source(ctx.db) { | 203 | if let Some(source) = const_.source(ctx.db) { |
206 | let snippet = make_const_compl_syntax(&source.value); | 204 | let snippet = make_const_compl_syntax(&source.value); |
207 | 205 | ||
208 | let range = | 206 | let range = replacement_range(ctx, const_def_node); |
209 | TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); | ||
210 | |||
211 | let mut item = | 207 | let mut item = |
212 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | 208 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
213 | item.text_edit(TextEdit::replace(range, snippet)) | 209 | item.text_edit(TextEdit::replace(range, snippet)) |
@@ -242,6 +238,17 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String { | |||
242 | format!("{} = ", syntax.trim_end()) | 238 | format!("{} = ", syntax.trim_end()) |
243 | } | 239 | } |
244 | 240 | ||
241 | fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange { | ||
242 | let first_child = item | ||
243 | .children_with_tokens() | ||
244 | .find(|child| { | ||
245 | !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR) | ||
246 | }) | ||
247 | .unwrap_or_else(|| SyntaxElement::Node(item.clone())); | ||
248 | |||
249 | TextRange::new(first_child.text_range().start(), ctx.source_range().end()) | ||
250 | } | ||
251 | |||
245 | #[cfg(test)] | 252 | #[cfg(test)] |
246 | mod tests { | 253 | mod tests { |
247 | use expect_test::{expect, Expect}; | 254 | use expect_test::{expect, Expect}; |
@@ -734,4 +741,50 @@ impl Test for T {{ | |||
734 | test("CONST", "const $0", "const CONST: u16 = ", next_sibling); | 741 | test("CONST", "const $0", "const CONST: u16 = ", next_sibling); |
735 | } | 742 | } |
736 | } | 743 | } |
744 | |||
745 | #[test] | ||
746 | fn snippet_does_not_overwrite_comment_or_attr() { | ||
747 | let test = |completion: &str, hint: &str, completed: &str| { | ||
748 | check_edit( | ||
749 | completion, | ||
750 | &format!( | ||
751 | r#" | ||
752 | trait Foo {{ | ||
753 | type Type; | ||
754 | fn function(); | ||
755 | const CONST: i32 = 0; | ||
756 | }} | ||
757 | struct T; | ||
758 | |||
759 | impl Foo for T {{ | ||
760 | // Comment | ||
761 | #[bar] | ||
762 | {} | ||
763 | }} | ||
764 | "#, | ||
765 | hint | ||
766 | ), | ||
767 | &format!( | ||
768 | r#" | ||
769 | trait Foo {{ | ||
770 | type Type; | ||
771 | fn function(); | ||
772 | const CONST: i32 = 0; | ||
773 | }} | ||
774 | struct T; | ||
775 | |||
776 | impl Foo for T {{ | ||
777 | // Comment | ||
778 | #[bar] | ||
779 | {} | ||
780 | }} | ||
781 | "#, | ||
782 | completed | ||
783 | ), | ||
784 | ) | ||
785 | }; | ||
786 | test("function", "fn f$0", "fn function() {\n $0\n}"); | ||
787 | test("Type", "type T$0", "type Type = "); | ||
788 | test("CONST", "const C$0", "const CONST: i32 = "); | ||
789 | } | ||
737 | } | 790 | } |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 1b8b063e7..7875500c1 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -17,8 +17,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
17 | return; | 17 | return; |
18 | } | 18 | } |
19 | 19 | ||
20 | if let Some(ty) = &ctx.expected_type { | 20 | if let Some(hir::Adt::Enum(e)) = |
21 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { | 21 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
22 | { | ||
23 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { | ||
22 | acc.add_qualified_enum_variant(ctx, variant, path) | 24 | acc.add_qualified_enum_variant(ctx, variant, path) |
23 | }); | 25 | }); |
24 | } | 26 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index f3fcb712c..62ef40818 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -381,7 +381,7 @@ impl<'a> CompletionContext<'a> { | |||
381 | let def = self.sema.to_def(&it); | 381 | let def = self.sema.to_def(&it); |
382 | (def.map(|def| def.ret_type(self.db)), None) | 382 | (def.map(|def| def.ret_type(self.db)), None) |
383 | }, | 383 | }, |
384 | ast::Stmt(it) => (None, None), | 384 | ast::Stmt(_it) => (None, None), |
385 | _ => { | 385 | _ => { |
386 | match node.parent() { | 386 | match node.parent() { |
387 | Some(n) => { | 387 | Some(n) => { |