diff options
Diffstat (limited to 'crates/ra_analysis/src/completion.rs')
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 343 |
1 files changed, 34 insertions, 309 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index 09894d955..a11e98ac0 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -16,7 +16,7 @@ use hir::source_binder; | |||
16 | use crate::{ | 16 | use crate::{ |
17 | db, | 17 | db, |
18 | Cancelable, FilePosition, | 18 | Cancelable, FilePosition, |
19 | completion::completion_item::Completions, | 19 | completion::completion_item::{Completions, CompletionKind}, |
20 | }; | 20 | }; |
21 | 21 | ||
22 | pub use crate::completion::completion_item::{CompletionItem, InsertText}; | 22 | pub use crate::completion::completion_item::{CompletionItem, InsertText}; |
@@ -81,7 +81,12 @@ fn param_completions(acc: &mut Completions, ctx: SyntaxNodeRef) { | |||
81 | Some((label, lookup)) | 81 | Some((label, lookup)) |
82 | } | 82 | } |
83 | }) | 83 | }) |
84 | .for_each(|(label, lookup)| CompletionItem::new(label).lookup_by(lookup).add_to(acc)); | 84 | .for_each(|(label, lookup)| { |
85 | CompletionItem::new(label) | ||
86 | .lookup_by(lookup) | ||
87 | .kind(CompletionKind::Magic) | ||
88 | .add_to(acc) | ||
89 | }); | ||
85 | 90 | ||
86 | fn process<'a, N: ast::FnDefOwner<'a>>( | 91 | fn process<'a, N: ast::FnDefOwner<'a>>( |
87 | node: N, | 92 | node: N, |
@@ -105,341 +110,61 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { | |||
105 | } | 110 | } |
106 | 111 | ||
107 | #[cfg(test)] | 112 | #[cfg(test)] |
108 | mod tests { | 113 | fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind) { |
109 | use test_utils::assert_eq_dbg; | 114 | use crate::mock_analysis::{single_file_with_position, analysis_and_position}; |
110 | 115 | let (analysis, position) = if code.contains("//-") { | |
111 | use crate::mock_analysis::single_file_with_position; | 116 | analysis_and_position(code) |
117 | } else { | ||
118 | single_file_with_position(code) | ||
119 | }; | ||
120 | let completions = completions(&analysis.imp.db, position).unwrap().unwrap(); | ||
121 | completions.assert_match(expected_completions, kind); | ||
122 | } | ||
112 | 123 | ||
124 | #[cfg(test)] | ||
125 | mod tests { | ||
113 | use super::*; | 126 | use super::*; |
114 | 127 | ||
115 | fn is_snippet(completion_item: &CompletionItem) -> bool { | 128 | fn check_magic_completion(code: &str, expected_completions: &str) { |
116 | match completion_item.insert_text() { | 129 | check_completion(code, expected_completions, CompletionKind::Magic); |
117 | InsertText::Snippet { .. } => true, | ||
118 | _ => false, | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn check_scope_completion(code: &str, expected_completions: &str) { | ||
123 | let (analysis, position) = single_file_with_position(code); | ||
124 | let completions = completions(&analysis.imp.db, position) | ||
125 | .unwrap() | ||
126 | .unwrap() | ||
127 | .into_iter() | ||
128 | .filter(|c| !is_snippet(c)) | ||
129 | .collect::<Vec<_>>(); | ||
130 | assert_eq_dbg(expected_completions, &completions); | ||
131 | } | ||
132 | |||
133 | fn check_snippet_completion(code: &str, expected_completions: &str) { | ||
134 | let (analysis, position) = single_file_with_position(code); | ||
135 | let completions = completions(&analysis.imp.db, position) | ||
136 | .unwrap() | ||
137 | .unwrap() | ||
138 | .into_iter() | ||
139 | .filter(is_snippet) | ||
140 | .collect::<Vec<_>>(); | ||
141 | assert_eq_dbg(expected_completions, &completions); | ||
142 | } | ||
143 | |||
144 | #[test] | ||
145 | fn test_completion_let_scope() { | ||
146 | check_scope_completion( | ||
147 | r" | ||
148 | fn quux(x: i32) { | ||
149 | let y = 92; | ||
150 | 1 + <|>; | ||
151 | let z = (); | ||
152 | } | ||
153 | ", | ||
154 | r#"[CompletionItem { label: "y", lookup: None, snippet: None }, | ||
155 | CompletionItem { label: "x", lookup: None, snippet: None }, | ||
156 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | ||
157 | ); | ||
158 | } | ||
159 | |||
160 | #[test] | ||
161 | fn test_completion_if_let_scope() { | ||
162 | check_scope_completion( | ||
163 | r" | ||
164 | fn quux() { | ||
165 | if let Some(x) = foo() { | ||
166 | let y = 92; | ||
167 | }; | ||
168 | if let Some(a) = bar() { | ||
169 | let b = 62; | ||
170 | 1 + <|> | ||
171 | } | ||
172 | } | ||
173 | ", | ||
174 | r#"[CompletionItem { label: "b", lookup: None, snippet: None }, | ||
175 | CompletionItem { label: "a", lookup: None, snippet: None }, | ||
176 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | ||
177 | ); | ||
178 | } | ||
179 | |||
180 | #[test] | ||
181 | fn test_completion_for_scope() { | ||
182 | check_scope_completion( | ||
183 | r" | ||
184 | fn quux() { | ||
185 | for x in &[1, 2, 3] { | ||
186 | <|> | ||
187 | } | ||
188 | } | ||
189 | ", | ||
190 | r#"[CompletionItem { label: "x", lookup: None, snippet: None }, | ||
191 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn test_completion_mod_scope() { | ||
197 | check_scope_completion( | ||
198 | r" | ||
199 | struct Foo; | ||
200 | enum Baz {} | ||
201 | fn quux() { | ||
202 | <|> | ||
203 | } | ||
204 | ", | ||
205 | r#"[CompletionItem { label: "quux", lookup: None, snippet: None }, | ||
206 | CompletionItem { label: "Foo", lookup: None, snippet: None }, | ||
207 | CompletionItem { label: "Baz", lookup: None, snippet: None }]"#, | ||
208 | ); | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn test_completion_mod_scope_no_self_use() { | ||
213 | check_scope_completion( | ||
214 | r" | ||
215 | use foo<|>; | ||
216 | ", | ||
217 | r#"[]"#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_completion_self_path() { | ||
223 | check_scope_completion( | ||
224 | r" | ||
225 | use self::m::<|>; | ||
226 | |||
227 | mod m { | ||
228 | struct Bar; | ||
229 | } | ||
230 | ", | ||
231 | r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }]"#, | ||
232 | ); | ||
233 | } | ||
234 | |||
235 | #[test] | ||
236 | fn test_completion_mod_scope_nested() { | ||
237 | check_scope_completion( | ||
238 | r" | ||
239 | struct Foo; | ||
240 | mod m { | ||
241 | struct Bar; | ||
242 | fn quux() { <|> } | ||
243 | } | ||
244 | ", | ||
245 | r#"[CompletionItem { label: "quux", lookup: None, snippet: None }, | ||
246 | CompletionItem { label: "Bar", lookup: None, snippet: None }]"#, | ||
247 | ); | ||
248 | } | ||
249 | |||
250 | #[test] | ||
251 | fn test_complete_type() { | ||
252 | check_scope_completion( | ||
253 | r" | ||
254 | struct Foo; | ||
255 | fn x() -> <|> | ||
256 | ", | ||
257 | r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, | ||
258 | CompletionItem { label: "x", lookup: None, snippet: None }]"#, | ||
259 | ) | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn test_complete_shadowing() { | ||
264 | check_scope_completion( | ||
265 | r" | ||
266 | fn foo() -> { | ||
267 | let bar = 92; | ||
268 | { | ||
269 | let bar = 62; | ||
270 | <|> | ||
271 | } | ||
272 | } | ||
273 | ", | ||
274 | r#"[CompletionItem { label: "bar", lookup: None, snippet: None }, | ||
275 | CompletionItem { label: "foo", lookup: None, snippet: None }]"#, | ||
276 | ) | ||
277 | } | ||
278 | |||
279 | #[test] | ||
280 | fn test_complete_self() { | ||
281 | check_scope_completion( | ||
282 | r" | ||
283 | impl S { fn foo(&self) { <|> } } | ||
284 | ", | ||
285 | r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#, | ||
286 | ) | ||
287 | } | ||
288 | |||
289 | #[test] | ||
290 | fn test_completion_kewords() { | ||
291 | check_snippet_completion(r" | ||
292 | fn quux() { | ||
293 | <|> | ||
294 | } | ||
295 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
296 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
297 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
298 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
299 | CompletionItem { label: "return", lookup: None, snippet: Some("return") }, | ||
300 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
301 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_completion_else() { | ||
306 | check_snippet_completion(r" | ||
307 | fn quux() { | ||
308 | if true { | ||
309 | () | ||
310 | } <|> | ||
311 | } | ||
312 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
313 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
314 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
315 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
316 | CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") }, | ||
317 | CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") }, | ||
318 | CompletionItem { label: "return", lookup: None, snippet: Some("return") }, | ||
319 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
320 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
321 | } | ||
322 | |||
323 | #[test] | ||
324 | fn test_completion_return_value() { | ||
325 | check_snippet_completion(r" | ||
326 | fn quux() -> i32 { | ||
327 | <|> | ||
328 | 92 | ||
329 | } | ||
330 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
331 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
332 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
333 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
334 | CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") }, | ||
335 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
336 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
337 | check_snippet_completion(r" | ||
338 | fn quux() { | ||
339 | <|> | ||
340 | 92 | ||
341 | } | ||
342 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
343 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
344 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
345 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
346 | CompletionItem { label: "return", lookup: None, snippet: Some("return;") }, | ||
347 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
348 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn test_completion_return_no_stmt() { | ||
353 | check_snippet_completion(r" | ||
354 | fn quux() -> i32 { | ||
355 | match () { | ||
356 | () => <|> | ||
357 | } | ||
358 | } | ||
359 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
360 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
361 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
362 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
363 | CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, | ||
364 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
365 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn test_continue_break_completion() { | ||
370 | check_snippet_completion(r" | ||
371 | fn quux() -> i32 { | ||
372 | loop { <|> } | ||
373 | } | ||
374 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
375 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
376 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
377 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
378 | CompletionItem { label: "continue", lookup: None, snippet: Some("continue") }, | ||
379 | CompletionItem { label: "break", lookup: None, snippet: Some("break") }, | ||
380 | CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, | ||
381 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
382 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
383 | check_snippet_completion(r" | ||
384 | fn quux() -> i32 { | ||
385 | loop { || { <|> } } | ||
386 | } | ||
387 | ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, | ||
388 | CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, | ||
389 | CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, | ||
390 | CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, | ||
391 | CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, | ||
392 | CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, | ||
393 | CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); | ||
394 | } | 130 | } |
395 | 131 | ||
396 | #[test] | 132 | #[test] |
397 | fn test_param_completion_last_param() { | 133 | fn test_param_completion_last_param() { |
398 | check_scope_completion(r" | 134 | check_magic_completion( |
135 | r" | ||
399 | fn foo(file_id: FileId) {} | 136 | fn foo(file_id: FileId) {} |
400 | fn bar(file_id: FileId) {} | 137 | fn bar(file_id: FileId) {} |
401 | fn baz(file<|>) {} | 138 | fn baz(file<|>) {} |
402 | ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); | 139 | ", |
140 | r#"file_id "file_id: FileId""#, | ||
141 | ); | ||
403 | } | 142 | } |
404 | 143 | ||
405 | #[test] | 144 | #[test] |
406 | fn test_param_completion_nth_param() { | 145 | fn test_param_completion_nth_param() { |
407 | check_scope_completion(r" | 146 | check_magic_completion( |
147 | r" | ||
408 | fn foo(file_id: FileId) {} | 148 | fn foo(file_id: FileId) {} |
409 | fn bar(file_id: FileId) {} | 149 | fn bar(file_id: FileId) {} |
410 | fn baz(file<|>, x: i32) {} | 150 | fn baz(file<|>, x: i32) {} |
411 | ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); | 151 | ", |
152 | r#"file_id "file_id: FileId""#, | ||
153 | ); | ||
412 | } | 154 | } |
413 | 155 | ||
414 | #[test] | 156 | #[test] |
415 | fn test_param_completion_trait_param() { | 157 | fn test_param_completion_trait_param() { |
416 | check_scope_completion(r" | 158 | check_magic_completion( |
159 | r" | ||
417 | pub(crate) trait SourceRoot { | 160 | pub(crate) trait SourceRoot { |
418 | pub fn contains(&self, file_id: FileId) -> bool; | 161 | pub fn contains(&self, file_id: FileId) -> bool; |
419 | pub fn module_map(&self) -> &ModuleMap; | 162 | pub fn module_map(&self) -> &ModuleMap; |
420 | pub fn lines(&self, file_id: FileId) -> &LineIndex; | 163 | pub fn lines(&self, file_id: FileId) -> &LineIndex; |
421 | pub fn syntax(&self, file<|>) | 164 | pub fn syntax(&self, file<|>) |
422 | } | 165 | } |
423 | ", r#"[CompletionItem { label: "self", lookup: None, snippet: None }, | ||
424 | CompletionItem { label: "SourceRoot", lookup: None, snippet: None }, | ||
425 | CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); | ||
426 | } | ||
427 | |||
428 | #[test] | ||
429 | fn test_item_snippets() { | ||
430 | // check_snippet_completion(r" | ||
431 | // <|> | ||
432 | // ", | ||
433 | // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##, | ||
434 | // ); | ||
435 | check_snippet_completion(r" | ||
436 | #[cfg(test)] | ||
437 | mod tests { | ||
438 | <|> | ||
439 | } | ||
440 | ", | 166 | ", |
441 | r##"[CompletionItem { label: "Test function", lookup: Some("tfn"), snippet: Some("#[test]\nfn ${1:feature}() {\n $0\n}") }, | 167 | r#"file_id "file_id: FileId""#, |
442 | CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##, | ||
443 | ); | 168 | ); |
444 | } | 169 | } |
445 | } | 170 | } |