mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
Draft the working completion
This commit is contained in:
parent
db335a1bbf
commit
68626e4ef5
3 changed files with 168 additions and 27 deletions
|
@ -20,11 +20,14 @@
|
||||||
//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
|
//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! Also completes associated items, that require trait imports.
|
||||||
|
//!
|
||||||
//! .Fuzzy search details
|
//! .Fuzzy search details
|
||||||
//!
|
//!
|
||||||
//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
|
//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
|
||||||
//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
|
//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
|
||||||
//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
|
//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols
|
||||||
|
//! (but shows all associated items for any input length).
|
||||||
//!
|
//!
|
||||||
//! .Import configuration
|
//! .Import configuration
|
||||||
//!
|
//!
|
||||||
|
@ -46,8 +49,11 @@
|
||||||
//! capability enabled.
|
//! capability enabled.
|
||||||
|
|
||||||
use hir::{ModPath, ScopeDef};
|
use hir::{ModPath, ScopeDef};
|
||||||
use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope};
|
use ide_db::helpers::{
|
||||||
use syntax::AstNode;
|
import_assets::{ImportAssets, ImportCandidate},
|
||||||
|
insert_use::ImportScope,
|
||||||
|
};
|
||||||
|
use syntax::{AstNode, SyntaxNode, T};
|
||||||
use test_utils::mark;
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -65,16 +71,24 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
|
if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let potential_import_name = ctx.token.to_string();
|
let potential_import_name = {
|
||||||
if potential_import_name.len() < 2 {
|
let token_kind = ctx.token.kind();
|
||||||
return None;
|
if token_kind == T![.] || token_kind == T![::] {
|
||||||
}
|
String::new()
|
||||||
|
} else {
|
||||||
|
ctx.token.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
|
let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
|
||||||
|
|
||||||
let import_scope =
|
|
||||||
ImportScope::find_insert_use_container(ctx.name_ref_syntax.as_ref()?.syntax(), &ctx.sema)?;
|
|
||||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||||
let mut all_mod_paths = import_assets(ctx, potential_import_name)?
|
let import_assets = import_assets(ctx, potential_import_name)?;
|
||||||
|
let import_scope = ImportScope::find_insert_use_container(
|
||||||
|
position_for_import(ctx, import_assets.import_candidate())?,
|
||||||
|
&ctx.sema,
|
||||||
|
)?;
|
||||||
|
let mut all_mod_paths = import_assets
|
||||||
.search_for_relative_paths(&ctx.sema)
|
.search_for_relative_paths(&ctx.sema)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(mod_path, item_in_ns)| {
|
.map(|(mod_path, item_in_ns)| {
|
||||||
|
@ -108,6 +122,17 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn position_for_import<'a>(
|
||||||
|
ctx: &'a CompletionContext,
|
||||||
|
import_candidate: &ImportCandidate,
|
||||||
|
) -> Option<&'a SyntaxNode> {
|
||||||
|
Some(match import_candidate {
|
||||||
|
ImportCandidate::Path(_) => ctx.name_ref_syntax.as_ref()?.syntax(),
|
||||||
|
ImportCandidate::TraitAssocItem(_) => ctx.path_qual.as_ref()?.syntax(),
|
||||||
|
ImportCandidate::TraitMethod(_) => ctx.dot_receiver.as_ref()?.syntax(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
|
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
|
||||||
let current_module = ctx.scope.module()?;
|
let current_module = ctx.scope.module()?;
|
||||||
if let Some(dot_receiver) = &ctx.dot_receiver {
|
if let Some(dot_receiver) = &ctx.dot_receiver {
|
||||||
|
@ -117,7 +142,22 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
|
||||||
fuzzy_name,
|
fuzzy_name,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ImportAssets::for_fuzzy_path(current_module, ctx.path_qual.clone(), fuzzy_name, &ctx.sema)
|
let fuzzy_name_length = fuzzy_name.len();
|
||||||
|
let assets_for_path = ImportAssets::for_fuzzy_path(
|
||||||
|
current_module,
|
||||||
|
ctx.path_qual.clone(),
|
||||||
|
fuzzy_name,
|
||||||
|
&ctx.sema,
|
||||||
|
);
|
||||||
|
|
||||||
|
if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
|
||||||
|
&& fuzzy_name_length < 2
|
||||||
|
{
|
||||||
|
mark::hit!(ignore_short_input_for_path);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
assets_for_path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,6 +272,30 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_paths_are_ignored() {
|
||||||
|
mark::check!(ignore_short_input_for_path);
|
||||||
|
|
||||||
|
check(
|
||||||
|
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() {
|
||||||
|
t$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzy_completions_come_in_specific_order() {
|
fn fuzzy_completions_come_in_specific_order() {
|
||||||
mark::check!(certain_fuzzy_order_test);
|
mark::check!(certain_fuzzy_order_test);
|
||||||
|
@ -524,4 +588,67 @@ fn main() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_input_assoc_item_completion() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:dep
|
||||||
|
pub mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
const SPECIAL_CONST: u8;
|
||||||
|
type HumbleType;
|
||||||
|
fn weird_function();
|
||||||
|
fn random_method(&self);
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
const SPECIAL_CONST: u8 = 42;
|
||||||
|
type HumbleType = ();
|
||||||
|
fn weird_function() {}
|
||||||
|
fn random_method(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /main.rs crate:main deps:dep
|
||||||
|
fn main() {
|
||||||
|
let test_struct = dep::test_mod::TestStruct {};
|
||||||
|
test_struct.$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
me random_method() (dep::test_mod::TestTrait) fn random_method(&self)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:dep
|
||||||
|
pub mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
const SPECIAL_CONST: u8;
|
||||||
|
type HumbleType;
|
||||||
|
fn weird_function();
|
||||||
|
fn random_method(&self);
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
const SPECIAL_CONST: u8 = 42;
|
||||||
|
type HumbleType = ();
|
||||||
|
fn weird_function() {}
|
||||||
|
fn random_method(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /main.rs crate:main deps:dep
|
||||||
|
fn main() {
|
||||||
|
dep::test_mod::TestStruct::$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
ct SPECIAL_CONST (dep::test_mod::TestTrait)
|
||||||
|
fn weird_function() (dep::test_mod::TestTrait) fn weird_function()
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hash::FxHashSet;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
imports_locator::{self, AssocItemSearch},
|
imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,6 +173,7 @@ impl ImportAssets {
|
||||||
let current_crate = self.module_with_candidate.krate();
|
let current_crate = self.module_with_candidate.krate();
|
||||||
|
|
||||||
let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| {
|
let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| {
|
||||||
|
// TODO kb process all traits at once instead?
|
||||||
trait_candidates.clear();
|
trait_candidates.clear();
|
||||||
match &self.import_candidate {
|
match &self.import_candidate {
|
||||||
ImportCandidate::TraitAssocItem(trait_candidate) => {
|
ImportCandidate::TraitAssocItem(trait_candidate) => {
|
||||||
|
@ -191,6 +192,11 @@ impl ImportAssets {
|
||||||
None,
|
None,
|
||||||
|_, assoc| {
|
|_, assoc| {
|
||||||
if canidate_assoc_item == assoc {
|
if canidate_assoc_item == assoc {
|
||||||
|
if let AssocItem::Function(f) = assoc {
|
||||||
|
if f.self_param(db).is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(assoc_to_module_def(assoc))
|
Some(assoc_to_module_def(assoc))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -238,17 +244,21 @@ impl ImportAssets {
|
||||||
// see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
|
// see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
|
||||||
// and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
|
// and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
|
||||||
// for the details
|
// for the details
|
||||||
NameToImport::Fuzzy(fuzzy_name) => imports_locator::find_similar_imports(
|
NameToImport::Fuzzy(fuzzy_name) => {
|
||||||
sema,
|
let (assoc_item_search, limit) = match self.import_candidate {
|
||||||
current_crate,
|
|
||||||
fuzzy_name.clone(),
|
|
||||||
match self.import_candidate {
|
|
||||||
ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
|
ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
|
||||||
AssocItemSearch::AssocItemsOnly
|
(AssocItemSearch::AssocItemsOnly, None)
|
||||||
}
|
}
|
||||||
_ => AssocItemSearch::Exclude,
|
_ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
|
||||||
},
|
};
|
||||||
),
|
imports_locator::find_similar_imports(
|
||||||
|
sema,
|
||||||
|
current_crate,
|
||||||
|
fuzzy_name.clone(),
|
||||||
|
assoc_item_search,
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = unfiltered_imports
|
let mut res = unfiltered_imports
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
const QUERY_SEARCH_LIMIT: usize = 40;
|
pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
|
||||||
|
|
||||||
pub fn find_exact_imports<'a>(
|
pub fn find_exact_imports<'a>(
|
||||||
sema: &Semantics<'a, RootDatabase>,
|
sema: &Semantics<'a, RootDatabase>,
|
||||||
|
@ -29,11 +29,11 @@ pub fn find_exact_imports<'a>(
|
||||||
{
|
{
|
||||||
let mut local_query = symbol_index::Query::new(name_to_import.clone());
|
let mut local_query = symbol_index::Query::new(name_to_import.clone());
|
||||||
local_query.exact();
|
local_query.exact();
|
||||||
local_query.limit(QUERY_SEARCH_LIMIT);
|
local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
|
||||||
local_query
|
local_query
|
||||||
},
|
},
|
||||||
import_map::Query::new(name_to_import)
|
import_map::Query::new(name_to_import)
|
||||||
.limit(QUERY_SEARCH_LIMIT)
|
.limit(DEFAULT_QUERY_SEARCH_LIMIT)
|
||||||
.name_only()
|
.name_only()
|
||||||
.search_mode(import_map::SearchMode::Equals)
|
.search_mode(import_map::SearchMode::Equals)
|
||||||
.case_sensitive(),
|
.case_sensitive(),
|
||||||
|
@ -51,13 +51,13 @@ pub fn find_similar_imports<'a>(
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
fuzzy_search_string: String,
|
fuzzy_search_string: String,
|
||||||
assoc_item_search: AssocItemSearch,
|
assoc_item_search: AssocItemSearch,
|
||||||
|
limit: Option<usize>,
|
||||||
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
|
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
|
||||||
let _p = profile::span("find_similar_imports");
|
let _p = profile::span("find_similar_imports");
|
||||||
|
|
||||||
let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
|
let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
|
||||||
.search_mode(import_map::SearchMode::Fuzzy)
|
.search_mode(import_map::SearchMode::Fuzzy)
|
||||||
.name_only()
|
.name_only();
|
||||||
.limit(QUERY_SEARCH_LIMIT);
|
|
||||||
|
|
||||||
match assoc_item_search {
|
match assoc_item_search {
|
||||||
AssocItemSearch::Include => {}
|
AssocItemSearch::Include => {}
|
||||||
|
@ -70,7 +70,11 @@ pub fn find_similar_imports<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut local_query = symbol_index::Query::new(fuzzy_search_string);
|
let mut local_query = symbol_index::Query::new(fuzzy_search_string);
|
||||||
local_query.limit(QUERY_SEARCH_LIMIT);
|
|
||||||
|
if let Some(limit) = limit {
|
||||||
|
external_query = external_query.limit(limit);
|
||||||
|
local_query.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
let db = sema.db;
|
let db = sema.db;
|
||||||
Box::new(find_imports(sema, krate, local_query, external_query).filter(
|
Box::new(find_imports(sema, krate, local_query, external_query).filter(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue