diff options
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 65 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 19 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_scope.rs | 55 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_context.rs | 23 |
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; | |||
98 | mod replace_if_let_with_match; | 98 | mod replace_if_let_with_match; |
99 | mod split_import; | 99 | mod split_import; |
100 | mod remove_dbg; | 100 | mod remove_dbg; |
101 | mod auto_import; | 101 | pub mod auto_import; |
102 | mod add_missing_impl_members; | 102 | mod add_missing_impl_members; |
103 | 103 | ||
104 | fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { | 104 | fn 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 | ||
58 | pub use self::{ | 58 | pub 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 | ||
4 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
5 | 5 | ||
6 | use ra_syntax::SmolStr; | ||
7 | |||
6 | use crate::{ | 8 | use 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)] | ||
31 | pub(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)] |
26 | pub(crate) struct ModuleItemMap { | 38 | pub(crate) struct ModuleItemMap { |
@@ -309,3 +321,56 @@ impl Scope { | |||
309 | } | 321 | } |
310 | } | 322 | } |
311 | } | 323 | } |
324 | |||
325 | impl 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 | ||
19 | use crate::{ | 20 | use 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)] |
171 | pub struct SourceAnalyzer { | 176 | pub 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 @@ | |||
1 | use crate::completion::{Completions, CompletionContext}; | 1 | use ra_text_edit::TextEditBuilder; |
2 | use ra_syntax::SmolStr; | ||
3 | use ra_assists::auto_import; | ||
4 | use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; | ||
2 | 5 | ||
3 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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 | |||
37 | fn 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 | |||
46 | fn 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 | }; |
8 | use hir::source_binder; | ||
9 | 8 | ||
10 | use crate::{db, FilePosition}; | 9 | use hir::{ source_binder, Name }; |
11 | 10 | ||
11 | use 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 | ||