diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-21 22:04:32 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-21 22:04:32 +0000 |
commit | 184665ff9b7b64730ecf481c1914a74e7191a6dd (patch) | |
tree | 4f5e97a0832821a4b06b784591d6cb3a417f1198 /crates/ra_analysis/src/completion/complete_keyword.rs | |
parent | 2351308d92f4c785d98cc24026a818d28945513e (diff) | |
parent | 2ae87ffc9afe67945e2ad655c3577b589ff640ab (diff) |
Merge #315
315: Split completion into manageable components r=matklad a=matklad
The main idea here is to do completion in two phases:
* first, we figure out surrounding context
* then, we run a series of completers on the given context.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src/completion/complete_keyword.rs')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_keyword.rs | 204 |
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 @@ | |||
1 | use ra_syntax::{ | ||
2 | algo::visit::{visitor, Visitor}, | ||
3 | AstNode, | ||
4 | ast::{self, LoopBodyOwner}, | ||
5 | SyntaxKind::*, SyntaxNodeRef, | ||
6 | }; | ||
7 | |||
8 | use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind::*}; | ||
9 | |||
10 | pub(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 | |||
34 | fn 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 | |||
53 | fn 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 | |||
63 | fn keyword(kw: &str, snippet: &str) -> CompletionItem { | ||
64 | CompletionItem::new(kw) | ||
65 | .kind(Keyword) | ||
66 | .snippet(snippet) | ||
67 | .build() | ||
68 | } | ||
69 | |||
70 | #[cfg(test)] | ||
71 | mod 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 | } | ||