]> git.lizzy.rs Git - rust.git/blob - crates/completion/src/completions/keyword.rs
Add braces when completing ifs
[rust.git] / crates / completion / src / completions / keyword.rs
1 //! Completes keywords.
2
3 use syntax::{ast, SyntaxKind};
4 use test_utils::mark;
5
6 use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
7
8 pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
9     // complete keyword "crate" in use stmt
10     let source_range = ctx.source_range();
11
12     if ctx.use_item_syntax.is_some() {
13         if ctx.path_qual.is_none() {
14             CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
15                 .kind(CompletionItemKind::Keyword)
16                 .insert_text("crate::")
17                 .add_to(acc);
18         }
19         CompletionItem::new(CompletionKind::Keyword, source_range, "self")
20             .kind(CompletionItemKind::Keyword)
21             .add_to(acc);
22         CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
23             .kind(CompletionItemKind::Keyword)
24             .insert_text("super::")
25             .add_to(acc);
26     }
27
28     // Suggest .await syntax for types that implement Future trait
29     if let Some(receiver) = &ctx.dot_receiver {
30         if let Some(ty) = ctx.sema.type_of_expr(receiver) {
31             if ty.impls_future(ctx.db) {
32                 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
33                     .kind(CompletionItemKind::Keyword)
34                     .detail("expr.await")
35                     .insert_text("await")
36                     .add_to(acc);
37             }
38         };
39     }
40 }
41
42 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
43     if ctx.token.kind() == SyntaxKind::COMMENT {
44         mark::hit!(no_keyword_completion_in_comments);
45         return;
46     }
47     if ctx.record_lit_syntax.is_some() {
48         mark::hit!(no_keyword_completion_in_record_lit);
49         return;
50     }
51
52     let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
53     if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
54         add_keyword(ctx, acc, "where", "where ");
55         return;
56     }
57     if ctx.unsafe_is_prev {
58         if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
59             add_keyword(ctx, acc, "fn", "fn $0() {}")
60         }
61
62         if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
63             add_keyword(ctx, acc, "trait", "trait $0 {}");
64             add_keyword(ctx, acc, "impl", "impl $0 {}");
65         }
66
67         return;
68     }
69     if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
70     {
71         add_keyword(ctx, acc, "fn", "fn $0() {}");
72     }
73     if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
74         add_keyword(ctx, acc, "use", "use ");
75         add_keyword(ctx, acc, "impl", "impl $0 {}");
76         add_keyword(ctx, acc, "trait", "trait $0 {}");
77     }
78
79     if ctx.has_item_list_or_source_file_parent {
80         add_keyword(ctx, acc, "enum", "enum $0 {}");
81         add_keyword(ctx, acc, "struct", "struct $0");
82         add_keyword(ctx, acc, "union", "union $0 {}");
83     }
84
85     if ctx.is_expr {
86         add_keyword(ctx, acc, "match", "match $0 {}");
87         add_keyword(ctx, acc, "while", "while $0 {}");
88         add_keyword(ctx, acc, "loop", "loop {$0}");
89         add_keyword(ctx, acc, "if", "if $0 {}");
90         add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
91     }
92
93     if ctx.if_is_prev || ctx.block_expr_parent {
94         add_keyword(ctx, acc, "let", "let ");
95     }
96
97     if ctx.after_if {
98         add_keyword(ctx, acc, "else", "else {$0}");
99         add_keyword(ctx, acc, "else if", "else if $0 {}");
100     }
101     if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
102         add_keyword(ctx, acc, "mod", "mod $0 {}");
103     }
104     if ctx.bind_pat_parent || ctx.ref_pat_parent {
105         add_keyword(ctx, acc, "mut", "mut ");
106     }
107     if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
108     {
109         add_keyword(ctx, acc, "const", "const ");
110         add_keyword(ctx, acc, "type", "type ");
111     }
112     if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
113         add_keyword(ctx, acc, "static", "static ");
114     };
115     if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
116         add_keyword(ctx, acc, "extern", "extern ");
117     }
118     if ctx.has_item_list_or_source_file_parent
119         || has_trait_or_impl_parent
120         || ctx.block_expr_parent
121         || ctx.is_match_arm
122     {
123         add_keyword(ctx, acc, "unsafe", "unsafe ");
124     }
125     if ctx.in_loop_body {
126         if ctx.can_be_stmt {
127             add_keyword(ctx, acc, "continue", "continue;");
128             add_keyword(ctx, acc, "break", "break;");
129         } else {
130             add_keyword(ctx, acc, "continue", "continue");
131             add_keyword(ctx, acc, "break", "break");
132         }
133     }
134     if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
135         add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
136         add_keyword(ctx, acc, "pub", "pub ");
137     }
138
139     if !ctx.is_trivial_path {
140         return;
141     }
142     let fn_def = match &ctx.function_syntax {
143         Some(it) => it,
144         None => return,
145     };
146     acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
147 }
148
149 fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
150     let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
151         .kind(CompletionItemKind::Keyword);
152
153     match ctx.config.snippet_cap {
154         Some(cap) => res.insert_snippet(cap, snippet),
155         _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
156     }
157     .build()
158 }
159
160 fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
161     acc.add(keyword(ctx, kw, snippet));
162 }
163
164 fn complete_return(
165     ctx: &CompletionContext,
166     fn_def: &ast::Fn,
167     can_be_stmt: bool,
168 ) -> Option<CompletionItem> {
169     let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
170         (true, true) => "return $0;",
171         (true, false) => "return;",
172         (false, true) => "return $0",
173         (false, false) => "return",
174     };
175     Some(keyword(ctx, "return", snip))
176 }
177
178 #[cfg(test)]
179 mod tests {
180     use expect_test::{expect, Expect};
181
182     use crate::{
183         test_utils::{check_edit, completion_list},
184         CompletionKind,
185     };
186     use test_utils::mark;
187
188     fn check(ra_fixture: &str, expect: Expect) {
189         let actual = completion_list(ra_fixture, CompletionKind::Keyword);
190         expect.assert_eq(&actual)
191     }
192
193     #[test]
194     fn test_keywords_in_use_stmt() {
195         check(
196             r"use $0",
197             expect![[r#"
198                 kw crate::
199                 kw self
200                 kw super::
201             "#]],
202         );
203
204         check(
205             r"use a::$0",
206             expect![[r#"
207                 kw self
208                 kw super::
209             "#]],
210         );
211
212         check(
213             r"use a::{b, $0}",
214             expect![[r#"
215                 kw self
216                 kw super::
217             "#]],
218         );
219     }
220
221     #[test]
222     fn test_keywords_at_source_file_level() {
223         check(
224             r"m$0",
225             expect![[r#"
226                 kw fn
227                 kw use
228                 kw impl
229                 kw trait
230                 kw enum
231                 kw struct
232                 kw union
233                 kw mod
234                 kw const
235                 kw type
236                 kw static
237                 kw extern
238                 kw unsafe
239                 kw pub(crate)
240                 kw pub
241             "#]],
242         );
243     }
244
245     #[test]
246     fn test_keywords_in_function() {
247         check(
248             r"fn quux() { $0 }",
249             expect![[r#"
250                 kw fn
251                 kw use
252                 kw impl
253                 kw trait
254                 kw match
255                 kw while
256                 kw loop
257                 kw if
258                 kw if let
259                 kw let
260                 kw mod
261                 kw const
262                 kw type
263                 kw static
264                 kw extern
265                 kw unsafe
266                 kw return
267             "#]],
268         );
269     }
270
271     #[test]
272     fn test_keywords_inside_block() {
273         check(
274             r"fn quux() { if true { $0 } }",
275             expect![[r#"
276                 kw fn
277                 kw use
278                 kw impl
279                 kw trait
280                 kw match
281                 kw while
282                 kw loop
283                 kw if
284                 kw if let
285                 kw let
286                 kw mod
287                 kw const
288                 kw type
289                 kw static
290                 kw extern
291                 kw unsafe
292                 kw return
293             "#]],
294         );
295     }
296
297     #[test]
298     fn test_keywords_after_if() {
299         check(
300             r#"fn quux() { if true { () } $0 }"#,
301             expect![[r#"
302                 kw fn
303                 kw use
304                 kw impl
305                 kw trait
306                 kw match
307                 kw while
308                 kw loop
309                 kw if
310                 kw if let
311                 kw let
312                 kw else
313                 kw else if
314                 kw mod
315                 kw const
316                 kw type
317                 kw static
318                 kw extern
319                 kw unsafe
320                 kw return
321             "#]],
322         );
323         check_edit(
324             "else",
325             r#"fn quux() { if true { () } $0 }"#,
326             r#"fn quux() { if true { () } else {$0} }"#,
327         );
328     }
329
330     #[test]
331     fn test_keywords_in_match_arm() {
332         check(
333             r#"
334 fn quux() -> i32 {
335     match () { () => $0 }
336 }
337 "#,
338             expect![[r#"
339                 kw match
340                 kw while
341                 kw loop
342                 kw if
343                 kw if let
344                 kw unsafe
345                 kw return
346             "#]],
347         );
348     }
349
350     #[test]
351     fn test_keywords_in_trait_def() {
352         check(
353             r"trait My { $0 }",
354             expect![[r#"
355                 kw fn
356                 kw const
357                 kw type
358                 kw unsafe
359             "#]],
360         );
361     }
362
363     #[test]
364     fn test_keywords_in_impl_def() {
365         check(
366             r"impl My { $0 }",
367             expect![[r#"
368                 kw fn
369                 kw const
370                 kw type
371                 kw unsafe
372                 kw pub(crate)
373                 kw pub
374             "#]],
375         );
376     }
377
378     #[test]
379     fn test_keywords_in_loop() {
380         check(
381             r"fn my() { loop { $0 } }",
382             expect![[r#"
383                 kw fn
384                 kw use
385                 kw impl
386                 kw trait
387                 kw match
388                 kw while
389                 kw loop
390                 kw if
391                 kw if let
392                 kw let
393                 kw mod
394                 kw const
395                 kw type
396                 kw static
397                 kw extern
398                 kw unsafe
399                 kw continue
400                 kw break
401                 kw return
402             "#]],
403         );
404     }
405
406     #[test]
407     fn test_keywords_after_unsafe_in_item_list() {
408         check(
409             r"unsafe $0",
410             expect![[r#"
411                 kw fn
412                 kw trait
413                 kw impl
414             "#]],
415         );
416     }
417
418     #[test]
419     fn test_keywords_after_unsafe_in_block_expr() {
420         check(
421             r"fn my_fn() { unsafe $0 }",
422             expect![[r#"
423                 kw fn
424                 kw trait
425                 kw impl
426             "#]],
427         );
428     }
429
430     #[test]
431     fn test_mut_in_ref_and_in_fn_parameters_list() {
432         check(
433             r"fn my_fn(&$0) {}",
434             expect![[r#"
435                 kw mut
436             "#]],
437         );
438         check(
439             r"fn my_fn($0) {}",
440             expect![[r#"
441                 kw mut
442             "#]],
443         );
444         check(
445             r"fn my_fn() { let &$0 }",
446             expect![[r#"
447                 kw mut
448             "#]],
449         );
450     }
451
452     #[test]
453     fn test_where_keyword() {
454         check(
455             r"trait A $0",
456             expect![[r#"
457                 kw where
458             "#]],
459         );
460         check(
461             r"impl A $0",
462             expect![[r#"
463                 kw where
464             "#]],
465         );
466     }
467
468     #[test]
469     fn no_keyword_completion_in_comments() {
470         mark::check!(no_keyword_completion_in_comments);
471         check(
472             r#"
473 fn test() {
474     let x = 2; // A comment$0
475 }
476 "#,
477             expect![[""]],
478         );
479         check(
480             r#"
481 /*
482 Some multi-line comment$0
483 */
484 "#,
485             expect![[""]],
486         );
487         check(
488             r#"
489 /// Some doc comment
490 /// let test$0 = 1
491 "#,
492             expect![[""]],
493         );
494     }
495
496     #[test]
497     fn test_completion_await_impls_future() {
498         check(
499             r#"
500 //- /main.rs crate:main deps:std
501 use std::future::*;
502 struct A {}
503 impl Future for A {}
504 fn foo(a: A) { a.$0 }
505
506 //- /std/lib.rs crate:std
507 pub mod future {
508     #[lang = "future_trait"]
509     pub trait Future {}
510 }
511 "#,
512             expect![[r#"
513                 kw await expr.await
514             "#]],
515         );
516
517         check(
518             r#"
519 //- /main.rs crate:main deps:std
520 use std::future::*;
521 fn foo() {
522     let a = async {};
523     a.$0
524 }
525
526 //- /std/lib.rs crate:std
527 pub mod future {
528     #[lang = "future_trait"]
529     pub trait Future {
530         type Output;
531     }
532 }
533 "#,
534             expect![[r#"
535                 kw await expr.await
536             "#]],
537         )
538     }
539
540     #[test]
541     fn after_let() {
542         check(
543             r#"fn main() { let _ = $0 }"#,
544             expect![[r#"
545                 kw match
546                 kw while
547                 kw loop
548                 kw if
549                 kw if let
550                 kw return
551             "#]],
552         )
553     }
554
555     #[test]
556     fn before_field() {
557         check(
558             r#"
559 struct Foo {
560     $0
561     pub f: i32,
562 }
563 "#,
564             expect![[r#"
565                 kw pub(crate)
566                 kw pub
567             "#]],
568         )
569     }
570
571     #[test]
572     fn skip_struct_initializer() {
573         mark::check!(no_keyword_completion_in_record_lit);
574         check(
575             r#"
576 struct Foo {
577     pub f: i32,
578 }
579 fn foo() {
580     Foo {
581         $0
582     }
583 }
584 "#,
585             expect![[r#""#]],
586         );
587     }
588
589     #[test]
590     fn struct_initializer_field_expr() {
591         check(
592             r#"
593 struct Foo {
594     pub f: i32,
595 }
596 fn foo() {
597     Foo {
598         f: $0
599     }
600 }
601 "#,
602             expect![[r#"
603                 kw match
604                 kw while
605                 kw loop
606                 kw if
607                 kw if let
608                 kw return
609             "#]],
610         );
611     }
612 }