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, Completions,
10 };
11 
complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext)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(acc, ctx, 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(super)", "pub(super)");
68         add_keyword("pub", "pub");
69     }
70 
71     if expects_item || expects_assoc_item || has_block_expr_parent {
72         add_keyword("unsafe", "unsafe");
73         add_keyword("fn", "fn $1($2) {\n    $0\n}");
74         add_keyword("const", "const $0");
75         add_keyword("type", "type $0");
76     }
77 
78     if expects_item || has_block_expr_parent {
79         if !ctx.has_visibility_prev_sibling() {
80             add_keyword("impl", "impl $1 {\n    $0\n}");
81             add_keyword("extern", "extern $0");
82         }
83         add_keyword("use", "use $0");
84         add_keyword("trait", "trait $1 {\n    $0\n}");
85         add_keyword("static", "static $0");
86         add_keyword("mod", "mod $0");
87     }
88 
89     if expects_item {
90         add_keyword("enum", "enum $1 {\n    $0\n}");
91         add_keyword("struct", "struct $0");
92         add_keyword("union", "union $1 {\n    $0\n}");
93     }
94 
95     if ctx.expects_type() {
96         return;
97     }
98 
99     if ctx.expects_expression() {
100         if !has_block_expr_parent {
101             add_keyword("unsafe", "unsafe {\n    $0\n}");
102         }
103         add_keyword("match", "match $1 {\n    $0\n}");
104         add_keyword("while", "while $1 {\n    $0\n}");
105         add_keyword("while let", "while let $1 = $2 {\n    $0\n}");
106         add_keyword("loop", "loop {\n    $0\n}");
107         add_keyword("if", "if $1 {\n    $0\n}");
108         add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
109         add_keyword("for", "for $1 in $2 {\n    $0\n}");
110         add_keyword("true", "true");
111         add_keyword("false", "false");
112     }
113 
114     if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
115         add_keyword("let", "let");
116     }
117 
118     if ctx.after_if() {
119         add_keyword("else", "else {\n    $0\n}");
120         add_keyword("else if", "else if $1 {\n    $0\n}");
121     }
122 
123     if ctx.expects_ident_pat_or_ref_expr() {
124         add_keyword("mut", "mut ");
125     }
126 
127     let (can_be_stmt, in_loop_body) = match ctx.path_context {
128         Some(PathCompletionContext {
129             is_trivial_path: true, can_be_stmt, in_loop_body, ..
130         }) => (can_be_stmt, in_loop_body),
131         _ => return,
132     };
133 
134     if in_loop_body {
135         if can_be_stmt {
136             add_keyword("continue", "continue;");
137             add_keyword("break", "break;");
138         } else {
139             add_keyword("continue", "continue");
140             add_keyword("break", "break");
141         }
142     }
143 
144     let fn_def = match &ctx.function_def {
145         Some(it) => it,
146         None => return,
147     };
148 
149     add_keyword(
150         "return",
151         match (can_be_stmt, fn_def.ret_type().is_some()) {
152             (true, true) => "return $0;",
153             (true, false) => "return;",
154             (false, true) => "return $0",
155             (false, false) => "return",
156         },
157     )
158 }
159 
add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str)160 fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
161     let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
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::tests::{check_edit, completion_list};
184 
check(ra_fixture: &str, expect: Expect)185     fn check(ra_fixture: &str, expect: Expect) {
186         let actual = completion_list(ra_fixture);
187         expect.assert_eq(&actual)
188     }
189 
190     #[test]
test_else_edit_after_if()191     fn test_else_edit_after_if() {
192         check_edit(
193             "else",
194             r#"fn quux() { if true { () } $0 }"#,
195             r#"fn quux() { if true { () } else {
196     $0
197 } }"#,
198         );
199     }
200 
201     #[test]
test_keywords_after_unsafe_in_block_expr()202     fn test_keywords_after_unsafe_in_block_expr() {
203         check(
204             r"fn my_fn() { unsafe $0 }",
205             expect![[r#"
206                 kw fn
207                 kw trait
208                 kw impl
209                 sn pd
210                 sn ppd
211             "#]],
212         );
213     }
214 
215     #[test]
test_completion_await_impls_future()216     fn test_completion_await_impls_future() {
217         check(
218             r#"
219 //- minicore: future
220 use core::future::*;
221 struct A {}
222 impl Future for A {}
223 fn foo(a: A) { a.$0 }
224 "#,
225             expect![[r#"
226                 kw await expr.await
227                 sn ref   &expr
228                 sn refm  &mut expr
229                 sn match match expr {}
230                 sn box   Box::new(expr)
231                 sn ok    Ok(expr)
232                 sn err   Err(expr)
233                 sn some  Some(expr)
234                 sn dbg   dbg!(expr)
235                 sn dbgr  dbg!(&expr)
236                 sn call  function(expr)
237                 sn let   let
238                 sn letm  let mut
239             "#]],
240         );
241 
242         check(
243             r#"
244 //- minicore: future
245 use std::future::*;
246 fn foo() {
247     let a = async {};
248     a.$0
249 }
250 "#,
251             expect![[r#"
252                 kw await expr.await
253                 sn ref   &expr
254                 sn refm  &mut expr
255                 sn match match expr {}
256                 sn box   Box::new(expr)
257                 sn ok    Ok(expr)
258                 sn err   Err(expr)
259                 sn some  Some(expr)
260                 sn dbg   dbg!(expr)
261                 sn dbgr  dbg!(&expr)
262                 sn call  function(expr)
263                 sn let   let
264                 sn letm  let mut
265             "#]],
266         )
267     }
268 
269     #[test]
let_semi()270     fn let_semi() {
271         cov_mark::check!(let_semi);
272         check_edit(
273             "match",
274             r#"
275 fn main() { let x = $0 }
276 "#,
277             r#"
278 fn main() { let x = match $1 {
279     $0
280 }; }
281 "#,
282         );
283 
284         check_edit(
285             "if",
286             r#"
287 fn main() {
288     let x = $0
289     let y = 92;
290 }
291 "#,
292             r#"
293 fn main() {
294     let x = if $1 {
295     $0
296 };
297     let y = 92;
298 }
299 "#,
300         );
301 
302         check_edit(
303             "loop",
304             r#"
305 fn main() {
306     let x = $0
307     bar();
308 }
309 "#,
310             r#"
311 fn main() {
312     let x = loop {
313     $0
314 };
315     bar();
316 }
317 "#,
318         );
319     }
320 }
321