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/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs280
-rw-r--r--crates/ide_completion/src/completions/keyword.rs14
-rw-r--r--crates/ide_completion/src/completions/postfix.rs14
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs6
-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.rs134
-rw-r--r--crates/ide_completion/src/lib.rs33
-rw-r--r--crates/ide_completion/src/render.rs218
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs9
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs7
-rw-r--r--crates/ide_completion/src/render/function.rs11
-rw-r--r--crates/ide_completion/src/render/macro_.rs7
-rw-r--r--crates/ide_completion/src/test_utils.rs3
16 files changed, 504 insertions, 257 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/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/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 03c6dd454..80aa9fb06 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
@@ -47,11 +46,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
47 46
48pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 47pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
49 if ctx.token.kind() == SyntaxKind::COMMENT { 48 if ctx.token.kind() == SyntaxKind::COMMENT {
50 mark::hit!(no_keyword_completion_in_comments); 49 cov_mark::hit!(no_keyword_completion_in_comments);
51 return; 50 return;
52 } 51 }
53 if ctx.record_lit_syntax.is_some() { 52 if ctx.record_lit_syntax.is_some() {
54 mark::hit!(no_keyword_completion_in_record_lit); 53 cov_mark::hit!(no_keyword_completion_in_record_lit);
55 return; 54 return;
56 } 55 }
57 56
@@ -172,7 +171,7 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
172 Some(cap) => { 171 Some(cap) => {
173 let tmp; 172 let tmp;
174 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 173 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
175 mark::hit!(let_semi); 174 cov_mark::hit!(let_semi);
176 tmp = format!("{};", snippet); 175 tmp = format!("{};", snippet);
177 &tmp 176 &tmp
178 } else { 177 } else {
@@ -188,7 +187,6 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
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/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 9c34ed0b6..d45ad7944 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),
@@ -325,6 +335,7 @@ fn main() {
325 sn match match expr {} 335 sn match match expr {}
326 sn box Box::new(expr) 336 sn box Box::new(expr)
327 sn ok Ok(expr) 337 sn ok Ok(expr)
338 sn err Err(expr)
328 sn some Some(expr) 339 sn some Some(expr)
329 sn dbg dbg!(expr) 340 sn dbg dbg!(expr)
330 sn dbgr dbg!(&expr) 341 sn dbgr dbg!(&expr)
@@ -357,6 +368,7 @@ fn main() {
357 sn match match expr {} 368 sn match match expr {}
358 sn box Box::new(expr) 369 sn box Box::new(expr)
359 sn ok Ok(expr) 370 sn ok Ok(expr)
371 sn err Err(expr)
360 sn some Some(expr) 372 sn some Some(expr)
361 sn dbg dbg!(expr) 373 sn dbg dbg!(expr)
362 sn dbgr dbg!(&expr) 374 sn dbgr dbg!(&expr)
@@ -380,6 +392,7 @@ fn main() {
380 sn match match expr {} 392 sn match match expr {}
381 sn box Box::new(expr) 393 sn box Box::new(expr)
382 sn ok Ok(expr) 394 sn ok Ok(expr)
395 sn err Err(expr)
383 sn some Some(expr) 396 sn some Some(expr)
384 sn dbg dbg!(expr) 397 sn dbg dbg!(expr)
385 sn dbgr dbg!(&expr) 398 sn dbgr dbg!(&expr)
@@ -408,6 +421,7 @@ fn main() {
408 sn match match expr {} 421 sn match match expr {}
409 sn box Box::new(expr) 422 sn box Box::new(expr)
410 sn ok Ok(expr) 423 sn ok Ok(expr)
424 sn err Err(expr)
411 sn some Some(expr) 425 sn some Some(expr)
412 sn dbg dbg!(expr) 426 sn dbg dbg!(expr)
413 sn dbgr dbg!(&expr) 427 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/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..14afec603 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: Relevance,
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,11 @@ 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 if self.relevance.is_relevant() {
104 s.field("score", score); 111 s.field("relevance", &self.relevance);
112 }
113 if let Some(mutability) = &self.ref_match {
114 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
105 } 115 }
106 if self.trigger_call_info { 116 if self.trigger_call_info {
107 s.field("trigger_call_info", &true); 117 s.field("trigger_call_info", &true);
@@ -118,6 +128,36 @@ pub enum CompletionScore {
118 TypeAndNameMatch, 128 TypeAndNameMatch,
119} 129}
120 130
131#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132pub struct Relevance {
133 /// This is set in cases like these:
134 ///
135 /// ```
136 /// fn f(spam: String) {}
137 /// fn main {
138 /// let spam = 92;
139 /// f($0) // name of local matches the name of param
140 /// }
141 /// ```
142 pub exact_name_match: bool,
143 /// This is set in cases like these:
144 ///
145 /// ```
146 /// fn f(spam: String) {}
147 /// fn main {
148 /// let foo = String::new();
149 /// f($0) // type of local matches the type of param
150 /// }
151 /// ```
152 pub exact_type_match: bool,
153}
154
155impl Relevance {
156 pub fn is_relevant(&self) -> bool {
157 self != &Relevance::default()
158 }
159}
160
121#[derive(Debug, Clone, Copy, PartialEq, Eq)] 161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum CompletionItemKind { 162pub enum CompletionItemKind {
123 SymbolKind(SymbolKind), 163 SymbolKind(SymbolKind),
@@ -207,9 +247,9 @@ impl CompletionItem {
207 lookup: None, 247 lookup: None,
208 kind: None, 248 kind: None,
209 text_edit: None, 249 text_edit: None,
210 deprecated: None, 250 deprecated: false,
211 trigger_call_info: None, 251 trigger_call_info: None,
212 score: None, 252 relevance: Relevance::default(),
213 ref_match: None, 253 ref_match: None,
214 import_to_add: None, 254 import_to_add: None,
215 } 255 }
@@ -252,15 +292,15 @@ impl CompletionItem {
252 self.deprecated 292 self.deprecated
253 } 293 }
254 294
255 pub fn score(&self) -> Option<CompletionScore> { 295 pub fn relevance(&self) -> Relevance {
256 self.score 296 self.relevance
257 } 297 }
258 298
259 pub fn trigger_call_info(&self) -> bool { 299 pub fn trigger_call_info(&self) -> bool {
260 self.trigger_call_info 300 self.trigger_call_info
261 } 301 }
262 302
263 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 303 pub fn ref_match(&self) -> Option<Mutability> {
264 self.ref_match 304 self.ref_match
265 } 305 }
266 306
@@ -272,22 +312,18 @@ impl CompletionItem {
272/// An extra import to add after the completion is applied. 312/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)] 313#[derive(Debug, Clone)]
274pub struct ImportEdit { 314pub struct ImportEdit {
275 pub import_path: ModPath, 315 pub import: LocatedImport,
276 pub import_scope: ImportScope, 316 pub scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278} 317}
279 318
280impl ImportEdit { 319impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit. 320 /// 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. 321 /// 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> { 322 pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit"); 323 let _p = profile::span("ImportEdit::to_text_edit");
285 324
286 let rewriter = insert_use::insert_use( 325 let rewriter =
287 &self.import_scope, 326 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()?; 327 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder(); 328 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 329 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -311,10 +347,10 @@ pub(crate) struct Builder {
311 lookup: Option<String>, 347 lookup: Option<String>,
312 kind: Option<CompletionItemKind>, 348 kind: Option<CompletionItemKind>,
313 text_edit: Option<TextEdit>, 349 text_edit: Option<TextEdit>,
314 deprecated: Option<bool>, 350 deprecated: bool,
315 trigger_call_info: Option<bool>, 351 trigger_call_info: Option<bool>,
316 score: Option<CompletionScore>, 352 relevance: Relevance,
317 ref_match: Option<(Mutability, CompletionScore)>, 353 ref_match: Option<Mutability>,
318} 354}
319 355
320impl Builder { 356impl Builder {
@@ -325,20 +361,19 @@ impl Builder {
325 let mut lookup = self.lookup; 361 let mut lookup = self.lookup;
326 let mut insert_text = self.insert_text; 362 let mut insert_text = self.insert_text;
327 363
328 if let Some(import_to_add) = self.import_to_add.as_ref() { 364 if let Some(original_path) = self
329 if import_to_add.import_for_trait_assoc_item { 365 .import_to_add
330 lookup = lookup.or_else(|| Some(label.clone())); 366 .as_ref()
331 insert_text = insert_text.or_else(|| Some(label.clone())); 367 .and_then(|import_edit| import_edit.import.original_path.as_ref())
332 label = format!("{} ({})", label, import_to_add.import_path); 368 {
369 lookup = lookup.or_else(|| Some(label.clone()));
370 insert_text = insert_text.or_else(|| Some(label.clone()));
371
372 let original_path_label = original_path.to_string();
373 if original_path_label.ends_with(&label) {
374 label = original_path_label;
333 } else { 375 } else {
334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 376 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 } 377 }
343 } 378 }
344 379
@@ -359,9 +394,9 @@ impl Builder {
359 lookup, 394 lookup,
360 kind: self.kind, 395 kind: self.kind,
361 completion_kind: self.completion_kind, 396 completion_kind: self.completion_kind,
362 deprecated: self.deprecated.unwrap_or(false), 397 deprecated: self.deprecated,
363 trigger_call_info: self.trigger_call_info.unwrap_or(false), 398 trigger_call_info: self.trigger_call_info.unwrap_or(false),
364 score: self.score, 399 relevance: self.relevance,
365 ref_match: self.ref_match, 400 ref_match: self.ref_match,
366 import_to_add: self.import_to_add, 401 import_to_add: self.import_to_add,
367 } 402 }
@@ -419,11 +454,11 @@ impl Builder {
419 self 454 self
420 } 455 }
421 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 456 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
422 self.deprecated = Some(deprecated); 457 self.deprecated = deprecated;
423 self 458 self
424 } 459 }
425 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { 460 pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder {
426 self.score = Some(score); 461 self.relevance = relevance;
427 self 462 self
428 } 463 }
429 pub(crate) fn trigger_call_info(mut self) -> Builder { 464 pub(crate) fn trigger_call_info(mut self) -> Builder {
@@ -434,17 +469,8 @@ impl Builder {
434 self.import_to_add = import_to_add; 469 self.import_to_add = import_to_add;
435 self 470 self
436 } 471 }
437 pub(crate) fn set_ref_match( 472 pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder {
438 mut self, 473 self.ref_match = Some(mutability);
439 ref_match: Option<(Mutability, CompletionScore)>,
440 ) -> Builder {
441 self.ref_match = ref_match;
442 self 474 self
443 } 475 }
444} 476}
445
446impl<'a> Into<CompletionItem> for Builder {
447 fn into(self) -> CompletionItem {
448 self.build()
449 }
450}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 76f31de9e..d46f521a0 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,10 @@ 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::{
27 CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
28 Relevance,
29 },
25}; 30};
26 31
27//FIXME: split the following feature into fine-grained features. 32//FIXME: split the following feature into fine-grained features.
@@ -139,25 +144,27 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 144 position: FilePosition,
140 full_import_path: &str, 145 full_import_path: &str,
141 imported_name: String, 146 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> { 147) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?; 148 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?; 149 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; 150 let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147 151
148 let current_module = ctx.sema.scope(position_for_import).module()?; 152 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate(); 153 let current_crate = current_module.krate();
150 154
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 155 let (import_path, item_to_import) =
152 .filter_map(|candidate| { 156 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 157 .into_iter()
154 current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) 158 .filter_map(|candidate| {
155 }) 159 current_module
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 160 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
161 .zip(Some(candidate))
162 })
163 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
164 let import =
165 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
157 166
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 167 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} 168}
162 169
163#[cfg(test)] 170#[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..8c8b149a1 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::{ImportEdit, Relevance},
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
@@ -151,8 +155,8 @@ impl<'a> Render<'a> {
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 = item.set_relevance(relevance);
156 } 160 }
157 161
158 item.build() 162 item.build()
@@ -242,16 +246,23 @@ impl<'a> Render<'a> {
242 } 246 }
243 }; 247 };
244 248
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution { 249 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 250 let ty = local.ty(self.ctx.db());
248 let ty = local.ty(self.ctx.db()); 251 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
249 if let Some(score) = 252 item = item.set_relevance(relevance)
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 253 }
251 { 254 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
252 item = item.set_score(score); 255 if let Some(ty_without_ref) = expected_type.remove_ref() {
256 if ty_without_ref == ty {
257 cov_mark::hit!(suggest_ref);
258 let mutability = if expected_type.is_mutable_reference() {
259 Mutability::Mut
260 } else {
261 Mutability::Shared
262 };
263 item = item.ref_match(mutability)
264 }
253 } 265 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 } 266 }
256 } 267 }
257 268
@@ -269,7 +280,7 @@ impl<'a> Render<'a> {
269 _ => false, 280 _ => false,
270 }; 281 };
271 if has_non_default_type_params { 282 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 283 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 284 item = item
274 .lookup_by(local_name.clone()) 285 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 286 .label(format!("{}<…>", local_name))
@@ -281,7 +292,6 @@ impl<'a> Render<'a> {
281 Some( 292 Some(
282 item.kind(kind) 293 item.kind(kind)
283 .add_import(import_to_add) 294 .add_import(import_to_add)
284 .set_ref_match(ref_match)
285 .set_documentation(self.docs(resolution)) 295 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution)) 296 .set_deprecated(self.is_deprecated(resolution))
287 .build(), 297 .build(),
@@ -313,56 +323,23 @@ impl<'a> Render<'a> {
313 } 323 }
314} 324}
315 325
316fn compute_score_from_active( 326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> {
317 active_type: &Type, 327 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
318 active_name: &str, 328 let mut res = Relevance::default();
319 ty: &Type, 329 res.exact_type_match = ty == &expected_type;
320 name: &str, 330 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) 331 Some(res)
336} 332}
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 333
356#[cfg(test)] 334#[cfg(test)]
357mod tests { 335mod tests {
358 use std::cmp::Reverse; 336 use std::cmp::Reverse;
359 337
360 use expect_test::{expect, Expect}; 338 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 339
363 use crate::{ 340 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 341 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore, 342 CompletionKind, Relevance,
366 }; 343 };
367 344
368 fn check(ra_fixture: &str, expect: Expect) { 345 fn check(ra_fixture: &str, expect: Expect) {
@@ -370,24 +347,25 @@ mod tests {
370 expect.assert_debug_eq(&actual); 347 expect.assert_debug_eq(&actual);
371 } 348 }
372 349
373 fn check_scores(ra_fixture: &str, expect: Expect) { 350 fn check_relevance(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str { 351 fn display_relevance(relevance: Relevance) -> &'static str {
375 match score { 352 match relevance {
376 Some(CompletionScore::TypeMatch) => "[type]", 353 Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]",
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 354 Relevance { exact_type_match: true, exact_name_match: false } => "[type]",
378 None => "[]".into(), 355 Relevance { exact_type_match: false, exact_name_match: true } => "[name]",
356 Relevance { exact_type_match: false, exact_name_match: false } => "[]",
379 } 357 }
380 } 358 }
381 359
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 360 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 361 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
384 let actual = completions 362 let actual = completions
385 .into_iter() 363 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference) 364 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| { 365 .map(|it| {
388 let tag = it.kind().unwrap().tag(); 366 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score()); 367 let relevance = display_relevance(it.relevance());
390 format!("{} {} {}\n", tag, it.label(), score) 368 format!("{} {} {}\n", tag, it.label(), relevance)
391 }) 369 })
392 .collect::<String>(); 370 .collect::<String>();
393 expect.assert_eq(&actual); 371 expect.assert_eq(&actual);
@@ -734,7 +712,7 @@ fn foo(s: S) { s.$0 }
734 712
735 #[test] 713 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 714 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 715 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 716 check_edit(
739 "foo", 717 "foo",
740 r#" 718 r#"
@@ -758,7 +736,7 @@ fn main() -> ManualVtable {
758 736
759 #[test] 737 #[test]
760 fn no_parens_in_use_item() { 738 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 739 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 740 check_edit(
763 "foo", 741 "foo",
764 r#" 742 r#"
@@ -802,7 +780,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 780
803 #[test] 781 #[test]
804 fn inserts_angle_brackets_for_generics() { 782 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 783 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 784 check_edit(
807 "Vec", 785 "Vec",
808 r#" 786 r#"
@@ -850,9 +828,9 @@ fn foo(xs: Vec<i128>)
850 } 828 }
851 829
852 #[test] 830 #[test]
853 fn active_param_score() { 831 fn active_param_relevance() {
854 mark::check!(active_param_type_match); 832 cov_mark::check!(active_param_type_match);
855 check_scores( 833 check_relevance(
856 r#" 834 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 835struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { } 836fn test(bar: u32) { }
@@ -867,9 +845,9 @@ fn foo(s: S) { test(s.$0) }
867 } 845 }
868 846
869 #[test] 847 #[test]
870 fn record_field_scores() { 848 fn record_field_relevances() {
871 mark::check!(record_field_type_match); 849 cov_mark::check!(record_field_type_match);
872 check_scores( 850 check_relevance(
873 r#" 851 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 852struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 } 853struct B { x: (), y: f32, bar: u32 }
@@ -884,8 +862,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
884 } 862 }
885 863
886 #[test] 864 #[test]
887 fn record_field_and_call_scores() { 865 fn record_field_and_call_relevances() {
888 check_scores( 866 check_relevance(
889 r#" 867 r#"
890struct A { foo: i64, bar: u32, baz: u32 } 868struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 } 869struct B { x: (), y: f32, bar: u32 }
@@ -898,7 +876,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
898 fd baz [] 876 fd baz []
899 "#]], 877 "#]],
900 ); 878 );
901 check_scores( 879 check_relevance(
902 r#" 880 r#"
903struct A { foo: i64, bar: u32, baz: u32 } 881struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 } 882struct B { x: (), y: f32, bar: u32 }
@@ -915,7 +893,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
915 893
916 #[test] 894 #[test]
917 fn prioritize_exact_ref_match() { 895 fn prioritize_exact_ref_match() {
918 check_scores( 896 check_relevance(
919 r#" 897 r#"
920struct WorldSnapshot { _f: () }; 898struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) } 899fn go(world: &WorldSnapshot) { go(w$0) }
@@ -930,7 +908,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
930 908
931 #[test] 909 #[test]
932 fn too_many_arguments() { 910 fn too_many_arguments() {
933 check_scores( 911 check_relevance(
934 r#" 912 r#"
935struct Foo; 913struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) } 914fn f(foo: &Foo) { f(foo, w$0) }
@@ -942,4 +920,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
942 "#]], 920 "#]],
943 ); 921 );
944 } 922 }
923
924 #[test]
925 fn suggest_ref_mut() {
926 cov_mark::check!(suggest_ref);
927 check(
928 r#"
929struct S;
930fn foo(s: &mut S) {}
931fn main() {
932 let mut s = S;
933 foo($0);
934}
935 "#,
936 expect![[r#"
937 [
938 CompletionItem {
939 label: "S",
940 source_range: 70..70,
941 delete: 70..70,
942 insert: "S",
943 kind: SymbolKind(
944 Struct,
945 ),
946 },
947 CompletionItem {
948 label: "foo(…)",
949 source_range: 70..70,
950 delete: 70..70,
951 insert: "foo(${1:&mut s})$0",
952 kind: SymbolKind(
953 Function,
954 ),
955 lookup: "foo",
956 detail: "-> ()",
957 trigger_call_info: true,
958 },
959 CompletionItem {
960 label: "main()",
961 source_range: 70..70,
962 delete: 70..70,
963 insert: "main()$0",
964 kind: SymbolKind(
965 Function,
966 ),
967 lookup: "main",
968 detail: "-> ()",
969 },
970 CompletionItem {
971 label: "s",
972 source_range: 70..70,
973 delete: 70..70,
974 insert: "s",
975 kind: SymbolKind(
976 Local,
977 ),
978 detail: "S",
979 relevance: Relevance {
980 exact_name_match: true,
981 exact_type_match: false,
982 },
983 ref_match: "&mut ",
984 },
985 ]
986 "#]],
987 )
988 }
945} 989}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index d053a988b..95a7596c1 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 }
@@ -67,7 +66,7 @@ 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))
@@ -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/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 9214193b4..ed055c1fb 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},
@@ -68,7 +67,7 @@ impl<'a> EnumRender<'a> {
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 builder =
74 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params); 73 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
@@ -103,13 +102,11 @@ impl<'a> EnumRender<'a> {
103 102
104#[cfg(test)] 103#[cfg(test)]
105mod tests { 104mod tests {
106 use test_utils::mark;
107
108 use crate::test_utils::check_edit; 105 use crate::test_utils::check_edit;
109 106
110 #[test] 107 #[test]
111 fn inserts_parens_for_tuple_enums() { 108 fn inserts_parens_for_tuple_enums() {
112 mark::check!(inserts_parens_for_tuple_enums); 109 cov_mark::check!(inserts_parens_for_tuple_enums);
113 check_edit( 110 check_edit(
114 "Some", 111 "Some",
115 r#" 112 r#"
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index e46e21d24..5931945a8 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},
@@ -82,7 +81,7 @@ impl<'a> FunctionRender<'a> {
82 self.func.method_params(self.ctx.db()).unwrap_or_default() 81 self.func.method_params(self.ctx.db()).unwrap_or_default()
83 } else { 82 } else {
84 if let Some(s) = ast_params.self_param() { 83 if let Some(s) = ast_params.self_param() {
85 mark::hit!(parens_for_method_call_as_assoc_fn); 84 cov_mark::hit!(parens_for_method_call_as_assoc_fn);
86 params_pats.push(Some(s.to_string())); 85 params_pats.push(Some(s.to_string()));
87 } 86 }
88 self.func.assoc_fn_params(self.ctx.db()) 87 self.func.assoc_fn_params(self.ctx.db())
@@ -114,8 +113,6 @@ impl<'a> FunctionRender<'a> {
114 113
115#[cfg(test)] 114#[cfg(test)]
116mod tests { 115mod tests {
117 use test_utils::mark;
118
119 use crate::{ 116 use crate::{
120 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, 117 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
121 CompletionConfig, 118 CompletionConfig,
@@ -123,7 +120,7 @@ mod tests {
123 120
124 #[test] 121 #[test]
125 fn inserts_parens_for_function_calls() { 122 fn inserts_parens_for_function_calls() {
126 mark::check!(inserts_parens_for_function_calls); 123 cov_mark::check!(inserts_parens_for_function_calls);
127 check_edit( 124 check_edit(
128 "no_args", 125 "no_args",
129 r#" 126 r#"
@@ -191,7 +188,7 @@ fn bar(s: &S) {
191 188
192 #[test] 189 #[test]
193 fn parens_for_method_call_as_assoc_fn() { 190 fn parens_for_method_call_as_assoc_fn() {
194 mark::check!(parens_for_method_call_as_assoc_fn); 191 cov_mark::check!(parens_for_method_call_as_assoc_fn);
195 check_edit( 192 check_edit(
196 "foo", 193 "foo",
197 r#" 194 r#"
@@ -213,7 +210,7 @@ fn main() { S::foo(${1:&self})$0 }
213 210
214 #[test] 211 #[test]
215 fn suppress_arg_snippets() { 212 fn suppress_arg_snippets() {
216 mark::check!(suppress_arg_snippets); 213 cov_mark::check!(suppress_arg_snippets);
217 check_edit_with_config( 214 check_edit_with_config(
218 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, 215 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
219 "with_args", 216 "with_args",
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a4535786f..a6cf3e479 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},
@@ -57,7 +56,7 @@ impl<'a> MacroRender<'a> {
57 } 56 }
58 None if needs_bang => builder.insert_text(self.banged_name()), 57 None if needs_bang => builder.insert_text(self.banged_name()),
59 _ => { 58 _ => {
60 mark::hit!(dont_insert_macro_call_parens_unncessary); 59 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
61 builder.insert_text(&self.name) 60 builder.insert_text(&self.name)
62 } 61 }
63 }; 62 };
@@ -125,13 +124,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
125 124
126#[cfg(test)] 125#[cfg(test)]
127mod tests { 126mod tests {
128 use test_utils::mark;
129
130 use crate::test_utils::check_edit; 127 use crate::test_utils::check_edit;
131 128
132 #[test] 129 #[test]
133 fn dont_insert_macro_call_parens_unncessary() { 130 fn dont_insert_macro_call_parens_unncessary() {
134 mark::check!(dont_insert_macro_call_parens_unncessary); 131 cov_mark::check!(dont_insert_macro_call_parens_unncessary);
135 check_edit( 132 check_edit(
136 "frobnicate!", 133 "frobnicate!",
137 r#" 134 r#"
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",