diff options
Diffstat (limited to 'crates/ra_ide/src/completion/complete_postfix.rs')
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs new file mode 100644 index 000000000..646a30c76 --- /dev/null +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -0,0 +1,282 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ast::AstNode, TextRange, TextUnit}; | ||
4 | use ra_text_edit::TextEdit; | ||
5 | |||
6 | use crate::{ | ||
7 | completion::{ | ||
8 | completion_context::CompletionContext, | ||
9 | completion_item::{Builder, CompletionKind, Completions}, | ||
10 | }, | ||
11 | CompletionItem, | ||
12 | }; | ||
13 | |||
14 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | ||
15 | if ctx.db.feature_flags.get("completion.enable-postfix") == false { | ||
16 | return; | ||
17 | } | ||
18 | |||
19 | let dot_receiver = match &ctx.dot_receiver { | ||
20 | Some(it) => it, | ||
21 | None => return, | ||
22 | }; | ||
23 | |||
24 | let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { | ||
25 | let text = dot_receiver.syntax().text(); | ||
26 | let without_dot = ..text.len() - TextUnit::of_char('.'); | ||
27 | text.slice(without_dot).to_string() | ||
28 | } else { | ||
29 | dot_receiver.syntax().text().to_string() | ||
30 | }; | ||
31 | |||
32 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | ||
33 | Some(it) => it, | ||
34 | None => return, | ||
35 | }; | ||
36 | |||
37 | if receiver_ty.is_bool() || receiver_ty.is_unknown() { | ||
38 | postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) | ||
39 | .add_to(acc); | ||
40 | postfix_snippet( | ||
41 | ctx, | ||
42 | "while", | ||
43 | "while expr {}", | ||
44 | &format!("while {} {{\n$0\n}}", receiver_text), | ||
45 | ) | ||
46 | .add_to(acc); | ||
47 | } | ||
48 | |||
49 | postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); | ||
50 | |||
51 | postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); | ||
52 | postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); | ||
53 | |||
54 | postfix_snippet( | ||
55 | ctx, | ||
56 | "match", | ||
57 | "match expr {}", | ||
58 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), | ||
59 | ) | ||
60 | .add_to(acc); | ||
61 | |||
62 | postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); | ||
63 | |||
64 | postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) | ||
65 | .add_to(acc); | ||
66 | } | ||
67 | |||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | ||
69 | let edit = { | ||
70 | let receiver_range = | ||
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | ||
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | ||
73 | TextEdit::replace(delete_range, snippet.to_string()) | ||
74 | }; | ||
75 | CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) | ||
76 | .detail(detail) | ||
77 | .snippet_edit(edit) | ||
78 | } | ||
79 | |||
80 | #[cfg(test)] | ||
81 | mod tests { | ||
82 | use insta::assert_debug_snapshot; | ||
83 | |||
84 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
85 | |||
86 | fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { | ||
87 | do_completion(code, CompletionKind::Postfix) | ||
88 | } | ||
89 | |||
90 | #[test] | ||
91 | fn postfix_completion_works_for_trivial_path_expression() { | ||
92 | assert_debug_snapshot!( | ||
93 | do_postfix_completion( | ||
94 | r#" | ||
95 | fn main() { | ||
96 | let bar = true; | ||
97 | bar.<|> | ||
98 | } | ||
99 | "#, | ||
100 | ), | ||
101 | @r###" | ||
102 | [ | ||
103 | CompletionItem { | ||
104 | label: "box", | ||
105 | source_range: [89; 89), | ||
106 | delete: [85; 89), | ||
107 | insert: "Box::new(bar)", | ||
108 | detail: "Box::new(expr)", | ||
109 | }, | ||
110 | CompletionItem { | ||
111 | label: "dbg", | ||
112 | source_range: [89; 89), | ||
113 | delete: [85; 89), | ||
114 | insert: "dbg!(bar)", | ||
115 | detail: "dbg!(expr)", | ||
116 | }, | ||
117 | CompletionItem { | ||
118 | label: "if", | ||
119 | source_range: [89; 89), | ||
120 | delete: [85; 89), | ||
121 | insert: "if bar {$0}", | ||
122 | detail: "if expr {}", | ||
123 | }, | ||
124 | CompletionItem { | ||
125 | label: "match", | ||
126 | source_range: [89; 89), | ||
127 | delete: [85; 89), | ||
128 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
129 | detail: "match expr {}", | ||
130 | }, | ||
131 | CompletionItem { | ||
132 | label: "not", | ||
133 | source_range: [89; 89), | ||
134 | delete: [85; 89), | ||
135 | insert: "!bar", | ||
136 | detail: "!expr", | ||
137 | }, | ||
138 | CompletionItem { | ||
139 | label: "ref", | ||
140 | source_range: [89; 89), | ||
141 | delete: [85; 89), | ||
142 | insert: "&bar", | ||
143 | detail: "&expr", | ||
144 | }, | ||
145 | CompletionItem { | ||
146 | label: "refm", | ||
147 | source_range: [89; 89), | ||
148 | delete: [85; 89), | ||
149 | insert: "&mut bar", | ||
150 | detail: "&mut expr", | ||
151 | }, | ||
152 | CompletionItem { | ||
153 | label: "while", | ||
154 | source_range: [89; 89), | ||
155 | delete: [85; 89), | ||
156 | insert: "while bar {\n$0\n}", | ||
157 | detail: "while expr {}", | ||
158 | }, | ||
159 | ] | ||
160 | "### | ||
161 | ); | ||
162 | } | ||
163 | |||
164 | #[test] | ||
165 | fn some_postfix_completions_ignored() { | ||
166 | assert_debug_snapshot!( | ||
167 | do_postfix_completion( | ||
168 | r#" | ||
169 | fn main() { | ||
170 | let bar: u8 = 12; | ||
171 | bar.<|> | ||
172 | } | ||
173 | "#, | ||
174 | ), | ||
175 | @r###" | ||
176 | [ | ||
177 | CompletionItem { | ||
178 | label: "box", | ||
179 | source_range: [91; 91), | ||
180 | delete: [87; 91), | ||
181 | insert: "Box::new(bar)", | ||
182 | detail: "Box::new(expr)", | ||
183 | }, | ||
184 | CompletionItem { | ||
185 | label: "dbg", | ||
186 | source_range: [91; 91), | ||
187 | delete: [87; 91), | ||
188 | insert: "dbg!(bar)", | ||
189 | detail: "dbg!(expr)", | ||
190 | }, | ||
191 | CompletionItem { | ||
192 | label: "match", | ||
193 | source_range: [91; 91), | ||
194 | delete: [87; 91), | ||
195 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
196 | detail: "match expr {}", | ||
197 | }, | ||
198 | CompletionItem { | ||
199 | label: "not", | ||
200 | source_range: [91; 91), | ||
201 | delete: [87; 91), | ||
202 | insert: "!bar", | ||
203 | detail: "!expr", | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "ref", | ||
207 | source_range: [91; 91), | ||
208 | delete: [87; 91), | ||
209 | insert: "&bar", | ||
210 | detail: "&expr", | ||
211 | }, | ||
212 | CompletionItem { | ||
213 | label: "refm", | ||
214 | source_range: [91; 91), | ||
215 | delete: [87; 91), | ||
216 | insert: "&mut bar", | ||
217 | detail: "&mut expr", | ||
218 | }, | ||
219 | ] | ||
220 | "### | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn postfix_completion_works_for_ambiguous_float_literal() { | ||
226 | assert_debug_snapshot!( | ||
227 | do_postfix_completion( | ||
228 | r#" | ||
229 | fn main() { | ||
230 | 42.<|> | ||
231 | } | ||
232 | "#, | ||
233 | ), | ||
234 | @r###" | ||
235 | [ | ||
236 | CompletionItem { | ||
237 | label: "box", | ||
238 | source_range: [52; 52), | ||
239 | delete: [49; 52), | ||
240 | insert: "Box::new(42)", | ||
241 | detail: "Box::new(expr)", | ||
242 | }, | ||
243 | CompletionItem { | ||
244 | label: "dbg", | ||
245 | source_range: [52; 52), | ||
246 | delete: [49; 52), | ||
247 | insert: "dbg!(42)", | ||
248 | detail: "dbg!(expr)", | ||
249 | }, | ||
250 | CompletionItem { | ||
251 | label: "match", | ||
252 | source_range: [52; 52), | ||
253 | delete: [49; 52), | ||
254 | insert: "match 42 {\n ${1:_} => {$0\\},\n}", | ||
255 | detail: "match expr {}", | ||
256 | }, | ||
257 | CompletionItem { | ||
258 | label: "not", | ||
259 | source_range: [52; 52), | ||
260 | delete: [49; 52), | ||
261 | insert: "!42", | ||
262 | detail: "!expr", | ||
263 | }, | ||
264 | CompletionItem { | ||
265 | label: "ref", | ||
266 | source_range: [52; 52), | ||
267 | delete: [49; 52), | ||
268 | insert: "&42", | ||
269 | detail: "&expr", | ||
270 | }, | ||
271 | CompletionItem { | ||
272 | label: "refm", | ||
273 | source_range: [52; 52), | ||
274 | delete: [49; 52), | ||
275 | insert: "&mut 42", | ||
276 | detail: "&mut expr", | ||
277 | }, | ||
278 | ] | ||
279 | "### | ||
280 | ); | ||
281 | } | ||
282 | } | ||