Move autoimport completion into the unqialified_path module

This commit is contained in:
Kirill Bulatov 2020-11-14 01:26:31 +02:00
parent 3b0fc4d7f2
commit ee99620754
5 changed files with 139 additions and 156 deletions

View file

@ -22,7 +22,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
base_db = { path = "../base_db", version = "0.0.0" }
ide_db = { path = "../ide_db", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" }
assists = { path = "../assists", version = "0.0.0" }
test_utils = { path = "../test_utils", version = "0.0.0" }
# completions crate should depend only on the top-level `hir` package. if you need

View file

@ -13,7 +13,6 @@ pub(crate) mod postfix;
pub(crate) mod macro_in_item_position;
pub(crate) mod trait_impl;
pub(crate) mod mod_;
pub(crate) mod magic;
use hir::{ModPath, ScopeDef, Type};

View file

@ -1,151 +0,0 @@
//! TODO kb move this into the complete_unqualified_path when starts to work properly
use assists::utils::{insert_use, mod_path_to_ast, ImportScope};
use either::Either;
use hir::{ModuleDef, ScopeDef};
use ide_db::imports_locator;
use syntax::{algo, AstNode};
use crate::{
context::CompletionContext,
render::{render_resolution, RenderContext},
};
use super::Completions;
// TODO kb add a setting toggle for this feature?
pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
return None;
}
let _p = profile::span("complete_magic");
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(())
}
#[cfg(test)]
mod tests {
use crate::test_utils::check_edit;
#[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
}
"#,
);
}
}

View file

@ -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
}
"#,
);
}
}

View file

@ -118,7 +118,6 @@ pub fn completions(
completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
completions::mod_::complete_mod(&mut acc, &ctx);
completions::magic::complete_magic(&mut acc, &ctx);
Some(acc)
}