Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/assists/src/handlers/auto_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>

let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
let group = import_group_message(import_assets.import_candidate());
let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?;
let scope =
ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
for (import, _) in proposed_imports {
acc.add_group(
&group,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ fn insert_import(
if let Some(mut mod_path) = mod_path {
mod_path.segments.pop();
mod_path.segments.push(variant_hir_name.clone());
let scope = ImportScope::find_insert_use_container(scope_node, ctx)?;

let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
*rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
}
Some(())
Expand Down
28 changes: 15 additions & 13 deletions crates/assists/src/handlers/replace_derive_with_manual_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,21 @@ pub(crate) fn replace_derive_with_manual_impl(
let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
let current_crate = current_module.krate();

let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
.into_iter()
.filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
_ => None,
})
.flat_map(|trait_| {
current_module
.find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
.as_ref()
.map(mod_path_to_ast)
.zip(Some(trait_))
});
let found_traits =
imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
.filter_map(
|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
_ => None,
},
)
.flat_map(|trait_| {
current_module
.find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
.as_ref()
.map(mod_path_to_ast)
.zip(Some(trait_))
});

let mut no_traits_found = true;
for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
}

let target = path.syntax().text_range();
let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
let syntax = scope.as_syntax_node();
acc.add(
AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
Expand Down
3 changes: 1 addition & 2 deletions crates/assists/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ use crate::{
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
};

pub use insert_use::MergeBehaviour;
pub(crate) use insert_use::{insert_use, ImportScope};
pub use insert_use::{insert_use, ImportScope, MergeBehaviour};

pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
let mut segments = Vec::new();
Expand Down
34 changes: 19 additions & 15 deletions crates/assists/src/utils/import_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,25 @@ impl ImportAssets {
}
};

let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query())
.into_iter()
.filter_map(filter)
.filter_map(|candidate| {
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
if let Some(prefix_kind) = prefixed {
self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
} else {
self.module_with_name_to_import.find_use_path(db, item)
}
.map(|path| (path, item))
})
.filter(|(use_path, _)| !use_path.segments.is_empty())
.take(20)
.collect::<Vec<_>>();
let mut res =
imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query())
.filter_map(filter)
.filter_map(|candidate| {
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
if let Some(prefix_kind) = prefixed {
self.module_with_name_to_import.find_use_path_prefixed(
db,
item,
prefix_kind,
)
} else {
self.module_with_name_to_import.find_use_path(db, item)
}
.map(|path| (path, item))
})
.filter(|(use_path, _)| use_path.len() > 1)
.take(20)
.collect::<Vec<_>>();
res.sort_by_key(|(path, _)| path.clone());
res
}
Expand Down
16 changes: 9 additions & 7 deletions crates/assists/src/utils/insert_use.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Handle syntactic aspects of inserting a new `use`.
use std::{cmp::Ordering, iter::successors};

use hir::Semantics;
use ide_db::RootDatabase;
use itertools::{EitherOrBoth, Itertools};
use syntax::{
algo::SyntaxRewriter,
Expand All @@ -13,8 +15,8 @@ use syntax::{
};
use test_utils::mark;

#[derive(Debug)]
pub(crate) enum ImportScope {
#[derive(Debug, Clone)]
pub enum ImportScope {
File(ast::SourceFile),
Module(ast::ItemList),
}
Expand All @@ -31,14 +33,14 @@ impl ImportScope {
}

/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
pub(crate) fn find_insert_use_container(
pub fn find_insert_use_container(
position: &SyntaxNode,
ctx: &crate::assist_context::AssistContext,
sema: &Semantics<'_, RootDatabase>,
) -> Option<Self> {
ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from)
sema.ancestors_with_macros(position.clone()).find_map(Self::from)
}

pub(crate) fn as_syntax_node(&self) -> &SyntaxNode {
pub fn as_syntax_node(&self) -> &SyntaxNode {
match self {
ImportScope::File(file) => file.syntax(),
ImportScope::Module(item_list) => item_list.syntax(),
Expand Down Expand Up @@ -88,7 +90,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
}

/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
pub(crate) fn insert_use<'a>(
pub fn insert_use<'a>(
scope: &ImportScope,
path: ast::Path,
merge: Option<MergeBehaviour>,
Expand Down
1 change: 1 addition & 0 deletions crates/completion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ doctest = false
itertools = "0.9.0"
log = "0.4.8"
rustc-hash = "1.1.0"
either = "1.6.1"

assists = { path = "../assists", version = "0.0.0" }
stdx = { path = "../stdx", version = "0.0.0" }
Expand Down
8 changes: 4 additions & 4 deletions crates/completion/src/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl Completions {
Some(it) => it,
None => return,
};
if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) {
if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) {
self.add(item);
}
}
Expand All @@ -101,7 +101,7 @@ impl Completions {
func: hir::Function,
local_name: Option<String>,
) {
let item = render_fn(RenderContext::new(ctx), local_name, func);
let item = render_fn(RenderContext::new(ctx), None, local_name, func);
self.add(item)
}

Expand All @@ -123,7 +123,7 @@ impl Completions {
variant: hir::EnumVariant,
path: ModPath,
) {
let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path));
let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path));
self.add(item);
}

Expand All @@ -133,7 +133,7 @@ impl Completions {
variant: hir::EnumVariant,
local_name: Option<String>,
) {
let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None);
let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None);
self.add(item);
}
}
130 changes: 129 additions & 1 deletion crates/completion/src/completions/unqualified_path.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
//! Completion of names from the current scope, e.g. locals and imported items.

use assists::utils::ImportScope;
use either::Either;
use hir::{Adt, ModuleDef, ScopeDef, Type};
use ide_db::imports_locator;
use syntax::AstNode;
use test_utils::mark;

use crate::{CompletionContext, Completions};
use crate::{
render::{render_resolution_with_import, 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) {
Expand Down Expand Up @@ -37,6 +43,8 @@ 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()
}

fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
Expand All @@ -63,6 +71,45 @@ 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 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(|(mod_path, _)| mod_path.len() > 1)
.filter_map(|(import_path, definition)| {
render_resolution_with_import(
RenderContext::new(ctx),
import_path.clone(),
import_scope.clone(),
ctx.config.merge,
&definition,
)
})
.take(20);

acc.add_all(possible_imports);
Some(())
}

#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
Expand Down Expand Up @@ -676,4 +723,85 @@ impl My<|>
"#]],
)
}

#[test]
fn function_fuzzy_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_fuzzy_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 struct_fuzzy_completion() {
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
}
"#,
);
}
}
4 changes: 4 additions & 0 deletions crates/completion/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
//! module, and we use to statically check that we only produce snippet
//! completions if we are allowed to.

use assists::utils::MergeBehaviour;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionConfig {
pub enable_postfix_completions: bool,
pub add_call_parenthesis: bool,
pub add_call_argument_snippets: bool,
pub snippet_cap: Option<SnippetCap>,
pub merge: Option<MergeBehaviour>,
}

impl CompletionConfig {
Expand All @@ -30,6 +33,7 @@ impl Default for CompletionConfig {
add_call_parenthesis: true,
add_call_argument_snippets: true,
snippet_cap: Some(SnippetCap { _private: () }),
merge: Some(MergeBehaviour::Full),
}
}
}
Loading