aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAndrea Pretto <[email protected]>2019-04-15 15:11:32 +0100
committerAndrea Pretto <[email protected]>2019-04-21 23:14:58 +0100
commit914421495835380b96e0016763fda6eff31a8179 (patch)
tree86b348e13e9272f1320aad0416313c0f6f93a19c /crates
parentcf0eff2e332f46eda4fcecb043854c9c0d710e4e (diff)
complete_import: add new import resolver infrastructure with some hardcoded importable name.
Changes complete_scope to support that.
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_hir/src/lib.rs2
-rw-r--r--crates/ra_hir/src/resolve.rs65
-rw-r--r--crates/ra_hir/src/source_binder.rs19
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs55
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs23
6 files changed, 155 insertions, 11 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index ded401b63..173c004cd 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -98,7 +98,7 @@ mod inline_local_variable;
98mod replace_if_let_with_match; 98mod replace_if_let_with_match;
99mod split_import; 99mod split_import;
100mod remove_dbg; 100mod remove_dbg;
101mod auto_import; 101pub mod auto_import;
102mod add_missing_impl_members; 102mod add_missing_impl_members;
103 103
104fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 104fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 4411715de..f156e3f07 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -52,7 +52,7 @@ use crate::{
52 db::{HirDatabase, DefDatabase}, 52 db::{HirDatabase, DefDatabase},
53 name::{AsName, KnownName}, 53 name::{AsName, KnownName},
54 source_id::{FileAstId, AstId}, 54 source_id::{FileAstId, AstId},
55 resolve::Resolver, 55 resolve::Resolver, resolve::ImportResolver,
56}; 56};
57 57
58pub use self::{ 58pub use self::{
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index f2c85eb66..0f866e6c2 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -3,6 +3,8 @@ use std::sync::Arc;
3 3
4use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
5 5
6use ra_syntax::SmolStr;
7
6use crate::{ 8use crate::{
7 ModuleDef, 9 ModuleDef,
8 code_model_api::Crate, 10 code_model_api::Crate,
@@ -12,8 +14,12 @@ use crate::{
12 generics::GenericParams, 14 generics::GenericParams,
13 expr::{scope::{ExprScopes, ScopeId}, PatId}, 15 expr::{scope::{ExprScopes, ScopeId}, PatId},
14 impl_block::ImplBlock, 16 impl_block::ImplBlock,
17<<<<<<< HEAD
15 path::Path, 18 path::Path,
16 Trait 19 Trait
20=======
21 path::Path, Trait,
22>>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name.
17}; 23};
18 24
19#[derive(Debug, Clone, Default)] 25#[derive(Debug, Clone, Default)]
@@ -21,6 +27,12 @@ pub(crate) struct Resolver {
21 scopes: Vec<Scope>, 27 scopes: Vec<Scope>,
22} 28}
23 29
30#[derive(Debug, Clone, Default)]
31pub(crate) struct ImportResolver {
32 // todo: use fst crate or something like that
33 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
34}
35
24// FIXME how to store these best 36// FIXME how to store these best
25#[derive(Debug, Clone)] 37#[derive(Debug, Clone)]
26pub(crate) struct ModuleItemMap { 38pub(crate) struct ModuleItemMap {
@@ -309,3 +321,56 @@ impl Scope {
309 } 321 }
310 } 322 }
311} 323}
324
325impl ImportResolver {
326 pub(crate) fn new() -> Self {
327 let dummy_names = vec![
328 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
329 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
330 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
331 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
332 (
333 SmolStr::new("Debug"),
334 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
335 ),
336 (
337 SmolStr::new("Display"),
338 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
339 ),
340 (
341 SmolStr::new("Hash"),
342 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
343 ),
344 (
345 SmolStr::new("Hasher"),
346 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
347 ),
348 (
349 SmolStr::new("Iterator"),
350 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
351 ),
352 ];
353
354 ImportResolver { dummy_names }
355 }
356
357 // Returns a map of importable items filtered by name.
358 // The map associates item name with its full path.
359 // todo: should return Resolutions
360 pub(crate) fn all_names(
361 &self,
362 _db: &impl HirDatabase,
363 name: &Name,
364 ) -> FxHashMap<SmolStr, Vec<SmolStr>> {
365 let name = name.to_smolstr();
366 if name.len() > 1 {
367 self.dummy_names
368 .iter()
369 .filter(|(n, _)| n.as_str().contains(name.as_str()))
370 .cloned()
371 .collect()
372 } else {
373 FxHashMap::default()
374 }
375 }
376}
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index f1bb13bc6..a6f0ab289 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -14,14 +14,19 @@ use ra_syntax::{
14 ast::{self, AstNode, NameOwner}, 14 ast::{self, AstNode, NameOwner},
15 algo::find_node_at_offset, 15 algo::find_node_at_offset,
16 SyntaxKind::*, 16 SyntaxKind::*,
17 SmolStr,
17}; 18};
18 19
19use crate::{ 20use crate::{
20 HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, 21 HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name,
22<<<<<<< HEAD
21 AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, 23 AsName, Module, HirFileId, Crate, Trait, Resolver, Ty,
24=======
25 AsName, Module, HirFileId, Crate, Trait, Resolver, ImportResolver,
26>>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name.
22 expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, 27 expr::{BodySourceMap, scope::{ScopeId, ExprScopes}},
23 ids::LocationCtx, 28 ids::LocationCtx,
24 expr, AstId 29 expr, AstId,
25}; 30};
26 31
27/// Locates the module by `FileId`. Picks topmost module in the file. 32/// Locates the module by `FileId`. Picks topmost module in the file.
@@ -170,6 +175,7 @@ fn def_with_body_from_child_node(
170#[derive(Debug)] 175#[derive(Debug)]
171pub struct SourceAnalyzer { 176pub struct SourceAnalyzer {
172 resolver: Resolver, 177 resolver: Resolver,
178 import_resolver: ImportResolver,
173 body_source_map: Option<Arc<BodySourceMap>>, 179 body_source_map: Option<Arc<BodySourceMap>>,
174 infer: Option<Arc<crate::ty::InferenceResult>>, 180 infer: Option<Arc<crate::ty::InferenceResult>>,
175 scopes: Option<Arc<crate::expr::ExprScopes>>, 181 scopes: Option<Arc<crate::expr::ExprScopes>>,
@@ -217,6 +223,7 @@ impl SourceAnalyzer {
217 offset: Option<TextUnit>, 223 offset: Option<TextUnit>,
218 ) -> SourceAnalyzer { 224 ) -> SourceAnalyzer {
219 let def_with_body = def_with_body_from_child_node(db, file_id, node); 225 let def_with_body = def_with_body_from_child_node(db, file_id, node);
226 let import_resolver = ImportResolver::new();
220 if let Some(def) = def_with_body { 227 if let Some(def) = def_with_body {
221 let source_map = def.body_source_map(db); 228 let source_map = def.body_source_map(db);
222 let scopes = db.expr_scopes(def); 229 let scopes = db.expr_scopes(def);
@@ -227,6 +234,7 @@ impl SourceAnalyzer {
227 let resolver = expr::resolver_for_scope(def.body(db), db, scope); 234 let resolver = expr::resolver_for_scope(def.body(db), db, scope);
228 SourceAnalyzer { 235 SourceAnalyzer {
229 resolver, 236 resolver,
237 import_resolver,
230 body_source_map: Some(source_map), 238 body_source_map: Some(source_map),
231 infer: Some(def.infer(db)), 239 infer: Some(def.infer(db)),
232 scopes: Some(scopes), 240 scopes: Some(scopes),
@@ -237,6 +245,7 @@ impl SourceAnalyzer {
237 .ancestors() 245 .ancestors()
238 .find_map(|node| try_get_resolver_for_node(db, file_id, node)) 246 .find_map(|node| try_get_resolver_for_node(db, file_id, node))
239 .unwrap_or_default(), 247 .unwrap_or_default(),
248 import_resolver,
240 body_source_map: None, 249 body_source_map: None,
241 infer: None, 250 infer: None,
242 scopes: None, 251 scopes: None,
@@ -323,6 +332,14 @@ impl SourceAnalyzer {
323 self.resolver.all_names(db) 332 self.resolver.all_names(db)
324 } 333 }
325 334
335 pub fn all_import_names(
336 &self,
337 db: &impl HirDatabase,
338 name: &Name,
339 ) -> FxHashMap<SmolStr, Vec<SmolStr>> {
340 self.import_resolver.all_names(db, name)
341 }
342
326 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { 343 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
327 // FIXME: at least, this should work with any DefWithBody, but ideally 344 // FIXME: at least, this should work with any DefWithBody, but ideally
328 // this should be hir-based altogether 345 // this should be hir-based altogether
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index fd256fc3b..63d475823 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -1,12 +1,57 @@
1use crate::completion::{Completions, CompletionContext}; 1use ra_text_edit::TextEditBuilder;
2use ra_syntax::SmolStr;
3use ra_assists::auto_import;
4use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
2 5
3pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
4 if !ctx.is_trivial_path { 7 if ctx.is_trivial_path {
5 return; 8 let names = ctx.analyzer.all_names(ctx.db);
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
6 } 10 }
7 let names = ctx.analyzer.all_names(ctx.db);
8 11
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); 12 if let Some(name) = ctx.path_ident.as_ref() {
13 let import_names = ctx.analyzer.all_import_names(ctx.db, name);
14 import_names.into_iter().for_each(|(name, path)| {
15 let edit = {
16 let mut builder = TextEditBuilder::default();
17 builder.replace(ctx.source_range(), name.to_string());
18 auto_import::auto_import_text_edit(
19 ctx.token.parent(),
20 ctx.token.parent(),
21 &path,
22 &mut builder,
23 );
24 builder.finish()
25 };
26 CompletionItem::new(
27 CompletionKind::Reference,
28 ctx.source_range(),
29 build_import_label(&name, &path),
30 )
31 .text_edit(edit)
32 .add_to(acc)
33 });
34 }
35}
36
37fn build_import_label(name: &str, path: &Vec<SmolStr>) -> String {
38 let mut buf = String::with_capacity(64);
39 buf.push_str(name);
40 buf.push_str(" (");
41 fmt_import_path(path, &mut buf);
42 buf.push_str(")");
43 buf
44}
45
46fn fmt_import_path(path: &Vec<SmolStr>, buf: &mut String) {
47 let mut segments = path.iter();
48 if let Some(s) = segments.next() {
49 buf.push_str(&s);
50 }
51 for s in segments {
52 buf.push_str("::");
53 buf.push_str(&s);
54 }
10} 55}
11 56
12#[cfg(test)] 57#[cfg(test)]
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 359f2cffa..ca8f7900d 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -5,10 +5,10 @@ use ra_syntax::{
5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, 5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset},
6 SyntaxKind::*, 6 SyntaxKind::*,
7}; 7};
8use hir::source_binder;
9 8
10use crate::{db, FilePosition}; 9use hir::{ source_binder, Name };
11 10
11use crate::{db, FilePosition};
12/// `CompletionContext` is created early during completion to figure out, where 12/// `CompletionContext` is created early during completion to figure out, where
13/// exactly is the cursor, syntax-wise. 13/// exactly is the cursor, syntax-wise.
14#[derive(Debug)] 14#[derive(Debug)]
@@ -27,8 +27,10 @@ pub(crate) struct CompletionContext<'a> {
27 pub(super) is_pat_binding: bool, 27 pub(super) is_pat_binding: bool,
28 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 28 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
29 pub(super) is_trivial_path: bool, 29 pub(super) is_trivial_path: bool,
30 /// If not a trivial, path, the prefix (qualifier). 30 /// If not a trivial path, the prefix (qualifier).
31 pub(super) path_prefix: Option<hir::Path>, 31 pub(super) path_prefix: Option<hir::Path>,
32 /// If a trivial path, the ident.
33 pub(super) path_ident: Option<Name>,
32 pub(super) after_if: bool, 34 pub(super) after_if: bool,
33 /// `true` if we are a statement or a last expr in the block. 35 /// `true` if we are a statement or a last expr in the block.
34 pub(super) can_be_stmt: bool, 36 pub(super) can_be_stmt: bool,
@@ -63,6 +65,7 @@ impl<'a> CompletionContext<'a> {
63 is_pat_binding: false, 65 is_pat_binding: false,
64 is_trivial_path: false, 66 is_trivial_path: false,
65 path_prefix: None, 67 path_prefix: None,
68 path_ident: None,
66 after_if: false, 69 after_if: false,
67 can_be_stmt: false, 70 can_be_stmt: false,
68 is_new_item: false, 71 is_new_item: false,
@@ -83,6 +86,18 @@ impl<'a> CompletionContext<'a> {
83 } 86 }
84 87
85 fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { 88 fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) {
89 // We heed the original NameRef before the "intellijRulezz" hack
90 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(original_file.syntax(), offset)
91 {
92 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
93 if let Some(path) = hir::Path::from_ast(path) {
94 if let Some(ident) = path.as_ident() {
95 self.path_ident = Some(ident.clone());
96 }
97 }
98 }
99 }
100
86 // Insert a fake ident to get a valid parse tree. We will use this file 101 // Insert a fake ident to get a valid parse tree. We will use this file
87 // to determine context, though the original_file will be used for 102 // to determine context, though the original_file will be used for
88 // actual completion. 103 // actual completion.
@@ -151,6 +166,7 @@ impl<'a> CompletionContext<'a> {
151 Some(it) => it, 166 Some(it) => it,
152 None => return, 167 None => return,
153 }; 168 };
169
154 if let Some(segment) = ast::PathSegment::cast(parent) { 170 if let Some(segment) = ast::PathSegment::cast(parent) {
155 let path = segment.parent_path(); 171 let path = segment.parent_path();
156 self.is_call = path 172 self.is_call = path
@@ -167,6 +183,7 @@ impl<'a> CompletionContext<'a> {
167 return; 183 return;
168 } 184 }
169 } 185 }
186
170 if path.qualifier().is_none() { 187 if path.qualifier().is_none() {
171 self.is_trivial_path = true; 188 self.is_trivial_path = true;
172 189