diff options
Diffstat (limited to 'crates/ra_analysis/src/completion/reference_completion.rs')
-rw-r--r-- | crates/ra_analysis/src/completion/reference_completion.rs | 512 |
1 files changed, 420 insertions, 92 deletions
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index f483ed045..c2a650b6d 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs | |||
@@ -6,21 +6,19 @@ use ra_syntax::{ | |||
6 | ast::{self, LoopBodyOwner}, | 6 | ast::{self, LoopBodyOwner}, |
7 | SyntaxKind::*, | 7 | SyntaxKind::*, |
8 | }; | 8 | }; |
9 | use hir::{ | 9 | use hir::{ |
10 | self, | 10 | self, |
11 | FnScopes, | 11 | FnScopes, Def, Path |
12 | Def, | ||
13 | Path, | ||
14 | }; | 12 | }; |
15 | 13 | ||
16 | use crate::{ | 14 | use crate::{ |
17 | db::RootDatabase, | 15 | db::RootDatabase, |
18 | completion::CompletionItem, | 16 | completion::{CompletionItem, Completions, CompletionKind::*}, |
19 | Cancelable | 17 | Cancelable |
20 | }; | 18 | }; |
21 | 19 | ||
22 | pub(super) fn completions( | 20 | pub(super) fn completions( |
23 | acc: &mut Vec<CompletionItem>, | 21 | acc: &mut Completions, |
24 | db: &RootDatabase, | 22 | db: &RootDatabase, |
25 | module: &hir::Module, | 23 | module: &hir::Module, |
26 | file: &SourceFileNode, | 24 | file: &SourceFileNode, |
@@ -41,25 +39,23 @@ pub(super) fn completions( | |||
41 | } | 39 | } |
42 | 40 | ||
43 | let module_scope = module.scope(db)?; | 41 | let module_scope = module.scope(db)?; |
44 | acc.extend( | 42 | module_scope |
45 | module_scope | 43 | .entries() |
46 | .entries() | 44 | .filter(|(_name, res)| { |
47 | .filter(|(_name, res)| { | 45 | // Don't expose this item |
48 | // Don't expose this item | 46 | match res.import { |
49 | match res.import { | 47 | None => true, |
50 | None => true, | 48 | Some(import) => { |
51 | Some(import) => { | 49 | let range = import.range(db, module.source().file_id()); |
52 | let range = import.range(db, module.source().file_id()); | 50 | !range.is_subrange(&name_ref.syntax().range()) |
53 | !range.is_subrange(&name_ref.syntax().range()) | ||
54 | } | ||
55 | } | 51 | } |
56 | }) | 52 | } |
57 | .map(|(name, _res)| CompletionItem { | 53 | }) |
58 | label: name.to_string(), | 54 | .for_each(|(name, _res)| { |
59 | lookup: None, | 55 | CompletionItem::new(name.to_string()) |
60 | snippet: None, | 56 | .kind(Reference) |
61 | }), | 57 | .add_to(acc) |
62 | ); | 58 | }); |
63 | } | 59 | } |
64 | NameRefKind::Path(path) => complete_path(acc, db, module, path)?, | 60 | NameRefKind::Path(path) => complete_path(acc, db, module, path)?, |
65 | NameRefKind::BareIdentInMod => { | 61 | NameRefKind::BareIdentInMod => { |
@@ -125,30 +121,24 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> { | |||
125 | None | 121 | None |
126 | } | 122 | } |
127 | 123 | ||
128 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { | 124 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) { |
129 | let mut shadowed = FxHashSet::default(); | 125 | let mut shadowed = FxHashSet::default(); |
130 | acc.extend( | 126 | scopes |
131 | scopes | 127 | .scope_chain(name_ref.syntax()) |
132 | .scope_chain(name_ref.syntax()) | 128 | .flat_map(|scope| scopes.entries(scope).iter()) |
133 | .flat_map(|scope| scopes.entries(scope).iter()) | 129 | .filter(|entry| shadowed.insert(entry.name())) |
134 | .filter(|entry| shadowed.insert(entry.name())) | 130 | .for_each(|entry| { |
135 | .map(|entry| CompletionItem { | 131 | CompletionItem::new(entry.name().to_string()) |
136 | label: entry.name().to_string(), | 132 | .kind(Reference) |
137 | lookup: None, | 133 | .add_to(acc) |
138 | snippet: None, | 134 | }); |
139 | }), | ||
140 | ); | ||
141 | if scopes.self_param.is_some() { | 135 | if scopes.self_param.is_some() { |
142 | acc.push(CompletionItem { | 136 | CompletionItem::new("self").kind(Reference).add_to(acc); |
143 | label: "self".to_string(), | ||
144 | lookup: None, | ||
145 | snippet: None, | ||
146 | }) | ||
147 | } | 137 | } |
148 | } | 138 | } |
149 | 139 | ||
150 | fn complete_path( | 140 | fn complete_path( |
151 | acc: &mut Vec<CompletionItem>, | 141 | acc: &mut Completions, |
152 | db: &RootDatabase, | 142 | db: &RootDatabase, |
153 | module: &hir::Module, | 143 | module: &hir::Module, |
154 | mut path: Path, | 144 | mut path: Path, |
@@ -166,58 +156,56 @@ fn complete_path( | |||
166 | _ => return Ok(()), | 156 | _ => return Ok(()), |
167 | }; | 157 | }; |
168 | let module_scope = target_module.scope(db)?; | 158 | let module_scope = target_module.scope(db)?; |
169 | let completions = module_scope.entries().map(|(name, _res)| CompletionItem { | 159 | module_scope.entries().for_each(|(name, _res)| { |
170 | label: name.to_string(), | 160 | CompletionItem::new(name.to_string()) |
171 | lookup: None, | 161 | .kind(Reference) |
172 | snippet: None, | 162 | .add_to(acc) |
173 | }); | 163 | }); |
174 | acc.extend(completions); | ||
175 | Ok(()) | 164 | Ok(()) |
176 | } | 165 | } |
177 | 166 | ||
178 | fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { | 167 | fn complete_mod_item_snippets(acc: &mut Completions) { |
179 | acc.push(CompletionItem { | 168 | CompletionItem::new("Test function") |
180 | label: "Test function".to_string(), | 169 | .lookup_by("tfn") |
181 | lookup: Some("tfn".to_string()), | 170 | .snippet( |
182 | snippet: Some( | 171 | "\ |
183 | "#[test]\n\ | 172 | #[test] |
184 | fn ${1:feature}() {\n\ | 173 | fn ${1:feature}() { |
185 | $0\n\ | 174 | $0 |
186 | }" | 175 | }", |
187 | .to_string(), | 176 | ) |
188 | ), | 177 | .kind(Snippet) |
189 | }); | 178 | .add_to(acc); |
190 | acc.push(CompletionItem { | 179 | CompletionItem::new("pub(crate)") |
191 | label: "pub(crate)".to_string(), | 180 | .snippet("pub(crate) $0") |
192 | lookup: None, | 181 | .kind(Snippet) |
193 | snippet: Some("pub(crate) $0".to_string()), | 182 | .add_to(acc); |
194 | }) | ||
195 | } | 183 | } |
196 | 184 | ||
197 | fn complete_expr_keywords( | 185 | fn complete_expr_keywords( |
198 | file: &SourceFileNode, | 186 | file: &SourceFileNode, |
199 | fn_def: ast::FnDef, | 187 | fn_def: ast::FnDef, |
200 | name_ref: ast::NameRef, | 188 | name_ref: ast::NameRef, |
201 | acc: &mut Vec<CompletionItem>, | 189 | acc: &mut Completions, |
202 | ) { | 190 | ) { |
203 | acc.push(keyword("if", "if $0 {}")); | 191 | acc.add(keyword("if", "if $0 {}")); |
204 | acc.push(keyword("match", "match $0 {}")); | 192 | acc.add(keyword("match", "match $0 {}")); |
205 | acc.push(keyword("while", "while $0 {}")); | 193 | acc.add(keyword("while", "while $0 {}")); |
206 | acc.push(keyword("loop", "loop {$0}")); | 194 | acc.add(keyword("loop", "loop {$0}")); |
207 | 195 | ||
208 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { | 196 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { |
209 | if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { | 197 | if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { |
210 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { | 198 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { |
211 | acc.push(keyword("else", "else {$0}")); | 199 | acc.add(keyword("else", "else {$0}")); |
212 | acc.push(keyword("else if", "else if $0 {}")); | 200 | acc.add(keyword("else if", "else if $0 {}")); |
213 | } | 201 | } |
214 | } | 202 | } |
215 | } | 203 | } |
216 | if is_in_loop_body(name_ref) { | 204 | if is_in_loop_body(name_ref) { |
217 | acc.push(keyword("continue", "continue")); | 205 | acc.add(keyword("continue", "continue")); |
218 | acc.push(keyword("break", "break")); | 206 | acc.add(keyword("break", "break")); |
219 | } | 207 | } |
220 | acc.extend(complete_return(fn_def, name_ref)); | 208 | acc.add_all(complete_return(fn_def, name_ref)); |
221 | } | 209 | } |
222 | 210 | ||
223 | fn is_in_loop_body(name_ref: ast::NameRef) -> bool { | 211 | fn is_in_loop_body(name_ref: ast::NameRef) -> bool { |
@@ -272,23 +260,363 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet | |||
272 | Some(keyword("return", snip)) | 260 | Some(keyword("return", snip)) |
273 | } | 261 | } |
274 | 262 | ||
275 | fn keyword(kw: &str, snip: &str) -> CompletionItem { | 263 | fn keyword(kw: &str, snippet: &str) -> CompletionItem { |
276 | CompletionItem { | 264 | CompletionItem::new(kw) |
277 | label: kw.to_string(), | 265 | .kind(Keyword) |
278 | lookup: None, | 266 | .snippet(snippet) |
279 | snippet: Some(snip.to_string()), | 267 | .build() |
280 | } | ||
281 | } | 268 | } |
282 | 269 | ||
283 | fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { | 270 | fn complete_expr_snippets(acc: &mut Completions) { |
284 | acc.push(CompletionItem { | 271 | CompletionItem::new("pd") |
285 | label: "pd".to_string(), | 272 | .snippet("eprintln!(\"$0 = {:?}\", $0);") |
286 | lookup: None, | 273 | .kind(Snippet) |
287 | snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()), | 274 | .add_to(acc); |
288 | }); | 275 | CompletionItem::new("ppd") |
289 | acc.push(CompletionItem { | 276 | .snippet("eprintln!(\"$0 = {:#?}\", $0);") |
290 | label: "ppd".to_string(), | 277 | .kind(Snippet) |
291 | lookup: None, | 278 | .add_to(acc); |
292 | snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), | 279 | } |
293 | }); | 280 | |
281 | #[cfg(test)] | ||
282 | mod tests { | ||
283 | use crate::completion::{CompletionKind, check_completion}; | ||
284 | |||
285 | fn check_reference_completion(code: &str, expected_completions: &str) { | ||
286 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
287 | } | ||
288 | |||
289 | fn check_keyword_completion(code: &str, expected_completions: &str) { | ||
290 | check_completion(code, expected_completions, CompletionKind::Keyword); | ||
291 | } | ||
292 | |||
293 | fn check_snippet_completion(code: &str, expected_completions: &str) { | ||
294 | check_completion(code, expected_completions, CompletionKind::Snippet); | ||
295 | } | ||
296 | |||
297 | #[test] | ||
298 | fn test_completion_let_scope() { | ||
299 | check_reference_completion( | ||
300 | r" | ||
301 | fn quux(x: i32) { | ||
302 | let y = 92; | ||
303 | 1 + <|>; | ||
304 | let z = (); | ||
305 | } | ||
306 | ", | ||
307 | "y;x;quux", | ||
308 | ); | ||
309 | } | ||
310 | |||
311 | #[test] | ||
312 | fn test_completion_if_let_scope() { | ||
313 | check_reference_completion( | ||
314 | r" | ||
315 | fn quux() { | ||
316 | if let Some(x) = foo() { | ||
317 | let y = 92; | ||
318 | }; | ||
319 | if let Some(a) = bar() { | ||
320 | let b = 62; | ||
321 | 1 + <|> | ||
322 | } | ||
323 | } | ||
324 | ", | ||
325 | "b;a;quux", | ||
326 | ); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
330 | fn test_completion_for_scope() { | ||
331 | check_reference_completion( | ||
332 | r" | ||
333 | fn quux() { | ||
334 | for x in &[1, 2, 3] { | ||
335 | <|> | ||
336 | } | ||
337 | } | ||
338 | ", | ||
339 | "x;quux", | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
344 | fn test_completion_mod_scope() { | ||
345 | check_reference_completion( | ||
346 | r" | ||
347 | struct Foo; | ||
348 | enum Baz {} | ||
349 | fn quux() { | ||
350 | <|> | ||
351 | } | ||
352 | ", | ||
353 | "quux;Foo;Baz", | ||
354 | ); | ||
355 | } | ||
356 | |||
357 | #[test] | ||
358 | fn test_completion_mod_scope_no_self_use() { | ||
359 | check_reference_completion( | ||
360 | r" | ||
361 | use foo<|>; | ||
362 | ", | ||
363 | "", | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn test_completion_self_path() { | ||
369 | check_reference_completion( | ||
370 | r" | ||
371 | use self::m::<|>; | ||
372 | |||
373 | mod m { | ||
374 | struct Bar; | ||
375 | } | ||
376 | ", | ||
377 | "Bar", | ||
378 | ); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn test_completion_mod_scope_nested() { | ||
383 | check_reference_completion( | ||
384 | r" | ||
385 | struct Foo; | ||
386 | mod m { | ||
387 | struct Bar; | ||
388 | fn quux() { <|> } | ||
389 | } | ||
390 | ", | ||
391 | "quux;Bar", | ||
392 | ); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn test_complete_type() { | ||
397 | check_reference_completion( | ||
398 | r" | ||
399 | struct Foo; | ||
400 | fn x() -> <|> | ||
401 | ", | ||
402 | "Foo;x", | ||
403 | ) | ||
404 | } | ||
405 | |||
406 | #[test] | ||
407 | fn test_complete_shadowing() { | ||
408 | check_reference_completion( | ||
409 | r" | ||
410 | fn foo() -> { | ||
411 | let bar = 92; | ||
412 | { | ||
413 | let bar = 62; | ||
414 | <|> | ||
415 | } | ||
416 | } | ||
417 | ", | ||
418 | "bar;foo", | ||
419 | ) | ||
420 | } | ||
421 | |||
422 | #[test] | ||
423 | fn test_complete_self() { | ||
424 | check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") | ||
425 | } | ||
426 | |||
427 | #[test] | ||
428 | fn test_complete_crate_path() { | ||
429 | check_reference_completion( | ||
430 | " | ||
431 | //- /lib.rs | ||
432 | mod foo; | ||
433 | struct Spam; | ||
434 | //- /foo.rs | ||
435 | use crate::Sp<|> | ||
436 | ", | ||
437 | "Spam;foo", | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
442 | fn test_complete_crate_path_with_braces() { | ||
443 | check_reference_completion( | ||
444 | " | ||
445 | //- /lib.rs | ||
446 | mod foo; | ||
447 | struct Spam; | ||
448 | //- /foo.rs | ||
449 | use crate::{Sp<|>}; | ||
450 | ", | ||
451 | "Spam;foo", | ||
452 | ); | ||
453 | } | ||
454 | |||
455 | #[test] | ||
456 | fn test_complete_crate_path_in_nested_tree() { | ||
457 | check_reference_completion( | ||
458 | " | ||
459 | //- /lib.rs | ||
460 | mod foo; | ||
461 | pub mod bar { | ||
462 | pub mod baz { | ||
463 | pub struct Spam; | ||
464 | } | ||
465 | } | ||
466 | //- /foo.rs | ||
467 | use crate::{bar::{baz::Sp<|>}}; | ||
468 | ", | ||
469 | "Spam", | ||
470 | ); | ||
471 | } | ||
472 | |||
473 | #[test] | ||
474 | fn test_completion_kewords() { | ||
475 | check_keyword_completion( | ||
476 | r" | ||
477 | fn quux() { | ||
478 | <|> | ||
479 | } | ||
480 | ", | ||
481 | r#" | ||
482 | if "if $0 {}" | ||
483 | match "match $0 {}" | ||
484 | while "while $0 {}" | ||
485 | loop "loop {$0}" | ||
486 | return "return" | ||
487 | "#, | ||
488 | ); | ||
489 | } | ||
490 | |||
491 | #[test] | ||
492 | fn test_completion_else() { | ||
493 | check_keyword_completion( | ||
494 | r" | ||
495 | fn quux() { | ||
496 | if true { | ||
497 | () | ||
498 | } <|> | ||
499 | } | ||
500 | ", | ||
501 | r#" | ||
502 | if "if $0 {}" | ||
503 | match "match $0 {}" | ||
504 | while "while $0 {}" | ||
505 | loop "loop {$0}" | ||
506 | else "else {$0}" | ||
507 | else if "else if $0 {}" | ||
508 | return "return" | ||
509 | "#, | ||
510 | ); | ||
511 | } | ||
512 | |||
513 | #[test] | ||
514 | fn test_completion_return_value() { | ||
515 | check_keyword_completion( | ||
516 | r" | ||
517 | fn quux() -> i32 { | ||
518 | <|> | ||
519 | 92 | ||
520 | } | ||
521 | ", | ||
522 | r#" | ||
523 | if "if $0 {}" | ||
524 | match "match $0 {}" | ||
525 | while "while $0 {}" | ||
526 | loop "loop {$0}" | ||
527 | return "return $0;" | ||
528 | "#, | ||
529 | ); | ||
530 | check_keyword_completion( | ||
531 | r" | ||
532 | fn quux() { | ||
533 | <|> | ||
534 | 92 | ||
535 | } | ||
536 | ", | ||
537 | r#" | ||
538 | if "if $0 {}" | ||
539 | match "match $0 {}" | ||
540 | while "while $0 {}" | ||
541 | loop "loop {$0}" | ||
542 | return "return;" | ||
543 | "#, | ||
544 | ); | ||
545 | } | ||
546 | |||
547 | #[test] | ||
548 | fn test_completion_return_no_stmt() { | ||
549 | check_keyword_completion( | ||
550 | r" | ||
551 | fn quux() -> i32 { | ||
552 | match () { | ||
553 | () => <|> | ||
554 | } | ||
555 | } | ||
556 | ", | ||
557 | r#" | ||
558 | if "if $0 {}" | ||
559 | match "match $0 {}" | ||
560 | while "while $0 {}" | ||
561 | loop "loop {$0}" | ||
562 | return "return $0" | ||
563 | "#, | ||
564 | ); | ||
565 | } | ||
566 | |||
567 | #[test] | ||
568 | fn test_continue_break_completion() { | ||
569 | check_keyword_completion( | ||
570 | r" | ||
571 | fn quux() -> i32 { | ||
572 | loop { <|> } | ||
573 | } | ||
574 | ", | ||
575 | r#" | ||
576 | if "if $0 {}" | ||
577 | match "match $0 {}" | ||
578 | while "while $0 {}" | ||
579 | loop "loop {$0}" | ||
580 | continue "continue" | ||
581 | break "break" | ||
582 | return "return $0" | ||
583 | "#, | ||
584 | ); | ||
585 | check_keyword_completion( | ||
586 | r" | ||
587 | fn quux() -> i32 { | ||
588 | loop { || { <|> } } | ||
589 | } | ||
590 | ", | ||
591 | r#" | ||
592 | if "if $0 {}" | ||
593 | match "match $0 {}" | ||
594 | while "while $0 {}" | ||
595 | loop "loop {$0}" | ||
596 | return "return $0" | ||
597 | "#, | ||
598 | ); | ||
599 | } | ||
600 | |||
601 | #[test] | ||
602 | fn test_item_snippets() { | ||
603 | // check_snippet_completion(r" | ||
604 | // <|> | ||
605 | // ", | ||
606 | // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##, | ||
607 | // ); | ||
608 | check_snippet_completion( | ||
609 | r" | ||
610 | #[cfg(test)] | ||
611 | mod tests { | ||
612 | <|> | ||
613 | } | ||
614 | ", | ||
615 | r##" | ||
616 | tfn "Test function" "#[test]\nfn ${1:feature}() {\n $0\n}" | ||
617 | pub(crate) "pub(crate) $0" | ||
618 | "##, | ||
619 | ); | ||
620 | } | ||
621 | |||
294 | } | 622 | } |