aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/completion/complete_keyword.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-08 19:48:48 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-08 19:48:48 +0000
commit46f74e33ca53a7897e9020d3de75cc76a6b89d79 (patch)
tree2bc001c8ecf58b49ac9a0da1f20d5644ce29fb3a /crates/ra_ide_api/src/completion/complete_keyword.rs
parent4f4f7933b1b7ff34f8633b1686b18b2d1b994c47 (diff)
parent0c62b1bb7a49bf527780ce1f8cade5eb4fbfdb2d (diff)
Merge #471
471: rename crates to match reality r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_ide_api/src/completion/complete_keyword.rs')
-rw-r--r--crates/ra_ide_api/src/completion/complete_keyword.rs339
1 files changed, 339 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_keyword.rs b/crates/ra_ide_api/src/completion/complete_keyword.rs
new file mode 100644
index 000000000..d350f06ce
--- /dev/null
+++ b/crates/ra_ide_api/src/completion/complete_keyword.rs
@@ -0,0 +1,339 @@
1use ra_syntax::{
2 algo::visit::{visitor, Visitor},
3 AstNode,
4 ast::{self, LoopBodyOwner},
5 SyntaxKind::*, SyntaxNode,
6};
7
8use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};
9
10pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 // complete keyword "crate" in use stmt
12 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
13 (Some(_), None) => {
14 CompletionItem::new(CompletionKind::Keyword, "crate")
15 .kind(CompletionItemKind::Keyword)
16 .lookup_by("crate")
17 .snippet("crate::")
18 .add_to(acc);
19 CompletionItem::new(CompletionKind::Keyword, "self")
20 .kind(CompletionItemKind::Keyword)
21 .lookup_by("self")
22 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, "super")
24 .kind(CompletionItemKind::Keyword)
25 .lookup_by("super")
26 .add_to(acc);
27 }
28 (Some(_), Some(_)) => {
29 CompletionItem::new(CompletionKind::Keyword, "self")
30 .kind(CompletionItemKind::Keyword)
31 .lookup_by("self")
32 .add_to(acc);
33 CompletionItem::new(CompletionKind::Keyword, "super")
34 .kind(CompletionItemKind::Keyword)
35 .lookup_by("super")
36 .add_to(acc);
37 }
38 _ => {}
39 }
40}
41
42fn keyword(kw: &str, snippet: &str) -> CompletionItem {
43 CompletionItem::new(CompletionKind::Keyword, kw)
44 .kind(CompletionItemKind::Keyword)
45 .snippet(snippet)
46 .build()
47}
48
49pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
50 if !ctx.is_trivial_path {
51 return;
52 }
53
54 let fn_def = match ctx.function_syntax {
55 Some(it) => it,
56 None => return,
57 };
58 acc.add(keyword("if", "if $0 {}"));
59 acc.add(keyword("match", "match $0 {}"));
60 acc.add(keyword("while", "while $0 {}"));
61 acc.add(keyword("loop", "loop {$0}"));
62
63 if ctx.after_if {
64 acc.add(keyword("else", "else {$0}"));
65 acc.add(keyword("else if", "else if $0 {}"));
66 }
67 if is_in_loop_body(ctx.leaf) {
68 if ctx.can_be_stmt {
69 acc.add(keyword("continue", "continue;"));
70 acc.add(keyword("break", "break;"));
71 } else {
72 acc.add(keyword("continue", "continue"));
73 acc.add(keyword("break", "break"));
74 }
75 }
76 acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
77}
78
79fn is_in_loop_body(leaf: &SyntaxNode) -> bool {
80 for node in leaf.ancestors() {
81 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
82 break;
83 }
84 let loop_body = visitor()
85 .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
86 .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
87 .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
88 .accept(node);
89 if let Some(Some(body)) = loop_body {
90 if leaf.range().is_subrange(&body.syntax().range()) {
91 return true;
92 }
93 }
94 }
95 false
96}
97
98fn complete_return(fn_def: &ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
99 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
100 (true, true) => "return $0;",
101 (true, false) => "return;",
102 (false, true) => "return $0",
103 (false, false) => "return",
104 };
105 Some(keyword("return", snip))
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::completion::{CompletionKind, check_completion};
111 fn check_keyword_completion(code: &str, expected_completions: &str) {
112 check_completion(code, expected_completions, CompletionKind::Keyword);
113 }
114
115 #[test]
116 fn completes_keywords_in_use_stmt() {
117 check_keyword_completion(
118 r"
119 use <|>
120 ",
121 r#"
122 crate "crate" "crate::"
123 self "self"
124 super "super"
125 "#,
126 );
127
128 check_keyword_completion(
129 r"
130 use a::<|>
131 ",
132 r#"
133 self "self"
134 super "super"
135 "#,
136 );
137
138 check_keyword_completion(
139 r"
140 use a::{b, <|>}
141 ",
142 r#"
143 self "self"
144 super "super"
145 "#,
146 );
147 }
148
149 #[test]
150 fn completes_various_keywords_in_function() {
151 check_keyword_completion(
152 r"
153 fn quux() {
154 <|>
155 }
156 ",
157 r#"
158 if "if $0 {}"
159 match "match $0 {}"
160 while "while $0 {}"
161 loop "loop {$0}"
162 return "return;"
163 "#,
164 );
165 }
166
167 #[test]
168 fn completes_else_after_if() {
169 check_keyword_completion(
170 r"
171 fn quux() {
172 if true {
173 ()
174 } <|>
175 }
176 ",
177 r#"
178 if "if $0 {}"
179 match "match $0 {}"
180 while "while $0 {}"
181 loop "loop {$0}"
182 else "else {$0}"
183 else if "else if $0 {}"
184 return "return;"
185 "#,
186 );
187 }
188
189 #[test]
190 fn test_completion_return_value() {
191 check_keyword_completion(
192 r"
193 fn quux() -> i32 {
194 <|>
195 92
196 }
197 ",
198 r#"
199 if "if $0 {}"
200 match "match $0 {}"
201 while "while $0 {}"
202 loop "loop {$0}"
203 return "return $0;"
204 "#,
205 );
206 check_keyword_completion(
207 r"
208 fn quux() {
209 <|>
210 92
211 }
212 ",
213 r#"
214 if "if $0 {}"
215 match "match $0 {}"
216 while "while $0 {}"
217 loop "loop {$0}"
218 return "return;"
219 "#,
220 );
221 }
222
223 #[test]
224 fn dont_add_semi_after_return_if_not_a_statement() {
225 check_keyword_completion(
226 r"
227 fn quux() -> i32 {
228 match () {
229 () => <|>
230 }
231 }
232 ",
233 r#"
234 if "if $0 {}"
235 match "match $0 {}"
236 while "while $0 {}"
237 loop "loop {$0}"
238 return "return $0"
239 "#,
240 );
241 }
242
243 #[test]
244 fn last_return_in_block_has_semi() {
245 check_keyword_completion(
246 r"
247 fn quux() -> i32 {
248 if condition {
249 <|>
250 }
251 }
252 ",
253 r#"
254 if "if $0 {}"
255 match "match $0 {}"
256 while "while $0 {}"
257 loop "loop {$0}"
258 return "return $0;"
259 "#,
260 );
261 check_keyword_completion(
262 r"
263 fn quux() -> i32 {
264 if condition {
265 <|>
266 }
267 let x = 92;
268 x
269 }
270 ",
271 r#"
272 if "if $0 {}"
273 match "match $0 {}"
274 while "while $0 {}"
275 loop "loop {$0}"
276 return "return $0;"
277 "#,
278 );
279 }
280
281 #[test]
282 fn completes_break_and_continue_in_loops() {
283 check_keyword_completion(
284 r"
285 fn quux() -> i32 {
286 loop { <|> }
287 }
288 ",
289 r#"
290 if "if $0 {}"
291 match "match $0 {}"
292 while "while $0 {}"
293 loop "loop {$0}"
294 continue "continue;"
295 break "break;"
296 return "return $0;"
297 "#,
298 );
299 // No completion: lambda isolates control flow
300 check_keyword_completion(
301 r"
302 fn quux() -> i32 {
303 loop { || { <|> } }
304 }
305 ",
306 r#"
307 if "if $0 {}"
308 match "match $0 {}"
309 while "while $0 {}"
310 loop "loop {$0}"
311 return "return $0;"
312 "#,
313 );
314 }
315
316 #[test]
317 fn no_semi_after_break_continue_in_expr() {
318 check_keyword_completion(
319 r"
320 fn f() {
321 loop {
322 match () {
323 () => br<|>
324 }
325 }
326 }
327 ",
328 r#"
329 if "if $0 {}"
330 match "match $0 {}"
331 while "while $0 {}"
332 loop "loop {$0}"
333 continue "continue"
334 break "break"
335 return "return"
336 "#,
337 )
338 }
339}