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