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.rs271
1 files changed, 39 insertions, 232 deletions
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 36280b703..5b56c6275 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,12 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ 3use ra_syntax::ast;
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxToken,
9};
10 4
11use crate::completion::{ 5use crate::completion::{
12 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 6 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -53,110 +47,56 @@ fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
53 .build() 47 .build()
54} 48}
55 49
56fn add_top_level_keywords(acc: &mut Completions, ctx: &CompletionContext) { 50fn add_keyword(
57 if let Some(token) = previous_non_triva_element(&ctx.token).and_then(|it| it.into_token()) { 51 ctx: &CompletionContext,
58 if token.kind() == UNSAFE_KW { 52 acc: &mut Completions,
59 acc.add(keyword(ctx, "impl", "impl $0 {}")); 53 kw: &str,
60 acc.add(keyword(ctx, "trait", "trait $0 {}")); 54 snippet: &str,
61 acc.add(keyword(ctx, "fn", "fn $0() {}")); 55 should_add: bool,
62 return; 56) {
63 } 57 if should_add {
58 acc.add(keyword(ctx, kw, snippet));
64 } 59 }
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} 60}
72 61
73pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 62pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
74 if ctx.is_new_item { 63 add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent);
75 add_top_level_keywords(acc, ctx); 64 add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent);
76 return; 65 add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent);
77 } 66 add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item);
78 if !ctx.is_trivial_path { 67 add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item);
79 return; 68 add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.after_unsafe);
80 } 69 add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.after_unsafe);
70 add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.after_unsafe);
71 add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent);
72 add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent);
73 add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent);
74 add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent);
75 add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent);
76 add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if);
77 add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if);
78 add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent);
79 add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent);
80 add_keyword(ctx, acc, "true", "true", !ctx.is_new_item); // this should be defined properly
81 add_keyword(ctx, acc, "false", "false", !ctx.is_new_item); // this should be defined properly
82 add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent);
83 add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent);
84 add_keyword(ctx, acc, "static", "static ", ctx.is_new_item || ctx.block_expr_parent);
85 add_keyword(ctx, acc, "extern", "extern ", ctx.is_new_item || ctx.block_expr_parent);
86 add_keyword(ctx, acc, "unsafe", "unsafe ", ctx.is_new_item || ctx.block_expr_parent);
87 add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt);
88 add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt);
89 add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt);
90 add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt);
91 complete_use_tree_keyword(acc, ctx);
81 92
82 let fn_def = match &ctx.function_syntax { 93 let fn_def = match &ctx.function_syntax {
83 Some(it) => it, 94 Some(it) => it,
84 None => return, 95 None => return,
85 }; 96 };
86 acc.add(keyword(ctx, "if", "if $0 {}"));
87 acc.add(keyword(ctx, "match", "match $0 {}"));
88 acc.add(keyword(ctx, "while", "while $0 {}"));
89 acc.add(keyword(ctx, "loop", "loop {$0}"));
90
91 if ctx.after_if {
92 acc.add(keyword(ctx, "else", "else {$0}"));
93 acc.add(keyword(ctx, "else if", "else if $0 {}"));
94 }
95 if is_in_loop_body(&ctx.token) {
96 if ctx.can_be_stmt {
97 acc.add(keyword(ctx, "continue", "continue;"));
98 acc.add(keyword(ctx, "break", "break;"));
99 } else {
100 acc.add(keyword(ctx, "continue", "continue"));
101 acc.add(keyword(ctx, "break", "break"));
102 }
103 }
104 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); 97 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
105} 98}
106 99
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
137fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
138 // FIXME move this to CompletionContext and make it handle macros
139 for node in leaf.parent().ancestors() {
140 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
141 break;
142 }
143 let loop_body = match_ast! {
144 match node {
145 ast::ForExpr(it) => it.loop_body(),
146 ast::WhileExpr(it) => it.loop_body(),
147 ast::LoopExpr(it) => it.loop_body(),
148 _ => None,
149 }
150 };
151 if let Some(body) = loop_body {
152 if body.syntax().text_range().contains_range(leaf.text_range()) {
153 return true;
154 }
155 }
156 }
157 false
158}
159
160fn complete_return( 100fn complete_return(
161 ctx: &CompletionContext, 101 ctx: &CompletionContext,
162 fn_def: &ast::FnDef, 102 fn_def: &ast::FnDef,
@@ -321,139 +261,6 @@ mod tests {
321 } 261 }
322 262
323 #[test] 263 #[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]
457 fn completes_else_after_if() { 264 fn completes_else_after_if() {
458 assert_debug_snapshot!( 265 assert_debug_snapshot!(
459 do_keyword_completion( 266 do_keyword_completion(