]> git.lizzy.rs Git - rust.git/blob - crates/ide-completion/src/completions/keyword.rs
More precise completion filtering
[rust.git] / crates / ide-completion / src / completions / keyword.rs
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.
4
5 use syntax::T;
6
7 use crate::{
8     context::{NameRefContext, PathKind},
9     CompletionContext, CompletionItem, CompletionItemKind, Completions,
10 };
11
12 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
13     if matches!(ctx.nameref_ctx(), Some(NameRefContext { record_expr: Some(_), .. })) {
14         cov_mark::hit!(no_keyword_completion_in_record_lit);
15         return;
16     }
17     if ctx.is_non_trivial_path() {
18         cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
19         return;
20     }
21     if ctx.pattern_ctx.is_some() {
22         return;
23     }
24
25     let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
26
27     let expects_assoc_item = ctx.expects_assoc_item();
28     let has_block_expr_parent = ctx.has_block_expr_parent();
29     let expects_item = ctx.expects_item();
30
31     if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
32         return;
33     }
34     if ctx.has_unfinished_impl_or_trait_prev_sibling() {
35         add_keyword("where", "where");
36         if ctx.has_impl_prev_sibling() {
37             add_keyword("for", "for");
38         }
39         return;
40     }
41     if ctx.previous_token_is(T![unsafe]) {
42         if expects_item || expects_assoc_item || has_block_expr_parent {
43             add_keyword("fn", "fn $1($2) {\n    $0\n}")
44         }
45
46         if expects_item || has_block_expr_parent {
47             add_keyword("trait", "trait $1 {\n    $0\n}");
48             add_keyword("impl", "impl $1 {\n    $0\n}");
49         }
50
51         return;
52     }
53
54     if ctx.qualifier_ctx.vis_node.is_none()
55         && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
56     {
57         add_keyword("pub(crate)", "pub(crate)");
58         add_keyword("pub(super)", "pub(super)");
59         add_keyword("pub", "pub");
60     }
61
62     if expects_item || expects_assoc_item || has_block_expr_parent {
63         add_keyword("unsafe", "unsafe");
64         add_keyword("fn", "fn $1($2) {\n    $0\n}");
65         add_keyword("const", "const $0");
66         add_keyword("type", "type $0");
67     }
68
69     if expects_item || has_block_expr_parent {
70         if ctx.qualifier_ctx.vis_node.is_none() {
71             add_keyword("impl", "impl $1 {\n    $0\n}");
72             add_keyword("extern", "extern $0");
73         }
74         add_keyword("use", "use $0");
75         add_keyword("trait", "trait $1 {\n    $0\n}");
76         add_keyword("static", "static $0");
77         add_keyword("mod", "mod $0");
78     }
79
80     if expects_item || has_block_expr_parent {
81         add_keyword("enum", "enum $1 {\n    $0\n}");
82         add_keyword("struct", "struct $0");
83         add_keyword("union", "union $1 {\n    $0\n}");
84     }
85 }
86
87 pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
88     let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
89
90     match ctx.config.snippet_cap {
91         Some(cap) => {
92             if snippet.ends_with('}') && ctx.incomplete_let {
93                 // complete block expression snippets with a trailing semicolon, if inside an incomplete let
94                 cov_mark::hit!(let_semi);
95                 item.insert_snippet(cap, format!("{};", snippet));
96             } else {
97                 item.insert_snippet(cap, snippet);
98             }
99         }
100         None => {
101             item.insert_text(if snippet.contains('$') { kw } else { snippet });
102         }
103     };
104     item.add_to(acc);
105 }
106
107 #[cfg(test)]
108 mod tests {
109     use expect_test::{expect, Expect};
110
111     use crate::tests::{check_edit, completion_list};
112
113     fn check(ra_fixture: &str, expect: Expect) {
114         let actual = completion_list(ra_fixture);
115         expect.assert_eq(&actual)
116     }
117
118     #[test]
119     fn test_else_edit_after_if() {
120         check_edit(
121             "else",
122             r#"fn quux() { if true { () } $0 }"#,
123             r#"fn quux() { if true { () } else {
124     $0
125 } }"#,
126         );
127     }
128
129     #[test]
130     fn test_keywords_after_unsafe_in_block_expr() {
131         check(
132             r"fn my_fn() { unsafe $0 }",
133             expect![[r#"
134                 kw fn
135                 kw impl
136                 kw trait
137                 sn pd
138                 sn ppd
139             "#]],
140         );
141     }
142
143     #[test]
144     fn test_completion_await_impls_future() {
145         check(
146             r#"
147 //- minicore: future
148 use core::future::*;
149 struct A {}
150 impl Future for A {}
151 fn foo(a: A) { a.$0 }
152 "#,
153             expect![[r#"
154                 kw await expr.await
155                 sn box   Box::new(expr)
156                 sn call  function(expr)
157                 sn dbg   dbg!(expr)
158                 sn dbgr  dbg!(&expr)
159                 sn let   let
160                 sn letm  let mut
161                 sn match match expr {}
162                 sn ref   &expr
163                 sn refm  &mut expr
164             "#]],
165         );
166
167         check(
168             r#"
169 //- minicore: future
170 use std::future::*;
171 fn foo() {
172     let a = async {};
173     a.$0
174 }
175 "#,
176             expect![[r#"
177                 kw await expr.await
178                 sn box   Box::new(expr)
179                 sn call  function(expr)
180                 sn dbg   dbg!(expr)
181                 sn dbgr  dbg!(&expr)
182                 sn let   let
183                 sn letm  let mut
184                 sn match match expr {}
185                 sn ref   &expr
186                 sn refm  &mut expr
187             "#]],
188         )
189     }
190
191     #[test]
192     fn let_semi() {
193         cov_mark::check!(let_semi);
194         check_edit(
195             "match",
196             r#"
197 fn main() { let x = $0 }
198 "#,
199             r#"
200 fn main() { let x = match $1 {
201     $0
202 }; }
203 "#,
204         );
205
206         check_edit(
207             "if",
208             r#"
209 fn main() {
210     let x = $0
211     let y = 92;
212 }
213 "#,
214             r#"
215 fn main() {
216     let x = if $1 {
217     $0
218 };
219     let y = 92;
220 }
221 "#,
222         );
223
224         check_edit(
225             "loop",
226             r#"
227 fn main() {
228     let x = $0
229     bar();
230 }
231 "#,
232             r#"
233 fn main() {
234     let x = loop {
235     $0
236 };
237     bar();
238 }
239 "#,
240         );
241     }
242 }