From 6742f38e49d001359a7a9911becc0fcae4c67910 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 16 Jan 2021 19:33:36 +0200 Subject: Share import_assets and related entities --- crates/assists/src/assist_config.rs | 8 +- crates/assists/src/handlers/auto_import.rs | 9 +- crates/assists/src/handlers/qualify_path.rs | 40 +++-- crates/assists/src/lib.rs | 2 +- crates/assists/src/tests.rs | 9 +- crates/assists/src/utils.rs | 1 - crates/assists/src/utils/import_assets.rs | 265 ---------------------------- 7 files changed, 34 insertions(+), 300 deletions(-) delete mode 100644 crates/assists/src/utils/import_assets.rs (limited to 'crates/assists/src') diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs index 4fe8ea761..9cabf037c 100644 --- a/crates/assists/src/assist_config.rs +++ b/crates/assists/src/assist_config.rs @@ -4,7 +4,7 @@ //! module, and we use to statically check that we only produce snippet //! assists if we are allowed to. -use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; +use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; use crate::AssistKind; @@ -14,9 +14,3 @@ pub struct AssistConfig { pub allowed: Option>, pub insert_use: InsertUseConfig, } - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct InsertUseConfig { - pub merge: Option, - pub prefix_kind: hir::PrefixKind, -} diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 55620f0f3..4e2a4fcd9 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs @@ -1,13 +1,11 @@ use ide_db::helpers::{ + import_assets::{ImportAssets, ImportCandidate}, insert_use::{insert_use, ImportScope}, mod_path_to_ast, }; use syntax::ast; -use crate::{ - utils::import_assets::{ImportAssets, ImportCandidate}, - AssistContext, AssistId, AssistKind, Assists, GroupLabel, -}; +use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // Feature: Auto Import // @@ -121,8 +119,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { let name = match import_candidate { - ImportCandidate::UnqualifiedName(candidate) - | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name), + ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name), ImportCandidate::TraitAssocItem(candidate) => { format!("Import a trait for item {}", &candidate.name) } diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs index f7fbf37f4..a7d9fd4dc 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs @@ -1,7 +1,10 @@ use std::iter; use hir::AsName; -use ide_db::helpers::mod_path_to_ast; +use ide_db::helpers::{ + import_assets::{ImportAssets, ImportCandidate}, + mod_path_to_ast, +}; use ide_db::RootDatabase; use syntax::{ ast, @@ -12,7 +15,6 @@ use test_utils::mark; use crate::{ assist_context::{AssistContext, Assists}, - utils::import_assets::{ImportAssets, ImportCandidate}, AssistId, AssistKind, GroupLabel, }; @@ -53,17 +55,18 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; let qualify_candidate = match candidate { - ImportCandidate::QualifierStart(_) => { - mark::hit!(qualify_path_qualifier_start); - let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; - let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); - QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) - } - ImportCandidate::UnqualifiedName(_) => { - mark::hit!(qualify_path_unqualified_name); - let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; - let generics = path.segment()?.generic_arg_list(); - QualifyCandidate::UnqualifiedName(generics) + ImportCandidate::Path(candidate) => { + if candidate.qualifier.is_some() { + mark::hit!(qualify_path_qualifier_start); + let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; + let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); + QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + } else { + mark::hit!(qualify_path_unqualified_name); + let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; + let generics = path.segment()?.generic_arg_list(); + QualifyCandidate::UnqualifiedName(generics) + } } ImportCandidate::TraitAssocItem(_) => { mark::hit!(qualify_path_trait_assoc_item); @@ -186,7 +189,7 @@ fn item_as_trait(item: hir::ItemInNs) -> Option { fn group_label(candidate: &ImportCandidate) -> GroupLabel { let name = match candidate { - ImportCandidate::UnqualifiedName(it) | ImportCandidate::QualifierStart(it) => &it.name, + ImportCandidate::Path(it) => &it.name, ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, }; GroupLabel(format!("Qualify {}", name)) @@ -194,8 +197,13 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { match candidate { - ImportCandidate::UnqualifiedName(_) => format!("Qualify as `{}`", &import), - ImportCandidate::QualifierStart(_) => format!("Qualify with `{}`", &import), + ImportCandidate::Path(candidate) => { + if candidate.qualifier.is_some() { + format!("Qualify with `{}`", &import) + } else { + format!("Qualify as `{}`", &import) + } + } ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), } diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 3d7971806..14178a651 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs @@ -24,7 +24,7 @@ use syntax::TextRange; pub(crate) use crate::assist_context::{AssistContext, Assists}; -pub use assist_config::{AssistConfig, InsertUseConfig}; +pub use assist_config::AssistConfig; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AssistKind { diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 71431b406..32bd8698b 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs @@ -3,16 +3,17 @@ mod generated; use hir::Semantics; use ide_db::{ base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, - helpers::{insert_use::MergeBehavior, SnippetCap}, + helpers::{ + insert_use::{InsertUseConfig, MergeBehavior}, + SnippetCap, + }, source_change::FileSystemEdit, RootDatabase, }; use syntax::TextRange; use test_utils::{assert_eq_text, extract_offset, extract_range}; -use crate::{ - handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists, InsertUseConfig, -}; +use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; use stdx::{format_to, trim_indent}; pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 9ea96eb73..fc9f83bab 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs @@ -1,5 +1,4 @@ //! Assorted functions shared by several assists. -pub(crate) mod import_assets; use std::ops; diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs deleted file mode 100644 index 4ce82c1ba..000000000 --- a/crates/assists/src/utils/import_assets.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! Look up accessible paths for items. -use either::Either; -use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; -use ide_db::{imports_locator, RootDatabase}; -use rustc_hash::FxHashSet; -use syntax::{ast, AstNode, SyntaxNode}; - -use crate::assist_config::InsertUseConfig; - -#[derive(Debug)] -pub(crate) enum ImportCandidate { - /// Simple name like 'HashMap' - UnqualifiedName(PathImportCandidate), - /// First part of the qualified name. - /// For 'std::collections::HashMap', that will be 'std'. - QualifierStart(PathImportCandidate), - /// A trait associated function (with no self parameter) or associated constant. - /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type - /// and `name` is the `test_function` - TraitAssocItem(TraitImportCandidate), - /// A trait method with self parameter. - /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type - /// and `name` is the `test_method` - TraitMethod(TraitImportCandidate), -} - -#[derive(Debug)] -pub(crate) struct TraitImportCandidate { - pub(crate) ty: hir::Type, - pub(crate) name: ast::NameRef, -} - -#[derive(Debug)] -pub(crate) struct PathImportCandidate { - pub(crate) name: ast::NameRef, -} - -#[derive(Debug)] -pub(crate) struct ImportAssets { - import_candidate: ImportCandidate, - module_with_name_to_import: hir::Module, - syntax_under_caret: SyntaxNode, -} - -impl ImportAssets { - pub(crate) fn for_method_call( - method_call: ast::MethodCallExpr, - sema: &Semantics, - ) -> Option { - let syntax_under_caret = method_call.syntax().to_owned(); - let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?; - Some(Self { - import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, - module_with_name_to_import, - syntax_under_caret, - }) - } - - pub(crate) fn for_regular_path( - path_under_caret: ast::Path, - sema: &Semantics, - ) -> Option { - let syntax_under_caret = path_under_caret.syntax().to_owned(); - if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { - return None; - } - - let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?; - Some(Self { - import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, - module_with_name_to_import, - syntax_under_caret, - }) - } - - pub(crate) fn syntax_under_caret(&self) -> &SyntaxNode { - &self.syntax_under_caret - } - - pub(crate) fn import_candidate(&self) -> &ImportCandidate { - &self.import_candidate - } - - fn get_search_query(&self) -> &str { - match &self.import_candidate { - ImportCandidate::UnqualifiedName(candidate) - | ImportCandidate::QualifierStart(candidate) => candidate.name.text(), - ImportCandidate::TraitAssocItem(candidate) - | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), - } - } - - pub(crate) fn search_for_imports( - &self, - sema: &Semantics, - config: &InsertUseConfig, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { - let _p = profile::span("import_assists::search_for_imports"); - self.search_for(sema, Some(config.prefix_kind)) - } - - /// This may return non-absolute paths if a part of the returned path is already imported into scope. - #[allow(dead_code)] - pub(crate) fn search_for_relative_paths( - &self, - sema: &Semantics, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { - let _p = profile::span("import_assists::search_for_relative_paths"); - self.search_for(sema, None) - } - - fn search_for( - &self, - sema: &Semantics, - prefixed: Option, - ) -> Vec<(hir::ModPath, hir::ItemInNs)> { - let db = sema.db; - let mut trait_candidates = FxHashSet::default(); - let current_crate = self.module_with_name_to_import.krate(); - - let filter = |candidate: Either| { - trait_candidates.clear(); - match &self.import_candidate { - ImportCandidate::TraitAssocItem(trait_candidate) => { - let located_assoc_item = match candidate { - Either::Left(ModuleDef::Function(located_function)) => { - located_function.as_assoc_item(db) - } - Either::Left(ModuleDef::Const(located_const)) => { - located_const.as_assoc_item(db) - } - _ => None, - } - .map(|assoc| assoc.container(db)) - .and_then(Self::assoc_to_trait)?; - - trait_candidates.insert(located_assoc_item.into()); - - trait_candidate - .ty - .iterate_path_candidates( - db, - current_crate, - &trait_candidates, - None, - |_, assoc| Self::assoc_to_trait(assoc.container(db)), - ) - .map(ModuleDef::from) - .map(Either::Left) - } - ImportCandidate::TraitMethod(trait_candidate) => { - let located_assoc_item = - if let Either::Left(ModuleDef::Function(located_function)) = candidate { - located_function - .as_assoc_item(db) - .map(|assoc| assoc.container(db)) - .and_then(Self::assoc_to_trait) - } else { - None - }?; - - trait_candidates.insert(located_assoc_item.into()); - - trait_candidate - .ty - .iterate_method_candidates( - db, - current_crate, - &trait_candidates, - None, - |_, function| { - Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) - }, - ) - .map(ModuleDef::from) - .map(Either::Left) - } - _ => Some(candidate), - } - }; - - let mut res = imports_locator::find_exact_imports( - sema, - current_crate, - self.get_search_query().to_string(), - ) - .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::>(); - res.sort_by_key(|(path, _)| path.clone()); - res - } - - fn assoc_to_trait(assoc: AssocItemContainer) -> Option { - if let AssocItemContainer::Trait(extracted_trait) = assoc { - Some(extracted_trait) - } else { - None - } - } -} - -impl ImportCandidate { - fn for_method_call( - sema: &Semantics, - method_call: &ast::MethodCallExpr, - ) -> Option { - match sema.resolve_method_call(method_call) { - Some(_) => None, - None => Some(Self::TraitMethod(TraitImportCandidate { - ty: sema.type_of_expr(&method_call.receiver()?)?, - name: method_call.name_ref()?, - })), - } - } - - fn for_regular_path( - sema: &Semantics, - path_under_caret: &ast::Path, - ) -> Option { - if sema.resolve_path(path_under_caret).is_some() { - return None; - } - - let segment = path_under_caret.segment()?; - let candidate = if let Some(qualifier) = path_under_caret.qualifier() { - let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; - let qualifier_start_path = - qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; - if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { - let qualifier_resolution = if qualifier_start_path == qualifier { - qualifier_start_resolution - } else { - sema.resolve_path(&qualifier)? - }; - match qualifier_resolution { - hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { - ImportCandidate::TraitAssocItem(TraitImportCandidate { - ty: assoc_item_path.ty(sema.db), - name: segment.name_ref()?, - }) - } - _ => return None, - } - } else { - ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start }) - } - } else { - ImportCandidate::UnqualifiedName(PathImportCandidate { - name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, - }) - }; - Some(candidate) - } -} -- cgit v1.2.3