From 582cee2cdf5355b681f14bbb33bd5c431c284d87 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 25 Feb 2021 01:06:31 +0200 Subject: Return more data about located imports --- crates/ide_assists/src/handlers/auto_import.rs | 10 +- crates/ide_assists/src/handlers/qualify_path.rs | 17 +- crates/ide_completion/src/completions/flyimport.rs | 23 ++- crates/ide_completion/src/item.rs | 28 ++-- crates/ide_completion/src/lib.rs | 20 ++- crates/ide_completion/src/render.rs | 2 +- crates/ide_db/src/helpers/import_assets.rs | 179 +++++++++++++-------- crates/ide_db/src/imports_locator.rs | 11 +- crates/rust-analyzer/src/handlers.rs | 7 +- 9 files changed, 172 insertions(+), 125 deletions(-) diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index 5fe3f47fd..7188724be 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs @@ -92,14 +92,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> 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); }, ); diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index c0463311e..a40cdd80e 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs @@ -74,17 +74,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> }; 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(), ) }, ); @@ -100,8 +100,13 @@ enum QualifyCandidate<'db> { } 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); diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 64b60bbdd..8ff76688e 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -96,21 +96,21 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) 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::>(); - 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) @@ -118,11 +118,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) .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(()) @@ -186,11 +183,11 @@ fn compute_fuzzy_completion_order_key( 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, } diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 9b2435c4b..0390fe226 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs @@ -2,9 +2,10 @@ 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, }, @@ -272,7 +273,7 @@ impl CompletionItem { /// 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, } @@ -283,8 +284,11 @@ impl ImportEdit { pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option { 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); @@ -323,19 +327,13 @@ impl Builder { 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(); } } diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index b0b809791..ca2e5e706 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs @@ -13,7 +13,9 @@ mod completions; 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; @@ -148,12 +150,16 @@ pub fn resolve_completion_edits( 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) diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index dcfac23c5..df26e7642 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -56,7 +56,7 @@ pub(crate) fn render_resolution_with_import<'a>( 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; diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index dc3b92a64..d8bf61aaa 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -117,6 +117,42 @@ impl ImportAssets { } } +#[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 @@ -134,16 +170,13 @@ impl ImportAssets { &self, sema: &Semantics, prefix_kind: PrefixKind, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { + ) -> Vec { 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, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { + pub fn search_for_relative_paths(&self, sema: &Semantics) -> Vec { let _p = profile::span("import_assets::search_for_relative_paths"); self.search_for(sema, None) } @@ -152,7 +185,7 @@ impl ImportAssets { &self, sema: &Semantics, prefixed: Option, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { + ) -> Vec { let current_crate = self.module_with_candidate.krate(); let imports_for_candidate_name = match self.name_to_import() { @@ -181,61 +214,53 @@ impl ImportAssets { } }; - let mut res = self - .applicable_defs(sema, prefixed, imports_for_candidate_name) - .filter(|(use_path, _)| use_path.len() > 1) - .collect::>(); - 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, + fn applicable_defs( + &self, + db: &RootDatabase, prefixed: Option, - unfiltered_defs: impl Iterator> + 'a, - ) -> Box + 'a> { + unfiltered_defs: impl Iterator>, + ) -> FxHashSet { 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, - unfiltered_defs: impl Iterator> + 'a, -) -> FxHashSet<(ModPath, ItemInNs)> { +fn path_applicable_imports( + db: &RootDatabase, + path_candidate: &PathImportCandidate, + import_path_locator: impl Fn(ItemInNs) -> Option, + unfiltered_defs: impl Iterator>, +) -> FxHashSet { let applicable_items = unfiltered_defs .filter_map(|def| { let (assoc_original, candidate) = match def { @@ -256,14 +281,15 @@ fn path_applicable_items<'a>( 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), @@ -283,19 +309,22 @@ fn path_applicable_items<'a>( .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() } @@ -336,13 +365,14 @@ fn module_with_matching_name( 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> + 'a, -) -> FxHashSet { + import_path_locator: impl Fn(ItemInNs) -> Option, + unfiltered_defs: impl Iterator>, +) -> FxHashSet { let mut required_assoc_items = FxHashSet::default(); let trait_candidates = unfiltered_defs @@ -357,7 +387,7 @@ fn trait_applicable_defs<'a>( }) .collect(); - let mut applicable_traits = FxHashSet::default(); + let mut located_imports = FxHashSet::default(); if trait_assoc_item { trait_candidate.receiver_ty.iterate_path_candidates( @@ -372,8 +402,13 @@ fn trait_applicable_defs<'a>( 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::<()> }, @@ -387,15 +422,19 @@ fn trait_applicable_defs<'a>( |_, 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( diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index 480cbf1ea..fd700e04f 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs @@ -17,8 +17,8 @@ use rustc_hash::FxHashSet; 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>> { @@ -48,7 +48,7 @@ pub enum AssocItemSearch { } 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, @@ -77,12 +77,11 @@ pub fn find_similar_imports<'a>( 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), }, )) } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4f6f250d6..d479d826f 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1534,14 +1534,13 @@ fn fill_resolve_data( 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(), -- cgit v1.2.3