diff options
author | Kirill Bulatov <[email protected]> | 2021-02-28 08:32:15 +0000 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2021-03-08 21:59:20 +0000 |
commit | 89d410cef571f5fa7631b17e2fbe52a8f8f03990 (patch) | |
tree | 6c40354741dda2072ef08f1bab9920547277bdd0 | |
parent | 9482353fa8e1e88cb720a029b9bb6304819c7399 (diff) |
Do not propose already imported imports
-rw-r--r-- | crates/hir/src/semantics.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/auto_import.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 23 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 43 |
4 files changed, 45 insertions, 27 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 945638cc5..69370ef3d 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -774,7 +774,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { | |||
774 | /// | 774 | /// |
775 | /// Note that if you are wondering "what does this specific existing name mean?", | 775 | /// Note that if you are wondering "what does this specific existing name mean?", |
776 | /// you'd better use the `resolve_` family of methods. | 776 | /// you'd better use the `resolve_` family of methods. |
777 | #[derive(Debug)] | 777 | #[derive(Debug, Clone)] |
778 | pub struct SemanticsScope<'a> { | 778 | pub struct SemanticsScope<'a> { |
779 | pub db: &'a dyn HirDatabase, | 779 | pub db: &'a dyn HirDatabase, |
780 | file_id: HirFileId, | 780 | file_id: HirFileId, |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index e9993a7cc..182547589 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -111,7 +111,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
111 | Some(()) | 111 | Some(()) |
112 | } | 112 | } |
113 | 113 | ||
114 | pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> { | 114 | pub(super) fn find_importable_node<'a>( |
115 | ctx: &'a AssistContext, | ||
116 | ) -> Option<(ImportAssets<'a>, SyntaxNode)> { | ||
115 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { | 117 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { |
116 | ImportAssets::for_exact_path(&path_under_caret, &ctx.sema) | 118 | ImportAssets::for_exact_path(&path_under_caret, &ctx.sema) |
117 | .zip(Some(path_under_caret.syntax().clone())) | 119 | .zip(Some(path_under_caret.syntax().clone())) |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index af49fdd26..d6adf70b1 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -48,12 +48,11 @@ | |||
48 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 48 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
49 | //! capability enabled. | 49 | //! capability enabled. |
50 | 50 | ||
51 | use hir::{AsAssocItem, ModPath, ScopeDef}; | 51 | use hir::{AsAssocItem, ItemInNs, ModPath, ScopeDef}; |
52 | use ide_db::helpers::{ | 52 | use ide_db::helpers::{ |
53 | import_assets::{ImportAssets, ImportCandidate}, | 53 | import_assets::{ImportAssets, ImportCandidate}, |
54 | insert_use::ImportScope, | 54 | insert_use::ImportScope, |
55 | }; | 55 | }; |
56 | use rustc_hash::FxHashSet; | ||
57 | use syntax::{AstNode, SyntaxNode, T}; | 56 | use syntax::{AstNode, SyntaxNode, T}; |
58 | 57 | ||
59 | use crate::{ | 58 | use crate::{ |
@@ -92,19 +91,17 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
92 | &ctx.sema, | 91 | &ctx.sema, |
93 | )?; | 92 | )?; |
94 | 93 | ||
95 | let scope_definitions = scope_definitions(ctx); | ||
96 | let mut all_mod_paths = import_assets | 94 | let mut all_mod_paths = import_assets |
97 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) | 95 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) |
98 | .into_iter() | 96 | .into_iter() |
99 | .map(|import| { | 97 | .map(|import| { |
100 | let proposed_def = match import.item_to_display() { | 98 | let proposed_def = match import.item_to_display() { |
101 | hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | 99 | ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), |
102 | hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | 100 | ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), |
103 | hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | 101 | ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), |
104 | }; | 102 | }; |
105 | (import, proposed_def) | 103 | (import, proposed_def) |
106 | }) | 104 | }) |
107 | .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) | ||
108 | .collect::<Vec<_>>(); | 105 | .collect::<Vec<_>>(); |
109 | all_mod_paths.sort_by_cached_key(|(import, _)| { | 106 | all_mod_paths.sort_by_cached_key(|(import, _)| { |
110 | compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased) | 107 | compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased) |
@@ -125,14 +122,6 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
125 | Some(()) | 122 | Some(()) |
126 | } | 123 | } |
127 | 124 | ||
128 | fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> { | ||
129 | let mut scope_definitions = FxHashSet::default(); | ||
130 | ctx.scope.process_all_names(&mut |_, scope_def| { | ||
131 | scope_definitions.insert(scope_def); | ||
132 | }); | ||
133 | scope_definitions | ||
134 | } | ||
135 | |||
136 | pub(crate) fn position_for_import<'a>( | 125 | pub(crate) fn position_for_import<'a>( |
137 | ctx: &'a CompletionContext, | 126 | ctx: &'a CompletionContext, |
138 | import_candidate: Option<&ImportCandidate>, | 127 | import_candidate: Option<&ImportCandidate>, |
@@ -150,13 +139,14 @@ pub(crate) fn position_for_import<'a>( | |||
150 | }) | 139 | }) |
151 | } | 140 | } |
152 | 141 | ||
153 | fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { | 142 | fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<ImportAssets<'a>> { |
154 | let current_module = ctx.scope.module()?; | 143 | let current_module = ctx.scope.module()?; |
155 | if let Some(dot_receiver) = &ctx.dot_receiver { | 144 | if let Some(dot_receiver) = &ctx.dot_receiver { |
156 | ImportAssets::for_fuzzy_method_call( | 145 | ImportAssets::for_fuzzy_method_call( |
157 | current_module, | 146 | current_module, |
158 | ctx.sema.type_of_expr(dot_receiver)?, | 147 | ctx.sema.type_of_expr(dot_receiver)?, |
159 | fuzzy_name, | 148 | fuzzy_name, |
149 | ctx.scope.clone(), | ||
160 | ) | 150 | ) |
161 | } else { | 151 | } else { |
162 | let fuzzy_name_length = fuzzy_name.len(); | 152 | let fuzzy_name_length = fuzzy_name.len(); |
@@ -165,6 +155,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
165 | ctx.path_qual.clone(), | 155 | ctx.path_qual.clone(), |
166 | fuzzy_name, | 156 | fuzzy_name, |
167 | &ctx.sema, | 157 | &ctx.sema, |
158 | ctx.scope.clone(), | ||
168 | )?; | 159 | )?; |
169 | 160 | ||
170 | if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) | 161 | if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 2e7a183d1..b25786928 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | use either::Either; | 2 | use either::Either; |
3 | use hir::{ | 3 | use hir::{ |
4 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, | 4 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, |
5 | ModuleDef, PathResolution, PrefixKind, Semantics, Type, | 5 | ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type, |
6 | }; | 6 | }; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstNode}; | 8 | use syntax::{ast, AstNode}; |
@@ -62,33 +62,38 @@ impl NameToImport { | |||
62 | } | 62 | } |
63 | 63 | ||
64 | #[derive(Debug)] | 64 | #[derive(Debug)] |
65 | pub struct ImportAssets { | 65 | pub struct ImportAssets<'a> { |
66 | import_candidate: ImportCandidate, | 66 | import_candidate: ImportCandidate, |
67 | module_with_candidate: Module, | 67 | module_with_candidate: Module, |
68 | scope: SemanticsScope<'a>, | ||
68 | } | 69 | } |
69 | 70 | ||
70 | impl ImportAssets { | 71 | impl<'a> ImportAssets<'a> { |
71 | pub fn for_method_call( | 72 | pub fn for_method_call( |
72 | method_call: &ast::MethodCallExpr, | 73 | method_call: &ast::MethodCallExpr, |
73 | sema: &Semantics<RootDatabase>, | 74 | sema: &'a Semantics<RootDatabase>, |
74 | ) -> Option<Self> { | 75 | ) -> Option<Self> { |
76 | let scope = sema.scope(method_call.syntax()); | ||
75 | Some(Self { | 77 | Some(Self { |
76 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, | 78 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, |
77 | module_with_candidate: sema.scope(method_call.syntax()).module()?, | 79 | module_with_candidate: scope.module()?, |
80 | scope, | ||
78 | }) | 81 | }) |
79 | } | 82 | } |
80 | 83 | ||
81 | pub fn for_exact_path( | 84 | pub fn for_exact_path( |
82 | fully_qualified_path: &ast::Path, | 85 | fully_qualified_path: &ast::Path, |
83 | sema: &Semantics<RootDatabase>, | 86 | sema: &'a Semantics<RootDatabase>, |
84 | ) -> Option<Self> { | 87 | ) -> Option<Self> { |
85 | let syntax_under_caret = fully_qualified_path.syntax(); | 88 | let syntax_under_caret = fully_qualified_path.syntax(); |
86 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { | 89 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { |
87 | return None; | 90 | return None; |
88 | } | 91 | } |
92 | let scope = sema.scope(syntax_under_caret); | ||
89 | Some(Self { | 93 | Some(Self { |
90 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, | 94 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, |
91 | module_with_candidate: sema.scope(syntax_under_caret).module()?, | 95 | module_with_candidate: scope.module()?, |
96 | scope, | ||
92 | }) | 97 | }) |
93 | } | 98 | } |
94 | 99 | ||
@@ -97,10 +102,12 @@ impl ImportAssets { | |||
97 | qualifier: Option<ast::Path>, | 102 | qualifier: Option<ast::Path>, |
98 | fuzzy_name: String, | 103 | fuzzy_name: String, |
99 | sema: &Semantics<RootDatabase>, | 104 | sema: &Semantics<RootDatabase>, |
105 | scope: SemanticsScope<'a>, | ||
100 | ) -> Option<Self> { | 106 | ) -> Option<Self> { |
101 | Some(Self { | 107 | Some(Self { |
102 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, | 108 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, |
103 | module_with_candidate, | 109 | module_with_candidate, |
110 | scope, | ||
104 | }) | 111 | }) |
105 | } | 112 | } |
106 | 113 | ||
@@ -108,6 +115,7 @@ impl ImportAssets { | |||
108 | module_with_method_call: Module, | 115 | module_with_method_call: Module, |
109 | receiver_ty: Type, | 116 | receiver_ty: Type, |
110 | fuzzy_method_name: String, | 117 | fuzzy_method_name: String, |
118 | scope: SemanticsScope<'a>, | ||
111 | ) -> Option<Self> { | 119 | ) -> Option<Self> { |
112 | Some(Self { | 120 | Some(Self { |
113 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | 121 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { |
@@ -115,6 +123,7 @@ impl ImportAssets { | |||
115 | name: NameToImport::Fuzzy(fuzzy_method_name), | 123 | name: NameToImport::Fuzzy(fuzzy_method_name), |
116 | }), | 124 | }), |
117 | module_with_candidate: module_with_method_call, | 125 | module_with_candidate: module_with_method_call, |
126 | scope, | ||
118 | }) | 127 | }) |
119 | } | 128 | } |
120 | } | 129 | } |
@@ -155,7 +164,7 @@ impl LocatedImport { | |||
155 | } | 164 | } |
156 | } | 165 | } |
157 | 166 | ||
158 | impl ImportAssets { | 167 | impl<'a> ImportAssets<'a> { |
159 | pub fn import_candidate(&self) -> &ImportCandidate { | 168 | pub fn import_candidate(&self) -> &ImportCandidate { |
160 | &self.import_candidate | 169 | &self.import_candidate |
161 | } | 170 | } |
@@ -189,6 +198,7 @@ impl ImportAssets { | |||
189 | prefixed: Option<PrefixKind>, | 198 | prefixed: Option<PrefixKind>, |
190 | ) -> Vec<LocatedImport> { | 199 | ) -> Vec<LocatedImport> { |
191 | let current_crate = self.module_with_candidate.krate(); | 200 | let current_crate = self.module_with_candidate.krate(); |
201 | let scope_definitions = self.scope_definitions(); | ||
192 | 202 | ||
193 | let imports_for_candidate_name = match self.name_to_import() { | 203 | let imports_for_candidate_name = match self.name_to_import() { |
194 | NameToImport::Exact(exact_name) => { | 204 | NameToImport::Exact(exact_name) => { |
@@ -219,9 +229,25 @@ impl ImportAssets { | |||
219 | self.applicable_defs(sema.db, prefixed, imports_for_candidate_name) | 229 | self.applicable_defs(sema.db, prefixed, imports_for_candidate_name) |
220 | .into_iter() | 230 | .into_iter() |
221 | .filter(|import| import.import_path().len() > 1) | 231 | .filter(|import| import.import_path().len() > 1) |
232 | .filter(|import| { | ||
233 | let proposed_def = match import.item_to_import() { | ||
234 | ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | ||
235 | ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | ||
236 | ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | ||
237 | }; | ||
238 | !scope_definitions.contains(&proposed_def) | ||
239 | }) | ||
222 | .collect() | 240 | .collect() |
223 | } | 241 | } |
224 | 242 | ||
243 | fn scope_definitions(&self) -> FxHashSet<ScopeDef> { | ||
244 | let mut scope_definitions = FxHashSet::default(); | ||
245 | self.scope.process_all_names(&mut |_, scope_def| { | ||
246 | scope_definitions.insert(scope_def); | ||
247 | }); | ||
248 | scope_definitions | ||
249 | } | ||
250 | |||
225 | fn applicable_defs( | 251 | fn applicable_defs( |
226 | &self, | 252 | &self, |
227 | db: &RootDatabase, | 253 | db: &RootDatabase, |
@@ -297,7 +323,6 @@ fn path_applicable_imports( | |||
297 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier), | 323 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier), |
298 | }; | 324 | }; |
299 | 325 | ||
300 | // TODO kb zz.syntax().ast_node() <- two options are now proposed despite the trait being imported | ||
301 | let unresolved_qualifier_string = unresolved_qualifier.to_string(); | 326 | let unresolved_qualifier_string = unresolved_qualifier.to_string(); |
302 | let unresolved_first_segment_string = unresolved_first_segment.to_string(); | 327 | let unresolved_first_segment_string = unresolved_first_segment.to_string(); |
303 | 328 | ||