|
1 | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 |
|
| 3 | +use assists::utils::{insert_use, mod_path_to_ast, ImportScope}; |
| 4 | +use either::Either; |
3 | 5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
4 | | -use syntax::AstNode; |
| 6 | +use ide_db::imports_locator; |
| 7 | +use syntax::{algo, AstNode}; |
5 | 8 | use test_utils::mark; |
6 | 9 |
|
7 | | -use crate::{CompletionContext, Completions}; |
| 10 | +use crate::{ |
| 11 | + render::{render_resolution, RenderContext}, |
| 12 | + CompletionContext, Completions, |
| 13 | +}; |
8 | 14 |
|
9 | 15 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | 16 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { |
@@ -37,6 +43,56 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC |
37 | 43 | } |
38 | 44 | acc.add_resolution(ctx, name.to_string(), &res) |
39 | 45 | }); |
| 46 | + |
| 47 | + fuzzy_completion(acc, ctx).unwrap_or_default() |
| 48 | +} |
| 49 | + |
| 50 | +// TODO kb add a setting toggle for this feature? |
| 51 | +fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
| 52 | + let _p = profile::span("fuzzy_completion®"); |
| 53 | + let current_module = ctx.scope.module()?; |
| 54 | + let anchor = ctx.name_ref_syntax.as_ref()?; |
| 55 | + let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; |
| 56 | + |
| 57 | + let potential_import_name = ctx.token.to_string(); |
| 58 | + |
| 59 | + let possible_imports = |
| 60 | + imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400) |
| 61 | + .filter_map(|import_candidate| match import_candidate { |
| 62 | + // when completing outside the use declaration, modules are pretty useless |
| 63 | + // and tend to bloat the completion suggestions a lot |
| 64 | + Either::Left(ModuleDef::Module(_)) => None, |
| 65 | + Either::Left(module_def) => Some(( |
| 66 | + current_module.find_use_path(ctx.db, module_def)?, |
| 67 | + ScopeDef::ModuleDef(module_def), |
| 68 | + )), |
| 69 | + Either::Right(macro_def) => Some(( |
| 70 | + current_module.find_use_path(ctx.db, macro_def)?, |
| 71 | + ScopeDef::MacroDef(macro_def), |
| 72 | + )), |
| 73 | + }) |
| 74 | + .filter_map(|(mod_path, definition)| { |
| 75 | + let mut resolution_with_missing_import = render_resolution( |
| 76 | + RenderContext::new(ctx), |
| 77 | + mod_path.segments.last()?.to_string(), |
| 78 | + &definition, |
| 79 | + )?; |
| 80 | + |
| 81 | + let mut text_edits = |
| 82 | + resolution_with_missing_import.text_edit().to_owned().into_builder(); |
| 83 | + |
| 84 | + let rewriter = |
| 85 | + insert_use(&import_scope, mod_path_to_ast(&mod_path), ctx.config.merge); |
| 86 | + let old_ast = rewriter.rewrite_root()?; |
| 87 | + algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); |
| 88 | + |
| 89 | + resolution_with_missing_import.update_text_edit(text_edits.finish()); |
| 90 | + |
| 91 | + Some(resolution_with_missing_import) |
| 92 | + }); |
| 93 | + |
| 94 | + acc.add_all(possible_imports); |
| 95 | + Some(()) |
40 | 96 | } |
41 | 97 |
|
42 | 98 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
@@ -676,4 +732,85 @@ impl My<|> |
676 | 732 | "#]], |
677 | 733 | ) |
678 | 734 | } |
| 735 | + |
| 736 | + #[test] |
| 737 | + fn function_magic_completion() { |
| 738 | + check_edit( |
| 739 | + "stdin", |
| 740 | + r#" |
| 741 | +//- /lib.rs crate:dep |
| 742 | +pub mod io { |
| 743 | + pub fn stdin() {} |
| 744 | +}; |
| 745 | +
|
| 746 | +//- /main.rs crate:main deps:dep |
| 747 | +fn main() { |
| 748 | + stdi<|> |
| 749 | +} |
| 750 | +"#, |
| 751 | + r#" |
| 752 | +use dep::io::stdin; |
| 753 | +
|
| 754 | +fn main() { |
| 755 | + stdin()$0 |
| 756 | +} |
| 757 | +"#, |
| 758 | + ); |
| 759 | + } |
| 760 | + |
| 761 | + #[test] |
| 762 | + fn macro_magic_completion() { |
| 763 | + check_edit( |
| 764 | + "macro_with_curlies!", |
| 765 | + r#" |
| 766 | +//- /lib.rs crate:dep |
| 767 | +/// Please call me as macro_with_curlies! {} |
| 768 | +#[macro_export] |
| 769 | +macro_rules! macro_with_curlies { |
| 770 | + () => {} |
| 771 | +} |
| 772 | +
|
| 773 | +//- /main.rs crate:main deps:dep |
| 774 | +fn main() { |
| 775 | + curli<|> |
| 776 | +} |
| 777 | +"#, |
| 778 | + r#" |
| 779 | +use dep::macro_with_curlies; |
| 780 | +
|
| 781 | +fn main() { |
| 782 | + macro_with_curlies! {$0} |
| 783 | +} |
| 784 | +"#, |
| 785 | + ); |
| 786 | + } |
| 787 | + |
| 788 | + #[test] |
| 789 | + fn case_insensitive_magic_completion_works() { |
| 790 | + check_edit( |
| 791 | + "ThirdStruct", |
| 792 | + r#" |
| 793 | +//- /lib.rs crate:dep |
| 794 | +pub struct FirstStruct; |
| 795 | +pub mod some_module { |
| 796 | + pub struct SecondStruct; |
| 797 | + pub struct ThirdStruct; |
| 798 | +} |
| 799 | +
|
| 800 | +//- /main.rs crate:main deps:dep |
| 801 | +use dep::{FirstStruct, some_module::SecondStruct}; |
| 802 | +
|
| 803 | +fn main() { |
| 804 | + this<|> |
| 805 | +} |
| 806 | +"#, |
| 807 | + r#" |
| 808 | +use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; |
| 809 | +
|
| 810 | +fn main() { |
| 811 | + ThirdStruct |
| 812 | +} |
| 813 | +"#, |
| 814 | + ); |
| 815 | + } |
679 | 816 | } |
0 commit comments