diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide/src/completion/complete_keyword.rs | 187 |
1 files changed, 186 insertions, 1 deletions
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index fd95bc410..36280b703 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -1,8 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::non_trivia_sibling, | ||
4 | ast::{self, LoopBodyOwner}, | 5 | ast::{self, LoopBodyOwner}, |
5 | match_ast, AstNode, | 6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, |
6 | SyntaxKind::*, | 7 | SyntaxKind::*, |
7 | SyntaxToken, | 8 | SyntaxToken, |
8 | }; | 9 | }; |
@@ -52,7 +53,28 @@ fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { | |||
52 | .build() | 53 | .build() |
53 | } | 54 | } |
54 | 55 | ||
56 | fn add_top_level_keywords(acc: &mut Completions, ctx: &CompletionContext) { | ||
57 | if let Some(token) = previous_non_triva_element(&ctx.token).and_then(|it| it.into_token()) { | ||
58 | if token.kind() == UNSAFE_KW { | ||
59 | acc.add(keyword(ctx, "impl", "impl $0 {}")); | ||
60 | acc.add(keyword(ctx, "trait", "trait $0 {}")); | ||
61 | acc.add(keyword(ctx, "fn", "fn $0() {}")); | ||
62 | return; | ||
63 | } | ||
64 | } | ||
65 | acc.add(keyword(ctx, "impl", "impl $0 {}")); | ||
66 | acc.add(keyword(ctx, "enum", "enum $0 {}")); | ||
67 | acc.add(keyword(ctx, "struct", "struct $0 {}")); | ||
68 | acc.add(keyword(ctx, "trait", "trait $0 {}")); | ||
69 | acc.add(keyword(ctx, "fn", "fn $0() {}")); | ||
70 | acc.add(keyword(ctx, "unsafe", "unsafe ")); | ||
71 | } | ||
72 | |||
55 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 73 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
74 | if ctx.is_new_item { | ||
75 | add_top_level_keywords(acc, ctx); | ||
76 | return; | ||
77 | } | ||
56 | if !ctx.is_trivial_path { | 78 | if !ctx.is_trivial_path { |
57 | return; | 79 | return; |
58 | } | 80 | } |
@@ -82,6 +104,36 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
82 | acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); | 104 | acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); |
83 | } | 105 | } |
84 | 106 | ||
107 | fn previous_non_triva_element(token: &SyntaxToken) -> Option<SyntaxElement> { | ||
108 | // trying to get first non triva sibling if we have one | ||
109 | let token_sibling = non_trivia_sibling(NodeOrToken::Token(token.to_owned()), Direction::Prev); | ||
110 | let mut wrapped = if let Some(sibling) = token_sibling { | ||
111 | sibling | ||
112 | } else { | ||
113 | // if not trying to find first ancestor which has such a sibling | ||
114 | let node = token.parent(); | ||
115 | let range = node.text_range(); | ||
116 | let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?; | ||
117 | let prev_sibling_node = top_node.ancestors().find(|it| { | ||
118 | non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() | ||
119 | })?; | ||
120 | non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? | ||
121 | }; | ||
122 | // traversing the tree down to get the last token or node, i.e. the closest one | ||
123 | loop { | ||
124 | if let Some(token) = wrapped.as_token() { | ||
125 | return Some(NodeOrToken::Token(token.clone())); | ||
126 | } else { | ||
127 | let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); | ||
128 | if new.is_some() { | ||
129 | wrapped = new.unwrap().clone(); | ||
130 | } else { | ||
131 | return Some(wrapped); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
85 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | 137 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { |
86 | // FIXME move this to CompletionContext and make it handle macros | 138 | // FIXME move this to CompletionContext and make it handle macros |
87 | for node in leaf.parent().ancestors() { | 139 | for node in leaf.parent().ancestors() { |
@@ -269,6 +321,139 @@ mod tests { | |||
269 | } | 321 | } |
270 | 322 | ||
271 | #[test] | 323 | #[test] |
324 | fn completes_unsafe_context_in_item_position_with_non_empty_token() { | ||
325 | assert_debug_snapshot!( | ||
326 | do_keyword_completion( | ||
327 | r" | ||
328 | mod my_mod { | ||
329 | unsafe i<|> | ||
330 | } | ||
331 | ", | ||
332 | ), | ||
333 | @r###" | ||
334 | [ | ||
335 | CompletionItem { | ||
336 | label: "fn", | ||
337 | source_range: 57..58, | ||
338 | delete: 57..58, | ||
339 | insert: "fn $0() {}", | ||
340 | kind: Keyword, | ||
341 | }, | ||
342 | CompletionItem { | ||
343 | label: "impl", | ||
344 | source_range: 57..58, | ||
345 | delete: 57..58, | ||
346 | insert: "impl $0 {}", | ||
347 | kind: Keyword, | ||
348 | }, | ||
349 | CompletionItem { | ||
350 | label: "trait", | ||
351 | source_range: 57..58, | ||
352 | delete: 57..58, | ||
353 | insert: "trait $0 {}", | ||
354 | kind: Keyword, | ||
355 | }, | ||
356 | ] | ||
357 | "### | ||
358 | ); | ||
359 | } | ||
360 | |||
361 | #[test] | ||
362 | fn completes_unsafe_context_in_item_position_with_empty_token() { | ||
363 | assert_debug_snapshot!( | ||
364 | do_keyword_completion( | ||
365 | r" | ||
366 | mod my_mod { | ||
367 | unsafe <|> | ||
368 | } | ||
369 | ", | ||
370 | ), | ||
371 | @r###" | ||
372 | [ | ||
373 | CompletionItem { | ||
374 | label: "fn", | ||
375 | source_range: 57..57, | ||
376 | delete: 57..57, | ||
377 | insert: "fn $0() {}", | ||
378 | kind: Keyword, | ||
379 | }, | ||
380 | CompletionItem { | ||
381 | label: "impl", | ||
382 | source_range: 57..57, | ||
383 | delete: 57..57, | ||
384 | insert: "impl $0 {}", | ||
385 | kind: Keyword, | ||
386 | }, | ||
387 | CompletionItem { | ||
388 | label: "trait", | ||
389 | source_range: 57..57, | ||
390 | delete: 57..57, | ||
391 | insert: "trait $0 {}", | ||
392 | kind: Keyword, | ||
393 | }, | ||
394 | ] | ||
395 | "### | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn completes_keywords_in_item_position_with_empty_token() { | ||
401 | assert_debug_snapshot!( | ||
402 | do_keyword_completion( | ||
403 | r" | ||
404 | <|> | ||
405 | ", | ||
406 | ), | ||
407 | @r###" | ||
408 | [ | ||
409 | CompletionItem { | ||
410 | label: "enum", | ||
411 | source_range: 17..17, | ||
412 | delete: 17..17, | ||
413 | insert: "enum $0 {}", | ||
414 | kind: Keyword, | ||
415 | }, | ||
416 | CompletionItem { | ||
417 | label: "fn", | ||
418 | source_range: 17..17, | ||
419 | delete: 17..17, | ||
420 | insert: "fn $0() {}", | ||
421 | kind: Keyword, | ||
422 | }, | ||
423 | CompletionItem { | ||
424 | label: "impl", | ||
425 | source_range: 17..17, | ||
426 | delete: 17..17, | ||
427 | insert: "impl $0 {}", | ||
428 | kind: Keyword, | ||
429 | }, | ||
430 | CompletionItem { | ||
431 | label: "struct", | ||
432 | source_range: 17..17, | ||
433 | delete: 17..17, | ||
434 | insert: "struct $0 {}", | ||
435 | kind: Keyword, | ||
436 | }, | ||
437 | CompletionItem { | ||
438 | label: "trait", | ||
439 | source_range: 17..17, | ||
440 | delete: 17..17, | ||
441 | insert: "trait $0 {}", | ||
442 | kind: Keyword, | ||
443 | }, | ||
444 | CompletionItem { | ||
445 | label: "unsafe", | ||
446 | source_range: 17..17, | ||
447 | delete: 17..17, | ||
448 | insert: "unsafe ", | ||
449 | kind: Keyword, | ||
450 | }, | ||
451 | ] | ||
452 | "### | ||
453 | ); | ||
454 | } | ||
455 | |||
456 | #[test] | ||
272 | fn completes_else_after_if() { | 457 | fn completes_else_after_if() { |
273 | assert_debug_snapshot!( | 458 | assert_debug_snapshot!( |
274 | do_keyword_completion( | 459 | do_keyword_completion( |