aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-11 11:07:52 +0100
committerGitHub <[email protected]>2020-06-11 11:07:52 +0100
commitf320c38aec185de2a02e0febff133da1f6a3462b (patch)
treef22c1d5af6dcd994667d656833c3a27c568961ff
parentdfbd81e84a699005db4e88e9fbf8d4440525a097 (diff)
parent6766a6b0e189f47d7a405c872598bca9a2395360 (diff)
Merge #4819
4819: Add an FST index to `ImportMap` and use it to speed up auto import r=matklad a=jonas-schievink For the importing crate, we still use the symbol index, but I've modified it to only look at files that comprise that crate (instead of the whole workspace). Oh, and since now the symbol query limit is respected correctly, it's possible that some results from the local crate now disappear if there are many matches. Fixes https://github.com/rust-analyzer/rust-analyzer/issues/4763 Co-authored-by: Jonas Schievink <[email protected]>
-rw-r--r--Cargo.lock3
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs103
-rw-r--r--crates/ra_hir/src/code_model.rs18
-rw-r--r--crates/ra_hir_def/Cargo.toml3
-rw-r--r--crates/ra_hir_def/src/import_map.rs355
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs46
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs53
7 files changed, 537 insertions, 44 deletions
diff --git a/Cargo.lock b/Cargo.lock
index df79334c9..e6338e316 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -981,7 +981,10 @@ dependencies = [
981 "anymap", 981 "anymap",
982 "drop_bomb", 982 "drop_bomb",
983 "either", 983 "either",
984 "fst",
985 "indexmap",
984 "insta", 986 "insta",
987 "itertools",
985 "log", 988 "log",
986 "once_cell", 989 "once_cell",
987 "ra_arena", 990 "ra_arena",
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
859fn 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
885struct S;
886impl 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
914fn 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
939fn 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"
14either = "1.5.3" 14either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17fst = { version = "0.4", default-features = false }
18itertools = "0.9.0"
19indexmap = "1.4.0"
17 20
18stdx = { path = "../stdx" } 21stdx = { 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
3use std::{collections::hash_map::Entry, fmt, sync::Arc}; 3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4 4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
5use ra_db::CrateId; 7use ra_db::CrateId;
6use rustc_hash::FxHashMap; 8use rustc_hash::FxHasher;
7 9
8use crate::{ 10use crate::{
9 db::DefDatabase, 11 db::DefDatabase,
@@ -13,6 +15,8 @@ use crate::{
13 ModuleDefId, ModuleId, 15 ModuleDefId, ModuleId,
14}; 16};
15 17
18type 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)]
25pub struct ImportMap { 28pub 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
29impl ImportMap { 41impl 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
139impl 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
146impl Eq for ImportMap {}
147
100impl fmt::Debug for ImportMap { 148impl 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
168fn fst_path(path: &ModPath) -> String {
169 let mut s = path.to_string();
170 s.make_ascii_lowercase();
171 s
172}
173
174fn 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)]
181pub struct Query {
182 query: String,
183 lowercased: String,
184 anchor_end: bool,
185 case_sensitive: bool,
186 limit: usize,
187}
188
189impl 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`.
220pub 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)]
121mod tests { 284mod 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
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,44 +11,46 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub struct ImportsLocator<'a> {
16 sema: Semantics<'a, RootDatabase>, 17 sema: Semantics<'a, RootDatabase>,
18 krate: Crate,
17} 19}
18 20
19impl<'a> ImportsLocator<'a> { 21impl<'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
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
36use ra_syntax::{ 37use 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`
115struct Snap(salsa::Snapshot<RootDatabase>);
116impl 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// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 143pub 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
179pub 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
176pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { 206pub 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 }