aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/complete_keyword.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/complete_keyword.rs')
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs562
1 files changed, 320 insertions, 242 deletions
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 3b174f916..b62064797 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::ast; 3use ra_syntax::{ast, SyntaxKind};
4use test_utils::mark;
4 5
5use crate::completion::{ 6use crate::completion::{
6 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -34,9 +35,27 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
34 } 35 }
35 _ => {} 36 _ => {}
36 } 37 }
38
39 // Suggest .await syntax for types that implement Future trait
40 if let Some(receiver) = &ctx.dot_receiver {
41 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
42 if ty.impls_future(ctx.db) {
43 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
44 .kind(CompletionItemKind::Keyword)
45 .detail("expr.await")
46 .insert_text("await")
47 .add_to(acc);
48 }
49 };
50 }
37} 51}
38 52
39pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 53pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
54 if ctx.token.kind() == SyntaxKind::COMMENT {
55 mark::hit!(no_keyword_completion_in_comments);
56 return;
57 }
58
40 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; 59 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
41 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { 60 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
42 add_keyword(ctx, acc, "where", "where "); 61 add_keyword(ctx, acc, "where", "where ");
@@ -47,73 +66,67 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
47 add_keyword(ctx, acc, "fn", "fn $0() {}") 66 add_keyword(ctx, acc, "fn", "fn $0() {}")
48 } 67 }
49 68
50 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 69 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
51 || ctx.block_expr_parent
52 {
53 add_keyword(ctx, acc, "trait", "trait $0 {}"); 70 add_keyword(ctx, acc, "trait", "trait $0 {}");
54 add_keyword(ctx, acc, "impl", "impl $0 {}"); 71 add_keyword(ctx, acc, "impl", "impl $0 {}");
55 } 72 }
56 73
57 return; 74 return;
58 } 75 }
59 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 76 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
77 {
60 add_keyword(ctx, acc, "fn", "fn $0() {}"); 78 add_keyword(ctx, acc, "fn", "fn $0() {}");
61 } 79 }
62 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 80 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
63 || ctx.block_expr_parent
64 {
65 add_keyword(ctx, acc, "use", "use "); 81 add_keyword(ctx, acc, "use", "use ");
66 add_keyword(ctx, acc, "impl", "impl $0 {}"); 82 add_keyword(ctx, acc, "impl", "impl $0 {}");
67 add_keyword(ctx, acc, "trait", "trait $0 {}"); 83 add_keyword(ctx, acc, "trait", "trait $0 {}");
68 } 84 }
69 85
70 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent { 86 if ctx.has_item_list_or_source_file_parent {
71 add_keyword(ctx, acc, "enum", "enum $0 {}"); 87 add_keyword(ctx, acc, "enum", "enum $0 {}");
72 add_keyword(ctx, acc, "struct", "struct $0 {}"); 88 add_keyword(ctx, acc, "struct", "struct $0");
73 add_keyword(ctx, acc, "union", "union $0 {}"); 89 add_keyword(ctx, acc, "union", "union $0 {}");
74 } 90 }
75 91
76 if ctx.block_expr_parent || ctx.is_match_arm { 92 if ctx.is_expr {
77 add_keyword(ctx, acc, "match", "match $0 {}"); 93 add_keyword(ctx, acc, "match", "match $0 {}");
78 add_keyword(ctx, acc, "loop", "loop {$0}");
79 }
80 if ctx.block_expr_parent {
81 add_keyword(ctx, acc, "while", "while $0 {}"); 94 add_keyword(ctx, acc, "while", "while $0 {}");
95 add_keyword(ctx, acc, "loop", "loop {$0}");
96 add_keyword(ctx, acc, "if", "if ");
97 add_keyword(ctx, acc, "if let", "if let ");
82 } 98 }
99
83 if ctx.if_is_prev || ctx.block_expr_parent { 100 if ctx.if_is_prev || ctx.block_expr_parent {
84 add_keyword(ctx, acc, "let", "let "); 101 add_keyword(ctx, acc, "let", "let ");
85 } 102 }
86 if ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm { 103
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
90 if ctx.after_if { 104 if ctx.after_if {
91 add_keyword(ctx, acc, "else", "else {$0}"); 105 add_keyword(ctx, acc, "else", "else {$0}");
92 add_keyword(ctx, acc, "else if", "else if $0 {}"); 106 add_keyword(ctx, acc, "else if", "else if $0 {}");
93 } 107 }
94 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 108 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
95 || ctx.block_expr_parent
96 {
97 add_keyword(ctx, acc, "mod", "mod $0 {}"); 109 add_keyword(ctx, acc, "mod", "mod $0 {}");
98 } 110 }
99 if ctx.bind_pat_parent || ctx.ref_pat_parent { 111 if ctx.bind_pat_parent || ctx.ref_pat_parent {
100 add_keyword(ctx, acc, "mut", "mut "); 112 add_keyword(ctx, acc, "mut", "mut ");
101 } 113 }
102 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 114 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
115 {
103 add_keyword(ctx, acc, "const", "const "); 116 add_keyword(ctx, acc, "const", "const ");
104 add_keyword(ctx, acc, "type", "type "); 117 add_keyword(ctx, acc, "type", "type ");
105 } 118 }
106 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 119 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
107 || ctx.block_expr_parent
108 {
109 add_keyword(ctx, acc, "static", "static "); 120 add_keyword(ctx, acc, "static", "static ");
110 }; 121 };
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 122 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "extern", "extern "); 123 add_keyword(ctx, acc, "extern", "extern ");
115 } 124 }
116 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm { 125 if ctx.has_item_list_or_source_file_parent
126 || has_trait_or_impl_parent
127 || ctx.block_expr_parent
128 || ctx.is_match_arm
129 {
117 add_keyword(ctx, acc, "unsafe", "unsafe "); 130 add_keyword(ctx, acc, "unsafe", "unsafe ");
118 } 131 }
119 if ctx.in_loop_body { 132 if ctx.in_loop_body {
@@ -125,7 +138,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
125 add_keyword(ctx, acc, "break", "break"); 138 add_keyword(ctx, acc, "break", "break");
126 } 139 }
127 } 140 }
128 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent { 141 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent {
129 add_keyword(ctx, acc, "pub", "pub ") 142 add_keyword(ctx, acc, "pub", "pub ")
130 } 143 }
131 144
@@ -156,7 +169,7 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
156 169
157fn complete_return( 170fn complete_return(
158 ctx: &CompletionContext, 171 ctx: &CompletionContext,
159 fn_def: &ast::FnDef, 172 fn_def: &ast::Fn,
160 can_be_stmt: bool, 173 can_be_stmt: bool,
161) -> Option<CompletionItem> { 174) -> Option<CompletionItem> {
162 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { 175 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
@@ -170,289 +183,354 @@ fn complete_return(
170 183
171#[cfg(test)] 184#[cfg(test)]
172mod tests { 185mod tests {
173 use crate::completion::{test_utils::completion_list, CompletionKind}; 186 use expect::{expect, Expect};
174 use insta::assert_snapshot; 187
188 use crate::completion::{
189 test_utils::{check_edit, completion_list},
190 CompletionKind,
191 };
192 use test_utils::mark;
175 193
176 fn get_keyword_completions(code: &str) -> String { 194 fn check(ra_fixture: &str, expect: Expect) {
177 completion_list(code, CompletionKind::Keyword) 195 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
196 expect.assert_eq(&actual)
178 } 197 }
179 198
180 #[test] 199 #[test]
181 fn test_keywords_in_use_stmt() { 200 fn test_keywords_in_use_stmt() {
182 assert_snapshot!( 201 check(
183 get_keyword_completions(r"use <|>"), 202 r"use <|>",
184 @r###" 203 expect![[r#"
185 kw crate:: 204 kw crate::
186 kw self 205 kw self
187 kw super:: 206 kw super::
188 "### 207 "#]],
189 ); 208 );
190 209
191 assert_snapshot!( 210 check(
192 get_keyword_completions(r"use a::<|>"), 211 r"use a::<|>",
193 @r###" 212 expect![[r#"
194 kw self 213 kw self
195 kw super:: 214 kw super::
196 "### 215 "#]],
197 ); 216 );
198 217
199 assert_snapshot!( 218 check(
200 get_keyword_completions(r"use a::{b, <|>}"), 219 r"use a::{b, <|>}",
201 @r###" 220 expect![[r#"
202 kw self 221 kw self
203 kw super:: 222 kw super::
204 "### 223 "#]],
205 ); 224 );
206 } 225 }
207 226
208 #[test] 227 #[test]
209 fn test_keywords_at_source_file_level() { 228 fn test_keywords_at_source_file_level() {
210 assert_snapshot!( 229 check(
211 get_keyword_completions(r"m<|>"), 230 r"m<|>",
212 @r###" 231 expect![[r#"
213 kw const 232 kw const
214 kw enum 233 kw enum
215 kw extern 234 kw extern
216 kw fn 235 kw fn
217 kw impl 236 kw impl
218 kw mod 237 kw mod
219 kw pub 238 kw pub
220 kw static 239 kw static
221 kw struct 240 kw struct
222 kw trait 241 kw trait
223 kw type 242 kw type
224 kw union 243 kw union
225 kw unsafe 244 kw unsafe
226 kw use 245 kw use
227 "### 246 "#]],
228 ); 247 );
229 } 248 }
230 249
231 #[test] 250 #[test]
232 fn test_keywords_in_function() { 251 fn test_keywords_in_function() {
233 assert_snapshot!( 252 check(
234 get_keyword_completions(r"fn quux() { <|> }"), 253 r"fn quux() { <|> }",
235 @r###" 254 expect![[r#"
236 kw const 255 kw const
237 kw extern 256 kw extern
238 kw fn 257 kw fn
239 kw if 258 kw if
240 kw if let 259 kw if let
241 kw impl 260 kw impl
242 kw let 261 kw let
243 kw loop 262 kw loop
244 kw match 263 kw match
245 kw mod 264 kw mod
246 kw return 265 kw return
247 kw static 266 kw static
248 kw trait 267 kw trait
249 kw type 268 kw type
250 kw unsafe 269 kw unsafe
251 kw use 270 kw use
252 kw while 271 kw while
253 "### 272 "#]],
254 ); 273 );
255 } 274 }
256 275
257 #[test] 276 #[test]
258 fn test_keywords_inside_block() { 277 fn test_keywords_inside_block() {
259 assert_snapshot!( 278 check(
260 get_keyword_completions(r"fn quux() { if true { <|> } }"), 279 r"fn quux() { if true { <|> } }",
261 @r###" 280 expect![[r#"
262 kw const 281 kw const
263 kw extern 282 kw extern
264 kw fn 283 kw fn
265 kw if 284 kw if
266 kw if let 285 kw if let
267 kw impl 286 kw impl
268 kw let 287 kw let
269 kw loop 288 kw loop
270 kw match 289 kw match
271 kw mod 290 kw mod
272 kw return 291 kw return
273 kw static 292 kw static
274 kw trait 293 kw trait
275 kw type 294 kw type
276 kw unsafe 295 kw unsafe
277 kw use 296 kw use
278 kw while 297 kw while
279 "### 298 "#]],
280 ); 299 );
281 } 300 }
282 301
283 #[test] 302 #[test]
284 fn test_keywords_after_if() { 303 fn test_keywords_after_if() {
285 assert_snapshot!( 304 check(
286 get_keyword_completions( 305 r#"fn quux() { if true { () } <|> }"#,
287 r" 306 expect![[r#"
288 fn quux() { 307 kw const
289 if true { 308 kw else
290 () 309 kw else if
291 } <|> 310 kw extern
292 } 311 kw fn
293 ", 312 kw if
294 ), 313 kw if let
295 @r###" 314 kw impl
296 kw const 315 kw let
297 kw else 316 kw loop
298 kw else if 317 kw match
299 kw extern 318 kw mod
300 kw fn 319 kw return
301 kw if 320 kw static
302 kw if let 321 kw trait
303 kw impl 322 kw type
304 kw let 323 kw unsafe
305 kw loop 324 kw use
306 kw match 325 kw while
307 kw mod 326 "#]],
308 kw return 327 );
309 kw static 328 check_edit(
310 kw trait 329 "else",
311 kw type 330 r#"fn quux() { if true { () } <|> }"#,
312 kw unsafe 331 r#"fn quux() { if true { () } else {$0} }"#,
313 kw use
314 kw while
315 "###
316 ); 332 );
317 } 333 }
318 334
319 #[test] 335 #[test]
320 fn test_keywords_in_match_arm() { 336 fn test_keywords_in_match_arm() {
321 assert_snapshot!( 337 check(
322 get_keyword_completions( 338 r#"
323 r" 339fn quux() -> i32 {
324 fn quux() -> i32 { 340 match () { () => <|> }
325 match () { 341}
326 () => <|> 342"#,
327 } 343 expect![[r#"
328 } 344 kw if
329 ", 345 kw if let
330 ), 346 kw loop
331 @r###" 347 kw match
332 kw if 348 kw return
333 kw if let 349 kw unsafe
334 kw loop 350 kw while
335 kw match 351 "#]],
336 kw return
337 kw unsafe
338 "###
339 ); 352 );
340 } 353 }
341 354
342 #[test] 355 #[test]
343 fn test_keywords_in_trait_def() { 356 fn test_keywords_in_trait_def() {
344 assert_snapshot!( 357 check(
345 get_keyword_completions(r"trait My { <|> }"), 358 r"trait My { <|> }",
346 @r###" 359 expect![[r#"
347 kw const 360 kw const
348 kw fn 361 kw fn
349 kw type 362 kw type
350 kw unsafe 363 kw unsafe
351 "### 364 "#]],
352 ); 365 );
353 } 366 }
354 367
355 #[test] 368 #[test]
356 fn test_keywords_in_impl_def() { 369 fn test_keywords_in_impl_def() {
357 assert_snapshot!( 370 check(
358 get_keyword_completions(r"impl My { <|> }"), 371 r"impl My { <|> }",
359 @r###" 372 expect![[r#"
360 kw const 373 kw const
361 kw fn 374 kw fn
362 kw pub 375 kw pub
363 kw type 376 kw type
364 kw unsafe 377 kw unsafe
365 "### 378 "#]],
366 ); 379 );
367 } 380 }
368 381
369 #[test] 382 #[test]
370 fn test_keywords_in_loop() { 383 fn test_keywords_in_loop() {
371 assert_snapshot!( 384 check(
372 get_keyword_completions(r"fn my() { loop { <|> } }"), 385 r"fn my() { loop { <|> } }",
373 @r###" 386 expect![[r#"
374 kw break 387 kw break
375 kw const 388 kw const
376 kw continue 389 kw continue
377 kw extern 390 kw extern
378 kw fn 391 kw fn
379 kw if 392 kw if
380 kw if let 393 kw if let
381 kw impl 394 kw impl
382 kw let 395 kw let
383 kw loop 396 kw loop
384 kw match 397 kw match
385 kw mod 398 kw mod
386 kw return 399 kw return
387 kw static 400 kw static
388 kw trait 401 kw trait
389 kw type 402 kw type
390 kw unsafe 403 kw unsafe
391 kw use 404 kw use
392 kw while 405 kw while
393 "### 406 "#]],
394 ); 407 );
395 } 408 }
396 409
397 #[test] 410 #[test]
398 fn test_keywords_after_unsafe_in_item_list() { 411 fn test_keywords_after_unsafe_in_item_list() {
399 assert_snapshot!( 412 check(
400 get_keyword_completions(r"unsafe <|>"), 413 r"unsafe <|>",
401 @r###" 414 expect![[r#"
402 kw fn 415 kw fn
403 kw impl 416 kw impl
404 kw trait 417 kw trait
405 "### 418 "#]],
406 ); 419 );
407 } 420 }
408 421
409 #[test] 422 #[test]
410 fn test_keywords_after_unsafe_in_block_expr() { 423 fn test_keywords_after_unsafe_in_block_expr() {
411 assert_snapshot!( 424 check(
412 get_keyword_completions(r"fn my_fn() { unsafe <|> }"), 425 r"fn my_fn() { unsafe <|> }",
413 @r###" 426 expect![[r#"
414 kw fn 427 kw fn
415 kw impl 428 kw impl
416 kw trait 429 kw trait
417 "### 430 "#]],
418 ); 431 );
419 } 432 }
420 433
421 #[test] 434 #[test]
422 fn test_mut_in_ref_and_in_fn_parameters_list() { 435 fn test_mut_in_ref_and_in_fn_parameters_list() {
423 assert_snapshot!( 436 check(
424 get_keyword_completions(r"fn my_fn(&<|>) {}"), 437 r"fn my_fn(&<|>) {}",
425 @r###" 438 expect![[r#"
426 kw mut 439 kw mut
427 "### 440 "#]],
428 ); 441 );
429 assert_snapshot!( 442 check(
430 get_keyword_completions(r"fn my_fn(<|>) {}"), 443 r"fn my_fn(<|>) {}",
431 @r###" 444 expect![[r#"
432 kw mut 445 kw mut
433 "### 446 "#]],
434 ); 447 );
435 assert_snapshot!( 448 check(
436 get_keyword_completions(r"fn my_fn() { let &<|> }"), 449 r"fn my_fn() { let &<|> }",
437 @r###" 450 expect![[r#"
438 kw mut 451 kw mut
439 "### 452 "#]],
440 ); 453 );
441 } 454 }
442 455
443 #[test] 456 #[test]
444 fn test_where_keyword() { 457 fn test_where_keyword() {
445 assert_snapshot!( 458 check(
446 get_keyword_completions(r"trait A <|>"), 459 r"trait A <|>",
447 @r###" 460 expect![[r#"
448 kw where 461 kw where
449 "### 462 "#]],
450 ); 463 );
451 assert_snapshot!( 464 check(
452 get_keyword_completions(r"impl A <|>"), 465 r"impl A <|>",
453 @r###" 466 expect![[r#"
454 kw where 467 kw where
455 "### 468 "#]],
456 ); 469 );
457 } 470 }
471
472 #[test]
473 fn no_keyword_completion_in_comments() {
474 mark::check!(no_keyword_completion_in_comments);
475 check(
476 r#"
477fn test() {
478 let x = 2; // A comment<|>
479}
480"#,
481 expect![[""]],
482 );
483 check(
484 r#"
485/*
486Some multi-line comment<|>
487*/
488"#,
489 expect![[""]],
490 );
491 check(
492 r#"
493/// Some doc comment
494/// let test<|> = 1
495"#,
496 expect![[""]],
497 );
498 }
499
500 #[test]
501 fn test_completion_await_impls_future() {
502 check(
503 r#"
504//- /main.rs
505use std::future::*;
506struct A {}
507impl Future for A {}
508fn foo(a: A) { a.<|> }
509
510//- /std/lib.rs
511pub mod future {
512 #[lang = "future_trait"]
513 pub trait Future {}
514}
515"#,
516 expect![[r#"
517 kw await expr.await
518 "#]],
519 )
520 }
521
522 #[test]
523 fn after_let() {
524 check(
525 r#"fn main() { let _ = <|> }"#,
526 expect![[r#"
527 kw if
528 kw if let
529 kw loop
530 kw match
531 kw return
532 kw while
533 "#]],
534 )
535 }
458} 536}