aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/completion/complete_postfix.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/completion/complete_postfix.rs')
-rw-r--r--crates/ide/src/completion/complete_postfix.rs454
1 files changed, 0 insertions, 454 deletions
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs
deleted file mode 100644
index db5319618..000000000
--- a/crates/ide/src/completion/complete_postfix.rs
+++ /dev/null
@@ -1,454 +0,0 @@
1//! FIXME: write short doc here
2
3mod format_like;
4
5use assists::utils::TryEnum;
6use syntax::{
7 ast::{self, AstNode, AstToken},
8 TextRange, TextSize,
9};
10use text_edit::TextEdit;
11
12use self::format_like::add_format_like_completions;
13use crate::{
14 completion::{
15 completion_config::SnippetCap,
16 completion_context::CompletionContext,
17 completion_item::{Builder, CompletionKind, Completions},
18 },
19 CompletionItem, CompletionItemKind,
20};
21
22pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
23 if !ctx.config.enable_postfix_completions {
24 return;
25 }
26
27 let dot_receiver = match &ctx.dot_receiver {
28 Some(it) => it,
29 None => return,
30 };
31
32 let receiver_text =
33 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
34
35 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
36 Some(it) => it,
37 None => return,
38 };
39
40 let cap = match ctx.config.snippet_cap {
41 Some(it) => it,
42 None => return,
43 };
44 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
45 if let Some(try_enum) = &try_enum {
46 match try_enum {
47 TryEnum::Result => {
48 postfix_snippet(
49 ctx,
50 cap,
51 &dot_receiver,
52 "ifl",
53 "if let Ok {}",
54 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
55 )
56 .add_to(acc);
57
58 postfix_snippet(
59 ctx,
60 cap,
61 &dot_receiver,
62 "while",
63 "while let Ok {}",
64 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
65 )
66 .add_to(acc);
67 }
68 TryEnum::Option => {
69 postfix_snippet(
70 ctx,
71 cap,
72 &dot_receiver,
73 "ifl",
74 "if let Some {}",
75 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
76 )
77 .add_to(acc);
78
79 postfix_snippet(
80 ctx,
81 cap,
82 &dot_receiver,
83 "while",
84 "while let Some {}",
85 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
86 )
87 .add_to(acc);
88 }
89 }
90 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
91 postfix_snippet(
92 ctx,
93 cap,
94 &dot_receiver,
95 "if",
96 "if expr {}",
97 &format!("if {} {{\n $0\n}}", receiver_text),
98 )
99 .add_to(acc);
100 postfix_snippet(
101 ctx,
102 cap,
103 &dot_receiver,
104 "while",
105 "while expr {}",
106 &format!("while {} {{\n $0\n}}", receiver_text),
107 )
108 .add_to(acc);
109 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
110 .add_to(acc);
111 }
112
113 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
114 .add_to(acc);
115 postfix_snippet(
116 ctx,
117 cap,
118 &dot_receiver,
119 "refm",
120 "&mut expr",
121 &format!("&mut {}", receiver_text),
122 )
123 .add_to(acc);
124
125 // The rest of the postfix completions create an expression that moves an argument,
126 // so it's better to consider references now to avoid breaking the compilation
127 let dot_receiver = include_references(dot_receiver);
128 let receiver_text =
129 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
130
131 match try_enum {
132 Some(try_enum) => match try_enum {
133 TryEnum::Result => {
134 postfix_snippet(
135 ctx,
136 cap,
137 &dot_receiver,
138 "match",
139 "match expr {}",
140 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
141 )
142 .add_to(acc);
143 }
144 TryEnum::Option => {
145 postfix_snippet(
146 ctx,
147 cap,
148 &dot_receiver,
149 "match",
150 "match expr {}",
151 &format!(
152 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
153 receiver_text
154 ),
155 )
156 .add_to(acc);
157 }
158 },
159 None => {
160 postfix_snippet(
161 ctx,
162 cap,
163 &dot_receiver,
164 "match",
165 "match expr {}",
166 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
167 )
168 .add_to(acc);
169 }
170 }
171
172 postfix_snippet(
173 ctx,
174 cap,
175 &dot_receiver,
176 "box",
177 "Box::new(expr)",
178 &format!("Box::new({})", receiver_text),
179 )
180 .add_to(acc);
181
182 postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
183 .add_to(acc);
184
185 postfix_snippet(
186 ctx,
187 cap,
188 &dot_receiver,
189 "dbg",
190 "dbg!(expr)",
191 &format!("dbg!({})", receiver_text),
192 )
193 .add_to(acc);
194
195 postfix_snippet(
196 ctx,
197 cap,
198 &dot_receiver,
199 "dbgr",
200 "dbg!(&expr)",
201 &format!("dbg!(&{})", receiver_text),
202 )
203 .add_to(acc);
204
205 postfix_snippet(
206 ctx,
207 cap,
208 &dot_receiver,
209 "call",
210 "function(expr)",
211 &format!("${{1}}({})", receiver_text),
212 )
213 .add_to(acc);
214
215 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
216 if let Some(literal_text) = ast::String::cast(literal.token()) {
217 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
218 }
219 }
220}
221
222fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
223 if receiver_is_ambiguous_float_literal {
224 let text = receiver.syntax().text();
225 let without_dot = ..text.len() - TextSize::of('.');
226 text.slice(without_dot).to_string()
227 } else {
228 receiver.to_string()
229 }
230}
231
232fn include_references(initial_element: &ast::Expr) -> ast::Expr {
233 let mut resulting_element = initial_element.clone();
234 while let Some(parent_ref_element) =
235 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
236 {
237 resulting_element = ast::Expr::from(parent_ref_element);
238 }
239 resulting_element
240}
241
242fn postfix_snippet(
243 ctx: &CompletionContext,
244 cap: SnippetCap,
245 receiver: &ast::Expr,
246 label: &str,
247 detail: &str,
248 snippet: &str,
249) -> Builder {
250 let edit = {
251 let receiver_syntax = receiver.syntax();
252 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
253 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
254 TextEdit::replace(delete_range, snippet.to_string())
255 };
256 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
257 .detail(detail)
258 .kind(CompletionItemKind::Snippet)
259 .snippet_edit(cap, edit)
260}
261
262#[cfg(test)]
263mod tests {
264 use expect_test::{expect, Expect};
265
266 use crate::completion::{
267 test_utils::{check_edit, completion_list},
268 CompletionKind,
269 };
270
271 fn check(ra_fixture: &str, expect: Expect) {
272 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
273 expect.assert_eq(&actual)
274 }
275
276 #[test]
277 fn postfix_completion_works_for_trivial_path_expression() {
278 check(
279 r#"
280fn main() {
281 let bar = true;
282 bar.<|>
283}
284"#,
285 expect![[r#"
286 sn box Box::new(expr)
287 sn call function(expr)
288 sn dbg dbg!(expr)
289 sn dbgr dbg!(&expr)
290 sn if if expr {}
291 sn match match expr {}
292 sn not !expr
293 sn ok Ok(expr)
294 sn ref &expr
295 sn refm &mut expr
296 sn while while expr {}
297 "#]],
298 );
299 }
300
301 #[test]
302 fn postfix_type_filtering() {
303 check(
304 r#"
305fn main() {
306 let bar: u8 = 12;
307 bar.<|>
308}
309"#,
310 expect![[r#"
311 sn box Box::new(expr)
312 sn call function(expr)
313 sn dbg dbg!(expr)
314 sn dbgr dbg!(&expr)
315 sn match match expr {}
316 sn ok Ok(expr)
317 sn ref &expr
318 sn refm &mut expr
319 "#]],
320 )
321 }
322
323 #[test]
324 fn option_iflet() {
325 check_edit(
326 "ifl",
327 r#"
328enum Option<T> { Some(T), None }
329
330fn main() {
331 let bar = Option::Some(true);
332 bar.<|>
333}
334"#,
335 r#"
336enum Option<T> { Some(T), None }
337
338fn main() {
339 let bar = Option::Some(true);
340 if let Some($1) = bar {
341 $0
342}
343}
344"#,
345 );
346 }
347
348 #[test]
349 fn result_match() {
350 check_edit(
351 "match",
352 r#"
353enum Result<T, E> { Ok(T), Err(E) }
354
355fn main() {
356 let bar = Result::Ok(true);
357 bar.<|>
358}
359"#,
360 r#"
361enum Result<T, E> { Ok(T), Err(E) }
362
363fn main() {
364 let bar = Result::Ok(true);
365 match bar {
366 Ok(${1:_}) => {$2},
367 Err(${3:_}) => {$0},
368}
369}
370"#,
371 );
372 }
373
374 #[test]
375 fn postfix_completion_works_for_ambiguous_float_literal() {
376 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
377 }
378
379 #[test]
380 fn works_in_simple_macro() {
381 check_edit(
382 "dbg",
383 r#"
384macro_rules! m { ($e:expr) => { $e } }
385fn main() {
386 let bar: u8 = 12;
387 m!(bar.d<|>)
388}
389"#,
390 r#"
391macro_rules! m { ($e:expr) => { $e } }
392fn main() {
393 let bar: u8 = 12;
394 m!(dbg!(bar))
395}
396"#,
397 );
398 }
399
400 #[test]
401 fn postfix_completion_for_references() {
402 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
403 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
404 }
405
406 #[test]
407 fn postfix_completion_for_format_like_strings() {
408 check_edit(
409 "fmt",
410 r#"fn main() { "{some_var:?}".<|> }"#,
411 r#"fn main() { format!("{:?}", some_var) }"#,
412 );
413 check_edit(
414 "panic",
415 r#"fn main() { "Panic with {a}".<|> }"#,
416 r#"fn main() { panic!("Panic with {}", a) }"#,
417 );
418 check_edit(
419 "println",
420 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#,
421 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
422 );
423 check_edit(
424 "loge",
425 r#"fn main() { "{2+2}".<|> }"#,
426 r#"fn main() { log::error!("{}", 2+2) }"#,
427 );
428 check_edit(
429 "logt",
430 r#"fn main() { "{2+2}".<|> }"#,
431 r#"fn main() { log::trace!("{}", 2+2) }"#,
432 );
433 check_edit(
434 "logd",
435 r#"fn main() { "{2+2}".<|> }"#,
436 r#"fn main() { log::debug!("{}", 2+2) }"#,
437 );
438 check_edit(
439 "logi",
440 r#"fn main() { "{2+2}".<|> }"#,
441 r#"fn main() { log::info!("{}", 2+2) }"#,
442 );
443 check_edit(
444 "logw",
445 r#"fn main() { "{2+2}".<|> }"#,
446 r#"fn main() { log::warn!("{}", 2+2) }"#,
447 );
448 check_edit(
449 "loge",
450 r#"fn main() { "{2+2}".<|> }"#,
451 r#"fn main() { log::error!("{}", 2+2) }"#,
452 );
453 }
454}