mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge #1194
1194: Pr 1190 r=matklad a=matklad Co-authored-by: Andrea Pretto <eulerdisk@gmail.com> Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
c416caeda2
7 changed files with 272 additions and 78 deletions
|
@ -1,12 +1,122 @@
|
|||
use crate::completion::{Completions, CompletionContext};
|
||||
use rustc_hash::FxHashMap;
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
use ra_syntax::{SmolStr, ast, AstNode};
|
||||
use ra_assists::auto_import;
|
||||
|
||||
use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
|
||||
|
||||
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
if !ctx.is_trivial_path {
|
||||
return;
|
||||
}
|
||||
let names = ctx.analyzer.all_names(ctx.db);
|
||||
if ctx.is_trivial_path {
|
||||
let names = ctx.analyzer.all_names(ctx.db);
|
||||
names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
|
||||
|
||||
names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
|
||||
// auto-import
|
||||
// We fetch ident from the original file, because we need to pre-filter auto-imports
|
||||
if ast::NameRef::cast(ctx.token.parent()).is_some() {
|
||||
let import_resolver = ImportResolver::new();
|
||||
let import_names = import_resolver.all_names(ctx.token.text());
|
||||
import_names.into_iter().for_each(|(name, path)| {
|
||||
let edit = {
|
||||
let mut builder = TextEditBuilder::default();
|
||||
builder.replace(ctx.source_range(), name.to_string());
|
||||
auto_import::auto_import_text_edit(
|
||||
ctx.token.parent(),
|
||||
ctx.token.parent(),
|
||||
&path,
|
||||
&mut builder,
|
||||
);
|
||||
builder.finish()
|
||||
};
|
||||
|
||||
// Hack: copied this check form conv.rs beacause auto import can produce edits
|
||||
// that invalidate assert in conv_with.
|
||||
if edit
|
||||
.as_atoms()
|
||||
.iter()
|
||||
.filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
|
||||
.all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
|
||||
{
|
||||
CompletionItem::new(
|
||||
CompletionKind::Reference,
|
||||
ctx.source_range(),
|
||||
build_import_label(&name, &path),
|
||||
)
|
||||
.text_edit(edit)
|
||||
.add_to(acc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_import_label(name: &str, path: &Vec<SmolStr>) -> String {
|
||||
let mut buf = String::with_capacity(64);
|
||||
buf.push_str(name);
|
||||
buf.push_str(" (");
|
||||
fmt_import_path(path, &mut buf);
|
||||
buf.push_str(")");
|
||||
buf
|
||||
}
|
||||
|
||||
fn fmt_import_path(path: &Vec<SmolStr>, buf: &mut String) {
|
||||
let mut segments = path.iter();
|
||||
if let Some(s) = segments.next() {
|
||||
buf.push_str(&s);
|
||||
}
|
||||
for s in segments {
|
||||
buf.push_str("::");
|
||||
buf.push_str(&s);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct ImportResolver {
|
||||
// todo: use fst crate or something like that
|
||||
dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
|
||||
}
|
||||
|
||||
impl ImportResolver {
|
||||
pub(crate) fn new() -> Self {
|
||||
let dummy_names = vec![
|
||||
(SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
|
||||
(SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
|
||||
(SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
|
||||
(SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
|
||||
(
|
||||
SmolStr::new("Debug"),
|
||||
vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
|
||||
),
|
||||
(
|
||||
SmolStr::new("Display"),
|
||||
vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
|
||||
),
|
||||
(
|
||||
SmolStr::new("Hash"),
|
||||
vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
|
||||
),
|
||||
(
|
||||
SmolStr::new("Hasher"),
|
||||
vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
|
||||
),
|
||||
(
|
||||
SmolStr::new("Iterator"),
|
||||
vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
|
||||
),
|
||||
];
|
||||
|
||||
ImportResolver { dummy_names }
|
||||
}
|
||||
|
||||
// Returns a map of importable items filtered by name.
|
||||
// The map associates item name with its full path.
|
||||
// todo: should return Resolutions
|
||||
pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
|
||||
if name.len() > 1 {
|
||||
self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
|
||||
} else {
|
||||
FxHashMap::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -27,7 +27,7 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) is_pat_binding: bool,
|
||||
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
||||
pub(super) is_trivial_path: bool,
|
||||
/// If not a trivial, path, the prefix (qualifier).
|
||||
/// If not a trivial path, the prefix (qualifier).
|
||||
pub(super) path_prefix: Option<hir::Path>,
|
||||
pub(super) after_if: bool,
|
||||
/// `true` if we are a statement or a last expr in the block.
|
||||
|
@ -151,6 +151,7 @@ impl<'a> CompletionContext<'a> {
|
|||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(segment) = ast::PathSegment::cast(parent) {
|
||||
let path = segment.parent_path();
|
||||
self.is_call = path
|
||||
|
@ -167,6 +168,7 @@ impl<'a> CompletionContext<'a> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if path.qualifier().is_none() {
|
||||
self.is_trivial_path = true;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue