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.rs527
1 files changed, 527 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..95e4ff1ac
--- /dev/null
+++ b/crates/ide/src/completion/complete_keyword.rs
@@ -0,0 +1,527 @@
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
14 if ctx.use_item_syntax.is_some() {
15 if ctx.path_qual.is_none() {
16 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
17 .kind(CompletionItemKind::Keyword)
18 .insert_text("crate::")
19 .add_to(acc);
20 }
21 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
22 .kind(CompletionItemKind::Keyword)
23 .add_to(acc);
24 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
25 .kind(CompletionItemKind::Keyword)
26 .insert_text("super::")
27 .add_to(acc);
28 }
29
30 // Suggest .await syntax for types that implement Future trait
31 if let Some(receiver) = &ctx.dot_receiver {
32 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
33 if ty.impls_future(ctx.db) {
34 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
35 .kind(CompletionItemKind::Keyword)
36 .detail("expr.await")
37 .insert_text("await")
38 .add_to(acc);
39 }
40 };
41 }
42}
43
44pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
45 if ctx.token.kind() == SyntaxKind::COMMENT {
46 mark::hit!(no_keyword_completion_in_comments);
47 return;
48 }
49
50 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
51 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
52 add_keyword(ctx, acc, "where", "where ");
53 return;
54 }
55 if ctx.unsafe_is_prev {
56 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
57 add_keyword(ctx, acc, "fn", "fn $0() {}")
58 }
59
60 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
61 add_keyword(ctx, acc, "trait", "trait $0 {}");
62 add_keyword(ctx, acc, "impl", "impl $0 {}");
63 }
64
65 return;
66 }
67 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
68 {
69 add_keyword(ctx, acc, "fn", "fn $0() {}");
70 }
71 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
72 add_keyword(ctx, acc, "use", "use ");
73 add_keyword(ctx, acc, "impl", "impl $0 {}");
74 add_keyword(ctx, acc, "trait", "trait $0 {}");
75 }
76
77 if ctx.has_item_list_or_source_file_parent {
78 add_keyword(ctx, acc, "enum", "enum $0 {}");
79 add_keyword(ctx, acc, "struct", "struct $0");
80 add_keyword(ctx, acc, "union", "union $0 {}");
81 }
82
83 if ctx.is_expr {
84 add_keyword(ctx, acc, "match", "match $0 {}");
85 add_keyword(ctx, acc, "while", "while $0 {}");
86 add_keyword(ctx, acc, "loop", "loop {$0}");
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
90
91 if ctx.if_is_prev || ctx.block_expr_parent {
92 add_keyword(ctx, acc, "let", "let ");
93 }
94
95 if ctx.after_if {
96 add_keyword(ctx, acc, "else", "else {$0}");
97 add_keyword(ctx, acc, "else if", "else if $0 {}");
98 }
99 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
100 add_keyword(ctx, acc, "mod", "mod $0 {}");
101 }
102 if ctx.bind_pat_parent || ctx.ref_pat_parent {
103 add_keyword(ctx, acc, "mut", "mut ");
104 }
105 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
106 {
107 add_keyword(ctx, acc, "const", "const ");
108 add_keyword(ctx, acc, "type", "type ");
109 }
110 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
111 add_keyword(ctx, acc, "static", "static ");
112 };
113 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
114 add_keyword(ctx, acc, "extern", "extern ");
115 }
116 if ctx.has_item_list_or_source_file_parent
117 || has_trait_or_impl_parent
118 || ctx.block_expr_parent
119 || ctx.is_match_arm
120 {
121 add_keyword(ctx, acc, "unsafe", "unsafe ");
122 }
123 if ctx.in_loop_body {
124 if ctx.can_be_stmt {
125 add_keyword(ctx, acc, "continue", "continue;");
126 add_keyword(ctx, acc, "break", "break;");
127 } else {
128 add_keyword(ctx, acc, "continue", "continue");
129 add_keyword(ctx, acc, "break", "break");
130 }
131 }
132 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent {
133 add_keyword(ctx, acc, "pub", "pub ")
134 }
135
136 if !ctx.is_trivial_path {
137 return;
138 }
139 let fn_def = match &ctx.function_syntax {
140 Some(it) => it,
141 None => return,
142 };
143 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
144}
145
146fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
147 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
148 .kind(CompletionItemKind::Keyword);
149
150 match ctx.config.snippet_cap {
151 Some(cap) => res.insert_snippet(cap, snippet),
152 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
153 }
154 .build()
155}
156
157fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
158 acc.add(keyword(ctx, kw, snippet));
159}
160
161fn complete_return(
162 ctx: &CompletionContext,
163 fn_def: &ast::Fn,
164 can_be_stmt: bool,
165) -> Option<CompletionItem> {
166 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
167 (true, true) => "return $0;",
168 (true, false) => "return;",
169 (false, true) => "return $0",
170 (false, false) => "return",
171 };
172 Some(keyword(ctx, "return", snip))
173}
174
175#[cfg(test)]
176mod tests {
177 use expect_test::{expect, Expect};
178
179 use crate::completion::{
180 test_utils::{check_edit, completion_list},
181 CompletionKind,
182 };
183 use test_utils::mark;
184
185 fn check(ra_fixture: &str, expect: Expect) {
186 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
187 expect.assert_eq(&actual)
188 }
189
190 #[test]
191 fn test_keywords_in_use_stmt() {
192 check(
193 r"use <|>",
194 expect![[r#"
195 kw crate::
196 kw self
197 kw super::
198 "#]],
199 );
200
201 check(
202 r"use a::<|>",
203 expect![[r#"
204 kw self
205 kw super::
206 "#]],
207 );
208
209 check(
210 r"use a::{b, <|>}",
211 expect![[r#"
212 kw self
213 kw super::
214 "#]],
215 );
216 }
217
218 #[test]
219 fn test_keywords_at_source_file_level() {
220 check(
221 r"m<|>",
222 expect![[r#"
223 kw const
224 kw enum
225 kw extern
226 kw fn
227 kw impl
228 kw mod
229 kw pub
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 type
368 kw unsafe
369 "#]],
370 );
371 }
372
373 #[test]
374 fn test_keywords_in_loop() {
375 check(
376 r"fn my() { loop { <|> } }",
377 expect![[r#"
378 kw break
379 kw const
380 kw continue
381 kw extern
382 kw fn
383 kw if
384 kw if let
385 kw impl
386 kw let
387 kw loop
388 kw match
389 kw mod
390 kw return
391 kw static
392 kw trait
393 kw type
394 kw unsafe
395 kw use
396 kw while
397 "#]],
398 );
399 }
400
401 #[test]
402 fn test_keywords_after_unsafe_in_item_list() {
403 check(
404 r"unsafe <|>",
405 expect![[r#"
406 kw fn
407 kw impl
408 kw trait
409 "#]],
410 );
411 }
412
413 #[test]
414 fn test_keywords_after_unsafe_in_block_expr() {
415 check(
416 r"fn my_fn() { unsafe <|> }",
417 expect![[r#"
418 kw fn
419 kw impl
420 kw trait
421 "#]],
422 );
423 }
424
425 #[test]
426 fn test_mut_in_ref_and_in_fn_parameters_list() {
427 check(
428 r"fn my_fn(&<|>) {}",
429 expect![[r#"
430 kw mut
431 "#]],
432 );
433 check(
434 r"fn my_fn(<|>) {}",
435 expect![[r#"
436 kw mut
437 "#]],
438 );
439 check(
440 r"fn my_fn() { let &<|> }",
441 expect![[r#"
442 kw mut
443 "#]],
444 );
445 }
446
447 #[test]
448 fn test_where_keyword() {
449 check(
450 r"trait A <|>",
451 expect![[r#"
452 kw where
453 "#]],
454 );
455 check(
456 r"impl A <|>",
457 expect![[r#"
458 kw where
459 "#]],
460 );
461 }
462
463 #[test]
464 fn no_keyword_completion_in_comments() {
465 mark::check!(no_keyword_completion_in_comments);
466 check(
467 r#"
468fn test() {
469 let x = 2; // A comment<|>
470}
471"#,
472 expect![[""]],
473 );
474 check(
475 r#"
476/*
477Some multi-line comment<|>
478*/
479"#,
480 expect![[""]],
481 );
482 check(
483 r#"
484/// Some doc comment
485/// let test<|> = 1
486"#,
487 expect![[""]],
488 );
489 }
490
491 #[test]
492 fn test_completion_await_impls_future() {
493 check(
494 r#"
495//- /main.rs
496use std::future::*;
497struct A {}
498impl Future for A {}
499fn foo(a: A) { a.<|> }
500
501//- /std/lib.rs
502pub mod future {
503 #[lang = "future_trait"]
504 pub trait Future {}
505}
506"#,
507 expect![[r#"
508 kw await expr.await
509 "#]],
510 )
511 }
512
513 #[test]
514 fn after_let() {
515 check(
516 r#"fn main() { let _ = <|> }"#,
517 expect![[r#"
518 kw if
519 kw if let
520 kw loop
521 kw match
522 kw return
523 kw while
524 "#]],
525 )
526 }
527}