aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-06-12 12:01:20 +0100
committerJonas Schievink <[email protected]>2020-06-12 12:01:20 +0100
commit0231e4ac77dacf6ca30f6b68c6081415f2da54ba (patch)
tree21b1df550c05bae5b9d9ff23107e69727605c552 /crates/ra_hir_def
parent36353bb1827dbd2efcde2d18c8598c4cc5e2e296 (diff)
find_path: return shorter paths for external items
If a containing module is already in scope, there's no need to use the full path to the item.
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/find_path.rs59
-rw-r--r--crates/ra_hir_def/src/import_map.rs39
2 files changed, 77 insertions, 21 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index a7f59e028..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -159,10 +159,16 @@ fn find_path_inner(
159 let crate_graph = db.crate_graph(); 159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { 160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id); 161 let import_map = db.import_map(dep.crate_id);
162 import_map.path_of(item).map(|modpath| { 162 import_map.import_info_for(item).and_then(|info| {
163 let mut modpath = modpath.clone(); 163 // Determine best path for containing module and append last segment from `info`.
164 modpath.segments.insert(0, dep.as_name()); 164 let mut path = find_path_inner(
165 modpath 165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
166 }) 172 })
167 }); 173 });
168 174
@@ -299,8 +305,8 @@ mod tests {
299 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
300 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
301 /// module the cursor is in. 307 /// module the cursor is in.
302 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
303 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
304 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
305 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
306 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -420,7 +426,6 @@ mod tests {
420 426
421 #[test] 427 #[test]
422 fn different_crate_renamed() { 428 fn different_crate_renamed() {
423 // Even if a local path exists, if the item is defined externally, prefer an external path.
424 let code = r#" 429 let code = r#"
425 //- /main.rs crate:main deps:std 430 //- /main.rs crate:main deps:std
426 extern crate std as std_renamed; 431 extern crate std as std_renamed;
@@ -428,7 +433,45 @@ mod tests {
428 //- /std.rs crate:std 433 //- /std.rs crate:std
429 pub struct S; 434 pub struct S;
430 "#; 435 "#;
431 check_found_path(code, "std::S"); 436 check_found_path(code, "std_renamed::S");
437 }
438
439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
432 } 475 }
433 476
434 #[test] 477 #[test]
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 36b4fdd81..68e20d06b 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -17,6 +17,15 @@ use crate::{
17 17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19 19
20/// Item import details stored in the `ImportMap`.
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ImportInfo {
23 /// A path that can be used to import the item, relative to the crate's root.
24 pub path: ModPath,
25 /// The module containing this item.
26 pub container: ModuleId,
27}
28
20/// A map from publicly exported items to the path needed to import/name them from a downstream 29/// A map from publicly exported items to the path needed to import/name them from a downstream
21/// crate. 30/// crate.
22/// 31///
@@ -26,7 +35,7 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
26/// Note that all paths are relative to the containing crate's root, so the crate name still needs 35/// Note that all paths are relative to the containing crate's root, so the crate name still needs
27/// to be prepended to the `ModPath` before the path is valid. 36/// to be prepended to the `ModPath` before the path is valid.
28pub struct ImportMap { 37pub struct ImportMap {
29 map: FxIndexMap<ItemInNs, ModPath>, 38 map: FxIndexMap<ItemInNs, ImportInfo>,
30 39
31 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the 40 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
32 /// values returned by running `fst`. 41 /// values returned by running `fst`.
@@ -78,12 +87,12 @@ impl ImportMap {
78 let path = mk_path(); 87 let path = mk_path();
79 match import_map.entry(item) { 88 match import_map.entry(item) {
80 Entry::Vacant(entry) => { 89 Entry::Vacant(entry) => {
81 entry.insert(path); 90 entry.insert(ImportInfo { path, container: module });
82 } 91 }
83 Entry::Occupied(mut entry) => { 92 Entry::Occupied(mut entry) => {
84 // If the new path is shorter, prefer that one. 93 // If the new path is shorter, prefer that one.
85 if path.len() < entry.get().len() { 94 if path.len() < entry.get().path.len() {
86 *entry.get_mut() = path; 95 *entry.get_mut() = ImportInfo { path, container: module };
87 } else { 96 } else {
88 continue; 97 continue;
89 } 98 }
@@ -119,7 +128,7 @@ impl ImportMap {
119 let start = last_batch_start; 128 let start = last_batch_start;
120 last_batch_start = idx + 1; 129 last_batch_start = idx + 1;
121 130
122 let key = fst_path(&importables[start].1); 131 let key = fst_path(&importables[start].1.path);
123 132
124 builder.insert(key, start as u64).unwrap(); 133 builder.insert(key, start as u64).unwrap();
125 } 134 }
@@ -132,6 +141,10 @@ impl ImportMap {
132 141
133 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
134 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { 143 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
144 Some(&self.map.get(&item)?.path)
145 }
146
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
135 self.map.get(&item) 148 self.map.get(&item)
136 } 149 }
137} 150}
@@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap {
150 let mut importable_paths: Vec<_> = self 163 let mut importable_paths: Vec<_> = self
151 .map 164 .map
152 .iter() 165 .iter()
153 .map(|(item, modpath)| { 166 .map(|(item, info)| {
154 let ns = match item { 167 let ns = match item {
155 ItemInNs::Types(_) => "t", 168 ItemInNs::Types(_) => "t",
156 ItemInNs::Values(_) => "v", 169 ItemInNs::Values(_) => "v",
157 ItemInNs::Macros(_) => "m", 170 ItemInNs::Macros(_) => "m",
158 }; 171 };
159 format!("- {} ({})", modpath, ns) 172 format!("- {} ({})", info.path, ns)
160 }) 173 })
161 .collect(); 174 .collect();
162 175
@@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String {
171 s 184 s
172} 185}
173 186
174fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { 187fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
175 let lhs_str = fst_path(lhs); 188 let lhs_str = fst_path(&lhs.path);
176 let rhs_str = fst_path(rhs); 189 let rhs_str = fst_path(&rhs.path);
177 lhs_str.cmp(&rhs_str) 190 lhs_str.cmp(&rhs_str)
178} 191}
179 192
@@ -243,7 +256,7 @@ pub fn search_dependencies<'a>(
243 let importables = &import_map.importables[indexed_value.value as usize..]; 256 let importables = &import_map.importables[indexed_value.value as usize..];
244 257
245 // Path shared by the importable items in this group. 258 // Path shared by the importable items in this group.
246 let path = &import_map.map[&importables[0]]; 259 let path = &import_map.map[&importables[0]].path;
247 260
248 if query.anchor_end { 261 if query.anchor_end {
249 // Last segment must match query. 262 // Last segment must match query.
@@ -256,14 +269,14 @@ pub fn search_dependencies<'a>(
256 // Add the items from this `ModPath` group. Those are all subsequent items in 269 // Add the items from this `ModPath` group. Those are all subsequent items in
257 // `importables` whose paths match `path`. 270 // `importables` whose paths match `path`.
258 let iter = importables.iter().copied().take_while(|item| { 271 let iter = importables.iter().copied().take_while(|item| {
259 let item_path = &import_map.map[item]; 272 let item_path = &import_map.map[item].path;
260 fst_path(item_path) == fst_path(path) 273 fst_path(item_path) == fst_path(path)
261 }); 274 });
262 275
263 if query.case_sensitive { 276 if query.case_sensitive {
264 // FIXME: This does not do a subsequence match. 277 // FIXME: This does not do a subsequence match.
265 res.extend(iter.filter(|item| { 278 res.extend(iter.filter(|item| {
266 let item_path = &import_map.map[item]; 279 let item_path = &import_map.map[item].path;
267 item_path.to_string().contains(&query.query) 280 item_path.to_string().contains(&query.query)
268 })); 281 }));
269 } else { 282 } else {