aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions.rs56
-rw-r--r--crates/ide_completion/src/completions/pattern.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs5
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs13
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs69
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs6
-rw-r--r--crates/ide_completion/src/context.rs2
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 {
203fn complete_enum_variants( 203fn 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#"
751enum Foo { 752enum 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#"
798enum Foo { 801enum 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};
36use syntax::{ 36use 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};
41use text_edit::TextEdit; 41use 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
241fn 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)]
246mod tests { 253mod 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#"
752trait Foo {{
753 type Type;
754 fn function();
755 const CONST: i32 = 0;
756}}
757struct T;
758
759impl Foo for T {{
760 // Comment
761 #[bar]
762 {}
763}}
764"#,
765 hint
766 ),
767 &format!(
768 r#"
769trait Foo {{
770 type Type;
771 fn function();
772 const CONST: i32 = 0;
773}}
774struct T;
775
776impl 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) => {