aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/completion')
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs120
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs4
2 files changed, 118 insertions, 6 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index fd256fc3b..2473e58b4 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -1,12 +1,122 @@
1use crate::completion::{Completions, CompletionContext}; 1use rustc_hash::FxHashMap;
2use ra_text_edit::TextEditBuilder;
3use ra_syntax::{SmolStr, ast, AstNode};
4use ra_assists::auto_import;
5
6use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
2 7
3pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 8pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
4 if !ctx.is_trivial_path { 9 if ctx.is_trivial_path {
5 return; 10 let names = ctx.analyzer.all_names(ctx.db);
11 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
12
13 // auto-import
14 // We fetch ident from the original file, because we need to pre-filter auto-imports
15 if ast::NameRef::cast(ctx.token.parent()).is_some() {
16 let import_resolver = ImportResolver::new();
17 let import_names = import_resolver.all_names(ctx.token.text());
18 import_names.into_iter().for_each(|(name, path)| {
19 let edit = {
20 let mut builder = TextEditBuilder::default();
21 builder.replace(ctx.source_range(), name.to_string());
22 auto_import::auto_import_text_edit(
23 ctx.token.parent(),
24 ctx.token.parent(),
25 &path,
26 &mut builder,
27 );
28 builder.finish()
29 };
30
31 // Hack: copied this check form conv.rs beacause auto import can produce edits
32 // that invalidate assert in conv_with.
33 if edit
34 .as_atoms()
35 .iter()
36 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
37 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
38 {
39 CompletionItem::new(
40 CompletionKind::Reference,
41 ctx.source_range(),
42 build_import_label(&name, &path),
43 )
44 .text_edit(edit)
45 .add_to(acc);
46 }
47 });
48 }
6 } 49 }
7 let names = ctx.analyzer.all_names(ctx.db); 50}
8 51
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); 52fn build_import_label(name: &str, path: &Vec<SmolStr>) -> String {
53 let mut buf = String::with_capacity(64);
54 buf.push_str(name);
55 buf.push_str(" (");
56 fmt_import_path(path, &mut buf);
57 buf.push_str(")");
58 buf
59}
60
61fn fmt_import_path(path: &Vec<SmolStr>, buf: &mut String) {
62 let mut segments = path.iter();
63 if let Some(s) = segments.next() {
64 buf.push_str(&s);
65 }
66 for s in segments {
67 buf.push_str("::");
68 buf.push_str(&s);
69 }
70}
71
72#[derive(Debug, Clone, Default)]
73pub(crate) struct ImportResolver {
74 // todo: use fst crate or something like that
75 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
76}
77
78impl ImportResolver {
79 pub(crate) fn new() -> Self {
80 let dummy_names = vec![
81 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
82 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
83 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
84 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
85 (
86 SmolStr::new("Debug"),
87 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
88 ),
89 (
90 SmolStr::new("Display"),
91 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
92 ),
93 (
94 SmolStr::new("Hash"),
95 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
96 ),
97 (
98 SmolStr::new("Hasher"),
99 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
100 ),
101 (
102 SmolStr::new("Iterator"),
103 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
104 ),
105 ];
106
107 ImportResolver { dummy_names }
108 }
109
110 // Returns a map of importable items filtered by name.
111 // The map associates item name with its full path.
112 // todo: should return Resolutions
113 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
114 if name.len() > 1 {
115 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
116 } else {
117 FxHashMap::default()
118 }
119 }
10} 120}
11 121
12#[cfg(test)] 122#[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..a8c8cc7b0 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -27,7 +27,7 @@ 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 pub(super) after_if: bool, 32 pub(super) after_if: bool,
33 /// `true` if we are a statement or a last expr in the block. 33 /// `true` if we are a statement or a last expr in the block.
@@ -151,6 +151,7 @@ impl<'a> CompletionContext<'a> {
151 Some(it) => it, 151 Some(it) => it,
152 None => return, 152 None => return,
153 }; 153 };
154
154 if let Some(segment) = ast::PathSegment::cast(parent) { 155 if let Some(segment) = ast::PathSegment::cast(parent) {
155 let path = segment.parent_path(); 156 let path = segment.parent_path();
156 self.is_call = path 157 self.is_call = path
@@ -167,6 +168,7 @@ impl<'a> CompletionContext<'a> {
167 return; 168 return;
168 } 169 }
169 } 170 }
171
170 if path.qualifier().is_none() { 172 if path.qualifier().is_none() {
171 self.is_trivial_path = true; 173 self.is_trivial_path = true;
172 174