aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/completion.rs66
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs411
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs224
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs162
4 files changed, 413 insertions, 450 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 67dfd6f2e..93157bbba 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -5,8 +5,7 @@ mod completion_context;
5mod presentation; 5mod presentation;
6 6
7mod complete_dot; 7mod complete_dot;
8mod complete_record_literal; 8mod complete_record;
9mod complete_record_pattern;
10mod complete_pattern; 9mod complete_pattern;
11mod complete_fn_param; 10mod complete_fn_param;
12mod complete_keyword; 11mod complete_keyword;
@@ -32,12 +31,6 @@ use crate::{
32pub use crate::completion::completion_item::{ 31pub use crate::completion::completion_item::{
33 CompletionItem, CompletionItemKind, InsertTextFormat, 32 CompletionItem, CompletionItemKind, InsertTextFormat,
34}; 33};
35use either::Either;
36use hir::{StructField, Type};
37use ra_syntax::{
38 ast::{self, NameOwner},
39 SmolStr,
40};
41 34
42#[derive(Clone, Debug, PartialEq, Eq)] 35#[derive(Clone, Debug, PartialEq, Eq)]
43pub struct CompletionConfig { 36pub struct CompletionConfig {
@@ -95,8 +88,7 @@ pub(crate) fn completions(
95 complete_path::complete_path(&mut acc, &ctx); 88 complete_path::complete_path(&mut acc, &ctx);
96 complete_scope::complete_scope(&mut acc, &ctx); 89 complete_scope::complete_scope(&mut acc, &ctx);
97 complete_dot::complete_dot(&mut acc, &ctx); 90 complete_dot::complete_dot(&mut acc, &ctx);
98 complete_record_literal::complete_record_literal(&mut acc, &ctx); 91 complete_record::complete_record(&mut acc, &ctx);
99 complete_record_pattern::complete_record_pattern(&mut acc, &ctx);
100 complete_pattern::complete_pattern(&mut acc, &ctx); 92 complete_pattern::complete_pattern(&mut acc, &ctx);
101 complete_postfix::complete_postfix(&mut acc, &ctx); 93 complete_postfix::complete_postfix(&mut acc, &ctx);
102 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 94 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
@@ -104,57 +96,3 @@ pub(crate) fn completions(
104 96
105 Some(acc) 97 Some(acc)
106} 98}
107
108pub(crate) fn get_missing_fields(
109 ctx: &CompletionContext,
110 record: Either<&ast::RecordLit, &ast::RecordPat>,
111) -> Option<Vec<(StructField, Type)>> {
112 let (ty, variant) = match record {
113 Either::Left(record_lit) => (
114 ctx.sema.type_of_expr(&record_lit.clone().into())?,
115 ctx.sema.resolve_record_literal(record_lit)?,
116 ),
117 Either::Right(record_pat) => (
118 ctx.sema.type_of_pat(&record_pat.clone().into())?,
119 ctx.sema.resolve_record_pattern(record_pat)?,
120 ),
121 };
122
123 let already_present_names = get_already_present_names(record);
124 Some(
125 ty.variant_fields(ctx.db, variant)
126 .into_iter()
127 .filter(|(field, _)| {
128 !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string()))
129 })
130 .collect(),
131 )
132}
133
134fn get_already_present_names(record: Either<&ast::RecordLit, &ast::RecordPat>) -> Vec<SmolStr> {
135 // TODO kb have a single match
136 match record {
137 Either::Left(record_lit) => record_lit
138 .record_field_list()
139 .map(|field_list| field_list.fields())
140 .map(|fields| {
141 fields
142 .into_iter()
143 .filter_map(|field| field.name_ref())
144 .map(|name_ref| name_ref.text().clone())
145 .collect()
146 })
147 .unwrap_or_default(),
148 Either::Right(record_pat) => record_pat
149 .record_field_pat_list()
150 .map(|pat_list| pat_list.bind_pats())
151 .map(|bind_pats| {
152 bind_pats
153 .into_iter()
154 .filter_map(|pat| pat.name())
155 .map(|name| name.text().clone())
156 .collect()
157 })
158 .unwrap_or_default(),
159 }
160}
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
new file mode 100644
index 000000000..01dd8c6db
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -0,0 +1,411 @@
1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions};
3use ra_syntax::{ast, ast::NameOwner, SmolStr};
4
5pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
6 let (ty, variant, already_present_fields) =
7 match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
8 (None, None) => return None,
9 (Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"),
10 (Some(record_pat), _) => (
11 ctx.sema.type_of_pat(&record_pat.clone().into())?,
12 ctx.sema.resolve_record_pattern(record_pat)?,
13 pattern_ascribed_fields(record_pat),
14 ),
15 (_, Some(record_lit)) => (
16 ctx.sema.type_of_expr(&record_lit.clone().into())?,
17 ctx.sema.resolve_record_literal(record_lit)?,
18 literal_ascribed_fields(record_lit),
19 ),
20 };
21
22 for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| {
23 // FIXME: already_present_names better be `Vec<hir::Name>`
24 !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
25 }) {
26 acc.add_field(ctx, field, &field_ty);
27 }
28 Some(())
29}
30
31fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
32 record_lit
33 .record_field_list()
34 .map(|field_list| field_list.fields())
35 .map(|fields| {
36 fields
37 .into_iter()
38 .filter_map(|field| field.name_ref())
39 .map(|name_ref| name_ref.text().clone())
40 .collect()
41 })
42 .unwrap_or_default()
43}
44
45fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> {
46 record_pat
47 .record_field_pat_list()
48 .map(|pat_list| {
49 pat_list
50 .record_field_pats()
51 .filter_map(|fild_pat| fild_pat.name())
52 .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
53 .map(|name| name.text().clone())
54 .collect()
55 })
56 .unwrap_or_default()
57}
58
59#[cfg(test)]
60mod tests {
61 mod record_lit_tests {
62 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
63 use insta::assert_debug_snapshot;
64
65 fn complete(code: &str) -> Vec<CompletionItem> {
66 do_completion(code, CompletionKind::Reference)
67 }
68
69 #[test]
70 fn test_record_pattern_field() {
71 let completions = complete(
72 r"
73 struct S { foo: u32 }
74
75 fn process(f: S) {
76 match f {
77 S { f<|>: 92 } => (),
78 }
79 }
80 ",
81 );
82 assert_debug_snapshot!(completions, @r###"
83 [
84 CompletionItem {
85 label: "foo",
86 source_range: [117; 118),
87 delete: [117; 118),
88 insert: "foo",
89 kind: Field,
90 detail: "u32",
91 },
92 ]
93 "###);
94 }
95
96 #[test]
97 fn test_record_pattern_enum_variant() {
98 let completions = complete(
99 r"
100 enum E {
101 S { foo: u32, bar: () }
102 }
103
104 fn process(e: E) {
105 match e {
106 E::S { <|> } => (),
107 }
108 }
109 ",
110 );
111 assert_debug_snapshot!(completions, @r###"
112 [
113 CompletionItem {
114 label: "bar",
115 source_range: [161; 161),
116 delete: [161; 161),
117 insert: "bar",
118 kind: Field,
119 detail: "()",
120 },
121 CompletionItem {
122 label: "foo",
123 source_range: [161; 161),
124 delete: [161; 161),
125 insert: "foo",
126 kind: Field,
127 detail: "u32",
128 },
129 ]
130 "###);
131 }
132
133 #[test]
134 fn test_record_pattern_field_in_simple_macro() {
135 let completions = complete(
136 r"
137 macro_rules! m { ($e:expr) => { $e } }
138 struct S { foo: u32 }
139
140 fn process(f: S) {
141 m!(match f {
142 S { f<|>: 92 } => (),
143 })
144 }
145 ",
146 );
147 assert_debug_snapshot!(completions, @r###"
148 [
149 CompletionItem {
150 label: "foo",
151 source_range: [171; 172),
152 delete: [171; 172),
153 insert: "foo",
154 kind: Field,
155 detail: "u32",
156 },
157 ]
158 "###);
159 }
160
161 #[test]
162 fn only_missing_fields_are_completed_in_destruct_pats() {
163 let completions = complete(
164 r"
165 struct S {
166 foo1: u32,
167 foo2: u32,
168 bar: u32,
169 baz: u32,
170 }
171
172 fn main() {
173 let s = S {
174 foo1: 1,
175 foo2: 2,
176 bar: 3,
177 baz: 4,
178 };
179 if let S { foo1, foo2: a, <|> } = s {}
180 }
181 ",
182 );
183 assert_debug_snapshot!(completions, @r###"
184 [
185 CompletionItem {
186 label: "bar",
187 source_range: [372; 372),
188 delete: [372; 372),
189 insert: "bar",
190 kind: Field,
191 detail: "u32",
192 },
193 CompletionItem {
194 label: "baz",
195 source_range: [372; 372),
196 delete: [372; 372),
197 insert: "baz",
198 kind: Field,
199 detail: "u32",
200 },
201 ]
202 "###);
203 }
204 }
205
206 mod record_pat_tests {
207 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
208 use insta::assert_debug_snapshot;
209
210 fn complete(code: &str) -> Vec<CompletionItem> {
211 do_completion(code, CompletionKind::Reference)
212 }
213
214 #[test]
215 fn test_record_literal_deprecated_field() {
216 let completions = complete(
217 r"
218 struct A {
219 #[deprecated]
220 the_field: u32,
221 }
222 fn foo() {
223 A { the<|> }
224 }
225 ",
226 );
227 assert_debug_snapshot!(completions, @r###"
228 [
229 CompletionItem {
230 label: "the_field",
231 source_range: [142; 145),
232 delete: [142; 145),
233 insert: "the_field",
234 kind: Field,
235 detail: "u32",
236 deprecated: true,
237 },
238 ]
239 "###);
240 }
241
242 #[test]
243 fn test_record_literal_field() {
244 let completions = complete(
245 r"
246 struct A { the_field: u32 }
247 fn foo() {
248 A { the<|> }
249 }
250 ",
251 );
252 assert_debug_snapshot!(completions, @r###"
253 [
254 CompletionItem {
255 label: "the_field",
256 source_range: [83; 86),
257 delete: [83; 86),
258 insert: "the_field",
259 kind: Field,
260 detail: "u32",
261 },
262 ]
263 "###);
264 }
265
266 #[test]
267 fn test_record_literal_enum_variant() {
268 let completions = complete(
269 r"
270 enum E {
271 A { a: u32 }
272 }
273 fn foo() {
274 let _ = E::A { <|> }
275 }
276 ",
277 );
278 assert_debug_snapshot!(completions, @r###"
279 [
280 CompletionItem {
281 label: "a",
282 source_range: [119; 119),
283 delete: [119; 119),
284 insert: "a",
285 kind: Field,
286 detail: "u32",
287 },
288 ]
289 "###);
290 }
291
292 #[test]
293 fn test_record_literal_two_structs() {
294 let completions = complete(
295 r"
296 struct A { a: u32 }
297 struct B { b: u32 }
298
299 fn foo() {
300 let _: A = B { <|> }
301 }
302 ",
303 );
304 assert_debug_snapshot!(completions, @r###"
305 [
306 CompletionItem {
307 label: "b",
308 source_range: [119; 119),
309 delete: [119; 119),
310 insert: "b",
311 kind: Field,
312 detail: "u32",
313 },
314 ]
315 "###);
316 }
317
318 #[test]
319 fn test_record_literal_generic_struct() {
320 let completions = complete(
321 r"
322 struct A<T> { a: T }
323
324 fn foo() {
325 let _: A<u32> = A { <|> }
326 }
327 ",
328 );
329 assert_debug_snapshot!(completions, @r###"
330 [
331 CompletionItem {
332 label: "a",
333 source_range: [93; 93),
334 delete: [93; 93),
335 insert: "a",
336 kind: Field,
337 detail: "u32",
338 },
339 ]
340 "###);
341 }
342
343 #[test]
344 fn test_record_literal_field_in_simple_macro() {
345 let completions = complete(
346 r"
347 macro_rules! m { ($e:expr) => { $e } }
348 struct A { the_field: u32 }
349 fn foo() {
350 m!(A { the<|> })
351 }
352 ",
353 );
354 assert_debug_snapshot!(completions, @r###"
355 [
356 CompletionItem {
357 label: "the_field",
358 source_range: [137; 140),
359 delete: [137; 140),
360 insert: "the_field",
361 kind: Field,
362 detail: "u32",
363 },
364 ]
365 "###);
366 }
367
368 #[test]
369 fn only_missing_fields_are_completed() {
370 let completions = complete(
371 r"
372 struct S {
373 foo1: u32,
374 foo2: u32,
375 bar: u32,
376 baz: u32,
377 }
378
379 fn main() {
380 let foo1 = 1;
381 let s = S {
382 foo1,
383 foo2: 5,
384 <|>
385 }
386 }
387 ",
388 );
389 assert_debug_snapshot!(completions, @r###"
390 [
391 CompletionItem {
392 label: "bar",
393 source_range: [302; 302),
394 delete: [302; 302),
395 insert: "bar",
396 kind: Field,
397 detail: "u32",
398 },
399 CompletionItem {
400 label: "baz",
401 source_range: [302; 302),
402 delete: [302; 302),
403 insert: "baz",
404 kind: Field,
405 detail: "u32",
406 },
407 ]
408 "###);
409 }
410 }
411}
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
deleted file mode 100644
index 8b67d3ba2..000000000
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ /dev/null
@@ -1,224 +0,0 @@
1//! FIXME: write short doc here
2
3use super::get_missing_fields;
4use crate::completion::{CompletionContext, Completions};
5use either::Either;
6
7/// Complete fields in fields literals.
8pub(super) fn complete_record_literal(
9 acc: &mut Completions,
10 ctx: &CompletionContext,
11) -> Option<()> {
12 let record_lit = ctx.record_lit_syntax.as_ref()?;
13 for (field, field_ty) in get_missing_fields(ctx, Either::Left(record_lit))? {
14 acc.add_field(ctx, field, &field_ty);
15 }
16 Some(())
17}
18
19#[cfg(test)]
20mod tests {
21 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
22 use insta::assert_debug_snapshot;
23
24 fn complete(code: &str) -> Vec<CompletionItem> {
25 do_completion(code, CompletionKind::Reference)
26 }
27
28 #[test]
29 fn test_record_literal_deprecated_field() {
30 let completions = complete(
31 r"
32 struct A {
33 #[deprecated]
34 the_field: u32,
35 }
36 fn foo() {
37 A { the<|> }
38 }
39 ",
40 );
41 assert_debug_snapshot!(completions, @r###"
42 [
43 CompletionItem {
44 label: "the_field",
45 source_range: [142; 145),
46 delete: [142; 145),
47 insert: "the_field",
48 kind: Field,
49 detail: "u32",
50 deprecated: true,
51 },
52 ]
53 "###);
54 }
55
56 #[test]
57 fn test_record_literal_field() {
58 let completions = complete(
59 r"
60 struct A { the_field: u32 }
61 fn foo() {
62 A { the<|> }
63 }
64 ",
65 );
66 assert_debug_snapshot!(completions, @r###"
67 [
68 CompletionItem {
69 label: "the_field",
70 source_range: [83; 86),
71 delete: [83; 86),
72 insert: "the_field",
73 kind: Field,
74 detail: "u32",
75 },
76 ]
77 "###);
78 }
79
80 #[test]
81 fn test_record_literal_enum_variant() {
82 let completions = complete(
83 r"
84 enum E {
85 A { a: u32 }
86 }
87 fn foo() {
88 let _ = E::A { <|> }
89 }
90 ",
91 );
92 assert_debug_snapshot!(completions, @r###"
93 [
94 CompletionItem {
95 label: "a",
96 source_range: [119; 119),
97 delete: [119; 119),
98 insert: "a",
99 kind: Field,
100 detail: "u32",
101 },
102 ]
103 "###);
104 }
105
106 #[test]
107 fn test_record_literal_two_structs() {
108 let completions = complete(
109 r"
110 struct A { a: u32 }
111 struct B { b: u32 }
112
113 fn foo() {
114 let _: A = B { <|> }
115 }
116 ",
117 );
118 assert_debug_snapshot!(completions, @r###"
119 [
120 CompletionItem {
121 label: "b",
122 source_range: [119; 119),
123 delete: [119; 119),
124 insert: "b",
125 kind: Field,
126 detail: "u32",
127 },
128 ]
129 "###);
130 }
131
132 #[test]
133 fn test_record_literal_generic_struct() {
134 let completions = complete(
135 r"
136 struct A<T> { a: T }
137
138 fn foo() {
139 let _: A<u32> = A { <|> }
140 }
141 ",
142 );
143 assert_debug_snapshot!(completions, @r###"
144 [
145 CompletionItem {
146 label: "a",
147 source_range: [93; 93),
148 delete: [93; 93),
149 insert: "a",
150 kind: Field,
151 detail: "u32",
152 },
153 ]
154 "###);
155 }
156
157 #[test]
158 fn test_record_literal_field_in_simple_macro() {
159 let completions = complete(
160 r"
161 macro_rules! m { ($e:expr) => { $e } }
162 struct A { the_field: u32 }
163 fn foo() {
164 m!(A { the<|> })
165 }
166 ",
167 );
168 assert_debug_snapshot!(completions, @r###"
169 [
170 CompletionItem {
171 label: "the_field",
172 source_range: [137; 140),
173 delete: [137; 140),
174 insert: "the_field",
175 kind: Field,
176 detail: "u32",
177 },
178 ]
179 "###);
180 }
181
182 #[test]
183 fn only_missing_fields_are_completed() {
184 let completions = complete(
185 r"
186 struct S {
187 foo1: u32,
188 foo2: u32,
189 bar: u32,
190 baz: u32,
191 }
192
193 fn main() {
194 let foo1 = 1;
195 let s = S {
196 foo1,
197 foo2: 5,
198 <|>
199 }
200 }
201 ",
202 );
203 assert_debug_snapshot!(completions, @r###"
204 [
205 CompletionItem {
206 label: "bar",
207 source_range: [302; 302),
208 delete: [302; 302),
209 insert: "bar",
210 kind: Field,
211 detail: "u32",
212 },
213 CompletionItem {
214 label: "baz",
215 source_range: [302; 302),
216 delete: [302; 302),
217 insert: "baz",
218 kind: Field,
219 detail: "u32",
220 },
221 ]
222 "###);
223 }
224}
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs
deleted file mode 100644
index f94dced04..000000000
--- a/crates/ra_ide/src/completion/complete_record_pattern.rs
+++ /dev/null
@@ -1,162 +0,0 @@
1//! FIXME: write short doc here
2
3use super::get_missing_fields;
4use crate::completion::{CompletionContext, Completions};
5use either::Either;
6
7pub(super) fn complete_record_pattern(
8 acc: &mut Completions,
9 ctx: &CompletionContext,
10) -> Option<()> {
11 let record_pat = ctx.record_lit_pat.as_ref()?;
12 for (field, field_ty) in get_missing_fields(ctx, Either::Right(record_pat))? {
13 acc.add_field(ctx, field, &field_ty);
14 }
15 Some(())
16}
17
18#[cfg(test)]
19mod tests {
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
21 use insta::assert_debug_snapshot;
22
23 fn complete(code: &str) -> Vec<CompletionItem> {
24 do_completion(code, CompletionKind::Reference)
25 }
26
27 #[test]
28 fn test_record_pattern_field() {
29 let completions = complete(
30 r"
31 struct S { foo: u32 }
32
33 fn process(f: S) {
34 match f {
35 S { f<|>: 92 } => (),
36 }
37 }
38 ",
39 );
40 assert_debug_snapshot!(completions, @r###"
41 [
42 CompletionItem {
43 label: "foo",
44 source_range: [117; 118),
45 delete: [117; 118),
46 insert: "foo",
47 kind: Field,
48 detail: "u32",
49 },
50 ]
51 "###);
52 }
53
54 #[test]
55 fn test_record_pattern_enum_variant() {
56 let completions = complete(
57 r"
58 enum E {
59 S { foo: u32, bar: () }
60 }
61
62 fn process(e: E) {
63 match e {
64 E::S { <|> } => (),
65 }
66 }
67 ",
68 );
69 assert_debug_snapshot!(completions, @r###"
70 [
71 CompletionItem {
72 label: "bar",
73 source_range: [161; 161),
74 delete: [161; 161),
75 insert: "bar",
76 kind: Field,
77 detail: "()",
78 },
79 CompletionItem {
80 label: "foo",
81 source_range: [161; 161),
82 delete: [161; 161),
83 insert: "foo",
84 kind: Field,
85 detail: "u32",
86 },
87 ]
88 "###);
89 }
90
91 #[test]
92 fn test_record_pattern_field_in_simple_macro() {
93 let completions = complete(
94 r"
95 macro_rules! m { ($e:expr) => { $e } }
96 struct S { foo: u32 }
97
98 fn process(f: S) {
99 m!(match f {
100 S { f<|>: 92 } => (),
101 })
102 }
103 ",
104 );
105 assert_debug_snapshot!(completions, @r###"
106 [
107 CompletionItem {
108 label: "foo",
109 source_range: [171; 172),
110 delete: [171; 172),
111 insert: "foo",
112 kind: Field,
113 detail: "u32",
114 },
115 ]
116 "###);
117 }
118
119 #[test]
120 fn only_missing_fields_are_completed_in_destruct_pats() {
121 let completions = complete(
122 r"
123 struct S {
124 foo1: u32,
125 foo2: u32,
126 bar: u32,
127 baz: u32,
128 }
129
130 fn main() {
131 let s = S {
132 foo1: 1,
133 foo2: 2,
134 bar: 3,
135 baz: 4,
136 };
137 if let S { foo1, foo2: a, <|> } = s {}
138 }
139 ",
140 );
141 assert_debug_snapshot!(completions, @r###"
142 [
143 CompletionItem {
144 label: "bar",
145 source_range: [372; 372),
146 delete: [372; 372),
147 insert: "bar",
148 kind: Field,
149 detail: "u32",
150 },
151 CompletionItem {
152 label: "baz",
153 source_range: [372; 372),
154 delete: [372; 372),
155 insert: "baz",
156 kind: Field,
157 detail: "u32",
158 },
159 ]
160 "###);
161 }
162}