aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/Cargo.toml3
-rw-r--r--crates/ide_completion/src/completions/attribute.rs35
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs280
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs7
-rw-r--r--crates/ide_completion/src/completions/keyword.rs58
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs21
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs6
-rw-r--r--crates/ide_completion/src/completions/record.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs25
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs32
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs12
-rw-r--r--crates/ide_completion/src/context.rs4
-rw-r--r--crates/ide_completion/src/item.rs257
-rw-r--r--crates/ide_completion/src/lib.rs30
-rw-r--r--crates/ide_completion/src/render.rs271
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs15
-rw-r--r--crates/ide_completion/src/render/const_.rs10
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs28
-rw-r--r--crates/ide_completion/src/render/function.rs24
-rw-r--r--crates/ide_completion/src/render/macro_.rs33
-rw-r--r--crates/ide_completion/src/render/pattern.rs18
-rw-r--r--crates/ide_completion/src/render/type_alias.rs10
-rw-r--r--crates/ide_completion/src/test_utils.rs3
25 files changed, 772 insertions, 437 deletions
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index c09101ccb..585ecca50 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13itertools = "0.10.0" 14itertools = "0.10.0"
14log = "0.4.8" 15log = "0.4.8"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
@@ -21,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
21base_db = { path = "../base_db", version = "0.0.0" } 22base_db = { path = "../base_db", version = "0.0.0" }
22ide_db = { path = "../ide_db", version = "0.0.0" } 23ide_db = { path = "../ide_db", version = "0.0.0" }
23profile = { path = "../profile", version = "0.0.0" } 24profile = { path = "../profile", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" }
25 25
26# completions crate should depend only on the top-level `hir` package. if you need 26# completions crate should depend only on the top-level `hir` package. if you need
27# something from some `hir_xxx` subpackage, reexport the API via `hir`. 27# something from some `hir_xxx` subpackage, reexport the API via `hir`.
28hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
31expect-test = "1.1" 32expect-test = "1.1"
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index cb05e85fc..e846678b4 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -45,15 +45,15 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
45 CompletionKind::Attribute, 45 CompletionKind::Attribute,
46 ctx.source_range(), 46 ctx.source_range(),
47 attr_completion.label, 47 attr_completion.label,
48 ) 48 );
49 .kind(CompletionItemKind::Attribute); 49 item.kind(CompletionItemKind::Attribute);
50 50
51 if let Some(lookup) = attr_completion.lookup { 51 if let Some(lookup) = attr_completion.lookup {
52 item = item.lookup_by(lookup); 52 item.lookup_by(lookup);
53 } 53 }
54 54
55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { 55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
56 item = item.insert_snippet(cap, snippet); 56 item.insert_snippet(cap, snippet);
57 } 57 }
58 58
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
@@ -168,16 +168,20 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
168 ); 168 );
169 let lookup = components.join(", "); 169 let lookup = components.join(", ");
170 let label = components.iter().rev().join(", "); 170 let label = components.iter().rev().join(", ");
171 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) 171 let mut item =
172 .lookup_by(lookup) 172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 .kind(CompletionItemKind::Attribute) 173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 .add_to(acc) 174 item.add_to(acc);
175 } 175 }
176 176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { 177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name) 178 let mut item = CompletionItem::new(
179 .kind(CompletionItemKind::Attribute) 179 CompletionKind::Attribute,
180 .add_to(acc) 180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
181 } 185 }
182 } 186 }
183} 187}
@@ -193,14 +197,13 @@ fn complete_lint(
193 .into_iter() 197 .into_iter()
194 .filter(|completion| !existing_lints.contains(completion.label)) 198 .filter(|completion| !existing_lints.contains(completion.label))
195 { 199 {
196 CompletionItem::new( 200 let mut item = CompletionItem::new(
197 CompletionKind::Attribute, 201 CompletionKind::Attribute,
198 ctx.source_range(), 202 ctx.source_range(),
199 lint_completion.label, 203 lint_completion.label,
200 ) 204 );
201 .kind(CompletionItemKind::Attribute) 205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
202 .detail(lint_completion.description) 206 item.add_to(acc)
203 .add_to(acc)
204 } 207 }
205 } 208 }
206} 209}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 084d7721d..5ee9a9f07 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -2,7 +2,6 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
6 5
7use crate::{context::CompletionContext, Completions}; 6use crate::{context::CompletionContext, Completions};
8 7
@@ -19,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
19 }; 18 };
20 19
21 if ctx.is_call { 20 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call); 21 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 22 } else {
24 complete_fields(acc, ctx, &receiver_ty); 23 complete_fields(acc, ctx, &receiver_ty);
25 } 24 }
@@ -62,7 +61,6 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
62#[cfg(test)] 61#[cfg(test)]
63mod tests { 62mod tests {
64 use expect_test::{expect, Expect}; 63 use expect_test::{expect, Expect};
65 use test_utils::mark;
66 64
67 use crate::{test_utils::completion_list, CompletionKind}; 65 use crate::{test_utils::completion_list, CompletionKind};
68 66
@@ -122,7 +120,7 @@ impl A {
122 120
123 #[test] 121 #[test]
124 fn test_no_struct_field_completion_for_method_call() { 122 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call); 123 cov_mark::check!(test_no_struct_field_completion_for_method_call);
126 check( 124 check(
127 r#" 125 r#"
128struct A { the_field: u32 } 126struct A { the_field: u32 }
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index da8375af9..391a11c91 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -21,6 +21,46 @@
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports. 23//! Also completes associated items, that require trait imports.
24//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
25//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
26//! (no fuzzy matching for qualifier).
27//!
28//! ```
29//! mod foo {
30//! pub mod bar {
31//! pub struct Item;
32//!
33//! impl Item {
34//! pub const TEST_ASSOC: usize = 3;
35//! }
36//! }
37//! }
38//!
39//! fn main() {
40//! bar::Item::TEST_A$0
41//! }
42//! ```
43//! ->
44//! ```
45//! use foo::bar;
46//!
47//! mod foo {
48//! pub mod bar {
49//! pub struct Item;
50//!
51//! impl Item {
52//! pub const TEST_ASSOC: usize = 3;
53//! }
54//! }
55//! }
56//!
57//! fn main() {
58//! bar::Item::TEST_ASSOC
59//! }
60//! ```
61//!
62//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path,
63//! no imports will be proposed.
24//! 64//!
25//! .Fuzzy search details 65//! .Fuzzy search details
26//! 66//!
@@ -48,14 +88,13 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 88//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 89//! capability enabled.
50 90
51use hir::{AsAssocItem, ModPath, ScopeDef}; 91use hir::ModPath;
52use ide_db::helpers::{ 92use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 93 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 94 insert_use::ImportScope,
55}; 95};
56use rustc_hash::FxHashSet; 96use itertools::Itertools;
57use syntax::{AstNode, SyntaxNode, T}; 97use syntax::{AstNode, SyntaxNode, T};
58use test_utils::mark;
59 98
60use crate::{ 99use crate::{
61 context::CompletionContext, 100 context::CompletionContext,
@@ -93,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
93 &ctx.sema, 132 &ctx.sema,
94 )?; 133 )?;
95 134
96 let scope_definitions = scope_definitions(ctx); 135 acc.add_all(
97 let mut all_mod_paths = import_assets 136 import_assets
98 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) 137 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
99 .into_iter() 138 .into_iter()
100 .map(|(mod_path, item_in_ns)| { 139 .sorted_by_key(|located_import| {
101 let scope_item = match item_in_ns { 140 compute_fuzzy_completion_order_key(
102 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), 141 &located_import.import_path,
103 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), 142 &user_input_lowercased,
104 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), 143 )
105 }; 144 })
106 (mod_path, scope_item) 145 .filter_map(|import| {
107 }) 146 render_resolution_with_import(
108 .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) 147 RenderContext::new(ctx),
109 .collect::<Vec<_>>(); 148 ImportEdit { import, scope: import_scope.clone() },
110 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 149 )
111 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 150 }),
112 }); 151 );
113
114 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
115 let import_for_trait_assoc_item = match definition {
116 ScopeDef::ModuleDef(module_def) => module_def
117 .as_assoc_item(ctx.db)
118 .and_then(|assoc| assoc.containing_trait(ctx.db))
119 .is_some(),
120 _ => false,
121 };
122 let import_edit = ImportEdit {
123 import_path,
124 import_scope: import_scope.clone(),
125 import_for_trait_assoc_item,
126 };
127 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
128 }));
129 Some(()) 152 Some(())
130} 153}
131 154
132fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
133 let mut scope_definitions = FxHashSet::default();
134 ctx.scope.process_all_names(&mut |_, scope_def| {
135 scope_definitions.insert(scope_def);
136 });
137 scope_definitions
138}
139
140pub(crate) fn position_for_import<'a>( 155pub(crate) fn position_for_import<'a>(
141 ctx: &'a CompletionContext, 156 ctx: &'a CompletionContext,
142 import_candidate: Option<&ImportCandidate>, 157 import_candidate: Option<&ImportCandidate>,
@@ -161,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
161 current_module, 176 current_module,
162 ctx.sema.type_of_expr(dot_receiver)?, 177 ctx.sema.type_of_expr(dot_receiver)?,
163 fuzzy_name, 178 fuzzy_name,
179 dot_receiver.syntax().clone(),
164 ) 180 )
165 } else { 181 } else {
166 let fuzzy_name_length = fuzzy_name.len(); 182 let fuzzy_name_length = fuzzy_name.len();
183 let approximate_node = match current_module.definition_source(ctx.db).value {
184 hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
185 hir::ModuleSource::Module(m) => m.syntax().clone(),
186 hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
187 };
167 let assets_for_path = ImportAssets::for_fuzzy_path( 188 let assets_for_path = ImportAssets::for_fuzzy_path(
168 current_module, 189 current_module,
169 ctx.path_qual.clone(), 190 ctx.path_qual.clone(),
170 fuzzy_name, 191 fuzzy_name,
171 &ctx.sema, 192 &ctx.sema,
172 ); 193 approximate_node,
194 )?;
173 195
174 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) 196 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
175 && fuzzy_name_length < 2 197 && fuzzy_name_length < 2
176 { 198 {
177 mark::hit!(ignore_short_input_for_path); 199 cov_mark::hit!(ignore_short_input_for_path);
178 None 200 None
179 } else { 201 } else {
180 assets_for_path 202 Some(assets_for_path)
181 } 203 }
182 } 204 }
183} 205}
@@ -186,12 +208,12 @@ fn compute_fuzzy_completion_order_key(
186 proposed_mod_path: &ModPath, 208 proposed_mod_path: &ModPath,
187 user_input_lowercased: &str, 209 user_input_lowercased: &str,
188) -> usize { 210) -> usize {
189 mark::hit!(certain_fuzzy_order_test); 211 cov_mark::hit!(certain_fuzzy_order_test);
190 let proposed_import_name = match proposed_mod_path.segments().last() { 212 let import_name = match proposed_mod_path.segments().last() {
191 Some(name) => name.to_string().to_lowercase(), 213 Some(name) => name.to_string().to_lowercase(),
192 None => return usize::MAX, 214 None => return usize::MAX,
193 }; 215 };
194 match proposed_import_name.match_indices(user_input_lowercased).next() { 216 match import_name.match_indices(user_input_lowercased).next() {
195 Some((first_matching_index, _)) => first_matching_index, 217 Some((first_matching_index, _)) => first_matching_index,
196 None => usize::MAX, 218 None => usize::MAX,
197 } 219 }
@@ -200,7 +222,6 @@ fn compute_fuzzy_completion_order_key(
200#[cfg(test)] 222#[cfg(test)]
201mod tests { 223mod tests {
202 use expect_test::{expect, Expect}; 224 use expect_test::{expect, Expect};
203 use test_utils::mark;
204 225
205 use crate::{ 226 use crate::{
206 item::CompletionKind, 227 item::CompletionKind,
@@ -295,7 +316,7 @@ fn main() {
295 316
296 #[test] 317 #[test]
297 fn short_paths_are_ignored() { 318 fn short_paths_are_ignored() {
298 mark::check!(ignore_short_input_for_path); 319 cov_mark::check!(ignore_short_input_for_path);
299 320
300 check( 321 check(
301 r#" 322 r#"
@@ -319,7 +340,7 @@ fn main() {
319 340
320 #[test] 341 #[test]
321 fn fuzzy_completions_come_in_specific_order() { 342 fn fuzzy_completions_come_in_specific_order() {
322 mark::check!(certain_fuzzy_order_test); 343 cov_mark::check!(certain_fuzzy_order_test);
323 check( 344 check(
324 r#" 345 r#"
325//- /lib.rs crate:dep 346//- /lib.rs crate:dep
@@ -775,4 +796,155 @@ fn main() {
775}"#, 796}"#,
776 ); 797 );
777 } 798 }
799
800 #[test]
801 fn unresolved_qualifier() {
802 let fixture = r#"
803mod foo {
804 pub mod bar {
805 pub mod baz {
806 pub struct Item;
807 }
808 }
809}
810
811fn main() {
812 bar::baz::Ite$0
813}"#;
814
815 check(
816 fixture,
817 expect![[r#"
818 st foo::bar::baz::Item
819 "#]],
820 );
821
822 check_edit(
823 "Item",
824 fixture,
825 r#"
826 use foo::bar;
827
828 mod foo {
829 pub mod bar {
830 pub mod baz {
831 pub struct Item;
832 }
833 }
834 }
835
836 fn main() {
837 bar::baz::Item
838 }"#,
839 );
840 }
841
842 #[test]
843 fn unresolved_assoc_item_container() {
844 let fixture = r#"
845mod foo {
846 pub struct Item;
847
848 impl Item {
849 pub const TEST_ASSOC: usize = 3;
850 }
851}
852
853fn main() {
854 Item::TEST_A$0
855}"#;
856
857 check(
858 fixture,
859 expect![[r#"
860 ct TEST_ASSOC (foo::Item)
861 "#]],
862 );
863
864 check_edit(
865 "TEST_ASSOC",
866 fixture,
867 r#"
868use foo::Item;
869
870mod foo {
871 pub struct Item;
872
873 impl Item {
874 pub const TEST_ASSOC: usize = 3;
875 }
876}
877
878fn main() {
879 Item::TEST_ASSOC
880}"#,
881 );
882 }
883
884 #[test]
885 fn unresolved_assoc_item_container_with_path() {
886 let fixture = r#"
887mod foo {
888 pub mod bar {
889 pub struct Item;
890
891 impl Item {
892 pub const TEST_ASSOC: usize = 3;
893 }
894 }
895}
896
897fn main() {
898 bar::Item::TEST_A$0
899}"#;
900
901 check(
902 fixture,
903 expect![[r#"
904 ct TEST_ASSOC (foo::bar::Item)
905 "#]],
906 );
907
908 check_edit(
909 "TEST_ASSOC",
910 fixture,
911 r#"
912use foo::bar;
913
914mod foo {
915 pub mod bar {
916 pub struct Item;
917
918 impl Item {
919 pub const TEST_ASSOC: usize = 3;
920 }
921 }
922}
923
924fn main() {
925 bar::Item::TEST_ASSOC
926}"#,
927 );
928 }
929
930 #[test]
931 fn fuzzy_unresolved_path() {
932 check(
933 r#"
934mod foo {
935 pub mod bar {
936 pub struct Item;
937
938 impl Item {
939 pub const TEST_ASSOC: usize = 3;
940 }
941 }
942}
943
944fn main() {
945 bar::Ass$0
946}"#,
947 expect![[]],
948 )
949 }
778} 950}
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 1bcc8727f..0243dce56 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -54,10 +54,9 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 } 54 }
55 55
56 params.into_iter().for_each(|(label, lookup)| { 56 params.into_iter().for_each(|(label, lookup)| {
57 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 57 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
58 .kind(CompletionItemKind::Binding) 58 item.kind(CompletionItemKind::Binding).lookup_by(lookup);
59 .lookup_by(lookup) 59 item.add_to(acc)
60 .add_to(acc)
61 }); 60 });
62} 61}
63 62
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 03c6dd454..b635e0ca3 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -3,7 +3,6 @@
3use std::iter; 3use std::iter;
4 4
5use syntax::SyntaxKind; 5use syntax::SyntaxKind;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
9 8
@@ -13,21 +12,19 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
13 12
14 if ctx.use_item_syntax.is_some() { 13 if ctx.use_item_syntax.is_some() {
15 if ctx.path_qual.is_none() { 14 if ctx.path_qual.is_none() {
16 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::") 15 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::");
17 .kind(CompletionItemKind::Keyword) 16 item.kind(CompletionItemKind::Keyword).insert_text("crate::");
18 .insert_text("crate::") 17 item.add_to(acc);
19 .add_to(acc);
20 } 18 }
21 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self");
22 .kind(CompletionItemKind::Keyword) 20 item.kind(CompletionItemKind::Keyword);
23 .add_to(acc); 21 item.add_to(acc);
24 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 22 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
25 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 23 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
26 { 24 {
27 CompletionItem::new(CompletionKind::Keyword, source_range, "super::") 25 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::");
28 .kind(CompletionItemKind::Keyword) 26 item.kind(CompletionItemKind::Keyword).insert_text("super::");
29 .insert_text("super::") 27 item.add_to(acc);
30 .add_to(acc);
31 } 28 }
32 } 29 }
33 30
@@ -35,11 +32,10 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
35 if let Some(receiver) = &ctx.dot_receiver { 32 if let Some(receiver) = &ctx.dot_receiver {
36 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 33 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
37 if ty.impls_future(ctx.db) { 34 if ty.impls_future(ctx.db) {
38 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 35 let mut item =
39 .kind(CompletionItemKind::Keyword) 36 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await");
40 .detail("expr.await") 37 item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await");
41 .insert_text("await") 38 item.add_to(acc);
42 .add_to(acc);
43 } 39 }
44 }; 40 };
45 } 41 }
@@ -47,11 +43,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
47 43
48pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 44pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
49 if ctx.token.kind() == SyntaxKind::COMMENT { 45 if ctx.token.kind() == SyntaxKind::COMMENT {
50 mark::hit!(no_keyword_completion_in_comments); 46 cov_mark::hit!(no_keyword_completion_in_comments);
51 return; 47 return;
52 } 48 }
53 if ctx.record_lit_syntax.is_some() { 49 if ctx.record_lit_syntax.is_some() {
54 mark::hit!(no_keyword_completion_in_record_lit); 50 cov_mark::hit!(no_keyword_completion_in_record_lit);
55 return; 51 return;
56 } 52 }
57 53
@@ -166,29 +162,31 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
166} 162}
167 163
168fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 164fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
169 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 165 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
170 .kind(CompletionItemKind::Keyword); 166 item.kind(CompletionItemKind::Keyword);
171 let builder = match ctx.config.snippet_cap { 167
168 match ctx.config.snippet_cap {
172 Some(cap) => { 169 Some(cap) => {
173 let tmp; 170 let tmp;
174 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 171 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
175 mark::hit!(let_semi); 172 cov_mark::hit!(let_semi);
176 tmp = format!("{};", snippet); 173 tmp = format!("{};", snippet);
177 &tmp 174 &tmp
178 } else { 175 } else {
179 snippet 176 snippet
180 }; 177 };
181 builder.insert_snippet(cap, snippet) 178 item.insert_snippet(cap, snippet);
179 }
180 None => {
181 item.insert_text(if snippet.contains('$') { kw } else { snippet });
182 } 182 }
183 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
184 }; 183 };
185 acc.add(builder.build()); 184 item.add_to(acc);
186} 185}
187 186
188#[cfg(test)] 187#[cfg(test)]
189mod tests { 188mod tests {
190 use expect_test::{expect, Expect}; 189 use expect_test::{expect, Expect};
191 use test_utils::mark;
192 190
193 use crate::{ 191 use crate::{
194 test_utils::{check_edit, completion_list}, 192 test_utils::{check_edit, completion_list},
@@ -494,7 +492,7 @@ fn quux() -> i32 {
494 492
495 #[test] 493 #[test]
496 fn no_keyword_completion_in_comments() { 494 fn no_keyword_completion_in_comments() {
497 mark::check!(no_keyword_completion_in_comments); 495 cov_mark::check!(no_keyword_completion_in_comments);
498 check( 496 check(
499 r#" 497 r#"
500fn test() { 498fn test() {
@@ -599,7 +597,7 @@ struct Foo {
599 597
600 #[test] 598 #[test]
601 fn skip_struct_initializer() { 599 fn skip_struct_initializer() {
602 mark::check!(no_keyword_completion_in_record_lit); 600 cov_mark::check!(no_keyword_completion_in_record_lit);
603 check( 601 check(
604 r#" 602 r#"
605struct Foo { 603struct Foo {
@@ -643,7 +641,7 @@ fn foo() {
643 641
644 #[test] 642 #[test]
645 fn let_semi() { 643 fn let_semi() {
646 mark::check!(let_semi); 644 cov_mark::check!(let_semi);
647 check_edit( 645 check_edit(
648 "match", 646 "match",
649 r#" 647 r#"
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 352fc7c77..4f9415736 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
80 if mod_under_caret.semicolon_token().is_none() { 80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';'); 81 label.push(';');
82 } 82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label);
84 .kind(SymbolKind::Module) 84 item.kind(SymbolKind::Module);
85 .add_to(acc) 85 item.add_to(acc)
86 }); 86 });
87 87
88 Some(()) 88 Some(())
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 9c34ed0b6..ac69b720a 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
187 ctx, 187 ctx,
188 cap, 188 cap,
189 &dot_receiver, 189 &dot_receiver,
190 "err",
191 "Err(expr)",
192 &format!("Err({})", receiver_text),
193 )
194 .add_to(acc);
195
196 postfix_snippet(
197 ctx,
198 cap,
199 &dot_receiver,
190 "some", 200 "some",
191 "Some(expr)", 201 "Some(expr)",
192 &format!("Some({})", receiver_text), 202 &format!("Some({})", receiver_text),
@@ -287,10 +297,9 @@ fn postfix_snippet(
287 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); 297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
288 TextEdit::replace(delete_range, snippet.to_string()) 298 TextEdit::replace(delete_range, snippet.to_string())
289 }; 299 };
290 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 300 let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
291 .detail(detail) 301 item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
292 .kind(CompletionItemKind::Snippet) 302 item
293 .snippet_edit(cap, edit)
294} 303}
295 304
296#[cfg(test)] 305#[cfg(test)]
@@ -325,6 +334,7 @@ fn main() {
325 sn match match expr {} 334 sn match match expr {}
326 sn box Box::new(expr) 335 sn box Box::new(expr)
327 sn ok Ok(expr) 336 sn ok Ok(expr)
337 sn err Err(expr)
328 sn some Some(expr) 338 sn some Some(expr)
329 sn dbg dbg!(expr) 339 sn dbg dbg!(expr)
330 sn dbgr dbg!(&expr) 340 sn dbgr dbg!(&expr)
@@ -357,6 +367,7 @@ fn main() {
357 sn match match expr {} 367 sn match match expr {}
358 sn box Box::new(expr) 368 sn box Box::new(expr)
359 sn ok Ok(expr) 369 sn ok Ok(expr)
370 sn err Err(expr)
360 sn some Some(expr) 371 sn some Some(expr)
361 sn dbg dbg!(expr) 372 sn dbg dbg!(expr)
362 sn dbgr dbg!(&expr) 373 sn dbgr dbg!(&expr)
@@ -380,6 +391,7 @@ fn main() {
380 sn match match expr {} 391 sn match match expr {}
381 sn box Box::new(expr) 392 sn box Box::new(expr)
382 sn ok Ok(expr) 393 sn ok Ok(expr)
394 sn err Err(expr)
383 sn some Some(expr) 395 sn some Some(expr)
384 sn dbg dbg!(expr) 396 sn dbg dbg!(expr)
385 sn dbgr dbg!(&expr) 397 sn dbgr dbg!(&expr)
@@ -408,6 +420,7 @@ fn main() {
408 sn match match expr {} 420 sn match match expr {}
409 sn box Box::new(expr) 421 sn box Box::new(expr)
410 sn ok Ok(expr) 422 sn ok Ok(expr)
423 sn err Err(expr)
411 sn some Some(expr) 424 sn some Some(expr)
412 sn dbg dbg!(expr) 425 sn dbg dbg!(expr)
413 sn dbgr dbg!(&expr) 426 sn dbgr dbg!(&expr)
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 72fb757b1..df74b739e 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -3,7 +3,6 @@
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::AstNode; 5use syntax::AstNode;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
9 8
@@ -39,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 38 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() { 39 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo$0`, don't suggest `foo` as a completion 40 // for `use self::foo$0`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use); 41 cov_mark::hit!(dont_complete_current_use);
43 continue; 42 continue;
44 } 43 }
45 } 44 }
@@ -155,7 +154,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
155#[cfg(test)] 154#[cfg(test)]
156mod tests { 155mod tests {
157 use expect_test::{expect, Expect}; 156 use expect_test::{expect, Expect};
158 use test_utils::mark;
159 157
160 use crate::{ 158 use crate::{
161 test_utils::{check_edit, completion_list}, 159 test_utils::{check_edit, completion_list},
@@ -174,7 +172,7 @@ mod tests {
174 172
175 #[test] 173 #[test]
176 fn dont_complete_current_use() { 174 fn dont_complete_current_use() {
177 mark::check!(dont_complete_current_use); 175 cov_mark::check!(dont_complete_current_use);
178 check(r#"use self::foo$0;"#, expect![[""]]); 176 check(r#"use self::foo$0;"#, expect![[""]]);
179 } 177 }
180 178
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 0a7927eb8..2f95b8687 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -22,16 +22,13 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
22 let completion_text = completion_text 22 let completion_text = completion_text
23 .strip_prefix(ctx.token.to_string().as_str()) 23 .strip_prefix(ctx.token.to_string().as_str())
24 .unwrap_or(completion_text); 24 .unwrap_or(completion_text);
25 acc.add( 25 let mut item = CompletionItem::new(
26 CompletionItem::new( 26 CompletionKind::Snippet,
27 CompletionKind::Snippet, 27 ctx.source_range(),
28 ctx.source_range(), 28 "..Default::default()",
29 "..Default::default()",
30 )
31 .insert_text(completion_text)
32 .kind(SymbolKind::Field)
33 .build(),
34 ); 29 );
30 item.insert_text(completion_text).kind(SymbolKind::Field);
31 item.add_to(acc);
35 } 32 }
36 33
37 missing_fields 34 missing_fields
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index df17a15c5..7f7830976 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -8,9 +8,9 @@ use crate::{
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 11 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label);
12 .insert_snippet(cap, snippet) 12 item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet);
13 .kind(CompletionItemKind::Snippet) 13 item
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 snippet( 38 let mut item = snippet(
39 ctx, 39 ctx,
40 cap, 40 cap,
41 "tmod (Test module)", 41 "tmod (Test module)",
@@ -49,11 +49,11 @@ mod tests {
49 $0 49 $0
50 } 50 }
51}", 51}",
52 ) 52 );
53 .lookup_by("tmod") 53 item.lookup_by("tmod");
54 .add_to(acc); 54 item.add_to(acc);
55 55
56 snippet( 56 let mut item = snippet(
57 ctx, 57 ctx,
58 cap, 58 cap,
59 "tfn (Test function)", 59 "tfn (Test function)",
@@ -62,11 +62,12 @@ mod tests {
62fn ${1:feature}() { 62fn ${1:feature}() {
63 $0 63 $0
64}", 64}",
65 ) 65 );
66 .lookup_by("tfn") 66 item.lookup_by("tfn");
67 .add_to(acc); 67 item.add_to(acc);
68 68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 69 let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}");
70 item.add_to(acc);
70} 71}
71 72
72#[cfg(test)] 73#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index b999540b8..5a7361f8e 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -145,9 +145,8 @@ fn add_function_impl(
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
146 }; 146 };
147 147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 148 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
149 .lookup_by(fn_name) 149 item.lookup_by(fn_name).set_documentation(func.docs(ctx.db));
150 .set_documentation(func.docs(ctx.db));
151 150
152 let completion_kind = if func.self_param(ctx.db).is_some() { 151 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 152 CompletionItemKind::Method
@@ -161,15 +160,15 @@ fn add_function_impl(
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
162 Some(cap) => { 161 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 item.snippet_edit(cap, TextEdit::replace(range, snippet));
165 } 164 }
166 None => { 165 None => {
167 let header = format!("{} {{", function_decl); 166 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header)) 167 item.text_edit(TextEdit::replace(range, header));
169 } 168 }
170 } 169 };
171 .kind(completion_kind) 170 item.kind(completion_kind);
172 .add_to(acc); 171 item.add_to(acc);
173 } 172 }
174} 173}
175 174
@@ -185,12 +184,12 @@ fn add_type_alias_impl(
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187 186
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 187 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 .text_edit(TextEdit::replace(range, snippet)) 188 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 189 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias) 190 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 191 .set_documentation(type_alias.docs(ctx.db));
193 .add_to(acc); 192 item.add_to(acc);
194} 193}
195 194
196fn add_const_impl( 195fn add_const_impl(
@@ -208,12 +207,13 @@ fn add_const_impl(
208 let range = 207 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 208 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210 209
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 210 let mut item =
212 .text_edit(TextEdit::replace(range, snippet)) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
212 item.text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(SymbolKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db));
216 .add_to(acc); 216 item.add_to(acc);
217 } 217 }
218 } 218 }
219} 219}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index e9d0ff665..044dfd160 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -2,7 +2,6 @@
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode; 4use syntax::AstNode;
5use test_utils::mark;
6 5
7use crate::{CompletionContext, Completions}; 6use crate::{CompletionContext, Completions};
8 7
@@ -30,13 +29,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 29
31 ctx.scope.process_all_names(&mut |name, res| { 30 ctx.scope.process_all_names(&mut |name, res| {
32 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 31 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
33 mark::hit!(skip_lifetime_completion); 32 cov_mark::hit!(skip_lifetime_completion);
34 return; 33 return;
35 } 34 }
36 if ctx.use_item_syntax.is_some() { 35 if ctx.use_item_syntax.is_some() {
37 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 36 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
38 if name_ref.syntax().text() == name.to_string().as_str() { 37 if name_ref.syntax().text() == name.to_string().as_str() {
39 mark::hit!(self_fulfilling_completion); 38 cov_mark::hit!(self_fulfilling_completion);
40 return; 39 return;
41 } 40 }
42 } 41 }
@@ -48,7 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
48#[cfg(test)] 47#[cfg(test)]
49mod tests { 48mod tests {
50 use expect_test::{expect, Expect}; 49 use expect_test::{expect, Expect};
51 use test_utils::mark;
52 50
53 use crate::{ 51 use crate::{
54 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, 52 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
@@ -66,7 +64,7 @@ mod tests {
66 64
67 #[test] 65 #[test]
68 fn self_fulfilling_completion() { 66 fn self_fulfilling_completion() {
69 mark::check!(self_fulfilling_completion); 67 cov_mark::check!(self_fulfilling_completion);
70 check( 68 check(
71 r#" 69 r#"
72use foo$0 70use foo$0
@@ -185,7 +183,7 @@ fn quux() {
185 183
186 #[test] 184 #[test]
187 fn completes_if_prefix_is_keyword() { 185 fn completes_if_prefix_is_keyword() {
188 mark::check!(completes_if_prefix_is_keyword); 186 cov_mark::check!(completes_if_prefix_is_keyword);
189 check_edit( 187 check_edit(
190 "wherewolf", 188 "wherewolf",
191 r#" 189 r#"
@@ -223,7 +221,7 @@ fn main() {
223 221
224 #[test] 222 #[test]
225 fn does_not_complete_lifetimes() { 223 fn does_not_complete_lifetimes() {
226 mark::check!(skip_lifetime_completion); 224 cov_mark::check!(skip_lifetime_completion);
227 check( 225 check(
228 r#"fn quux<'a>() { $0 }"#, 226 r#"fn quux<'a>() { $0 }"#,
229 expect![[r#" 227 expect![[r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 3db357855..17d9a3adf 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -7,7 +7,7 @@ use syntax::{
7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, 7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode,
8 SyntaxToken, TextRange, TextSize, 8 SyntaxToken, TextRange, TextSize,
9}; 9};
10use test_utils::mark; 10
11use text_edit::Indel; 11use text_edit::Indel;
12 12
13use crate::{ 13use crate::{
@@ -240,7 +240,7 @@ impl<'a> CompletionContext<'a> {
240 // check kind of macro-expanded token, but use range of original token 240 // check kind of macro-expanded token, but use range of original token
241 let kind = self.token.kind(); 241 let kind = self.token.kind();
242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { 242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
243 mark::hit!(completes_if_prefix_is_keyword); 243 cov_mark::hit!(completes_if_prefix_is_keyword);
244 self.original_token.text_range() 244 self.original_token.text_range()
245 } else { 245 } else {
246 TextRange::empty(self.position.offset) 246 TextRange::empty(self.position.offset)
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 884711f11..3febab32b 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -2,15 +2,16 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, Mutability};
6use ide_db::{ 6use ide_db::{
7 helpers::{ 7 helpers::{
8 insert_use::{self, ImportScope, MergeBehavior}, 8 import_assets::LocatedImport,
9 insert_use::{self, ImportScope, InsertUseConfig},
9 mod_path_to_ast, SnippetCap, 10 mod_path_to_ast, SnippetCap,
10 }, 11 },
11 SymbolKind, 12 SymbolKind,
12}; 13};
13use stdx::{impl_from, never}; 14use stdx::{format_to, impl_from, never};
14use syntax::{algo, TextRange}; 15use syntax::{algo, TextRange};
15use text_edit::TextEdit; 16use text_edit::TextEdit;
16 17
@@ -62,12 +63,18 @@ pub struct CompletionItem {
62 /// after completion. 63 /// after completion.
63 trigger_call_info: bool, 64 trigger_call_info: bool,
64 65
65 /// Score is useful to pre select or display in better order completion items 66 /// We use this to sort completion. Relevance records facts like "do the
66 score: Option<CompletionScore>, 67 /// types align precisely?". We can't sort by relevances directly, they are
68 /// only partially ordered.
69 ///
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: CompletionRelevance,
67 74
68 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
69 /// possible match. 76 /// possible match.
70 ref_match: Option<(Mutability, CompletionScore)>, 77 ref_match: Option<Mutability>,
71 78
72 /// The import data to add to completion's edits. 79 /// The import data to add to completion's edits.
73 import_to_add: Option<ImportEdit>, 80 import_to_add: Option<ImportEdit>,
@@ -100,8 +107,13 @@ impl fmt::Debug for CompletionItem {
100 if self.deprecated { 107 if self.deprecated {
101 s.field("deprecated", &true); 108 s.field("deprecated", &true);
102 } 109 }
103 if let Some(score) = &self.score { 110
104 s.field("score", score); 111 if self.relevance != CompletionRelevance::default() {
112 s.field("relevance", &self.relevance);
113 }
114
115 if let Some(mutability) = &self.ref_match {
116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
105 } 117 }
106 if self.trigger_call_info { 118 if self.trigger_call_info {
107 s.field("trigger_call_info", &true); 119 s.field("trigger_call_info", &true);
@@ -110,12 +122,59 @@ impl fmt::Debug for CompletionItem {
110 } 122 }
111} 123}
112 124
113#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
114pub enum CompletionScore { 126pub struct CompletionRelevance {
115 /// If only type match 127 /// This is set in cases like these:
116 TypeMatch, 128 ///
117 /// If type and name match 129 /// ```
118 TypeAndNameMatch, 130 /// fn f(spam: String) {}
131 /// fn main {
132 /// let spam = 92;
133 /// f($0) // name of local matches the name of param
134 /// }
135 /// ```
136 pub exact_name_match: bool,
137 /// This is set in cases like these:
138 ///
139 /// ```
140 /// fn f(spam: String) {}
141 /// fn main {
142 /// let foo = String::new();
143 /// f($0) // type of local matches the type of param
144 /// }
145 /// ```
146 pub exact_type_match: bool,
147}
148
149impl CompletionRelevance {
150 /// Provides a relevance score. Higher values are more relevant.
151 ///
152 /// The absolute value of the relevance score is not meaningful, for
153 /// example a value of 0 doesn't mean "not relevant", rather
154 /// it means "least relevant". The score value should only be used
155 /// for relative ordering.
156 ///
157 /// See is_relevant if you need to make some judgement about score
158 /// in an absolute sense.
159 pub fn score(&self) -> u32 {
160 let mut score = 0;
161
162 if self.exact_name_match {
163 score += 1;
164 }
165 if self.exact_type_match {
166 score += 1;
167 }
168
169 score
170 }
171
172 /// Returns true when the score for this threshold is above
173 /// some threshold such that we think it is especially likely
174 /// to be relevant.
175 pub fn is_relevant(&self) -> bool {
176 self.score() > 0
177 }
119} 178}
120 179
121#[derive(Debug, Clone, Copy, PartialEq, Eq)] 180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -207,9 +266,9 @@ impl CompletionItem {
207 lookup: None, 266 lookup: None,
208 kind: None, 267 kind: None,
209 text_edit: None, 268 text_edit: None,
210 deprecated: None, 269 deprecated: false,
211 trigger_call_info: None, 270 trigger_call_info: None,
212 score: None, 271 relevance: CompletionRelevance::default(),
213 ref_match: None, 272 ref_match: None,
214 import_to_add: None, 273 import_to_add: None,
215 } 274 }
@@ -252,16 +311,22 @@ impl CompletionItem {
252 self.deprecated 311 self.deprecated
253 } 312 }
254 313
255 pub fn score(&self) -> Option<CompletionScore> { 314 pub fn relevance(&self) -> CompletionRelevance {
256 self.score 315 self.relevance
257 } 316 }
258 317
259 pub fn trigger_call_info(&self) -> bool { 318 pub fn trigger_call_info(&self) -> bool {
260 self.trigger_call_info 319 self.trigger_call_info
261 } 320 }
262 321
263 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 322 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
264 self.ref_match 323 // Relevance of the ref match should be the same as the original
324 // match, but with exact type match set because self.ref_match
325 // is only set if there is an exact type match.
326 let mut relevance = self.relevance;
327 relevance.exact_type_match = true;
328
329 self.ref_match.map(|mutability| (mutability, relevance))
265 } 330 }
266 331
267 pub fn import_to_add(&self) -> Option<&ImportEdit> { 332 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -272,22 +337,18 @@ impl CompletionItem {
272/// An extra import to add after the completion is applied. 337/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)] 338#[derive(Debug, Clone)]
274pub struct ImportEdit { 339pub struct ImportEdit {
275 pub import_path: ModPath, 340 pub import: LocatedImport,
276 pub import_scope: ImportScope, 341 pub scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278} 342}
279 343
280impl ImportEdit { 344impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit. 345 /// Attempts to insert the import to the given scope, producing a text edit.
282 /// May return no edit in edge cases, such as scope already containing the import. 346 /// May return no edit in edge cases, such as scope already containing the import.
283 pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> { 347 pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit"); 348 let _p = profile::span("ImportEdit::to_text_edit");
285 349
286 let rewriter = insert_use::insert_use( 350 let rewriter =
287 &self.import_scope, 351 insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
288 mod_path_to_ast(&self.import_path),
289 merge_behavior,
290 );
291 let old_ast = rewriter.rewrite_root()?; 352 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder(); 353 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 354 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -311,10 +372,10 @@ pub(crate) struct Builder {
311 lookup: Option<String>, 372 lookup: Option<String>,
312 kind: Option<CompletionItemKind>, 373 kind: Option<CompletionItemKind>,
313 text_edit: Option<TextEdit>, 374 text_edit: Option<TextEdit>,
314 deprecated: Option<bool>, 375 deprecated: bool,
315 trigger_call_info: Option<bool>, 376 trigger_call_info: Option<bool>,
316 score: Option<CompletionScore>, 377 relevance: CompletionRelevance,
317 ref_match: Option<(Mutability, CompletionScore)>, 378 ref_match: Option<Mutability>,
318} 379}
319 380
320impl Builder { 381impl Builder {
@@ -325,20 +386,19 @@ impl Builder {
325 let mut lookup = self.lookup; 386 let mut lookup = self.lookup;
326 let mut insert_text = self.insert_text; 387 let mut insert_text = self.insert_text;
327 388
328 if let Some(import_to_add) = self.import_to_add.as_ref() { 389 if let Some(original_path) = self
329 if import_to_add.import_for_trait_assoc_item { 390 .import_to_add
330 lookup = lookup.or_else(|| Some(label.clone())); 391 .as_ref()
331 insert_text = insert_text.or_else(|| Some(label.clone())); 392 .and_then(|import_edit| import_edit.import.original_path.as_ref())
332 label = format!("{} ({})", label, import_to_add.import_path); 393 {
394 lookup = lookup.or_else(|| Some(label.clone()));
395 insert_text = insert_text.or_else(|| Some(label.clone()));
396
397 let original_path_label = original_path.to_string();
398 if original_path_label.ends_with(&label) {
399 label = original_path_label;
333 } else { 400 } else {
334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 401 format_to!(label, " ({})", original_path)
335 let _ = import_path_without_last_segment.pop_segment();
336
337 if !import_path_without_last_segment.segments().is_empty() {
338 lookup = lookup.or_else(|| Some(label.clone()));
339 insert_text = insert_text.or_else(|| Some(label.clone()));
340 label = format!("{}::{}", import_path_without_last_segment, label);
341 }
342 } 402 }
343 } 403 }
344 404
@@ -359,49 +419,49 @@ impl Builder {
359 lookup, 419 lookup,
360 kind: self.kind, 420 kind: self.kind,
361 completion_kind: self.completion_kind, 421 completion_kind: self.completion_kind,
362 deprecated: self.deprecated.unwrap_or(false), 422 deprecated: self.deprecated,
363 trigger_call_info: self.trigger_call_info.unwrap_or(false), 423 trigger_call_info: self.trigger_call_info.unwrap_or(false),
364 score: self.score, 424 relevance: self.relevance,
365 ref_match: self.ref_match, 425 ref_match: self.ref_match,
366 import_to_add: self.import_to_add, 426 import_to_add: self.import_to_add,
367 } 427 }
368 } 428 }
369 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 429 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
370 self.lookup = Some(lookup.into()); 430 self.lookup = Some(lookup.into());
371 self 431 self
372 } 432 }
373 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 433 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
374 self.label = label.into(); 434 self.label = label.into();
375 self 435 self
376 } 436 }
377 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 437 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
378 self.insert_text = Some(insert_text.into()); 438 self.insert_text = Some(insert_text.into());
379 self 439 self
380 } 440 }
381 pub(crate) fn insert_snippet( 441 pub(crate) fn insert_snippet(
382 mut self, 442 &mut self,
383 _cap: SnippetCap, 443 _cap: SnippetCap,
384 snippet: impl Into<String>, 444 snippet: impl Into<String>,
385 ) -> Builder { 445 ) -> &mut Builder {
386 self.insert_text_format = InsertTextFormat::Snippet; 446 self.insert_text_format = InsertTextFormat::Snippet;
387 self.insert_text(snippet) 447 self.insert_text(snippet)
388 } 448 }
389 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 449 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
390 self.kind = Some(kind.into()); 450 self.kind = Some(kind.into());
391 self 451 self
392 } 452 }
393 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 453 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
394 self.text_edit = Some(edit); 454 self.text_edit = Some(edit);
395 self 455 self
396 } 456 }
397 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 457 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
398 self.insert_text_format = InsertTextFormat::Snippet; 458 self.insert_text_format = InsertTextFormat::Snippet;
399 self.text_edit(edit) 459 self.text_edit(edit)
400 } 460 }
401 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 461 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
402 self.set_detail(Some(detail)) 462 self.set_detail(Some(detail))
403 } 463 }
404 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 464 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
405 self.detail = detail.map(Into::into); 465 self.detail = detail.map(Into::into);
406 if let Some(detail) = &self.detail { 466 if let Some(detail) = &self.detail {
407 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 467 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -411,40 +471,91 @@ impl Builder {
411 self 471 self
412 } 472 }
413 #[allow(unused)] 473 #[allow(unused)]
414 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 474 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
415 self.set_documentation(Some(docs)) 475 self.set_documentation(Some(docs))
416 } 476 }
417 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 477 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
418 self.documentation = docs.map(Into::into); 478 self.documentation = docs.map(Into::into);
419 self 479 self
420 } 480 }
421 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 481 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
422 self.deprecated = Some(deprecated); 482 self.deprecated = deprecated;
423 self 483 self
424 } 484 }
425 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { 485 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
426 self.score = Some(score); 486 self.relevance = relevance;
427 self 487 self
428 } 488 }
429 pub(crate) fn trigger_call_info(mut self) -> Builder { 489 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
430 self.trigger_call_info = Some(true); 490 self.trigger_call_info = Some(true);
431 self 491 self
432 } 492 }
433 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 493 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
434 self.import_to_add = import_to_add; 494 self.import_to_add = import_to_add;
435 self 495 self
436 } 496 }
437 pub(crate) fn set_ref_match( 497 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
438 mut self, 498 self.ref_match = Some(mutability);
439 ref_match: Option<(Mutability, CompletionScore)>,
440 ) -> Builder {
441 self.ref_match = ref_match;
442 self 499 self
443 } 500 }
444} 501}
445 502
446impl<'a> Into<CompletionItem> for Builder { 503#[cfg(test)]
447 fn into(self) -> CompletionItem { 504mod tests {
448 self.build() 505 use itertools::Itertools;
506 use test_utils::assert_eq_text;
507
508 use super::CompletionRelevance;
509
510 /// Check that these are CompletionRelevance are sorted in ascending order
511 /// by their relevance score.
512 ///
513 /// We want to avoid making assertions about the absolute score of any
514 /// item, but we do want to assert whether each is >, <, or == to the
515 /// others.
516 ///
517 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
518 /// a.score < b.score == c.score < d.score
519 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
520 let expected = format!("{:#?}", &expected_relevance_order);
521
522 let actual_relevance_order = expected_relevance_order
523 .into_iter()
524 .flatten()
525 .map(|r| (r.score(), r))
526 .sorted_by_key(|(score, _r)| *score)
527 .fold(
528 (u32::MIN, vec![vec![]]),
529 |(mut currently_collecting_score, mut out), (score, r)| {
530 if currently_collecting_score == score {
531 out.last_mut().unwrap().push(r);
532 } else {
533 currently_collecting_score = score;
534 out.push(vec![r]);
535 }
536 (currently_collecting_score, out)
537 },
538 )
539 .1;
540
541 let actual = format!("{:#?}", &actual_relevance_order);
542
543 assert_eq_text!(&expected, &actual);
544 }
545
546 #[test]
547 fn relevance_score() {
548 // This test asserts that the relevance score for these items is ascending, and
549 // that any items in the same vec have the same score.
550 let expected_relevance_order = vec![
551 vec![CompletionRelevance::default()],
552 vec![
553 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
554 CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() },
555 ],
556 vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }],
557 ];
558
559 check_relevance_score_ordered(expected_relevance_order);
449 } 560 }
450} 561}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 76f31de9e..263554ecf 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -13,7 +13,9 @@ mod completions;
13 13
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope},
18 items_locator, RootDatabase,
17}; 19};
18use text_edit::TextEdit; 20use text_edit::TextEdit;
19 21
@@ -21,7 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
21 23
22pub use crate::{ 24pub use crate::{
23 config::CompletionConfig, 25 config::CompletionConfig,
24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 26 item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat},
25}; 27};
26 28
27//FIXME: split the following feature into fine-grained features. 29//FIXME: split the following feature into fine-grained features.
@@ -139,25 +141,27 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 141 position: FilePosition,
140 full_import_path: &str, 142 full_import_path: &str,
141 imported_name: String, 143 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> { 144) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?; 145 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?; 146 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; 147 let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147 148
148 let current_module = ctx.sema.scope(position_for_import).module()?; 149 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate(); 150 let current_crate = current_module.krate();
150 151
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 152 let (import_path, item_to_import) =
152 .filter_map(|candidate| { 153 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 154 .into_iter()
154 current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) 155 .filter_map(|candidate| {
155 }) 156 current_module
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 157 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
158 .zip(Some(candidate))
159 })
160 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
161 let import =
162 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
157 163
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 164 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
159 .to_text_edit(config.insert_use.merge)
160 .map(|edit| vec![edit])
161} 165}
162 166
163#[cfg(test)] 167#[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..db31896e5 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,13 +13,15 @@ mod builder_ext;
13use hir::{ 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{
17 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind,
19};
17use syntax::TextRange; 20use syntax::TextRange;
18use test_utils::mark;
19 21
20use crate::{ 22use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{CompletionRelevance, ImportEdit},
22 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
23}; 25};
24 26
25use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
51pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import<'a>(
52 ctx: RenderContext<'a>, 54 ctx: RenderContext<'a>,
53 import_edit: ImportEdit, 55 import_edit: ImportEdit,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item);
56 let local_name = match resolution { 58 let local_name = match resolution {
57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
60 _ => import_edit.import_path.segments().last()?.to_string(), 62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
61 }; 63 };
62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { 64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
63 item.completion_kind = CompletionKind::Magic; 65 |mut item| {
64 item 66 item.completion_kind = CompletionKind::Magic;
65 }) 67 item
68 },
69 )
66} 70}
67 71
68/// Interface for data and methods required for items rendering. 72/// Interface for data and methods required for items rendering.
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> {
113 node.docs(self.db()) 117 node.docs(self.db())
114 } 118 }
115 119
116 fn active_name_and_type(&self) -> Option<(String, Type)> { 120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax { 121 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match); 122 cov_mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
120 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) 124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
121 } else if let Some(active_parameter) = &self.completion.active_parameter { 125 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match); 126 cov_mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone())) 127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else { 128 } else {
125 None 129 None
@@ -145,24 +149,29 @@ impl<'a> Render<'a> {
145 CompletionKind::Reference, 149 CompletionKind::Reference,
146 self.ctx.source_range(), 150 self.ctx.source_range(),
147 name.to_string(), 151 name.to_string(),
148 ) 152 );
149 .kind(SymbolKind::Field) 153 item.kind(SymbolKind::Field)
150 .detail(ty.display(self.ctx.db()).to_string()) 154 .detail(ty.display(self.ctx.db()).to_string())
151 .set_documentation(field.docs(self.ctx.db())) 155 .set_documentation(field.docs(self.ctx.db()))
152 .set_deprecated(is_deprecated); 156 .set_deprecated(is_deprecated);
153 157
154 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { 158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) {
155 item = item.set_score(score); 159 item.set_relevance(relevance);
156 } 160 }
157 161
158 item.build() 162 item.build()
159 } 163 }
160 164
161 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
162 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 166 let mut item = CompletionItem::new(
163 .kind(SymbolKind::Field) 167 CompletionKind::Reference,
164 .detail(ty.display(self.ctx.db()).to_string()) 168 self.ctx.source_range(),
165 .build() 169 field.to_string(),
170 );
171
172 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
173
174 item.build()
166 } 175 }
167 176
168 fn render_resolution( 177 fn render_resolution(
@@ -221,15 +230,13 @@ impl<'a> Render<'a> {
221 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 230 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
222 } 231 }
223 ScopeDef::Unknown => { 232 ScopeDef::Unknown => {
224 let item = CompletionItem::new( 233 let mut item = CompletionItem::new(
225 CompletionKind::Reference, 234 CompletionKind::Reference,
226 self.ctx.source_range(), 235 self.ctx.source_range(),
227 local_name, 236 local_name,
228 ) 237 );
229 .kind(CompletionItemKind::UnresolvedReference) 238 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
230 .add_import(import_to_add) 239 return Some(item.build());
231 .build();
232 return Some(item);
233 } 240 }
234 }; 241 };
235 242
@@ -238,20 +245,27 @@ impl<'a> Render<'a> {
238 if let ScopeDef::Local(local) = resolution { 245 if let ScopeDef::Local(local) = resolution {
239 let ty = local.ty(self.ctx.db()); 246 let ty = local.ty(self.ctx.db());
240 if !ty.is_unknown() { 247 if !ty.is_unknown() {
241 item = item.detail(ty.display(self.ctx.db()).to_string()); 248 item.detail(ty.display(self.ctx.db()).to_string());
242 } 249 }
243 }; 250 };
244 251
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution { 252 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 253 let ty = local.ty(self.ctx.db());
248 let ty = local.ty(self.ctx.db()); 254 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
249 if let Some(score) = 255 item.set_relevance(relevance);
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 256 }
251 { 257 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
252 item = item.set_score(score); 258 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 if ty_without_ref == ty {
260 cov_mark::hit!(suggest_ref);
261 let mutability = if expected_type.is_mutable_reference() {
262 Mutability::Mut
263 } else {
264 Mutability::Shared
265 };
266 item.ref_match(mutability);
267 }
253 } 268 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 } 269 }
256 } 270 }
257 271
@@ -269,23 +283,18 @@ impl<'a> Render<'a> {
269 _ => false, 283 _ => false,
270 }; 284 };
271 if has_non_default_type_params { 285 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 286 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 287 item.lookup_by(local_name.clone())
274 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 288 .label(format!("{}<…>", local_name))
276 .insert_snippet(cap, format!("{}<$0>", local_name)); 289 .insert_snippet(cap, format!("{}<$0>", local_name));
277 } 290 }
278 } 291 }
279 } 292 }
280 293 item.kind(kind)
281 Some( 294 .add_import(import_to_add)
282 item.kind(kind) 295 .set_documentation(self.docs(resolution))
283 .add_import(import_to_add) 296 .set_deprecated(self.is_deprecated(resolution));
284 .set_ref_match(ref_match) 297 Some(item.build())
285 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution))
287 .build(),
288 )
289 } 298 }
290 299
291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 300 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -313,56 +322,23 @@ impl<'a> Render<'a> {
313 } 322 }
314} 323}
315 324
316fn compute_score_from_active( 325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
317 active_type: &Type, 326 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
318 active_name: &str, 327 let mut res = CompletionRelevance::default();
319 ty: &Type, 328 res.exact_type_match = ty == &expected_type;
320 name: &str, 329 res.exact_name_match = name == &expected_name;
321) -> Option<CompletionScore> {
322 // Compute score
323 // For the same type
324 if active_type != ty {
325 return None;
326 }
327
328 let mut res = CompletionScore::TypeMatch;
329
330 // If same type + same name then go top position
331 if active_name == name {
332 res = CompletionScore::TypeAndNameMatch
333 }
334
335 Some(res) 330 Some(res)
336} 331}
337fn refed_type_matches(
338 active_type: &Type,
339 active_name: &str,
340 ty: &Type,
341 name: &str,
342) -> Option<(Mutability, CompletionScore)> {
343 let derefed_active = active_type.remove_ref()?;
344 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
345 Some((
346 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
347 score,
348 ))
349}
350
351fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
352 let (active_name, active_type) = ctx.active_name_and_type()?;
353 compute_score_from_active(&active_type, &active_name, ty, name)
354}
355 332
356#[cfg(test)] 333#[cfg(test)]
357mod tests { 334mod tests {
358 use std::cmp::Reverse; 335 use std::cmp::Reverse;
359 336
360 use expect_test::{expect, Expect}; 337 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 338
363 use crate::{ 339 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 340 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore, 341 CompletionKind, CompletionRelevance,
366 }; 342 };
367 343
368 fn check(ra_fixture: &str, expect: Expect) { 344 fn check(ra_fixture: &str, expect: Expect) {
@@ -370,24 +346,27 @@ mod tests {
370 expect.assert_debug_eq(&actual); 346 expect.assert_debug_eq(&actual);
371 } 347 }
372 348
373 fn check_scores(ra_fixture: &str, expect: Expect) { 349 fn check_relevance(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str { 350 fn display_relevance(relevance: CompletionRelevance) -> &'static str {
375 match score { 351 match relevance {
376 Some(CompletionScore::TypeMatch) => "[type]", 352 CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 353 "[type+name]"
378 None => "[]".into(), 354 }
355 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
356 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
357 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
379 } 358 }
380 } 359 }
381 360
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 361 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 362 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
384 let actual = completions 363 let actual = completions
385 .into_iter() 364 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference) 365 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| { 366 .map(|it| {
388 let tag = it.kind().unwrap().tag(); 367 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score()); 368 let relevance = display_relevance(it.relevance());
390 format!("{} {} {}\n", tag, it.label(), score) 369 format!("{} {} {}\n", tag, it.label(), relevance)
391 }) 370 })
392 .collect::<String>(); 371 .collect::<String>();
393 expect.assert_eq(&actual); 372 expect.assert_eq(&actual);
@@ -734,7 +713,7 @@ fn foo(s: S) { s.$0 }
734 713
735 #[test] 714 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 715 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 716 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 717 check_edit(
739 "foo", 718 "foo",
740 r#" 719 r#"
@@ -758,7 +737,7 @@ fn main() -> ManualVtable {
758 737
759 #[test] 738 #[test]
760 fn no_parens_in_use_item() { 739 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 740 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 741 check_edit(
763 "foo", 742 "foo",
764 r#" 743 r#"
@@ -802,7 +781,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 781
803 #[test] 782 #[test]
804 fn inserts_angle_brackets_for_generics() { 783 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 784 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 785 check_edit(
807 "Vec", 786 "Vec",
808 r#" 787 r#"
@@ -850,9 +829,9 @@ fn foo(xs: Vec<i128>)
850 } 829 }
851 830
852 #[test] 831 #[test]
853 fn active_param_score() { 832 fn active_param_relevance() {
854 mark::check!(active_param_type_match); 833 cov_mark::check!(active_param_type_match);
855 check_scores( 834 check_relevance(
856 r#" 835 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 836struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { } 837fn test(bar: u32) { }
@@ -867,9 +846,9 @@ fn foo(s: S) { test(s.$0) }
867 } 846 }
868 847
869 #[test] 848 #[test]
870 fn record_field_scores() { 849 fn record_field_relevances() {
871 mark::check!(record_field_type_match); 850 cov_mark::check!(record_field_type_match);
872 check_scores( 851 check_relevance(
873 r#" 852 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 853struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 } 854struct B { x: (), y: f32, bar: u32 }
@@ -884,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
884 } 863 }
885 864
886 #[test] 865 #[test]
887 fn record_field_and_call_scores() { 866 fn record_field_and_call_relevances() {
888 check_scores( 867 check_relevance(
889 r#" 868 r#"
890struct A { foo: i64, bar: u32, baz: u32 } 869struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 } 870struct B { x: (), y: f32, bar: u32 }
@@ -898,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
898 fd baz [] 877 fd baz []
899 "#]], 878 "#]],
900 ); 879 );
901 check_scores( 880 check_relevance(
902 r#" 881 r#"
903struct A { foo: i64, bar: u32, baz: u32 } 882struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 } 883struct B { x: (), y: f32, bar: u32 }
@@ -915,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
915 894
916 #[test] 895 #[test]
917 fn prioritize_exact_ref_match() { 896 fn prioritize_exact_ref_match() {
918 check_scores( 897 check_relevance(
919 r#" 898 r#"
920struct WorldSnapshot { _f: () }; 899struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) } 900fn go(world: &WorldSnapshot) { go(w$0) }
@@ -930,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
930 909
931 #[test] 910 #[test]
932 fn too_many_arguments() { 911 fn too_many_arguments() {
933 check_scores( 912 check_relevance(
934 r#" 913 r#"
935struct Foo; 914struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) } 915fn f(foo: &Foo) { f(foo, w$0) }
@@ -942,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
942 "#]], 921 "#]],
943 ); 922 );
944 } 923 }
924
925 #[test]
926 fn suggest_ref_mut() {
927 cov_mark::check!(suggest_ref);
928 check(
929 r#"
930struct S;
931fn foo(s: &mut S) {}
932fn main() {
933 let mut s = S;
934 foo($0);
935}
936 "#,
937 expect![[r#"
938 [
939 CompletionItem {
940 label: "S",
941 source_range: 70..70,
942 delete: 70..70,
943 insert: "S",
944 kind: SymbolKind(
945 Struct,
946 ),
947 },
948 CompletionItem {
949 label: "foo(…)",
950 source_range: 70..70,
951 delete: 70..70,
952 insert: "foo(${1:&mut s})$0",
953 kind: SymbolKind(
954 Function,
955 ),
956 lookup: "foo",
957 detail: "-> ()",
958 trigger_call_info: true,
959 },
960 CompletionItem {
961 label: "main()",
962 source_range: 70..70,
963 delete: 70..70,
964 insert: "main()$0",
965 kind: SymbolKind(
966 Function,
967 ),
968 lookup: "main",
969 detail: "-> ()",
970 },
971 CompletionItem {
972 label: "s",
973 source_range: 70..70,
974 delete: 70..70,
975 insert: "s",
976 kind: SymbolKind(
977 Local,
978 ),
979 detail: "S",
980 relevance: CompletionRelevance {
981 exact_name_match: true,
982 exact_type_match: false,
983 },
984 ref_match: "&mut ",
985 },
986 ]
987 "#]],
988 )
989 }
945} 990}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index d053a988b..6d062b3b9 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -1,7 +1,6 @@
1//! Extensions for `Builder` structure required for item rendering. 1//! Extensions for `Builder` structure required for item rendering.
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4use test_utils::mark;
5 4
6use crate::{item::Builder, CompletionContext}; 5use crate::{item::Builder, CompletionContext};
7 6
@@ -30,7 +29,7 @@ impl Builder {
30 return false; 29 return false;
31 } 30 }
32 if ctx.use_item_syntax.is_some() { 31 if ctx.use_item_syntax.is_some() {
33 mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
34 return false; 33 return false;
35 } 34 }
36 if ctx.is_pattern_call { 35 if ctx.is_pattern_call {
@@ -43,7 +42,7 @@ impl Builder {
43 // Don't add parentheses if the expected type is some function reference. 42 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type { 43 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() { 44 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed); 45 cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false; 46 return false;
48 } 47 }
49 } 48 }
@@ -53,11 +52,11 @@ impl Builder {
53 } 52 }
54 53
55 pub(super) fn add_call_parens( 54 pub(super) fn add_call_parens(
56 mut self, 55 &mut self,
57 ctx: &CompletionContext, 56 ctx: &CompletionContext,
58 name: String, 57 name: String,
59 params: Params, 58 params: Params,
60 ) -> Builder { 59 ) -> &mut Builder {
61 if !self.should_add_parens(ctx) { 60 if !self.should_add_parens(ctx) {
62 return self; 61 return self;
63 } 62 }
@@ -67,12 +66,12 @@ impl Builder {
67 None => return self, 66 None => return self,
68 }; 67 };
69 // If not an import, add parenthesis automatically. 68 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls); 69 cov_mark::hit!(inserts_parens_for_function_calls);
71 70
72 let (snippet, label) = if params.is_empty() { 71 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name)) 72 (format!("{}()$0", name), format!("{}()", name))
74 } else { 73 } else {
75 self = self.trigger_call_info(); 74 self.trigger_call_info();
76 let snippet = match (ctx.config.add_call_argument_snippets, params) { 75 let snippet = match (ctx.config.add_call_argument_snippets, params) {
77 (true, Params::Named(params)) => { 76 (true, Params::Named(params)) => {
78 let function_params_snippet = 77 let function_params_snippet =
@@ -82,7 +81,7 @@ impl Builder {
82 format!("{}({})$0", name, function_params_snippet) 81 format!("{}({})$0", name, function_params_snippet)
83 } 82 }
84 _ => { 83 _ => {
85 mark::hit!(suppress_arg_snippets); 84 cov_mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name) 85 format!("{}($0)", name)
87 } 86 }
88 }; 87 };
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index 5010b642a..8add369e4 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -36,17 +36,17 @@ impl<'a> ConstRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::Const) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::Const)
41 .set_documentation(self.ctx.docs(self.const_)) 42 .set_documentation(self.ctx.docs(self.const_))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.const_) 44 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_), 45 || self.ctx.is_deprecated_assoc_item(self.const_),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 9214193b4..e8cfcc0c7 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -3,7 +3,6 @@
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use itertools::Itertools; 5use itertools::Itertools;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -56,27 +55,26 @@ impl<'a> EnumRender<'a> {
56 } 55 }
57 56
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let mut builder = CompletionItem::new( 58 let mut item = CompletionItem::new(
60 CompletionKind::Reference, 59 CompletionKind::Reference,
61 self.ctx.source_range(), 60 self.ctx.source_range(),
62 self.qualified_name.clone(), 61 self.qualified_name.clone(),
63 ) 62 );
64 .kind(SymbolKind::Variant) 63 item.kind(SymbolKind::Variant)
65 .set_documentation(self.variant.docs(self.ctx.db())) 64 .set_documentation(self.variant.docs(self.ctx.db()))
66 .set_deprecated(self.ctx.is_deprecated(self.variant)) 65 .set_deprecated(self.ctx.is_deprecated(self.variant))
67 .add_import(import_to_add) 66 .add_import(import_to_add)
68 .detail(self.detail()); 67 .detail(self.detail());
69 68
70 if self.variant_kind == StructKind::Tuple { 69 if self.variant_kind == StructKind::Tuple {
71 mark::hit!(inserts_parens_for_tuple_enums); 70 cov_mark::hit!(inserts_parens_for_tuple_enums);
72 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73 builder = 72 item.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
74 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
75 } else if self.path.is_some() { 73 } else if self.path.is_some() {
76 builder = builder.lookup_by(self.short_qualified_name); 74 item.lookup_by(self.short_qualified_name);
77 } 75 }
78 76
79 builder.build() 77 item.build()
80 } 78 }
81 79
82 fn detail(&self) -> String { 80 fn detail(&self) -> String {
@@ -103,13 +101,11 @@ impl<'a> EnumRender<'a> {
103 101
104#[cfg(test)] 102#[cfg(test)]
105mod tests { 103mod tests {
106 use test_utils::mark;
107
108 use crate::test_utils::check_edit; 104 use crate::test_utils::check_edit;
109 105
110 #[test] 106 #[test]
111 fn inserts_parens_for_tuple_enums() { 107 fn inserts_parens_for_tuple_enums() {
112 mark::check!(inserts_parens_for_tuple_enums); 108 cov_mark::check!(inserts_parens_for_tuple_enums);
113 check_edit( 109 check_edit(
114 "Some", 110 "Some",
115 r#" 111 r#"
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index e46e21d24..f4dabe3d1 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -3,7 +3,6 @@
3use hir::{HasSource, HirDisplay, Type}; 3use hir::{HasSource, HirDisplay, Type};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::ast::Fn; 5use syntax::ast::Fn;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
@@ -42,16 +41,21 @@ impl<'a> FunctionRender<'a> {
42 41
43 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
44 let params = self.params(); 43 let params = self.params();
45 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 let mut item = CompletionItem::new(
46 .kind(self.kind()) 45 CompletionKind::Reference,
46 self.ctx.source_range(),
47 self.name.clone(),
48 );
49 item.kind(self.kind())
47 .set_documentation(self.ctx.docs(self.func)) 50 .set_documentation(self.ctx.docs(self.func))
48 .set_deprecated( 51 .set_deprecated(
49 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 52 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
50 ) 53 )
51 .detail(self.detail()) 54 .detail(self.detail())
52 .add_call_parens(self.ctx.completion, self.name, params) 55 .add_call_parens(self.ctx.completion, self.name, params)
53 .add_import(import_to_add) 56 .add_import(import_to_add);
54 .build() 57
58 item.build()
55 } 59 }
56 60
57 fn detail(&self) -> String { 61 fn detail(&self) -> String {
@@ -82,7 +86,7 @@ impl<'a> FunctionRender<'a> {
82 self.func.method_params(self.ctx.db()).unwrap_or_default() 86 self.func.method_params(self.ctx.db()).unwrap_or_default()
83 } else { 87 } else {
84 if let Some(s) = ast_params.self_param() { 88 if let Some(s) = ast_params.self_param() {
85 mark::hit!(parens_for_method_call_as_assoc_fn); 89 cov_mark::hit!(parens_for_method_call_as_assoc_fn);
86 params_pats.push(Some(s.to_string())); 90 params_pats.push(Some(s.to_string()));
87 } 91 }
88 self.func.assoc_fn_params(self.ctx.db()) 92 self.func.assoc_fn_params(self.ctx.db())
@@ -114,8 +118,6 @@ impl<'a> FunctionRender<'a> {
114 118
115#[cfg(test)] 119#[cfg(test)]
116mod tests { 120mod tests {
117 use test_utils::mark;
118
119 use crate::{ 121 use crate::{
120 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, 122 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
121 CompletionConfig, 123 CompletionConfig,
@@ -123,7 +125,7 @@ mod tests {
123 125
124 #[test] 126 #[test]
125 fn inserts_parens_for_function_calls() { 127 fn inserts_parens_for_function_calls() {
126 mark::check!(inserts_parens_for_function_calls); 128 cov_mark::check!(inserts_parens_for_function_calls);
127 check_edit( 129 check_edit(
128 "no_args", 130 "no_args",
129 r#" 131 r#"
@@ -191,7 +193,7 @@ fn bar(s: &S) {
191 193
192 #[test] 194 #[test]
193 fn parens_for_method_call_as_assoc_fn() { 195 fn parens_for_method_call_as_assoc_fn() {
194 mark::check!(parens_for_method_call_as_assoc_fn); 196 cov_mark::check!(parens_for_method_call_as_assoc_fn);
195 check_edit( 197 check_edit(
196 "foo", 198 "foo",
197 r#" 199 r#"
@@ -213,7 +215,7 @@ fn main() { S::foo(${1:&self})$0 }
213 215
214 #[test] 216 #[test]
215 fn suppress_arg_snippets() { 217 fn suppress_arg_snippets() {
216 mark::check!(suppress_arg_snippets); 218 cov_mark::check!(suppress_arg_snippets);
217 check_edit_with_config( 219 check_edit_with_config(
218 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, 220 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
219 "with_args", 221 "with_args",
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a4535786f..3fa21ba7c 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -3,7 +3,6 @@
3use hir::{Documentation, HasSource}; 3use hir::{Documentation, HasSource};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -40,29 +39,31 @@ impl<'a> MacroRender<'a> {
40 } 39 }
41 40
42 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
43 let mut builder = 42 let mut item =
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label());
45 .kind(SymbolKind::Macro) 44 item.kind(SymbolKind::Macro)
46 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
47 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
48 .add_import(import_to_add) 47 .add_import(import_to_add)
49 .set_detail(self.detail()); 48 .set_detail(self.detail());
50 49
51 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
52 builder = match self.ctx.snippet_cap() { 51 match self.ctx.snippet_cap() {
53 Some(cap) if needs_bang => { 52 Some(cap) if needs_bang => {
54 let snippet = self.snippet(); 53 let snippet = self.snippet();
55 let lookup = self.lookup(); 54 let lookup = self.lookup();
56 builder.insert_snippet(cap, snippet).lookup_by(lookup) 55 item.insert_snippet(cap, snippet).lookup_by(lookup);
56 }
57 None if needs_bang => {
58 item.insert_text(self.banged_name());
57 } 59 }
58 None if needs_bang => builder.insert_text(self.banged_name()),
59 _ => { 60 _ => {
60 mark::hit!(dont_insert_macro_call_parens_unncessary); 61 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
61 builder.insert_text(&self.name) 62 item.insert_text(&self.name);
62 } 63 }
63 }; 64 };
64 65
65 Some(builder.build()) 66 Some(item.build())
66 } 67 }
67 68
68 fn needs_bang(&self) -> bool { 69 fn needs_bang(&self) -> bool {
@@ -125,13 +126,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
125 126
126#[cfg(test)] 127#[cfg(test)]
127mod tests { 128mod tests {
128 use test_utils::mark;
129
130 use crate::test_utils::check_edit; 129 use crate::test_utils::check_edit;
131 130
132 #[test] 131 #[test]
133 fn dont_insert_macro_call_parens_unncessary() { 132 fn dont_insert_macro_call_parens_unncessary() {
134 mark::check!(dont_insert_macro_call_parens_unncessary); 133 cov_mark::check!(dont_insert_macro_call_parens_unncessary);
135 check_edit( 134 check_edit(
136 "frobnicate!", 135 "frobnicate!",
137 r#" 136 r#"
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index 465dfe00c..ca2926125 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -69,19 +69,19 @@ fn build_completion(
69 ctx: RenderContext<'_>, 69 ctx: RenderContext<'_>,
70 name: String, 70 name: String,
71 pat: String, 71 pat: String,
72 item: impl HasAttrs + Copy, 72 def: impl HasAttrs + Copy,
73) -> CompletionItem { 73) -> CompletionItem {
74 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) 74 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name);
75 .kind(CompletionItemKind::Binding) 75 item.kind(CompletionItemKind::Binding)
76 .set_documentation(ctx.docs(item)) 76 .set_documentation(ctx.docs(def))
77 .set_deprecated(ctx.is_deprecated(item)) 77 .set_deprecated(ctx.is_deprecated(def))
78 .detail(&pat); 78 .detail(&pat);
79 let completion = if let Some(snippet_cap) = ctx.snippet_cap() { 79 if let Some(snippet_cap) = ctx.snippet_cap() {
80 completion.insert_snippet(snippet_cap, pat) 80 item.insert_snippet(snippet_cap, pat);
81 } else { 81 } else {
82 completion.insert_text(pat) 82 item.insert_text(pat);
83 }; 83 };
84 completion.build() 84 item.build()
85} 85}
86 86
87fn render_pat( 87fn render_pat(
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index bd97c3692..e47b4c745 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -36,17 +36,17 @@ impl<'a> TypeAliasRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::TypeAlias) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::TypeAlias)
41 .set_documentation(self.ctx.docs(self.type_alias)) 42 .set_documentation(self.ctx.docs(self.type_alias))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias) 44 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias), 45 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index baff83305..9da844031 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -25,6 +25,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
25 insert_use: InsertUseConfig { 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full), 26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain, 27 prefix_kind: PrefixKind::Plain,
28 group: true,
28 }, 29 },
29}; 30};
30 31
@@ -119,7 +120,7 @@ pub(crate) fn check_edit_with_config(
119 120
120 let mut combined_edit = completion.text_edit().to_owned(); 121 let mut combined_edit = completion.text_edit().to_owned();
121 if let Some(import_text_edit) = 122 if let Some(import_text_edit) =
122 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge)) 123 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use))
123 { 124 {
124 combined_edit.union(import_text_edit).expect( 125 combined_edit.union(import_text_edit).expect(
125 "Failed to apply completion resolve changes: change ranges overlap, but should not", 126 "Failed to apply completion resolve changes: change ranges overlap, but should not",