aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion/reference_completion.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/completion/reference_completion.rs')
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs512
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};
9use hir::{ 9use hir::{
10 self, 10 self,
11 FnScopes, 11 FnScopes, Def, Path
12 Def,
13 Path,
14}; 12};
15 13
16use crate::{ 14use crate::{
17 db::RootDatabase, 15 db::RootDatabase,
18 completion::CompletionItem, 16 completion::{CompletionItem, Completions, CompletionKind::*},
19 Cancelable 17 Cancelable
20}; 18};
21 19
22pub(super) fn completions( 20pub(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
128fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { 124fn 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
150fn complete_path( 140fn 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
178fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { 167fn 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\ 173fn ${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
197fn complete_expr_keywords( 185fn 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
223fn is_in_loop_body(name_ref: ast::NameRef) -> bool { 211fn 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
275fn keyword(kw: &str, snip: &str) -> CompletionItem { 263fn 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
283fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { 270fn 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)]
282mod 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}