From ee99620754cdcfbab28a2c067dfa31087376d6c3 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 14 Nov 2020 01:26:31 +0200 Subject: Move autoimport completion into the unqialified_path module --- .../completion/src/completions/unqualified_path.rs | 141 ++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 7df58e1da..ecda37862 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,10 +1,16 @@ //! Completion of names from the current scope, e.g. locals and imported items. +use assists::utils::{insert_use, mod_path_to_ast, ImportScope}; +use either::Either; use hir::{Adt, ModuleDef, ScopeDef, Type}; -use syntax::AstNode; +use ide_db::imports_locator; +use syntax::{algo, AstNode}; use test_utils::mark; -use crate::{CompletionContext, Completions}; +use crate::{ + render::{render_resolution, RenderContext}, + CompletionContext, Completions, +}; pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 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 } acc.add_resolution(ctx, name.to_string(), &res) }); + + fuzzy_completion(acc, ctx).unwrap_or_default() +} + +// TODO kb add a setting toggle for this feature? +fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + let _p = profile::span("fuzzy_completion®"); + let current_module = ctx.scope.module()?; + let anchor = ctx.name_ref_syntax.as_ref()?; + let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; + + let potential_import_name = ctx.token.to_string(); + + let possible_imports = + imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400) + .filter_map(|import_candidate| match import_candidate { + // when completing outside the use declaration, modules are pretty useless + // and tend to bloat the completion suggestions a lot + Either::Left(ModuleDef::Module(_)) => None, + Either::Left(module_def) => Some(( + current_module.find_use_path(ctx.db, module_def)?, + ScopeDef::ModuleDef(module_def), + )), + Either::Right(macro_def) => Some(( + current_module.find_use_path(ctx.db, macro_def)?, + ScopeDef::MacroDef(macro_def), + )), + }) + .filter_map(|(mod_path, definition)| { + let mut resolution_with_missing_import = render_resolution( + RenderContext::new(ctx), + mod_path.segments.last()?.to_string(), + &definition, + )?; + + let mut text_edits = + resolution_with_missing_import.text_edit().to_owned().into_builder(); + + let rewriter = + insert_use(&import_scope, mod_path_to_ast(&mod_path), ctx.config.merge); + let old_ast = rewriter.rewrite_root()?; + algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); + + resolution_with_missing_import.update_text_edit(text_edits.finish()); + + Some(resolution_with_missing_import) + }); + + acc.add_all(possible_imports); + Some(()) } fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { @@ -676,4 +732,85 @@ impl My<|> "#]], ) } + + #[test] + fn function_magic_completion() { + check_edit( + "stdin", + r#" +//- /lib.rs crate:dep +pub mod io { + pub fn stdin() {} +}; + +//- /main.rs crate:main deps:dep +fn main() { + stdi<|> +} +"#, + r#" +use dep::io::stdin; + +fn main() { + stdin()$0 +} +"#, + ); + } + + #[test] + fn macro_magic_completion() { + check_edit( + "macro_with_curlies!", + r#" +//- /lib.rs crate:dep +/// Please call me as macro_with_curlies! {} +#[macro_export] +macro_rules! macro_with_curlies { + () => {} +} + +//- /main.rs crate:main deps:dep +fn main() { + curli<|> +} +"#, + r#" +use dep::macro_with_curlies; + +fn main() { + macro_with_curlies! {$0} +} +"#, + ); + } + + #[test] + fn case_insensitive_magic_completion_works() { + check_edit( + "ThirdStruct", + r#" +//- /lib.rs crate:dep +pub struct FirstStruct; +pub mod some_module { + pub struct SecondStruct; + pub struct ThirdStruct; +} + +//- /main.rs crate:main deps:dep +use dep::{FirstStruct, some_module::SecondStruct}; + +fn main() { + this<|> +} +"#, + r#" +use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; + +fn main() { + ThirdStruct +} +"#, + ); + } } -- cgit v1.2.3 From 38ef1fd4ad7fd26439201a1a4147a7d90a13601f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 14 Nov 2020 11:59:23 +0200 Subject: Better filter mod paths --- .../completion/src/completions/unqualified_path.rs | 52 +++++++++++----------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index ecda37862..7ce92a07b 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -47,6 +47,30 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC fuzzy_completion(acc, ctx).unwrap_or_default() } +fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { + if let Some(Adt::Enum(enum_data)) = ty.as_adt() { + let variants = enum_data.variants(ctx.db); + + let module = if let Some(module) = ctx.scope.module() { + // Compute path from the completion site if available. + module + } else { + // Otherwise fall back to the enum's definition site. + enum_data.module(ctx.db) + }; + + for variant in variants { + if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { + // Variants with trivial paths are already added by the existing completion logic, + // so we should avoid adding these twice + if path.segments.len() > 1 { + acc.add_qualified_enum_variant(ctx, variant, path); + } + } + } + } +} + // TODO kb add a setting toggle for this feature? fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let _p = profile::span("fuzzy_completion®"); @@ -71,6 +95,7 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() ScopeDef::MacroDef(macro_def), )), }) + .filter(|(mod_path, _)| mod_path.len() > 1) .filter_map(|(mod_path, definition)| { let mut resolution_with_missing_import = render_resolution( RenderContext::new(ctx), @@ -89,36 +114,13 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() resolution_with_missing_import.update_text_edit(text_edits.finish()); Some(resolution_with_missing_import) - }); + }) + .take(20); acc.add_all(possible_imports); Some(()) } -fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { - if let Some(Adt::Enum(enum_data)) = ty.as_adt() { - let variants = enum_data.variants(ctx.db); - - let module = if let Some(module) = ctx.scope.module() { - // Compute path from the completion site if available. - module - } else { - // Otherwise fall back to the enum's definition site. - enum_data.module(ctx.db) - }; - - for variant in variants { - if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { - // Variants with trivial paths are already added by the existing completion logic, - // so we should avoid adding these twice - if path.segments.len() > 1 { - acc.add_qualified_enum_variant(ctx, variant, path); - } - } - } - } -} - #[cfg(test)] mod tests { use expect_test::{expect, Expect}; -- cgit v1.2.3 From bbe1fbd1786b416908d3c6bc34c8cf805b39b761 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 14 Nov 2020 14:50:57 +0200 Subject: Qualify autoimport completion suggestions --- .../completion/src/completions/unqualified_path.rs | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 7ce92a07b..fca8d3a72 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -71,7 +71,6 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T } } -// TODO kb add a setting toggle for this feature? fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let _p = profile::span("fuzzy_completion®"); let current_module = ctx.scope.module()?; @@ -97,23 +96,35 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() }) .filter(|(mod_path, _)| mod_path.len() > 1) .filter_map(|(mod_path, definition)| { - let mut resolution_with_missing_import = render_resolution( - RenderContext::new(ctx), - mod_path.segments.last()?.to_string(), - &definition, - )?; + let use_to_insert = mod_path_to_ast(&mod_path); + let mut mod_path_without_last_segment = mod_path; + let name_after_import = mod_path_without_last_segment.segments.pop()?.to_string(); + + let resolution_with_missing_import = + render_resolution(RenderContext::new(ctx), name_after_import, &definition)?; + let lookup_string = resolution_with_missing_import.lookup().to_owned(); let mut text_edits = resolution_with_missing_import.text_edit().to_owned().into_builder(); - - let rewriter = - insert_use(&import_scope, mod_path_to_ast(&mod_path), ctx.config.merge); + let rewriter = insert_use(&import_scope, use_to_insert, ctx.config.merge); let old_ast = rewriter.rewrite_root()?; algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); - resolution_with_missing_import.update_text_edit(text_edits.finish()); - - Some(resolution_with_missing_import) + let qualifier_string = mod_path_without_last_segment.to_string(); + let qualified_label = if qualifier_string.is_empty() { + resolution_with_missing_import.label().to_owned() + } else { + format!("{}::{}", qualifier_string, resolution_with_missing_import.label()) + }; + + Some( + resolution_with_missing_import + .into_builder() + .text_edit(text_edits.finish()) + .label(qualified_label) + .lookup_by(lookup_string) + .build(), + ) }) .take(20); -- cgit v1.2.3 From 1de7848b5710f21cccf90dabc99a0cf6fcdabad3 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 14 Nov 2020 14:59:03 +0200 Subject: Fix the other test --- crates/completion/src/completions/unqualified_path.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index fca8d3a72..362d31f25 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -747,7 +747,7 @@ impl My<|> } #[test] - fn function_magic_completion() { + fn function_fuzzy_completion() { check_edit( "stdin", r#" @@ -772,7 +772,7 @@ fn main() { } #[test] - fn macro_magic_completion() { + fn macro_fuzzy_completion() { check_edit( "macro_with_curlies!", r#" @@ -799,7 +799,7 @@ fn main() { } #[test] - fn case_insensitive_magic_completion_works() { + fn struct_fuzzy_completion() { check_edit( "ThirdStruct", r#" -- cgit v1.2.3 From 410996893489f6c64b472e6128f099f1de229806 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 16 Nov 2020 21:24:54 +0200 Subject: Remove query aliases --- crates/completion/src/completions/unqualified_path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 362d31f25..4f8ec1e67 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -72,7 +72,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T } fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let _p = profile::span("fuzzy_completion®"); + let _p = profile::span("fuzzy_completion"); let current_module = ctx.scope.module()?; let anchor = ctx.name_ref_syntax.as_ref()?; let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; -- cgit v1.2.3 From d4128beb3d8c647674ae43407d0ed6edd71ff420 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 16 Nov 2020 23:16:41 +0200 Subject: Avoid turning completion objects into builders --- .../completion/src/completions/unqualified_path.rs | 42 ++++++---------------- 1 file changed, 10 insertions(+), 32 deletions(-) (limited to 'crates/completion/src/completions/unqualified_path.rs') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 4f8ec1e67..86c143b63 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,14 +1,14 @@ //! Completion of names from the current scope, e.g. locals and imported items. -use assists::utils::{insert_use, mod_path_to_ast, ImportScope}; +use assists::utils::ImportScope; use either::Either; use hir::{Adt, ModuleDef, ScopeDef, Type}; use ide_db::imports_locator; -use syntax::{algo, AstNode}; +use syntax::AstNode; use test_utils::mark; use crate::{ - render::{render_resolution, RenderContext}, + render::{render_resolution_with_import, RenderContext}, CompletionContext, Completions, }; @@ -95,35 +95,13 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() )), }) .filter(|(mod_path, _)| mod_path.len() > 1) - .filter_map(|(mod_path, definition)| { - let use_to_insert = mod_path_to_ast(&mod_path); - let mut mod_path_without_last_segment = mod_path; - let name_after_import = mod_path_without_last_segment.segments.pop()?.to_string(); - - let resolution_with_missing_import = - render_resolution(RenderContext::new(ctx), name_after_import, &definition)?; - let lookup_string = resolution_with_missing_import.lookup().to_owned(); - - let mut text_edits = - resolution_with_missing_import.text_edit().to_owned().into_builder(); - let rewriter = insert_use(&import_scope, use_to_insert, ctx.config.merge); - let old_ast = rewriter.rewrite_root()?; - algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); - - let qualifier_string = mod_path_without_last_segment.to_string(); - let qualified_label = if qualifier_string.is_empty() { - resolution_with_missing_import.label().to_owned() - } else { - format!("{}::{}", qualifier_string, resolution_with_missing_import.label()) - }; - - Some( - resolution_with_missing_import - .into_builder() - .text_edit(text_edits.finish()) - .label(qualified_label) - .lookup_by(lookup_string) - .build(), + .filter_map(|(import_path, definition)| { + render_resolution_with_import( + RenderContext::new(ctx), + import_path.clone(), + import_scope.clone(), + ctx.config.merge, + &definition, ) }) .take(20); -- cgit v1.2.3