aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-12-21 21:52:02 +0000
committerAleksey Kladov <[email protected]>2018-12-21 21:52:02 +0000
commitccca5aae43a27c94180bb099bcc09bb6c29c2ea3 (patch)
tree3d6ce58a0e87cb89ef4ca2df29e5f25bac86d0b8 /crates/ra_analysis/src
parent2136e75c0bce090d104bb5b5006e48e42fb22a0a (diff)
scope-based copmletions on original file
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r--crates/ra_analysis/src/completion.rs18
-rw-r--r--crates/ra_analysis/src/completion/complete_path.rs2
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs171
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs307
4 files changed, 177 insertions, 321 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index d91304bc2..93edcc4c2 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -1,10 +1,10 @@
1mod completion_item; 1mod completion_item;
2mod reference_completion;
3 2
4mod complete_fn_param; 3mod complete_fn_param;
5mod complete_keyword; 4mod complete_keyword;
6mod complete_snippet; 5mod complete_snippet;
7mod complete_path; 6mod complete_path;
7mod complete_scope;
8 8
9use ra_editor::find_node_at_offset; 9use ra_editor::find_node_at_offset;
10use ra_text_edit::AtomTextEdit; 10use ra_text_edit::AtomTextEdit;
@@ -33,26 +33,16 @@ pub(crate) fn completions(
33 position: FilePosition, 33 position: FilePosition,
34) -> Cancelable<Option<Completions>> { 34) -> Cancelable<Option<Completions>> {
35 let original_file = db.source_file(position.file_id); 35 let original_file = db.source_file(position.file_id);
36 // Insert a fake ident to get a valid parse tree 36 let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?);
37 let file = {
38 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
39 original_file.reparse(&edit)
40 };
41 let module = ctry!(source_binder::module_from_position(db, position)?);
42 37
43 let mut acc = Completions::default(); 38 let mut acc = Completions::default();
44 39
45 // First, let's try to complete a reference to some declaration.
46 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
47 reference_completion::completions(&mut acc, db, &module, &file, name_ref)?;
48 }
49
50 let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?);
51 complete_fn_param::complete_fn_param(&mut acc, &ctx); 40 complete_fn_param::complete_fn_param(&mut acc, &ctx);
52 complete_keyword::complete_expr_keyword(&mut acc, &ctx); 41 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
53 complete_snippet::complete_expr_snippet(&mut acc, &ctx); 42 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
54 complete_snippet::complete_item_snippet(&mut acc, &ctx); 43 complete_snippet::complete_item_snippet(&mut acc, &ctx);
55 complete_path::complete_path(&mut acc, &ctx)?; 44 complete_path::complete_path(&mut acc, &ctx)?;
45 complete_scope::complete_scope(&mut acc, &ctx)?;
56 46
57 Ok(Some(acc)) 47 Ok(Some(acc))
58} 48}
@@ -62,6 +52,7 @@ pub(crate) fn completions(
62#[derive(Debug)] 52#[derive(Debug)]
63pub(super) struct SyntaxContext<'a> { 53pub(super) struct SyntaxContext<'a> {
64 db: &'a db::RootDatabase, 54 db: &'a db::RootDatabase,
55 offset: TextUnit,
65 leaf: SyntaxNodeRef<'a>, 56 leaf: SyntaxNodeRef<'a>,
66 module: Option<hir::Module>, 57 module: Option<hir::Module>,
67 enclosing_fn: Option<ast::FnDef<'a>>, 58 enclosing_fn: Option<ast::FnDef<'a>>,
@@ -88,6 +79,7 @@ impl<'a> SyntaxContext<'a> {
88 let mut ctx = SyntaxContext { 79 let mut ctx = SyntaxContext {
89 db, 80 db,
90 leaf, 81 leaf,
82 offset: position.offset,
91 module, 83 module,
92 enclosing_fn: None, 84 enclosing_fn: None,
93 is_param: false, 85 is_param: false,
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs
index d04503e46..8374ec346 100644
--- a/crates/ra_analysis/src/completion/complete_path.rs
+++ b/crates/ra_analysis/src/completion/complete_path.rs
@@ -9,8 +9,8 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &SyntaxContext) -> Cance
9 _ => return Ok(()), 9 _ => return Ok(()),
10 }; 10 };
11 let def_id = match module.resolve_path(ctx.db, path)? { 11 let def_id = match module.resolve_path(ctx.db, path)? {
12 None => return Ok(()),
13 Some(it) => it, 12 Some(it) => it,
13 None => return Ok(()),
14 }; 14 };
15 let target_module = match def_id.resolve(ctx.db)? { 15 let target_module = match def_id.resolve(ctx.db)? {
16 hir::Def::Module(it) => it, 16 hir::Def::Module(it) => it,
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
new file mode 100644
index 000000000..4ffd63016
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -0,0 +1,171 @@
1use rustc_hash::FxHashSet;
2use ra_syntax::TextUnit;
3
4use crate::{
5 completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext},
6 Cancelable
7};
8
9pub(super) fn complete_scope(acc: &mut Completions, ctx: &SyntaxContext) -> Cancelable<()> {
10 if !ctx.is_trivial_path {
11 return Ok(());
12 }
13 if let Some(fn_def) = ctx.enclosing_fn {
14 let scopes = hir::FnScopes::new(fn_def);
15 complete_fn(acc, &scopes, ctx.offset);
16 }
17
18 if let Some(module) = &ctx.module {
19 let module_scope = module.scope(ctx.db)?;
20 module_scope
21 .entries()
22 .filter(|(_name, res)| {
23 // Don't expose this item
24 match res.import {
25 None => true,
26 Some(import) => {
27 let range = import.range(ctx.db, module.source().file_id());
28 !range.is_subrange(&ctx.leaf.range())
29 }
30 }
31 })
32 .for_each(|(name, _res)| {
33 CompletionItem::new(name.to_string())
34 .kind(Reference)
35 .add_to(acc)
36 });
37 }
38
39 Ok(())
40}
41
42fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) {
43 let mut shadowed = FxHashSet::default();
44 scopes
45 .scope_chain_for_offset(offset)
46 .flat_map(|scope| scopes.entries(scope).iter())
47 .filter(|entry| shadowed.insert(entry.name()))
48 .for_each(|entry| {
49 CompletionItem::new(entry.name().to_string())
50 .kind(Reference)
51 .add_to(acc)
52 });
53 if scopes.self_param.is_some() {
54 CompletionItem::new("self").kind(Reference).add_to(acc);
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use crate::completion::{CompletionKind, check_completion};
61
62 fn check_reference_completion(code: &str, expected_completions: &str) {
63 check_completion(code, expected_completions, CompletionKind::Reference);
64 }
65
66 #[test]
67 fn test_completion_let_scope() {
68 check_reference_completion(
69 r"
70 fn quux(x: i32) {
71 let y = 92;
72 1 + <|>;
73 let z = ();
74 }
75 ",
76 "y;x;quux",
77 );
78 }
79
80 #[test]
81 fn test_completion_if_let_scope() {
82 check_reference_completion(
83 r"
84 fn quux() {
85 if let Some(x) = foo() {
86 let y = 92;
87 };
88 if let Some(a) = bar() {
89 let b = 62;
90 1 + <|>
91 }
92 }
93 ",
94 "b;a;quux",
95 );
96 }
97
98 #[test]
99 fn test_completion_for_scope() {
100 check_reference_completion(
101 r"
102 fn quux() {
103 for x in &[1, 2, 3] {
104 <|>
105 }
106 }
107 ",
108 "x;quux",
109 );
110 }
111
112 #[test]
113 fn test_completion_mod_scope() {
114 check_reference_completion(
115 r"
116 struct Foo;
117 enum Baz {}
118 fn quux() {
119 <|>
120 }
121 ",
122 "quux;Foo;Baz",
123 );
124 }
125
126 #[test]
127 fn test_completion_mod_scope_nested() {
128 check_reference_completion(
129 r"
130 struct Foo;
131 mod m {
132 struct Bar;
133 fn quux() { <|> }
134 }
135 ",
136 "quux;Bar",
137 );
138 }
139
140 #[test]
141 fn test_complete_type() {
142 check_reference_completion(
143 r"
144 struct Foo;
145 fn x() -> <|>
146 ",
147 "Foo;x",
148 )
149 }
150
151 #[test]
152 fn test_complete_shadowing() {
153 check_reference_completion(
154 r"
155 fn foo() -> {
156 let bar = 92;
157 {
158 let bar = 62;
159 <|>
160 }
161 }
162 ",
163 "bar;foo",
164 )
165 }
166
167 #[test]
168 fn test_complete_self() {
169 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
170 }
171}
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
deleted file mode 100644
index 459ed8f6f..000000000
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ /dev/null
@@ -1,307 +0,0 @@
1use rustc_hash::FxHashSet;
2use ra_syntax::{
3 SourceFileNode, AstNode,
4 ast,
5 SyntaxKind::*,
6};
7use hir::{
8 self,
9 FnScopes, Path
10};
11
12use crate::{
13 db::RootDatabase,
14 completion::{CompletionItem, Completions, CompletionKind::*},
15 Cancelable
16};
17
18pub(super) fn completions(
19 acc: &mut Completions,
20 db: &RootDatabase,
21 module: &hir::Module,
22 _file: &SourceFileNode,
23 name_ref: ast::NameRef,
24) -> Cancelable<()> {
25 let kind = match classify_name_ref(name_ref) {
26 Some(it) => it,
27 None => return Ok(()),
28 };
29
30 match kind {
31 NameRefKind::LocalRef { enclosing_fn } => {
32 if let Some(fn_def) = enclosing_fn {
33 let scopes = FnScopes::new(fn_def);
34 complete_fn(name_ref, &scopes, acc);
35 }
36
37 let module_scope = module.scope(db)?;
38 module_scope
39 .entries()
40 .filter(|(_name, res)| {
41 // Don't expose this item
42 match res.import {
43 None => true,
44 Some(import) => {
45 let range = import.range(db, module.source().file_id());
46 !range.is_subrange(&name_ref.syntax().range())
47 }
48 }
49 })
50 .for_each(|(name, _res)| {
51 CompletionItem::new(name.to_string())
52 .kind(Reference)
53 .add_to(acc)
54 });
55 }
56 NameRefKind::Path(_) => (),
57 NameRefKind::BareIdentInMod => (),
58 }
59 Ok(())
60}
61
62enum NameRefKind<'a> {
63 /// NameRef is a part of single-segment path, for example, a refernece to a
64 /// local variable.
65 LocalRef {
66 enclosing_fn: Option<ast::FnDef<'a>>,
67 },
68 /// NameRef is the last segment in some path
69 Path(Path),
70 /// NameRef is bare identifier at the module's root.
71 /// Used for keyword completion
72 BareIdentInMod,
73}
74
75fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
76 let name_range = name_ref.syntax().range();
77 let top_node = name_ref
78 .syntax()
79 .ancestors()
80 .take_while(|it| it.range() == name_range)
81 .last()
82 .unwrap();
83 match top_node.parent().map(|it| it.kind()) {
84 Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
85 _ => (),
86 }
87
88 let parent = name_ref.syntax().parent()?;
89 if let Some(segment) = ast::PathSegment::cast(parent) {
90 let path = segment.parent_path();
91 if let Some(path) = Path::from_ast(path) {
92 if !path.is_ident() {
93 return Some(NameRefKind::Path(path));
94 }
95 }
96 if path.qualifier().is_none() {
97 let enclosing_fn = name_ref
98 .syntax()
99 .ancestors()
100 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
101 .find_map(ast::FnDef::cast);
102 return Some(NameRefKind::LocalRef { enclosing_fn });
103 }
104 }
105 None
106}
107
108fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) {
109 let mut shadowed = FxHashSet::default();
110 scopes
111 .scope_chain(name_ref.syntax())
112 .flat_map(|scope| scopes.entries(scope).iter())
113 .filter(|entry| shadowed.insert(entry.name()))
114 .for_each(|entry| {
115 CompletionItem::new(entry.name().to_string())
116 .kind(Reference)
117 .add_to(acc)
118 });
119 if scopes.self_param.is_some() {
120 CompletionItem::new("self").kind(Reference).add_to(acc);
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use crate::completion::{CompletionKind, check_completion};
127
128 fn check_reference_completion(code: &str, expected_completions: &str) {
129 check_completion(code, expected_completions, CompletionKind::Reference);
130 }
131
132 #[test]
133 fn test_completion_let_scope() {
134 check_reference_completion(
135 r"
136 fn quux(x: i32) {
137 let y = 92;
138 1 + <|>;
139 let z = ();
140 }
141 ",
142 "y;x;quux",
143 );
144 }
145
146 #[test]
147 fn test_completion_if_let_scope() {
148 check_reference_completion(
149 r"
150 fn quux() {
151 if let Some(x) = foo() {
152 let y = 92;
153 };
154 if let Some(a) = bar() {
155 let b = 62;
156 1 + <|>
157 }
158 }
159 ",
160 "b;a;quux",
161 );
162 }
163
164 #[test]
165 fn test_completion_for_scope() {
166 check_reference_completion(
167 r"
168 fn quux() {
169 for x in &[1, 2, 3] {
170 <|>
171 }
172 }
173 ",
174 "x;quux",
175 );
176 }
177
178 #[test]
179 fn test_completion_mod_scope() {
180 check_reference_completion(
181 r"
182 struct Foo;
183 enum Baz {}
184 fn quux() {
185 <|>
186 }
187 ",
188 "quux;Foo;Baz",
189 );
190 }
191
192 #[test]
193 fn test_completion_mod_scope_no_self_use() {
194 check_reference_completion(
195 r"
196 use foo<|>;
197 ",
198 "",
199 );
200 }
201
202 #[test]
203 fn test_completion_self_path() {
204 check_reference_completion(
205 r"
206 use self::m::<|>;
207
208 mod m {
209 struct Bar;
210 }
211 ",
212 "Bar",
213 );
214 }
215
216 #[test]
217 fn test_completion_mod_scope_nested() {
218 check_reference_completion(
219 r"
220 struct Foo;
221 mod m {
222 struct Bar;
223 fn quux() { <|> }
224 }
225 ",
226 "quux;Bar",
227 );
228 }
229
230 #[test]
231 fn test_complete_type() {
232 check_reference_completion(
233 r"
234 struct Foo;
235 fn x() -> <|>
236 ",
237 "Foo;x",
238 )
239 }
240
241 #[test]
242 fn test_complete_shadowing() {
243 check_reference_completion(
244 r"
245 fn foo() -> {
246 let bar = 92;
247 {
248 let bar = 62;
249 <|>
250 }
251 }
252 ",
253 "bar;foo",
254 )
255 }
256
257 #[test]
258 fn test_complete_self() {
259 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
260 }
261
262 #[test]
263 fn test_complete_crate_path() {
264 check_reference_completion(
265 "
266 //- /lib.rs
267 mod foo;
268 struct Spam;
269 //- /foo.rs
270 use crate::Sp<|>
271 ",
272 "Spam;foo",
273 );
274 }
275
276 #[test]
277 fn test_complete_crate_path_with_braces() {
278 check_reference_completion(
279 "
280 //- /lib.rs
281 mod foo;
282 struct Spam;
283 //- /foo.rs
284 use crate::{Sp<|>};
285 ",
286 "Spam;foo",
287 );
288 }
289
290 #[test]
291 fn test_complete_crate_path_in_nested_tree() {
292 check_reference_completion(
293 "
294 //- /lib.rs
295 mod foo;
296 pub mod bar {
297 pub mod baz {
298 pub struct Spam;
299 }
300 }
301 //- /foo.rs
302 use crate::{bar::{baz::Sp<|>}};
303 ",
304 "Spam",
305 );
306 }
307}