aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src')
-rw-r--r--crates/completion/src/completions.rs1
-rw-r--r--crates/completion/src/completions/flyimport.rs291
-rw-r--r--crates/completion/src/completions/keyword.rs2
-rw-r--r--crates/completion/src/completions/unqualified_path.rs251
-rw-r--r--crates/completion/src/config.rs4
-rw-r--r--crates/completion/src/item.rs4
-rw-r--r--crates/completion/src/lib.rs5
-rw-r--r--crates/completion/src/render.rs15
-rw-r--r--crates/completion/src/test_utils.rs14
9 files changed, 325 insertions, 262 deletions
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 00c9e76f0..c3ce6e51d 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -13,6 +13,7 @@ pub(crate) mod postfix;
13pub(crate) mod macro_in_item_position; 13pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl; 14pub(crate) mod trait_impl;
15pub(crate) mod mod_; 15pub(crate) mod mod_;
16pub(crate) mod flyimport;
16 17
17use hir::{ModPath, ScopeDef, Type}; 18use hir::{ModPath, ScopeDef, Type};
18 19
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
new file mode 100644
index 000000000..222809638
--- /dev/null
+++ b/crates/completion/src/completions/flyimport.rs
@@ -0,0 +1,291 @@
1//! Feature: completion with imports-on-the-fly
2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input
5//! (case-insensitive, in any order or places).
6//!
7//! ```
8//! fn main() {
9//! pda$0
10//! }
11//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
12//! ```
13//! ->
14//! ```
15//! use std::marker::PhantomData;
16//!
17//! fn main() {
18//! PhantomData
19//! }
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ```
22//!
23//! .Fuzzy search details
24//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
26//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
27//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
28//!
29//! .Import configuration
30//!
31//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
32//! Mimics the corresponding behavior of the `Auto Import` feature.
33//!
34//! .LSP and performance implications
35//!
36//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
37//! (case sensitive) resolve client capability in its client capabilities.
38//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
39//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
40//! which might be slow ergo the feature is automatically disabled.
41//!
42//! .Feature toggle
43//!
44//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
46//! capability enabled.
47
48use either::Either;
49use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator};
51use syntax::AstNode;
52use test_utils::mark;
53
54use crate::{
55 context::CompletionContext,
56 render::{render_resolution_with_import, RenderContext},
57 ImportEdit,
58};
59
60use super::Completions;
61
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions {
64 return None;
65 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None;
68 }
69 let potential_import_name = ctx.token.to_string();
70 if potential_import_name.len() < 2 {
71 return None;
72 }
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
74
75 let current_module = ctx.scope.module()?;
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78
79 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports(
81 &ctx.sema,
82 ctx.krate?,
83 Some(40),
84 potential_import_name,
85 true,
86 true,
87 )
88 .filter_map(|import_candidate| {
89 Some(match import_candidate {
90 Either::Left(module_def) => {
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
92 }
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 })
97 })
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 });
104
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import(
107 RenderContext::new(ctx),
108 ImportEdit { import_path, import_scope: import_scope.clone() },
109 &definition,
110 )
111 }));
112 Some(())
113}
114
115fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str,
118) -> usize {
119 mark::hit!(certain_fuzzy_order_test);
120 let proposed_import_name = match proposed_mod_path.segments.last() {
121 Some(name) => name.to_string().to_lowercase(),
122 None => return usize::MAX,
123 };
124 match proposed_import_name.match_indices(user_input_lowercased).next() {
125 Some((first_matching_index, _)) => first_matching_index,
126 None => usize::MAX,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use expect_test::{expect, Expect};
133 use test_utils::mark;
134
135 use crate::{
136 item::CompletionKind,
137 test_utils::{check_edit, completion_list},
138 };
139
140 fn check(ra_fixture: &str, expect: Expect) {
141 let actual = completion_list(ra_fixture, CompletionKind::Magic);
142 expect.assert_eq(&actual);
143 }
144
145 #[test]
146 fn function_fuzzy_completion() {
147 check_edit(
148 "stdin",
149 r#"
150//- /lib.rs crate:dep
151pub mod io {
152 pub fn stdin() {}
153};
154
155//- /main.rs crate:main deps:dep
156fn main() {
157 stdi$0
158}
159"#,
160 r#"
161use dep::io::stdin;
162
163fn main() {
164 stdin()$0
165}
166"#,
167 );
168 }
169
170 #[test]
171 fn macro_fuzzy_completion() {
172 check_edit(
173 "macro_with_curlies!",
174 r#"
175//- /lib.rs crate:dep
176/// Please call me as macro_with_curlies! {}
177#[macro_export]
178macro_rules! macro_with_curlies {
179 () => {}
180}
181
182//- /main.rs crate:main deps:dep
183fn main() {
184 curli$0
185}
186"#,
187 r#"
188use dep::macro_with_curlies;
189
190fn main() {
191 macro_with_curlies! {$0}
192}
193"#,
194 );
195 }
196
197 #[test]
198 fn struct_fuzzy_completion() {
199 check_edit(
200 "ThirdStruct",
201 r#"
202//- /lib.rs crate:dep
203pub struct FirstStruct;
204pub mod some_module {
205 pub struct SecondStruct;
206 pub struct ThirdStruct;
207}
208
209//- /main.rs crate:main deps:dep
210use dep::{FirstStruct, some_module::SecondStruct};
211
212fn main() {
213 this$0
214}
215"#,
216 r#"
217use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
218
219fn main() {
220 ThirdStruct
221}
222"#,
223 );
224 }
225
226 #[test]
227 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test);
229 check(
230 r#"
231//- /lib.rs crate:dep
232pub struct FirstStruct;
233pub mod some_module {
234 // already imported, omitted
235 pub struct SecondStruct;
236 // does not contain all letters from the query, omitted
237 pub struct UnrelatedOne;
238 // contains all letters from the query, but not in sequence, displayed last
239 pub struct ThiiiiiirdStruct;
240 // contains all letters from the query, but not in the beginning, displayed second
241 pub struct AfterThirdStruct;
242 // contains all letters from the query in the begginning, displayed first
243 pub struct ThirdStruct;
244}
245
246//- /main.rs crate:main deps:dep
247use dep::{FirstStruct, some_module::SecondStruct};
248
249fn main() {
250 hir$0
251}
252"#,
253 expect![[r#"
254 st dep::some_module::ThirdStruct
255 st dep::some_module::AfterThirdStruct
256 st dep::some_module::ThiiiiiirdStruct
257 "#]],
258 );
259 }
260
261 #[test]
262 fn does_not_propose_names_in_scope() {
263 check(
264 r#"
265//- /lib.rs crate:dep
266pub mod test_mod {
267 pub trait TestTrait {
268 const SPECIAL_CONST: u8;
269 type HumbleType;
270 fn weird_function();
271 fn random_method(&self);
272 }
273 pub struct TestStruct {}
274 impl TestTrait for TestStruct {
275 const SPECIAL_CONST: u8 = 42;
276 type HumbleType = ();
277 fn weird_function() {}
278 fn random_method(&self) {}
279 }
280}
281
282//- /main.rs crate:main deps:dep
283use dep::test_mod::TestStruct;
284fn main() {
285 TestSt$0
286}
287"#,
288 expect![[r#""#]],
289 );
290 }
291}
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
index c1af348dc..47e146128 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -99,7 +99,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
99 add_keyword(ctx, acc, "else if", "else if $0 {}"); 99 add_keyword(ctx, acc, "else if", "else if $0 {}");
100 } 100 }
101 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 101 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
102 add_keyword(ctx, acc, "mod", "mod $0 {}"); 102 add_keyword(ctx, acc, "mod", "mod $0");
103 } 103 }
104 if ctx.bind_pat_parent || ctx.ref_pat_parent { 104 if ctx.bind_pat_parent || ctx.ref_pat_parent {
105 add_keyword(ctx, acc, "mut", "mut "); 105 add_keyword(ctx, acc, "mut", "mut ");
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 53e1391f3..ac5596ca4 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -2,17 +2,11 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use either::Either; 5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type};
7use ide_db::helpers::insert_use::ImportScope;
8use ide_db::imports_locator;
9use syntax::AstNode; 6use syntax::AstNode;
10use test_utils::mark; 7use test_utils::mark;
11 8
12use crate::{ 9use crate::{CompletionContext, Completions};
13 render::{render_resolution_with_import, RenderContext},
14 CompletionContext, Completions, ImportEdit,
15};
16 10
17pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 11pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
18 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 12 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -45,10 +39,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
45 } 39 }
46 acc.add_resolution(ctx, name.to_string(), &res) 40 acc.add_resolution(ctx, name.to_string(), &res)
47 }); 41 });
48
49 if ctx.config.enable_autoimport_completions {
50 fuzzy_completion(acc, ctx);
51 }
52} 42}
53 43
54fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 44fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
@@ -77,124 +67,13 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
77 } 67 }
78} 68}
79 69
80// Feature: Fuzzy Completion and Autoimports
81//
82// When completing names in the current scope, proposes additional imports from other modules or crates,
83// if they can be qualified in the scope and their name contains all symbols from the completion input
84// (case-insensitive, in any order or places).
85//
86// ```
87// fn main() {
88// pda$0
89// }
90// # pub mod std { pub mod marker { pub struct PhantomData { } } }
91// ```
92// ->
93// ```
94// use std::marker::PhantomData;
95//
96// fn main() {
97// PhantomData
98// }
99// # pub mod std { pub mod marker { pub struct PhantomData { } } }
100// ```
101//
102// .Fuzzy search details
103//
104// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
105// (i.e. in `HashMap` in the `std::collections::HashMap` path).
106// For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
107//
108// .Merge Behavior
109//
110// It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
111// Mimics the corresponding behavior of the `Auto Import` feature.
112//
113// .LSP and performance implications
114//
115// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
116// (case sensitive) resolve client capability in its client capabilities.
117// This way the server is able to defer the costly computations, doing them for a selected completion item only.
118// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
119// which might be slow ergo the feature is automatically disabled.
120//
121// .Feature toggle
122//
123// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
125// capability enabled.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
127 let potential_import_name = ctx.token.to_string();
128 let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone());
129
130 if potential_import_name.len() < 2 {
131 return None;
132 }
133
134 let current_module = ctx.scope.module()?;
135 let anchor = ctx.name_ref_syntax.as_ref()?;
136 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
137
138 let user_input_lowercased = potential_import_name.to_lowercase();
139 let mut all_mod_paths = imports_locator::find_similar_imports(
140 &ctx.sema,
141 ctx.krate?,
142 Some(40),
143 potential_import_name,
144 true,
145 true,
146 )
147 .filter_map(|import_candidate| {
148 Some(match import_candidate {
149 Either::Left(module_def) => {
150 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
151 }
152 Either::Right(macro_def) => {
153 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
154 }
155 })
156 })
157 .filter(|(mod_path, _)| mod_path.len() > 1)
158 .collect::<Vec<_>>();
159
160 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
161 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
162 });
163
164 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
165 render_resolution_with_import(
166 RenderContext::new(ctx),
167 ImportEdit { import_path, import_scope: import_scope.clone() },
168 &definition,
169 )
170 }));
171 Some(())
172}
173
174fn compute_fuzzy_completion_order_key(
175 proposed_mod_path: &ModPath,
176 user_input_lowercased: &str,
177) -> usize {
178 mark::hit!(certain_fuzzy_order_test);
179 let proposed_import_name = match proposed_mod_path.segments.last() {
180 Some(name) => name.to_string().to_lowercase(),
181 None => return usize::MAX,
182 };
183 match proposed_import_name.match_indices(user_input_lowercased).next() {
184 Some((first_matching_index, _)) => first_matching_index,
185 None => usize::MAX,
186 }
187}
188
189#[cfg(test)] 70#[cfg(test)]
190mod tests { 71mod tests {
191 use expect_test::{expect, Expect}; 72 use expect_test::{expect, Expect};
192 use test_utils::mark; 73 use test_utils::mark;
193 74
194 use crate::{ 75 use crate::{
195 test_utils::{ 76 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
196 check_edit, check_edit_with_config, completion_list_with_config, TEST_CONFIG,
197 },
198 CompletionConfig, CompletionKind, 77 CompletionConfig, CompletionKind,
199 }; 78 };
200 79
@@ -855,128 +734,4 @@ impl My$0
855 "#]], 734 "#]],
856 ) 735 )
857 } 736 }
858
859 #[test]
860 fn function_fuzzy_completion() {
861 check_edit_with_config(
862 TEST_CONFIG,
863 "stdin",
864 r#"
865//- /lib.rs crate:dep
866pub mod io {
867 pub fn stdin() {}
868};
869
870//- /main.rs crate:main deps:dep
871fn main() {
872 stdi$0
873}
874"#,
875 r#"
876use dep::io::stdin;
877
878fn main() {
879 stdin()$0
880}
881"#,
882 );
883 }
884
885 #[test]
886 fn macro_fuzzy_completion() {
887 check_edit_with_config(
888 TEST_CONFIG,
889 "macro_with_curlies!",
890 r#"
891//- /lib.rs crate:dep
892/// Please call me as macro_with_curlies! {}
893#[macro_export]
894macro_rules! macro_with_curlies {
895 () => {}
896}
897
898//- /main.rs crate:main deps:dep
899fn main() {
900 curli$0
901}
902"#,
903 r#"
904use dep::macro_with_curlies;
905
906fn main() {
907 macro_with_curlies! {$0}
908}
909"#,
910 );
911 }
912
913 #[test]
914 fn struct_fuzzy_completion() {
915 check_edit_with_config(
916 TEST_CONFIG,
917 "ThirdStruct",
918 r#"
919//- /lib.rs crate:dep
920pub struct FirstStruct;
921pub mod some_module {
922 pub struct SecondStruct;
923 pub struct ThirdStruct;
924}
925
926//- /main.rs crate:main deps:dep
927use dep::{FirstStruct, some_module::SecondStruct};
928
929fn main() {
930 this$0
931}
932"#,
933 r#"
934use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
935
936fn main() {
937 ThirdStruct
938}
939"#,
940 );
941 }
942
943 #[test]
944 fn fuzzy_completions_come_in_specific_order() {
945 mark::check!(certain_fuzzy_order_test);
946 check_with_config(
947 TEST_CONFIG,
948 r#"
949//- /lib.rs crate:dep
950pub struct FirstStruct;
951pub mod some_module {
952 // already imported, omitted
953 pub struct SecondStruct;
954 // does not contain all letters from the query, omitted
955 pub struct UnrelatedOne;
956 // contains all letters from the query, but not in sequence, displayed last
957 pub struct ThiiiiiirdStruct;
958 // contains all letters from the query, but not in the beginning, displayed second
959 pub struct AfterThirdStruct;
960 // contains all letters from the query in the begginning, displayed first
961 pub struct ThirdStruct;
962}
963
964//- /main.rs crate:main deps:dep
965use dep::{FirstStruct, some_module::SecondStruct};
966
967fn main() {
968 hir$0
969}
970"#,
971 expect![[r#"
972 fn main() fn main()
973 st SecondStruct
974 st FirstStruct
975 md dep
976 st dep::some_module::ThirdStruct
977 st dep::some_module::AfterThirdStruct
978 st dep::some_module::ThiiiiiirdStruct
979 "#]],
980 );
981 }
982} 737}
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index b4439b7d1..58fc700f3 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,7 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; 7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
@@ -13,5 +13,5 @@ pub struct CompletionConfig {
13 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
16 pub merge: Option<MergeBehavior>, 16 pub insert_use: InsertUseConfig,
17} 17}
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 0134ff219..5d91d3a5c 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -398,7 +398,9 @@ impl Builder {
398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
399 self.detail = detail.map(Into::into); 399 self.detail = detail.map(Into::into);
400 if let Some(detail) = &self.detail { 400 if let Some(detail) = &self.detail {
401 assert_never!(detail.contains('\n'), "multiline detail: {}", detail); 401 if assert_never!(detail.contains('\n'), "multiline detail: {}", detail) {
402 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
403 }
402 } 404 }
403 self 405 self
404 } 406 }
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 6cba88a6b..ee1b822e7 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -127,6 +127,7 @@ pub fn completions(
127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
129 completions::mod_::complete_mod(&mut acc, &ctx); 129 completions::mod_::complete_mod(&mut acc, &ctx);
130 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
130 131
131 Some(acc) 132 Some(acc)
132} 133}
@@ -153,7 +154,9 @@ pub fn resolve_completion_edits(
153 }) 154 })
154 .find(|mod_path| mod_path.to_string() == full_import_path)?; 155 .find(|mod_path| mod_path.to_string() == full_import_path)?;
155 156
156 ImportEdit { import_path, import_scope }.to_text_edit(config.merge).map(|edit| vec![edit]) 157 ImportEdit { import_path, import_scope }
158 .to_text_edit(config.insert_use.merge)
159 .map(|edit| vec![edit])
157} 160}
158 161
159#[cfg(test)] 162#[cfg(test)]
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index e93c59f71..820dd01d1 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -51,11 +51,16 @@ pub(crate) fn render_resolution_with_import<'a>(
51 import_edit: ImportEdit, 51 import_edit: ImportEdit,
52 resolution: &ScopeDef, 52 resolution: &ScopeDef,
53) -> Option<CompletionItem> { 53) -> Option<CompletionItem> {
54 Render::new(ctx).render_resolution( 54 Render::new(ctx)
55 import_edit.import_path.segments.last()?.to_string(), 55 .render_resolution(
56 Some(import_edit), 56 import_edit.import_path.segments.last()?.to_string(),
57 resolution, 57 Some(import_edit),
58 ) 58 resolution,
59 )
60 .map(|mut item| {
61 item.completion_kind = CompletionKind::Magic;
62 item
63 })
59} 64}
60 65
61/// Interface for data and methods required for items rendering. 66/// Interface for data and methods required for items rendering.
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 3f4b9d4ac..6ea6da989 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -1,9 +1,12 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics; 3use hir::{PrefixKind, Semantics};
4use ide_db::{ 4use ide_db::{
5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, 5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
6 helpers::{insert_use::MergeBehavior, SnippetCap}, 6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
7 RootDatabase, 10 RootDatabase,
8}; 11};
9use itertools::Itertools; 12use itertools::Itertools;
@@ -19,7 +22,10 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
19 add_call_parenthesis: true, 22 add_call_parenthesis: true,
20 add_call_argument_snippets: true, 23 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true), 24 snippet_cap: SnippetCap::new(true),
22 merge: Some(MergeBehavior::Full), 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain,
28 },
23}; 29};
24 30
25/// Creates analysis from a multi-file fixture, returns positions marked with $0. 31/// Creates analysis from a multi-file fixture, returns positions marked with $0.
@@ -110,7 +116,7 @@ pub(crate) fn check_edit_with_config(
110 116
111 let mut combined_edit = completion.text_edit().to_owned(); 117 let mut combined_edit = completion.text_edit().to_owned();
112 if let Some(import_text_edit) = 118 if let Some(import_text_edit) =
113 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.merge)) 119 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge))
114 { 120 {
115 combined_edit.union(import_text_edit).expect( 121 combined_edit.union(import_text_edit).expect(
116 "Failed to apply completion resolve changes: change ranges overlap, but should not", 122 "Failed to apply completion resolve changes: change ranges overlap, but should not",