aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/defs.rs13
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs356
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs2
-rw-r--r--crates/ide_db/src/imports_locator.rs76
-rw-r--r--crates/ide_db/src/lib.rs24
-rw-r--r--crates/ide_db/src/search.rs10
-rw-r--r--crates/ide_db/src/symbol_index.rs6
9 files changed, 319 insertions, 171 deletions
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index c770a236b..9d9b6de7a 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -149,6 +149,7 @@ impl RootDatabase {
149 149
150 // DefDatabase 150 // DefDatabase
151 hir::db::ItemTreeQuery 151 hir::db::ItemTreeQuery
152 hir::db::BlockDefMapQuery
152 hir::db::CrateDefMapQueryQuery 153 hir::db::CrateDefMapQueryQuery
153 hir::db::StructDataQuery 154 hir::db::StructDataQuery
154 hir::db::UnionDataQuery 155 hir::db::UnionDataQuery
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 231e886a9..a8091dbee 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -117,13 +117,6 @@ impl NameClass {
117 } 117 }
118 } 118 }
119 119
120 pub fn classify_self_param(
121 sema: &Semantics<RootDatabase>,
122 self_param: &ast::SelfParam,
123 ) -> Option<NameClass> {
124 sema.to_def(self_param).map(Definition::Local).map(NameClass::Definition)
125 }
126
127 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 120 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
128 let _p = profile::span("classify_name"); 121 let _p = profile::span("classify_name");
129 122
@@ -186,6 +179,10 @@ impl NameClass {
186 179
187 Some(NameClass::Definition(Definition::Local(local))) 180 Some(NameClass::Definition(Definition::Local(local)))
188 }, 181 },
182 ast::SelfParam(it) => {
183 let def = sema.to_def(&it)?;
184 Some(NameClass::Definition(Definition::Local(def.into())))
185 },
189 ast::RecordField(it) => { 186 ast::RecordField(it) => {
190 let field: hir::Field = sema.to_def(&it)?; 187 let field: hir::Field = sema.to_def(&it)?;
191 Some(NameClass::Definition(Definition::Field(field))) 188 Some(NameClass::Definition(Definition::Field(field)))
@@ -346,7 +343,7 @@ impl NameRefClass {
346 hir::AssocItem::TypeAlias(it) => Some(*it), 343 hir::AssocItem::TypeAlias(it) => Some(*it),
347 _ => None, 344 _ => None,
348 }) 345 })
349 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) 346 .find(|alias| &alias.name(sema.db).to_string() == name_ref.text())
350 { 347 {
351 return Some(NameRefClass::Definition(Definition::ModuleDef( 348 return Some(NameRefClass::Definition(Definition::ModuleDef(
352 ModuleDef::TypeAlias(ty), 349 ModuleDef::TypeAlias(ty),
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index edc3da318..517abbb4b 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,12 +1,13 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode};
6 6
7use crate::{imports_locator, RootDatabase}; 7use crate::{
8 8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9use super::insert_use::InsertUseConfig; 9 RootDatabase,
10};
10 11
11#[derive(Debug)] 12#[derive(Debug)]
12pub enum ImportCandidate { 13pub enum ImportCandidate {
@@ -24,86 +25,141 @@ pub enum ImportCandidate {
24 25
25#[derive(Debug)] 26#[derive(Debug)]
26pub struct TraitImportCandidate { 27pub struct TraitImportCandidate {
27 pub ty: hir::Type, 28 pub receiver_ty: hir::Type,
28 pub name: ast::NameRef, 29 pub name: NameToImport,
29} 30}
30 31
31#[derive(Debug)] 32#[derive(Debug)]
32pub struct PathImportCandidate { 33pub struct PathImportCandidate {
33 pub qualifier: Option<ast::Path>, 34 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef, 35 pub name: NameToImport,
36}
37
38#[derive(Debug)]
39pub enum NameToImport {
40 Exact(String),
41 Fuzzy(String),
42}
43
44impl NameToImport {
45 pub fn text(&self) -> &str {
46 match self {
47 NameToImport::Exact(text) => text.as_str(),
48 NameToImport::Fuzzy(text) => text.as_str(),
49 }
50 }
35} 51}
36 52
37#[derive(Debug)] 53#[derive(Debug)]
38pub struct ImportAssets { 54pub struct ImportAssets {
39 import_candidate: ImportCandidate, 55 import_candidate: ImportCandidate,
40 module_with_name_to_import: hir::Module, 56 module_with_candidate: hir::Module,
41 syntax_under_caret: SyntaxNode,
42} 57}
43 58
44impl ImportAssets { 59impl ImportAssets {
45 pub fn for_method_call( 60 pub fn for_method_call(
46 method_call: ast::MethodCallExpr, 61 method_call: &ast::MethodCallExpr,
47 sema: &Semantics<RootDatabase>, 62 sema: &Semantics<RootDatabase>,
48 ) -> Option<Self> { 63 ) -> Option<Self> {
49 let syntax_under_caret = method_call.syntax().to_owned();
50 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
51 Some(Self { 64 Some(Self {
52 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, 65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
53 module_with_name_to_import, 66 module_with_candidate: sema.scope(method_call.syntax()).module()?,
54 syntax_under_caret,
55 }) 67 })
56 } 68 }
57 69
58 pub fn for_regular_path( 70 pub fn for_exact_path(
59 path_under_caret: ast::Path, 71 fully_qualified_path: &ast::Path,
60 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
61 ) -> Option<Self> { 73 ) -> Option<Self> {
62 let syntax_under_caret = path_under_caret.syntax().to_owned(); 74 let syntax_under_caret = fully_qualified_path.syntax();
63 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
64 return None; 76 return None;
65 } 77 }
66
67 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
68 Some(Self { 78 Some(Self {
69 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, 79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
70 module_with_name_to_import, 80 module_with_candidate: sema.scope(syntax_under_caret).module()?,
71 syntax_under_caret,
72 }) 81 })
73 } 82 }
74 83
75 pub fn syntax_under_caret(&self) -> &SyntaxNode { 84 pub fn for_fuzzy_path(
76 &self.syntax_under_caret 85 module_with_path: Module,
86 qualifier: Option<ast::Path>,
87 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>,
89 ) -> Option<Self> {
90 Some(match qualifier {
91 Some(qualifier) => {
92 let qualifier_resolution = sema.resolve_path(&qualifier)?;
93 match qualifier_resolution {
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 })
77 } 118 }
78 119
120 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module,
122 receiver_ty: hir::Type,
123 fuzzy_method_name: String,
124 ) -> Option<Self> {
125 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name),
129 }),
130 module_with_candidate: module_with_method_call,
131 })
132 }
133}
134
135impl ImportAssets {
79 pub fn import_candidate(&self) -> &ImportCandidate { 136 pub fn import_candidate(&self) -> &ImportCandidate {
80 &self.import_candidate 137 &self.import_candidate
81 } 138 }
82 139
83 fn get_search_query(&self) -> &str { 140 fn name_to_import(&self) -> &NameToImport {
84 match &self.import_candidate { 141 match &self.import_candidate {
85 ImportCandidate::Path(candidate) => candidate.name.text(), 142 ImportCandidate::Path(candidate) => &candidate.name,
86 ImportCandidate::TraitAssocItem(candidate) 143 ImportCandidate::TraitAssocItem(candidate)
87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
88 } 145 }
89 } 146 }
90 147
91 pub fn search_for_imports( 148 pub fn search_for_imports(
92 &self, 149 &self,
93 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
94 config: &InsertUseConfig, 151 prefix_kind: PrefixKind,
95 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
96 let _p = profile::span("import_assists::search_for_imports"); 153 let _p = profile::span("import_assets::search_for_imports");
97 self.search_for(sema, Some(config.prefix_kind)) 154 self.search_for(sema, Some(prefix_kind))
98 } 155 }
99 156
100 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 157 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
101 #[allow(dead_code)]
102 pub fn search_for_relative_paths( 158 pub fn search_for_relative_paths(
103 &self, 159 &self,
104 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
106 let _p = profile::span("import_assists::search_for_relative_paths"); 162 let _p = profile::span("import_assets::search_for_relative_paths");
107 self.search_for(sema, None) 163 self.search_for(sema, None)
108 } 164 }
109 165
@@ -112,99 +168,142 @@ impl ImportAssets {
112 sema: &Semantics<RootDatabase>, 168 sema: &Semantics<RootDatabase>,
113 prefixed: Option<hir::PrefixKind>, 169 prefixed: Option<hir::PrefixKind>,
114 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
115 let db = sema.db; 171 let current_crate = self.module_with_candidate.krate();
116 let mut trait_candidates = FxHashSet::default();
117 let current_crate = self.module_with_name_to_import.krate();
118 172
119 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { 173 let unfiltered_imports = match self.name_to_import() {
120 trait_candidates.clear(); 174 NameToImport::Exact(exact_name) => {
121 match &self.import_candidate { 175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
122 ImportCandidate::TraitAssocItem(trait_candidate) => { 176 }
123 let located_assoc_item = match candidate { 177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
124 Either::Left(ModuleDef::Function(located_function)) => { 178 // instead, we need to look up all trait impls for a certain struct and search through them only
125 located_function.as_assoc_item(db) 179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
126 } 180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
127 Either::Left(ModuleDef::Const(located_const)) => { 181 // for the details
128 located_const.as_assoc_item(db) 182 NameToImport::Fuzzy(fuzzy_name) => {
129 } 183 let (assoc_item_search, limit) = match self.import_candidate {
130 _ => None, 184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
185 (AssocItemSearch::AssocItemsOnly, None)
131 } 186 }
132 .map(|assoc| assoc.container(db)) 187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
133 .and_then(Self::assoc_to_trait)?; 188 };
134 189 imports_locator::find_similar_imports(
135 trait_candidates.insert(located_assoc_item.into()); 190 sema,
191 current_crate,
192 fuzzy_name.clone(),
193 assoc_item_search,
194 limit,
195 )
196 }
197 };
136 198
137 trait_candidate 199 let db = sema.db;
138 .ty 200 let mut res =
139 .iterate_path_candidates( 201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
140 db, 202 .filter_map(|candidate| {
141 current_crate, 203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
142 &trait_candidates,
143 None,
144 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
145 )
146 .map(ModuleDef::from)
147 .map(Either::Left)
148 }
149 ImportCandidate::TraitMethod(trait_candidate) => {
150 let located_assoc_item =
151 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
152 located_function
153 .as_assoc_item(db)
154 .map(|assoc| assoc.container(db))
155 .and_then(Self::assoc_to_trait)
156 } else {
157 None
158 }?;
159 204
160 trait_candidates.insert(located_assoc_item.into()); 205 let item_to_search = match self.import_candidate {
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
207 let canidate_trait = match candidate {
208 Either::Left(module_def) => {
209 module_def.as_assoc_item(db)?.containing_trait(db)
210 }
211 _ => None,
212 }?;
213 ModuleDef::from(canidate_trait).into()
214 }
215 _ => item,
216 };
161 217
162 trait_candidate 218 if let Some(prefix_kind) = prefixed {
163 .ty 219 self.module_with_candidate.find_use_path_prefixed(
164 .iterate_method_candidates(
165 db, 220 db,
166 current_crate, 221 item_to_search,
167 &trait_candidates, 222 prefix_kind,
168 None,
169 |_, function| {
170 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
171 },
172 ) 223 )
173 .map(ModuleDef::from) 224 } else {
174 .map(Either::Left) 225 self.module_with_candidate.find_use_path(db, item_to_search)
175 } 226 }
176 _ => Some(candidate), 227 .map(|path| (path, item))
177 } 228 })
178 }; 229 .filter(|(use_path, _)| use_path.len() > 1)
179 230 .collect::<Vec<_>>();
180 let mut res = imports_locator::find_exact_imports( 231 res.sort_by_cached_key(|(path, _)| path.clone());
181 sema,
182 current_crate,
183 self.get_search_query().to_string(),
184 )
185 .filter_map(filter)
186 .filter_map(|candidate| {
187 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
188 if let Some(prefix_kind) = prefixed {
189 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
190 } else {
191 self.module_with_name_to_import.find_use_path(db, item)
192 }
193 .map(|path| (path, item))
194 })
195 .filter(|(use_path, _)| use_path.len() > 1)
196 .take(20)
197 .collect::<Vec<_>>();
198 res.sort_by_key(|(path, _)| path.clone());
199 res 232 res
200 } 233 }
234}
201 235
202 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { 236fn applicable_defs<'a>(
203 if let AssocItemContainer::Trait(extracted_trait) = assoc { 237 import_candidate: &ImportCandidate,
204 Some(extracted_trait) 238 current_crate: Crate,
205 } else { 239 db: &RootDatabase,
206 None 240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
242 let receiver_ty = match import_candidate {
243 ImportCandidate::Path(_) => return unfiltered_imports,
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
245 &candidate.receiver_ty
207 } 246 }
247 };
248
249 let mut required_assoc_items = FxHashSet::default();
250
251 let trait_candidates = unfiltered_imports
252 .filter_map(|input| match input {
253 Either::Left(module_def) => module_def.as_assoc_item(db),
254 _ => None,
255 })
256 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc);
259 Some(assoc_item_trait.into())
260 })
261 .collect();
262
263 let mut applicable_defs = FxHashSet::default();
264
265 match import_candidate {
266 ImportCandidate::Path(_) => unreachable!(),
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db,
269 current_crate,
270 &trait_candidates,
271 None,
272 |_, assoc| {
273 if required_assoc_items.contains(&assoc) {
274 if let AssocItem::Function(f) = assoc {
275 if f.self_param(db).is_some() {
276 return None;
277 }
278 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
280 }
281 None::<()>
282 },
283 ),
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates(
285 db,
286 current_crate,
287 &trait_candidates,
288 None,
289 |_, function| {
290 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
293 }
294 None::<()>
295 },
296 ),
297 };
298
299 Box::new(applicable_defs.into_iter())
300}
301
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {
303 match assoc {
304 AssocItem::Function(f) => f.into(),
305 AssocItem::Const(c) => c.into(),
306 AssocItem::TypeAlias(t) => t.into(),
208 } 307 }
209} 308}
210 309
@@ -216,22 +315,19 @@ impl ImportCandidate {
216 match sema.resolve_method_call(method_call) { 315 match sema.resolve_method_call(method_call) {
217 Some(_) => None, 316 Some(_) => None,
218 None => Some(Self::TraitMethod(TraitImportCandidate { 317 None => Some(Self::TraitMethod(TraitImportCandidate {
219 ty: sema.type_of_expr(&method_call.receiver()?)?, 318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
220 name: method_call.name_ref()?, 319 name: NameToImport::Exact(method_call.name_ref()?.to_string()),
221 })), 320 })),
222 } 321 }
223 } 322 }
224 323
225 fn for_regular_path( 324 fn for_regular_path(sema: &Semantics<RootDatabase>, path: &ast::Path) -> Option<Self> {
226 sema: &Semantics<RootDatabase>, 325 if sema.resolve_path(path).is_some() {
227 path_under_caret: &ast::Path,
228 ) -> Option<Self> {
229 if sema.resolve_path(path_under_caret).is_some() {
230 return None; 326 return None;
231 } 327 }
232 328
233 let segment = path_under_caret.segment()?; 329 let segment = path.segment()?;
234 let candidate = if let Some(qualifier) = path_under_caret.qualifier() { 330 let candidate = if let Some(qualifier) = path.qualifier() {
235 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
236 let qualifier_start_path = 332 let qualifier_start_path =
237 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -244,8 +340,8 @@ impl ImportCandidate {
244 match qualifier_resolution { 340 match qualifier_resolution {
245 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { 341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
246 ImportCandidate::TraitAssocItem(TraitImportCandidate { 342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
247 ty: assoc_item_path.ty(sema.db), 343 receiver_ty: assoc_item_path.ty(sema.db),
248 name: segment.name_ref()?, 344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
249 }) 345 })
250 } 346 }
251 _ => return None, 347 _ => return None,
@@ -253,13 +349,15 @@ impl ImportCandidate {
253 } else { 349 } else {
254 ImportCandidate::Path(PathImportCandidate { 350 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier), 351 qualifier: Some(qualifier),
256 name: qualifier_start, 352 name: NameToImport::Exact(qualifier_start.to_string()),
257 }) 353 })
258 } 354 }
259 } else { 355 } else {
260 ImportCandidate::Path(PathImportCandidate { 356 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None, 357 qualifier: None,
262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 358 name: NameToImport::Exact(
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(),
360 ),
263 }) 361 })
264 }; 362 };
265 Some(candidate) 363 Some(candidate)
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 877d4f1c7..fd4035198 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -507,7 +507,7 @@ impl ImportGroup {
507 PathSegmentKind::SelfKw => ImportGroup::ThisModule, 507 PathSegmentKind::SelfKw => ImportGroup::ThisModule,
508 PathSegmentKind::SuperKw => ImportGroup::SuperModule, 508 PathSegmentKind::SuperKw => ImportGroup::SuperModule,
509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate, 509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
510 PathSegmentKind::Name(name) => match name.text().as_str() { 510 PathSegmentKind::Name(name) => match name.text() {
511 "std" => ImportGroup::Std, 511 "std" => ImportGroup::Std,
512 "core" => ImportGroup::Std, 512 "core" => ImportGroup::Std,
513 _ => ImportGroup::ExternCrate, 513 _ => ImportGroup::ExternCrate,
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index a603fe87f..4bbe66f1f 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -599,7 +599,7 @@ fn check(
599 599
600 let rewriter = insert_use(&file, path, mb); 600 let rewriter = insert_use(&file, path, mb);
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 601 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(&result, ra_fixture_after); 602 assert_eq_text!(ra_fixture_after, &result);
603} 603}
604 604
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index d111fba92..502e8281a 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{
5 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
7};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 8use syntax::{ast, AstNode, SyntaxKind::NAME};
6 9
7use crate::{ 10use crate::{
@@ -12,69 +15,84 @@ use crate::{
12use either::Either; 15use either::Either;
13use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
14 17
15const QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
16 19
17pub fn find_exact_imports<'a>( 20pub fn find_exact_imports<'a>(
18 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'a, RootDatabase>,
19 krate: Crate, 22 krate: Crate,
20 name_to_import: String, 23 name_to_import: String,
21) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
22 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
23 find_imports( 26 Box::new(find_imports(
24 sema, 27 sema,
25 krate, 28 krate,
26 { 29 {
27 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(name_to_import.clone());
28 local_query.exact(); 31 local_query.exact();
29 local_query.limit(QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
30 local_query 33 local_query
31 }, 34 },
32 import_map::Query::new(name_to_import) 35 import_map::Query::new(name_to_import)
33 .limit(QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
34 .name_only() 37 .name_only()
35 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
36 .case_sensitive(), 39 .case_sensitive(),
37 ) 40 ))
41}
42
43pub enum AssocItemSearch {
44 Include,
45 Exclude,
46 AssocItemsOnly,
38} 47}
39 48
40pub fn find_similar_imports<'a>( 49pub fn find_similar_imports<'a>(
41 sema: &Semantics<'a, RootDatabase>, 50 sema: &Semantics<'a, RootDatabase>,
42 krate: Crate, 51 krate: Crate,
43 limit: Option<usize>,
44 fuzzy_search_string: String, 52 fuzzy_search_string: String,
45 ignore_assoc_items: bool, 53 assoc_item_search: AssocItemSearch,
46 name_only: bool, 54 limit: Option<usize>,
47) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { 55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
48 let _p = profile::span("find_similar_imports"); 56 let _p = profile::span("find_similar_imports");
49 57
50 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
51 .search_mode(import_map::SearchMode::Fuzzy); 59 .search_mode(import_map::SearchMode::Fuzzy)
52 if name_only { 60 .name_only();
53 external_query = external_query.name_only(); 61
62 match assoc_item_search {
63 AssocItemSearch::Include => {}
64 AssocItemSearch::Exclude => {
65 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
66 }
67 AssocItemSearch::AssocItemsOnly => {
68 external_query = external_query.assoc_items_only();
69 }
54 } 70 }
55 71
56 let mut local_query = symbol_index::Query::new(fuzzy_search_string); 72 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
57 73
58 if let Some(limit) = limit { 74 if let Some(limit) = limit {
59 local_query.limit(limit);
60 external_query = external_query.limit(limit); 75 external_query = external_query.limit(limit);
76 local_query.limit(limit);
61 } 77 }
62 78
63 let db = sema.db; 79 let db = sema.db;
64 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { 80 Box::new(find_imports(sema, krate, local_query, external_query).filter(
65 if ignore_assoc_items { 81 move |import_candidate| match assoc_item_search {
66 match import_candidate { 82 AssocItemSearch::Include => true,
67 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), 83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
68 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), 84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
69 Either::Left(ModuleDef::TypeAlias(type_alias)) => { 85 },
70 type_alias.as_assoc_item(db).is_none() 86 ))
71 } 87}
72 _ => true, 88
73 } 89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
74 } else { 90 match import_candidate {
75 true 91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
76 } 92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
77 }) 93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
78} 96}
79 97
80fn find_imports<'a>( 98fn find_imports<'a>(
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 118c090d7..6eb34b06b 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -134,3 +134,27 @@ fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
134 let text = db.file_text(file_id); 134 let text = db.file_text(file_id);
135 Arc::new(LineIndex::new(&*text)) 135 Arc::new(LineIndex::new(&*text))
136} 136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
139pub enum SymbolKind {
140 Const,
141 ConstParam,
142 Enum,
143 Field,
144 Function,
145 Impl,
146 Label,
147 LifetimeParam,
148 Local,
149 Macro,
150 Module,
151 SelfParam,
152 Static,
153 Struct,
154 Trait,
155 TypeAlias,
156 TypeParam,
157 Union,
158 ValueParam,
159 Variant,
160}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 0ecb13a64..b9ba0aed5 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -228,6 +228,15 @@ impl Definition {
228 // so do nothing. 228 // so do nothing.
229 } 229 }
230 } 230 }
231 ModuleSource::BlockExpr(b) => {
232 if is_first {
233 let range = Some(b.syntax().text_range());
234 res.insert(file_id, range);
235 } else {
236 // We have already added the enclosing file to the search scope,
237 // so do nothing.
238 }
239 }
231 ModuleSource::SourceFile(_) => { 240 ModuleSource::SourceFile(_) => {
232 res.insert(file_id, None); 241 res.insert(file_id, None);
233 } 242 }
@@ -257,6 +266,7 @@ impl Definition {
257 let mut res = FxHashMap::default(); 266 let mut res = FxHashMap::default();
258 let range = match module_src.value { 267 let range = match module_src.value {
259 ModuleSource::Module(m) => Some(m.syntax().text_range()), 268 ModuleSource::Module(m) => Some(m.syntax().text_range()),
269 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
260 ModuleSource::SourceFile(_) => None, 270 ModuleSource::SourceFile(_) => None,
261 }; 271 };
262 res.insert(file_id, range); 272 res.insert(file_id, range);
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 0aa6a0765..e954bd72e 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -191,7 +191,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
191 191
192 let def_map = db.crate_def_map(krate); 192 let def_map = db.crate_def_map(krate);
193 let mut files = Vec::new(); 193 let mut files = Vec::new();
194 let mut modules = vec![def_map.root]; 194 let mut modules = vec![def_map.root()];
195 while let Some(module) = modules.pop() { 195 while let Some(module) = modules.pop() {
196 let data = &def_map[module]; 196 let data = &def_map[module];
197 files.extend(data.origin.file_id()); 197 files.extend(data.origin.file_id());
@@ -209,7 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
209 query.search(&buf) 209 query.search(&buf)
210} 210}
211 211
212pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> { 212pub fn index_resolve(db: &RootDatabase, name: &str) -> Vec<FileSymbol> {
213 let mut query = Query::new(name.to_string()); 213 let mut query = Query::new(name.to_string());
214 query.exact(); 214 query.exact();
215 query.limit(4); 215 query.limit(4);
@@ -409,7 +409,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
409 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { 409 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
410 let name = node.name()?; 410 let name = node.name()?;
411 let name_range = name.syntax().text_range(); 411 let name_range = name.syntax().text_range();
412 let name = name.text().clone(); 412 let name = name.text().into();
413 let ptr = SyntaxNodePtr::new(node.syntax()); 413 let ptr = SyntaxNodePtr::new(node.syntax());
414 414
415 Some((name, ptr, name_range)) 415 Some((name, ptr, name_range))