mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
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.
This commit is contained in:
parent
36353bb182
commit
0231e4ac77
2 changed files with 77 additions and 21 deletions
|
@ -159,10 +159,16 @@ fn find_path_inner(
|
||||||
let crate_graph = db.crate_graph();
|
let crate_graph = db.crate_graph();
|
||||||
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
|
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
|
||||||
let import_map = db.import_map(dep.crate_id);
|
let import_map = db.import_map(dep.crate_id);
|
||||||
import_map.path_of(item).map(|modpath| {
|
import_map.import_info_for(item).and_then(|info| {
|
||||||
let mut modpath = modpath.clone();
|
// Determine best path for containing module and append last segment from `info`.
|
||||||
modpath.segments.insert(0, dep.as_name());
|
let mut path = find_path_inner(
|
||||||
modpath
|
db,
|
||||||
|
ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
|
||||||
|
from,
|
||||||
|
best_path_len - 1,
|
||||||
|
)?;
|
||||||
|
path.segments.push(info.path.segments.last().unwrap().clone());
|
||||||
|
Some(path)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -299,8 +305,8 @@ mod tests {
|
||||||
/// `code` needs to contain a cursor marker; checks that `find_path` for the
|
/// `code` needs to contain a cursor marker; checks that `find_path` for the
|
||||||
/// item the `path` refers to returns that same path when called from the
|
/// item the `path` refers to returns that same path when called from the
|
||||||
/// module the cursor is in.
|
/// module the cursor is in.
|
||||||
fn check_found_path(code: &str, path: &str) {
|
fn check_found_path(ra_fixture: &str, path: &str) {
|
||||||
let (db, pos) = TestDB::with_position(code);
|
let (db, pos) = TestDB::with_position(ra_fixture);
|
||||||
let module = db.module_for_file(pos.file_id);
|
let module = db.module_for_file(pos.file_id);
|
||||||
let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
|
let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
|
||||||
let ast_path = parsed_path_file
|
let ast_path = parsed_path_file
|
||||||
|
@ -420,7 +426,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn different_crate_renamed() {
|
fn different_crate_renamed() {
|
||||||
// Even if a local path exists, if the item is defined externally, prefer an external path.
|
|
||||||
let code = r#"
|
let code = r#"
|
||||||
//- /main.rs crate:main deps:std
|
//- /main.rs crate:main deps:std
|
||||||
extern crate std as std_renamed;
|
extern crate std as std_renamed;
|
||||||
|
@ -428,7 +433,45 @@ mod tests {
|
||||||
//- /std.rs crate:std
|
//- /std.rs crate:std
|
||||||
pub struct S;
|
pub struct S;
|
||||||
"#;
|
"#;
|
||||||
check_found_path(code, "std::S");
|
check_found_path(code, "std_renamed::S");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partially_imported() {
|
||||||
|
// Tests that short paths are used even for external items, when parts of the path are
|
||||||
|
// already in scope.
|
||||||
|
check_found_path(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main deps:ra_syntax
|
||||||
|
|
||||||
|
use ra_syntax::ast;
|
||||||
|
<|>
|
||||||
|
|
||||||
|
//- /lib.rs crate:ra_syntax
|
||||||
|
pub mod ast {
|
||||||
|
pub enum ModuleItem {
|
||||||
|
A, B, C,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"ast::ModuleItem",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_found_path(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main deps:ra_syntax
|
||||||
|
|
||||||
|
<|>
|
||||||
|
|
||||||
|
//- /lib.rs crate:ra_syntax
|
||||||
|
pub mod ast {
|
||||||
|
pub enum ModuleItem {
|
||||||
|
A, B, C,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"ra_syntax::ast::ModuleItem",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -17,6 +17,15 @@ use crate::{
|
||||||
|
|
||||||
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
|
|
||||||
|
/// Item import details stored in the `ImportMap`.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct ImportInfo {
|
||||||
|
/// A path that can be used to import the item, relative to the crate's root.
|
||||||
|
pub path: ModPath,
|
||||||
|
/// The module containing this item.
|
||||||
|
pub container: ModuleId,
|
||||||
|
}
|
||||||
|
|
||||||
/// A map from publicly exported items to the path needed to import/name them from a downstream
|
/// A map from publicly exported items to the path needed to import/name them from a downstream
|
||||||
/// crate.
|
/// crate.
|
||||||
///
|
///
|
||||||
|
@ -26,7 +35,7 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
/// Note that all paths are relative to the containing crate's root, so the crate name still needs
|
/// Note that all paths are relative to the containing crate's root, so the crate name still needs
|
||||||
/// to be prepended to the `ModPath` before the path is valid.
|
/// to be prepended to the `ModPath` before the path is valid.
|
||||||
pub struct ImportMap {
|
pub struct ImportMap {
|
||||||
map: FxIndexMap<ItemInNs, ModPath>,
|
map: FxIndexMap<ItemInNs, ImportInfo>,
|
||||||
|
|
||||||
/// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
|
/// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
|
||||||
/// values returned by running `fst`.
|
/// values returned by running `fst`.
|
||||||
|
@ -78,12 +87,12 @@ impl ImportMap {
|
||||||
let path = mk_path();
|
let path = mk_path();
|
||||||
match import_map.entry(item) {
|
match import_map.entry(item) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(path);
|
entry.insert(ImportInfo { path, container: module });
|
||||||
}
|
}
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
// If the new path is shorter, prefer that one.
|
// If the new path is shorter, prefer that one.
|
||||||
if path.len() < entry.get().len() {
|
if path.len() < entry.get().path.len() {
|
||||||
*entry.get_mut() = path;
|
*entry.get_mut() = ImportInfo { path, container: module };
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +128,7 @@ impl ImportMap {
|
||||||
let start = last_batch_start;
|
let start = last_batch_start;
|
||||||
last_batch_start = idx + 1;
|
last_batch_start = idx + 1;
|
||||||
|
|
||||||
let key = fst_path(&importables[start].1);
|
let key = fst_path(&importables[start].1.path);
|
||||||
|
|
||||||
builder.insert(key, start as u64).unwrap();
|
builder.insert(key, start as u64).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -132,6 +141,10 @@ impl ImportMap {
|
||||||
|
|
||||||
/// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
|
/// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
|
||||||
pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
|
pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
|
||||||
|
Some(&self.map.get(&item)?.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
|
||||||
self.map.get(&item)
|
self.map.get(&item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap {
|
||||||
let mut importable_paths: Vec<_> = self
|
let mut importable_paths: Vec<_> = self
|
||||||
.map
|
.map
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(item, modpath)| {
|
.map(|(item, info)| {
|
||||||
let ns = match item {
|
let ns = match item {
|
||||||
ItemInNs::Types(_) => "t",
|
ItemInNs::Types(_) => "t",
|
||||||
ItemInNs::Values(_) => "v",
|
ItemInNs::Values(_) => "v",
|
||||||
ItemInNs::Macros(_) => "m",
|
ItemInNs::Macros(_) => "m",
|
||||||
};
|
};
|
||||||
format!("- {} ({})", modpath, ns)
|
format!("- {} ({})", info.path, ns)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering {
|
fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
|
||||||
let lhs_str = fst_path(lhs);
|
let lhs_str = fst_path(&lhs.path);
|
||||||
let rhs_str = fst_path(rhs);
|
let rhs_str = fst_path(&rhs.path);
|
||||||
lhs_str.cmp(&rhs_str)
|
lhs_str.cmp(&rhs_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +256,7 @@ pub fn search_dependencies<'a>(
|
||||||
let importables = &import_map.importables[indexed_value.value as usize..];
|
let importables = &import_map.importables[indexed_value.value as usize..];
|
||||||
|
|
||||||
// Path shared by the importable items in this group.
|
// Path shared by the importable items in this group.
|
||||||
let path = &import_map.map[&importables[0]];
|
let path = &import_map.map[&importables[0]].path;
|
||||||
|
|
||||||
if query.anchor_end {
|
if query.anchor_end {
|
||||||
// Last segment must match query.
|
// Last segment must match query.
|
||||||
|
@ -256,14 +269,14 @@ pub fn search_dependencies<'a>(
|
||||||
// Add the items from this `ModPath` group. Those are all subsequent items in
|
// Add the items from this `ModPath` group. Those are all subsequent items in
|
||||||
// `importables` whose paths match `path`.
|
// `importables` whose paths match `path`.
|
||||||
let iter = importables.iter().copied().take_while(|item| {
|
let iter = importables.iter().copied().take_while(|item| {
|
||||||
let item_path = &import_map.map[item];
|
let item_path = &import_map.map[item].path;
|
||||||
fst_path(item_path) == fst_path(path)
|
fst_path(item_path) == fst_path(path)
|
||||||
});
|
});
|
||||||
|
|
||||||
if query.case_sensitive {
|
if query.case_sensitive {
|
||||||
// FIXME: This does not do a subsequence match.
|
// FIXME: This does not do a subsequence match.
|
||||||
res.extend(iter.filter(|item| {
|
res.extend(iter.filter(|item| {
|
||||||
let item_path = &import_map.map[item];
|
let item_path = &import_map.map[item].path;
|
||||||
item_path.to_string().contains(&query.query)
|
item_path.to_string().contains(&query.query)
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue