move auto-imoprter into IDE

auto-import is purely an IDE concern, so it should be done outside of
HIR
This commit is contained in:
Aleksey Kladov 2019-04-22 15:56:28 +03:00
parent 200032852b
commit e01052d1f0
7 changed files with 68 additions and 92 deletions

View file

@ -492,7 +492,6 @@ fn apply_auto_import(
} }
} }
#[allow(unused)]
pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> { pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
let mut ps = Vec::<SmolStr>::with_capacity(10); let mut ps = Vec::<SmolStr>::with_capacity(10);
match path.kind { match path.kind {
@ -503,7 +502,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
hir::PathKind::Super => ps.push("super".into()), hir::PathKind::Super => ps.push("super".into()),
} }
for s in path.segments.iter() { for s in path.segments.iter() {
ps.push(s.name.to_smolstr()); ps.push(s.name.to_string().into());
} }
ps ps
} }
@ -511,7 +510,6 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
// This function produces sequence of text edits into edit // This function produces sequence of text edits into edit
// to import the target path in the most appropriate scope given // to import the target path in the most appropriate scope given
// the cursor position // the cursor position
#[allow(unused)]
pub fn auto_import_text_edit( pub fn auto_import_text_edit(
// Ideally the position of the cursor, used to // Ideally the position of the cursor, used to
position: &SyntaxNode, position: &SyntaxNode,

View file

@ -52,7 +52,7 @@ use crate::{
db::{HirDatabase, DefDatabase}, db::{HirDatabase, DefDatabase},
name::{AsName, KnownName}, name::{AsName, KnownName},
source_id::{FileAstId, AstId}, source_id::{FileAstId, AstId},
resolve::Resolver, resolve::ImportResolver, resolve::Resolver,
}; };
pub use self::{ pub use self::{

View file

@ -46,8 +46,17 @@ impl Name {
Name::new(idx.to_string().into()) Name::new(idx.to_string().into())
} }
pub fn to_smolstr(&self) -> SmolStr { // There's should be no way to extract a string out of `Name`: `Name` in the
self.text.clone() // future, `Name` will include hygiene information, and you can't encode
// hygiene into a String.
//
// If you need to compare something with `Name`, compare `Name`s directly.
//
// If you need to render `Name` for the user, use the `Display` impl, but be
// aware that it strips hygiene info.
#[deprecated(note = "use to_string instead")]
pub fn as_smolstr(&self) -> &SmolStr {
&self.text
} }
pub(crate) fn as_known_name(&self) -> Option<KnownName> { pub(crate) fn as_known_name(&self) -> Option<KnownName> {

View file

@ -23,12 +23,6 @@ pub(crate) struct Resolver {
scopes: Vec<Scope>, scopes: Vec<Scope>,
} }
#[derive(Debug, Clone, Default)]
pub(crate) struct ImportResolver {
// todo: use fst crate or something like that
dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
}
// FIXME how to store these best // FIXME how to store these best
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ModuleItemMap { pub(crate) struct ModuleItemMap {
@ -317,56 +311,3 @@ impl Scope {
} }
} }
} }
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,
_db: &impl HirDatabase,
name: &Name,
) -> FxHashMap<SmolStr, Vec<SmolStr>> {
let name = name.to_smolstr();
if name.len() > 1 {
self.dummy_names
.iter()
.filter(|(n, _)| n.as_str().contains(name.as_str()))
.cloned()
.collect()
} else {
FxHashMap::default()
}
}
}

View file

