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