aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions/flyimport.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions/flyimport.rs')
-rw-r--r--crates/completion/src/completions/flyimport.rs300
1 files changed, 268 insertions, 32 deletions
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
index 222809638..9101e405c 100644
--- a/crates/completion/src/completions/flyimport.rs
+++ b/crates/completion/src/completions/flyimport.rs
@@ -45,9 +45,8 @@
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 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. 46//! capability enabled.
47 47
48use either::Either;
49use hir::{ModPath, ScopeDef}; 48use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator}; 49use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope};
51use syntax::AstNode; 50use syntax::AstNode;
52use test_utils::mark; 51use test_utils::mark;
53 52
@@ -60,7 +59,7 @@ use crate::{
60use super::Completions; 59use super::Completions;
61 60
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 61pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions { 62 if !ctx.config.enable_imports_on_the_fly {
64 return None; 63 return None;
65 } 64 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { 65 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
@@ -72,46 +71,56 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
72 } 71 }
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); 72 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
74 73
75 let current_module = ctx.scope.module()?; 74 let import_scope =
76 let anchor = ctx.name_ref_syntax.as_ref()?; 75 ImportScope::find_insert_use_container(ctx.name_ref_syntax.as_ref()?.syntax(), &ctx.sema)?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78
79 let user_input_lowercased = potential_import_name.to_lowercase(); 76 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports( 77 let mut all_mod_paths = import_assets(ctx, potential_import_name)?
81 &ctx.sema, 78 .search_for_relative_paths(&ctx.sema)
82 ctx.krate?, 79 .into_iter()
83 Some(40), 80 .map(|(mod_path, item_in_ns)| {
84 potential_import_name, 81 let scope_item = match item_in_ns {
85 true, 82 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
86 true, 83 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
87 ) 84 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
88 .filter_map(|import_candidate| { 85 };
89 Some(match import_candidate { 86 (mod_path, scope_item)
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 }) 87 })
97 }) 88 .collect::<Vec<_>>();
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 89 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 90 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 }); 91 });
104 92
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { 93 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import( 94 let import_for_trait_assoc_item = match definition {
107 RenderContext::new(ctx), 95 ScopeDef::ModuleDef(module_def) => module_def
108 ImportEdit { import_path, import_scope: import_scope.clone() }, 96 .as_assoc_item(ctx.db)
109 &definition, 97 .and_then(|assoc| assoc.containing_trait(ctx.db))
110 ) 98 .is_some(),
99 _ => false,
100 };
101 let import_edit = ImportEdit {
102 import_path,
103 import_scope: import_scope.clone(),
104 import_for_trait_assoc_item,
105 };
106 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
111 })); 107 }));
112 Some(()) 108 Some(())
113} 109}
114 110
111fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
112 let current_module = ctx.scope.module()?;
113 if let Some(dot_receiver) = &ctx.dot_receiver {
114 ImportAssets::for_fuzzy_method_call(
115 current_module,
116 ctx.sema.type_of_expr(dot_receiver)?,
117 fuzzy_name,
118 )
119 } else {
120 ImportAssets::for_fuzzy_path(current_module, ctx.path_qual.clone(), fuzzy_name, &ctx.sema)
121 }
122}
123
115fn compute_fuzzy_completion_order_key( 124fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath, 125 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str, 126 user_input_lowercased: &str,
@@ -259,6 +268,176 @@ fn main() {
259 } 268 }
260 269
261 #[test] 270 #[test]
271 fn trait_function_fuzzy_completion() {
272 let fixture = r#"
273 //- /lib.rs crate:dep
274 pub mod test_mod {
275 pub trait TestTrait {
276 const SPECIAL_CONST: u8;
277 type HumbleType;
278 fn weird_function();
279 fn random_method(&self);
280 }
281 pub struct TestStruct {}
282 impl TestTrait for TestStruct {
283 const SPECIAL_CONST: u8 = 42;
284 type HumbleType = ();
285 fn weird_function() {}
286 fn random_method(&self) {}
287 }
288 }
289
290 //- /main.rs crate:main deps:dep
291 fn main() {
292 dep::test_mod::TestStruct::wei$0
293 }
294 "#;
295
296 check(
297 fixture,
298 expect![[r#"
299 fn weird_function() (dep::test_mod::TestTrait) fn weird_function()
300 "#]],
301 );
302
303 check_edit(
304 "weird_function",
305 fixture,
306 r#"
307use dep::test_mod::TestTrait;
308
309fn main() {
310 dep::test_mod::TestStruct::weird_function()$0
311}
312"#,
313 );
314 }
315
316 #[test]
317 fn trait_const_fuzzy_completion() {
318 let fixture = r#"
319 //- /lib.rs crate:dep
320 pub mod test_mod {
321 pub trait TestTrait {
322 const SPECIAL_CONST: u8;
323 type HumbleType;
324 fn weird_function();
325 fn random_method(&self);
326 }
327 pub struct TestStruct {}
328 impl TestTrait for TestStruct {
329 const SPECIAL_CONST: u8 = 42;
330 type HumbleType = ();
331 fn weird_function() {}
332 fn random_method(&self) {}
333 }
334 }
335
336 //- /main.rs crate:main deps:dep
337 fn main() {
338 dep::test_mod::TestStruct::spe$0
339 }
340 "#;
341
342 check(
343 fixture,
344 expect![[r#"
345 ct SPECIAL_CONST (dep::test_mod::TestTrait)
346 "#]],
347 );
348
349 check_edit(
350 "SPECIAL_CONST",
351 fixture,
352 r#"
353use dep::test_mod::TestTrait;
354
355fn main() {
356 dep::test_mod::TestStruct::SPECIAL_CONST
357}
358"#,
359 );
360 }
361
362 #[test]
363 fn trait_method_fuzzy_completion() {
364 let fixture = r#"
365 //- /lib.rs crate:dep
366 pub mod test_mod {
367 pub trait TestTrait {
368 const SPECIAL_CONST: u8;
369 type HumbleType;
370 fn weird_function();
371 fn random_method(&self);
372 }
373 pub struct TestStruct {}
374 impl TestTrait for TestStruct {
375 const SPECIAL_CONST: u8 = 42;
376 type HumbleType = ();
377 fn weird_function() {}
378 fn random_method(&self) {}
379 }
380 }
381
382 //- /main.rs crate:main deps:dep
383 fn main() {
384 let test_struct = dep::test_mod::TestStruct {};
385 test_struct.ran$0
386 }
387 "#;
388
389 check(
390 fixture,
391 expect![[r#"
392 me random_method() (dep::test_mod::TestTrait) fn random_method(&self)
393 "#]],
394 );
395
396 check_edit(
397 "random_method",
398 fixture,
399 r#"
400use dep::test_mod::TestTrait;
401
402fn main() {
403 let test_struct = dep::test_mod::TestStruct {};
404 test_struct.random_method()$0
405}
406"#,
407 );
408 }
409
410 #[test]
411 fn no_trait_type_fuzzy_completion() {
412 check(
413 r#"
414//- /lib.rs crate:dep
415pub mod test_mod {
416 pub trait TestTrait {
417 const SPECIAL_CONST: u8;
418 type HumbleType;
419 fn weird_function();
420 fn random_method(&self);
421 }
422 pub struct TestStruct {}
423 impl TestTrait for TestStruct {
424 const SPECIAL_CONST: u8 = 42;
425 type HumbleType = ();
426 fn weird_function() {}
427 fn random_method(&self) {}
428 }
429}
430
431//- /main.rs crate:main deps:dep
432fn main() {
433 dep::test_mod::TestStruct::hum$0
434}
435"#,
436 expect![[r#""#]],
437 );
438 }
439
440 #[test]
262 fn does_not_propose_names_in_scope() { 441 fn does_not_propose_names_in_scope() {
263 check( 442 check(
264 r#" 443 r#"
@@ -288,4 +467,61 @@ fn main() {
288 expect![[r#""#]], 467 expect![[r#""#]],
289 ); 468 );
290 } 469 }
470
471 #[test]
472 fn does_not_propose_traits_in_scope() {
473 check(
474 r#"
475//- /lib.rs crate:dep
476pub mod test_mod {
477 pub trait TestTrait {
478 const SPECIAL_CONST: u8;
479 type HumbleType;
480 fn weird_function();
481 fn random_method(&self);
482 }
483 pub struct TestStruct {}
484 impl TestTrait for TestStruct {
485 const SPECIAL_CONST: u8 = 42;
486 type HumbleType = ();
487 fn weird_function() {}
488 fn random_method(&self) {}
489 }
490}
491
492//- /main.rs crate:main deps:dep
493use dep::test_mod::{TestStruct, TestTrait};
494fn main() {
495 dep::test_mod::TestStruct::hum$0
496}
497"#,
498 expect![[r#""#]],
499 );
500 }
501
502 #[test]
503 fn blanket_trait_impl_import() {
504 check(
505 r#"
506//- /lib.rs crate:dep
507pub mod test_mod {
508 pub struct TestStruct {}
509 pub trait TestTrait {
510 fn another_function();
511 }
512 impl<T> TestTrait for T {
513 fn another_function() {}
514 }
515}
516
517//- /main.rs crate:main deps:dep
518fn main() {
519 dep::test_mod::TestStruct::ano$0
520}
521"#,
522 expect![[r#"
523 fn another_function() (dep::test_mod::TestTrait) fn another_function()
524 "#]],
525 );
526 }
291} 527}