aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/completion.rs16
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs411
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs241
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs118
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs8
-rw-r--r--crates/ra_ide/src/completion/presentation.rs12
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs6
-rw-r--r--crates/ra_ide/src/inlay_hints.rs101
-rw-r--r--crates/ra_ide/src/lib.rs17
-rw-r--r--crates/ra_ide/src/ssr.rs116
11 files changed, 590 insertions, 458 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index cd0757be5..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;
@@ -34,15 +33,15 @@ pub use crate::completion::completion_item::{
34}; 33};
35 34
36#[derive(Clone, Debug, PartialEq, Eq)] 35#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct CompletionOptions { 36pub struct CompletionConfig {
38 pub enable_postfix_completions: bool, 37 pub enable_postfix_completions: bool,
39 pub add_call_parenthesis: bool, 38 pub add_call_parenthesis: bool,
40 pub add_call_argument_snippets: bool, 39 pub add_call_argument_snippets: bool,
41} 40}
42 41
43impl Default for CompletionOptions { 42impl Default for CompletionConfig {
44 fn default() -> Self { 43 fn default() -> Self {
45 CompletionOptions { 44 CompletionConfig {
46 enable_postfix_completions: true, 45 enable_postfix_completions: true,
47 add_call_parenthesis: true, 46 add_call_parenthesis: true,
48 add_call_argument_snippets: true, 47 add_call_argument_snippets: true,
@@ -75,9 +74,9 @@ impl Default for CompletionOptions {
75pub(crate) fn completions( 74pub(crate) fn completions(
76 db: &RootDatabase, 75 db: &RootDatabase,
77 position: FilePosition, 76 position: FilePosition,
78 options: &CompletionOptions, 77 config: &CompletionConfig,
79) -> Option<Completions> { 78) -> Option<Completions> {
80 let ctx = CompletionContext::new(db, position, options)?; 79 let ctx = CompletionContext::new(db, position, config)?;
81 80
82 let mut acc = Completions::default(); 81 let mut acc = Completions::default();
83 82
@@ -89,8 +88,7 @@ pub(crate) fn completions(
89 complete_path::complete_path(&mut acc, &ctx); 88 complete_path::complete_path(&mut acc, &ctx);
90 complete_scope::complete_scope(&mut acc, &ctx); 89 complete_scope::complete_scope(&mut acc, &ctx);
91 complete_dot::complete_dot(&mut acc, &ctx); 90 complete_dot::complete_dot(&mut acc, &ctx);
92 complete_record_literal::complete_record_literal(&mut acc, &ctx); 91 complete_record::complete_record(&mut acc, &ctx);
93 complete_record_pattern::complete_record_pattern(&mut acc, &ctx);
94 complete_pattern::complete_pattern(&mut acc, &ctx); 92 complete_pattern::complete_pattern(&mut acc, &ctx);
95 complete_postfix::complete_postfix(&mut acc, &ctx); 93 complete_postfix::complete_postfix(&mut acc, &ctx);
96 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);
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0a00054b2..29c2881c6 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -15,7 +15,7 @@ use crate::{
15}; 15};
16 16
17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
18 if !ctx.options.enable_postfix_completions { 18 if !ctx.config.enable_postfix_completions {
19 return; 19 return;
20 } 20 }
21 21
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 e4e764f58..000000000
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ /dev/null
@@ -1,241 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::SmolStr;
5
6/// Complete fields in fields literals.
7pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
8 let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| {
9 Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?))
10 }) {
11 Some(it) => it,
12 _ => return,
13 };
14
15 let already_present_names: Vec<SmolStr> = ctx
16 .record_lit_syntax
17 .as_ref()
18 .and_then(|record_literal| record_literal.record_field_list())
19 .map(|field_list| field_list.fields())
20 .map(|fields| {
21 fields
22 .into_iter()
23 .filter_map(|field| field.name_ref())
24 .map(|name_ref| name_ref.text().clone())
25 .collect()
26 })
27 .unwrap_or_default();
28
29 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
30 if !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string())) {
31 acc.add_field(ctx, field, &field_ty);
32 }
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
39 use insta::assert_debug_snapshot;
40
41 fn complete(code: &str) -> Vec<CompletionItem> {
42 do_completion(code, CompletionKind::Reference)
43 }
44
45 #[test]
46 fn test_record_literal_deprecated_field() {
47 let completions = complete(
48 r"
49 struct A {
50 #[deprecated]
51 the_field: u32,
52 }
53 fn foo() {
54 A { the<|> }
55 }
56 ",
57 );
58 assert_debug_snapshot!(completions, @r###"
59 [
60 CompletionItem {
61 label: "the_field",
62 source_range: [142; 145),
63 delete: [142; 145),
64 insert: "the_field",
65 kind: Field,
66 detail: "u32",
67 deprecated: true,
68 },
69 ]
70 "###);
71 }
72
73 #[test]
74 fn test_record_literal_field() {
75 let completions = complete(
76 r"
77 struct A { the_field: u32 }
78 fn foo() {
79 A { the<|> }
80 }
81 ",
82 );
83 assert_debug_snapshot!(completions, @r###"
84 [
85 CompletionItem {
86 label: "the_field",
87 source_range: [83; 86),
88 delete: [83; 86),
89 insert: "the_field",
90 kind: Field,
91 detail: "u32",
92 },
93 ]
94 "###);
95 }
96
97 #[test]
98 fn test_record_literal_enum_variant() {
99 let completions = complete(
100 r"
101 enum E {
102 A { a: u32 }
103 }
104 fn foo() {
105 let _ = E::A { <|> }
106 }
107 ",
108 );
109 assert_debug_snapshot!(completions, @r###"
110 [
111 CompletionItem {
112 label: "a",
113 source_range: [119; 119),
114 delete: [119; 119),
115 insert: "a",
116 kind: Field,
117 detail: "u32",
118 },
119 ]
120 "###);
121 }
122
123 #[test]
124 fn test_record_literal_two_structs() {
125 let completions = complete(
126 r"
127 struct A { a: u32 }
128 struct B { b: u32 }
129
130 fn foo() {
131 let _: A = B { <|> }
132 }
133 ",
134 );
135 assert_debug_snapshot!(completions, @r###"
136 [
137 CompletionItem {
138 label: "b",
139 source_range: [119; 119),
140 delete: [119; 119),
141 insert: "b",
142 kind: Field,
143 detail: "u32",
144 },
145 ]
146 "###);
147 }
148
149 #[test]
150 fn test_record_literal_generic_struct() {
151 let completions = complete(
152 r"
153 struct A<T> { a: T }
154
155 fn foo() {
156 let _: A<u32> = A { <|> }
157 }
158 ",
159 );
160 assert_debug_snapshot!(completions, @r###"
161 [
162 CompletionItem {
163 label: "a",
164 source_range: [93; 93),
165 delete: [93; 93),
166 insert: "a",
167 kind: Field,
168 detail: "u32",
169 },
170 ]
171 "###);
172 }
173
174 #[test]
175 fn test_record_literal_field_in_simple_macro() {
176 let completions = complete(
177 r"
178 macro_rules! m { ($e:expr) => { $e } }
179 struct A { the_field: u32 }
180 fn foo() {
181 m!(A { the<|> })
182 }
183 ",
184 );
185 assert_debug_snapshot!(completions, @r###"
186 [
187 CompletionItem {
188 label: "the_field",
189 source_range: [137; 140),
190 delete: [137; 140),
191 insert: "the_field",
192 kind: Field,
193 detail: "u32",
194 },
195 ]
196 "###);
197 }
198
199 #[test]
200 fn only_missing_fields_are_completed() {
201 let completions = complete(
202 r"
203 struct S {
204 foo1: u32,
205 foo2: u32,
206 bar: u32,
207 baz: u32,
208 }
209
210 fn main() {
211 let foo1 = 1;
212 let s = S {
213 foo1,
214 foo2: 5,
215 <|>
216 }
217 }
218 ",
219 );
220 assert_debug_snapshot!(completions, @r###"
221 [
222 CompletionItem {
223 label: "bar",
224 source_range: [302; 302),
225 delete: [302; 302),
226 insert: "bar",
227 kind: Field,
228 detail: "u32",
229 },
230 CompletionItem {
231 label: "baz",
232 source_range: [302; 302),
233 delete: [302; 302),
234 insert: "baz",
235 kind: Field,
236 detail: "u32",
237 },
238 ]
239 "###);
240 }
241}
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 962376428..000000000
--- a/crates/ra_ide/src/completion/complete_record_pattern.rs
+++ /dev/null
@@ -1,118 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
6 let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| {
7 Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?))
8 }) {
9 Some(it) => it,
10 _ => return,
11 };
12
13 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
14 acc.add_field(ctx, field, &field_ty);
15 }
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}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 319e33b61..fdc0da2c5 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionOptions, FilePosition}; 14use crate::{completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,7 +19,7 @@ use crate::{completion::CompletionOptions, FilePosition};
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions, 22 pub(super) config: &'a CompletionConfig,
23 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
24 /// The token before the cursor, in the original file. 24 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken, 25 pub(super) original_token: SyntaxToken,
@@ -61,7 +61,7 @@ impl<'a> CompletionContext<'a> {
61 pub(super) fn new( 61 pub(super) fn new(
62 db: &'a RootDatabase, 62 db: &'a RootDatabase,
63 position: FilePosition, 63 position: FilePosition,
64 options: &'a CompletionOptions, 64 config: &'a CompletionConfig,
65 ) -> Option<CompletionContext<'a>> { 65 ) -> Option<CompletionContext<'a>> {
66 let sema = Semantics::new(db); 66 let sema = Semantics::new(db);
67 67
@@ -85,7 +85,7 @@ impl<'a> CompletionContext<'a> {
85 let mut ctx = CompletionContext { 85 let mut ctx = CompletionContext {
86 sema, 86 sema,
87 db, 87 db,
88 options, 88 config,
89 original_token, 89 original_token,
90 token, 90 token,
91 offset: position.offset, 91 offset: position.offset,
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 60f1b83f3..1c7c0924d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -106,7 +106,7 @@ impl Completions {
106 }; 106 };
107 107
108 // Add `<>` for generic types 108 // Add `<>` for generic types
109 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis { 109 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
110 let has_non_default_type_params = match resolution { 110 let has_non_default_type_params = match resolution {
111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,14 +211,14 @@ impl Completions {
211 .detail(function_signature.to_string()); 211 .detail(function_signature.to_string());
212 212
213 // If not an import, add parenthesis automatically. 213 // If not an import, add parenthesis automatically.
214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis { 214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis {
215 tested_by!(inserts_parens_for_function_calls); 215 tested_by!(inserts_parens_for_function_calls);
216 216
217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
218 (format!("{}()$0", name), format!("{}()", name)) 218 (format!("{}()$0", name), format!("{}()", name))
219 } else { 219 } else {
220 builder = builder.trigger_call_info(); 220 builder = builder.trigger_call_info();
221 let snippet = if ctx.options.add_call_argument_snippets { 221 let snippet = if ctx.config.add_call_argument_snippets {
222 let to_skip = if has_self_param { 1 } else { 0 }; 222 let to_skip = if has_self_param { 1 } else { 0 };
223 let function_params_snippet = function_signature 223 let function_params_snippet = function_signature
224 .parameter_names 224 .parameter_names
@@ -311,7 +311,7 @@ mod tests {
311 311
312 use crate::completion::{ 312 use crate::completion::{
313 test_utils::{do_completion, do_completion_with_options}, 313 test_utils::{do_completion, do_completion_with_options},
314 CompletionItem, CompletionKind, CompletionOptions, 314 CompletionConfig, CompletionItem, CompletionKind,
315 }; 315 };
316 316
317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
@@ -320,7 +320,7 @@ mod tests {
320 320
321 fn do_reference_completion_with_options( 321 fn do_reference_completion_with_options(
322 ra_fixture: &str, 322 ra_fixture: &str,
323 options: CompletionOptions, 323 options: CompletionConfig,
324 ) -> Vec<CompletionItem> { 324 ) -> Vec<CompletionItem> {
325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
326 } 326 }
@@ -589,7 +589,7 @@ mod tests {
589 s.f<|> 589 s.f<|>
590 } 590 }
591 ", 591 ",
592 CompletionOptions { 592 CompletionConfig {
593 add_call_argument_snippets: false, 593 add_call_argument_snippets: false,
594 .. Default::default() 594 .. Default::default()
595 } 595 }
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index 136857315..eb90b5279 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,19 +1,19 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use crate::{ 3use crate::{
4 completion::{completion_item::CompletionKind, CompletionOptions}, 4 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem, 6 CompletionItem,
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionOptions::default()) 10 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 11}
12 12
13pub(crate) fn do_completion_with_options( 13pub(crate) fn do_completion_with_options(
14 code: &str, 14 code: &str,
15 kind: CompletionKind, 15 kind: CompletionKind,
16 options: &CompletionOptions, 16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 17) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 18 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 19 analysis_and_position(code)
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index f4f0751c0..4b133b19b 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12 12
13#[derive(Clone, Debug, PartialEq, Eq)] 13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsOptions { 14pub struct InlayHintsConfig {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool, 17 pub chaining_hints: bool,
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsOptions { 21impl Default for InlayHintsConfig {
22 fn default() -> Self { 22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None } 23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 } 24 }
@@ -41,7 +41,7 @@ pub struct InlayHint {
41pub(crate) fn inlay_hints( 41pub(crate) fn inlay_hints(
42 db: &RootDatabase, 42 db: &RootDatabase,
43 file_id: FileId, 43 file_id: FileId,
44 options: &InlayHintsOptions, 44 config: &InlayHintsConfig,
45) -> Vec<InlayHint> { 45) -> Vec<InlayHint> {
46 let _p = profile("inlay_hints"); 46 let _p = profile("inlay_hints");
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
@@ -50,14 +50,14 @@ pub(crate) fn inlay_hints(
50 let mut res = Vec::new(); 50 let mut res = Vec::new();
51 for node in file.syntax().descendants() { 51 for node in file.syntax().descendants() {
52 if let Some(expr) = ast::Expr::cast(node.clone()) { 52 if let Some(expr) = ast::Expr::cast(node.clone()) {
53 get_chaining_hints(&mut res, &sema, options, expr); 53 get_chaining_hints(&mut res, &sema, config, expr);
54 } 54 }
55 55
56 match_ast! { 56 match_ast! {
57 match node { 57 match node {
58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, options, it); }, 60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
61 _ => (), 61 _ => (),
62 } 62 }
63 } 63 }
@@ -68,15 +68,14 @@ pub(crate) fn inlay_hints(
68fn get_chaining_hints( 68fn get_chaining_hints(
69 acc: &mut Vec<InlayHint>, 69 acc: &mut Vec<InlayHint>,
70 sema: &Semantics<RootDatabase>, 70 sema: &Semantics<RootDatabase>,
71 options: &InlayHintsOptions, 71 config: &InlayHintsConfig,
72 expr: ast::Expr, 72 expr: ast::Expr,
73) -> Option<()> { 73) -> Option<()> {
74 if !options.chaining_hints { 74 if !config.chaining_hints {
75 return None; 75 return None;
76 } 76 }
77 77
78 let ty = sema.type_of_expr(&expr)?; 78 if matches!(expr, ast::Expr::RecordLit(_)) {
79 if ty.is_unknown() {
80 return None; 79 return None;
81 } 80 }
82 81
@@ -95,7 +94,18 @@ fn get_chaining_hints(
95 let next = tokens.next()?.kind(); 94 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind(); 95 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 96 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
98 let label = ty.display_truncated(sema.db, options.max_length).to_string(); 97 let ty = sema.type_of_expr(&expr)?;
98 if ty.is_unknown() {
99 return None;
100 }
101 if matches!(expr, ast::Expr::PathExpr(_)) {
102 if let Some(Adt::Struct(st)) = ty.as_adt() {
103 if st.fields(sema.db).is_empty() {
104 return None;
105 }
106 }
107 }
108 let label = ty.display_truncated(sema.db, config.max_length).to_string();
99 acc.push(InlayHint { 109 acc.push(InlayHint {
100 range: expr.syntax().text_range(), 110 range: expr.syntax().text_range(),
101 kind: InlayKind::ChainingHint, 111 kind: InlayKind::ChainingHint,
@@ -108,10 +118,10 @@ fn get_chaining_hints(
108fn get_param_name_hints( 118fn get_param_name_hints(
109 acc: &mut Vec<InlayHint>, 119 acc: &mut Vec<InlayHint>,
110 sema: &Semantics<RootDatabase>, 120 sema: &Semantics<RootDatabase>,
111 options: &InlayHintsOptions, 121 config: &InlayHintsConfig,
112 expr: ast::Expr, 122 expr: ast::Expr,
113) -> Option<()> { 123) -> Option<()> {
114 if !options.parameter_hints { 124 if !config.parameter_hints {
115 return None; 125 return None;
116 } 126 }
117 127
@@ -148,10 +158,10 @@ fn get_param_name_hints(
148fn get_bind_pat_hints( 158fn get_bind_pat_hints(
149 acc: &mut Vec<InlayHint>, 159 acc: &mut Vec<InlayHint>,
150 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
151 options: &InlayHintsOptions, 161 config: &InlayHintsConfig,
152 pat: ast::BindPat, 162 pat: ast::BindPat,
153) -> Option<()> { 163) -> Option<()> {
154 if !options.type_hints { 164 if !config.type_hints {
155 return None; 165 return None;
156 } 166 }
157 167
@@ -164,7 +174,7 @@ fn get_bind_pat_hints(
164 acc.push(InlayHint { 174 acc.push(InlayHint {
165 range: pat.syntax().text_range(), 175 range: pat.syntax().text_range(),
166 kind: InlayKind::TypeHint, 176 kind: InlayKind::TypeHint,
167 label: ty.display_truncated(sema.db, options.max_length).to_string().into(), 177 label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
168 }); 178 });
169 Some(()) 179 Some(())
170} 180}
@@ -270,7 +280,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
270 280
271#[cfg(test)] 281#[cfg(test)]
272mod tests { 282mod tests {
273 use crate::inlay_hints::InlayHintsOptions; 283 use crate::inlay_hints::InlayHintsConfig;
274 use insta::assert_debug_snapshot; 284 use insta::assert_debug_snapshot;
275 285
276 use crate::mock_analysis::single_file; 286 use crate::mock_analysis::single_file;
@@ -284,7 +294,7 @@ mod tests {
284 let _x = foo(4, 4); 294 let _x = foo(4, 4);
285 }"#, 295 }"#,
286 ); 296 );
287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 297 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
288 [ 298 [
289 InlayHint { 299 InlayHint {
290 range: [106; 107), 300 range: [106; 107),
@@ -308,7 +318,7 @@ mod tests {
308 let _x = foo(4, 4); 318 let _x = foo(4, 4);
309 }"#, 319 }"#,
310 ); 320 );
311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); 321 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
312 } 322 }
313 323
314 #[test] 324 #[test]
@@ -320,7 +330,7 @@ mod tests {
320 let _x = foo(4, 4); 330 let _x = foo(4, 4);
321 }"#, 331 }"#,
322 ); 332 );
323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 333 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
324 [ 334 [
325 InlayHint { 335 InlayHint {
326 range: [97; 99), 336 range: [97; 99),
@@ -344,7 +354,7 @@ fn main() {
344}"#, 354}"#,
345 ); 355 );
346 356
347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 357 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
348 [ 358 [
349 InlayHint { 359 InlayHint {
350 range: [69; 71), 360 range: [69; 71),
@@ -401,7 +411,7 @@ fn main() {
401}"#, 411}"#,
402 ); 412 );
403 413
404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 414 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
405 [ 415 [
406 InlayHint { 416 InlayHint {
407 range: [193; 197), 417 range: [193; 197),
@@ -481,7 +491,7 @@ fn main() {
481}"#, 491}"#,
482 ); 492 );
483 493
484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 494 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
485 [ 495 [
486 InlayHint { 496 InlayHint {
487 range: [21; 30), 497 range: [21; 30),
@@ -545,7 +555,7 @@ fn main() {
545}"#, 555}"#,
546 ); 556 );
547 557
548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 558 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
549 [ 559 [
550 InlayHint { 560 InlayHint {
551 range: [21; 30), 561 range: [21; 30),
@@ -595,7 +605,7 @@ fn main() {
595}"#, 605}"#,
596 ); 606 );
597 607
598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 608 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
599 [ 609 [
600 InlayHint { 610 InlayHint {
601 range: [188; 192), 611 range: [188; 192),
@@ -690,7 +700,7 @@ fn main() {
690}"#, 700}"#,
691 ); 701 );
692 702
693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 703 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
694 [ 704 [
695 InlayHint { 705 InlayHint {
696 range: [188; 192), 706 range: [188; 192),
@@ -785,7 +795,7 @@ fn main() {
785}"#, 795}"#,
786 ); 796 );
787 797
788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 798 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
789 [ 799 [
790 InlayHint { 800 InlayHint {
791 range: [252; 256), 801 range: [252; 256),
@@ -857,7 +867,7 @@ fn main() {
857}"#, 867}"#,
858 ); 868 );
859 869
860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 870 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
861 [ 871 [
862 InlayHint { 872 InlayHint {
863 range: [74; 75), 873 range: [74; 75),
@@ -945,7 +955,7 @@ fn main() {
945}"#, 955}"#,
946 ); 956 );
947 957
948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 958 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
949 [ 959 [
950 InlayHint { 960 InlayHint {
951 range: [798; 809), 961 range: [798; 809),
@@ -1067,7 +1077,7 @@ fn main() {
1067}"#, 1077}"#,
1068 ); 1078 );
1069 1079
1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1080 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1071 [] 1081 []
1072 "### 1082 "###
1073 ); 1083 );
@@ -1093,7 +1103,7 @@ fn main() {
1093}"#, 1103}"#,
1094 ); 1104 );
1095 1105
1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1106 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1097 [] 1107 []
1098 "### 1108 "###
1099 ); 1109 );
@@ -1115,7 +1125,7 @@ fn main() {
1115 .into_c(); 1125 .into_c();
1116 }"#, 1126 }"#,
1117 ); 1127 );
1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1128 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1119 [ 1129 [
1120 InlayHint { 1130 InlayHint {
1121 range: [232; 269), 1131 range: [232; 269),
@@ -1144,7 +1154,7 @@ fn main() {
1144 let c = A(B(C)).into_b().into_c(); 1154 let c = A(B(C)).into_b().into_c();
1145 }"#, 1155 }"#,
1146 ); 1156 );
1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); 1157 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1148 } 1158 }
1149 1159
1150 #[test] 1160 #[test]
@@ -1154,32 +1164,35 @@ fn main() {
1154 struct A { pub b: B } 1164 struct A { pub b: B }
1155 struct B { pub c: C } 1165 struct B { pub c: C }
1156 struct C(pub bool); 1166 struct C(pub bool);
1167 struct D;
1168
1169 impl D {
1170 fn foo(&self) -> i32 { 42 }
1171 }
1157 1172
1158 fn main() { 1173 fn main() {
1159 let x = A { b: B { c: C(true) } } 1174 let x = A { b: B { c: C(true) } }
1160 .b 1175 .b
1161 .c 1176 .c
1162 .0; 1177 .0;
1178 let x = D
1179 .foo();
1163 }"#, 1180 }"#,
1164 ); 1181 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1182 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [ 1183 [
1167 InlayHint { 1184 InlayHint {
1168 range: [150; 221), 1185 range: [252; 323),
1169 kind: ChainingHint, 1186 kind: ChainingHint,
1170 label: "C", 1187 label: "C",
1171 }, 1188 },
1172 InlayHint { 1189 InlayHint {
1173 range: [150; 198), 1190 range: [252; 300),
1174 kind: ChainingHint, 1191 kind: ChainingHint,
1175 label: "B", 1192 label: "B",
1176 }, 1193 },
1177 InlayHint { 1194 ]
1178 range: [150; 175), 1195 "###);
1179 kind: ChainingHint,
1180 label: "A",
1181 },
1182 ]"###);
1183 } 1196 }
1184 1197
1185 #[test] 1198 #[test]
@@ -1204,7 +1217,7 @@ fn main() {
1204 .into_c(); 1217 .into_c();
1205 }"#, 1218 }"#,
1206 ); 1219 );
1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1220 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1208 [ 1221 [
1209 InlayHint { 1222 InlayHint {
1210 range: [403; 452), 1223 range: [403; 452),
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e43414985..285381086 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,13 +62,13 @@ use crate::display::ToNav;
62pub use crate::{ 62pub use crate::{
63 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
64 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat}, 65 completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat},
66 diagnostics::Severity, 66 diagnostics::Severity,
67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
68 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
69 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
70 hover::HoverResult, 70 hover::HoverResult,
71 inlay_hints::{InlayHint, InlayHintsOptions, InlayKind}, 71 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
73 runnables::{Runnable, RunnableKind, TestId}, 73 runnables::{Runnable, RunnableKind, TestId},
74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -138,6 +138,11 @@ impl AnalysisHost {
138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
139 AnalysisHost { db: RootDatabase::new(lru_capacity) } 139 AnalysisHost { db: RootDatabase::new(lru_capacity) }
140 } 140 }
141
142 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
143 self.db.update_lru_capacity(lru_capacity);
144 }
145
141 /// Returns a snapshot of the current state, which you can query for 146 /// Returns a snapshot of the current state, which you can query for
142 /// semantic information. 147 /// semantic information.
143 pub fn analysis(&self) -> Analysis { 148 pub fn analysis(&self) -> Analysis {
@@ -320,9 +325,9 @@ impl Analysis {
320 pub fn inlay_hints( 325 pub fn inlay_hints(
321 &self, 326 &self,
322 file_id: FileId, 327 file_id: FileId,
323 inlay_hint_opts: &InlayHintsOptions, 328 config: &InlayHintsConfig,
324 ) -> Cancelable<Vec<InlayHint>> { 329 ) -> Cancelable<Vec<InlayHint>> {
325 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, inlay_hint_opts)) 330 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
326 } 331 }
327 332
328 /// Returns the set of folding ranges. 333 /// Returns the set of folding ranges.
@@ -445,9 +450,9 @@ impl Analysis {
445 pub fn completions( 450 pub fn completions(
446 &self, 451 &self,
447 position: FilePosition, 452 position: FilePosition,
448 options: &CompletionOptions, 453 config: &CompletionConfig,
449 ) -> Cancelable<Option<Vec<CompletionItem>>> { 454 ) -> Cancelable<Option<Vec<CompletionItem>>> {
450 self.with_db(|db| completion::completions(db, position, options).map(Into::into)) 455 self.with_db(|db| completion::completions(db, position, config).map(Into::into))
451 } 456 }
452 457
453 /// Computes assists (aka code actions aka intentions) for the given 458 /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1c9710a5d..1abb891c1 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase; 5use ra_ide_db::symbol_index::SymbolsDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::make::try_expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
8use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
186} 186}
187 187
188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { 188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
189 fn check_record_lit(
190 pattern: RecordLit,
191 code: RecordLit,
192 placeholders: &[Var],
193 match_: Match,
194 ) -> Option<Match> {
195 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
196
197 let mut pattern_fields =
198 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
199 let mut code_fields =
200 code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
201
202 if pattern_fields.len() != code_fields.len() {
203 return None;
204 }
205
206 let by_name = |a: &RecordField, b: &RecordField| {
207 a.name_ref()
208 .map(|x| x.syntax().text().to_string())
209 .cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
210 };
211 pattern_fields.sort_by(by_name);
212 code_fields.sort_by(by_name);
213
214 pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
215 Some(match_),
216 |accum, (a, b)| {
217 accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
218 },
219 )
220 }
221
222 fn check_opt_nodes(
223 pattern: Option<impl AstNode>,
224 code: Option<impl AstNode>,
225 placeholders: &[Var],
226 match_: Match,
227 ) -> Option<Match> {
228 match (pattern, code) {
229 (Some(pattern), Some(code)) => check(
230 &SyntaxElement::from(pattern.syntax().clone()),
231 &SyntaxElement::from(code.syntax().clone()),
232 placeholders,
233 match_,
234 ),
235 (None, None) => Some(match_),
236 _ => None,
237 }
238 }
239
189 fn check( 240 fn check(
190 pattern: &SyntaxElement, 241 pattern: &SyntaxElement,
191 code: &SyntaxElement, 242 code: &SyntaxElement,
192 placeholders: &[Var], 243 placeholders: &[Var],
193 mut match_: Match, 244 mut match_: Match,
194 ) -> Option<Match> { 245 ) -> Option<Match> {
195 match (pattern, code) { 246 match (&pattern, &code) {
196 (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => { 247 (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
197 if pattern.text() == code.text() { 248 if pattern.text() == code.text() {
198 Some(match_) 249 Some(match_)
199 } else { 250 } else {
200 None 251 None
201 } 252 }
202 } 253 }
203 (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => { 254 (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
204 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) { 255 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
205 match_.binding.insert(Var(pattern.text().to_string()), code.clone()); 256 match_.binding.insert(Var(pattern.text().to_string()), code.clone());
206 Some(match_) 257 Some(match_)
207 } else { 258 } else {
208 let mut pattern_children = pattern 259 if let (Some(pattern), Some(code)) =
209 .children_with_tokens() 260 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
210 .filter(|element| !element.kind().is_trivia()); 261 {
211 let mut code_children = 262 check_record_lit(pattern, code, placeholders, match_)
212 code.children_with_tokens().filter(|element| !element.kind().is_trivia()); 263 } else {
213 let new_ignored_comments = code.children_with_tokens().filter_map(|element| { 264 let mut pattern_children = pattern
214 element.as_token().and_then(|token| Comment::cast(token.clone())) 265 .children_with_tokens()
215 }); 266 .filter(|element| !element.kind().is_trivia());
216 match_.ignored_comments.extend(new_ignored_comments); 267 let mut code_children = code
217 let match_from_children = pattern_children 268 .children_with_tokens()
218 .by_ref() 269 .filter(|element| !element.kind().is_trivia());
219 .zip(code_children.by_ref()) 270 let new_ignored_comments =
220 .fold(Some(match_), |accum, (a, b)| { 271 code.children_with_tokens().filter_map(|element| {
221 accum.and_then(|match_| check(&a, &b, placeholders, match_)) 272 element.as_token().and_then(|token| Comment::cast(token.clone()))
222 }); 273 });
223 match_from_children.and_then(|match_| { 274 match_.ignored_comments.extend(new_ignored_comments);
224 if pattern_children.count() == 0 && code_children.count() == 0 { 275 pattern_children
225 Some(match_) 276 .by_ref()
226 } else { 277 .zip(code_children.by_ref())
227 None 278 .fold(Some(match_), |accum, (a, b)| {
228 } 279 accum.and_then(|match_| check(&a, &b, placeholders, match_))
229 }) 280 })
281 .filter(|_| {
282 pattern_children.next().is_none() && code_children.next().is_none()
283 })
284 }
230 } 285 }
231 } 286 }
232 _ => None, 287 _ => None,
@@ -434,4 +489,13 @@ mod tests {
434 "fn main() { bar(5)/* using 5 */ }", 489 "fn main() { bar(5)/* using 5 */ }",
435 ) 490 )
436 } 491 }
492
493 #[test]
494 fn ssr_struct_lit() {
495 assert_ssr_transform(
496 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
497 "fn main() { foo{b:2, a:1} }",
498 "fn main() { foo::new(1, 2) }",
499 )
500 }
437} 501}