aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion/complete_keyword.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/completion/complete_keyword.rs')
-rw-r--r--crates/ra_analysis/src/completion/complete_keyword.rs204
1 files changed, 204 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
new file mode 100644
index 000000000..dead15bb6
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -0,0 +1,204 @@
1use ra_syntax::{
2 algo::visit::{visitor, Visitor},
3 AstNode,
4 ast::{self, LoopBodyOwner},
5 SyntaxKind::*, SyntaxNodeRef,
6};
7
8use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind::*};
9
10pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 if !ctx.is_trivial_path {
12 return;
13 }
14 let fn_def = match ctx.enclosing_fn {
15 Some(it) => it,
16 None => return,
17 };
18 acc.add(keyword("if", "if $0 {}"));
19 acc.add(keyword("match", "match $0 {}"));
20 acc.add(keyword("while", "while $0 {}"));
21 acc.add(keyword("loop", "loop {$0}"));
22
23 if ctx.after_if {
24 acc.add(keyword("else", "else {$0}"));
25 acc.add(keyword("else if", "else if $0 {}"));
26 }
27 if is_in_loop_body(ctx.leaf) {
28 acc.add(keyword("continue", "continue"));
29 acc.add(keyword("break", "break"));
30 }
31 acc.add_all(complete_return(fn_def, ctx.is_stmt));
32}
33
34fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
35 for node in leaf.ancestors() {
36 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
37 break;
38 }
39 let loop_body = visitor()
40 .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
41 .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
42 .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
43 .accept(node);
44 if let Some(Some(body)) = loop_body {
45 if leaf.range().is_subrange(&body.syntax().range()) {
46 return true;
47 }
48 }
49 }
50 false
51}
52
53fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> {
54 let snip = match (is_stmt, fn_def.ret_type().is_some()) {
55 (true, true) => "return $0;",
56 (true, false) => "return;",
57 (false, true) => "return $0",
58 (false, false) => "return",
59 };
60 Some(keyword("return", snip))
61}
62
63fn keyword(kw: &str, snippet: &str) -> CompletionItem {
64 CompletionItem::new(kw)
65 .kind(Keyword)
66 .snippet(snippet)
67 .build()
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::completion::{CompletionKind, check_completion};
73 fn check_keyword_completion(code: &str, expected_completions: &str) {
74 check_completion(code, expected_completions, CompletionKind::Keyword);
75 }
76
77 #[test]
78 fn test_completion_kewords() {
79 check_keyword_completion(
80 r"
81 fn quux() {
82 <|>
83 }
84 ",
85 r#"
86 if "if $0 {}"
87 match "match $0 {}"
88 while "while $0 {}"
89 loop "loop {$0}"
90 return "return"
91 "#,
92 );
93 }
94
95 #[test]
96 fn test_completion_else() {
97 check_keyword_completion(
98 r"
99 fn quux() {
100 if true {
101 ()
102 } <|>
103 }
104 ",
105 r#"
106 if "if $0 {}"
107 match "match $0 {}"
108 while "while $0 {}"
109 loop "loop {$0}"
110 else "else {$0}"
111 else if "else if $0 {}"
112 return "return"
113 "#,
114 );
115 }
116
117 #[test]
118 fn test_completion_return_value() {
119 check_keyword_completion(
120 r"
121 fn quux() -> i32 {
122 <|>
123 92
124 }
125 ",
126 r#"
127 if "if $0 {}"
128 match "match $0 {}"
129 while "while $0 {}"
130 loop "loop {$0}"
131 return "return $0;"
132 "#,
133 );
134 check_keyword_completion(
135 r"
136 fn quux() {
137 <|>
138 92
139 }
140 ",
141 r#"
142 if "if $0 {}"
143 match "match $0 {}"
144 while "while $0 {}"
145 loop "loop {$0}"
146 return "return;"
147 "#,
148 );
149 }
150
151 #[test]
152 fn test_completion_return_no_stmt() {
153 check_keyword_completion(
154 r"
155 fn quux() -> i32 {
156 match () {
157 () => <|>
158 }
159 }
160 ",
161 r#"
162 if "if $0 {}"
163 match "match $0 {}"
164 while "while $0 {}"
165 loop "loop {$0}"
166 return "return $0"
167 "#,
168 );
169 }
170
171 #[test]
172 fn test_continue_break_completion() {
173 check_keyword_completion(
174 r"
175 fn quux() -> i32 {
176 loop { <|> }
177 }
178 ",
179 r#"
180 if "if $0 {}"
181 match "match $0 {}"
182 while "while $0 {}"
183 loop "loop {$0}"
184 continue "continue"
185 break "break"
186 return "return $0"
187 "#,
188 );
189 check_keyword_completion(
190 r"
191 fn quux() -> i32 {
192 loop { || { <|> } }
193 }
194 ",
195 r#"
196 if "if $0 {}"
197 match "match $0 {}"
198 while "while $0 {}"
199 loop "loop {$0}"
200 return "return $0"
201 "#,
202 );
203 }
204}