]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/keyword.rs
Merge #9765
[rust.git] / crates / ide_completion / src / completions / keyword.rs
1 //! Completes keywords, except:
2 //! - `self`, `super` and `crate`, as these are considered part of path completions.
3 //! - `await`, as this is a postfix completion we handle this in the postfix completions.
4
5 use syntax::{SyntaxKind, T};
6
7 use crate::{
8     context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9     CompletionItemKind, CompletionKind, Completions,
10 };
11
12 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
13     if ctx.token.kind() == SyntaxKind::COMMENT {
14         cov_mark::hit!(no_keyword_completion_in_comments);
15         return;
16     }
17     if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
18         cov_mark::hit!(no_keyword_completion_in_record_lit);
19         return;
20     }
21     if ctx.attribute_under_caret.is_some() {
22         cov_mark::hit!(no_keyword_completion_in_attr_of_expr);
23         return;
24     }
25     if ctx.is_non_trivial_path() {
26         cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
27         return;
28     }
29
30     let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
31
32     let expects_assoc_item = ctx.expects_assoc_item();
33     let has_block_expr_parent = ctx.has_block_expr_parent();
34     let expects_item = ctx.expects_item();
35
36     if let Some(ImmediateLocation::Visibility(vis)) = &ctx.completion_location {
37         if vis.in_token().is_none() {
38             cov_mark::hit!(kw_completion_in);
39             add_keyword("in", "in");
40         }
41         return;
42     }
43     if ctx.has_impl_or_trait_prev_sibling() {
44         add_keyword("where", "where");
45         if ctx.has_impl_prev_sibling() {
46             add_keyword("for", "for");
47         }
48         return;
49     }
50     if ctx.previous_token_is(T![unsafe]) {
51         if expects_item || expects_assoc_item || has_block_expr_parent {
52             add_keyword("fn", "fn $1($2) {\n    $0\n}")
53         }
54
55         if expects_item || has_block_expr_parent {
56             add_keyword("trait", "trait $1 {\n    $0\n}");
57             add_keyword("impl", "impl $1 {\n    $0\n}");
58         }
59
60         return;
61     }
62
63     if !ctx.has_visibility_prev_sibling()
64         && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
65     {
66         add_keyword("pub(crate)", "pub(crate)");
67         add_keyword("pub", "pub");
68     }
69
70     if expects_item || expects_assoc_item || has_block_expr_parent {
71         add_keyword("unsafe", "unsafe");
72         add_keyword("fn", "fn $1($2) {\n    $0\n}");
73         add_keyword("const", "const $0");
74         add_keyword("type", "type $0");
75     }
76
77     if expects_item || has_block_expr_parent {
78         if !ctx.has_visibility_prev_sibling() {
79             add_keyword("impl", "impl $1 {\n    $0\n}");
80             add_keyword("extern", "extern $0");
81         }
82         add_keyword("use", "use $0");
83         add_keyword("trait", "trait $1 {\n    $0\n}");
84         add_keyword("static", "static $0");
85         add_keyword("mod", "mod $0");
86     }
87
88     if expects_item {
89         add_keyword("enum", "enum $1 {\n    $0\n}");
90         add_keyword("struct", "struct $0");
91         add_keyword("union", "union $1 {\n    $0\n}");
92     }
93
94     if ctx.expects_type() {
95         return;
96     }
97
98     if ctx.expects_expression() {
99         if !has_block_expr_parent {
100             add_keyword("unsafe", "unsafe {\n    $0\n}");
101         }
102         add_keyword("match", "match $1 {\n    $0\n}");
103         add_keyword("while", "while $1 {\n    $0\n}");
104         add_keyword("while let", "while let $1 = $2 {\n    $0\n}");
105         add_keyword("loop", "loop {\n    $0\n}");
106         add_keyword("if", "if $1 {\n    $0\n}");
107         add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
108         add_keyword("for", "for $1 in $2 {\n    $0\n}");
109         add_keyword("true", "true");
110         add_keyword("false", "false");
111     }
112
113     if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
114         add_keyword("let", "let");
115     }
116
117     if ctx.after_if() {
118         add_keyword("else", "else {\n    $0\n}");
119         add_keyword("else if", "else if $1 {\n    $0\n}");
120     }
121
122     if ctx.expects_ident_pat_or_ref_expr() {
123         add_keyword("mut", "mut ");
124     }
125
126     let (can_be_stmt, in_loop_body) = match ctx.path_context {
127         Some(PathCompletionContext {
128             is_trivial_path: true, can_be_stmt, in_loop_body, ..
129         }) => (can_be_stmt, in_loop_body),
130         _ => return,
131     };
132
133     if in_loop_body {
134         if can_be_stmt {
135             add_keyword("continue", "continue;");
136             add_keyword("break", "break;");
137         } else {
138             add_keyword("continue", "continue");
139             add_keyword("break", "break");
140         }
141     }
142
143     let fn_def = match &ctx.function_def {
144         Some(it) => it,
145         None => return,
146     };
147
148     add_keyword(
149         "return",
150         match (can_be_stmt, fn_def.ret_type().is_some()) {
151             (true, true) => "return $0;",
152             (true, false) => "return;",
153             (false, true) => "return $0",
154             (false, false) => "return",
155         },
156     )
157 }
158
159 fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
160     let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
161     item.kind(CompletionItemKind::Keyword);
162
163     match ctx.config.snippet_cap {
164         Some(cap) => {
165             if snippet.ends_with('}') && ctx.incomplete_let {
166                 cov_mark::hit!(let_semi);
167                 item.insert_snippet(cap, format!("{};", snippet));
168             } else {
169                 item.insert_snippet(cap, snippet);
170             }
171         }
172         None => {
173             item.insert_text(if snippet.contains('$') { kw } else { snippet });
174         }
175     };
176     item.add_to(acc);
177 }
178
179 #[cfg(test)]
180 mod tests {
181     use expect_test::{expect, Expect};
182
183     use crate::{
184         tests::{check_edit, filtered_completion_list},
185         CompletionKind,
186     };
187
188     fn check(ra_fixture: &str, expect: Expect) {
189         let actual = filtered_completion_list(ra_fixture, CompletionKind::Keyword);
190         expect.assert_eq(&actual)
191     }
192
193     #[test]
194     fn test_keywords_in_function() {
195         check(
196             r"fn quux() { $0 }",
197             expect![[r#"
198                 kw unsafe
199                 kw fn
200                 kw const
201                 kw type
202                 kw impl
203                 kw extern
204                 kw use
205                 kw trait
206                 kw static
207                 kw mod
208                 kw match
209                 kw while
210                 kw while let
211                 kw loop
212                 kw if
213                 kw if let
214                 kw for
215                 kw true
216                 kw false
217                 kw let
218                 kw return
219                 kw self
220                 kw super
221                 kw crate
222             "#]],
223         );
224     }
225
226     #[test]
227     fn test_keywords_inside_block() {
228         check(
229             r"fn quux() { if true { $0 } }",
230             expect![[r#"
231                 kw unsafe
232                 kw fn
233                 kw const
234                 kw type
235                 kw impl
236                 kw extern
237                 kw use
238                 kw trait
239                 kw static
240                 kw mod
241                 kw match
242                 kw while
243                 kw while let
244                 kw loop
245                 kw if
246                 kw if let
247                 kw for
248                 kw true
249                 kw false
250                 kw let
251                 kw return
252                 kw self
253                 kw super
254                 kw crate
255             "#]],
256         );
257     }
258
259     #[test]
260     fn test_keywords_after_if() {
261         check(
262             r#"fn quux() { if true { () } $0 }"#,
263             expect![[r#"
264                 kw unsafe
265                 kw fn
266                 kw const
267                 kw type
268                 kw impl
269                 kw extern
270                 kw use
271                 kw trait
272                 kw static
273                 kw mod
274                 kw match
275                 kw while
276                 kw while let
277                 kw loop
278                 kw if
279                 kw if let
280                 kw for
281                 kw true
282                 kw false
283                 kw let
284                 kw else
285                 kw else if
286                 kw return
287                 kw self
288                 kw super
289                 kw crate
290             "#]],
291         );
292         check_edit(
293             "else",
294             r#"fn quux() { if true { () } $0 }"#,
295             r#"fn quux() { if true { () } else {
296     $0
297 } }"#,
298         );
299     }
300
301     #[test]
302     fn test_keywords_in_match_arm() {
303         check(
304             r#"
305 fn quux() -> i32 {
306     match () { () => $0 }
307 }
308 "#,
309             expect![[r#"
310                 kw unsafe
311                 kw match
312                 kw while
313                 kw while let
314                 kw loop
315                 kw if
316                 kw if let
317                 kw for
318                 kw true
319                 kw false
320                 kw return
321                 kw self
322                 kw super
323                 kw crate
324             "#]],
325         );
326     }
327
328     #[test]
329     fn test_keywords_in_loop() {
330         check(
331             r"fn my() { loop { $0 } }",
332             expect![[r#"
333                 kw unsafe
334                 kw fn
335                 kw const
336                 kw type
337                 kw impl
338                 kw extern
339                 kw use
340                 kw trait
341                 kw static
342                 kw mod
343                 kw match
344                 kw while
345                 kw while let
346                 kw loop
347                 kw if
348                 kw if let
349                 kw for
350                 kw true
351                 kw false
352                 kw let
353                 kw continue
354                 kw break
355                 kw return
356                 kw self
357                 kw super
358                 kw crate
359             "#]],
360         );
361     }
362
363     #[test]
364     fn test_keywords_after_unsafe_in_block_expr() {
365         check(
366             r"fn my_fn() { unsafe $0 }",
367             expect![[r#"
368                 kw fn
369                 kw trait
370                 kw impl
371             "#]],
372         );
373     }
374
375     #[test]
376     fn no_keyword_completion_in_comments() {
377         cov_mark::check!(no_keyword_completion_in_comments);
378         check(
379             r#"
380 fn test() {
381     let x = 2; // A comment$0
382 }
383 "#,
384             expect![[""]],
385         );
386         check(
387             r#"
388 /*
389 Some multi-line comment$0
390 */
391 "#,
392             expect![[""]],
393         );
394         check(
395             r#"
396 /// Some doc comment
397 /// let test$0 = 1
398 "#,
399             expect![[""]],
400         );
401     }
402
403     #[test]
404     fn test_completion_await_impls_future() {
405         check(
406             r#"
407 //- minicore: future
408 use core::future::*;
409 struct A {}
410 impl Future for A {}
411 fn foo(a: A) { a.$0 }
412 "#,
413             expect![[r#"
414                 kw await expr.await
415             "#]],
416         );
417
418         check(
419             r#"
420 //- minicore: future
421 use std::future::*;
422 fn foo() {
423     let a = async {};
424     a.$0
425 }
426 "#,
427             expect![[r#"
428                 kw await expr.await
429             "#]],
430         )
431     }
432
433     #[test]
434     fn after_let() {
435         check(
436             r#"fn main() { let _ = $0 }"#,
437             expect![[r#"
438                 kw unsafe
439                 kw match
440                 kw while
441                 kw while let
442                 kw loop
443                 kw if
444                 kw if let
445                 kw for
446                 kw true
447                 kw false
448                 kw return
449                 kw self
450                 kw super
451                 kw crate
452             "#]],
453         )
454     }
455
456     #[test]
457     fn skip_struct_initializer() {
458         cov_mark::check!(no_keyword_completion_in_record_lit);
459         check(
460             r#"
461 struct Foo {
462     pub f: i32,
463 }
464 fn foo() {
465     Foo {
466         $0
467     }
468 }
469 "#,
470             expect![[r#""#]],
471         );
472     }
473
474     #[test]
475     fn struct_initializer_field_expr() {
476         check(
477             r#"
478 struct Foo {
479     pub f: i32,
480 }
481 fn foo() {
482     Foo {
483         f: $0
484     }
485 }
486 "#,
487             expect![[r#"
488                 kw unsafe
489                 kw match
490                 kw while
491                 kw while let
492                 kw loop
493                 kw if
494                 kw if let
495                 kw for
496                 kw true
497                 kw false
498                 kw return
499                 kw self
500                 kw super
501                 kw crate
502             "#]],
503         );
504     }
505
506     #[test]
507     fn let_semi() {
508         cov_mark::check!(let_semi);
509         check_edit(
510             "match",
511             r#"
512 fn main() { let x = $0 }
513 "#,
514             r#"
515 fn main() { let x = match $1 {
516     $0
517 }; }
518 "#,
519         );
520
521         check_edit(
522             "if",
523             r#"
524 fn main() {
525     let x = $0
526     let y = 92;
527 }
528 "#,
529             r#"
530 fn main() {
531     let x = if $1 {
532     $0
533 };
534     let y = 92;
535 }
536 "#,
537         );
538
539         check_edit(
540             "loop",
541             r#"
542 fn main() {
543     let x = $0
544     bar();
545 }
546 "#,
547             r#"
548 fn main() {
549     let x = loop {
550     $0
551 };
552     bar();
553 }
554 "#,
555         );
556     }
557 }