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, PathKind},
9 patterns::ImmediateLocation,
10 CompletionContext, CompletionItem, CompletionItemKind, Completions,
13 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
14 if ctx.token.kind() == SyntaxKind::COMMENT {
15 cov_mark::hit!(no_keyword_completion_in_comments);
18 if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
19 cov_mark::hit!(no_keyword_completion_in_record_lit);
22 if ctx.attribute_under_caret.is_some() {
23 cov_mark::hit!(no_keyword_completion_in_attr_of_expr);
26 if ctx.is_non_trivial_path() {
27 cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
31 let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
33 let expects_assoc_item = ctx.expects_assoc_item();
34 let has_block_expr_parent = ctx.has_block_expr_parent();
35 let expects_item = ctx.expects_item();
37 if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
39 cov_mark::hit!(kw_completion_in);
40 add_keyword("in", "in");
44 if ctx.has_impl_or_trait_prev_sibling() {
45 add_keyword("where", "where");
46 if ctx.has_impl_prev_sibling() {
47 add_keyword("for", "for");
51 if ctx.previous_token_is(T![unsafe]) {
52 if expects_item || expects_assoc_item || has_block_expr_parent {
53 add_keyword("fn", "fn $1($2) {\n $0\n}")
56 if expects_item || has_block_expr_parent {
57 add_keyword("trait", "trait $1 {\n $0\n}");
58 add_keyword("impl", "impl $1 {\n $0\n}");
64 if !ctx.has_visibility_prev_sibling()
65 && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
67 add_keyword("pub(crate)", "pub(crate)");
68 add_keyword("pub(super)", "pub(super)");
69 add_keyword("pub", "pub");
72 if expects_item || expects_assoc_item || has_block_expr_parent {
73 add_keyword("unsafe", "unsafe");
74 add_keyword("fn", "fn $1($2) {\n $0\n}");
75 add_keyword("const", "const $0");
76 add_keyword("type", "type $0");
79 if expects_item || has_block_expr_parent {
80 if !ctx.has_visibility_prev_sibling() {
81 add_keyword("impl", "impl $1 {\n $0\n}");
82 add_keyword("extern", "extern $0");
84 add_keyword("use", "use $0");
85 add_keyword("trait", "trait $1 {\n $0\n}");
86 add_keyword("static", "static $0");
87 add_keyword("mod", "mod $0");
91 add_keyword("enum", "enum $1 {\n $0\n}");
92 add_keyword("struct", "struct $0");
93 add_keyword("union", "union $1 {\n $0\n}");
96 if ctx.expects_type() {
100 if ctx.expects_expression() {
101 if !has_block_expr_parent {
102 add_keyword("unsafe", "unsafe {\n $0\n}");
104 add_keyword("match", "match $1 {\n $0\n}");
105 add_keyword("while", "while $1 {\n $0\n}");
106 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
107 add_keyword("loop", "loop {\n $0\n}");
108 add_keyword("if", "if $1 {\n $0\n}");
109 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
110 add_keyword("for", "for $1 in $2 {\n $0\n}");
111 add_keyword("true", "true");
112 add_keyword("false", "false");
115 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
116 add_keyword("let", "let");
120 add_keyword("else", "else {\n $0\n}");
121 add_keyword("else if", "else if $1 {\n $0\n}");
124 if ctx.expects_ident_pat_or_ref_expr() {
125 add_keyword("mut", "mut ");
128 let (can_be_stmt, in_loop_body) = match ctx.path_context {
129 Some(PathCompletionContext {
130 is_trivial_path: true, can_be_stmt, in_loop_body, ..
131 }) => (can_be_stmt, in_loop_body),
137 add_keyword("continue", "continue;");
138 add_keyword("break", "break;");
140 add_keyword("continue", "continue");
141 add_keyword("break", "break");
145 let fn_def = match &ctx.function_def {
152 match (can_be_stmt, fn_def.ret_type().is_some()) {
153 (true, true) => "return $0;",
154 (true, false) => "return;",
155 (false, true) => "return $0",
156 (false, false) => "return",
161 fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
162 let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
164 match ctx.config.snippet_cap {
166 if snippet.ends_with('}') && ctx.incomplete_let {
167 cov_mark::hit!(let_semi);
168 item.insert_snippet(cap, format!("{};", snippet));
170 item.insert_snippet(cap, snippet);
174 item.insert_text(if snippet.contains('$') { kw } else { snippet });
182 use expect_test::{expect, Expect};
184 use crate::tests::{check_edit, completion_list};
186 fn check(ra_fixture: &str, expect: Expect) {
187 let actual = completion_list(ra_fixture);
188 expect.assert_eq(&actual)
192 fn test_else_edit_after_if() {
195 r#"fn quux() { if true { () } $0 }"#,
196 r#"fn quux() { if true { () } else {
203 fn test_keywords_after_unsafe_in_block_expr() {
205 r"fn my_fn() { unsafe $0 }",
217 fn test_completion_await_impls_future() {
224 fn foo(a: A) { a.$0 }
230 sn match match expr {}
231 sn box Box::new(expr)
237 sn call function(expr)
256 sn match match expr {}
257 sn box Box::new(expr)
263 sn call function(expr)
272 cov_mark::check!(let_semi);
276 fn main() { let x = $0 }
279 fn main() { let x = match $1 {