diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 103 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir_def/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 355 | ||||
-rw-r--r-- | crates/ra_ide_db/src/imports_locator.rs | 46 | ||||
-rw-r--r-- | crates/ra_ide_db/src/symbol_index.rs | 53 |
6 files changed, 534 insertions, 44 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index edf96d50e..5092bf336 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -130,7 +130,7 @@ impl AutoImportAssets { | |||
130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | 130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { |
131 | let _p = profile("auto_import::search_for_imports"); | 131 | let _p = profile("auto_import::search_for_imports"); |
132 | let current_crate = self.module_with_name_to_import.krate(); | 132 | let current_crate = self.module_with_name_to_import.krate(); |
133 | ImportsLocator::new(db) | 133 | ImportsLocator::new(db, current_crate) |
134 | .find_imports(&self.get_search_query()) | 134 | .find_imports(&self.get_search_query()) |
135 | .into_iter() | 135 | .into_iter() |
136 | .filter_map(|candidate| match &self.import_candidate { | 136 | .filter_map(|candidate| match &self.import_candidate { |
@@ -841,4 +841,105 @@ fn main() { | |||
841 | ", | 841 | ", |
842 | ) | 842 | ) |
843 | } | 843 | } |
844 | |||
845 | #[test] | ||
846 | fn dep_import() { | ||
847 | check_assist( | ||
848 | auto_import, | ||
849 | r" | ||
850 | //- /lib.rs crate:dep | ||
851 | pub struct Struct; | ||
852 | |||
853 | //- /main.rs crate:main deps:dep | ||
854 | fn main() { | ||
855 | Struct<|> | ||
856 | }", | ||
857 | r"use dep::Struct; | ||
858 | |||
859 | fn main() { | ||
860 | Struct | ||
861 | } | ||
862 | ", | ||
863 | ); | ||
864 | } | ||
865 | |||
866 | #[test] | ||
867 | fn whole_segment() { | ||
868 | // Tests that only imports whose last segment matches the identifier get suggested. | ||
869 | check_assist( | ||
870 | auto_import, | ||
871 | r" | ||
872 | //- /lib.rs crate:dep | ||
873 | pub mod fmt { | ||
874 | pub trait Display {} | ||
875 | } | ||
876 | |||
877 | pub fn panic_fmt() {} | ||
878 | |||
879 | //- /main.rs crate:main deps:dep | ||
880 | struct S; | ||
881 | |||
882 | impl f<|>mt::Display for S {}", | ||
883 | r"use dep::fmt; | ||
884 | |||
885 | struct S; | ||
886 | impl fmt::Display for S {} | ||
887 | ", | ||
888 | ); | ||
889 | } | ||
890 | |||
891 | #[test] | ||
892 | fn macro_generated() { | ||
893 | // Tests that macro-generated items are suggested from external crates. | ||
894 | check_assist( | ||
895 | auto_import, | ||
896 | r" | ||
897 | //- /lib.rs crate:dep | ||
898 | |||
899 | macro_rules! mac { | ||
900 | () => { | ||
901 | pub struct Cheese; | ||
902 | }; | ||
903 | } | ||
904 | |||
905 | mac!(); | ||
906 | |||
907 | //- /main.rs crate:main deps:dep | ||
908 | |||
909 | fn main() { | ||
910 | Cheese<|>; | ||
911 | }", | ||
912 | r"use dep::Cheese; | ||
913 | |||
914 | fn main() { | ||
915 | Cheese; | ||
916 | } | ||
917 | ", | ||
918 | ); | ||
919 | } | ||
920 | |||
921 | #[test] | ||
922 | fn casing() { | ||
923 | // Tests that differently cased names don't interfere and we only suggest the matching one. | ||
924 | check_assist( | ||
925 | auto_import, | ||
926 | r" | ||
927 | //- /lib.rs crate:dep | ||
928 | |||
929 | pub struct FMT; | ||
930 | pub struct fmt; | ||
931 | |||
932 | //- /main.rs crate:main deps:dep | ||
933 | |||
934 | fn main() { | ||
935 | FMT<|>; | ||
936 | }", | ||
937 | r"use dep::FMT; | ||
938 | |||
939 | fn main() { | ||
940 | FMT; | ||
941 | } | ||
942 | ", | ||
943 | ); | ||
944 | } | ||
844 | } | 945 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4a06f3bcd..1a9f6cc76 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -9,6 +9,7 @@ use hir_def::{ | |||
9 | builtin_type::BuiltinType, | 9 | builtin_type::BuiltinType, |
10 | docs::Documentation, | 10 | docs::Documentation, |
11 | expr::{BindingAnnotation, Pat, PatId}, | 11 | expr::{BindingAnnotation, Pat, PatId}, |
12 | import_map, | ||
12 | per_ns::PerNs, | 13 | per_ns::PerNs, |
13 | resolver::{HasResolver, Resolver}, | 14 | resolver::{HasResolver, Resolver}, |
14 | type_ref::{Mutability, TypeRef}, | 15 | type_ref::{Mutability, TypeRef}, |
@@ -98,6 +99,23 @@ impl Crate { | |||
98 | db.crate_graph()[self.id].display_name.as_ref().cloned() | 99 | db.crate_graph()[self.id].display_name.as_ref().cloned() |
99 | } | 100 | } |
100 | 101 | ||
102 | pub fn query_external_importables( | ||
103 | self, | ||
104 | db: &dyn DefDatabase, | ||
105 | query: &str, | ||
106 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
107 | import_map::search_dependencies( | ||
108 | db, | ||
109 | self.into(), | ||
110 | import_map::Query::new(query).anchor_end().case_sensitive().limit(40), | ||
111 | ) | ||
112 | .into_iter() | ||
113 | .map(|item| match item { | ||
114 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
115 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
116 | }) | ||
117 | } | ||
118 | |||
101 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | 119 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { |
102 | db.crate_graph().iter().map(|id| Crate { id }).collect() | 120 | db.crate_graph().iter().map(|id| Crate { id }).collect() |
103 | } | 121 | } |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index b85358308..ef1f65ee0 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0" | |||
14 | either = "1.5.3" | 14 | either = "1.5.3" |
15 | anymap = "0.12.1" | 15 | anymap = "0.12.1" |
16 | drop_bomb = "0.1.4" | 16 | drop_bomb = "0.1.4" |
17 | fst = { version = "0.4", default-features = false } | ||
18 | itertools = "0.9.0" | ||
19 | indexmap = "1.4.0" | ||
17 | 20 | ||
18 | stdx = { path = "../stdx" } | 21 | stdx = { path = "../stdx" } |
19 | 22 | ||
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 4284a0a91..36b4fdd81 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | //! A map of all publicly exported items in a crate. | 1 | //! A map of all publicly exported items in a crate. |
2 | 2 | ||
3 | use std::{collections::hash_map::Entry, fmt, sync::Arc}; | 3 | use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; |
4 | 4 | ||
5 | use fst::{self, Streamer}; | ||
6 | use indexmap::{map::Entry, IndexMap}; | ||
5 | use ra_db::CrateId; | 7 | use ra_db::CrateId; |
6 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHasher; |
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
9 | db::DefDatabase, | 11 | db::DefDatabase, |
@@ -13,6 +15,8 @@ use crate::{ | |||
13 | ModuleDefId, ModuleId, | 15 | ModuleDefId, ModuleId, |
14 | }; | 16 | }; |
15 | 17 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | ||
19 | |||
16 | /// A map from publicly exported items to the path needed to import/name them from a downstream | 20 | /// A map from publicly exported items to the path needed to import/name them from a downstream |
17 | /// crate. | 21 | /// crate. |
18 | /// | 22 | /// |
@@ -21,16 +25,24 @@ use crate::{ | |||
21 | /// | 25 | /// |
22 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | 26 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs |
23 | /// to be prepended to the `ModPath` before the path is valid. | 27 | /// to be prepended to the `ModPath` before the path is valid. |
24 | #[derive(Eq, PartialEq)] | ||
25 | pub struct ImportMap { | 28 | pub struct ImportMap { |
26 | map: FxHashMap<ItemInNs, ModPath>, | 29 | map: FxIndexMap<ItemInNs, ModPath>, |
30 | |||
31 | /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the | ||
32 | /// values returned by running `fst`. | ||
33 | /// | ||
34 | /// Since a path can refer to multiple items due to namespacing, we store all items with the | ||
35 | /// same path right after each other. This allows us to find all items after the FST gives us | ||
36 | /// the index of the first one. | ||
37 | importables: Vec<ItemInNs>, | ||
38 | fst: fst::Map<Vec<u8>>, | ||
27 | } | 39 | } |
28 | 40 | ||
29 | impl ImportMap { | 41 | impl ImportMap { |
30 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 42 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
31 | let _p = ra_prof::profile("import_map_query"); | 43 | let _p = ra_prof::profile("import_map_query"); |
32 | let def_map = db.crate_def_map(krate); | 44 | let def_map = db.crate_def_map(krate); |
33 | let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); | 45 | let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); |
34 | 46 | ||
35 | // We look only into modules that are public(ly reexported), starting with the crate root. | 47 | // We look only into modules that are public(ly reexported), starting with the crate root. |
36 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 48 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; |
@@ -88,7 +100,34 @@ impl ImportMap { | |||
88 | } | 100 | } |
89 | } | 101 | } |
90 | 102 | ||
91 | Arc::new(Self { map: import_map }) | 103 | let mut importables = import_map.iter().collect::<Vec<_>>(); |
104 | |||
105 | importables.sort_by(cmp); | ||
106 | |||
107 | // Build the FST, taking care not to insert duplicate values. | ||
108 | |||
109 | let mut builder = fst::MapBuilder::memory(); | ||
110 | let mut last_batch_start = 0; | ||
111 | |||
112 | for idx in 0..importables.len() { | ||
113 | if let Some(next_item) = importables.get(idx + 1) { | ||
114 | if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { | ||
115 | continue; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | let start = last_batch_start; | ||
120 | last_batch_start = idx + 1; | ||
121 | |||
122 | let key = fst_path(&importables[start].1); | ||
123 | |||
124 | builder.insert(key, start as u64).unwrap(); | ||
125 | } | ||
126 | |||
127 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | ||
128 | let importables = importables.iter().map(|(item, _)| **item).collect(); | ||
129 | |||
130 | Arc::new(Self { map: import_map, fst, importables }) | ||
92 | } | 131 | } |
93 | 132 | ||
94 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 133 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
@@ -97,6 +136,15 @@ impl ImportMap { | |||
97 | } | 136 | } |
98 | } | 137 | } |
99 | 138 | ||
139 | impl PartialEq for ImportMap { | ||
140 | fn eq(&self, other: &Self) -> bool { | ||
141 | // `fst` and `importables` are built from `map`, so we don't need to compare them. | ||
142 | self.map == other.map | ||
143 | } | ||
144 | } | ||
145 | |||
146 | impl Eq for ImportMap {} | ||
147 | |||
100 | impl fmt::Debug for ImportMap { | 148 | impl fmt::Debug for ImportMap { |
101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 149 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
102 | let mut importable_paths: Vec<_> = self | 150 | let mut importable_paths: Vec<_> = self |
@@ -117,19 +165,135 @@ impl fmt::Debug for ImportMap { | |||
117 | } | 165 | } |
118 | } | 166 | } |
119 | 167 | ||
168 | fn fst_path(path: &ModPath) -> String { | ||
169 | let mut s = path.to_string(); | ||
170 | s.make_ascii_lowercase(); | ||
171 | s | ||
172 | } | ||
173 | |||
174 | fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { | ||
175 | let lhs_str = fst_path(lhs); | ||
176 | let rhs_str = fst_path(rhs); | ||
177 | lhs_str.cmp(&rhs_str) | ||
178 | } | ||
179 | |||
180 | #[derive(Debug)] | ||
181 | pub struct Query { | ||
182 | query: String, | ||
183 | lowercased: String, | ||
184 | anchor_end: bool, | ||
185 | case_sensitive: bool, | ||
186 | limit: usize, | ||
187 | } | ||
188 | |||
189 | impl Query { | ||
190 | pub fn new(query: &str) -> Self { | ||
191 | Self { | ||
192 | lowercased: query.to_lowercase(), | ||
193 | query: query.to_string(), | ||
194 | anchor_end: false, | ||
195 | case_sensitive: false, | ||
196 | limit: usize::max_value(), | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /// Only returns items whose paths end with the (case-insensitive) query string as their last | ||
201 | /// segment. | ||
202 | pub fn anchor_end(self) -> Self { | ||
203 | Self { anchor_end: true, ..self } | ||
204 | } | ||
205 | |||
206 | /// Limits the returned number of items to `limit`. | ||
207 | pub fn limit(self, limit: usize) -> Self { | ||
208 | Self { limit, ..self } | ||
209 | } | ||
210 | |||
211 | /// Respect casing of the query string when matching. | ||
212 | pub fn case_sensitive(self) -> Self { | ||
213 | Self { case_sensitive: true, ..self } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | /// Searches dependencies of `krate` for an importable path matching `query`. | ||
218 | /// | ||
219 | /// This returns a list of items that could be imported from dependencies of `krate`. | ||
220 | pub fn search_dependencies<'a>( | ||
221 | db: &'a dyn DefDatabase, | ||
222 | krate: CrateId, | ||
223 | query: Query, | ||
224 | ) -> Vec<ItemInNs> { | ||
225 | let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query)); | ||
226 | |||
227 | let graph = db.crate_graph(); | ||
228 | let import_maps: Vec<_> = | ||
229 | graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); | ||
230 | |||
231 | let automaton = fst::automaton::Subsequence::new(&query.lowercased); | ||
232 | |||
233 | let mut op = fst::map::OpBuilder::new(); | ||
234 | for map in &import_maps { | ||
235 | op = op.add(map.fst.search(&automaton)); | ||
236 | } | ||
237 | |||
238 | let mut stream = op.union(); | ||
239 | let mut res = Vec::new(); | ||
240 | while let Some((_, indexed_values)) = stream.next() { | ||
241 | for indexed_value in indexed_values { | ||
242 | let import_map = &import_maps[indexed_value.index]; | ||
243 | let importables = &import_map.importables[indexed_value.value as usize..]; | ||
244 | |||
245 | // Path shared by the importable items in this group. | ||
246 | let path = &import_map.map[&importables[0]]; | ||
247 | |||
248 | if query.anchor_end { | ||
249 | // Last segment must match query. | ||
250 | let last = path.segments.last().unwrap().to_string(); | ||
251 | if last.to_lowercase() != query.lowercased { | ||
252 | continue; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | // Add the items from this `ModPath` group. Those are all subsequent items in | ||
257 | // `importables` whose paths match `path`. | ||
258 | let iter = importables.iter().copied().take_while(|item| { | ||
259 | let item_path = &import_map.map[item]; | ||
260 | fst_path(item_path) == fst_path(path) | ||
261 | }); | ||
262 | |||
263 | if query.case_sensitive { | ||
264 | // FIXME: This does not do a subsequence match. | ||
265 | res.extend(iter.filter(|item| { | ||
266 | let item_path = &import_map.map[item]; | ||
267 | item_path.to_string().contains(&query.query) | ||
268 | })); | ||
269 | } else { | ||
270 | res.extend(iter); | ||
271 | } | ||
272 | |||
273 | if res.len() >= query.limit { | ||
274 | res.truncate(query.limit); | ||
275 | return res; | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | res | ||
281 | } | ||
282 | |||
120 | #[cfg(test)] | 283 | #[cfg(test)] |
121 | mod tests { | 284 | mod tests { |
122 | use super::*; | 285 | use super::*; |
123 | use crate::test_db::TestDB; | 286 | use crate::test_db::TestDB; |
124 | use insta::assert_snapshot; | 287 | use insta::assert_snapshot; |
288 | use itertools::Itertools; | ||
125 | use ra_db::fixture::WithFixture; | 289 | use ra_db::fixture::WithFixture; |
126 | use ra_db::SourceDatabase; | 290 | use ra_db::{SourceDatabase, Upcast}; |
127 | 291 | ||
128 | fn import_map(ra_fixture: &str) -> String { | 292 | fn import_map(ra_fixture: &str) -> String { |
129 | let db = TestDB::with_files(ra_fixture); | 293 | let db = TestDB::with_files(ra_fixture); |
130 | let crate_graph = db.crate_graph(); | 294 | let crate_graph = db.crate_graph(); |
131 | 295 | ||
132 | let import_maps: Vec<_> = crate_graph | 296 | let s = crate_graph |
133 | .iter() | 297 | .iter() |
134 | .filter_map(|krate| { | 298 | .filter_map(|krate| { |
135 | let cdata = &crate_graph[krate]; | 299 | let cdata = &crate_graph[krate]; |
@@ -139,9 +303,41 @@ mod tests { | |||
139 | 303 | ||
140 | Some(format!("{}:\n{:?}", name, map)) | 304 | Some(format!("{}:\n{:?}", name, map)) |
141 | }) | 305 | }) |
142 | .collect(); | 306 | .join("\n"); |
307 | s | ||
308 | } | ||
143 | 309 | ||
144 | import_maps.join("\n") | 310 | fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { |
311 | let db = TestDB::with_files(ra_fixture); | ||
312 | let crate_graph = db.crate_graph(); | ||
313 | let krate = crate_graph | ||
314 | .iter() | ||
315 | .find(|krate| { | ||
316 | crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) | ||
317 | == Some(krate_name.to_string()) | ||
318 | }) | ||
319 | .unwrap(); | ||
320 | |||
321 | search_dependencies(db.upcast(), krate, query) | ||
322 | .into_iter() | ||
323 | .filter_map(|item| { | ||
324 | let mark = match item { | ||
325 | ItemInNs::Types(_) => "t", | ||
326 | ItemInNs::Values(_) => "v", | ||
327 | ItemInNs::Macros(_) => "m", | ||
328 | }; | ||
329 | item.krate(db.upcast()).map(|krate| { | ||
330 | let map = db.import_map(krate); | ||
331 | let path = map.path_of(item).unwrap(); | ||
332 | format!( | ||
333 | "{}::{} ({})", | ||
334 | crate_graph[krate].display_name.as_ref().unwrap(), | ||
335 | path, | ||
336 | mark | ||
337 | ) | ||
338 | }) | ||
339 | }) | ||
340 | .join("\n") | ||
145 | } | 341 | } |
146 | 342 | ||
147 | #[test] | 343 | #[test] |
@@ -328,4 +524,143 @@ mod tests { | |||
328 | lib: | 524 | lib: |
329 | "###); | 525 | "###); |
330 | } | 526 | } |
527 | |||
528 | #[test] | ||
529 | fn namespacing() { | ||
530 | let map = import_map( | ||
531 | r" | ||
532 | //- /lib.rs crate:lib | ||
533 | pub struct Thing; // t + v | ||
534 | #[macro_export] | ||
535 | macro_rules! Thing { // m | ||
536 | () => {}; | ||
537 | } | ||
538 | ", | ||
539 | ); | ||
540 | |||
541 | assert_snapshot!(map, @r###" | ||
542 | lib: | ||
543 | - Thing (m) | ||
544 | - Thing (t) | ||
545 | - Thing (v) | ||
546 | "###); | ||
547 | |||
548 | let map = import_map( | ||
549 | r" | ||
550 | //- /lib.rs crate:lib | ||
551 | pub mod Thing {} // t | ||
552 | #[macro_export] | ||
553 | macro_rules! Thing { // m | ||
554 | () => {}; | ||
555 | } | ||
556 | ", | ||
557 | ); | ||
558 | |||
559 | assert_snapshot!(map, @r###" | ||
560 | lib: | ||
561 | - Thing (m) | ||
562 | - Thing (t) | ||
563 | "###); | ||
564 | } | ||
565 | |||
566 | #[test] | ||
567 | fn search() { | ||
568 | let ra_fixture = r#" | ||
569 | //- /main.rs crate:main deps:dep | ||
570 | //- /dep.rs crate:dep deps:tdep | ||
571 | use tdep::fmt as fmt_dep; | ||
572 | pub mod fmt { | ||
573 | pub trait Display { | ||
574 | fn fmt(); | ||
575 | } | ||
576 | } | ||
577 | #[macro_export] | ||
578 | macro_rules! Fmt { | ||
579 | () => {}; | ||
580 | } | ||
581 | pub struct Fmt; | ||
582 | |||
583 | pub fn format() {} | ||
584 | pub fn no() {} | ||
585 | |||
586 | //- /tdep.rs crate:tdep | ||
587 | pub mod fmt { | ||
588 | pub struct NotImportableFromMain; | ||
589 | } | ||
590 | "#; | ||
591 | |||
592 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); | ||
593 | assert_snapshot!(res, @r###" | ||
594 | dep::fmt (t) | ||
595 | dep::Fmt (t) | ||
596 | dep::Fmt (v) | ||
597 | dep::Fmt (m) | ||
598 | dep::fmt::Display (t) | ||
599 | dep::format (v) | ||
600 | "###); | ||
601 | |||
602 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | ||
603 | assert_snapshot!(res, @r###" | ||
604 | dep::fmt (t) | ||
605 | dep::Fmt (t) | ||
606 | dep::Fmt (v) | ||
607 | dep::Fmt (m) | ||
608 | "###); | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn search_casing() { | ||
613 | let ra_fixture = r#" | ||
614 | //- /main.rs crate:main deps:dep | ||
615 | //- /dep.rs crate:dep | ||
616 | |||
617 | pub struct fmt; | ||
618 | pub struct FMT; | ||
619 | "#; | ||
620 | |||
621 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); | ||
622 | |||
623 | assert_snapshot!(res, @r###" | ||
624 | dep::fmt (t) | ||
625 | dep::fmt (v) | ||
626 | dep::FMT (t) | ||
627 | dep::FMT (v) | ||
628 | "###); | ||
629 | |||
630 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); | ||
631 | |||
632 | assert_snapshot!(res, @r###" | ||
633 | dep::FMT (t) | ||
634 | dep::FMT (v) | ||
635 | "###); | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn search_limit() { | ||
640 | let res = search_dependencies_of( | ||
641 | r#" | ||
642 | //- /main.rs crate:main deps:dep | ||
643 | //- /dep.rs crate:dep | ||
644 | pub mod fmt { | ||
645 | pub trait Display { | ||
646 | fn fmt(); | ||
647 | } | ||
648 | } | ||
649 | #[macro_export] | ||
650 | macro_rules! Fmt { | ||
651 | () => {}; | ||
652 | } | ||
653 | pub struct Fmt; | ||
654 | |||
655 | pub fn format() {} | ||
656 | pub fn no() {} | ||
657 | "#, | ||
658 | "main", | ||
659 | Query::new("").limit(2), | ||
660 | ); | ||
661 | assert_snapshot!(res, @r###" | ||
662 | dep::fmt (t) | ||
663 | dep::Fmt (t) | ||
664 | "###); | ||
665 | } | ||
331 | } | 666 | } |
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index bf0d8db60..fff112e66 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the ra_assists module. | 1 | //! This module contains an import search funcionality that is provided to the ra_assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. |
3 | 3 | ||
4 | use hir::{MacroDef, ModuleDef, Semantics}; | 4 | use hir::{Crate, MacroDef, ModuleDef, Semantics}; |
5 | use ra_prof::profile; | 5 | use ra_prof::profile; |
6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; | 6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; |
7 | 7 | ||
@@ -11,44 +11,46 @@ use crate::{ | |||
11 | RootDatabase, | 11 | RootDatabase, |
12 | }; | 12 | }; |
13 | use either::Either; | 13 | use either::Either; |
14 | use rustc_hash::FxHashSet; | ||
14 | 15 | ||
15 | pub struct ImportsLocator<'a> { | 16 | pub struct ImportsLocator<'a> { |
16 | sema: Semantics<'a, RootDatabase>, | 17 | sema: Semantics<'a, RootDatabase>, |
18 | krate: Crate, | ||
17 | } | 19 | } |
18 | 20 | ||
19 | impl<'a> ImportsLocator<'a> { | 21 | impl<'a> ImportsLocator<'a> { |
20 | pub fn new(db: &'a RootDatabase) -> Self { | 22 | pub fn new(db: &'a RootDatabase, krate: Crate) -> Self { |
21 | Self { sema: Semantics::new(db) } | 23 | Self { sema: Semantics::new(db), krate } |
22 | } | 24 | } |
23 | 25 | ||
24 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { | 26 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { |
25 | let _p = profile("search_for_imports"); | 27 | let _p = profile("search_for_imports"); |
26 | let db = self.sema.db; | 28 | let db = self.sema.db; |
27 | 29 | ||
28 | let project_results = { | 30 | // Query dependencies first. |
29 | let mut query = Query::new(name_to_import.to_string()); | 31 | let mut candidates: FxHashSet<_> = |
30 | query.exact(); | 32 | self.krate.query_external_importables(db, name_to_import).collect(); |
31 | query.limit(40); | 33 | |
32 | symbol_index::world_symbols(db, query) | 34 | // Query the local crate using the symbol index. |
33 | }; | 35 | let local_results = { |
34 | let lib_results = { | ||
35 | let mut query = Query::new(name_to_import.to_string()); | 36 | let mut query = Query::new(name_to_import.to_string()); |
36 | query.libs(); | ||
37 | query.exact(); | 37 | query.exact(); |
38 | query.limit(40); | 38 | query.limit(40); |
39 | symbol_index::world_symbols(db, query) | 39 | symbol_index::crate_symbols(db, self.krate.into(), query) |
40 | }; | 40 | }; |
41 | 41 | ||
42 | project_results | 42 | candidates.extend( |
43 | .into_iter() | 43 | local_results |
44 | .chain(lib_results.into_iter()) | 44 | .into_iter() |
45 | .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) | 45 | .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) |
46 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 46 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
47 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), | 47 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), |
48 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), | 48 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), |
49 | _ => None, | 49 | _ => None, |
50 | }) | 50 | }), |
51 | .collect() | 51 | ); |
52 | |||
53 | candidates.into_iter().collect() | ||
52 | } | 54 | } |
53 | 55 | ||
54 | fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { | 56 | fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { |
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index acc31fe3b..aab918973 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs | |||
@@ -29,9 +29,10 @@ use std::{ | |||
29 | }; | 29 | }; |
30 | 30 | ||
31 | use fst::{self, Streamer}; | 31 | use fst::{self, Streamer}; |
32 | use hir::db::DefDatabase; | ||
32 | use ra_db::{ | 33 | use ra_db::{ |
33 | salsa::{self, ParallelDatabase}, | 34 | salsa::{self, ParallelDatabase}, |
34 | FileId, SourceDatabaseExt, SourceRootId, | 35 | CrateId, FileId, SourceDatabaseExt, SourceRootId, |
35 | }; | 36 | }; |
36 | use ra_syntax::{ | 37 | use ra_syntax::{ |
37 | ast::{self, NameOwner}, | 38 | ast::{self, NameOwner}, |
@@ -110,6 +111,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> | |||
110 | Arc::new(SymbolIndex::new(symbols)) | 111 | Arc::new(SymbolIndex::new(symbols)) |
111 | } | 112 | } |
112 | 113 | ||
114 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | ||
115 | struct Snap(salsa::Snapshot<RootDatabase>); | ||
116 | impl Clone for Snap { | ||
117 | fn clone(&self) -> Snap { | ||
118 | Snap(self.0.snapshot()) | ||
119 | } | ||
120 | } | ||
121 | |||
113 | // Feature: Workspace Symbol | 122 | // Feature: Workspace Symbol |
114 | // | 123 | // |
115 | // Uses fuzzy-search to find types, modules and functions by name across your | 124 | // Uses fuzzy-search to find types, modules and functions by name across your |
@@ -132,13 +141,7 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> | |||
132 | // | VS Code | kbd:[Ctrl+T] | 141 | // | VS Code | kbd:[Ctrl+T] |
133 | // |=== | 142 | // |=== |
134 | pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { | 143 | pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { |
135 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | 144 | let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone()); |
136 | struct Snap(salsa::Snapshot<RootDatabase>); | ||
137 | impl Clone for Snap { | ||
138 | fn clone(&self) -> Snap { | ||
139 | Snap(self.0.snapshot()) | ||
140 | } | ||
141 | } | ||
142 | 145 | ||
143 | let buf: Vec<Arc<SymbolIndex>> = if query.libs { | 146 | let buf: Vec<Arc<SymbolIndex>> = if query.libs { |
144 | let snap = Snap(db.snapshot()); | 147 | let snap = Snap(db.snapshot()); |
@@ -173,6 +176,33 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { | |||
173 | query.search(&buf) | 176 | query.search(&buf) |
174 | } | 177 | } |
175 | 178 | ||
179 | pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> { | ||
180 | // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from | ||
181 | // that instead? | ||
182 | |||
183 | let def_map = db.crate_def_map(krate); | ||
184 | let mut files = Vec::new(); | ||
185 | let mut modules = vec![def_map.root]; | ||
186 | while let Some(module) = modules.pop() { | ||
187 | let data = &def_map[module]; | ||
188 | files.extend(data.origin.file_id()); | ||
189 | modules.extend(data.children.values()); | ||
190 | } | ||
191 | |||
192 | let snap = Snap(db.snapshot()); | ||
193 | |||
194 | #[cfg(not(feature = "wasm"))] | ||
195 | let buf = files | ||
196 | .par_iter() | ||
197 | .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) | ||
198 | .collect::<Vec<_>>(); | ||
199 | |||
200 | #[cfg(feature = "wasm")] | ||
201 | let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>(); | ||
202 | |||
203 | query.search(&buf) | ||
204 | } | ||
205 | |||
176 | pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { | 206 | pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { |
177 | let name = name_ref.text(); | 207 | let name = name_ref.text(); |
178 | let mut query = Query::new(name.to_string()); | 208 | let mut query = Query::new(name.to_string()); |
@@ -298,9 +328,6 @@ impl Query { | |||
298 | let mut stream = op.union(); | 328 | let mut stream = op.union(); |
299 | let mut res = Vec::new(); | 329 | let mut res = Vec::new(); |
300 | while let Some((_, indexed_values)) = stream.next() { | 330 | while let Some((_, indexed_values)) = stream.next() { |
301 | if res.len() >= self.limit { | ||
302 | break; | ||
303 | } | ||
304 | for indexed_value in indexed_values { | 331 | for indexed_value in indexed_values { |
305 | let symbol_index = &indices[indexed_value.index]; | 332 | let symbol_index = &indices[indexed_value.index]; |
306 | let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); | 333 | let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); |
@@ -312,7 +339,11 @@ impl Query { | |||
312 | if self.exact && symbol.name != self.query { | 339 | if self.exact && symbol.name != self.query { |
313 | continue; | 340 | continue; |
314 | } | 341 | } |
342 | |||
315 | res.push(symbol.clone()); | 343 | res.push(symbol.clone()); |
344 | if res.len() >= self.limit { | ||
345 | return res; | ||
346 | } | ||
316 | } | 347 | } |
317 | } | 348 | } |
318 | } | 349 | } |