let range = ctx.sema.original_range(&syntax_under_caret).range;
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 {
+ for import in proposed_imports {
acc.add_group(
&group,
AssistId("auto_import", AssistKind::QuickFix),
- format!("Import `{}`", &import),
+ format!("Import `{}`", import.display_path()),
range,
|builder| {
- let rewriter = insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use);
+ let rewriter = insert_use(
+ &scope,
+ mod_path_to_ast(import.import_path()),
+ ctx.config.insert_use,
+ );
builder.rewrite(rewriter);
},
);
};
let group_label = group_label(candidate);
- for (import, item) in proposed_imports {
+ for import in proposed_imports {
acc.add_group(
&group_label,
AssistId("qualify_path", AssistKind::QuickFix),
- label(candidate, &import),
+ label(candidate, import.display_path()),
range,
|builder| {
qualify_candidate.qualify(
|replace_with: String| builder.replace(range, replace_with),
- import,
- item,
+ import.import_path(),
+ import.item_to_import(),
)
},
);
}
impl QualifyCandidate<'_> {
- fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
- let import = mod_path_to_ast(&import);
+ fn qualify(
+ &self,
+ mut replacer: impl FnMut(String),
+ import: &hir::ModPath,
+ item: hir::ItemInNs,
+ ) {
+ let import = mod_path_to_ast(import);
match self {
QualifyCandidate::QualifierStart(segment, generics) => {
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
let mut all_mod_paths = import_assets
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
.into_iter()
- .map(|(mod_path, item_in_ns)| {
- let scope_item = match item_in_ns {
+ .map(|import| {
+ let proposed_def = match import.item_to_import() {
hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
};
- (mod_path, scope_item)
+ (import, proposed_def)
})
.filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def))
.collect::<Vec<_>>();
- all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
- compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
+ all_mod_paths.sort_by_cached_key(|(import, _)| {
+ compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
});
- acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
+ acc.add_all(all_mod_paths.into_iter().filter_map(|(import, definition)| {
let import_for_trait_assoc_item = match definition {
ScopeDef::ModuleDef(module_def) => module_def
.as_assoc_item(ctx.db)
.is_some(),
_ => false,
};
- let import_edit = ImportEdit {
- import_path,
- import_scope: import_scope.clone(),
- import_for_trait_assoc_item,
- };
+ let import_edit =
+ ImportEdit { import, import_scope: import_scope.clone(), import_for_trait_assoc_item };
render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
}));
Some(())
user_input_lowercased: &str,
) -> usize {
cov_mark::hit!(certain_fuzzy_order_test);
- let proposed_import_name = match proposed_mod_path.segments().last() {
+ let import_name = match proposed_mod_path.segments().last() {
Some(name) => name.to_string().to_lowercase(),
None => return usize::MAX,
};
- match proposed_import_name.match_indices(user_input_lowercased).next() {
+ match import_name.match_indices(user_input_lowercased).next() {
Some((first_matching_index, _)) => first_matching_index,
None => usize::MAX,
}
use std::fmt;
-use hir::{Documentation, ModPath, Mutability};
+use hir::{Documentation, Mutability};
use ide_db::{
helpers::{
+ import_assets::LocatedImport,
insert_use::{self, ImportScope, InsertUseConfig},
mod_path_to_ast, SnippetCap,
},
/// An extra import to add after the completion is applied.
#[derive(Debug, Clone)]
pub struct ImportEdit {
- pub import_path: ModPath,
+ pub import: LocatedImport,
pub import_scope: ImportScope,
pub import_for_trait_assoc_item: bool,
}
pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
let _p = profile::span("ImportEdit::to_text_edit");
- let rewriter =
- insert_use::insert_use(&self.import_scope, mod_path_to_ast(&self.import_path), cfg);
+ let rewriter = insert_use::insert_use(
+ &self.import_scope,
+ mod_path_to_ast(self.import.import_path()),
+ cfg,
+ );
let old_ast = rewriter.rewrite_root()?;
let mut import_insert = TextEdit::builder();
algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
let mut 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 {
- lookup = lookup.or_else(|| Some(label.clone()));
- insert_text = insert_text.or_else(|| Some(label.clone()));
- label = format!("{} ({})", label, import_to_add.import_path);
+ label = format!("{} ({})", label, display_path);
} else {
- let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
- let _ = import_path_without_last_segment.pop_segment();
-
- if !import_path_without_last_segment.segments().is_empty() {
- lookup = lookup.or_else(|| Some(label.clone()));
- insert_text = insert_text.or_else(|| Some(label.clone()));
- label = format!("{}::{}", import_path_without_last_segment, label);
- }
+ label = display_path.to_string();
}
}
use completions::flyimport::position_for_import;
use ide_db::{
- base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
+ base_db::FilePosition,
+ helpers::{import_assets::LocatedImport, insert_use::ImportScope},
+ imports_locator, RootDatabase,
};
use text_edit::TextEdit;
let current_module = ctx.sema.scope(position_for_import).module()?;
let current_crate = current_module.krate();
- let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
- .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)
- })
- .find(|mod_path| mod_path.to_string() == full_import_path)?;
+ let (import_path, item_to_import) =
+ imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
+ .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(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
+ let import = LocatedImport::new(import_path, item_to_import, None);
ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
.to_text_edit(config.insert_use)
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(),
- _ => import_edit.import_path.segments().last()?.to_string(),
+ _ => import_edit.import.display_path().segments().last()?.to_string(),
};
Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
item.completion_kind = CompletionKind::Magic;
}
}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct LocatedImport {
+ import_path: ModPath,
+ item_to_import: ItemInNs,
+ import_display_override: Option<(ModPath, ItemInNs)>,
+}
+
+impl LocatedImport {
+ pub fn new(
+ import_path: ModPath,
+ item_to_import: ItemInNs,
+ import_display_override: Option<(ModPath, ItemInNs)>,
+ ) -> Self {
+ Self { import_path, item_to_import, import_display_override }
+ }
+
+ pub fn display_path(&self) -> &ModPath {
+ self.import_display_override
+ .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.import_display_override.as_ref().map(|&(_, item)| item).unwrap_or(self.item_to_import)
+ }
+
+ pub fn item_to_import(&self) -> ItemInNs {
+ self.item_to_import
+ }
+}
+
impl ImportAssets {
pub fn import_candidate(&self) -> &ImportCandidate {
&self.import_candidate
&self,
sema: &Semantics<RootDatabase>,
prefix_kind: PrefixKind,
- ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
+ ) -> Vec<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<(hir::ModPath, hir::ItemInNs)> {
+ pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
let _p = profile::span("import_assets::search_for_relative_paths");
self.search_for(sema, None)
}
&self,
sema: &Semantics<RootDatabase>,
prefixed: Option<hir::PrefixKind>,
- ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
+ ) -> Vec<LocatedImport> {
let current_crate = self.module_with_candidate.krate();
let imports_for_candidate_name = match self.name_to_import() {
}
};
- let mut res = self
- .applicable_defs(sema, prefixed, imports_for_candidate_name)
- .filter(|(use_path, _)| use_path.len() > 1)
- .collect::<Vec<_>>();
- res.sort_by_cached_key(|(path, _)| path.clone());
- res
+ self.applicable_defs(sema.db, prefixed, imports_for_candidate_name)
+ .into_iter()
+ .filter(|import| import.import_path().len() > 1)
+ .collect()
}
- fn applicable_defs<'a>(
- &'a self,
- sema: &'a Semantics<RootDatabase>,
+ fn applicable_defs(
+ &self,
+ db: &RootDatabase,
prefixed: Option<hir::PrefixKind>,
- unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a,
- ) -> Box<dyn Iterator<Item = (ModPath, ItemInNs)> + 'a> {
+ unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
+ ) -> FxHashSet<LocatedImport> {
let current_crate = self.module_with_candidate.krate();
- let db = sema.db;
+
+ let import_path_locator =
+ |item| get_mod_path(db, item, &self.module_with_candidate, prefixed);
match &self.import_candidate {
- ImportCandidate::Path(path_candidate) => Box::new(
- path_applicable_items(
- db,
- path_candidate,
- &self.module_with_candidate,
- prefixed,
- unfiltered_defs,
- )
- .into_iter(),
- ),
- ImportCandidate::TraitAssocItem(trait_candidate) => Box::new(
- trait_applicable_defs(db, current_crate, trait_candidate, true, unfiltered_defs)
- .into_iter()
- .filter_map(move |item_to_search| {
- get_mod_path(db, item_to_search, &self.module_with_candidate, prefixed)
- .zip(Some(item_to_search))
- }),
+ ImportCandidate::Path(path_candidate) => {
+ path_applicable_imports(db, path_candidate, import_path_locator, unfiltered_defs)
+ }
+ ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
+ db,
+ current_crate,
+ trait_candidate,
+ true,
+ import_path_locator,
+ unfiltered_defs,
),
- ImportCandidate::TraitMethod(trait_candidate) => Box::new(
- trait_applicable_defs(db, current_crate, trait_candidate, false, unfiltered_defs)
- .into_iter()
- .filter_map(move |item_to_search| {
- get_mod_path(db, item_to_search, &self.module_with_candidate, prefixed)
- .zip(Some(item_to_search))
- }),
+ ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
+ db,
+ current_crate,
+ trait_candidate,
+ false,
+ import_path_locator,
+ unfiltered_defs,
),
}
}
}
-fn path_applicable_items<'a>(
- db: &'a RootDatabase,
- path_candidate: &'a PathImportCandidate,
- module_with_candidate: &hir::Module,
- prefixed: Option<hir::PrefixKind>,
- unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a,
-) -> FxHashSet<(ModPath, ItemInNs)> {
+fn path_applicable_imports(
+ db: &RootDatabase,
+ path_candidate: &PathImportCandidate,
+ import_path_locator: impl Fn(ItemInNs) -> Option<ModPath>,
+ unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
+) -> FxHashSet<LocatedImport> {
let applicable_items = unfiltered_defs
.filter_map(|def| {
let (assoc_original, candidate) = match def {
Some((assoc_original, candidate))
})
.filter_map(|(assoc_original, candidate)| {
- get_mod_path(db, candidate, module_with_candidate, prefixed)
- .zip(Some((assoc_original, candidate)))
+ import_path_locator(candidate).zip(Some((assoc_original, candidate)))
});
let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
Qualifier::Absent => {
return applicable_items
- .map(|(candidate_path, (_, candidate))| (candidate_path, candidate))
+ .map(|(candidate_path, (_, candidate))| {
+ LocatedImport::new(candidate_path, candidate, None)
+ })
.collect();
}
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier),
.filter_map(|(candidate_path, (assoc_original, candidate))| {
if let Some(assoc_original) = assoc_original {
if item_name(db, candidate)?.to_string() == unresolved_first_segment_string {
- return Some((candidate_path, ItemInNs::from(assoc_original)));
+ return Some(LocatedImport::new(
+ candidate_path.clone(),
+ ItemInNs::from(assoc_original),
+ Some((candidate_path, candidate)),
+ ));
}
}
let matching_module =
module_with_matching_name(db, &unresolved_first_segment_string, candidate)?;
- let path = get_mod_path(
- db,
- ItemInNs::from(ModuleDef::from(matching_module)),
- module_with_candidate,
- prefixed,
- )?;
- Some((path, candidate))
+ let item = ItemInNs::from(ModuleDef::from(matching_module));
+ Some(LocatedImport::new(
+ import_path_locator(item)?,
+ item,
+ Some((candidate_path, candidate)),
+ ))
})
.collect()
}
None
}
-fn trait_applicable_defs<'a>(
- db: &'a RootDatabase,
+fn trait_applicable_items(
+ db: &RootDatabase,
current_crate: Crate,
trait_candidate: &TraitImportCandidate,
trait_assoc_item: bool,
- unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a,
-) -> FxHashSet<ItemInNs> {
+ import_path_locator: impl Fn(ItemInNs) -> Option<ModPath>,
+ unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
+) -> FxHashSet<LocatedImport> {
let mut required_assoc_items = FxHashSet::default();
let trait_candidates = unfiltered_defs
})
.collect();
- let mut applicable_traits = FxHashSet::default();
+ let mut located_imports = FxHashSet::default();
if trait_assoc_item {
trait_candidate.receiver_ty.iterate_path_candidates(
return None;
}
}
- applicable_traits
- .insert(ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)));
+
+ let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
+ located_imports.insert(LocatedImport::new(
+ import_path_locator(item)?,
+ item,
+ None,
+ ));
}
None::<()>
},
|_, function| {
let assoc = function.as_assoc_item(db)?;
if required_assoc_items.contains(&assoc) {
- applicable_traits
- .insert(ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)));
+ let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
+ located_imports.insert(LocatedImport::new(
+ import_path_locator(item)?,
+ item,
+ None,
+ ));
}
None::<()>
},
)
};
- applicable_traits
+ located_imports
}
fn get_mod_path(
pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
-pub fn find_exact_imports<'a>(
- sema: &Semantics<'a, RootDatabase>,
+pub fn find_exact_imports(
+ sema: &Semantics<'_, RootDatabase>,
krate: Crate,
name_to_import: String,
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
}
pub fn find_similar_imports<'a>(
- sema: &Semantics<'a, RootDatabase>,
+ sema: &'a Semantics<'a, RootDatabase>,
krate: Crate,
fuzzy_search_string: String,
assoc_item_search: AssocItemSearch,
local_query.limit(limit);
}
- let db = sema.db;
Box::new(find_imports(sema, krate, local_query, external_query).filter(
move |import_candidate| match assoc_item_search {
AssocItemSearch::Include => true,
- AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
- AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
+ AssocItemSearch::Exclude => !is_assoc_item(import_candidate, sema.db),
+ AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, sema.db),
},
))
}
position: &TextDocumentPositionParams,
) -> Option<()> {
let import_edit = item.import_to_add()?;
- let full_import_path = import_edit.import_path.to_string();
- let imported_name = import_edit.import_path.segments().last()?.to_string();
+ let import_path = import_edit.import.import_path();
*resolve_data = Some(
to_value(CompletionResolveData {
position: position.to_owned(),
- full_import_path,
- imported_name,
+ 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(),