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