aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
authorMikhail Rakhmanov <[email protected]>2020-06-02 00:29:54 +0100
committerMikhail Rakhmanov <[email protected]>2020-06-02 00:29:54 +0100
commitb4af02d110b2515295d8375b40311c630b90d7be (patch)
tree3ba3752ea120bbe3ac57ed7198ac52417c1f88db /crates/ra_ide/src/completion
parentabf7d1747d9910e7b4e11357ae9bcf5c594f0d55 (diff)
Add top level keywords completion
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs187
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
3use ra_syntax::{ 3use 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
56fn 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
55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 73pub(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
107fn 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
85fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 137fn 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(