diff options
author | Mikhail Rakhmanov <[email protected]> | 2020-06-12 23:55:21 +0100 |
---|---|---|
committer | Mikhail Rakhmanov <[email protected]> | 2020-06-12 23:55:21 +0100 |
commit | 6feb52c12accbf0ef54475cf66a03e035b922749 (patch) | |
tree | 62af8c1e8c781e672f6cc9a498ccb2a5abb17274 | |
parent | 357667104371d446cc029267e8095365c17ba085 (diff) |
Add more patterns, tests and fix keywords
-rw-r--r-- | crates/ra_ide/src/completion/complete_keyword.rs | 690 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 19 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/patterns.rs | 61 |
3 files changed, 297 insertions, 473 deletions
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 432793de2..79432113c 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -60,32 +60,104 @@ fn add_keyword( | |||
60 | } | 60 | } |
61 | 61 | ||
62 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 62 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
63 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; | ||
64 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { | ||
65 | add_keyword(ctx, acc, "where", "where ", true); | ||
66 | return; | ||
67 | } | ||
68 | if ctx.unsafe_is_prev { | ||
69 | add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); | ||
70 | add_keyword( | ||
71 | ctx, | ||
72 | acc, | ||
73 | "trait", | ||
74 | "trait $0 {}", | ||
75 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
76 | ); | ||
77 | add_keyword( | ||
78 | ctx, | ||
79 | acc, | ||
80 | "impl", | ||
81 | "impl $0 {}", | ||
82 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
83 | ); | ||
84 | return; | ||
85 | } | ||
63 | add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); | 86 | add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); |
64 | add_keyword(ctx, acc, "use", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); | 87 | add_keyword( |
65 | add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); | 88 | ctx, |
66 | add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); | 89 | acc, |
67 | add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); | 90 | "use", |
68 | add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); | 91 | "use ", |
69 | add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); | 92 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, |
70 | add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent); | 93 | ); |
71 | add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent); | 94 | add_keyword( |
95 | ctx, | ||
96 | acc, | ||
97 | "impl", | ||
98 | "impl $0 {}", | ||
99 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
100 | ); | ||
101 | add_keyword( | ||
102 | ctx, | ||
103 | acc, | ||
104 | "trait", | ||
105 | "trait $0 {}", | ||
106 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
107 | ); | ||
108 | add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); | ||
109 | add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); | ||
110 | add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); | ||
111 | add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent || ctx.is_match_arm); | ||
112 | add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent || ctx.is_match_arm); | ||
72 | add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); | 113 | add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); |
73 | add_keyword(ctx, acc, "let", "let ", ctx.if_is_prev || ctx.block_expr_parent); | 114 | add_keyword(ctx, acc, "let", "let ", ctx.if_is_prev || ctx.block_expr_parent); |
115 | add_keyword(ctx, acc, "if", "if ", ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm); | ||
116 | add_keyword( | ||
117 | ctx, | ||
118 | acc, | ||
119 | "if let", | ||
120 | "if let ", | ||
121 | ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm, | ||
122 | ); | ||
74 | add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); | 123 | add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); |
75 | add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); | 124 | add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); |
76 | add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); | 125 | add_keyword( |
126 | ctx, | ||
127 | acc, | ||
128 | "mod", | ||
129 | "mod $0 {}", | ||
130 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
131 | ); | ||
77 | add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); | 132 | add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); |
78 | add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); | 133 | add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); |
79 | add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); | 134 | add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); |
80 | add_keyword(ctx, acc, "static", "static ", ctx.is_new_item || ctx.block_expr_parent); | 135 | add_keyword( |
81 | add_keyword(ctx, acc, "extern", "extern ", ctx.is_new_item || ctx.block_expr_parent); | 136 | ctx, |
82 | add_keyword(ctx, acc, "unsafe", "unsafe ", ctx.is_new_item || ctx.block_expr_parent); | 137 | acc, |
138 | "static", | ||
139 | "static ", | ||
140 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
141 | ); | ||
142 | add_keyword( | ||
143 | ctx, | ||
144 | acc, | ||
145 | "extern", | ||
146 | "extern ", | ||
147 | (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, | ||
148 | ); | ||
149 | add_keyword( | ||
150 | ctx, | ||
151 | acc, | ||
152 | "unsafe", | ||
153 | "unsafe ", | ||
154 | ctx.is_new_item || ctx.block_expr_parent || ctx.is_match_arm, | ||
155 | ); | ||
83 | add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); | 156 | add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); |
84 | add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); | 157 | add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); |
85 | add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); | 158 | add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); |
86 | add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); | 159 | add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); |
87 | add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.inside_trait); | 160 | add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.has_trait_parent); |
88 | add_keyword(ctx, acc, "where", "where ", ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling); | ||
89 | 161 | ||
90 | let fn_def = match &ctx.function_syntax { | 162 | let fn_def = match &ctx.function_syntax { |
91 | Some(it) => it, | 163 | Some(it) => it, |
@@ -111,21 +183,17 @@ fn complete_return( | |||
111 | #[cfg(test)] | 183 | #[cfg(test)] |
112 | mod tests { | 184 | mod tests { |
113 | use crate::completion::{ | 185 | use crate::completion::{ |
114 | test_utils::{do_completion, get_completions}, | 186 | test_utils::get_completions, |
115 | CompletionItem, CompletionKind, | 187 | CompletionKind, |
116 | }; | 188 | }; |
117 | use insta::assert_debug_snapshot; | 189 | use insta::assert_debug_snapshot; |
118 | 190 | ||
119 | fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { | ||
120 | do_completion(code, CompletionKind::Keyword) | ||
121 | } | ||
122 | |||
123 | fn get_keyword_completions(code: &str) -> Vec<String> { | 191 | fn get_keyword_completions(code: &str) -> Vec<String> { |
124 | get_completions(code, CompletionKind::Keyword) | 192 | get_completions(code, CompletionKind::Keyword) |
125 | } | 193 | } |
126 | 194 | ||
127 | #[test] | 195 | #[test] |
128 | fn completes_keywords_in_use_stmt() { | 196 | fn test_keywords_in_use_stmt() { |
129 | assert_debug_snapshot!( | 197 | assert_debug_snapshot!( |
130 | get_keyword_completions(r"use <|>"), | 198 | get_keyword_completions(r"use <|>"), |
131 | @r###" | 199 | @r###" |
@@ -159,7 +227,7 @@ mod tests { | |||
159 | } | 227 | } |
160 | 228 | ||
161 | #[test] | 229 | #[test] |
162 | fn completes_various_keywords_in_function() { | 230 | fn test_keywords_in_function() { |
163 | assert_debug_snapshot!( | 231 | assert_debug_snapshot!( |
164 | get_keyword_completions(r"fn quux() { <|> }"), | 232 | get_keyword_completions(r"fn quux() { <|> }"), |
165 | @r###" | 233 | @r###" |
@@ -167,12 +235,16 @@ mod tests { | |||
167 | "kw const", | 235 | "kw const", |
168 | "kw extern", | 236 | "kw extern", |
169 | "kw fn", | 237 | "kw fn", |
238 | "kw if", | ||
239 | "kw if let", | ||
240 | "kw impl", | ||
170 | "kw let", | 241 | "kw let", |
171 | "kw loop", | 242 | "kw loop", |
172 | "kw match", | 243 | "kw match", |
173 | "kw mod", | 244 | "kw mod", |
174 | "kw return", | 245 | "kw return", |
175 | "kw static", | 246 | "kw static", |
247 | "kw trait", | ||
176 | "kw type", | 248 | "kw type", |
177 | "kw unsafe", | 249 | "kw unsafe", |
178 | "kw use", | 250 | "kw use", |
@@ -183,9 +255,37 @@ mod tests { | |||
183 | } | 255 | } |
184 | 256 | ||
185 | #[test] | 257 | #[test] |
186 | fn completes_else_after_if() { | 258 | fn test_keywords_inside_block() { |
187 | assert_debug_snapshot!( | 259 | assert_debug_snapshot!( |
188 | do_keyword_completion( | 260 | get_keyword_completions(r"fn quux() { if true { <|> } }"), |
261 | @r###" | ||
262 | [ | ||
263 | "kw const", | ||
264 | "kw extern", | ||
265 | "kw fn", | ||
266 | "kw if", | ||
267 | "kw if let", | ||
268 | "kw impl", | ||
269 | "kw let", | ||
270 | "kw loop", | ||
271 | "kw match", | ||
272 | "kw mod", | ||
273 | "kw return", | ||
274 | "kw static", | ||
275 | "kw trait", | ||
276 | "kw type", | ||
277 | "kw unsafe", | ||
278 | "kw use", | ||
279 | "kw while", | ||
280 | ] | ||
281 | "### | ||
282 | ); | ||
283 | } | ||
284 | |||
285 | #[test] | ||
286 | fn test_keywords_after_if() { | ||
287 | assert_debug_snapshot!( | ||
288 | get_keyword_completions( | ||
189 | r" | 289 | r" |
190 | fn quux() { | 290 | fn quux() { |
191 | if true { | 291 | if true { |
@@ -196,505 +296,189 @@ mod tests { | |||
196 | ), | 296 | ), |
197 | @r###" | 297 | @r###" |
198 | [ | 298 | [ |
199 | CompletionItem { | 299 | "kw const", |
200 | label: "else", | 300 | "kw else", |
201 | source_range: 108..108, | 301 | "kw else if", |
202 | delete: 108..108, | 302 | "kw extern", |
203 | insert: "else {$0}", | 303 | "kw fn", |
204 | kind: Keyword, | 304 | "kw if", |
205 | }, | 305 | "kw if let", |
206 | CompletionItem { | 306 | "kw impl", |
207 | label: "else if", | 307 | "kw let", |
208 | source_range: 108..108, | 308 | "kw loop", |
209 | delete: 108..108, | 309 | "kw match", |
210 | insert: "else if $0 {}", | 310 | "kw mod", |
211 | kind: Keyword, | 311 | "kw return", |
212 | }, | 312 | "kw static", |
213 | CompletionItem { | 313 | "kw trait", |
214 | label: "if", | 314 | "kw type", |
215 | source_range: 108..108, | 315 | "kw unsafe", |
216 | delete: 108..108, | 316 | "kw use", |
217 | insert: "if $0 {}", | 317 | "kw while", |
218 | kind: Keyword, | ||
219 | }, | ||
220 | CompletionItem { | ||
221 | label: "loop", | ||
222 | source_range: 108..108, | ||
223 | delete: 108..108, | ||
224 | insert: "loop {$0}", | ||
225 | kind: Keyword, | ||
226 | }, | ||
227 | CompletionItem { | ||
228 | label: "match", | ||
229 | source_range: 108..108, | ||
230 | delete: 108..108, | ||
231 | insert: "match $0 {}", | ||
232 | kind: Keyword, | ||
233 | }, | ||
234 | CompletionItem { | ||
235 | label: "return", | ||
236 | source_range: 108..108, | ||
237 | delete: 108..108, | ||
238 | insert: "return;", | ||
239 | kind: Keyword, | ||
240 | }, | ||
241 | CompletionItem { | ||
242 | label: "while", | ||
243 | source_range: 108..108, | ||
244 | delete: 108..108, | ||
245 | insert: "while $0 {}", | ||
246 | kind: Keyword, | ||
247 | }, | ||
248 | ] | 318 | ] |
249 | "### | 319 | "### |
250 | ); | 320 | ); |
251 | } | 321 | } |
252 | 322 | ||
253 | #[test] | 323 | #[test] |
254 | fn test_completion_return_value() { | 324 | fn test_keywords_in_match_arm() { |
255 | assert_debug_snapshot!( | 325 | assert_debug_snapshot!( |
256 | do_keyword_completion( | 326 | get_keyword_completions( |
257 | r" | 327 | r" |
258 | fn quux() -> i32 { | 328 | fn quux() -> i32 { |
259 | <|> | 329 | match () { |
260 | 92 | 330 | () => <|> |
331 | } | ||
261 | } | 332 | } |
262 | ", | 333 | ", |
263 | ), | 334 | ), |
264 | @r###" | 335 | @r###" |
265 | [ | 336 | [ |
266 | CompletionItem { | 337 | "kw if", |
267 | label: "if", | 338 | "kw if let", |
268 | source_range: 56..56, | 339 | "kw loop", |
269 | delete: 56..56, | 340 | "kw match", |
270 | insert: "if $0 {}", | 341 | "kw return", |
271 | kind: Keyword, | 342 | "kw unsafe", |
272 | }, | ||
273 | CompletionItem { | ||
274 | label: "loop", | ||
275 | source_range: 56..56, | ||
276 | delete: 56..56, | ||
277 | insert: "loop {$0}", | ||
278 | kind: Keyword, | ||
279 | }, | ||
280 | CompletionItem { | ||
281 | label: "match", | ||
282 | source_range: 56..56, | ||
283 | delete: 56..56, | ||
284 | insert: "match $0 {}", | ||
285 | kind: Keyword, | ||
286 | }, | ||
287 | CompletionItem { | ||
288 | label: "return", | ||
289 | source_range: 56..56, | ||
290 | delete: 56..56, | ||
291 | insert: "return $0;", | ||
292 | kind: Keyword, | ||
293 | }, | ||
294 | CompletionItem { | ||
295 | label: "while", | ||
296 | source_range: 56..56, | ||
297 | delete: 56..56, | ||
298 | insert: "while $0 {}", | ||
299 | kind: Keyword, | ||
300 | }, | ||
301 | ] | 343 | ] |
302 | "### | 344 | "### |
303 | ); | 345 | ); |
346 | } | ||
347 | |||
348 | #[test] | ||
349 | fn test_keywords_in_trait_def() { | ||
304 | assert_debug_snapshot!( | 350 | assert_debug_snapshot!( |
305 | do_keyword_completion( | 351 | get_keyword_completions(r"trait My { <|> }"), |
306 | r" | ||
307 | fn quux() { | ||
308 | <|> | ||
309 | 92 | ||
310 | } | ||
311 | ", | ||
312 | ), | ||
313 | @r###" | 352 | @r###" |
314 | [ | 353 | [ |
315 | CompletionItem { | 354 | "kw const", |
316 | label: "if", | 355 | "kw fn", |
317 | source_range: 49..49, | 356 | "kw type", |
318 | delete: 49..49, | 357 | "kw unsafe", |
319 | insert: "if $0 {}", | ||
320 | kind: Keyword, | ||
321 | }, | ||
322 | CompletionItem { | ||
323 | label: "loop", | ||
324 | source_range: 49..49, | ||
325 | delete: 49..49, | ||
326 | insert: "loop {$0}", | ||
327 | kind: Keyword, | ||
328 | }, | ||
329 | CompletionItem { | ||
330 | label: "match", | ||
331 | source_range: 49..49, | ||
332 | delete: 49..49, | ||
333 | insert: "match $0 {}", | ||
334 | kind: Keyword, | ||
335 | }, | ||
336 | CompletionItem { | ||
337 | label: "return", | ||
338 | source_range: 49..49, | ||
339 | delete: 49..49, | ||
340 | insert: "return;", | ||
341 | kind: Keyword, | ||
342 | }, | ||
343 | CompletionItem { | ||
344 | label: "while", | ||
345 | source_range: 49..49, | ||
346 | delete: 49..49, | ||
347 | insert: "while $0 {}", | ||
348 | kind: Keyword, | ||
349 | }, | ||
350 | ] | 358 | ] |
351 | "### | 359 | "### |
352 | ); | 360 | ); |
353 | } | 361 | } |
354 | 362 | ||
355 | #[test] | 363 | #[test] |
356 | fn dont_add_semi_after_return_if_not_a_statement() { | 364 | fn test_keywords_in_impl_def() { |
357 | assert_debug_snapshot!( | 365 | assert_debug_snapshot!( |
358 | do_keyword_completion( | 366 | get_keyword_completions(r"impl My { <|> }"), |
359 | r" | ||
360 | fn quux() -> i32 { | ||
361 | match () { | ||
362 | () => <|> | ||
363 | } | ||
364 | } | ||
365 | ", | ||
366 | ), | ||
367 | @r###" | 367 | @r###" |
368 | [ | 368 | [ |
369 | CompletionItem { | 369 | "kw const", |
370 | label: "if", | 370 | "kw fn", |
371 | source_range: 97..97, | 371 | "kw pub", |
372 | delete: 97..97, | 372 | "kw type", |
373 | insert: "if $0 {}", | 373 | "kw unsafe", |
374 | kind: Keyword, | ||
375 | }, | ||
376 | CompletionItem { | ||
377 | label: "loop", | ||
378 | source_range: 97..97, | ||
379 | delete: 97..97, | ||
380 | insert: "loop {$0}", | ||
381 | kind: Keyword, | ||
382 | }, | ||
383 | CompletionItem { | ||
384 | label: "match", | ||
385 | source_range: 97..97, | ||
386 | delete: 97..97, | ||
387 | insert: "match $0 {}", | ||
388 | kind: Keyword, | ||
389 | }, | ||
390 | CompletionItem { | ||
391 | label: "return", | ||
392 | source_range: 97..97, | ||
393 | delete: 97..97, | ||
394 | insert: "return $0", | ||
395 | kind: Keyword, | ||
396 | }, | ||
397 | CompletionItem { | ||
398 | label: "while", | ||
399 | source_range: 97..97, | ||
400 | delete: 97..97, | ||
401 | insert: "while $0 {}", | ||
402 | kind: Keyword, | ||
403 | }, | ||
404 | ] | 374 | ] |
405 | "### | 375 | "### |
406 | ); | 376 | ); |
407 | } | 377 | } |
408 | 378 | ||
409 | #[test] | 379 | #[test] |
410 | fn last_return_in_block_has_semi() { | 380 | fn test_keywords_in_loop() { |
411 | assert_debug_snapshot!( | 381 | assert_debug_snapshot!( |
412 | do_keyword_completion( | 382 | get_keyword_completions(r"fn my() { loop { <|> } }"), |
413 | r" | ||
414 | fn quux() -> i32 { | ||
415 | if condition { | ||
416 | <|> | ||
417 | } | ||
418 | } | ||
419 | ", | ||
420 | ), | ||
421 | @r###" | 383 | @r###" |
422 | [ | 384 | [ |
423 | CompletionItem { | 385 | "kw break", |
424 | label: "if", | 386 | "kw const", |
425 | source_range: 95..95, | 387 | "kw continue", |
426 | delete: 95..95, | 388 | "kw extern", |
427 | insert: "if $0 {}", | 389 | "kw fn", |
428 | kind: Keyword, | 390 | "kw if", |
429 | }, | 391 | "kw if let", |
430 | CompletionItem { | 392 | "kw impl", |
431 | label: "loop", | 393 | "kw let", |
432 | source_range: 95..95, | 394 | "kw loop", |
433 | delete: 95..95, | 395 | "kw match", |
434 | insert: "loop {$0}", | 396 | "kw mod", |
435 | kind: Keyword, | 397 | "kw return", |
436 | }, | 398 | "kw static", |
437 | CompletionItem { | 399 | "kw trait", |
438 | label: "match", | 400 | "kw type", |
439 | source_range: 95..95, | 401 | "kw unsafe", |
440 | delete: 95..95, | 402 | "kw use", |
441 | insert: "match $0 {}", | 403 | "kw while", |
442 | kind: Keyword, | ||
443 | }, | ||
444 | CompletionItem { | ||
445 | label: "return", | ||
446 | source_range: 95..95, | ||
447 | delete: 95..95, | ||
448 | insert: "return $0;", | ||
449 | kind: Keyword, | ||
450 | }, | ||
451 | CompletionItem { | ||
452 | label: "while", | ||
453 | source_range: 95..95, | ||
454 | delete: 95..95, | ||
455 | insert: "while $0 {}", | ||
456 | kind: Keyword, | ||
457 | }, | ||
458 | ] | 404 | ] |
459 | "### | 405 | "### |
460 | ); | 406 | ); |
407 | } | ||
408 | |||
409 | #[test] | ||
410 | fn test_keywords_after_unsafe_in_item_list() { | ||
461 | assert_debug_snapshot!( | 411 | assert_debug_snapshot!( |
462 | do_keyword_completion( | 412 | get_keyword_completions(r"unsafe <|>"), |
463 | r" | ||
464 | fn quux() -> i32 { | ||
465 | if condition { | ||
466 | <|> | ||
467 | } | ||
468 | let x = 92; | ||
469 | x | ||
470 | } | ||
471 | ", | ||
472 | ), | ||
473 | @r###" | 413 | @r###" |
474 | [ | 414 | [ |
475 | CompletionItem { | 415 | "kw fn", |
476 | label: "if", | 416 | "kw impl", |
477 | source_range: 95..95, | 417 | "kw trait", |
478 | delete: 95..95, | ||
479 | insert: "if $0 {}", | ||
480 | kind: Keyword, | ||
481 | }, | ||
482 | CompletionItem { | ||
483 | label: "loop", | ||
484 | source_range: 95..95, | ||
485 | delete: 95..95, | ||
486 | insert: "loop {$0}", | ||
487 | kind: Keyword, | ||
488 | }, | ||
489 | CompletionItem { | ||
490 | label: "match", | ||
491 | source_range: 95..95, | ||
492 | delete: 95..95, | ||
493 | insert: "match $0 {}", | ||
494 | kind: Keyword, | ||
495 | }, | ||
496 | CompletionItem { | ||
497 | label: "return", | ||
498 | source_range: 95..95, | ||
499 | delete: 95..95, | ||
500 | insert: "return $0;", | ||
501 | kind: Keyword, | ||
502 | }, | ||
503 | CompletionItem { | ||
504 | label: "while", | ||
505 | source_range: 95..95, | ||
506 | delete: 95..95, | ||
507 | insert: "while $0 {}", | ||
508 | kind: Keyword, | ||
509 | }, | ||
510 | ] | 418 | ] |
511 | "### | 419 | "### |
512 | ); | 420 | ); |
513 | } | 421 | } |
514 | 422 | ||
515 | #[test] | 423 | #[test] |
516 | fn completes_break_and_continue_in_loops() { | 424 | fn test_keywords_after_unsafe_in_block_expr() { |
517 | assert_debug_snapshot!( | 425 | assert_debug_snapshot!( |
518 | do_keyword_completion( | 426 | get_keyword_completions(r"fn my_fn() { unsafe <|> }"), |
519 | r" | ||
520 | fn quux() -> i32 { | ||
521 | loop { <|> } | ||
522 | } | ||
523 | ", | ||
524 | ), | ||
525 | @r###" | 427 | @r###" |
526 | [ | 428 | [ |
527 | CompletionItem { | 429 | "kw fn", |
528 | label: "break", | 430 | "kw impl", |
529 | source_range: 63..63, | 431 | "kw trait", |
530 | delete: 63..63, | ||
531 | insert: "break;", | ||
532 | kind: Keyword, | ||
533 | }, | ||
534 | CompletionItem { | ||
535 | label: "continue", | ||
536 | source_range: 63..63, | ||
537 | delete: 63..63, | ||
538 | insert: "continue;", | ||
539 | kind: Keyword, | ||
540 | }, | ||
541 | CompletionItem { | ||
542 | label: "if", | ||
543 | source_range: 63..63, | ||
544 | delete: 63..63, | ||
545 | insert: "if $0 {}", | ||
546 | kind: Keyword, | ||
547 | }, | ||
548 | CompletionItem { | ||
549 | label: "loop", | ||
550 | source_range: 63..63, | ||
551 | delete: 63..63, | ||
552 | insert: "loop {$0}", | ||
553 | kind: Keyword, | ||
554 | }, | ||
555 | CompletionItem { | ||
556 | label: "match", | ||
557 | source_range: 63..63, | ||
558 | delete: 63..63, | ||
559 | insert: "match $0 {}", | ||
560 | kind: Keyword, | ||
561 | }, | ||
562 | CompletionItem { | ||
563 | label: "return", | ||
564 | source_range: 63..63, | ||
565 | delete: 63..63, | ||
566 | insert: "return $0;", | ||
567 | kind: Keyword, | ||
568 | }, | ||
569 | CompletionItem { | ||
570 | label: "while", | ||
571 | source_range: 63..63, | ||
572 | delete: 63..63, | ||
573 | insert: "while $0 {}", | ||
574 | kind: Keyword, | ||
575 | }, | ||
576 | ] | 432 | ] |
577 | "### | 433 | "### |
578 | ); | 434 | ); |
435 | } | ||
579 | 436 | ||
580 | // No completion: lambda isolates control flow | 437 | #[test] |
438 | fn test_mut_in_ref_and_in_fn_parameters_list() { | ||
581 | assert_debug_snapshot!( | 439 | assert_debug_snapshot!( |
582 | do_keyword_completion( | 440 | get_keyword_completions(r"fn my_fn(&<|>) {}"), |
583 | r" | 441 | @r###" |
584 | fn quux() -> i32 { | 442 | [ |
585 | loop { || { <|> } } | 443 | "kw mut", |
586 | } | 444 | ] |
587 | ", | 445 | "### |
588 | ), | 446 | ); |
447 | assert_debug_snapshot!( | ||
448 | get_keyword_completions(r"fn my_fn(<|>) {}"), | ||
449 | @r###" | ||
450 | [ | ||
451 | "kw mut", | ||
452 | ] | ||
453 | "### | ||
454 | ); | ||
455 | assert_debug_snapshot!( | ||
456 | get_keyword_completions(r"fn my_fn() { let &<|> }"), | ||
589 | @r###" | 457 | @r###" |
590 | [ | 458 | [ |
591 | CompletionItem { | 459 | "kw mut", |
592 | label: "if", | ||
593 | source_range: 68..68, | ||
594 | delete: 68..68, | ||
595 | insert: "if $0 {}", | ||
596 | kind: Keyword, | ||
597 | }, | ||
598 | CompletionItem { | ||
599 | label: "loop", | ||
600 | source_range: 68..68, | ||
601 | delete: 68..68, | ||
602 | insert: "loop {$0}", | ||
603 | kind: Keyword, | ||
604 | }, | ||
605 | CompletionItem { | ||
606 | label: "match", | ||
607 | source_range: 68..68, | ||
608 | delete: 68..68, | ||
609 | insert: "match $0 {}", | ||
610 | kind: Keyword, | ||
611 | }, | ||
612 | CompletionItem { | ||
613 | label: "return", | ||
614 | source_range: 68..68, | ||
615 | delete: 68..68, | ||
616 | insert: "return $0;", | ||
617 | kind: Keyword, | ||
618 | }, | ||
619 | CompletionItem { | ||
620 | label: "while", | ||
621 | source_range: 68..68, | ||
622 | delete: 68..68, | ||
623 | insert: "while $0 {}", | ||
624 | kind: Keyword, | ||
625 | }, | ||
626 | ] | 460 | ] |
627 | "### | 461 | "### |
628 | ); | 462 | ); |
629 | } | 463 | } |
630 | 464 | ||
631 | #[test] | 465 | #[test] |
632 | fn no_semi_after_break_continue_in_expr() { | 466 | fn test_where_keyword() { |
633 | assert_debug_snapshot!( | 467 | assert_debug_snapshot!( |
634 | do_keyword_completion( | 468 | get_keyword_completions(r"trait A <|>"), |
635 | r" | 469 | @r###" |
636 | fn f() { | 470 | [ |
637 | loop { | 471 | "kw where", |
638 | match () { | 472 | ] |
639 | () => br<|> | 473 | "### |
640 | } | 474 | ); |
641 | } | 475 | assert_debug_snapshot!( |
642 | } | 476 | get_keyword_completions(r"impl A <|>"), |
643 | ", | ||
644 | ), | ||
645 | @r###" | 477 | @r###" |
646 | [ | 478 | [ |
647 | CompletionItem { | 479 | "kw where", |
648 | label: "break", | ||
649 | source_range: 122..124, | ||
650 | delete: 122..124, | ||
651 | insert: "break", | ||
652 | kind: Keyword, | ||
653 | }, | ||
654 | CompletionItem { | ||
655 | label: "continue", | ||
656 | source_range: 122..124, | ||
657 | delete: 122..124, | ||
658 | insert: "continue", | ||
659 | kind: Keyword, | ||
660 | }, | ||
661 | CompletionItem { | ||
662 | label: "if", | ||
663 | source_range: 122..124, | ||
664 | delete: 122..124, | ||
665 | insert: "if $0 {}", | ||
666 | kind: Keyword, | ||
667 | }, | ||
668 | CompletionItem { | ||
669 | label: "loop", | ||
670 | source_range: 122..124, | ||
671 | delete: 122..124, | ||
672 | insert: "loop {$0}", | ||
673 | kind: Keyword, | ||
674 | }, | ||
675 | CompletionItem { | ||
676 | label: "match", | ||
677 | source_range: 122..124, | ||
678 | delete: 122..124, | ||
679 | insert: "match $0 {}", | ||
680 | kind: Keyword, | ||
681 | }, | ||
682 | CompletionItem { | ||
683 | label: "return", | ||
684 | source_range: 122..124, | ||
685 | delete: 122..124, | ||
686 | insert: "return", | ||
687 | kind: Keyword, | ||
688 | }, | ||
689 | CompletionItem { | ||
690 | label: "while", | ||
691 | source_range: 122..124, | ||
692 | delete: 122..124, | ||
693 | insert: "while $0 {}", | ||
694 | kind: Keyword, | ||
695 | }, | ||
696 | ] | 480 | ] |
697 | "### | 481 | "### |
698 | ) | 482 | ); |
699 | } | 483 | } |
700 | } | 484 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 41aec5686..2f96861ca 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -12,8 +12,9 @@ use ra_syntax::{ | |||
12 | use ra_text_edit::Indel; | 12 | use ra_text_edit::Indel; |
13 | 13 | ||
14 | use super::patterns::{ | 14 | use super::patterns::{ |
15 | has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_ref_pat_parent, | 15 | has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent, |
16 | has_trait_as_prev_sibling, if_is_prev, inside_trait, is_in_loop_body, unsafe_is_prev, | 16 | has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_in_loop_body, |
17 | is_match_arm, unsafe_is_prev, | ||
17 | }; | 18 | }; |
18 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; | 19 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; |
19 | use test_utils::mark; | 20 | use test_utils::mark; |
@@ -70,9 +71,11 @@ pub(crate) struct CompletionContext<'a> { | |||
70 | pub(super) bind_pat_parent: bool, | 71 | pub(super) bind_pat_parent: bool, |
71 | pub(super) ref_pat_parent: bool, | 72 | pub(super) ref_pat_parent: bool, |
72 | pub(super) in_loop_body: bool, | 73 | pub(super) in_loop_body: bool, |
73 | pub(super) inside_trait: bool, | 74 | pub(super) has_trait_parent: bool, |
75 | pub(super) has_impl_parent: bool, | ||
74 | pub(super) trait_as_prev_sibling: bool, | 76 | pub(super) trait_as_prev_sibling: bool, |
75 | pub(super) impl_as_prev_sibling: bool, | 77 | pub(super) impl_as_prev_sibling: bool, |
78 | pub(super) is_match_arm: bool, | ||
76 | } | 79 | } |
77 | 80 | ||
78 | impl<'a> CompletionContext<'a> { | 81 | impl<'a> CompletionContext<'a> { |
@@ -136,10 +139,12 @@ impl<'a> CompletionContext<'a> { | |||
136 | ref_pat_parent: false, | 139 | ref_pat_parent: false, |
137 | bind_pat_parent: false, | 140 | bind_pat_parent: false, |
138 | block_expr_parent: false, | 141 | block_expr_parent: false, |
139 | inside_trait: false, | 142 | has_trait_parent: false, |
143 | has_impl_parent: false, | ||
140 | trait_as_prev_sibling: false, | 144 | trait_as_prev_sibling: false, |
141 | impl_as_prev_sibling: false, | 145 | impl_as_prev_sibling: false, |
142 | if_is_prev: false, | 146 | if_is_prev: false, |
147 | is_match_arm: false, | ||
143 | }; | 148 | }; |
144 | 149 | ||
145 | let mut original_file = original_file.syntax().clone(); | 150 | let mut original_file = original_file.syntax().clone(); |
@@ -217,11 +222,13 @@ impl<'a> CompletionContext<'a> { | |||
217 | self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); | 222 | self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); |
218 | self.if_is_prev = if_is_prev(syntax_element.clone()); | 223 | self.if_is_prev = if_is_prev(syntax_element.clone()); |
219 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); | 224 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); |
220 | self.ref_pat_parent = has_ref_pat_parent(syntax_element.clone()); | 225 | self.ref_pat_parent = has_ref_parent(syntax_element.clone()); |
221 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 226 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
222 | self.inside_trait = inside_trait(syntax_element.clone()); | 227 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); |
228 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); | ||
223 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); | 229 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); |
224 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | 230 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); |
231 | self.is_match_arm = is_match_arm(syntax_element.clone()); | ||
225 | } | 232 | } |
226 | 233 | ||
227 | fn fill( | 234 | fn fill( |
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index bc39689ab..bc37196d5 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs | |||
@@ -6,16 +6,42 @@ use ra_syntax::{ | |||
6 | SyntaxNode, SyntaxToken, | 6 | SyntaxNode, SyntaxToken, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | pub(crate) fn inside_trait(element: SyntaxElement) -> bool { | 9 | pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { |
10 | element.ancestors().find(|it| it.kind() == TRAIT_DEF).is_some() | 10 | not_same_range_ancestor(element) |
11 | .filter(|it| it.kind() == ITEM_LIST) | ||
12 | .and_then(|it| it.parent()) | ||
13 | .filter(|it| it.kind() == TRAIT_DEF) | ||
14 | .is_some() | ||
15 | } | ||
16 | |||
17 | pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { | ||
18 | not_same_range_ancestor(element) | ||
19 | .filter(|it| it.kind() == ITEM_LIST) | ||
20 | .and_then(|it| it.parent()) | ||
21 | .filter(|it| it.kind() == IMPL_DEF) | ||
22 | .is_some() | ||
23 | } | ||
24 | |||
25 | pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { | ||
26 | not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() | ||
11 | } | 27 | } |
12 | 28 | ||
13 | pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { | 29 | pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { |
14 | element.ancestors().find(|it| it.kind() == BIND_PAT).is_some() | 30 | element.ancestors().find(|it| it.kind() == BIND_PAT).is_some() |
15 | } | 31 | } |
16 | 32 | ||
17 | pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { | 33 | pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { |
18 | element.ancestors().find(|it| it.kind() == REF_PAT).is_some() | 34 | not_same_range_ancestor(element) |
35 | .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR) | ||
36 | .is_some() | ||
37 | } | ||
38 | |||
39 | pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { | ||
40 | not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() | ||
41 | && previous_sibling_or_ancestor_sibling(element) | ||
42 | .and_then(|it| it.into_token()) | ||
43 | .filter(|it| it.kind() == FAT_ARROW) | ||
44 | .is_some() | ||
19 | } | 45 | } |
20 | 46 | ||
21 | pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { | 47 | pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { |
@@ -34,10 +60,6 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | |||
34 | .is_some() | 60 | .is_some() |
35 | } | 61 | } |
36 | 62 | ||
37 | pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { | ||
38 | not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() | ||
39 | } | ||
40 | |||
41 | pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { | 63 | pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { |
42 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() | 64 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() |
43 | } | 65 | } |
@@ -114,8 +136,9 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<Syntax | |||
114 | #[cfg(test)] | 136 | #[cfg(test)] |
115 | mod tests { | 137 | mod tests { |
116 | use super::{ | 138 | use super::{ |
117 | has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_ref_pat_parent, | 139 | has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent, |
118 | has_trait_as_prev_sibling, if_is_prev, inside_trait, unsafe_is_prev, | 140 | has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_match_arm, |
141 | unsafe_is_prev, | ||
119 | }; | 142 | }; |
120 | use crate::completion::test_utils::check_pattern_is_applicable; | 143 | use crate::completion::test_utils::check_pattern_is_applicable; |
121 | 144 | ||
@@ -130,8 +153,13 @@ mod tests { | |||
130 | } | 153 | } |
131 | 154 | ||
132 | #[test] | 155 | #[test] |
133 | fn test_inside_trait() { | 156 | fn test_has_trait_parent() { |
134 | check_pattern_is_applicable(r"trait A { fn<|> }", inside_trait); | 157 | check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent); |
158 | } | ||
159 | |||
160 | #[test] | ||
161 | fn test_has_impl_parent() { | ||
162 | check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); | ||
135 | } | 163 | } |
136 | 164 | ||
137 | #[test] | 165 | #[test] |
@@ -151,12 +179,12 @@ mod tests { | |||
151 | 179 | ||
152 | #[test] | 180 | #[test] |
153 | fn test_has_ref_pat_parent_in_func_parameters() { | 181 | fn test_has_ref_pat_parent_in_func_parameters() { |
154 | check_pattern_is_applicable(r"fn my_fn(&<|>) {}", has_ref_pat_parent); | 182 | check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent); |
155 | } | 183 | } |
156 | 184 | ||
157 | #[test] | 185 | #[test] |
158 | fn test_has_ref_pat_parent_in_let_statement() { | 186 | fn test_has_ref_pat_parent_in_let_statement() { |
159 | check_pattern_is_applicable(r"fn my_fn() { let &<|> }", has_ref_pat_parent); | 187 | check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent); |
160 | } | 188 | } |
161 | 189 | ||
162 | #[test] | 190 | #[test] |
@@ -168,4 +196,9 @@ mod tests { | |||
168 | fn test_has_bind_pat_parent_in_let_statement() { | 196 | fn test_has_bind_pat_parent_in_let_statement() { |
169 | check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); | 197 | check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); |
170 | } | 198 | } |
199 | |||
200 | #[test] | ||
201 | fn test_is_match_arm() { | ||
202 | check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm); | ||
203 | } | ||
171 | } | 204 | } |