@ -14,12 +14,11 @@ use ra_syntax::{
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
algo::find_node_at_offset, algo::find_node_at_offset,
SyntaxKind::*, SyntaxKind::*,
SmolStr,
}; };
use crate::{ use crate::{
HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name,
AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, ImportResolver, AsName, Module, HirFileId, Crate, Trait, Resolver, Ty,
expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, expr::{BodySourceMap, scope::{ScopeId, ExprScopes}},
ids::LocationCtx, ids::LocationCtx,
expr, AstId, expr, AstId,
@ -171,7 +170,6 @@ fn def_with_body_from_child_node(
#[derive(Debug)] #[derive(Debug)]
pub struct SourceAnalyzer { pub struct SourceAnalyzer {
resolver: Resolver, resolver: Resolver,
import_resolver: ImportResolver,
body_source_map: Option<Arc<BodySourceMap>>, body_source_map: Option<Arc<BodySourceMap>>,
infer: Option<Arc<crate::ty::InferenceResult>>, infer: Option<Arc<crate::ty::InferenceResult>>,
scopes: Option<Arc<crate::expr::ExprScopes>>, scopes: Option<Arc<crate::expr::ExprScopes>>,
@ -219,7 +217,6 @@ impl SourceAnalyzer {
offset: Option<TextUnit>, offset: Option<TextUnit>,
) -> SourceAnalyzer { ) -> SourceAnalyzer {
let def_with_body = def_with_body_from_child_node(db, file_id, node); let def_with_body = def_with_body_from_child_node(db, file_id, node);
let import_resolver = ImportResolver::new();
if let Some(def) = def_with_body { if let Some(def) = def_with_body {
let source_map = def.body_source_map(db); let source_map = def.body_source_map(db);
let scopes = db.expr_scopes(def); let scopes = db.expr_scopes(def);
@ -230,7 +227,6 @@ impl SourceAnalyzer {
let resolver = expr::resolver_for_scope(def.body(db), db, scope); let resolver = expr::resolver_for_scope(def.body(db), db, scope);
SourceAnalyzer { SourceAnalyzer {
resolver, resolver,
import_resolver,
body_source_map: Some(source_map), body_source_map: Some(source_map),
infer: Some(def.infer(db)), infer: Some(def.infer(db)),
scopes: Some(scopes), scopes: Some(scopes),
@ -241,7 +237,6 @@ impl SourceAnalyzer {
.ancestors() .ancestors()
.find_map(|node| try_get_resolver_for_node(db, file_id, node)) .find_map(|node| try_get_resolver_for_node(db, file_id, node))
.unwrap_or_default(), .unwrap_or_default(),
import_resolver,
body_source_map: None, body_source_map: None,
infer: None, infer: None,
scopes: None, scopes: None,
@ -328,14 +323,6 @@ impl SourceAnalyzer {
self.resolver.all_names(db) self.resolver.all_names(db)
} }
pub fn all_import_names(
&self,
db: &impl HirDatabase,
name: &Name,
) -> FxHashMap<SmolStr, Vec<SmolStr>> {
self.import_resolver.all_names(db, name)
}
pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
// FIXME: at least, this should work with any DefWithBody, but ideally // FIXME: at least, this should work with any DefWithBody, but ideally
// this should be hir-based altogether // this should be hir-based altogether

View file

@ -1,6 +1,8 @@
use rustc_hash::FxHashMap;
use ra_text_edit::TextEditBuilder; use ra_text_edit::TextEditBuilder;
use ra_syntax::SmolStr; use ra_syntax::SmolStr;
use ra_assists::auto_import; use ra_assists::auto_import;
use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
@ -10,7 +12,8 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
} }
if let Some(name) = ctx.path_ident.as_ref() { if let Some(name) = ctx.path_ident.as_ref() {
let import_names = ctx.analyzer.all_import_names(ctx.db, name); let import_resolver = ImportResolver::new();
let import_names = import_resolver.all_names(&name.to_string());
import_names.into_iter().for_each(|(name, path)| { import_names.into_iter().for_each(|(name, path)| {
let edit = { let edit = {
let mut builder = TextEditBuilder::default(); let mut builder = TextEditBuilder::default();
@ -64,6 +67,56 @@ fn fmt_import_path(path: &Vec<SmolStr>, buf: &mut String) {
} }
} }
#[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)] #[cfg(test)]
mod tests { mod tests {
use crate::completion::{CompletionKind, check_completion}; use crate::completion::{CompletionKind, check_completion};

View file

@ -86,18 +86,6 @@ impl<'a> CompletionContext<'a> {
} }
fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) {
// We heed the original NameRef before the "intellijRulezz" hack
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(original_file.syntax(), offset)
{
if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
if let Some(path) = hir::Path::from_ast(path) {
if let Some(ident) = path.as_ident() {
self.path_ident = Some(ident.clone());
}
}
}
}
// Insert a fake ident to get a valid parse tree. We will use this file // Insert a fake ident to get a valid parse tree. We will use this file
// to determine context, though the original_file will be used for // to determine context, though the original_file will be used for
// actual completion. // actual completion.