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.
5 use syntax::{SyntaxKind, T};
8 context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9 CompletionItemKind, CompletionKind, Completions,
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);
17 if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
18 cov_mark::hit!(no_keyword_completion_in_record_lit);
21 if ctx.attribute_under_caret.is_some() {
22 cov_mark::hit!(no_keyword_completion_in_attr_of_expr);
25 if ctx.is_non_trivial_path() {
26 cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
30 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
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();
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");
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");
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}")
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}");
63 if !ctx.has_visibility_prev_sibling()
64 && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
66 add_keyword("pub(crate)", "pub(crate)");
67 add_keyword("pub", "pub");
70 if expects_item || expects_assoc_item || has_block_expr_parent {
71 add_keyword("unsafe", "unsafe");
72 add_keyword("fn", "fn $1($2) {\n $0\n}");
73 add_keyword("const", "const $0");
74 add_keyword("type", "type $0");
77 if expects_item || has_block_expr_parent {
78 if !ctx.has_visibility_prev_sibling() {
79 add_keyword("impl", "impl $1 {\n $0\n}");
80 add_keyword("extern", "extern $0");
82 add_keyword("use", "use $0");
83 add_keyword("trait", "trait $1 {\n $0\n}");
84 add_keyword("static", "static $0");
85 add_keyword("mod", "mod $0");
89 add_keyword("enum", "enum $1 {\n $0\n}");
90 add_keyword("struct", "struct $0");
91 add_keyword("union", "union $1 {\n $0\n}");
94 if ctx.expects_type() {
98 if ctx.expects_expression() {
99 if !has_block_expr_parent {
100 add_keyword("unsafe", "unsafe {\n $0\n}");
102 add_keyword("match", "match $1 {\n $0\n}");
103 add_keyword("while", "while $1 {\n $0\n}");
104 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
105 add_keyword("loop", "loop {\n $0\n}");
106 add_keyword("if", "if $1 {\n $0\n}");
107 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
108 add_keyword("for", "for $1 in $2 {\n $0\n}");
109 add_keyword("true", "true");
110 add_keyword("false", "false");
113 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
114 add_keyword("let", "let");
118 add_keyword("else", "else {\n $0\n}");
119 add_keyword("else if", "else if $1 {\n $0\n}");
122 if ctx.expects_ident_pat_or_ref_expr() {
123 add_keyword("mut", "mut ");
126 let (can_be_stmt, in_loop_body) = match ctx.path_context {
127 Some(PathCompletionContext {
128 is_trivial_path: true, can_be_stmt, in_loop_body, ..
129 }) => (can_be_stmt, in_loop_body),
135 add_keyword("continue", "continue;");
136 add_keyword("break", "break;");
138 add_keyword("continue", "continue");
139 add_keyword("break", "break");
143 let fn_def = match &ctx.function_def {
150 match (can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;",
152 (true, false) => "return;",
153 (false, true) => "return $0",
154 (false, false) => "return",
159 fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
160 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
161 item.kind(CompletionItemKind::Keyword);
163 match ctx.config.snippet_cap {
165 if snippet.ends_with('}') && ctx.incomplete_let {
166 cov_mark::hit!(let_semi);
167 item.insert_snippet(cap, format!("{};", snippet));
169 item.insert_snippet(cap, snippet);
173 item.insert_text(if snippet.contains('$') { kw } else { snippet });
181 use expect_test::{expect, Expect};
184 tests::{check_edit, filtered_completion_list},
188 fn check(ra_fixture: &str, expect: Expect) {
189 let actual = filtered_completion_list(ra_fixture, CompletionKind::Keyword);
190 expect.assert_eq(&actual)
194 fn test_keywords_in_function() {
227 fn test_keywords_inside_block() {
229 r"fn quux() { if true { $0 } }",
260 fn test_keywords_after_if() {
262 r#"fn quux() { if true { () } $0 }"#,
294 r#"fn quux() { if true { () } $0 }"#,
295 r#"fn quux() { if true { () } else {
302 fn test_keywords_in_match_arm() {
306 match () { () => $0 }
329 fn test_keywords_in_loop() {
331 r"fn my() { loop { $0 } }",
364 fn test_keywords_after_unsafe_in_block_expr() {
366 r"fn my_fn() { unsafe $0 }",
376 fn no_keyword_completion_in_comments() {
377 cov_mark::check!(no_keyword_completion_in_comments);
381 let x = 2; // A comment$0
389 Some multi-line comment$0
404 fn test_completion_await_impls_future() {
411 fn foo(a: A) { a.$0 }
436 r#"fn main() { let _ = $0 }"#,
457 fn skip_struct_initializer() {
458 cov_mark::check!(no_keyword_completion_in_record_lit);
475 fn struct_initializer_field_expr() {
508 cov_mark::check!(let_semi);
512 fn main() { let x = $0 }
515 fn main() { let x = match $1 {