diff options
Diffstat (limited to 'crates/completion/src')
-rw-r--r-- | crates/completion/src/completions.rs | 8 | ||||
-rw-r--r-- | crates/completion/src/completions/keyword.rs | 46 | ||||
-rw-r--r-- | crates/completion/src/completions/postfix.rs | 12 | ||||
-rw-r--r-- | crates/completion/src/completions/record.rs | 108 | ||||
-rw-r--r-- | crates/completion/src/completions/unqualified_path.rs | 132 | ||||
-rw-r--r-- | crates/completion/src/config.rs | 6 | ||||
-rw-r--r-- | crates/completion/src/item.rs | 59 | ||||
-rw-r--r-- | crates/completion/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/completion/src/render.rs | 57 | ||||
-rw-r--r-- | crates/completion/src/render/enum_variant.rs | 10 | ||||
-rw-r--r-- | crates/completion/src/render/function.rs | 12 | ||||
-rw-r--r-- | crates/completion/src/render/macro_.rs | 12 |
12 files changed, 437 insertions, 32 deletions
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 75dbb1a23..9b7d6c580 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -90,7 +90,7 @@ impl Completions { | |||
90 | Some(it) => it, | 90 | Some(it) => it, |
91 | None => return, | 91 | None => return, |
92 | }; | 92 | }; |
93 | if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) { | 93 | if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { |
94 | self.add(item); | 94 | self.add(item); |
95 | } | 95 | } |
96 | } | 96 | } |
@@ -101,7 +101,7 @@ impl Completions { | |||
101 | func: hir::Function, | 101 | func: hir::Function, |
102 | local_name: Option<String>, | 102 | local_name: Option<String>, |
103 | ) { | 103 | ) { |
104 | let item = render_fn(RenderContext::new(ctx), local_name, func); | 104 | let item = render_fn(RenderContext::new(ctx), None, local_name, func); |
105 | self.add(item) | 105 | self.add(item) |
106 | } | 106 | } |
107 | 107 | ||
@@ -123,7 +123,7 @@ impl Completions { | |||
123 | variant: hir::EnumVariant, | 123 | variant: hir::EnumVariant, |
124 | path: ModPath, | 124 | path: ModPath, |
125 | ) { | 125 | ) { |
126 | let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path)); | 126 | let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path)); |
127 | self.add(item); | 127 | self.add(item); |
128 | } | 128 | } |
129 | 129 | ||
@@ -133,7 +133,7 @@ impl Completions { | |||
133 | variant: hir::EnumVariant, | 133 | variant: hir::EnumVariant, |
134 | local_name: Option<String>, | 134 | local_name: Option<String>, |
135 | ) { | 135 | ) { |
136 | let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None); | 136 | let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None); |
137 | self.add(item); | 137 | self.add(item); |
138 | } | 138 | } |
139 | } | 139 | } |
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs index c7df15900..720349b9d 100644 --- a/crates/completion/src/completions/keyword.rs +++ b/crates/completion/src/completions/keyword.rs | |||
@@ -44,6 +44,10 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
44 | mark::hit!(no_keyword_completion_in_comments); | 44 | mark::hit!(no_keyword_completion_in_comments); |
45 | return; | 45 | return; |
46 | } | 46 | } |
47 | if ctx.record_lit_syntax.is_some() { | ||
48 | mark::hit!(no_keyword_completion_in_record_lit); | ||
49 | return; | ||
50 | } | ||
47 | 51 | ||
48 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; | 52 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; |
49 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { | 53 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { |
@@ -563,4 +567,46 @@ struct Foo { | |||
563 | "#]], | 567 | "#]], |
564 | ) | 568 | ) |
565 | } | 569 | } |
570 | |||
571 | #[test] | ||
572 | fn skip_struct_initializer() { | ||
573 | mark::check!(no_keyword_completion_in_record_lit); | ||
574 | check( | ||
575 | r#" | ||
576 | struct Foo { | ||
577 | pub f: i32, | ||
578 | } | ||
579 | fn foo() { | ||
580 | Foo { | ||
581 | <|> | ||
582 | } | ||
583 | } | ||
584 | "#, | ||
585 | expect![[r#""#]], | ||
586 | ); | ||
587 | } | ||
588 | |||
589 | #[test] | ||
590 | fn struct_initializer_field_expr() { | ||
591 | check( | ||
592 | r#" | ||
593 | struct Foo { | ||
594 | pub f: i32, | ||
595 | } | ||
596 | fn foo() { | ||
597 | Foo { | ||
598 | f: <|> | ||
599 | } | ||
600 | } | ||
601 | "#, | ||
602 | expect![[r#" | ||
603 | kw if | ||
604 | kw if let | ||
605 | kw loop | ||
606 | kw match | ||
607 | kw return | ||
608 | kw while | ||
609 | "#]], | ||
610 | ); | ||
611 | } | ||
566 | } | 612 | } |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index 348f017bd..7fbda7a6b 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -184,6 +184,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
184 | ctx, | 184 | ctx, |
185 | cap, | 185 | cap, |
186 | &dot_receiver, | 186 | &dot_receiver, |
187 | "some", | ||
188 | "Some(expr)", | ||
189 | &format!("Some({})", receiver_text), | ||
190 | ) | ||
191 | .add_to(acc); | ||
192 | |||
193 | postfix_snippet( | ||
194 | ctx, | ||
195 | cap, | ||
196 | &dot_receiver, | ||
187 | "dbg", | 197 | "dbg", |
188 | "dbg!(expr)", | 198 | "dbg!(expr)", |
189 | &format!("dbg!({})", receiver_text), | 199 | &format!("dbg!({})", receiver_text), |
@@ -291,6 +301,7 @@ fn main() { | |||
291 | sn ok Ok(expr) | 301 | sn ok Ok(expr) |
292 | sn ref &expr | 302 | sn ref &expr |
293 | sn refm &mut expr | 303 | sn refm &mut expr |
304 | sn some Some(expr) | ||
294 | sn while while expr {} | 305 | sn while while expr {} |
295 | "#]], | 306 | "#]], |
296 | ); | 307 | ); |
@@ -314,6 +325,7 @@ fn main() { | |||
314 | sn ok Ok(expr) | 325 | sn ok Ok(expr) |
315 | sn ref &expr | 326 | sn ref &expr |
316 | sn refm &mut expr | 327 | sn refm &mut expr |
328 | sn some Some(expr) | ||
317 | "#]], | 329 | "#]], |
318 | ) | 330 | ) |
319 | } | 331 | } |
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 0f611084b..2049b9d09 100644 --- a/crates/completion/src/completions/record.rs +++ b/crates/completion/src/completions/record.rs | |||
@@ -1,16 +1,43 @@ | |||
1 | //! Complete fields in record literals and patterns. | 1 | //! Complete fields in record literals and patterns. |
2 | use crate::{CompletionContext, Completions}; | 2 | use assists::utils::FamousDefs; |
3 | use syntax::ast::Expr; | ||
4 | |||
5 | use crate::{ | ||
6 | item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions, | ||
7 | }; | ||
3 | 8 | ||
4 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 9 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
5 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { | 10 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { |
6 | (None, None) => return None, | 11 | (None, None) => return None, |
7 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | 12 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), |
8 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), | 13 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), |
9 | (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit), | 14 | (_, Some(record_lit)) => { |
15 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); | ||
16 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); | ||
17 | let impl_default_trait = default_trait | ||
18 | .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[]))) | ||
19 | .unwrap_or(false); | ||
20 | |||
21 | let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); | ||
22 | if impl_default_trait && !missing_fields.is_empty() { | ||
23 | acc.add( | ||
24 | CompletionItem::new( | ||
25 | CompletionKind::Snippet, | ||
26 | ctx.source_range(), | ||
27 | "..Default::default()", | ||
28 | ) | ||
29 | .insert_text("..Default::default()") | ||
30 | .kind(CompletionItemKind::Field) | ||
31 | .build(), | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | missing_fields | ||
36 | } | ||
10 | }; | 37 | }; |
11 | 38 | ||
12 | for (field, ty) in missing_fields { | 39 | for (field, ty) in missing_fields { |
13 | acc.add_field(ctx, field, &ty) | 40 | acc.add_field(ctx, field, &ty); |
14 | } | 41 | } |
15 | 42 | ||
16 | Some(()) | 43 | Some(()) |
@@ -18,6 +45,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
18 | 45 | ||
19 | #[cfg(test)] | 46 | #[cfg(test)] |
20 | mod tests { | 47 | mod tests { |
48 | use assists::utils::FamousDefs; | ||
21 | use expect_test::{expect, Expect}; | 49 | use expect_test::{expect, Expect}; |
22 | 50 | ||
23 | use crate::{test_utils::completion_list, CompletionKind}; | 51 | use crate::{test_utils::completion_list, CompletionKind}; |
@@ -27,6 +55,80 @@ mod tests { | |||
27 | expect.assert_eq(&actual); | 55 | expect.assert_eq(&actual); |
28 | } | 56 | } |
29 | 57 | ||
58 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
59 | let actual = completion_list( | ||
60 | &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE), | ||
61 | CompletionKind::Snippet, | ||
62 | ); | ||
63 | expect.assert_eq(&actual); | ||
64 | } | ||
65 | |||
66 | #[test] | ||
67 | fn test_record_literal_field_default() { | ||
68 | let test_code = r#" | ||
69 | struct S { foo: u32, bar: usize } | ||
70 | |||
71 | impl core::default::Default for S { | ||
72 | fn default() -> Self { | ||
73 | S { | ||
74 | foo: 0, | ||
75 | bar: 0, | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | fn process(f: S) { | ||
81 | let other = S { | ||
82 | foo: 5, | ||
83 | .<|> | ||
84 | }; | ||
85 | } | ||
86 | "#; | ||
87 | check( | ||
88 | test_code, | ||
89 | expect![[r#" | ||
90 | fd bar usize | ||
91 | "#]], | ||
92 | ); | ||
93 | |||
94 | check_snippet( | ||
95 | test_code, | ||
96 | expect![[r#" | ||
97 | fd ..Default::default() | ||
98 | sn pd | ||
99 | sn ppd | ||
100 | "#]], | ||
101 | ); | ||
102 | } | ||
103 | |||
104 | #[test] | ||
105 | fn test_record_literal_field_without_default() { | ||
106 | let test_code = r#" | ||
107 | struct S { foo: u32, bar: usize } | ||
108 | |||
109 | fn process(f: S) { | ||
110 | let other = S { | ||
111 | foo: 5, | ||
112 | .<|> | ||
113 | }; | ||
114 | } | ||
115 | "#; | ||
116 | check( | ||
117 | test_code, | ||
118 | expect![[r#" | ||
119 | fd bar usize | ||
120 | "#]], | ||
121 | ); | ||
122 | |||
123 | check_snippet( | ||
124 | test_code, | ||
125 | expect![[r#" | ||
126 | sn pd | ||
127 | sn ppd | ||
128 | "#]], | ||
129 | ); | ||
130 | } | ||
131 | |||
30 | #[test] | 132 | #[test] |
31 | fn test_record_pattern_field() { | 133 | fn test_record_pattern_field() { |
32 | check( | 134 | check( |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 7df58e1da..4f1c9faa0 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,10 +1,16 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use assists::utils::ImportScope; | ||
4 | use either::Either; | ||
3 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
6 | use ide_db::imports_locator; | ||
4 | use syntax::AstNode; | 7 | use syntax::AstNode; |
5 | use test_utils::mark; | 8 | use test_utils::mark; |
6 | 9 | ||
7 | use crate::{CompletionContext, Completions}; | 10 | use crate::{ |
11 | render::{render_resolution_with_import, RenderContext}, | ||
12 | CompletionContext, Completions, | ||
13 | }; | ||
8 | 14 | ||
9 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 15 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { | 16 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { |
@@ -37,6 +43,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
37 | } | 43 | } |
38 | acc.add_resolution(ctx, name.to_string(), &res) | 44 | acc.add_resolution(ctx, name.to_string(), &res) |
39 | }); | 45 | }); |
46 | |||
47 | if ctx.config.enable_experimental_completions { | ||
48 | fuzzy_completion(acc, ctx).unwrap_or_default() | ||
49 | } | ||
40 | } | 50 | } |
41 | 51 | ||
42 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | 52 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
@@ -63,6 +73,45 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
63 | } | 73 | } |
64 | } | 74 | } |
65 | 75 | ||
76 | fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
77 | let _p = profile::span("fuzzy_completion"); | ||
78 | let current_module = ctx.scope.module()?; | ||
79 | let anchor = ctx.name_ref_syntax.as_ref()?; | ||
80 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | ||
81 | |||
82 | let potential_import_name = ctx.token.to_string(); | ||
83 | |||
84 | let possible_imports = | ||
85 | imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400) | ||
86 | .filter_map(|import_candidate| match import_candidate { | ||
87 | // when completing outside the use declaration, modules are pretty useless | ||
88 | // and tend to bloat the completion suggestions a lot | ||
89 | Either::Left(ModuleDef::Module(_)) => None, | ||
90 | Either::Left(module_def) => Some(( | ||
91 | current_module.find_use_path(ctx.db, module_def)?, | ||
92 | ScopeDef::ModuleDef(module_def), | ||
93 | )), | ||
94 | Either::Right(macro_def) => Some(( | ||
95 | current_module.find_use_path(ctx.db, macro_def)?, | ||
96 | ScopeDef::MacroDef(macro_def), | ||
97 | )), | ||
98 | }) | ||
99 | .filter(|(mod_path, _)| mod_path.len() > 1) | ||
100 | .filter_map(|(import_path, definition)| { | ||
101 | render_resolution_with_import( | ||
102 | RenderContext::new(ctx), | ||
103 | import_path.clone(), | ||
104 | import_scope.clone(), | ||
105 | ctx.config.merge, | ||
106 | &definition, | ||
107 | ) | ||
108 | }) | ||
109 | .take(20); | ||
110 | |||
111 | acc.add_all(possible_imports); | ||
112 | Some(()) | ||
113 | } | ||
114 | |||
66 | #[cfg(test)] | 115 | #[cfg(test)] |
67 | mod tests { | 116 | mod tests { |
68 | use expect_test::{expect, Expect}; | 117 | use expect_test::{expect, Expect}; |
@@ -676,4 +725,85 @@ impl My<|> | |||
676 | "#]], | 725 | "#]], |
677 | ) | 726 | ) |
678 | } | 727 | } |
728 | |||
729 | #[test] | ||
730 | fn function_fuzzy_completion() { | ||
731 | check_edit( | ||
732 | "stdin", | ||
733 | r#" | ||
734 | //- /lib.rs crate:dep | ||
735 | pub mod io { | ||
736 | pub fn stdin() {} | ||
737 | }; | ||
738 | |||
739 | //- /main.rs crate:main deps:dep | ||
740 | fn main() { | ||
741 | stdi<|> | ||
742 | } | ||
743 | "#, | ||
744 | r#" | ||
745 | use dep::io::stdin; | ||
746 | |||
747 | fn main() { | ||
748 | stdin()$0 | ||
749 | } | ||
750 | "#, | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn macro_fuzzy_completion() { | ||
756 | check_edit( | ||
757 | "macro_with_curlies!", | ||
758 | r#" | ||
759 | //- /lib.rs crate:dep | ||
760 | /// Please call me as macro_with_curlies! {} | ||
761 | #[macro_export] | ||
762 | macro_rules! macro_with_curlies { | ||
763 | () => {} | ||
764 | } | ||
765 | |||
766 | //- /main.rs crate:main deps:dep | ||
767 | fn main() { | ||
768 | curli<|> | ||
769 | } | ||
770 | "#, | ||
771 | r#" | ||
772 | use dep::macro_with_curlies; | ||
773 | |||
774 | fn main() { | ||
775 | macro_with_curlies! {$0} | ||
776 | } | ||
777 | "#, | ||
778 | ); | ||
779 | } | ||
780 | |||
781 | #[test] | ||
782 | fn struct_fuzzy_completion() { | ||
783 | check_edit( | ||
784 | "ThirdStruct", | ||
785 | r#" | ||
786 | //- /lib.rs crate:dep | ||
787 | pub struct FirstStruct; | ||
788 | pub mod some_module { | ||
789 | pub struct SecondStruct; | ||
790 | pub struct ThirdStruct; | ||
791 | } | ||
792 | |||
793 | //- /main.rs crate:main deps:dep | ||
794 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
795 | |||
796 | fn main() { | ||
797 | this<|> | ||
798 | } | ||
799 | "#, | ||
800 | r#" | ||
801 | use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; | ||
802 | |||
803 | fn main() { | ||
804 | ThirdStruct | ||
805 | } | ||
806 | "#, | ||
807 | ); | ||
808 | } | ||
679 | } | 809 | } |
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 71b49ace8..f50735372 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs | |||
@@ -4,12 +4,16 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! completions if we are allowed to. | 5 | //! completions if we are allowed to. |
6 | 6 | ||
7 | use assists::utils::MergeBehaviour; | ||
8 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
8 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
9 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_experimental_completions: bool, | ||
10 | pub add_call_parenthesis: bool, | 13 | pub add_call_parenthesis: bool, |
11 | pub add_call_argument_snippets: bool, | 14 | pub add_call_argument_snippets: bool, |
12 | pub snippet_cap: Option<SnippetCap>, | 15 | pub snippet_cap: Option<SnippetCap>, |
16 | pub merge: Option<MergeBehaviour>, | ||
13 | } | 17 | } |
14 | 18 | ||
15 | impl CompletionConfig { | 19 | impl CompletionConfig { |
@@ -27,9 +31,11 @@ impl Default for CompletionConfig { | |||
27 | fn default() -> Self { | 31 | fn default() -> Self { |
28 | CompletionConfig { | 32 | CompletionConfig { |
29 | enable_postfix_completions: true, | 33 | enable_postfix_completions: true, |
34 | enable_experimental_completions: true, | ||
30 | add_call_parenthesis: true, | 35 | add_call_parenthesis: true, |
31 | add_call_argument_snippets: true, | 36 | add_call_argument_snippets: true, |
32 | snippet_cap: Some(SnippetCap { _private: () }), | 37 | snippet_cap: Some(SnippetCap { _private: () }), |
38 | merge: Some(MergeBehaviour::Full), | ||
33 | } | 39 | } |
34 | } | 40 | } |
35 | } | 41 | } |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 6d1d085f4..b13c3f376 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::{Documentation, Mutability}; | 5 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; |
6 | use syntax::TextRange; | 6 | use hir::{Documentation, ModPath, Mutability}; |
7 | use syntax::{algo, TextRange}; | ||
7 | use text_edit::TextEdit; | 8 | use text_edit::TextEdit; |
8 | 9 | ||
9 | use crate::config::SnippetCap; | 10 | use crate::config::SnippetCap; |
@@ -31,6 +32,7 @@ pub struct CompletionItem { | |||
31 | /// | 32 | /// |
32 | /// Typically, replaces `source_range` with new identifier. | 33 | /// Typically, replaces `source_range` with new identifier. |
33 | text_edit: TextEdit, | 34 | text_edit: TextEdit, |
35 | |||
34 | insert_text_format: InsertTextFormat, | 36 | insert_text_format: InsertTextFormat, |
35 | 37 | ||
36 | /// What item (struct, function, etc) are we completing. | 38 | /// What item (struct, function, etc) are we completing. |
@@ -199,8 +201,10 @@ impl CompletionItem { | |||
199 | trigger_call_info: None, | 201 | trigger_call_info: None, |
200 | score: None, | 202 | score: None, |
201 | ref_match: None, | 203 | ref_match: None, |
204 | import_data: None, | ||
202 | } | 205 | } |
203 | } | 206 | } |
207 | |||
204 | /// What user sees in pop-up in the UI. | 208 | /// What user sees in pop-up in the UI. |
205 | pub fn label(&self) -> &str { | 209 | pub fn label(&self) -> &str { |
206 | &self.label | 210 | &self.label |
@@ -257,6 +261,7 @@ impl CompletionItem { | |||
257 | pub(crate) struct Builder { | 261 | pub(crate) struct Builder { |
258 | source_range: TextRange, | 262 | source_range: TextRange, |
259 | completion_kind: CompletionKind, | 263 | completion_kind: CompletionKind, |
264 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
260 | label: String, | 265 | label: String, |
261 | insert_text: Option<String>, | 266 | insert_text: Option<String>, |
262 | insert_text_format: InsertTextFormat, | 267 | insert_text_format: InsertTextFormat, |
@@ -273,23 +278,50 @@ pub(crate) struct Builder { | |||
273 | 278 | ||
274 | impl Builder { | 279 | impl Builder { |
275 | pub(crate) fn build(self) -> CompletionItem { | 280 | pub(crate) fn build(self) -> CompletionItem { |
276 | let label = self.label; | 281 | let mut label = self.label; |
277 | let text_edit = match self.text_edit { | 282 | let mut lookup = self.lookup; |
283 | let mut insert_text = self.insert_text; | ||
284 | let mut text_edits = TextEdit::builder(); | ||
285 | |||
286 | if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { | ||
287 | let import = mod_path_to_ast(&import_path); | ||
288 | let mut import_path_without_last_segment = import_path; | ||
289 | let _ = import_path_without_last_segment.segments.pop(); | ||
290 | |||
291 | if !import_path_without_last_segment.segments.is_empty() { | ||
292 | if lookup.is_none() { | ||
293 | lookup = Some(label.clone()); | ||
294 | } | ||
295 | if insert_text.is_none() { | ||
296 | insert_text = Some(label.clone()); | ||
297 | } | ||
298 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
299 | } | ||
300 | |||
301 | let rewriter = insert_use(&import_scope, import, merge_behaviour); | ||
302 | if let Some(old_ast) = rewriter.rewrite_root() { | ||
303 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | let original_edit = match self.text_edit { | ||
278 | Some(it) => it, | 308 | Some(it) => it, |
279 | None => TextEdit::replace( | 309 | None => { |
280 | self.source_range, | 310 | TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) |
281 | self.insert_text.unwrap_or_else(|| label.clone()), | 311 | } |
282 | ), | ||
283 | }; | 312 | }; |
284 | 313 | ||
314 | let mut resulting_edit = text_edits.finish(); | ||
315 | resulting_edit.union(original_edit).expect("Failed to unite text edits"); | ||
316 | |||
285 | CompletionItem { | 317 | CompletionItem { |
286 | source_range: self.source_range, | 318 | source_range: self.source_range, |
287 | label, | 319 | label, |
288 | insert_text_format: self.insert_text_format, | 320 | insert_text_format: self.insert_text_format, |
289 | text_edit, | 321 | text_edit: resulting_edit, |
290 | detail: self.detail, | 322 | detail: self.detail, |
291 | documentation: self.documentation, | 323 | documentation: self.documentation, |
292 | lookup: self.lookup, | 324 | lookup, |
293 | kind: self.kind, | 325 | kind: self.kind, |
294 | completion_kind: self.completion_kind, | 326 | completion_kind: self.completion_kind, |
295 | deprecated: self.deprecated.unwrap_or(false), | 327 | deprecated: self.deprecated.unwrap_or(false), |
@@ -358,6 +390,13 @@ impl Builder { | |||
358 | self.trigger_call_info = Some(true); | 390 | self.trigger_call_info = Some(true); |
359 | self | 391 | self |
360 | } | 392 | } |
393 | pub(crate) fn import_data( | ||
394 | mut self, | ||
395 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
396 | ) -> Builder { | ||
397 | self.import_data = import_data; | ||
398 | self | ||
399 | } | ||
361 | pub(crate) fn set_ref_match( | 400 | pub(crate) fn set_ref_match( |
362 | mut self, | 401 | mut self, |
363 | ref_match: Option<(Mutability, CompletionScore)>, | 402 | ref_match: Option<(Mutability, CompletionScore)>, |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index cb6e0554e..aecc1378b 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -67,6 +67,13 @@ pub use crate::{ | |||
67 | // fn test_name() {} | 67 | // fn test_name() {} |
68 | // } | 68 | // } |
69 | // ``` | 69 | // ``` |
70 | // | ||
71 | // And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. | ||
72 | // This flag enables or disables: | ||
73 | // | ||
74 | // - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input | ||
75 | // | ||
76 | // Experimental completions might cause issues with performance and completion list look. | ||
70 | 77 | ||
71 | /// Main entry point for completion. We run completion as a two-phase process. | 78 | /// Main entry point for completion. We run completion as a two-phase process. |
72 | /// | 79 | /// |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 1fa02c375..e892d4de8 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -9,7 +9,8 @@ pub(crate) mod type_alias; | |||
9 | 9 | ||
10 | mod builder_ext; | 10 | mod builder_ext; |
11 | 11 | ||
12 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; | 12 | use assists::utils::{ImportScope, MergeBehaviour}; |
13 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; | ||
13 | use ide_db::RootDatabase; | 14 | use ide_db::RootDatabase; |
14 | use syntax::TextRange; | 15 | use syntax::TextRange; |
15 | use test_utils::mark; | 16 | use test_utils::mark; |
@@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>( | |||
42 | local_name: String, | 43 | local_name: String, |
43 | resolution: &ScopeDef, | 44 | resolution: &ScopeDef, |
44 | ) -> Option<CompletionItem> { | 45 | ) -> Option<CompletionItem> { |
45 | Render::new(ctx).render_resolution(local_name, resolution) | 46 | Render::new(ctx).render_resolution(local_name, None, resolution) |
47 | } | ||
48 | |||
49 | pub(crate) fn render_resolution_with_import<'a>( | ||
50 | ctx: RenderContext<'a>, | ||
51 | import: ModPath, | ||
52 | import_scope: ImportScope, | ||
53 | merge_behaviour: Option<MergeBehaviour>, | ||
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | ||
56 | let local_name = import.segments.last()?.to_string(); | ||
57 | Render::new(ctx).render_resolution( | ||
58 | local_name, | ||
59 | Some((import, import_scope, merge_behaviour)), | ||
60 | resolution, | ||
61 | ) | ||
46 | } | 62 | } |
47 | 63 | ||
48 | /// Interface for data and methods required for items rendering. | 64 | /// Interface for data and methods required for items rendering. |
@@ -131,6 +147,7 @@ impl<'a> Render<'a> { | |||
131 | fn render_resolution( | 147 | fn render_resolution( |
132 | self, | 148 | self, |
133 | local_name: String, | 149 | local_name: String, |
150 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
134 | resolution: &ScopeDef, | 151 | resolution: &ScopeDef, |
135 | ) -> Option<CompletionItem> { | 152 | ) -> Option<CompletionItem> { |
136 | use hir::ModuleDef::*; | 153 | use hir::ModuleDef::*; |
@@ -142,15 +159,15 @@ impl<'a> Render<'a> { | |||
142 | 159 | ||
143 | let kind = match resolution { | 160 | let kind = match resolution { |
144 | ScopeDef::ModuleDef(Function(func)) => { | 161 | ScopeDef::ModuleDef(Function(func)) => { |
145 | let item = render_fn(self.ctx, Some(local_name), *func); | 162 | let item = render_fn(self.ctx, import_data, Some(local_name), *func); |
146 | return Some(item); | 163 | return Some(item); |
147 | } | 164 | } |
148 | ScopeDef::ModuleDef(EnumVariant(var)) => { | 165 | ScopeDef::ModuleDef(EnumVariant(var)) => { |
149 | let item = render_enum_variant(self.ctx, Some(local_name), *var, None); | 166 | let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); |
150 | return Some(item); | 167 | return Some(item); |
151 | } | 168 | } |
152 | ScopeDef::MacroDef(mac) => { | 169 | ScopeDef::MacroDef(mac) => { |
153 | let item = render_macro(self.ctx, local_name, *mac); | 170 | let item = render_macro(self.ctx, import_data, local_name, *mac); |
154 | return item; | 171 | return item; |
155 | } | 172 | } |
156 | 173 | ||
@@ -175,6 +192,7 @@ impl<'a> Render<'a> { | |||
175 | local_name, | 192 | local_name, |
176 | ) | 193 | ) |
177 | .kind(CompletionItemKind::UnresolvedReference) | 194 | .kind(CompletionItemKind::UnresolvedReference) |
195 | .import_data(import_data) | ||
178 | .build(); | 196 | .build(); |
179 | return Some(item); | 197 | return Some(item); |
180 | } | 198 | } |
@@ -227,7 +245,12 @@ impl<'a> Render<'a> { | |||
227 | } | 245 | } |
228 | } | 246 | } |
229 | 247 | ||
230 | let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); | 248 | let item = item |
249 | .kind(kind) | ||
250 | .import_data(import_data) | ||
251 | .set_documentation(docs) | ||
252 | .set_ref_match(ref_match) | ||
253 | .build(); | ||
231 | Some(item) | 254 | Some(item) |
232 | } | 255 | } |
233 | 256 | ||
@@ -426,6 +449,28 @@ fn main() { let _: m::Spam = S<|> } | |||
426 | kind: Module, | 449 | kind: Module, |
427 | }, | 450 | }, |
428 | CompletionItem { | 451 | CompletionItem { |
452 | label: "m::Spam", | ||
453 | source_range: 75..76, | ||
454 | text_edit: TextEdit { | ||
455 | indels: [ | ||
456 | Indel { | ||
457 | insert: "use m::Spam;", | ||
458 | delete: 0..0, | ||
459 | }, | ||
460 | Indel { | ||
461 | insert: "\n\n", | ||
462 | delete: 0..0, | ||
463 | }, | ||
464 | Indel { | ||
465 | insert: "Spam", | ||
466 | delete: 75..76, | ||
467 | }, | ||
468 | ], | ||
469 | }, | ||
470 | kind: Enum, | ||
471 | lookup: "Spam", | ||
472 | }, | ||
473 | CompletionItem { | ||
429 | label: "m::Spam::Foo", | 474 | label: "m::Spam::Foo", |
430 | source_range: 75..76, | 475 | source_range: 75..76, |
431 | delete: 75..76, | 476 | delete: 75..76, |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index fd412ed0e..6070e9b1d 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! Renderer for `enum` variants. | 1 | //! Renderer for `enum` variants. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 4 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
4 | use itertools::Itertools; | 5 | use itertools::Itertools; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
@@ -11,11 +12,12 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_enum_variant<'a>( | 13 | pub(crate) fn render_enum_variant<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | local_name: Option<String>, | 16 | local_name: Option<String>, |
15 | variant: hir::EnumVariant, | 17 | variant: hir::EnumVariant, |
16 | path: Option<ModPath>, | 18 | path: Option<ModPath>, |
17 | ) -> CompletionItem { | 19 | ) -> CompletionItem { |
18 | EnumVariantRender::new(ctx, local_name, variant, path).render() | 20 | EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) |
19 | } | 21 | } |
20 | 22 | ||
21 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -60,7 +62,10 @@ impl<'a> EnumVariantRender<'a> { | |||
60 | } | 62 | } |
61 | } | 63 | } |
62 | 64 | ||
63 | fn render(self) -> CompletionItem { | 65 | fn render( |
66 | self, | ||
67 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
68 | ) -> CompletionItem { | ||
64 | let mut builder = CompletionItem::new( | 69 | let mut builder = CompletionItem::new( |
65 | CompletionKind::Reference, | 70 | CompletionKind::Reference, |
66 | self.ctx.source_range(), | 71 | self.ctx.source_range(), |
@@ -69,6 +74,7 @@ impl<'a> EnumVariantRender<'a> { | |||
69 | .kind(CompletionItemKind::EnumVariant) | 74 | .kind(CompletionItemKind::EnumVariant) |
70 | .set_documentation(self.variant.docs(self.ctx.db())) | 75 | .set_documentation(self.variant.docs(self.ctx.db())) |
71 | .set_deprecated(self.ctx.is_deprecated(self.variant)) | 76 | .set_deprecated(self.ctx.is_deprecated(self.variant)) |
77 | .import_data(import_data) | ||
72 | .detail(self.detail()); | 78 | .detail(self.detail()); |
73 | 79 | ||
74 | if self.variant_kind == StructKind::Tuple { | 80 | if self.variant_kind == StructKind::Tuple { |
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 4fa6eafd7..9dd5cd18c 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for function calls. | 1 | //! Renderer for function calls. |
2 | 2 | ||
3 | use hir::{HasSource, Type}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{HasSource, ModPath, Type}; | ||
4 | use syntax::{ast::Fn, display::function_declaration}; | 5 | use syntax::{ast::Fn, display::function_declaration}; |
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
@@ -10,10 +11,11 @@ use crate::{ | |||
10 | 11 | ||
11 | pub(crate) fn render_fn<'a>( | 12 | pub(crate) fn render_fn<'a>( |
12 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
14 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
13 | local_name: Option<String>, | 15 | local_name: Option<String>, |
14 | fn_: hir::Function, | 16 | fn_: hir::Function, |
15 | ) -> CompletionItem { | 17 | ) -> CompletionItem { |
16 | FunctionRender::new(ctx, local_name, fn_).render() | 18 | FunctionRender::new(ctx, local_name, fn_).render(import_data) |
17 | } | 19 | } |
18 | 20 | ||
19 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> FunctionRender<'a> { | |||
36 | FunctionRender { ctx, name, fn_, ast_node } | 38 | FunctionRender { ctx, name, fn_, ast_node } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(self) -> CompletionItem { | 41 | fn render( |
42 | self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> CompletionItem { | ||
40 | let params = self.params(); | 45 | let params = self.params(); |
41 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | 46 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) |
42 | .kind(self.kind()) | 47 | .kind(self.kind()) |
@@ -44,6 +49,7 @@ impl<'a> FunctionRender<'a> { | |||
44 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) | 49 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) |
45 | .detail(self.detail()) | 50 | .detail(self.detail()) |
46 | .add_call_parens(self.ctx.completion, self.name, params) | 51 | .add_call_parens(self.ctx.completion, self.name, params) |
52 | .import_data(import_data) | ||
47 | .build() | 53 | .build() |
48 | } | 54 | } |
49 | 55 | ||
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 96be59cc3..fead59e41 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for macro invocations. | 1 | //! Renderer for macro invocations. |
2 | 2 | ||
3 | use hir::{Documentation, HasSource}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{Documentation, HasSource, ModPath}; | ||
4 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
6 | 7 | ||
@@ -11,10 +12,11 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_macro<'a>( | 13 | pub(crate) fn render_macro<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | name: String, | 16 | name: String, |
15 | macro_: hir::MacroDef, | 17 | macro_: hir::MacroDef, |
16 | ) -> Option<CompletionItem> { | 18 | ) -> Option<CompletionItem> { |
17 | MacroRender::new(ctx, name, macro_).render() | 19 | MacroRender::new(ctx, name, macro_).render(import_data) |
18 | } | 20 | } |
19 | 21 | ||
20 | #[derive(Debug)] | 22 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> MacroRender<'a> { | |||
36 | MacroRender { ctx, name, macro_, docs, bra, ket } | 38 | MacroRender { ctx, name, macro_, docs, bra, ket } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(&self) -> Option<CompletionItem> { | 41 | fn render( |
42 | &self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> Option<CompletionItem> { | ||
40 | // FIXME: Currently proc-macro do not have ast-node, | 45 | // FIXME: Currently proc-macro do not have ast-node, |
41 | // such that it does not have source | 46 | // such that it does not have source |
42 | if self.macro_.is_proc_macro() { | 47 | if self.macro_.is_proc_macro() { |
@@ -48,6 +53,7 @@ impl<'a> MacroRender<'a> { | |||
48 | .kind(CompletionItemKind::Macro) | 53 | .kind(CompletionItemKind::Macro) |
49 | .set_documentation(self.docs.clone()) | 54 | .set_documentation(self.docs.clone()) |
50 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) | 55 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) |
56 | .import_data(import_data) | ||
51 | .detail(self.detail()); | 57 | .detail(self.detail()); |
52 | 58 | ||
53 | let needs_bang = self.needs_bang(); | 59 | let needs_bang = self.needs_bang(); |