mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-28 17:53:56 +00:00
Work towards better import labels
This commit is contained in:
parent
4d4ac1d4fa
commit
33c83e72b9
@ -1094,4 +1094,27 @@ mod tests {
|
||||
expect![[r#""#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_with_path() {
|
||||
check_search(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:dep
|
||||
//- /dep.rs crate:dep
|
||||
pub mod foo {
|
||||
pub mod bar {
|
||||
pub mod baz {
|
||||
pub trait Display {
|
||||
fn fmt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
"main",
|
||||
Query::new("baz::fmt".to_string()).search_mode(SearchMode::Fuzzy),
|
||||
expect![[r#"
|
||||
dep::foo::bar::baz::Display::fmt (a)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +478,6 @@ impl Analysis {
|
||||
position: FilePosition,
|
||||
full_import_path: &str,
|
||||
imported_name: String,
|
||||
import_for_trait_assoc_item: bool,
|
||||
) -> Cancelable<Vec<TextEdit>> {
|
||||
Ok(self
|
||||
.with_db(|db| {
|
||||
@ -488,7 +487,6 @@ impl Analysis {
|
||||
position,
|
||||
full_import_path,
|
||||
imported_name,
|
||||
import_for_trait_assoc_item,
|
||||
)
|
||||
})?
|
||||
.unwrap_or_default())
|
||||
|
@ -93,17 +93,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
let group = import_group_message(import_assets.import_candidate());
|
||||
let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
|
||||
for import in proposed_imports {
|
||||
let name = match import.original_item_name(ctx.db()) {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
acc.add_group(
|
||||
&group,
|
||||
AssistId("auto_import", AssistKind::QuickFix),
|
||||
format!("Import `{}`", import.display_path()),
|
||||
format!("Import `{}`", name),
|
||||
range,
|
||||
|builder| {
|
||||
let rewriter = insert_use(
|
||||
&scope,
|
||||
mod_path_to_ast(import.import_path()),
|
||||
ctx.config.insert_use,
|
||||
);
|
||||
let rewriter =
|
||||
insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
|
||||
builder.rewrite(rewriter);
|
||||
},
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ use std::iter;
|
||||
|
||||
use hir::AsAssocItem;
|
||||
use ide_db::helpers::{
|
||||
import_assets::{ImportCandidate, Qualifier},
|
||||
import_assets::{ImportCandidate, LocatedImport, Qualifier},
|
||||
mod_path_to_ast,
|
||||
};
|
||||
use ide_db::RootDatabase;
|
||||
@ -78,13 +78,13 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
acc.add_group(
|
||||
&group_label,
|
||||
AssistId("qualify_path", AssistKind::QuickFix),
|
||||
label(candidate, import.display_path()),
|
||||
label(ctx.db(), candidate, &import),
|
||||
range,
|
||||
|builder| {
|
||||
qualify_candidate.qualify(
|
||||
|replace_with: String| builder.replace(range, replace_with),
|
||||
import.import_path(),
|
||||
import.item_to_import(),
|
||||
&import.import_path,
|
||||
import.item_to_import,
|
||||
)
|
||||
},
|
||||
);
|
||||
@ -197,17 +197,21 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
|
||||
GroupLabel(format!("Qualify {}", name))
|
||||
}
|
||||
|
||||
fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
|
||||
fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
|
||||
let display_path = match import.original_item_name(db) {
|
||||
Some(display_path) => display_path.to_string(),
|
||||
None => "{unknown}".to_string(),
|
||||
};
|
||||
match candidate {
|
||||
ImportCandidate::Path(candidate) => {
|
||||
if !matches!(candidate.qualifier, Qualifier::Absent) {
|
||||
format!("Qualify with `{}`", &import)
|
||||
format!("Qualify with `{}`", display_path)
|
||||
} else {
|
||||
format!("Qualify as `{}`", &import)
|
||||
format!("Qualify as `{}`", display_path)
|
||||
}
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
|
||||
ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
|
||||
ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
|
||||
ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use hir::ModuleDef;
|
||||
use ide_db::helpers::mod_path_to_ast;
|
||||
use ide_db::imports_locator;
|
||||
use ide_db::items_locator;
|
||||
use itertools::Itertools;
|
||||
use syntax::{
|
||||
ast::{self, make, AstNode, NameOwner},
|
||||
@ -64,13 +65,14 @@ 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_exact_imports(
|
||||
let found_traits = items_locator::with_for_exact_name(
|
||||
&ctx.sema,
|
||||
current_crate,
|
||||
trait_token.text().to_string(),
|
||||
)
|
||||
.filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
|
||||
either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
|
||||
.into_iter()
|
||||
.filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
|
||||
ModuleDef::Trait(trait_) => Some(trait_),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|trait_| {
|
||||
|
@ -87,11 +87,12 @@
|
||||
//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
|
||||
//! capability enabled.
|
||||
|
||||
use hir::{AsAssocItem, ModPath, ModuleDef, ScopeDef};
|
||||
use hir::ModPath;
|
||||
use ide_db::helpers::{
|
||||
import_assets::{ImportAssets, ImportCandidate},
|
||||
insert_use::ImportScope,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use syntax::{AstNode, SyntaxNode, T};
|
||||
|
||||
use crate::{
|
||||
@ -130,27 +131,23 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||
&ctx.sema,
|
||||
)?;
|
||||
|
||||
let mut all_imports =
|
||||
import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
|
||||
all_imports.sort_by_cached_key(|import| {
|
||||
compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
|
||||
});
|
||||
|
||||
acc.add_all(all_imports.into_iter().filter_map(|import| {
|
||||
let import_for_trait_assoc_item = import
|
||||
.item_to_display()
|
||||
.as_module_def_id()
|
||||
.and_then(|module_def_id| {
|
||||
ModuleDef::from(module_def_id).as_assoc_item(ctx.db)?.containing_trait(ctx.db)
|
||||
acc.add_all(
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
|
||||
.into_iter()
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(
|
||||
&located_import.import_path,
|
||||
&user_input_lowercased,
|
||||
)
|
||||
})
|
||||
.is_some();
|
||||
let def_to_display = ScopeDef::from(import.item_to_display());
|
||||
render_resolution_with_import(
|
||||
RenderContext::new(ctx),
|
||||
ImportEdit { import, import_scope: import_scope.clone(), import_for_trait_assoc_item },
|
||||
&def_to_display,
|
||||
)
|
||||
}));
|
||||
.filter_map(|import| {
|
||||
render_resolution_with_import(
|
||||
RenderContext::new(ctx),
|
||||
ImportEdit { import, import_scope: import_scope.clone() },
|
||||
)
|
||||
}),
|
||||
);
|
||||
Some(())
|
||||
}
|
||||
|
||||
@ -190,6 +187,7 @@ fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<I
|
||||
ctx.scope.clone(),
|
||||
)?;
|
||||
|
||||
// TODO kb bad: with the path prefix, the "min 3 symbols" limit applies. Fix in a separate PR on the symbol_index level
|
||||
if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
|
||||
&& fuzzy_name_length < 2
|
||||
{
|
||||
@ -796,9 +794,7 @@ fn main() {
|
||||
|
||||
#[test]
|
||||
fn unresolved_qualifier() {
|
||||
check_edit(
|
||||
"Item",
|
||||
r#"
|
||||
let fixture = r#"
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub mod baz {
|
||||
@ -809,31 +805,34 @@ mod foo {
|
||||
|
||||
fn main() {
|
||||
bar::baz::Ite$0
|
||||
}
|
||||
"#,
|
||||
}"#;
|
||||
|
||||
check(fixture, expect![["st Item (foo::bar::baz::Item)"]]);
|
||||
|
||||
check_edit(
|
||||
"Item",
|
||||
fixture,
|
||||
r#"
|
||||
use foo::bar;
|
||||
use foo::bar;
|
||||
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub mod baz {
|
||||
pub struct Item;
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub mod baz {
|
||||
pub struct Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
bar::baz::Item
|
||||
}
|
||||
"#,
|
||||
fn main() {
|
||||
bar::baz::Item
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_assoc_item_container() {
|
||||
check_edit(
|
||||
"TEST_ASSOC",
|
||||
r#"
|
||||
let fixture = r#"
|
||||
mod foo {
|
||||
pub struct Item;
|
||||
|
||||
@ -844,8 +843,13 @@ mod foo {
|
||||
|
||||
fn main() {
|
||||
Item::TEST_A$0
|
||||
}
|
||||
"#,
|
||||
}"#;
|
||||
|
||||
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
|
||||
|
||||
check_edit(
|
||||
"TEST_ASSOC",
|
||||
fixture,
|
||||
r#"
|
||||
use foo::Item;
|
||||
|
||||
@ -866,9 +870,7 @@ fn main() {
|
||||
|
||||
#[test]
|
||||
fn unresolved_assoc_item_container_with_path() {
|
||||
check_edit(
|
||||
"TEST_ASSOC",
|
||||
r#"
|
||||
let fixture = r#"
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub struct Item;
|
||||
@ -881,8 +883,13 @@ mod foo {
|
||||
|
||||
fn main() {
|
||||
bar::Item::TEST_A$0
|
||||
}
|
||||
"#,
|
||||
}"#;
|
||||
|
||||
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
|
||||
|
||||
check_edit(
|
||||
"TEST_ASSOC",
|
||||
fixture,
|
||||
r#"
|
||||
use foo::bar;
|
||||
|
||||
|
@ -275,7 +275,6 @@ impl CompletionItem {
|
||||
pub struct ImportEdit {
|
||||
pub import: LocatedImport,
|
||||
pub import_scope: ImportScope,
|
||||
pub import_for_trait_assoc_item: bool,
|
||||
}
|
||||
|
||||
impl ImportEdit {
|
||||
@ -286,7 +285,7 @@ impl ImportEdit {
|
||||
|
||||
let rewriter = insert_use::insert_use(
|
||||
&self.import_scope,
|
||||
mod_path_to_ast(self.import.import_path()),
|
||||
mod_path_to_ast(&self.import.import_path),
|
||||
cfg,
|
||||
);
|
||||
let old_ast = rewriter.rewrite_root()?;
|
||||
@ -303,6 +302,7 @@ impl ImportEdit {
|
||||
pub(crate) struct Builder {
|
||||
source_range: TextRange,
|
||||
completion_kind: CompletionKind,
|
||||
// TODO kb also add a db here, to resolve the completion label?
|
||||
import_to_add: Option<ImportEdit>,
|
||||
label: String,
|
||||
insert_text: Option<String>,
|
||||
@ -322,19 +322,22 @@ impl Builder {
|
||||
pub(crate) fn build(self) -> CompletionItem {
|
||||
let _p = profile::span("item::Builder::build");
|
||||
|
||||
let mut label = self.label;
|
||||
let mut lookup = self.lookup;
|
||||
let mut insert_text = self.insert_text;
|
||||
let label = self.label;
|
||||
let lookup = self.lookup;
|
||||
let insert_text = self.insert_text;
|
||||
|
||||
if let Some(import_to_add) = self.import_to_add.as_ref() {
|
||||
lookup = lookup.or_else(|| Some(label.clone()));
|
||||
insert_text = insert_text.or_else(|| Some(label.clone()));
|
||||
let display_path = import_to_add.import.display_path();
|
||||
if import_to_add.import_for_trait_assoc_item {
|
||||
label = format!("{} ({})", label, display_path);
|
||||
} else {
|
||||
label = display_path.to_string();
|
||||
}
|
||||
if let Some(_import_to_add) = self.import_to_add.as_ref() {
|
||||
todo!("todo kb")
|
||||
// let import = &import_to_add.import;
|
||||
// let item_to_import = import.item_to_import();
|
||||
// lookup = lookup.or_else(|| Some(label.clone()));
|
||||
// insert_text = insert_text.or_else(|| Some(label.clone()));
|
||||
// let display_path = import_to_add.import.display_path();
|
||||
// if import_to_add.import {
|
||||
// label = format!("{} ({})", label, display_path);
|
||||
// } else {
|
||||
// label = display_path.to_string();
|
||||
// }
|
||||
}
|
||||
|
||||
let text_edit = match self.text_edit {
|
||||
@ -438,8 +441,8 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<CompletionItem> for Builder {
|
||||
fn into(self) -> CompletionItem {
|
||||
self.build()
|
||||
}
|
||||
}
|
||||
// impl<'a> Into<CompletionItem> for Builder {
|
||||
// fn into(self) -> CompletionItem {
|
||||
// self.build()
|
||||
// }
|
||||
// }
|
||||
|
@ -15,7 +15,7 @@ use completions::flyimport::position_for_import;
|
||||
use ide_db::{
|
||||
base_db::FilePosition,
|
||||
helpers::{import_assets::LocatedImport, insert_use::ImportScope},
|
||||
imports_locator, RootDatabase,
|
||||
items_locator, RootDatabase,
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
@ -141,7 +141,6 @@ pub fn resolve_completion_edits(
|
||||
position: FilePosition,
|
||||
full_import_path: &str,
|
||||
imported_name: String,
|
||||
import_for_trait_assoc_item: bool,
|
||||
) -> Option<Vec<TextEdit>> {
|
||||
let ctx = CompletionContext::new(db, position, config)?;
|
||||
let position_for_import = position_for_import(&ctx, None)?;
|
||||
@ -151,19 +150,17 @@ pub fn resolve_completion_edits(
|
||||
let current_crate = current_module.krate();
|
||||
|
||||
let (import_path, item_to_import) =
|
||||
imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
|
||||
items_locator::with_for_exact_name(&ctx.sema, current_crate, imported_name)
|
||||
.into_iter()
|
||||
.filter_map(|candidate| {
|
||||
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
|
||||
current_module
|
||||
.find_use_path_prefixed(db, item, config.insert_use.prefix_kind)
|
||||
.zip(Some(item))
|
||||
.find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
|
||||
.zip(Some(candidate))
|
||||
})
|
||||
.find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
|
||||
let import = LocatedImport::new(import_path, item_to_import, None);
|
||||
let import = LocatedImport::new(import_path, item_to_import, item_to_import);
|
||||
|
||||
ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
|
||||
.to_text_edit(config.insert_use)
|
||||
.map(|edit| vec![edit])
|
||||
ImportEdit { import, import_scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -53,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
|
||||
pub(crate) fn render_resolution_with_import<'a>(
|
||||
ctx: RenderContext<'a>,
|
||||
import_edit: ImportEdit,
|
||||
resolution: &ScopeDef,
|
||||
) -> Option<CompletionItem> {
|
||||
let resolution = ScopeDef::from(import_edit.import.original_item);
|
||||
let local_name = match resolution {
|
||||
ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
|
||||
ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
|
||||
ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
|
||||
_ => item_name(ctx.db(), import_edit.import.item_to_display())?.to_string(),
|
||||
_ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
|
||||
};
|
||||
Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
|
||||
item.completion_kind = CompletionKind::Magic;
|
||||
item
|
||||
})
|
||||
Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
|
||||
|mut item| {
|
||||
item.completion_kind = CompletionKind::Magic;
|
||||
item
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Interface for data and methods required for items rendering.
|
||||
|
@ -1,14 +1,13 @@
|
||||
//! Look up accessible paths for items.
|
||||
use either::Either;
|
||||
use hir::{
|
||||
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
|
||||
ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
|
||||
ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
use crate::{
|
||||
imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
|
||||
items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
|
||||
RootDatabase,
|
||||
};
|
||||
|
||||
@ -130,34 +129,23 @@ impl<'a> ImportAssets<'a> {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct LocatedImport {
|
||||
import_path: ModPath,
|
||||
item_to_import: ItemInNs,
|
||||
data_to_display: Option<(ModPath, ItemInNs)>,
|
||||
pub import_path: ModPath,
|
||||
pub item_to_import: ItemInNs,
|
||||
pub original_item: ItemInNs,
|
||||
}
|
||||
|
||||
impl LocatedImport {
|
||||
pub fn new(
|
||||
import_path: ModPath,
|
||||
item_to_import: ItemInNs,
|
||||
data_to_display: Option<(ModPath, ItemInNs)>,
|
||||
) -> Self {
|
||||
Self { import_path, item_to_import, data_to_display }
|
||||
pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self {
|
||||
Self { import_path, item_to_import, original_item }
|
||||
}
|
||||
|
||||
pub fn display_path(&self) -> &ModPath {
|
||||
self.data_to_display.as_ref().map(|(mod_path, _)| mod_path).unwrap_or(&self.import_path)
|
||||
}
|
||||
|
||||
pub fn import_path(&self) -> &ModPath {
|
||||
&self.import_path
|
||||
}
|
||||
|
||||
pub fn item_to_display(&self) -> ItemInNs {
|
||||
self.data_to_display.as_ref().map(|&(_, item)| item).unwrap_or(self.item_to_import)
|
||||
}
|
||||
|
||||
pub fn item_to_import(&self) -> ItemInNs {
|
||||
self.item_to_import
|
||||
pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> {
|
||||
match self.original_item {
|
||||
ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => {
|
||||
ModuleDef::from(module_def_id).name(db)
|
||||
}
|
||||
ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,25 +154,20 @@ impl<'a> ImportAssets<'a> {
|
||||
&self.import_candidate
|
||||
}
|
||||
|
||||
fn name_to_import(&self) -> &NameToImport {
|
||||
match &self.import_candidate {
|
||||
ImportCandidate::Path(candidate) => &candidate.name,
|
||||
ImportCandidate::TraitAssocItem(candidate)
|
||||
| ImportCandidate::TraitMethod(candidate) => &candidate.name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_for_imports(
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
prefix_kind: PrefixKind,
|
||||
) -> Vec<LocatedImport> {
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let _p = profile::span("import_assets::search_for_imports");
|
||||
self.search_for(sema, Some(prefix_kind))
|
||||
}
|
||||
|
||||
/// This may return non-absolute paths if a part of the returned path is already imported into scope.
|
||||
pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
|
||||
pub fn search_for_relative_paths(
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let _p = profile::span("import_assets::search_for_relative_paths");
|
||||
self.search_for(sema, None)
|
||||
}
|
||||
@ -193,14 +176,13 @@ impl<'a> ImportAssets<'a> {
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
prefixed: Option<PrefixKind>,
|
||||
) -> Vec<LocatedImport> {
|
||||
let current_crate = self.module_with_candidate.krate();
|
||||
let scope_definitions = self.scope_definitions();
|
||||
|
||||
let defs_for_candidate_name = match self.name_to_import() {
|
||||
NameToImport::Exact(exact_name) => {
|
||||
imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
|
||||
}
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let items_with_candidate_name = match self.name_to_import() {
|
||||
NameToImport::Exact(exact_name) => items_locator::with_for_exact_name(
|
||||
sema,
|
||||
self.module_with_candidate.krate(),
|
||||
exact_name.clone(),
|
||||
),
|
||||
// FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
|
||||
// instead, we need to look up all trait impls for a certain struct and search through them only
|
||||
// see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
|
||||
@ -213,9 +195,9 @@ impl<'a> ImportAssets<'a> {
|
||||
(AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
|
||||
};
|
||||
|
||||
imports_locator::find_similar_imports(
|
||||
items_locator::with_similar_name(
|
||||
sema,
|
||||
current_crate,
|
||||
self.module_with_candidate.krate(),
|
||||
fuzzy_name.clone(),
|
||||
assoc_item_search,
|
||||
limit,
|
||||
@ -223,10 +205,11 @@ impl<'a> ImportAssets<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.applicable_defs(sema.db, prefixed, defs_for_candidate_name)
|
||||
let scope_definitions = self.scope_definitions();
|
||||
self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
|
||||
.into_iter()
|
||||
.filter(|import| import.import_path().len() > 1)
|
||||
.filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import())))
|
||||
.filter(|import| import.import_path.len() > 1)
|
||||
.filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -238,11 +221,19 @@ impl<'a> ImportAssets<'a> {
|
||||
scope_definitions
|
||||
}
|
||||
|
||||
fn name_to_import(&self) -> &NameToImport {
|
||||
match &self.import_candidate {
|
||||
ImportCandidate::Path(candidate) => &candidate.name,
|
||||
ImportCandidate::TraitAssocItem(candidate)
|
||||
| ImportCandidate::TraitMethod(candidate) => &candidate.name,
|
||||
}
|
||||
}
|
||||
|
||||
fn applicable_defs(
|
||||
&self,
|
||||
db: &RootDatabase,
|
||||
prefixed: Option<PrefixKind>,
|
||||
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
|
||||
items_with_candidate_name: FxHashSet<ItemInNs>,
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let _p = profile::span("import_assets::applicable_defs");
|
||||
let current_crate = self.module_with_candidate.krate();
|
||||
@ -251,7 +242,7 @@ impl<'a> ImportAssets<'a> {
|
||||
|
||||
match &self.import_candidate {
|
||||
ImportCandidate::Path(path_candidate) => {
|
||||
path_applicable_imports(db, path_candidate, mod_path, defs_for_candidate_name)
|
||||
path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
|
||||
db,
|
||||
@ -259,7 +250,7 @@ impl<'a> ImportAssets<'a> {
|
||||
trait_candidate,
|
||||
true,
|
||||
mod_path,
|
||||
defs_for_candidate_name,
|
||||
items_with_candidate_name,
|
||||
),
|
||||
ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
|
||||
db,
|
||||
@ -267,7 +258,7 @@ impl<'a> ImportAssets<'a> {
|
||||
trait_candidate,
|
||||
false,
|
||||
mod_path,
|
||||
defs_for_candidate_name,
|
||||
items_with_candidate_name,
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -277,17 +268,15 @@ fn path_applicable_imports(
|
||||
db: &RootDatabase,
|
||||
path_candidate: &PathImportCandidate,
|
||||
mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
|
||||
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
|
||||
items_with_candidate_name: FxHashSet<ItemInNs>,
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let _p = profile::span("import_assets::path_applicable_imports");
|
||||
|
||||
let items_for_candidate_name =
|
||||
defs_for_candidate_name.map(|def| def.either(ItemInNs::from, ItemInNs::from));
|
||||
|
||||
let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
|
||||
Qualifier::Absent => {
|
||||
return items_for_candidate_name
|
||||
.filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, None)))
|
||||
return items_with_candidate_name
|
||||
.into_iter()
|
||||
.filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, item)))
|
||||
.collect();
|
||||
}
|
||||
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => {
|
||||
@ -295,7 +284,8 @@ fn path_applicable_imports(
|
||||
}
|
||||
};
|
||||
|
||||
items_for_candidate_name
|
||||
items_with_candidate_name
|
||||
.into_iter()
|
||||
.filter_map(|item| {
|
||||
import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
|
||||
})
|
||||
@ -336,7 +326,6 @@ fn import_for_item(
|
||||
}
|
||||
|
||||
let segment_import = find_import_for_segment(db, item_candidate, &unresolved_first_segment)?;
|
||||
let data_to_display = Some((import_path_candidate.clone(), original_item));
|
||||
Some(match (segment_import == item_candidate, trait_to_import) {
|
||||
(true, Some(_)) => {
|
||||
// FIXME we should be able to import both the trait and the segment,
|
||||
@ -345,11 +334,11 @@ fn import_for_item(
|
||||
return None;
|
||||
}
|
||||
(false, Some(trait_to_import)) => {
|
||||
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, data_to_display)
|
||||
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
|
||||
}
|
||||
(true, None) => LocatedImport::new(import_path_candidate, item_candidate, data_to_display),
|
||||
(true, None) => LocatedImport::new(import_path_candidate, item_candidate, original_item),
|
||||
(false, None) => {
|
||||
LocatedImport::new(mod_path(segment_import)?, segment_import, data_to_display)
|
||||
LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -399,16 +388,14 @@ fn trait_applicable_items(
|
||||
trait_candidate: &TraitImportCandidate,
|
||||
trait_assoc_item: bool,
|
||||
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
|
||||
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
|
||||
items_with_candidate_name: FxHashSet<ItemInNs>,
|
||||
) -> FxHashSet<LocatedImport> {
|
||||
let _p = profile::span("import_assets::trait_applicable_items");
|
||||
let mut required_assoc_items = FxHashSet::default();
|
||||
|
||||
let trait_candidates = defs_for_candidate_name
|
||||
.filter_map(|input| match input {
|
||||
Either::Left(module_def) => module_def.as_assoc_item(db),
|
||||
_ => None,
|
||||
})
|
||||
let trait_candidates = items_with_candidate_name
|
||||
.into_iter()
|
||||
.filter_map(|input| ModuleDef::from(input.as_module_def_id()?).as_assoc_item(db))
|
||||
.filter_map(|assoc| {
|
||||
let assoc_item_trait = assoc.containing_trait(db)?;
|
||||
required_assoc_items.insert(assoc);
|
||||
@ -433,20 +420,10 @@ fn trait_applicable_items(
|
||||
}
|
||||
|
||||
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
||||
let item_path = mod_path(item)?;
|
||||
|
||||
let assoc_item = assoc_to_item(assoc);
|
||||
let assoc_item_path = match assoc.container(db) {
|
||||
AssocItemContainer::Trait(_) => item_path.clone(),
|
||||
AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from(
|
||||
ModuleDef::from(impl_.target_ty(db).as_adt()?),
|
||||
))?,
|
||||
};
|
||||
|
||||
located_imports.insert(LocatedImport::new(
|
||||
item_path,
|
||||
mod_path(item)?,
|
||||
item,
|
||||
Some((assoc_item_path, assoc_item)),
|
||||
assoc_to_item(assoc),
|
||||
));
|
||||
}
|
||||
None::<()>
|
||||
@ -462,20 +439,10 @@ fn trait_applicable_items(
|
||||
let assoc = function.as_assoc_item(db)?;
|
||||
if required_assoc_items.contains(&assoc) {
|
||||
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
||||
let item_path = mod_path(item)?;
|
||||
|
||||
let assoc_item = assoc_to_item(assoc);
|
||||
let assoc_item_path = match assoc.container(db) {
|
||||
AssocItemContainer::Trait(_) => item_path.clone(),
|
||||
AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from(
|
||||
ModuleDef::from(impl_.target_ty(db).as_adt()?),
|
||||
))?,
|
||||
};
|
||||
|
||||
located_imports.insert(LocatedImport::new(
|
||||
item_path,
|
||||
mod_path(item)?,
|
||||
item,
|
||||
Some((assoc_item_path, assoc_item)),
|
||||
assoc_to_item(assoc),
|
||||
));
|
||||
}
|
||||
None::<()>
|
||||
|
@ -1,9 +1,10 @@
|
||||
//! This module contains an import search functionality that is provided to the assists module.
|
||||
//! Later, this should be moved away to a separate crate that is accessible from the assists module.
|
||||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
import_map::{self, ImportKind},
|
||||
AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
|
||||
AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
|
||||
};
|
||||
use syntax::{ast, AstNode, SyntaxKind::NAME};
|
||||
|
||||
@ -12,32 +13,31 @@ use crate::{
|
||||
symbol_index::{self, FileSymbol},
|
||||
RootDatabase,
|
||||
};
|
||||
use either::Either;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
|
||||
|
||||
pub fn find_exact_imports(
|
||||
pub fn with_for_exact_name(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
krate: Crate,
|
||||
name_to_import: String,
|
||||
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
|
||||
exact_name: String,
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
let _p = profile::span("find_exact_imports");
|
||||
Box::new(find_imports(
|
||||
find_items(
|
||||
sema,
|
||||
krate,
|
||||
{
|
||||
let mut local_query = symbol_index::Query::new(name_to_import.clone());
|
||||
let mut local_query = symbol_index::Query::new(exact_name.clone());
|
||||
local_query.exact();
|
||||
local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
|
||||
local_query
|
||||
},
|
||||
import_map::Query::new(name_to_import)
|
||||
import_map::Query::new(exact_name)
|
||||
.limit(DEFAULT_QUERY_SEARCH_LIMIT)
|
||||
.name_only()
|
||||
.search_mode(import_map::SearchMode::Equals)
|
||||
.case_sensitive(),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -47,13 +47,13 @@ pub enum AssocItemSearch {
|
||||
AssocItemsOnly,
|
||||
}
|
||||
|
||||
pub fn find_similar_imports<'a>(
|
||||
sema: &'a Semantics<'a, RootDatabase>,
|
||||
pub fn with_similar_name(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
krate: Crate,
|
||||
fuzzy_search_string: String,
|
||||
assoc_item_search: AssocItemSearch,
|
||||
limit: Option<usize>,
|
||||
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
let _p = profile::span("find_similar_imports");
|
||||
|
||||
let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
|
||||
@ -77,36 +77,39 @@ pub fn find_similar_imports<'a>(
|
||||
local_query.limit(limit);
|
||||
}
|
||||
|
||||
Box::new(find_imports(sema, krate, local_query, external_query).filter(
|
||||
move |import_candidate| match assoc_item_search {
|
||||
find_items(sema, krate, local_query, external_query)
|
||||
.into_iter()
|
||||
.filter(move |&item| match assoc_item_search {
|
||||
AssocItemSearch::Include => true,
|
||||
AssocItemSearch::Exclude => !is_assoc_item(import_candidate, sema.db),
|
||||
AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, sema.db),
|
||||
},
|
||||
))
|
||||
AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
|
||||
AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
|
||||
match import_candidate {
|
||||
Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
|
||||
Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
|
||||
Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
|
||||
item.as_module_def_id()
|
||||
.and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn find_imports<'a>(
|
||||
sema: &Semantics<'a, RootDatabase>,
|
||||
fn find_items(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
krate: Crate,
|
||||
local_query: symbol_index::Query,
|
||||
external_query: import_map::Query,
|
||||
) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
let _p = profile::span("find_similar_imports");
|
||||
let db = sema.db;
|
||||
|
||||
// Query dependencies first.
|
||||
let mut candidates: FxHashSet<_> =
|
||||
krate.query_external_importables(db, external_query).collect();
|
||||
let mut candidates = krate
|
||||
.query_external_importables(db, external_query)
|
||||
.map(|external_importable| match external_importable {
|
||||
Either::Left(module_def) => ItemInNs::from(module_def),
|
||||
Either::Right(macro_def) => ItemInNs::from(macro_def),
|
||||
})
|
||||
.collect::<FxHashSet<_>>();
|
||||
|
||||
// Query the local crate using the symbol index.
|
||||
let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
|
||||
@ -114,19 +117,19 @@ fn find_imports<'a>(
|
||||
candidates.extend(
|
||||
local_results
|
||||
.into_iter()
|
||||
.filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
|
||||
.filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
|
||||
.filter_map(|name_definition_to_import| match name_definition_to_import {
|
||||
Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
|
||||
Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
|
||||
Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
|
||||
Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
|
||||
_ => None,
|
||||
}),
|
||||
);
|
||||
|
||||
candidates.into_iter()
|
||||
candidates
|
||||
}
|
||||
|
||||
fn get_name_definition<'a>(
|
||||
sema: &Semantics<'a, RootDatabase>,
|
||||
fn get_name_definition(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
import_candidate: &FileSymbol,
|
||||
) -> Option<Definition> {
|
||||
let _p = profile::span("get_name_definition");
|
@ -8,7 +8,7 @@ pub mod line_index;
|
||||
pub mod symbol_index;
|
||||
pub mod defs;
|
||||
pub mod search;
|
||||
pub mod imports_locator;
|
||||
pub mod items_locator;
|
||||
pub mod source_change;
|
||||
pub mod ty_filter;
|
||||
pub mod traits;
|
||||
|
@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
|
||||
FilePosition { file_id, offset },
|
||||
&resolve_data.full_import_path,
|
||||
resolve_data.imported_name,
|
||||
resolve_data.import_for_trait_assoc_item,
|
||||
)?
|
||||
.into_iter()
|
||||
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
|
||||
@ -1525,7 +1524,6 @@ struct CompletionResolveData {
|
||||
position: lsp_types::TextDocumentPositionParams,
|
||||
full_import_path: String,
|
||||
imported_name: String,
|
||||
import_for_trait_assoc_item: bool,
|
||||
}
|
||||
|
||||
fn fill_resolve_data(
|
||||
@ -1534,14 +1532,13 @@ fn fill_resolve_data(
|
||||
position: &TextDocumentPositionParams,
|
||||
) -> Option<()> {
|
||||
let import_edit = item.import_to_add()?;
|
||||
let import_path = import_edit.import.import_path();
|
||||
let import_path = &import_edit.import.import_path;
|
||||
|
||||
*resolve_data = Some(
|
||||
to_value(CompletionResolveData {
|
||||
position: position.to_owned(),
|
||||
full_import_path: import_path.to_string(),
|
||||
imported_name: import_path.segments().last()?.to_string(),
|
||||
import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user