aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/complete_keyword.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-11-27 18:45:05 +0000
committerGitHub <[email protected]>2019-11-27 18:45:05 +0000
commit360f6b7bb32d6280ed787075c4a54f4e5b46fcb6 (patch)
tree4c059427819ef442c785125f48fe83f81f6d667a /crates/ra_ide/src/completion/complete_keyword.rs
parent4946169a96f3d442f463724af481fdb760381ccb (diff)
parent27b362b05910c81fd5b28f6cd5d2c075311032f9 (diff)
Merge #2430
2430: rename ra_ide_api -> ra_ide r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_ide/src/completion/complete_keyword.rs')
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs781
1 files changed, 781 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
new file mode 100644
index 000000000..eb7cd9ac2
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -0,0 +1,781 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{
4 ast::{self, LoopBodyOwner},
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9
10use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
12};
13
14pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
15 // complete keyword "crate" in use stmt
16 let source_range = ctx.source_range();
17 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
18 (Some(_), None) => {
19 CompletionItem::new(CompletionKind::Keyword, source_range, "crate")
20 .kind(CompletionItemKind::Keyword)
21 .insert_text("crate::")
22 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
24 .kind(CompletionItemKind::Keyword)
25 .add_to(acc);
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super")
27 .kind(CompletionItemKind::Keyword)
28 .insert_text("super::")
29 .add_to(acc);
30 }
31 (Some(_), Some(_)) => {
32 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
33 .kind(CompletionItemKind::Keyword)
34 .add_to(acc);
35 CompletionItem::new(CompletionKind::Keyword, source_range, "super")
36 .kind(CompletionItemKind::Keyword)
37 .insert_text("super::")
38 .add_to(acc);
39 }
40 _ => {}
41 }
42}
43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword)
47 .insert_snippet(snippet)
48 .build()
49}
50
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
52 if !ctx.is_trivial_path {
53 return;
54 }
55
56 let fn_def = match &ctx.function_syntax {
57 Some(it) => it,
58 None => return,
59 };
60 acc.add(keyword(ctx, "if", "if $0 {}"));
61 acc.add(keyword(ctx, "match", "match $0 {}"));
62 acc.add(keyword(ctx, "while", "while $0 {}"));
63 acc.add(keyword(ctx, "loop", "loop {$0}"));
64
65 if ctx.after_if {
66 acc.add(keyword(ctx, "else", "else {$0}"));
67 acc.add(keyword(ctx, "else if", "else if $0 {}"));
68 }
69 if is_in_loop_body(&ctx.token) {
70 if ctx.can_be_stmt {
71 acc.add(keyword(ctx, "continue", "continue;"));
72 acc.add(keyword(ctx, "break", "break;"));
73 } else {
74 acc.add(keyword(ctx, "continue", "continue"));
75 acc.add(keyword(ctx, "break", "break"));
76 }
77 }
78 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
79}
80
81fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
82 for node in leaf.parent().ancestors() {
83 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
84 break;
85 }
86 let loop_body = match_ast! {
87 match node {
88 ast::ForExpr(it) => { it.loop_body() },
89 ast::WhileExpr(it) => { it.loop_body() },
90 ast::LoopExpr(it) => { it.loop_body() },
91 _ => None,
92 }
93 };
94 if let Some(body) = loop_body {
95 if leaf.text_range().is_subrange(&body.syntax().text_range()) {
96 return true;
97 }
98 }
99 }
100 false
101}
102
103fn complete_return(
104 ctx: &CompletionContext,
105 fn_def: &ast::FnDef,
106 can_be_stmt: bool,
107) -> Option<CompletionItem> {
108 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
109 (true, true) => "return $0;",
110 (true, false) => "return;",
111 (false, true) => "return $0",
112 (false, false) => "return",
113 };
114 Some(keyword(ctx, "return", snip))
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::completion::{do_completion, CompletionItem, CompletionKind};
120 use insta::assert_debug_snapshot;
121
122 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> {
123 do_completion(code, CompletionKind::Keyword)
124 }
125
126 #[test]
127 fn completes_keywords_in_use_stmt() {
128 assert_debug_snapshot!(
129 do_keyword_completion(
130 r"
131 use <|>
132 ",
133 ),
134 @r###"
135 [
136 CompletionItem {
137 label: "crate",
138 source_range: [21; 21),
139 delete: [21; 21),
140 insert: "crate::",
141 kind: Keyword,
142 },
143 CompletionItem {
144 label: "self",
145 source_range: [21; 21),
146 delete: [21; 21),
147 insert: "self",
148 kind: Keyword,
149 },
150 CompletionItem {
151 label: "super",
152 source_range: [21; 21),
153 delete: [21; 21),
154 insert: "super::",
155 kind: Keyword,
156 },
157 ]
158 "###
159 );
160
161 assert_debug_snapshot!(
162 do_keyword_completion(
163 r"
164 use a::<|>
165 ",
166 ),
167 @r###"
168 [
169 CompletionItem {
170 label: "self",
171 source_range: [24; 24),
172 delete: [24; 24),
173 insert: "self",
174 kind: Keyword,
175 },
176 CompletionItem {
177 label: "super",
178 source_range: [24; 24),
179 delete: [24; 24),
180 insert: "super::",
181 kind: Keyword,
182 },
183 ]
184 "###
185 );
186
187 assert_debug_snapshot!(
188 do_keyword_completion(
189 r"
190 use a::{b, <|>}
191 ",
192 ),
193 @r###"
194 [
195 CompletionItem {
196 label: "self",
197 source_range: [28; 28),
198 delete: [28; 28),
199 insert: "self",
200 kind: Keyword,
201 },
202 CompletionItem {
203 label: "super",
204 source_range: [28; 28),
205 delete: [28; 28),
206 insert: "super::",
207 kind: Keyword,
208 },
209 ]
210 "###
211 );
212 }
213
214 #[test]
215 fn completes_various_keywords_in_function() {
216 assert_debug_snapshot!(
217 do_keyword_completion(
218 r"
219 fn quux() {
220 <|>
221 }
222 ",
223 ),
224 @r###"
225 [
226 CompletionItem {
227 label: "if",
228 source_range: [49; 49),
229 delete: [49; 49),
230 insert: "if $0 {}",
231 kind: Keyword,
232 },
233 CompletionItem {
234 label: "loop",
235 source_range: [49; 49),
236 delete: [49; 49),
237 insert: "loop {$0}",
238 kind: Keyword,
239 },
240 CompletionItem {
241 label: "match",
242 source_range: [49; 49),
243 delete: [49; 49),
244 insert: "match $0 {}",
245 kind: Keyword,
246 },
247 CompletionItem {
248 label: "return",
249 source_range: [49; 49),
250 delete: [49; 49),
251 insert: "return;",
252 kind: Keyword,
253 },
254 CompletionItem {
255 label: "while",
256 source_range: [49; 49),
257 delete: [49; 49),
258 insert: "while $0 {}",
259 kind: Keyword,
260 },
261 ]
262 "###
263 );
264 }
265
266 #[test]
267 fn completes_else_after_if() {
268 assert_debug_snapshot!(
269 do_keyword_completion(
270 r"
271 fn quux() {
272 if true {
273 ()
274 } <|>
275 }
276 ",
277 ),
278 @r###"
279 [
280 CompletionItem {
281 label: "else",
282 source_range: [108; 108),
283 delete: [108; 108),
284 insert: "else {$0}",
285 kind: Keyword,
286 },
287 CompletionItem {
288 label: "else if",
289 source_range: [108; 108),
290 delete: [108; 108),
291 insert: "else if $0 {}",
292 kind: Keyword,
293 },
294 CompletionItem {
295 label: "if",
296 source_range: [108; 108),
297 delete: [108; 108),
298 insert: "if $0 {}",
299 kind: Keyword,
300 },
301 CompletionItem {
302 label: "loop",
303 source_range: [108; 108),
304 delete: [108; 108),
305 insert: "loop {$0}",
306 kind: Keyword,
307 },
308 CompletionItem {
309 label: "match",
310 source_range: [108; 108),
311 delete: [108; 108),
312 insert: "match $0 {}",
313 kind: Keyword,
314 },
315 CompletionItem {
316 label: "return",
317 source_range: [108; 108),
318 delete: [108; 108),
319 insert: "return;",
320 kind: Keyword,
321 },
322 CompletionItem {
323 label: "while",
324 source_range: [108; 108),
325 delete: [108; 108),
326 insert: "while $0 {}",
327 kind: Keyword,
328 },
329 ]
330 "###
331 );
332 }
333
334 #[test]
335 fn test_completion_return_value() {
336 assert_debug_snapshot!(
337 do_keyword_completion(
338 r"
339 fn quux() -> i32 {
340 <|>
341 92
342 }
343 ",
344 ),
345 @r###"
346 [
347 CompletionItem {
348 label: "if",
349 source_range: [56; 56),
350 delete: [56; 56),
351 insert: "if $0 {}",
352 kind: Keyword,
353 },
354 CompletionItem {
355 label: "loop",
356 source_range: [56; 56),
357 delete: [56; 56),
358 insert: "loop {$0}",
359 kind: Keyword,
360 },
361 CompletionItem {
362 label: "match",
363 source_range: [56; 56),
364 delete: [56; 56),
365 insert: "match $0 {}",
366 kind: Keyword,
367 },
368 CompletionItem {
369 label: "return",
370 source_range: [56; 56),
371 delete: [56; 56),
372 insert: "return $0;",
373 kind: Keyword,
374 },
375 CompletionItem {
376 label: "while",
377 source_range: [56; 56),
378 delete: [56; 56),
379 insert: "while $0 {}",
380 kind: Keyword,
381 },
382 ]
383 "###
384 );
385 assert_debug_snapshot!(
386 do_keyword_completion(
387 r"
388 fn quux() {
389 <|>
390 92
391 }
392 ",
393 ),
394 @r###"
395 [
396 CompletionItem {
397 label: "if",
398 source_range: [49; 49),
399 delete: [49; 49),
400 insert: "if $0 {}",
401 kind: Keyword,
402 },
403 CompletionItem {
404 label: "loop",
405 source_range: [49; 49),
406 delete: [49; 49),
407 insert: "loop {$0}",
408 kind: Keyword,
409 },
410 CompletionItem {
411 label: "match",
412 source_range: [49; 49),
413 delete: [49; 49),
414 insert: "match $0 {}",
415 kind: Keyword,
416 },
417 CompletionItem {
418 label: "return",
419 source_range: [49; 49),
420 delete: [49; 49),
421 insert: "return;",
422 kind: Keyword,
423 },
424 CompletionItem {
425 label: "while",
426 source_range: [49; 49),
427 delete: [49; 49),
428 insert: "while $0 {}",
429 kind: Keyword,
430 },
431 ]
432 "###
433 );
434 }
435
436 #[test]
437 fn dont_add_semi_after_return_if_not_a_statement() {
438 assert_debug_snapshot!(
439 do_keyword_completion(
440 r"
441 fn quux() -> i32 {
442 match () {
443 () => <|>
444 }
445 }
446 ",
447 ),
448 @r###"
449 [
450 CompletionItem {
451 label: "if",
452 source_range: [97; 97),
453 delete: [97; 97),
454 insert: "if $0 {}",
455 kind: Keyword,
456 },
457 CompletionItem {
458 label: "loop",
459 source_range: [97; 97),
460 delete: [97; 97),
461 insert: "loop {$0}",
462 kind: Keyword,
463 },
464 CompletionItem {
465 label: "match",
466 source_range: [97; 97),
467 delete: [97; 97),
468 insert: "match $0 {}",
469 kind: Keyword,
470 },
471 CompletionItem {
472 label: "return",
473 source_range: [97; 97),
474 delete: [97; 97),
475 insert: "return $0",
476 kind: Keyword,
477 },
478 CompletionItem {
479 label: "while",
480 source_range: [97; 97),
481 delete: [97; 97),
482 insert: "while $0 {}",
483 kind: Keyword,
484 },
485 ]
486 "###
487 );
488 }
489
490 #[test]
491 fn last_return_in_block_has_semi() {
492 assert_debug_snapshot!(
493 do_keyword_completion(
494 r"
495 fn quux() -> i32 {
496 if condition {
497 <|>
498 }
499 }
500 ",
501 ),
502 @r###"
503 [
504 CompletionItem {
505 label: "if",
506 source_range: [95; 95),
507 delete: [95; 95),
508 insert: "if $0 {}",
509 kind: Keyword,
510 },
511 CompletionItem {
512 label: "loop",
513 source_range: [95; 95),
514 delete: [95; 95),
515 insert: "loop {$0}",
516 kind: Keyword,
517 },
518 CompletionItem {
519 label: "match",
520 source_range: [95; 95),
521 delete: [95; 95),
522 insert: "match $0 {}",
523 kind: Keyword,
524 },
525 CompletionItem {
526 label: "return",
527 source_range: [95; 95),
528 delete: [95; 95),
529 insert: "return $0;",
530 kind: Keyword,
531 },
532 CompletionItem {
533 label: "while",
534 source_range: [95; 95),
535 delete: [95; 95),
536 insert: "while $0 {}",
537 kind: Keyword,
538 },
539 ]
540 "###
541 );
542 assert_debug_snapshot!(
543 do_keyword_completion(
544 r"
545 fn quux() -> i32 {
546 if condition {
547 <|>
548 }
549 let x = 92;
550 x
551 }
552 ",
553 ),
554 @r###"
555 [
556 CompletionItem {
557 label: "if",
558 source_range: [95; 95),
559 delete: [95; 95),
560 insert: "if $0 {}",
561 kind: Keyword,
562 },
563 CompletionItem {
564 label: "loop",
565 source_range: [95; 95),
566 delete: [95; 95),
567 insert: "loop {$0}",
568 kind: Keyword,
569 },
570 CompletionItem {
571 label: "match",
572 source_range: [95; 95),
573 delete: [95; 95),
574 insert: "match $0 {}",
575 kind: Keyword,
576 },
577 CompletionItem {
578 label: "return",
579 source_range: [95; 95),
580 delete: [95; 95),
581 insert: "return $0;",
582 kind: Keyword,
583 },
584 CompletionItem {
585 label: "while",
586 source_range: [95; 95),
587 delete: [95; 95),
588 insert: "while $0 {}",
589 kind: Keyword,
590 },
591 ]
592 "###
593 );
594 }
595
596 #[test]
597 fn completes_break_and_continue_in_loops() {
598 assert_debug_snapshot!(
599 do_keyword_completion(
600 r"
601 fn quux() -> i32 {
602 loop { <|> }
603 }
604 ",
605 ),
606 @r###"
607 [
608 CompletionItem {
609 label: "break",
610 source_range: [63; 63),
611 delete: [63; 63),
612 insert: "break;",
613 kind: Keyword,
614 },
615 CompletionItem {
616 label: "continue",
617 source_range: [63; 63),
618 delete: [63; 63),
619 insert: "continue;",
620 kind: Keyword,
621 },
622 CompletionItem {
623 label: "if",
624 source_range: [63; 63),
625 delete: [63; 63),
626 insert: "if $0 {}",
627 kind: Keyword,
628 },
629 CompletionItem {
630 label: "loop",
631 source_range: [63; 63),
632 delete: [63; 63),
633 insert: "loop {$0}",
634 kind: Keyword,
635 },
636 CompletionItem {
637 label: "match",
638 source_range: [63; 63),
639 delete: [63; 63),
640 insert: "match $0 {}",
641 kind: Keyword,
642 },
643 CompletionItem {
644 label: "return",
645 source_range: [63; 63),
646 delete: [63; 63),
647 insert: "return $0;",
648 kind: Keyword,
649 },
650 CompletionItem {
651 label: "while",
652 source_range: [63; 63),
653 delete: [63; 63),
654 insert: "while $0 {}",
655 kind: Keyword,
656 },
657 ]
658 "###
659 );
660
661 // No completion: lambda isolates control flow
662 assert_debug_snapshot!(
663 do_keyword_completion(
664 r"
665 fn quux() -> i32 {
666 loop { || { <|> } }
667 }
668 ",
669 ),
670 @r###"
671 [
672 CompletionItem {
673 label: "if",
674 source_range: [68; 68),
675 delete: [68; 68),
676 insert: "if $0 {}",
677 kind: Keyword,
678 },
679 CompletionItem {
680 label: "loop",
681 source_range: [68; 68),
682 delete: [68; 68),
683 insert: "loop {$0}",
684 kind: Keyword,
685 },
686 CompletionItem {
687 label: "match",
688 source_range: [68; 68),
689 delete: [68; 68),
690 insert: "match $0 {}",
691 kind: Keyword,
692 },
693 CompletionItem {
694 label: "return",
695 source_range: [68; 68),
696 delete: [68; 68),
697 insert: "return $0;",
698 kind: Keyword,
699 },
700 CompletionItem {
701 label: "while",
702 source_range: [68; 68),
703 delete: [68; 68),
704 insert: "while $0 {}",
705 kind: Keyword,
706 },
707 ]
708 "###
709 );
710 }
711
712 #[test]
713 fn no_semi_after_break_continue_in_expr() {
714 assert_debug_snapshot!(
715 do_keyword_completion(
716 r"
717 fn f() {
718 loop {
719 match () {
720 () => br<|>
721 }
722 }
723 }
724 ",
725 ),
726 @r###"
727 [
728 CompletionItem {
729 label: "break",
730 source_range: [122; 124),
731 delete: [122; 124),
732 insert: "break",
733 kind: Keyword,
734 },
735 CompletionItem {
736 label: "continue",
737 source_range: [122; 124),
738 delete: [122; 124),
739 insert: "continue",
740 kind: Keyword,
741 },
742 CompletionItem {
743 label: "if",
744 source_range: [122; 124),
745 delete: [122; 124),
746 insert: "if $0 {}",
747 kind: Keyword,
748 },
749 CompletionItem {
750 label: "loop",
751 source_range: [122; 124),
752 delete: [122; 124),
753 insert: "loop {$0}",
754 kind: Keyword,
755 },
756 CompletionItem {
757 label: "match",
758 source_range: [122; 124),
759 delete: [122; 124),
760 insert: "match $0 {}",
761 kind: Keyword,
762 },
763 CompletionItem {
764 label: "return",
765 source_range: [122; 124),
766 delete: [122; 124),
767 insert: "return",
768 kind: Keyword,
769 },
770 CompletionItem {
771 label: "while",
772 source_range: [122; 124),
773 delete: [122; 124),
774 insert: "while $0 {}",
775 kind: Keyword,
776 },
777 ]
778 "###
779 )
780 }
781}