aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions')
-rw-r--r--crates/completion/src/completions/keyword.rs46
-rw-r--r--crates/completion/src/completions/postfix.rs12
-rw-r--r--crates/completion/src/completions/record.rs108
-rw-r--r--crates/completion/src/completions/unqualified_path.rs132
4 files changed, 294 insertions, 4 deletions
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#"
576struct Foo {
577 pub f: i32,
578}
579fn 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#"
593struct Foo {
594 pub f: i32,
595}
596fn 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.
2use crate::{CompletionContext, Completions}; 2use assists::utils::FamousDefs;
3use syntax::ast::Expr;
4
5use crate::{
6 item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
7};
3 8
4pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 9pub(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)]
20mod tests { 47mod 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#"
69struct S { foo: u32, bar: usize }
70
71impl core::default::Default for S {
72 fn default() -> Self {
73 S {
74 foo: 0,
75 bar: 0,
76 }
77 }
78}
79
80fn 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#"
107struct S { foo: u32, bar: usize }
108
109fn 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
3use assists::utils::ImportScope;
4use either::Either;
3use hir::{Adt, ModuleDef, ScopeDef, Type}; 5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use ide_db::imports_locator;
4use syntax::AstNode; 7use syntax::AstNode;
5use test_utils::mark; 8use test_utils::mark;
6 9
7use crate::{CompletionContext, Completions}; 10use crate::{
11 render::{render_resolution_with_import, RenderContext},
12 CompletionContext, Completions,
13};
8 14
9pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 15pub(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
42fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 52fn 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
76fn 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)]
67mod tests { 116mod 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
735pub mod io {
736 pub fn stdin() {}
737};
738
739//- /main.rs crate:main deps:dep
740fn main() {
741 stdi<|>
742}
743"#,
744 r#"
745use dep::io::stdin;
746
747fn 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]
762macro_rules! macro_with_curlies {
763 () => {}
764}
765
766//- /main.rs crate:main deps:dep
767fn main() {
768 curli<|>
769}
770"#,
771 r#"
772use dep::macro_with_curlies;
773
774fn 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
787pub struct FirstStruct;
788pub mod some_module {
789 pub struct SecondStruct;
790 pub struct ThirdStruct;
791}
792
793//- /main.rs crate:main deps:dep
794use dep::{FirstStruct, some_module::SecondStruct};
795
796fn main() {
797 this<|>
798}
799"#,
800 r#"
801use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
802
803fn main() {
804 ThirdStruct
805}
806"#,
807 );
808 }
679} 809}