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