diff options
Diffstat (limited to 'crates/ra_ide_api/src')
26 files changed, 2119 insertions, 1035 deletions
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 09913787b..050249c0e 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs | |||
@@ -4,7 +4,7 @@ use std::{fmt, sync::Arc, time}; | |||
4 | 4 | ||
5 | use ra_db::{ | 5 | use ra_db::{ |
6 | salsa::{Database, Durability, SweepStrategy}, | 6 | salsa::{Database, Durability, SweepStrategy}, |
7 | CrateGraph, CrateId, FileId, SourceDatabase, SourceRoot, SourceRootId, | 7 | CrateGraph, CrateId, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, |
8 | }; | 8 | }; |
9 | use ra_prof::{memory_usage, profile, Bytes}; | 9 | use ra_prof::{memory_usage, profile, Bytes}; |
10 | use ra_syntax::SourceFile; | 10 | use ra_syntax::SourceFile; |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index 28c8324d0..b4df6ee2a 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -119,27 +119,28 @@ mod tests { | |||
119 | ", | 119 | ", |
120 | ), | 120 | ), |
121 | @r###" | 121 | @r###" |
122 | ⋮[ | 122 | [ |
123 | ⋮ CompletionItem { | 123 | CompletionItem { |
124 | ⋮ label: "foo", | 124 | label: "foo()", |
125 | ⋮ source_range: [187; 187), | 125 | source_range: [187; 187), |
126 | ⋮ delete: [187; 187), | 126 | delete: [187; 187), |
127 | ⋮ insert: "foo()$0", | 127 | insert: "foo()$0", |
128 | ⋮ kind: Method, | 128 | kind: Method, |
129 | ⋮ detail: "fn foo(self)", | 129 | lookup: "foo", |
130 | ⋮ }, | 130 | detail: "fn foo(self)", |
131 | ⋮ CompletionItem { | 131 | }, |
132 | ⋮ label: "the_field", | 132 | CompletionItem { |
133 | ⋮ source_range: [187; 187), | 133 | label: "the_field", |
134 | ⋮ delete: [187; 187), | 134 | source_range: [187; 187), |
135 | ⋮ insert: "the_field", | 135 | delete: [187; 187), |
136 | ⋮ kind: Field, | 136 | insert: "the_field", |
137 | ⋮ detail: "(u32,)", | 137 | kind: Field, |
138 | ⋮ documentation: Documentation( | 138 | detail: "(u32,)", |
139 | ⋮ "This is the_field", | 139 | documentation: Documentation( |
140 | ⋮ ), | 140 | "This is the_field", |
141 | ⋮ }, | 141 | ), |
142 | ⋮] | 142 | }, |
143 | ] | ||
143 | "### | 144 | "### |
144 | ); | 145 | ); |
145 | } | 146 | } |
@@ -158,24 +159,25 @@ mod tests { | |||
158 | ", | 159 | ", |
159 | ), | 160 | ), |
160 | @r###" | 161 | @r###" |
161 | ⋮[ | 162 | [ |
162 | ⋮ CompletionItem { | 163 | CompletionItem { |
163 | ⋮ label: "foo", | 164 | label: "foo()", |
164 | ⋮ source_range: [126; 126), | 165 | source_range: [126; 126), |
165 | ⋮ delete: [126; 126), | 166 | delete: [126; 126), |
166 | ⋮ insert: "foo()$0", | 167 | insert: "foo()$0", |
167 | ⋮ kind: Method, | 168 | kind: Method, |
168 | ⋮ detail: "fn foo(&self)", | 169 | lookup: "foo", |
169 | ⋮ }, | 170 | detail: "fn foo(&self)", |
170 | ⋮ CompletionItem { | 171 | }, |
171 | ⋮ label: "the_field", | 172 | CompletionItem { |
172 | ⋮ source_range: [126; 126), | 173 | label: "the_field", |
173 | ⋮ delete: [126; 126), | 174 | source_range: [126; 126), |
174 | ⋮ insert: "the_field", | 175 | delete: [126; 126), |
175 | ⋮ kind: Field, | 176 | insert: "the_field", |
176 | ⋮ detail: "(u32, i32)", | 177 | kind: Field, |
177 | ⋮ }, | 178 | detail: "(u32, i32)", |
178 | ⋮] | 179 | }, |
180 | ] | ||
179 | "### | 181 | "### |
180 | ); | 182 | ); |
181 | } | 183 | } |
@@ -210,16 +212,17 @@ mod tests { | |||
210 | ", | 212 | ", |
211 | ), | 213 | ), |
212 | @r###" | 214 | @r###" |
213 | ⋮[ | 215 | [ |
214 | ⋮ CompletionItem { | 216 | CompletionItem { |
215 | ⋮ label: "the_method", | 217 | label: "the_method()", |
216 | ⋮ source_range: [144; 144), | 218 | source_range: [144; 144), |
217 | ⋮ delete: [144; 144), | 219 | delete: [144; 144), |
218 | ⋮ insert: "the_method()$0", | 220 | insert: "the_method()$0", |
219 | ⋮ kind: Method, | 221 | kind: Method, |
220 | ⋮ detail: "fn the_method(&self)", | 222 | lookup: "the_method", |
221 | ⋮ }, | 223 | detail: "fn the_method(&self)", |
222 | ⋮] | 224 | }, |
225 | ] | ||
223 | "### | 226 | "### |
224 | ); | 227 | ); |
225 | } | 228 | } |
@@ -238,16 +241,17 @@ mod tests { | |||
238 | ", | 241 | ", |
239 | ), | 242 | ), |
240 | @r###" | 243 | @r###" |
241 | ⋮[ | 244 | [ |
242 | ⋮ CompletionItem { | 245 | CompletionItem { |
243 | ⋮ label: "the_method", | 246 | label: "the_method()", |
244 | ⋮ source_range: [151; 151), | 247 | source_range: [151; 151), |
245 | ⋮ delete: [151; 151), | 248 | delete: [151; 151), |
246 | ⋮ insert: "the_method()$0", | 249 | insert: "the_method()$0", |
247 | ⋮ kind: Method, | 250 | kind: Method, |
248 | ⋮ detail: "fn the_method(&self)", | 251 | lookup: "the_method", |
249 | ⋮ }, | 252 | detail: "fn the_method(&self)", |
250 | ⋮] | 253 | }, |
254 | ] | ||
251 | "### | 255 | "### |
252 | ); | 256 | ); |
253 | } | 257 | } |
@@ -266,16 +270,17 @@ mod tests { | |||
266 | ", | 270 | ", |
267 | ), | 271 | ), |
268 | @r###" | 272 | @r###" |
269 | ⋮[ | 273 | [ |
270 | ⋮ CompletionItem { | 274 | CompletionItem { |
271 | ⋮ label: "the_method", | 275 | label: "the_method()", |
272 | ⋮ source_range: [155; 155), | 276 | source_range: [155; 155), |
273 | ⋮ delete: [155; 155), | 277 | delete: [155; 155), |
274 | ⋮ insert: "the_method()$0", | 278 | insert: "the_method()$0", |
275 | ⋮ kind: Method, | 279 | kind: Method, |
276 | ⋮ detail: "fn the_method(&self)", | 280 | lookup: "the_method", |
277 | ⋮ }, | 281 | detail: "fn the_method(&self)", |
278 | ⋮] | 282 | }, |
283 | ] | ||
279 | "### | 284 | "### |
280 | ); | 285 | ); |
281 | } | 286 | } |
@@ -317,16 +322,17 @@ mod tests { | |||
317 | ", | 322 | ", |
318 | ), | 323 | ), |
319 | @r###" | 324 | @r###" |
320 | ⋮[ | 325 | [ |
321 | ⋮ CompletionItem { | 326 | CompletionItem { |
322 | ⋮ label: "the_method", | 327 | label: "the_method()", |
323 | ⋮ source_range: [249; 249), | 328 | source_range: [249; 249), |
324 | ⋮ delete: [249; 249), | 329 | delete: [249; 249), |
325 | ⋮ insert: "the_method()$0", | 330 | insert: "the_method()$0", |
326 | ⋮ kind: Method, | 331 | kind: Method, |
327 | ⋮ detail: "fn the_method(&self)", | 332 | lookup: "the_method", |
328 | ⋮ }, | 333 | detail: "fn the_method(&self)", |
329 | ⋮] | 334 | }, |
335 | ] | ||
330 | "### | 336 | "### |
331 | ); | 337 | ); |
332 | } | 338 | } |
@@ -386,16 +392,17 @@ mod tests { | |||
386 | ", | 392 | ", |
387 | ), | 393 | ), |
388 | @r###" | 394 | @r###" |
389 | ⋮[ | 395 | [ |
390 | ⋮ CompletionItem { | 396 | CompletionItem { |
391 | ⋮ label: "blah", | 397 | label: "blah()", |
392 | ⋮ source_range: [299; 300), | 398 | source_range: [299; 300), |
393 | ⋮ delete: [299; 300), | 399 | delete: [299; 300), |
394 | ⋮ insert: "blah()$0", | 400 | insert: "blah()$0", |
395 | ⋮ kind: Method, | 401 | kind: Method, |
396 | ⋮ detail: "pub fn blah(&self)", | 402 | lookup: "blah", |
397 | ⋮ }, | 403 | detail: "pub fn blah(&self)", |
398 | ⋮] | 404 | }, |
405 | ] | ||
399 | "### | 406 | "### |
400 | ); | 407 | ); |
401 | } | 408 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs index d808b2357..09f743c66 100644 --- a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | |||
@@ -56,6 +56,16 @@ mod tests { | |||
56 | do_reference_completion( | 56 | do_reference_completion( |
57 | " | 57 | " |
58 | //- /main.rs | 58 | //- /main.rs |
59 | /// Creates a [`Vec`] containing the arguments. | ||
60 | /// | ||
61 | /// - Create a [`Vec`] containing a given list of elements: | ||
62 | /// | ||
63 | /// ``` | ||
64 | /// let v = vec![1, 2, 3]; | ||
65 | /// assert_eq!(v[0], 1); | ||
66 | /// assert_eq!(v[1], 2); | ||
67 | /// assert_eq!(v[2], 3); | ||
68 | /// ``` | ||
59 | macro_rules! vec { | 69 | macro_rules! vec { |
60 | () => {} | 70 | () => {} |
61 | } | 71 | } |
@@ -68,13 +78,61 @@ mod tests { | |||
68 | @r##"[ | 78 | @r##"[ |
69 | CompletionItem { | 79 | CompletionItem { |
70 | label: "vec!", | 80 | label: "vec!", |
71 | source_range: [46; 46), | 81 | source_range: [280; 280), |
72 | delete: [46; 46), | 82 | delete: [280; 280), |
73 | insert: "vec![$0]", | 83 | insert: "vec![$0]", |
74 | kind: Macro, | 84 | kind: Macro, |
75 | detail: "macro_rules! vec", | 85 | detail: "macro_rules! vec", |
86 | documentation: Documentation( | ||
87 | "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```", | ||
88 | ), | ||
76 | }, | 89 | }, |
77 | ]"## | 90 | ]"## |
78 | ); | 91 | ); |
79 | } | 92 | } |
93 | |||
94 | #[test] | ||
95 | fn completes_macros_braces_guessing() { | ||
96 | assert_debug_snapshot!( | ||
97 | do_reference_completion( | ||
98 | " | ||
99 | //- /main.rs | ||
100 | /// Foo | ||
101 | /// | ||
102 | /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`. | ||
103 | /// Call as `let _=foo! { hello world };` | ||
104 | macro_rules! foo { | ||
105 | () => {} | ||
106 | } | ||
107 | |||
108 | fn main() { | ||
109 | <|> | ||
110 | } | ||
111 | " | ||
112 | ), | ||
113 | @r###"[ | ||
114 | CompletionItem { | ||
115 | label: "foo!", | ||
116 | source_range: [163; 163), | ||
117 | delete: [163; 163), | ||
118 | insert: "foo! {$0}", | ||
119 | kind: Macro, | ||
120 | detail: "macro_rules! foo", | ||
121 | documentation: Documentation( | ||
122 | "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`", | ||
123 | ), | ||
124 | }, | ||
125 | CompletionItem { | ||
126 | label: "main()", | ||
127 | source_range: [163; 163), | ||
128 | delete: [163; 163), | ||
129 | insert: "main()$0", | ||
130 | kind: Function, | ||
131 | lookup: "main", | ||
132 | detail: "fn main()", | ||
133 | }, | ||
134 | ] | ||
135 | "### | ||
136 | ); | ||
137 | } | ||
80 | } | 138 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index e01197fe4..23dece73c 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs | |||
@@ -375,19 +375,22 @@ mod tests { | |||
375 | fn foo() { let _ = S::<|> } | 375 | fn foo() { let _ = S::<|> } |
376 | " | 376 | " |
377 | ), | 377 | ), |
378 | @r###"[ | 378 | @r###" |
379 | CompletionItem { | 379 | [ |
380 | label: "m", | 380 | CompletionItem { |
381 | source_range: [100; 100), | 381 | label: "m()", |
382 | delete: [100; 100), | 382 | source_range: [100; 100), |
383 | insert: "m()$0", | 383 | delete: [100; 100), |
384 | kind: Function, | 384 | insert: "m()$0", |
385 | detail: "fn m()", | 385 | kind: Function, |
386 | documentation: Documentation( | 386 | lookup: "m", |
387 | "An associated method", | 387 | detail: "fn m()", |
388 | ), | 388 | documentation: Documentation( |
389 | }, | 389 | "An associated method", |
390 | ]"### | 390 | ), |
391 | }, | ||
392 | ] | ||
393 | "### | ||
391 | ); | 394 | ); |
392 | } | 395 | } |
393 | 396 | ||
@@ -474,19 +477,22 @@ mod tests { | |||
474 | fn foo() { let _ = S::<|> } | 477 | fn foo() { let _ = S::<|> } |
475 | " | 478 | " |
476 | ), | 479 | ), |
477 | @r###"[ | 480 | @r###" |
478 | CompletionItem { | 481 | [ |
479 | label: "m", | 482 | CompletionItem { |
480 | source_range: [100; 100), | 483 | label: "m()", |
481 | delete: [100; 100), | 484 | source_range: [100; 100), |
482 | insert: "m()$0", | 485 | delete: [100; 100), |
483 | kind: Function, | 486 | insert: "m()$0", |
484 | detail: "fn m()", | 487 | kind: Function, |
485 | documentation: Documentation( | 488 | lookup: "m", |
486 | "An associated method", | 489 | detail: "fn m()", |
487 | ), | 490 | documentation: Documentation( |
488 | }, | 491 | "An associated method", |
489 | ]"### | 492 | ), |
493 | }, | ||
494 | ] | ||
495 | "### | ||
490 | ); | 496 | ); |
491 | } | 497 | } |
492 | 498 | ||
@@ -507,19 +513,22 @@ mod tests { | |||
507 | fn foo() { let _ = U::<|> } | 513 | fn foo() { let _ = U::<|> } |
508 | " | 514 | " |
509 | ), | 515 | ), |
510 | @r###"[ | 516 | @r###" |
511 | CompletionItem { | 517 | [ |
512 | label: "m", | 518 | CompletionItem { |
513 | source_range: [101; 101), | 519 | label: "m()", |
514 | delete: [101; 101), | 520 | source_range: [101; 101), |
515 | insert: "m()$0", | 521 | delete: [101; 101), |
516 | kind: Function, | 522 | insert: "m()$0", |
517 | detail: "fn m()", | 523 | kind: Function, |
518 | documentation: Documentation( | 524 | lookup: "m", |
519 | "An associated method", | 525 | detail: "fn m()", |
520 | ), | 526 | documentation: Documentation( |
521 | }, | 527 | "An associated method", |
522 | ]"### | 528 | ), |
529 | }, | ||
530 | ] | ||
531 | "### | ||
523 | ); | 532 | ); |
524 | } | 533 | } |
525 | 534 | ||
@@ -564,24 +573,28 @@ mod tests { | |||
564 | } | 573 | } |
565 | " | 574 | " |
566 | ), | 575 | ), |
567 | @r###"[ | 576 | @r###" |
568 | CompletionItem { | 577 | [ |
569 | label: "bar", | 578 | CompletionItem { |
570 | source_range: [185; 185), | 579 | label: "bar()", |
571 | delete: [185; 185), | 580 | source_range: [185; 185), |
572 | insert: "bar()$0", | 581 | delete: [185; 185), |
573 | kind: Function, | 582 | insert: "bar()$0", |
574 | detail: "fn bar()", | 583 | kind: Function, |
575 | }, | 584 | lookup: "bar", |
576 | CompletionItem { | 585 | detail: "fn bar()", |
577 | label: "foo", | 586 | }, |
578 | source_range: [185; 185), | 587 | CompletionItem { |
579 | delete: [185; 185), | 588 | label: "foo()", |
580 | insert: "foo()$0", | 589 | source_range: [185; 185), |
581 | kind: Function, | 590 | delete: [185; 185), |
582 | detail: "fn foo()", | 591 | insert: "foo()$0", |
583 | }, | 592 | kind: Function, |
584 | ]"### | 593 | lookup: "foo", |
594 | detail: "fn foo()", | ||
595 | }, | ||
596 | ] | ||
597 | "### | ||
585 | ); | 598 | ); |
586 | } | 599 | } |
587 | 600 | ||
@@ -600,24 +613,27 @@ mod tests { | |||
600 | } | 613 | } |
601 | " | 614 | " |
602 | ), | 615 | ), |
603 | @r###"[ | 616 | @r###" |
604 | CompletionItem { | 617 | [ |
605 | label: "foo!", | 618 | CompletionItem { |
606 | source_range: [179; 179), | 619 | label: "foo!", |
607 | delete: [179; 179), | 620 | source_range: [179; 179), |
608 | insert: "foo!($0)", | 621 | delete: [179; 179), |
609 | kind: Macro, | 622 | insert: "foo!($0)", |
610 | detail: "#[macro_export]\nmacro_rules! foo", | 623 | kind: Macro, |
611 | }, | 624 | detail: "#[macro_export]\nmacro_rules! foo", |
612 | CompletionItem { | 625 | }, |
613 | label: "main", | 626 | CompletionItem { |
614 | source_range: [179; 179), | 627 | label: "main()", |
615 | delete: [179; 179), | 628 | source_range: [179; 179), |
616 | insert: "main()$0", | 629 | delete: [179; 179), |
617 | kind: Function, | 630 | insert: "main()$0", |
618 | detail: "fn main()", | 631 | kind: Function, |
619 | }, | 632 | lookup: "main", |
620 | ]"### | 633 | detail: "fn main()", |
634 | }, | ||
635 | ] | ||
636 | "### | ||
621 | ); | 637 | ); |
622 | } | 638 | } |
623 | } | 639 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs index 445a02676..555cecb73 100644 --- a/crates/ra_ide_api/src/completion/complete_postfix.rs +++ b/crates/ra_ide_api/src/completion/complete_postfix.rs | |||
@@ -8,7 +8,7 @@ use crate::{ | |||
8 | CompletionItem, | 8 | CompletionItem, |
9 | }; | 9 | }; |
10 | use hir::{Ty, TypeCtor}; | 10 | use hir::{Ty, TypeCtor}; |
11 | use ra_syntax::{ast::AstNode, TextRange}; | 11 | use ra_syntax::{ast::AstNode, TextRange, TextUnit}; |
12 | use ra_text_edit::TextEditBuilder; | 12 | use ra_text_edit::TextEditBuilder; |
13 | 13 | ||
14 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 14 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { |
@@ -42,7 +42,13 @@ fn is_bool_or_unknown(ty: Option<Ty>) -> bool { | |||
42 | 42 | ||
43 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 43 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { |
44 | if let Some(dot_receiver) = &ctx.dot_receiver { | 44 | if let Some(dot_receiver) = &ctx.dot_receiver { |
45 | let receiver_text = dot_receiver.syntax().text().to_string(); | 45 | let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { |
46 | let text = dot_receiver.syntax().text(); | ||
47 | let without_dot = ..text.len() - TextUnit::of_char('.'); | ||
48 | text.slice(without_dot).to_string() | ||
49 | } else { | ||
50 | dot_receiver.syntax().text().to_string() | ||
51 | }; | ||
46 | let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); | 52 | let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); |
47 | if is_bool_or_unknown(receiver_ty) { | 53 | if is_bool_or_unknown(receiver_ty) { |
48 | postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) | 54 | postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) |
@@ -209,4 +215,61 @@ mod tests { | |||
209 | ]"### | 215 | ]"### |
210 | ); | 216 | ); |
211 | } | 217 | } |
218 | |||
219 | #[test] | ||
220 | fn postfix_completion_works_for_ambiguous_float_literal() { | ||
221 | assert_debug_snapshot!( | ||
222 | do_postfix_completion( | ||
223 | r#" | ||
224 | fn main() { | ||
225 | 42.<|> | ||
226 | } | ||
227 | "#, | ||
228 | ), | ||
229 | @r###"[ | ||
230 | CompletionItem { | ||
231 | label: "box", | ||
232 | source_range: [52; 52), | ||
233 | delete: [49; 52), | ||
234 | insert: "Box::new(42)", | ||
235 | detail: "Box::new(expr)", | ||
236 | }, | ||
237 | CompletionItem { | ||
238 | label: "dbg", | ||
239 | source_range: [52; 52), | ||
240 | delete: [49; 52), | ||
241 | insert: "dbg!(42)", | ||
242 | detail: "dbg!(expr)", | ||
243 | }, | ||
244 | CompletionItem { | ||
245 | label: "match", | ||
246 | source_range: [52; 52), | ||
247 | delete: [49; 52), | ||
248 | insert: "match 42 {\n ${1:_} => {$0\\},\n}", | ||
249 | detail: "match expr {}", | ||
250 | }, | ||
251 | CompletionItem { | ||
252 | label: "not", | ||
253 | source_range: [52; 52), | ||
254 | delete: [49; 52), | ||
255 | insert: "!42", | ||
256 | detail: "!expr", | ||
257 | }, | ||
258 | CompletionItem { | ||
259 | label: "ref", | ||
260 | source_range: [52; 52), | ||
261 | delete: [49; 52), | ||
262 | insert: "&42", | ||
263 | detail: "&expr", | ||
264 | }, | ||
265 | CompletionItem { | ||
266 | label: "refm", | ||
267 | source_range: [52; 52), | ||
268 | delete: [49; 52), | ||
269 | insert: "&mut 42", | ||
270 | detail: "&mut expr", | ||
271 | }, | ||
272 | ]"### | ||
273 | ); | ||
274 | } | ||
212 | } | 275 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 515a6285c..4e56de3f5 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs | |||
@@ -145,32 +145,35 @@ mod tests { | |||
145 | } | 145 | } |
146 | " | 146 | " |
147 | ), | 147 | ), |
148 | @r###"[ | 148 | @r###" |
149 | CompletionItem { | 149 | [ |
150 | label: "quux", | 150 | CompletionItem { |
151 | source_range: [91; 91), | 151 | label: "quux(…)", |
152 | delete: [91; 91), | 152 | source_range: [91; 91), |
153 | insert: "quux($0)", | 153 | delete: [91; 91), |
154 | kind: Function, | 154 | insert: "quux($0)", |
155 | detail: "fn quux(x: i32)", | 155 | kind: Function, |
156 | }, | 156 | lookup: "quux", |
157 | CompletionItem { | 157 | detail: "fn quux(x: i32)", |
158 | label: "x", | 158 | }, |
159 | source_range: [91; 91), | 159 | CompletionItem { |
160 | delete: [91; 91), | 160 | label: "x", |
161 | insert: "x", | 161 | source_range: [91; 91), |
162 | kind: Binding, | 162 | delete: [91; 91), |
163 | detail: "i32", | 163 | insert: "x", |
164 | }, | 164 | kind: Binding, |
165 | CompletionItem { | 165 | detail: "i32", |
166 | label: "y", | 166 | }, |
167 | source_range: [91; 91), | 167 | CompletionItem { |
168 | delete: [91; 91), | 168 | label: "y", |
169 | insert: "y", | 169 | source_range: [91; 91), |
170 | kind: Binding, | 170 | delete: [91; 91), |
171 | detail: "i32", | 171 | insert: "y", |
172 | }, | 172 | kind: Binding, |
173 | ]"### | 173 | detail: "i32", |
174 | }, | ||
175 | ] | ||
176 | "### | ||
174 | ); | 177 | ); |
175 | } | 178 | } |
176 | 179 | ||
@@ -190,31 +193,34 @@ mod tests { | |||
190 | } | 193 | } |
191 | " | 194 | " |
192 | ), | 195 | ), |
193 | @r###"[ | 196 | @r###" |
194 | CompletionItem { | 197 | [ |
195 | label: "a", | 198 | CompletionItem { |
196 | source_range: [242; 242), | 199 | label: "a", |
197 | delete: [242; 242), | 200 | source_range: [242; 242), |
198 | insert: "a", | 201 | delete: [242; 242), |
199 | kind: Binding, | 202 | insert: "a", |
200 | }, | 203 | kind: Binding, |
201 | CompletionItem { | 204 | }, |
202 | label: "b", | 205 | CompletionItem { |
203 | source_range: [242; 242), | 206 | label: "b", |
204 | delete: [242; 242), | 207 | source_range: [242; 242), |
205 | insert: "b", | 208 | delete: [242; 242), |
206 | kind: Binding, | 209 | insert: "b", |
207 | detail: "i32", | 210 | kind: Binding, |
208 | }, | 211 | detail: "i32", |
209 | CompletionItem { | 212 | }, |
210 | label: "quux", | 213 | CompletionItem { |
211 | source_range: [242; 242), | 214 | label: "quux()", |
212 | delete: [242; 242), | 215 | source_range: [242; 242), |
213 | insert: "quux()$0", | 216 | delete: [242; 242), |
214 | kind: Function, | 217 | insert: "quux()$0", |
215 | detail: "fn quux()", | 218 | kind: Function, |
216 | }, | 219 | lookup: "quux", |
217 | ]"### | 220 | detail: "fn quux()", |
221 | }, | ||
222 | ] | ||
223 | "### | ||
218 | ); | 224 | ); |
219 | } | 225 | } |
220 | 226 | ||
@@ -230,23 +236,26 @@ mod tests { | |||
230 | } | 236 | } |
231 | " | 237 | " |
232 | ), | 238 | ), |
233 | @r###"[ | 239 | @r###" |
234 | CompletionItem { | 240 | [ |
235 | label: "quux", | 241 | CompletionItem { |
236 | source_range: [95; 95), | 242 | label: "quux()", |
237 | delete: [95; 95), | 243 | source_range: [95; 95), |
238 | insert: "quux()$0", | 244 | delete: [95; 95), |
239 | kind: Function, | 245 | insert: "quux()$0", |
240 | detail: "fn quux()", | 246 | kind: Function, |
241 | }, | 247 | lookup: "quux", |
242 | CompletionItem { | 248 | detail: "fn quux()", |
243 | label: "x", | 249 | }, |
244 | source_range: [95; 95), | 250 | CompletionItem { |
245 | delete: [95; 95), | 251 | label: "x", |
246 | insert: "x", | 252 | source_range: [95; 95), |
247 | kind: Binding, | 253 | delete: [95; 95), |
248 | }, | 254 | insert: "x", |
249 | ]"### | 255 | kind: Binding, |
256 | }, | ||
257 | ] | ||
258 | "### | ||
250 | ); | 259 | ); |
251 | } | 260 | } |
252 | 261 | ||
@@ -260,23 +269,26 @@ mod tests { | |||
260 | } | 269 | } |
261 | " | 270 | " |
262 | ), | 271 | ), |
263 | @r###"[ | 272 | @r###" |
264 | CompletionItem { | 273 | [ |
265 | label: "T", | 274 | CompletionItem { |
266 | source_range: [52; 52), | 275 | label: "T", |
267 | delete: [52; 52), | 276 | source_range: [52; 52), |
268 | insert: "T", | 277 | delete: [52; 52), |
269 | kind: TypeParam, | 278 | insert: "T", |
270 | }, | 279 | kind: TypeParam, |
271 | CompletionItem { | 280 | }, |
272 | label: "quux", | 281 | CompletionItem { |
273 | source_range: [52; 52), | 282 | label: "quux()", |
274 | delete: [52; 52), | 283 | source_range: [52; 52), |
275 | insert: "quux()$0", | 284 | delete: [52; 52), |
276 | kind: Function, | 285 | insert: "quux()$0", |
277 | detail: "fn quux<T>()", | 286 | kind: Function, |
278 | }, | 287 | lookup: "quux", |
279 | ]"### | 288 | detail: "fn quux<T>()", |
289 | }, | ||
290 | ] | ||
291 | "### | ||
280 | ); | 292 | ); |
281 | } | 293 | } |
282 | 294 | ||
@@ -290,22 +302,56 @@ mod tests { | |||
290 | } | 302 | } |
291 | " | 303 | " |
292 | ), | 304 | ), |
293 | @r###"[ | 305 | @r###" |
294 | CompletionItem { | 306 | [ |
295 | label: "T", | 307 | CompletionItem { |
296 | source_range: [54; 54), | 308 | label: "T", |
297 | delete: [54; 54), | 309 | source_range: [54; 54), |
298 | insert: "T", | 310 | delete: [54; 54), |
299 | kind: TypeParam, | 311 | insert: "T", |
300 | }, | 312 | kind: TypeParam, |
301 | CompletionItem { | 313 | }, |
302 | label: "X", | 314 | CompletionItem { |
303 | source_range: [54; 54), | 315 | label: "X<…>", |
304 | delete: [54; 54), | 316 | source_range: [54; 54), |
305 | insert: "X", | 317 | delete: [54; 54), |
306 | kind: Struct, | 318 | insert: "X<$0>", |
307 | }, | 319 | kind: Struct, |
308 | ]"### | 320 | lookup: "X", |
321 | }, | ||
322 | ] | ||
323 | "### | ||
324 | ); | ||
325 | } | ||
326 | |||
327 | #[test] | ||
328 | fn completes_self_in_enum() { | ||
329 | assert_debug_snapshot!( | ||
330 | do_reference_completion( | ||
331 | r" | ||
332 | enum X { | ||
333 | Y(<|>) | ||
334 | } | ||
335 | " | ||
336 | ), | ||
337 | @r###" | ||
338 | [ | ||
339 | CompletionItem { | ||
340 | label: "Self", | ||
341 | source_range: [48; 48), | ||
342 | delete: [48; 48), | ||
343 | insert: "Self", | ||
344 | kind: TypeParam, | ||
345 | }, | ||
346 | CompletionItem { | ||
347 | label: "X", | ||
348 | source_range: [48; 48), | ||
349 | delete: [48; 48), | ||
350 | insert: "X", | ||
351 | kind: Enum, | ||
352 | }, | ||
353 | ] | ||
354 | "### | ||
309 | ); | 355 | ); |
310 | } | 356 | } |
311 | 357 | ||
@@ -321,30 +367,33 @@ mod tests { | |||
321 | } | 367 | } |
322 | " | 368 | " |
323 | ), | 369 | ), |
324 | @r###"[ | 370 | @r###" |
325 | CompletionItem { | 371 | [ |
326 | label: "Baz", | 372 | CompletionItem { |
327 | source_range: [105; 105), | 373 | label: "Baz", |
328 | delete: [105; 105), | 374 | source_range: [105; 105), |
329 | insert: "Baz", | 375 | delete: [105; 105), |
330 | kind: Enum, | 376 | insert: "Baz", |
331 | }, | 377 | kind: Enum, |
332 | CompletionItem { | 378 | }, |
333 | label: "Foo", | 379 | CompletionItem { |
334 | source_range: [105; 105), | 380 | label: "Foo", |
335 | delete: [105; 105), | 381 | source_range: [105; 105), |
336 | insert: "Foo", | 382 | delete: [105; 105), |
337 | kind: Struct, | 383 | insert: "Foo", |
338 | }, | 384 | kind: Struct, |
339 | CompletionItem { | 385 | }, |
340 | label: "quux", | 386 | CompletionItem { |
341 | source_range: [105; 105), | 387 | label: "quux()", |
342 | delete: [105; 105), | 388 | source_range: [105; 105), |
343 | insert: "quux()$0", | 389 | delete: [105; 105), |
344 | kind: Function, | 390 | insert: "quux()$0", |
345 | detail: "fn quux()", | 391 | kind: Function, |
346 | }, | 392 | lookup: "quux", |
347 | ]"### | 393 | detail: "fn quux()", |
394 | }, | ||
395 | ] | ||
396 | "### | ||
348 | ); | 397 | ); |
349 | } | 398 | } |
350 | 399 | ||
@@ -384,23 +433,26 @@ mod tests { | |||
384 | } | 433 | } |
385 | " | 434 | " |
386 | ), | 435 | ), |
387 | @r###"[ | 436 | @r###" |
388 | CompletionItem { | 437 | [ |
389 | label: "Bar", | 438 | CompletionItem { |
390 | source_range: [117; 117), | 439 | label: "Bar", |
391 | delete: [117; 117), | 440 | source_range: [117; 117), |
392 | insert: "Bar", | 441 | delete: [117; 117), |
393 | kind: Struct, | 442 | insert: "Bar", |
394 | }, | 443 | kind: Struct, |
395 | CompletionItem { | 444 | }, |
396 | label: "quux", | 445 | CompletionItem { |
397 | source_range: [117; 117), | 446 | label: "quux()", |
398 | delete: [117; 117), | 447 | source_range: [117; 117), |
399 | insert: "quux()$0", | 448 | delete: [117; 117), |
400 | kind: Function, | 449 | insert: "quux()$0", |
401 | detail: "fn quux()", | 450 | kind: Function, |
402 | }, | 451 | lookup: "quux", |
403 | ]"### | 452 | detail: "fn quux()", |
453 | }, | ||
454 | ] | ||
455 | "### | ||
404 | ); | 456 | ); |
405 | } | 457 | } |
406 | 458 | ||
@@ -413,23 +465,26 @@ mod tests { | |||
413 | fn x() -> <|> | 465 | fn x() -> <|> |
414 | " | 466 | " |
415 | ), | 467 | ), |
416 | @r###"[ | 468 | @r###" |
417 | CompletionItem { | 469 | [ |
418 | label: "Foo", | 470 | CompletionItem { |
419 | source_range: [55; 55), | 471 | label: "Foo", |
420 | delete: [55; 55), | 472 | source_range: [55; 55), |
421 | insert: "Foo", | 473 | delete: [55; 55), |
422 | kind: Struct, | 474 | insert: "Foo", |
423 | }, | 475 | kind: Struct, |
424 | CompletionItem { | 476 | }, |
425 | label: "x", | 477 | CompletionItem { |
426 | source_range: [55; 55), | 478 | label: "x()", |
427 | delete: [55; 55), | 479 | source_range: [55; 55), |
428 | insert: "x()$0", | 480 | delete: [55; 55), |
429 | kind: Function, | 481 | insert: "x()$0", |
430 | detail: "fn x()", | 482 | kind: Function, |
431 | }, | 483 | lookup: "x", |
432 | ]"### | 484 | detail: "fn x()", |
485 | }, | ||
486 | ] | ||
487 | "### | ||
433 | ); | 488 | ); |
434 | } | 489 | } |
435 | 490 | ||
@@ -447,24 +502,27 @@ mod tests { | |||
447 | } | 502 | } |
448 | " | 503 | " |
449 | ), | 504 | ), |
450 | @r###"[ | 505 | @r###" |
451 | CompletionItem { | 506 | [ |
452 | label: "bar", | 507 | CompletionItem { |
453 | source_range: [146; 146), | 508 | label: "bar", |
454 | delete: [146; 146), | 509 | source_range: [146; 146), |
455 | insert: "bar", | 510 | delete: [146; 146), |
456 | kind: Binding, | 511 | insert: "bar", |
457 | detail: "i32", | 512 | kind: Binding, |
458 | }, | 513 | detail: "i32", |
459 | CompletionItem { | 514 | }, |
460 | label: "foo", | 515 | CompletionItem { |
461 | source_range: [146; 146), | 516 | label: "foo()", |
462 | delete: [146; 146), | 517 | source_range: [146; 146), |
463 | insert: "foo()$0", | 518 | delete: [146; 146), |
464 | kind: Function, | 519 | insert: "foo()$0", |
465 | detail: "fn foo()", | 520 | kind: Function, |
466 | }, | 521 | lookup: "foo", |
467 | ]"### | 522 | detail: "fn foo()", |
523 | }, | ||
524 | ] | ||
525 | "### | ||
468 | ); | 526 | ); |
469 | } | 527 | } |
470 | 528 | ||
@@ -509,30 +567,33 @@ mod tests { | |||
509 | } | 567 | } |
510 | " | 568 | " |
511 | ), | 569 | ), |
512 | @r#"[ | 570 | @r###" |
513 | CompletionItem { | 571 | [ |
514 | label: "Option", | 572 | CompletionItem { |
515 | source_range: [18; 18), | 573 | label: "Option", |
516 | delete: [18; 18), | 574 | source_range: [18; 18), |
517 | insert: "Option", | 575 | delete: [18; 18), |
518 | kind: Struct, | 576 | insert: "Option", |
519 | }, | 577 | kind: Struct, |
520 | CompletionItem { | 578 | }, |
521 | label: "foo", | 579 | CompletionItem { |
522 | source_range: [18; 18), | 580 | label: "foo()", |
523 | delete: [18; 18), | 581 | source_range: [18; 18), |
524 | insert: "foo()$0", | 582 | delete: [18; 18), |
525 | kind: Function, | 583 | insert: "foo()$0", |
526 | detail: "fn foo()", | 584 | kind: Function, |
527 | }, | 585 | lookup: "foo", |
528 | CompletionItem { | 586 | detail: "fn foo()", |
529 | label: "std", | 587 | }, |
530 | source_range: [18; 18), | 588 | CompletionItem { |
531 | delete: [18; 18), | 589 | label: "std", |
532 | insert: "std", | 590 | source_range: [18; 18), |
533 | kind: Module, | 591 | delete: [18; 18), |
534 | }, | 592 | insert: "std", |
535 | ]"# | 593 | kind: Module, |
594 | }, | ||
595 | ] | ||
596 | "### | ||
536 | ); | 597 | ); |
537 | } | 598 | } |
538 | 599 | ||
@@ -569,54 +630,57 @@ mod tests { | |||
569 | } | 630 | } |
570 | " | 631 | " |
571 | ), | 632 | ), |
572 | @r##"[ | 633 | @r###" |
573 | CompletionItem { | 634 | [ |
574 | label: "bar!", | 635 | CompletionItem { |
575 | source_range: [252; 252), | 636 | label: "bar!", |
576 | delete: [252; 252), | 637 | source_range: [252; 252), |
577 | insert: "bar!($0)", | 638 | delete: [252; 252), |
578 | kind: Macro, | 639 | insert: "bar!($0)", |
579 | detail: "macro_rules! bar", | 640 | kind: Macro, |
580 | }, | 641 | detail: "macro_rules! bar", |
581 | CompletionItem { | 642 | }, |
582 | label: "baz!", | 643 | CompletionItem { |
583 | source_range: [252; 252), | 644 | label: "baz!", |
584 | delete: [252; 252), | 645 | source_range: [252; 252), |
585 | insert: "baz!($0)", | 646 | delete: [252; 252), |
586 | kind: Macro, | 647 | insert: "baz!($0)", |
587 | detail: "#[macro_export]\nmacro_rules! baz", | 648 | kind: Macro, |
588 | }, | 649 | detail: "#[macro_export]\nmacro_rules! baz", |
589 | CompletionItem { | 650 | }, |
590 | label: "foo!", | 651 | CompletionItem { |
591 | source_range: [252; 252), | 652 | label: "foo!", |
592 | delete: [252; 252), | 653 | source_range: [252; 252), |
593 | insert: "foo!($0)", | 654 | delete: [252; 252), |
594 | kind: Macro, | 655 | insert: "foo!($0)", |
595 | detail: "macro_rules! foo", | 656 | kind: Macro, |
596 | }, | 657 | detail: "macro_rules! foo", |
597 | CompletionItem { | 658 | }, |
598 | label: "m1", | 659 | CompletionItem { |
599 | source_range: [252; 252), | 660 | label: "m1", |
600 | delete: [252; 252), | 661 | source_range: [252; 252), |
601 | insert: "m1", | 662 | delete: [252; 252), |
602 | kind: Module, | 663 | insert: "m1", |
603 | }, | 664 | kind: Module, |
604 | CompletionItem { | 665 | }, |
605 | label: "m2", | 666 | CompletionItem { |
606 | source_range: [252; 252), | 667 | label: "m2", |
607 | delete: [252; 252), | 668 | source_range: [252; 252), |
608 | insert: "m2", | 669 | delete: [252; 252), |
609 | kind: Module, | 670 | insert: "m2", |
610 | }, | 671 | kind: Module, |
611 | CompletionItem { | 672 | }, |
612 | label: "main", | 673 | CompletionItem { |
613 | source_range: [252; 252), | 674 | label: "main()", |
614 | delete: [252; 252), | 675 | source_range: [252; 252), |
615 | insert: "main()$0", | 676 | delete: [252; 252), |
616 | kind: Function, | 677 | insert: "main()$0", |
617 | detail: "fn main()", | 678 | kind: Function, |
618 | }, | 679 | lookup: "main", |
619 | ]"## | 680 | detail: "fn main()", |
681 | }, | ||
682 | ] | ||
683 | "### | ||
620 | ); | 684 | ); |
621 | } | 685 | } |
622 | 686 | ||
@@ -635,24 +699,27 @@ mod tests { | |||
635 | } | 699 | } |
636 | " | 700 | " |
637 | ), | 701 | ), |
638 | @r##"[ | 702 | @r###" |
639 | CompletionItem { | 703 | [ |
640 | label: "foo", | 704 | CompletionItem { |
641 | source_range: [49; 49), | 705 | label: "foo!", |
642 | delete: [49; 49), | 706 | source_range: [49; 49), |
643 | insert: "foo()$0", | 707 | delete: [49; 49), |
644 | kind: Function, | 708 | insert: "foo!($0)", |
645 | detail: "fn foo()", | 709 | kind: Macro, |
646 | }, | 710 | detail: "macro_rules! foo", |
647 | CompletionItem { | 711 | }, |
648 | label: "foo!", | 712 | CompletionItem { |
649 | source_range: [49; 49), | 713 | label: "foo()", |
650 | delete: [49; 49), | 714 | source_range: [49; 49), |
651 | insert: "foo!($0)", | 715 | delete: [49; 49), |
652 | kind: Macro, | 716 | insert: "foo()$0", |
653 | detail: "macro_rules! foo", | 717 | kind: Function, |
654 | }, | 718 | lookup: "foo", |
655 | ]"## | 719 | detail: "fn foo()", |
720 | }, | ||
721 | ] | ||
722 | "### | ||
656 | ); | 723 | ); |
657 | } | 724 | } |
658 | 725 | ||
@@ -671,24 +738,27 @@ mod tests { | |||
671 | } | 738 | } |
672 | " | 739 | " |
673 | ), | 740 | ), |
674 | @r##"[ | 741 | @r###" |
675 | CompletionItem { | 742 | [ |
676 | label: "foo!", | 743 | CompletionItem { |
677 | source_range: [57; 57), | 744 | label: "foo!", |
678 | delete: [57; 57), | 745 | source_range: [57; 57), |
679 | insert: "foo!($0)", | 746 | delete: [57; 57), |
680 | kind: Macro, | 747 | insert: "foo!($0)", |
681 | detail: "macro_rules! foo", | 748 | kind: Macro, |
682 | }, | 749 | detail: "macro_rules! foo", |
683 | CompletionItem { | 750 | }, |
684 | label: "main", | 751 | CompletionItem { |
685 | source_range: [57; 57), | 752 | label: "main()", |
686 | delete: [57; 57), | 753 | source_range: [57; 57), |
687 | insert: "main()$0", | 754 | delete: [57; 57), |
688 | kind: Function, | 755 | insert: "main()$0", |
689 | detail: "fn main()", | 756 | kind: Function, |
690 | }, | 757 | lookup: "main", |
691 | ]"## | 758 | detail: "fn main()", |
759 | }, | ||
760 | ] | ||
761 | "### | ||
692 | ); | 762 | ); |
693 | } | 763 | } |
694 | 764 | ||
@@ -707,24 +777,27 @@ mod tests { | |||
707 | } | 777 | } |
708 | " | 778 | " |
709 | ), | 779 | ), |
710 | @r##"[ | 780 | @r###" |
711 | CompletionItem { | 781 | [ |
712 | label: "foo!", | 782 | CompletionItem { |
713 | source_range: [50; 50), | 783 | label: "foo!", |
714 | delete: [50; 50), | 784 | source_range: [50; 50), |
715 | insert: "foo!($0)", | 785 | delete: [50; 50), |
716 | kind: Macro, | 786 | insert: "foo!($0)", |
717 | detail: "macro_rules! foo", | 787 | kind: Macro, |
718 | }, | 788 | detail: "macro_rules! foo", |
719 | CompletionItem { | 789 | }, |
720 | label: "main", | 790 | CompletionItem { |
721 | source_range: [50; 50), | 791 | label: "main()", |
722 | delete: [50; 50), | 792 | source_range: [50; 50), |
723 | insert: "main()$0", | 793 | delete: [50; 50), |
724 | kind: Function, | 794 | insert: "main()$0", |
725 | detail: "fn main()", | 795 | kind: Function, |
726 | }, | 796 | lookup: "main", |
727 | ]"## | 797 | detail: "fn main()", |
798 | }, | ||
799 | ] | ||
800 | "### | ||
728 | ); | 801 | ); |
729 | } | 802 | } |
730 | } | 803 | } |
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index e9ad06965..64cbc0f98 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs | |||
@@ -38,8 +38,11 @@ pub(crate) struct CompletionContext<'a> { | |||
38 | pub(super) is_new_item: bool, | 38 | pub(super) is_new_item: bool, |
39 | /// The receiver if this is a field or method access, i.e. writing something.<|> | 39 | /// The receiver if this is a field or method access, i.e. writing something.<|> |
40 | pub(super) dot_receiver: Option<ast::Expr>, | 40 | pub(super) dot_receiver: Option<ast::Expr>, |
41 | pub(super) dot_receiver_is_ambiguous_float_literal: bool, | ||
41 | /// If this is a call (method or function) in particular, i.e. the () are already there. | 42 | /// If this is a call (method or function) in particular, i.e. the () are already there. |
42 | pub(super) is_call: bool, | 43 | pub(super) is_call: bool, |
44 | pub(super) is_path_type: bool, | ||
45 | pub(super) has_type_args: bool, | ||
43 | } | 46 | } |
44 | 47 | ||
45 | impl<'a> CompletionContext<'a> { | 48 | impl<'a> CompletionContext<'a> { |
@@ -76,6 +79,9 @@ impl<'a> CompletionContext<'a> { | |||
76 | is_new_item: false, | 79 | is_new_item: false, |
77 | dot_receiver: None, | 80 | dot_receiver: None, |
78 | is_call: false, | 81 | is_call: false, |
82 | is_path_type: false, | ||
83 | has_type_args: false, | ||
84 | dot_receiver_is_ambiguous_float_literal: false, | ||
79 | }; | 85 | }; |
80 | ctx.fill(&original_parse, position.offset); | 86 | ctx.fill(&original_parse, position.offset); |
81 | Some(ctx) | 87 | Some(ctx) |
@@ -176,6 +182,9 @@ impl<'a> CompletionContext<'a> { | |||
176 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | 182 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) |
177 | .is_some(); | 183 | .is_some(); |
178 | 184 | ||
185 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | ||
186 | self.has_type_args = segment.type_arg_list().is_some(); | ||
187 | |||
179 | if let Some(mut path) = hir::Path::from_ast(path.clone()) { | 188 | if let Some(mut path) = hir::Path::from_ast(path.clone()) { |
180 | if !path.is_ident() { | 189 | if !path.is_ident() { |
181 | path.segments.pop().unwrap(); | 190 | path.segments.pop().unwrap(); |
@@ -228,6 +237,16 @@ impl<'a> CompletionContext<'a> { | |||
228 | .expr() | 237 | .expr() |
229 | .map(|e| e.syntax().text_range()) | 238 | .map(|e| e.syntax().text_range()) |
230 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 239 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); |
240 | self.dot_receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = | ||
241 | &self.dot_receiver | ||
242 | { | ||
243 | match l.kind() { | ||
244 | ast::LiteralKind::FloatNumber { suffix: _ } => l.token().text().ends_with('.'), | ||
245 | _ => false, | ||
246 | } | ||
247 | } else { | ||
248 | false | ||
249 | } | ||
231 | } | 250 | } |
232 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 251 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
233 | // As above | 252 | // As above |
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index b1f0390ec..3e6933bc1 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs | |||
@@ -216,6 +216,10 @@ impl Builder { | |||
216 | self.lookup = Some(lookup.into()); | 216 | self.lookup = Some(lookup.into()); |
217 | self | 217 | self |
218 | } | 218 | } |
219 | pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { | ||
220 | self.label = label.into(); | ||
221 | self | ||
222 | } | ||
219 | pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { | 223 | pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { |
220 | self.insert_text = Some(insert_text.into()); | 224 | self.insert_text = Some(insert_text.into()); |
221 | self | 225 | self |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 48028a2f9..aed4ce6d4 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | 2 | ||
3 | use hir::{Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; | 3 | use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; |
4 | use join_to_string::join; | 4 | use join_to_string::join; |
5 | use ra_syntax::ast::NameOwner; | 5 | use ra_syntax::ast::NameOwner; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::completion::{ | 8 | use crate::completion::{ |
9 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | 9 | db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::display::{const_label, function_label, macro_label, type_label}; | 12 | use crate::display::{const_label, function_label, macro_label, type_label}; |
@@ -44,54 +44,56 @@ impl Completions { | |||
44 | ) { | 44 | ) { |
45 | use hir::ModuleDef::*; | 45 | use hir::ModuleDef::*; |
46 | 46 | ||
47 | let mut completion_kind = CompletionKind::Reference; | 47 | let completion_kind = match resolution { |
48 | let (kind, docs) = match resolution { | 48 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, |
49 | ScopeDef::ModuleDef(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)), | 49 | _ => CompletionKind::Reference, |
50 | }; | ||
51 | |||
52 | let kind = match resolution { | ||
53 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | ||
50 | ScopeDef::ModuleDef(Function(func)) => { | 54 | ScopeDef::ModuleDef(Function(func)) => { |
51 | return self.add_function_with_name(ctx, Some(local_name), *func); | 55 | return self.add_function_with_name(ctx, Some(local_name), *func); |
52 | } | 56 | } |
53 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(it))) => { | 57 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, |
54 | (CompletionItemKind::Struct, it.docs(ctx.db)) | 58 | // FIXME: add CompletionItemKind::Union |
55 | } | 59 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, |
56 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(it))) => { | 60 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, |
57 | (CompletionItemKind::Struct, it.docs(ctx.db)) | 61 | |
58 | } | 62 | ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant, |
59 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(it))) => { | 63 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, |
60 | (CompletionItemKind::Enum, it.docs(ctx.db)) | 64 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, |
61 | } | 65 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, |
62 | ScopeDef::ModuleDef(EnumVariant(it)) => { | 66 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, |
63 | (CompletionItemKind::EnumVariant, it.docs(ctx.db)) | 67 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, |
64 | } | 68 | ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, |
65 | ScopeDef::ModuleDef(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)), | 69 | ScopeDef::LocalBinding(..) => CompletionItemKind::Binding, |
66 | ScopeDef::ModuleDef(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)), | 70 | // (does this need its own kind?) |
67 | ScopeDef::ModuleDef(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)), | 71 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, |
68 | ScopeDef::ModuleDef(TypeAlias(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), | ||
69 | ScopeDef::ModuleDef(BuiltinType(..)) => { | ||
70 | completion_kind = CompletionKind::BuiltinType; | ||
71 | (CompletionItemKind::BuiltinType, None) | ||
72 | } | ||
73 | ScopeDef::GenericParam(..) => (CompletionItemKind::TypeParam, None), | ||
74 | ScopeDef::LocalBinding(..) => (CompletionItemKind::Binding, None), | ||
75 | ScopeDef::SelfType(..) => ( | ||
76 | CompletionItemKind::TypeParam, // (does this need its own kind?) | ||
77 | None, | ||
78 | ), | ||
79 | ScopeDef::MacroDef(mac) => { | 72 | ScopeDef::MacroDef(mac) => { |
80 | self.add_macro(ctx, Some(local_name), *mac); | 73 | return self.add_macro(ctx, Some(local_name), *mac); |
81 | return; | ||
82 | } | 74 | } |
83 | ScopeDef::Unknown => { | 75 | ScopeDef::Unknown => { |
84 | self.add(CompletionItem::new( | 76 | return self.add(CompletionItem::new( |
85 | CompletionKind::Reference, | 77 | CompletionKind::Reference, |
86 | ctx.source_range(), | 78 | ctx.source_range(), |
87 | local_name, | 79 | local_name, |
88 | )); | 80 | )); |
89 | return; | ||
90 | } | 81 | } |
91 | }; | 82 | }; |
92 | 83 | ||
84 | let docs = match resolution { | ||
85 | ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db), | ||
86 | ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db), | ||
87 | ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db), | ||
88 | ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db), | ||
89 | ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db), | ||
90 | ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db), | ||
91 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db), | ||
92 | _ => None, | ||
93 | }; | ||
94 | |||
93 | let mut completion_item = | 95 | let mut completion_item = |
94 | CompletionItem::new(completion_kind, ctx.source_range(), local_name); | 96 | CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone()); |
95 | if let ScopeDef::LocalBinding(pat_id) = resolution { | 97 | if let ScopeDef::LocalBinding(pat_id) = resolution { |
96 | let ty = ctx | 98 | let ty = ctx |
97 | .analyzer | 99 | .analyzer |
@@ -100,6 +102,28 @@ impl Completions { | |||
100 | .map(|t| t.display(ctx.db).to_string()); | 102 | .map(|t| t.display(ctx.db).to_string()); |
101 | completion_item = completion_item.set_detail(ty); | 103 | completion_item = completion_item.set_detail(ty); |
102 | }; | 104 | }; |
105 | |||
106 | // If not an import, add parenthesis automatically. | ||
107 | if ctx.is_path_type | ||
108 | && !ctx.has_type_args | ||
109 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | ||
110 | { | ||
111 | let generic_def: Option<hir::GenericDef> = match resolution { | ||
112 | ScopeDef::ModuleDef(Adt(it)) => Some((*it).into()), | ||
113 | ScopeDef::ModuleDef(TypeAlias(it)) => Some((*it).into()), | ||
114 | _ => None, | ||
115 | }; | ||
116 | if let Some(def) = generic_def { | ||
117 | if has_non_default_type_params(def, ctx.db) { | ||
118 | tested_by!(inserts_angle_brackets_for_generics); | ||
119 | completion_item = completion_item | ||
120 | .lookup_by(local_name.clone()) | ||
121 | .label(format!("{}<…>", local_name)) | ||
122 | .insert_snippet(format!("{}<$0>", local_name)); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
103 | completion_item.kind(kind).set_documentation(docs).add_to(self) | 127 | completion_item.kind(kind).set_documentation(docs).add_to(self) |
104 | } | 128 | } |
105 | 129 | ||
@@ -107,6 +131,33 @@ impl Completions { | |||
107 | self.add_function_with_name(ctx, None, func) | 131 | self.add_function_with_name(ctx, None, func) |
108 | } | 132 | } |
109 | 133 | ||
134 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { | ||
135 | let mut votes = [0, 0, 0]; | ||
136 | for (idx, s) in docs.match_indices(¯o_name) { | ||
137 | let (before, after) = (&docs[..idx], &docs[idx + s.len()..]); | ||
138 | // Ensure to match the full word | ||
139 | if after.starts_with("!") | ||
140 | && before | ||
141 | .chars() | ||
142 | .rev() | ||
143 | .next() | ||
144 | .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric()) | ||
145 | { | ||
146 | // It may have spaces before the braces like `foo! {}` | ||
147 | match after[1..].chars().find(|&c| !c.is_whitespace()) { | ||
148 | Some('{') => votes[0] += 1, | ||
149 | Some('[') => votes[1] += 1, | ||
150 | Some('(') => votes[2] += 1, | ||
151 | _ => {} | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | // Insert a space before `{}`. | ||
157 | // We prefer the last one when some votes equal. | ||
158 | *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1 | ||
159 | } | ||
160 | |||
110 | pub(crate) fn add_macro( | 161 | pub(crate) fn add_macro( |
111 | &mut self, | 162 | &mut self, |
112 | ctx: &CompletionContext, | 163 | ctx: &CompletionContext, |
@@ -117,10 +168,9 @@ impl Completions { | |||
117 | if let Some(name) = name { | 168 | if let Some(name) = name { |
118 | let detail = macro_label(&ast_node); | 169 | let detail = macro_label(&ast_node); |
119 | 170 | ||
120 | let macro_braces_to_insert = match name.as_str() { | 171 | let docs = macro_.docs(ctx.db); |
121 | "vec" => "[$0]", | 172 | let macro_braces_to_insert = |
122 | _ => "($0)", | 173 | self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); |
123 | }; | ||
124 | let macro_declaration = name + "!"; | 174 | let macro_declaration = name + "!"; |
125 | 175 | ||
126 | let builder = CompletionItem::new( | 176 | let builder = CompletionItem::new( |
@@ -129,7 +179,7 @@ impl Completions { | |||
129 | ¯o_declaration, | 179 | ¯o_declaration, |
130 | ) | 180 | ) |
131 | .kind(CompletionItemKind::Macro) | 181 | .kind(CompletionItemKind::Macro) |
132 | .set_documentation(macro_.docs(ctx.db)) | 182 | .set_documentation(docs) |
133 | .detail(detail) | 183 | .detail(detail) |
134 | .insert_snippet(macro_declaration + macro_braces_to_insert); | 184 | .insert_snippet(macro_declaration + macro_braces_to_insert); |
135 | 185 | ||
@@ -148,28 +198,31 @@ impl Completions { | |||
148 | let ast_node = func.source(ctx.db).ast; | 198 | let ast_node = func.source(ctx.db).ast; |
149 | let detail = function_label(&ast_node); | 199 | let detail = function_label(&ast_node); |
150 | 200 | ||
151 | let mut builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name) | 201 | let mut builder = |
152 | .kind(if data.has_self_param() { | 202 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) |
153 | CompletionItemKind::Method | 203 | .kind(if data.has_self_param() { |
154 | } else { | 204 | CompletionItemKind::Method |
155 | CompletionItemKind::Function | 205 | } else { |
156 | }) | 206 | CompletionItemKind::Function |
157 | .set_documentation(func.docs(ctx.db)) | 207 | }) |
158 | .detail(detail); | 208 | .set_documentation(func.docs(ctx.db)) |
159 | // If not an import, add parenthesis automatically. | 209 | .detail(detail); |
210 | |||
211 | // Add `<>` for generic types | ||
160 | if ctx.use_item_syntax.is_none() | 212 | if ctx.use_item_syntax.is_none() |
161 | && !ctx.is_call | 213 | && !ctx.is_call |
162 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | 214 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") |
163 | { | 215 | { |
164 | tested_by!(inserts_parens_for_function_calls); | 216 | tested_by!(inserts_parens_for_function_calls); |
165 | let snippet = | 217 | let (snippet, label) = |
166 | if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { | 218 | if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { |
167 | format!("{}()$0", data.name()) | 219 | (format!("{}()$0", data.name()), format!("{}()", name)) |
168 | } else { | 220 | } else { |
169 | format!("{}($0)", data.name()) | 221 | (format!("{}($0)", data.name()), format!("{}(…)", name)) |
170 | }; | 222 | }; |
171 | builder = builder.insert_snippet(snippet); | 223 | builder = builder.lookup_by(name.clone()).label(label).insert_snippet(snippet); |
172 | } | 224 | } |
225 | |||
173 | self.add(builder) | 226 | self.add(builder) |
174 | } | 227 | } |
175 | 228 | ||
@@ -213,7 +266,6 @@ impl Completions { | |||
213 | .separator(", ") | 266 | .separator(", ") |
214 | .surround_with("(", ")") | 267 | .surround_with("(", ")") |
215 | .to_string(); | 268 | .to_string(); |
216 | |||
217 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) | 269 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) |
218 | .kind(CompletionItemKind::EnumVariant) | 270 | .kind(CompletionItemKind::EnumVariant) |
219 | .set_documentation(variant.docs(ctx.db)) | 271 | .set_documentation(variant.docs(ctx.db)) |
@@ -222,6 +274,11 @@ impl Completions { | |||
222 | } | 274 | } |
223 | } | 275 | } |
224 | 276 | ||
277 | fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool { | ||
278 | let subst = db.generic_defaults(def); | ||
279 | subst.iter().any(|ty| ty == &Ty::Unknown) | ||
280 | } | ||
281 | |||
225 | #[cfg(test)] | 282 | #[cfg(test)] |
226 | mod tests { | 283 | mod tests { |
227 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | 284 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; |
@@ -242,24 +299,28 @@ mod tests { | |||
242 | fn main() { no_<|> } | 299 | fn main() { no_<|> } |
243 | " | 300 | " |
244 | ), | 301 | ), |
245 | @r###"[ | 302 | @r###" |
246 | CompletionItem { | 303 | [ |
247 | label: "main", | 304 | CompletionItem { |
248 | source_range: [61; 64), | 305 | label: "main()", |
249 | delete: [61; 64), | 306 | source_range: [61; 64), |
250 | insert: "main()$0", | 307 | delete: [61; 64), |
251 | kind: Function, | 308 | insert: "main()$0", |
252 | detail: "fn main()", | 309 | kind: Function, |
253 | }, | 310 | lookup: "main", |
254 | CompletionItem { | 311 | detail: "fn main()", |
255 | label: "no_args", | 312 | }, |
256 | source_range: [61; 64), | 313 | CompletionItem { |
257 | delete: [61; 64), | 314 | label: "no_args()", |
258 | insert: "no_args()$0", | 315 | source_range: [61; 64), |
259 | kind: Function, | 316 | delete: [61; 64), |
260 | detail: "fn no_args()", | 317 | insert: "no_args()$0", |
261 | }, | 318 | kind: Function, |
262 | ]"### | 319 | lookup: "no_args", |
320 | detail: "fn no_args()", | ||
321 | }, | ||
322 | ] | ||
323 | "### | ||
263 | ); | 324 | ); |
264 | assert_debug_snapshot!( | 325 | assert_debug_snapshot!( |
265 | do_reference_completion( | 326 | do_reference_completion( |
@@ -268,24 +329,28 @@ mod tests { | |||
268 | fn main() { with_<|> } | 329 | fn main() { with_<|> } |
269 | " | 330 | " |
270 | ), | 331 | ), |
271 | @r###"[ | 332 | @r###" |
272 | CompletionItem { | 333 | [ |
273 | label: "main", | 334 | CompletionItem { |
274 | source_range: [80; 85), | 335 | label: "main()", |
275 | delete: [80; 85), | 336 | source_range: [80; 85), |
276 | insert: "main()$0", | 337 | delete: [80; 85), |
277 | kind: Function, | 338 | insert: "main()$0", |
278 | detail: "fn main()", | 339 | kind: Function, |
279 | }, | 340 | lookup: "main", |
280 | CompletionItem { | 341 | detail: "fn main()", |
281 | label: "with_args", | 342 | }, |
282 | source_range: [80; 85), | 343 | CompletionItem { |
283 | delete: [80; 85), | 344 | label: "with_args(…)", |
284 | insert: "with_args($0)", | 345 | source_range: [80; 85), |
285 | kind: Function, | 346 | delete: [80; 85), |
286 | detail: "fn with_args(x: i32, y: String)", | 347 | insert: "with_args($0)", |
287 | }, | 348 | kind: Function, |
288 | ]"### | 349 | lookup: "with_args", |
350 | detail: "fn with_args(x: i32, y: String)", | ||
351 | }, | ||
352 | ] | ||
353 | "### | ||
289 | ); | 354 | ); |
290 | assert_debug_snapshot!( | 355 | assert_debug_snapshot!( |
291 | do_reference_completion( | 356 | do_reference_completion( |
@@ -299,16 +364,19 @@ mod tests { | |||
299 | } | 364 | } |
300 | " | 365 | " |
301 | ), | 366 | ), |
302 | @r###"[ | 367 | @r###" |
303 | CompletionItem { | 368 | [ |
304 | label: "foo", | 369 | CompletionItem { |
305 | source_range: [163; 164), | 370 | label: "foo()", |
306 | delete: [163; 164), | 371 | source_range: [163; 164), |
307 | insert: "foo()$0", | 372 | delete: [163; 164), |
308 | kind: Method, | 373 | insert: "foo()$0", |
309 | detail: "fn foo(&self)", | 374 | kind: Method, |
310 | }, | 375 | lookup: "foo", |
311 | ]"### | 376 | detail: "fn foo(&self)", |
377 | }, | ||
378 | ] | ||
379 | "### | ||
312 | ); | 380 | ); |
313 | } | 381 | } |
314 | 382 | ||
@@ -389,4 +457,123 @@ mod tests { | |||
389 | ]"# | 457 | ]"# |
390 | ); | 458 | ); |
391 | } | 459 | } |
460 | |||
461 | #[test] | ||
462 | fn inserts_angle_brackets_for_generics() { | ||
463 | covers!(inserts_angle_brackets_for_generics); | ||
464 | assert_debug_snapshot!( | ||
465 | do_reference_completion( | ||
466 | r" | ||
467 | struct Vec<T> {} | ||
468 | fn foo(xs: Ve<|>) | ||
469 | " | ||
470 | ), | ||
471 | @r###" | ||
472 | [ | ||
473 | CompletionItem { | ||
474 | label: "Vec<…>", | ||
475 | source_range: [61; 63), | ||
476 | delete: [61; 63), | ||
477 | insert: "Vec<$0>", | ||
478 | kind: Struct, | ||
479 | lookup: "Vec", | ||
480 | }, | ||
481 | CompletionItem { | ||
482 | label: "foo(…)", | ||
483 | source_range: [61; 63), | ||
484 | delete: [61; 63), | ||
485 | insert: "foo($0)", | ||
486 | kind: Function, | ||
487 | lookup: "foo", | ||
488 | detail: "fn foo(xs: Ve)", | ||
489 | }, | ||
490 | ] | ||
491 | "### | ||
492 | ); | ||
493 | assert_debug_snapshot!( | ||
494 | do_reference_completion( | ||
495 | r" | ||
496 | type Vec<T> = (T,); | ||
497 | fn foo(xs: Ve<|>) | ||
498 | " | ||
499 | ), | ||
500 | @r###" | ||
501 | [ | ||
502 | CompletionItem { | ||
503 | label: "Vec<…>", | ||
504 | source_range: [64; 66), | ||
505 | delete: [64; 66), | ||
506 | insert: "Vec<$0>", | ||
507 | kind: TypeAlias, | ||
508 | lookup: "Vec", | ||
509 | }, | ||
510 | CompletionItem { | ||
511 | label: "foo(…)", | ||
512 | source_range: [64; 66), | ||
513 | delete: [64; 66), | ||
514 | insert: "foo($0)", | ||
515 | kind: Function, | ||
516 | lookup: "foo", | ||
517 | detail: "fn foo(xs: Ve)", | ||
518 | }, | ||
519 | ] | ||
520 | "### | ||
521 | ); | ||
522 | assert_debug_snapshot!( | ||
523 | do_reference_completion( | ||
524 | r" | ||
525 | struct Vec<T = i128> {} | ||
526 | fn foo(xs: Ve<|>) | ||
527 | " | ||
528 | ), | ||
529 | @r###" | ||
530 | [ | ||
531 | CompletionItem { | ||
532 | label: "Vec", | ||
533 | source_range: [68; 70), | ||
534 | delete: [68; 70), | ||
535 | insert: "Vec", | ||
536 | kind: Struct, | ||
537 | }, | ||
538 | CompletionItem { | ||
539 | label: "foo(…)", | ||
540 | source_range: [68; 70), | ||
541 | delete: [68; 70), | ||
542 | insert: "foo($0)", | ||
543 | kind: Function, | ||
544 | lookup: "foo", | ||
545 | detail: "fn foo(xs: Ve)", | ||
546 | }, | ||
547 | ] | ||
548 | "### | ||
549 | ); | ||
550 | assert_debug_snapshot!( | ||
551 | do_reference_completion( | ||
552 | r" | ||
553 | struct Vec<T> {} | ||
554 | fn foo(xs: Ve<|><i128>) | ||
555 | " | ||
556 | ), | ||
557 | @r###" | ||
558 | [ | ||
559 | CompletionItem { | ||
560 | label: "Vec", | ||
561 | source_range: [61; 63), | ||
562 | delete: [61; 63), | ||
563 | insert: "Vec", | ||
564 | kind: Struct, | ||
565 | }, | ||
566 | CompletionItem { | ||
567 | label: "foo(…)", | ||
568 | source_range: [61; 63), | ||
569 | delete: [61; 63), | ||
570 | insert: "foo($0)", | ||
571 | kind: Function, | ||
572 | lookup: "foo", | ||
573 | detail: "fn foo(xs: Ve<i128>)", | ||
574 | }, | ||
575 | ] | ||
576 | "### | ||
577 | ); | ||
578 | } | ||
392 | } | 579 | } |
diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs index ea0714add..9146b647a 100644 --- a/crates/ra_ide_api/src/db.rs +++ b/crates/ra_ide_api/src/db.rs | |||
@@ -4,8 +4,10 @@ use std::sync::Arc; | |||
4 | 4 | ||
5 | use ra_db::{ | 5 | use ra_db::{ |
6 | salsa::{self, Database, Durability}, | 6 | salsa::{self, Database, Durability}, |
7 | Canceled, CheckCanceled, CrateId, FileId, SourceDatabase, SourceRootId, | 7 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, |
8 | SourceDatabaseExt, SourceRootId, | ||
8 | }; | 9 | }; |
10 | use relative_path::RelativePath; | ||
9 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
10 | 12 | ||
11 | use crate::{ | 13 | use crate::{ |
@@ -15,6 +17,7 @@ use crate::{ | |||
15 | 17 | ||
16 | #[salsa::database( | 18 | #[salsa::database( |
17 | ra_db::SourceDatabaseStorage, | 19 | ra_db::SourceDatabaseStorage, |
20 | ra_db::SourceDatabaseExtStorage, | ||
18 | LineIndexDatabaseStorage, | 21 | LineIndexDatabaseStorage, |
19 | symbol_index::SymbolsDatabaseStorage, | 22 | symbol_index::SymbolsDatabaseStorage, |
20 | hir::db::InternDatabaseStorage, | 23 | hir::db::InternDatabaseStorage, |
@@ -31,6 +34,22 @@ pub(crate) struct RootDatabase { | |||
31 | pub(crate) last_gc_check: crate::wasm_shims::Instant, | 34 | pub(crate) last_gc_check: crate::wasm_shims::Instant, |
32 | } | 35 | } |
33 | 36 | ||
37 | impl FileLoader for RootDatabase { | ||
38 | fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
39 | FileLoaderDelegate(self).file_text(file_id) | ||
40 | } | ||
41 | fn resolve_relative_path( | ||
42 | &self, | ||
43 | anchor: FileId, | ||
44 | relative_path: &RelativePath, | ||
45 | ) -> Option<FileId> { | ||
46 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
47 | } | ||
48 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | ||
49 | FileLoaderDelegate(self).relevant_crates(file_id) | ||
50 | } | ||
51 | } | ||
52 | |||
34 | impl hir::debug::HirDebugHelper for RootDatabase { | 53 | impl hir::debug::HirDebugHelper for RootDatabase { |
35 | fn crate_name(&self, krate: CrateId) -> Option<String> { | 54 | fn crate_name(&self, krate: CrateId) -> Option<String> { |
36 | self.debug_data.crate_names.get(&krate).cloned() | 55 | self.debug_data.crate_names.get(&krate).cloned() |
@@ -39,7 +58,7 @@ impl hir::debug::HirDebugHelper for RootDatabase { | |||
39 | let source_root_id = self.file_source_root(file_id); | 58 | let source_root_id = self.file_source_root(file_id); |
40 | let source_root_path = self.debug_data.root_paths.get(&source_root_id)?; | 59 | let source_root_path = self.debug_data.root_paths.get(&source_root_id)?; |
41 | let file_path = self.file_relative_path(file_id); | 60 | let file_path = self.file_relative_path(file_id); |
42 | Some(format!("{}/{}", source_root_path, file_path.display())) | 61 | Some(format!("{}/{}", source_root_path, file_path)) |
43 | } | 62 | } |
44 | } | 63 | } |
45 | 64 | ||
@@ -104,7 +123,7 @@ pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { | |||
104 | fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; | 123 | fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; |
105 | } | 124 | } |
106 | 125 | ||
107 | fn line_index(db: &impl ra_db::SourceDatabase, file_id: FileId) -> Arc<LineIndex> { | 126 | fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> { |
108 | let text = db.file_text(file_id); | 127 | let text = db.file_text(file_id); |
109 | Arc::new(LineIndex::new(&*text)) | 128 | Arc::new(LineIndex::new(&*text)) |
110 | } | 129 | } |
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 0435188c8..8743a3a79 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -4,7 +4,7 @@ use std::cell::RefCell; | |||
4 | 4 | ||
5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; | 5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use ra_db::SourceDatabase; | 7 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
8 | use ra_prof::profile; | 8 | use ra_prof::profile; |
9 | use ra_syntax::{ | 9 | use ra_syntax::{ |
10 | algo, | 10 | algo, |
@@ -12,6 +12,7 @@ use ra_syntax::{ | |||
12 | Location, SyntaxNode, TextRange, T, | 12 | Location, SyntaxNode, TextRange, T, |
13 | }; | 13 | }; |
14 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 14 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
15 | use relative_path::RelativePath; | ||
15 | 16 | ||
16 | use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; | 17 | use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; |
17 | 18 | ||
@@ -47,8 +48,14 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
47 | }) | 48 | }) |
48 | }) | 49 | }) |
49 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | 50 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { |
50 | let source_root = db.file_source_root(d.source().file_id.original_file(db)); | 51 | let original_file = d.source().file_id.original_file(db); |
51 | let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; | 52 | let source_root = db.file_source_root(original_file); |
53 | let path = db | ||
54 | .file_relative_path(original_file) | ||
55 | .parent() | ||
56 | .unwrap_or_else(|| RelativePath::new("")) | ||
57 | .join(&d.candidate); | ||
58 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | ||
52 | let fix = SourceChange::file_system_edit("create module", create_file); | 59 | let fix = SourceChange::file_system_edit("create module", create_file); |
53 | res.borrow_mut().push(Diagnostic { | 60 | res.borrow_mut().push(Diagnostic { |
54 | range: d.highlight_range(), | 61 | range: d.highlight_range(), |
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index d0b1a8a2a..5cb67fb95 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -119,7 +119,7 @@ impl NavigationTarget { | |||
119 | 119 | ||
120 | pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | 120 | pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { |
121 | let src = module.definition_source(db); | 121 | let src = module.definition_source(db); |
122 | let file_id = src.file_id.as_original_file(); | 122 | let file_id = src.file_id.original_file(db); |
123 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 123 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
124 | match src.ast { | 124 | match src.ast { |
125 | ModuleSource::SourceFile(node) => { | 125 | ModuleSource::SourceFile(node) => { |
@@ -139,7 +139,7 @@ impl NavigationTarget { | |||
139 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | 139 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { |
140 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 140 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
141 | if let Some(src) = module.declaration_source(db) { | 141 | if let Some(src) = module.declaration_source(db) { |
142 | let file_id = src.file_id.as_original_file(); | 142 | let file_id = src.file_id.original_file(db); |
143 | return NavigationTarget::from_syntax( | 143 | return NavigationTarget::from_syntax( |
144 | file_id, | 144 | file_id, |
145 | name, | 145 | name, |
@@ -213,7 +213,7 @@ impl NavigationTarget { | |||
213 | ) -> NavigationTarget { | 213 | ) -> NavigationTarget { |
214 | let src = impl_block.source(db); | 214 | let src = impl_block.source(db); |
215 | NavigationTarget::from_syntax( | 215 | NavigationTarget::from_syntax( |
216 | src.file_id.as_original_file(), | 216 | src.file_id.original_file(db), |
217 | "impl".into(), | 217 | "impl".into(), |
218 | None, | 218 | None, |
219 | src.ast.syntax(), | 219 | src.ast.syntax(), |
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs index 33fefb541..602757e92 100644 --- a/crates/ra_ide_api/src/extend_selection.rs +++ b/crates/ra_ide_api/src/extend_selection.rs | |||
@@ -32,6 +32,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange | |||
32 | PARAM_LIST, | 32 | PARAM_LIST, |
33 | ARG_LIST, | 33 | ARG_LIST, |
34 | ARRAY_EXPR, | 34 | ARRAY_EXPR, |
35 | TUPLE_EXPR, | ||
35 | ]; | 36 | ]; |
36 | 37 | ||
37 | if range.is_empty() { | 38 | if range.is_empty() { |
@@ -245,6 +246,8 @@ mod tests { | |||
245 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); | 246 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); |
246 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", ", 33"]); | 247 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", ", 33"]); |
247 | 248 | ||
249 | do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); | ||
250 | |||
248 | do_check( | 251 | do_check( |
249 | r#" | 252 | r#" |
250 | const FOO: [usize; 2] = [ | 253 | const FOO: [usize; 2] = [ |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 41a88314f..1f3fa6c57 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -10,7 +10,7 @@ use ra_syntax::{ | |||
10 | use crate::{ | 10 | use crate::{ |
11 | db::RootDatabase, | 11 | db::RootDatabase, |
12 | display::ShortLabel, | 12 | display::ShortLabel, |
13 | name_ref_kind::{classify_name_ref, NameRefKind::*}, | 13 | references::{classify_name_ref, NameKind::*}, |
14 | FilePosition, NavigationTarget, RangeInfo, | 14 | FilePosition, NavigationTarget, RangeInfo, |
15 | }; | 15 | }; |
16 | 16 | ||
@@ -54,13 +54,11 @@ pub(crate) fn reference_definition( | |||
54 | ) -> ReferenceResult { | 54 | ) -> ReferenceResult { |
55 | use self::ReferenceResult::*; | 55 | use self::ReferenceResult::*; |
56 | 56 | ||
57 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 57 | let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); |
58 | 58 | match name_kind { | |
59 | match classify_name_ref(db, &analyzer, name_ref) { | ||
60 | Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)), | 59 | Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)), |
61 | Some(FieldAccess(field)) => return Exact(NavigationTarget::from_field(db, field)), | 60 | Some(Field(field)) => return Exact(NavigationTarget::from_field(db, field)), |
62 | Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_assoc_item(db, assoc)), | 61 | Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_assoc_item(db, assoc)), |
63 | Some(Method(func)) => return Exact(NavigationTarget::from_def_source(db, func)), | ||
64 | Some(Def(def)) => match NavigationTarget::from_def(db, def) { | 62 | Some(Def(def)) => match NavigationTarget::from_def(db, def) { |
65 | Some(nav) => return Exact(nav), | 63 | Some(nav) => return Exact(nav), |
66 | None => return Approximate(vec![]), | 64 | None => return Approximate(vec![]), |
@@ -70,7 +68,7 @@ pub(crate) fn reference_definition( | |||
70 | return Exact(NavigationTarget::from_adt_def(db, def_id)); | 68 | return Exact(NavigationTarget::from_adt_def(db, def_id)); |
71 | } | 69 | } |
72 | } | 70 | } |
73 | Some(Pat(pat)) => return Exact(NavigationTarget::from_pat(db, file_id, pat)), | 71 | Some(Pat((_, pat))) => return Exact(NavigationTarget::from_pat(db, file_id, pat)), |
74 | Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)), | 72 | Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)), |
75 | Some(GenericParam(_)) => { | 73 | Some(GenericParam(_)) => { |
76 | // FIXME: go to the generic param def | 74 | // FIXME: go to the generic param def |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 24b161c5c..ba328efa1 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -14,7 +14,7 @@ use crate::{ | |||
14 | description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, | 14 | description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, |
15 | rust_code_markup_with_doc, ShortLabel, | 15 | rust_code_markup_with_doc, ShortLabel, |
16 | }, | 16 | }, |
17 | name_ref_kind::{classify_name_ref, NameRefKind::*}, | 17 | references::{classify_name_ref, NameKind::*}, |
18 | FilePosition, FileRange, RangeInfo, | 18 | FilePosition, FileRange, RangeInfo, |
19 | }; | 19 | }; |
20 | 20 | ||
@@ -99,17 +99,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
99 | 99 | ||
100 | let mut range = None; | 100 | let mut range = None; |
101 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { | 101 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { |
102 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | ||
103 | |||
104 | let mut no_fallback = false; | 102 | let mut no_fallback = false; |
105 | 103 | let name_kind = classify_name_ref(db, position.file_id, &name_ref).map(|d| d.kind); | |
106 | match classify_name_ref(db, &analyzer, &name_ref) { | 104 | match name_kind { |
107 | Some(Method(it)) => res.extend(from_def_source(db, it)), | ||
108 | Some(Macro(it)) => { | 105 | Some(Macro(it)) => { |
109 | let src = it.source(db); | 106 | let src = it.source(db); |
110 | res.extend(hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast)))); | 107 | res.extend(hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast)))); |
111 | } | 108 | } |
112 | Some(FieldAccess(it)) => { | 109 | Some(Field(it)) => { |
113 | let src = it.source(db); | 110 | let src = it.source(db); |
114 | if let hir::FieldSource::Named(it) = src.ast { | 111 | if let hir::FieldSource::Named(it) = src.ast { |
115 | res.extend(hover_text(it.doc_comment_text(), it.short_label())); | 112 | res.extend(hover_text(it.doc_comment_text(), it.short_label())); |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 24f1b91f6..19669a7f0 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -19,7 +19,6 @@ mod feature_flags; | |||
19 | mod status; | 19 | mod status; |
20 | mod completion; | 20 | mod completion; |
21 | mod runnables; | 21 | mod runnables; |
22 | mod name_ref_kind; | ||
23 | mod goto_definition; | 22 | mod goto_definition; |
24 | mod goto_type_definition; | 23 | mod goto_type_definition; |
25 | mod extend_selection; | 24 | mod extend_selection; |
@@ -52,7 +51,7 @@ use std::sync::Arc; | |||
52 | use ra_cfg::CfgOptions; | 51 | use ra_cfg::CfgOptions; |
53 | use ra_db::{ | 52 | use ra_db::{ |
54 | salsa::{self, ParallelDatabase}, | 53 | salsa::{self, ParallelDatabase}, |
55 | CheckCanceled, SourceDatabase, | 54 | CheckCanceled, FileLoader, SourceDatabase, |
56 | }; | 55 | }; |
57 | use ra_syntax::{SourceFile, TextRange, TextUnit}; | 56 | use ra_syntax::{SourceFile, TextRange, TextUnit}; |
58 | use ra_text_edit::TextEdit; | 57 | use ra_text_edit::TextEdit; |
@@ -289,10 +288,14 @@ impl AnalysisHost { | |||
289 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { | 288 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { |
290 | self.db.per_query_memory_usage() | 289 | self.db.per_query_memory_usage() |
291 | } | 290 | } |
292 | pub fn raw_database(&self) -> &(impl hir::db::HirDatabase + salsa::Database) { | 291 | pub fn raw_database( |
292 | &self, | ||
293 | ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { | ||
293 | &self.db | 294 | &self.db |
294 | } | 295 | } |
295 | pub fn raw_database_mut(&mut self) -> &mut (impl hir::db::HirDatabase + salsa::Database) { | 296 | pub fn raw_database_mut( |
297 | &mut self, | ||
298 | ) -> &mut (impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { | ||
296 | &mut self.db | 299 | &mut self.db |
297 | } | 300 | } |
298 | } | 301 | } |
@@ -325,7 +328,8 @@ impl Analysis { | |||
325 | let file_id = FileId(0); | 328 | let file_id = FileId(0); |
326 | // FIXME: cfg options | 329 | // FIXME: cfg options |
327 | // Default to enable test for single file. | 330 | // Default to enable test for single file. |
328 | let cfg_options = CfgOptions::default().atom("test".into()); | 331 | let mut cfg_options = CfgOptions::default(); |
332 | cfg_options.insert_atom("test".into()); | ||
329 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); | 333 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); |
330 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 334 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
331 | change.set_crate_graph(crate_graph); | 335 | change.set_crate_graph(crate_graph); |
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index 3f4ba248b..848ae4dc7 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! See test_utils/src/marks.rs | 1 | //! See test_utils/src/marks.rs |
2 | 2 | ||
3 | test_utils::marks!( | 3 | test_utils::marks!( |
4 | inserts_angle_brackets_for_generics | ||
4 | inserts_parens_for_function_calls | 5 | inserts_parens_for_function_calls |
5 | goto_definition_works_for_macros | 6 | goto_definition_works_for_macros |
6 | goto_definition_works_for_methods | 7 | goto_definition_works_for_methods |
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs deleted file mode 100644 index 149585971..000000000 --- a/crates/ra_ide_api/src/name_ref_kind.rs +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::Either; | ||
4 | use ra_syntax::{ast, AstNode, AstPtr}; | ||
5 | use test_utils::tested_by; | ||
6 | |||
7 | use crate::db::RootDatabase; | ||
8 | |||
9 | pub enum NameRefKind { | ||
10 | Method(hir::Function), | ||
11 | Macro(hir::MacroDef), | ||
12 | FieldAccess(hir::StructField), | ||
13 | AssocItem(hir::AssocItem), | ||
14 | Def(hir::ModuleDef), | ||
15 | SelfType(hir::Ty), | ||
16 | Pat(AstPtr<ast::BindPat>), | ||
17 | SelfParam(AstPtr<ast::SelfParam>), | ||
18 | GenericParam(u32), | ||
19 | } | ||
20 | |||
21 | pub(crate) fn classify_name_ref( | ||
22 | db: &RootDatabase, | ||
23 | analyzer: &hir::SourceAnalyzer, | ||
24 | name_ref: &ast::NameRef, | ||
25 | ) -> Option<NameRefKind> { | ||
26 | use NameRefKind::*; | ||
27 | |||
28 | // Check if it is a method | ||
29 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | ||
30 | tested_by!(goto_definition_works_for_methods); | ||
31 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | ||
32 | return Some(Method(func)); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | // It could be a macro call | ||
37 | if let Some(macro_call) = name_ref | ||
38 | .syntax() | ||
39 | .parent() | ||
40 | .and_then(|node| node.parent()) | ||
41 | .and_then(|node| node.parent()) | ||
42 | .and_then(ast::MacroCall::cast) | ||
43 | { | ||
44 | tested_by!(goto_definition_works_for_macros); | ||
45 | if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) { | ||
46 | return Some(Macro(mac)); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | // It could also be a field access | ||
51 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
52 | tested_by!(goto_definition_works_for_fields); | ||
53 | if let Some(field) = analyzer.resolve_field(&field_expr) { | ||
54 | return Some(FieldAccess(field)); | ||
55 | }; | ||
56 | } | ||
57 | |||
58 | // It could also be a named field | ||
59 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::RecordField::cast) { | ||
60 | tested_by!(goto_definition_works_for_record_fields); | ||
61 | |||
62 | let record_lit = field_expr.syntax().ancestors().find_map(ast::RecordLit::cast); | ||
63 | |||
64 | if let Some(ty) = record_lit.and_then(|lit| analyzer.type_of(db, &lit.into())) { | ||
65 | if let Some((hir::Adt::Struct(s), _)) = ty.as_adt() { | ||
66 | let hir_path = hir::Path::from_name_ref(name_ref); | ||
67 | let hir_name = hir_path.as_ident().unwrap(); | ||
68 | |||
69 | if let Some(field) = s.field(db, hir_name) { | ||
70 | return Some(FieldAccess(field)); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | // General case, a path or a local: | ||
77 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { | ||
78 | if let Some(resolved) = analyzer.resolve_path(db, &path) { | ||
79 | return match resolved { | ||
80 | hir::PathResolution::Def(def) => Some(Def(def)), | ||
81 | hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)), | ||
82 | hir::PathResolution::LocalBinding(Either::B(par)) => Some(SelfParam(par)), | ||
83 | hir::PathResolution::GenericParam(par) => { | ||
84 | // FIXME: get generic param def | ||
85 | Some(GenericParam(par)) | ||
86 | } | ||
87 | hir::PathResolution::Macro(def) => Some(Macro(def)), | ||
88 | hir::PathResolution::SelfType(impl_block) => { | ||
89 | let ty = impl_block.target_ty(db); | ||
90 | Some(SelfType(ty)) | ||
91 | } | ||
92 | hir::PathResolution::AssocItem(assoc) => Some(AssocItem(assoc)), | ||
93 | }; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | None | ||
98 | } | ||
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 84c2eb793..f35d835ac 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -1,13 +1,29 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! This module implements a reference search. |
2 | 2 | //! First, the element at the cursor position must be either an `ast::Name` | |
3 | use hir::{Either, ModuleSource}; | 3 | //! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we |
4 | use ra_db::SourceDatabase; | 4 | //! try to resolve the direct tree parent of this element, otherwise we |
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}; | 5 | //! already have a definition and just need to get its HIR together with |
6 | use relative_path::{RelativePath, RelativePathBuf}; | 6 | //! some information that is needed for futher steps of searching. |
7 | 7 | //! After that, we collect files that might contain references and look | |
8 | use crate::{ | 8 | //! for text occurrences of the identifier. If there's an `ast::NameRef` |
9 | db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, | 9 | //! at the index that the match starts at and its tree parent is |
10 | SourceChange, SourceFileEdit, TextRange, | 10 | //! resolved to the search element definition, we get a reference. |
11 | |||
12 | mod classify; | ||
13 | mod name_definition; | ||
14 | mod rename; | ||
15 | mod search_scope; | ||
16 | |||
17 | use once_cell::unsync::Lazy; | ||
18 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | ||
19 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; | ||
20 | |||
21 | use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo}; | ||
22 | |||
23 | pub(crate) use self::{ | ||
24 | classify::{classify_name, classify_name_ref}, | ||
25 | name_definition::{NameDefinition, NameKind}, | ||
26 | rename::rename, | ||
11 | }; | 27 | }; |
12 | 28 | ||
13 | #[derive(Debug, Clone)] | 29 | #[derive(Debug, Clone)] |
@@ -52,161 +68,82 @@ pub(crate) fn find_all_refs( | |||
52 | position: FilePosition, | 68 | position: FilePosition, |
53 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 69 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
54 | let parse = db.parse(position.file_id); | 70 | let parse = db.parse(position.file_id); |
55 | let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?; | 71 | let syntax = parse.tree().syntax().clone(); |
56 | let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); | 72 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; |
57 | 73 | ||
58 | let references = analyzer | 74 | let declaration = match def.kind { |
59 | .find_all_refs(&binding) | 75 | NameKind::Macro(mac) => NavigationTarget::from_macro_def(db, mac), |
60 | .into_iter() | 76 | NameKind::Field(field) => NavigationTarget::from_field(db, field), |
61 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) | 77 | NameKind::AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), |
62 | .collect::<Vec<_>>(); | 78 | NameKind::Def(def) => NavigationTarget::from_def(db, def)?, |
63 | 79 | NameKind::SelfType(ref ty) => match ty.as_adt() { | |
64 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); | 80 | Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id), |
65 | 81 | None => return None, | |
66 | fn find_binding<'a>( | 82 | }, |
67 | db: &RootDatabase, | 83 | NameKind::Pat((_, pat)) => NavigationTarget::from_pat(db, position.file_id, pat), |
68 | source_file: &SourceFile, | 84 | NameKind::SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par), |
69 | position: FilePosition, | 85 | NameKind::GenericParam(_) => return None, |
70 | ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> { | 86 | }; |
71 | let syntax = source_file.syntax(); | ||
72 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { | ||
73 | let range = binding.syntax().text_range(); | ||
74 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); | ||
75 | return Some(RangeInfo::new(range, (binding, analyzer))); | ||
76 | }; | ||
77 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; | ||
78 | let range = name_ref.syntax().text_range(); | ||
79 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | ||
80 | let resolved = analyzer.resolve_local_name(&name_ref)?; | ||
81 | if let Either::A(ptr) = resolved.ptr() { | ||
82 | if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { | ||
83 | return Some(RangeInfo::new(range, (binding, analyzer))); | ||
84 | } | ||
85 | } | ||
86 | None | ||
87 | } | ||
88 | } | ||
89 | 87 | ||
90 | pub(crate) fn rename( | 88 | let references = process_definition(db, def, name); |
91 | db: &RootDatabase, | 89 | |
92 | position: FilePosition, | 90 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) |
93 | new_name: &str, | ||
94 | ) -> Option<RangeInfo<SourceChange>> { | ||
95 | let parse = db.parse(position.file_id); | ||
96 | if let Some((ast_name, ast_module)) = | ||
97 | find_name_and_module_at_offset(parse.tree().syntax(), position) | ||
98 | { | ||
99 | let range = ast_name.syntax().text_range(); | ||
100 | rename_mod(db, &ast_name, &ast_module, position, new_name) | ||
101 | .map(|info| RangeInfo::new(range, info)) | ||
102 | } else { | ||
103 | rename_reference(db, position, new_name) | ||
104 | } | ||
105 | } | 91 | } |
106 | 92 | ||
107 | fn find_name_and_module_at_offset( | 93 | fn find_name<'a>( |
94 | db: &RootDatabase, | ||
108 | syntax: &SyntaxNode, | 95 | syntax: &SyntaxNode, |
109 | position: FilePosition, | 96 | position: FilePosition, |
110 | ) -> Option<(ast::Name, ast::Module)> { | 97 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
111 | let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; | 98 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { |
112 | let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; | 99 | let def = classify_name(db, position.file_id, &name)?; |
113 | Some((ast_name, ast_module)) | 100 | let range = name.syntax().text_range(); |
114 | } | 101 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
115 | |||
116 | fn source_edit_from_file_id_range( | ||
117 | file_id: FileId, | ||
118 | range: TextRange, | ||
119 | new_name: &str, | ||
120 | ) -> SourceFileEdit { | ||
121 | SourceFileEdit { | ||
122 | file_id, | ||
123 | edit: { | ||
124 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
125 | builder.replace(range, new_name.into()); | ||
126 | builder.finish() | ||
127 | }, | ||
128 | } | 102 | } |
103 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | ||
104 | let def = classify_name_ref(db, position.file_id, &name_ref)?; | ||
105 | let range = name_ref.syntax().text_range(); | ||
106 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | ||
129 | } | 107 | } |
130 | 108 | ||
131 | fn rename_mod( | 109 | fn process_definition(db: &RootDatabase, def: NameDefinition, name: String) -> Vec<FileRange> { |
132 | db: &RootDatabase, | 110 | let pat = name.as_str(); |
133 | ast_name: &ast::Name, | 111 | let scope = def.search_scope(db); |
134 | ast_module: &ast::Module, | 112 | let mut refs = vec![]; |
135 | position: FilePosition, | 113 | |
136 | new_name: &str, | 114 | for (file_id, search_range) in scope { |
137 | ) -> Option<SourceChange> { | 115 | let text = db.file_text(file_id); |
138 | let mut source_file_edits = Vec::new(); | 116 | let parse = Lazy::new(|| SourceFile::parse(&text)); |
139 | let mut file_system_edits = Vec::new(); | 117 | |
140 | let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() }; | 118 | for (idx, _) in text.match_indices(pat) { |
141 | if let Some(module) = hir::Module::from_declaration(db, module_src) { | 119 | let offset = TextUnit::from_usize(idx); |
142 | let src = module.definition_source(db); | 120 | |
143 | let file_id = src.file_id.as_original_file(); | 121 | if let Some(name_ref) = |
144 | match src.ast { | 122 | find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) |
145 | ModuleSource::SourceFile(..) => { | 123 | { |
146 | let mod_path: RelativePathBuf = db.file_relative_path(file_id); | 124 | let range = name_ref.syntax().text_range(); |
147 | // mod is defined in path/to/dir/mod.rs | 125 | if let Some(search_range) = search_range { |
148 | let dst_path = if mod_path.file_stem() == Some("mod") { | 126 | if !range.is_subrange(&search_range) { |
149 | mod_path | 127 | continue; |
150 | .parent() | 128 | } |
151 | .and_then(|p| p.parent()) | 129 | } |
152 | .or_else(|| Some(RelativePath::new(""))) | 130 | if let Some(d) = classify_name_ref(db, file_id, &name_ref) { |
153 | .map(|p| p.join(new_name).join("mod.rs")) | 131 | if d == def { |
154 | } else { | 132 | refs.push(FileRange { file_id, range }); |
155 | Some(mod_path.with_file_name(new_name).with_extension("rs")) | 133 | } |
156 | }; | ||
157 | if let Some(path) = dst_path { | ||
158 | let move_file = FileSystemEdit::MoveFile { | ||
159 | src: file_id, | ||
160 | dst_source_root: db.file_source_root(position.file_id), | ||
161 | dst_path: path, | ||
162 | }; | ||
163 | file_system_edits.push(move_file); | ||
164 | } | 134 | } |
165 | } | 135 | } |
166 | ModuleSource::Module(..) => {} | ||
167 | } | 136 | } |
168 | } | 137 | } |
169 | 138 | refs | |
170 | let edit = SourceFileEdit { | ||
171 | file_id: position.file_id, | ||
172 | edit: { | ||
173 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
174 | builder.replace(ast_name.syntax().text_range(), new_name.into()); | ||
175 | builder.finish() | ||
176 | }, | ||
177 | }; | ||
178 | source_file_edits.push(edit); | ||
179 | |||
180 | Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) | ||
181 | } | ||
182 | |||
183 | fn rename_reference( | ||
184 | db: &RootDatabase, | ||
185 | position: FilePosition, | ||
186 | new_name: &str, | ||
187 | ) -> Option<RangeInfo<SourceChange>> { | ||
188 | let RangeInfo { range, info: refs } = find_all_refs(db, position)?; | ||
189 | |||
190 | let edit = refs | ||
191 | .into_iter() | ||
192 | .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name)) | ||
193 | .collect::<Vec<_>>(); | ||
194 | |||
195 | if edit.is_empty() { | ||
196 | return None; | ||
197 | } | ||
198 | |||
199 | Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) | ||
200 | } | 139 | } |
201 | 140 | ||
202 | #[cfg(test)] | 141 | #[cfg(test)] |
203 | mod tests { | 142 | mod tests { |
204 | use crate::{ | 143 | use crate::{ |
205 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, | 144 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, |
206 | ReferenceSearchResult, | 145 | ReferenceSearchResult, |
207 | }; | 146 | }; |
208 | use insta::assert_debug_snapshot; | ||
209 | use test_utils::assert_eq_text; | ||
210 | 147 | ||
211 | #[test] | 148 | #[test] |
212 | fn test_find_all_refs_for_local() { | 149 | fn test_find_all_refs_for_local() { |
@@ -249,211 +186,144 @@ mod tests { | |||
249 | assert_eq!(refs.len(), 2); | 186 | assert_eq!(refs.len(), 2); |
250 | } | 187 | } |
251 | 188 | ||
252 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | ||
253 | let (analysis, position) = single_file_with_position(text); | ||
254 | analysis.find_all_refs(position).unwrap().unwrap() | ||
255 | } | ||
256 | |||
257 | #[test] | 189 | #[test] |
258 | fn test_rename_for_local() { | 190 | fn test_find_all_refs_field_name() { |
259 | test_rename( | 191 | let code = r#" |
260 | r#" | 192 | //- /lib.rs |
261 | fn main() { | 193 | struct Foo { |
262 | let mut i = 1; | 194 | pub spam<|>: u32, |
263 | let j = 1; | 195 | } |
264 | i = i<|> + j; | ||
265 | |||
266 | { | ||
267 | i = 0; | ||
268 | } | ||
269 | |||
270 | i = 5; | ||
271 | }"#, | ||
272 | "k", | ||
273 | r#" | ||
274 | fn main() { | ||
275 | let mut k = 1; | ||
276 | let j = 1; | ||
277 | k = k + j; | ||
278 | 196 | ||
279 | { | 197 | fn main(s: Foo) { |
280 | k = 0; | 198 | let f = s.spam; |
281 | } | 199 | } |
200 | "#; | ||
282 | 201 | ||
283 | k = 5; | 202 | let refs = get_all_refs(code); |
284 | }"#, | 203 | assert_eq!(refs.len(), 2); |
285 | ); | ||
286 | } | 204 | } |
287 | 205 | ||
288 | #[test] | 206 | #[test] |
289 | fn test_rename_for_param_inside() { | 207 | fn test_find_all_refs_impl_item_name() { |
290 | test_rename( | 208 | let code = r#" |
291 | r#" | 209 | //- /lib.rs |
292 | fn foo(i : u32) -> u32 { | 210 | struct Foo; |
293 | i<|> | 211 | impl Foo { |
294 | }"#, | 212 | fn f<|>(&self) { } |
295 | "j", | 213 | } |
296 | r#" | 214 | "#; |
297 | fn foo(j : u32) -> u32 { | ||
298 | j | ||
299 | }"#, | ||
300 | ); | ||
301 | } | ||
302 | 215 | ||
303 | #[test] | 216 | let refs = get_all_refs(code); |
304 | fn test_rename_refs_for_fn_param() { | 217 | assert_eq!(refs.len(), 1); |
305 | test_rename( | ||
306 | r#" | ||
307 | fn foo(i<|> : u32) -> u32 { | ||
308 | i | ||
309 | }"#, | ||
310 | "new_name", | ||
311 | r#" | ||
312 | fn foo(new_name : u32) -> u32 { | ||
313 | new_name | ||
314 | }"#, | ||
315 | ); | ||
316 | } | 218 | } |
317 | 219 | ||
318 | #[test] | 220 | #[test] |
319 | fn test_rename_for_mut_param() { | 221 | fn test_find_all_refs_enum_var_name() { |
320 | test_rename( | 222 | let code = r#" |
321 | r#" | 223 | //- /lib.rs |
322 | fn foo(mut i<|> : u32) -> u32 { | 224 | enum Foo { |
323 | i | 225 | A, |
324 | }"#, | 226 | B<|>, |
325 | "new_name", | 227 | C, |
326 | r#" | 228 | } |
327 | fn foo(mut new_name : u32) -> u32 { | 229 | "#; |
328 | new_name | 230 | |
329 | }"#, | 231 | let refs = get_all_refs(code); |
330 | ); | 232 | assert_eq!(refs.len(), 1); |
331 | } | 233 | } |
332 | 234 | ||
333 | #[test] | 235 | #[test] |
334 | fn test_rename_mod() { | 236 | fn test_find_all_refs_two_modules() { |
335 | let (analysis, position) = analysis_and_position( | 237 | let code = r#" |
336 | " | ||
337 | //- /lib.rs | 238 | //- /lib.rs |
338 | mod bar; | 239 | pub mod foo; |
240 | pub mod bar; | ||
241 | |||
242 | fn f() { | ||
243 | let i = foo::Foo { n: 5 }; | ||
244 | } | ||
245 | |||
246 | //- /foo.rs | ||
247 | use crate::bar; | ||
248 | |||
249 | pub struct Foo { | ||
250 | pub n: u32, | ||
251 | } | ||
252 | |||
253 | fn f() { | ||
254 | let i = bar::Bar { n: 5 }; | ||
255 | } | ||
339 | 256 | ||
340 | //- /bar.rs | 257 | //- /bar.rs |
258 | use crate::foo; | ||
259 | |||
260 | pub struct Bar { | ||
261 | pub n: u32, | ||
262 | } | ||
263 | |||
264 | fn f() { | ||
265 | let i = foo::Foo<|> { n: 5 }; | ||
266 | } | ||
267 | "#; | ||
268 | |||
269 | let (analysis, pos) = analysis_and_position(code); | ||
270 | let refs = analysis.find_all_refs(pos).unwrap().unwrap(); | ||
271 | assert_eq!(refs.len(), 3); | ||
272 | } | ||
273 | |||
274 | // `mod foo;` is not in the results because `foo` is an `ast::Name`. | ||
275 | // So, there are two references: the first one is a definition of the `foo` module, | ||
276 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. | ||
277 | #[test] | ||
278 | fn test_find_all_refs_decl_module() { | ||
279 | let code = r#" | ||
280 | //- /lib.rs | ||
341 | mod foo<|>; | 281 | mod foo<|>; |
342 | 282 | ||
343 | //- /bar/foo.rs | 283 | use foo::Foo; |
344 | // emtpy | 284 | |
345 | ", | 285 | fn f() { |
346 | ); | 286 | let i = Foo { n: 5 }; |
347 | let new_name = "foo2"; | 287 | } |
348 | let source_change = analysis.rename(position, new_name).unwrap(); | 288 | |
349 | assert_debug_snapshot!(&source_change, | 289 | //- /foo.rs |
350 | @r###" | 290 | pub struct Foo { |
351 | Some( | 291 | pub n: u32, |
352 | RangeInfo { | 292 | } |
353 | range: [4; 7), | 293 | "#; |
354 | info: SourceChange { | 294 | |
355 | label: "rename", | 295 | let (analysis, pos) = analysis_and_position(code); |
356 | source_file_edits: [ | 296 | let refs = analysis.find_all_refs(pos).unwrap().unwrap(); |
357 | SourceFileEdit { | 297 | assert_eq!(refs.len(), 2); |
358 | file_id: FileId( | ||
359 | 2, | ||
360 | ), | ||
361 | edit: TextEdit { | ||
362 | atoms: [ | ||
363 | AtomTextEdit { | ||
364 | delete: [4; 7), | ||
365 | insert: "foo2", | ||
366 | }, | ||
367 | ], | ||
368 | }, | ||
369 | }, | ||
370 | ], | ||
371 | file_system_edits: [ | ||
372 | MoveFile { | ||
373 | src: FileId( | ||
374 | 3, | ||
375 | ), | ||
376 | dst_source_root: SourceRootId( | ||
377 | 0, | ||
378 | ), | ||
379 | dst_path: "bar/foo2.rs", | ||
380 | }, | ||
381 | ], | ||
382 | cursor_position: None, | ||
383 | }, | ||
384 | }, | ||
385 | ) | ||
386 | "###); | ||
387 | } | 298 | } |
388 | 299 | ||
389 | #[test] | 300 | #[test] |
390 | fn test_rename_mod_in_dir() { | 301 | fn test_find_all_refs_super_mod_vis() { |
391 | let (analysis, position) = analysis_and_position( | 302 | let code = r#" |
392 | " | ||
393 | //- /lib.rs | 303 | //- /lib.rs |
394 | mod fo<|>o; | 304 | mod foo; |
395 | //- /foo/mod.rs | 305 | |
396 | // emtpy | 306 | //- /foo.rs |
397 | ", | 307 | mod some; |
398 | ); | 308 | use some::Foo; |
399 | let new_name = "foo2"; | 309 | |
400 | let source_change = analysis.rename(position, new_name).unwrap(); | 310 | fn f() { |
401 | assert_debug_snapshot!(&source_change, | 311 | let i = Foo { n: 5 }; |
402 | @r###" | 312 | } |
403 | Some( | 313 | |
404 | RangeInfo { | 314 | //- /foo/some.rs |
405 | range: [4; 7), | 315 | pub(super) struct Foo<|> { |
406 | info: SourceChange { | 316 | pub n: u32, |
407 | label: "rename", | 317 | } |
408 | source_file_edits: [ | 318 | "#; |
409 | SourceFileEdit { | 319 | |
410 | file_id: FileId( | 320 | let (analysis, pos) = analysis_and_position(code); |
411 | 1, | 321 | let refs = analysis.find_all_refs(pos).unwrap().unwrap(); |
412 | ), | 322 | assert_eq!(refs.len(), 3); |
413 | edit: TextEdit { | ||
414 | atoms: [ | ||
415 | AtomTextEdit { | ||
416 | delete: [4; 7), | ||
417 | insert: "foo2", | ||
418 | }, | ||
419 | ], | ||
420 | }, | ||
421 | }, | ||
422 | ], | ||
423 | file_system_edits: [ | ||
424 | MoveFile { | ||
425 | src: FileId( | ||
426 | 2, | ||
427 | ), | ||
428 | dst_source_root: SourceRootId( | ||
429 | 0, | ||
430 | ), | ||
431 | dst_path: "foo2/mod.rs", | ||
432 | }, | ||
433 | ], | ||
434 | cursor_position: None, | ||
435 | }, | ||
436 | }, | ||
437 | ) | ||
438 | "### | ||
439 | ); | ||
440 | } | 323 | } |
441 | 324 | ||
442 | fn test_rename(text: &str, new_name: &str, expected: &str) { | 325 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
443 | let (analysis, position) = single_file_with_position(text); | 326 | let (analysis, position) = single_file_with_position(text); |
444 | let source_change = analysis.rename(position, new_name).unwrap(); | 327 | analysis.find_all_refs(position).unwrap().unwrap() |
445 | let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); | ||
446 | let mut file_id: Option<FileId> = None; | ||
447 | if let Some(change) = source_change { | ||
448 | for edit in change.info.source_file_edits { | ||
449 | file_id = Some(edit.file_id); | ||
450 | for atom in edit.edit.as_atoms() { | ||
451 | text_edit_builder.replace(atom.delete, atom.insert.clone()); | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | let result = | ||
456 | text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); | ||
457 | assert_eq_text!(expected, &*result); | ||
458 | } | 328 | } |
459 | } | 329 | } |
diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs new file mode 100644 index 000000000..c8daff9b1 --- /dev/null +++ b/crates/ra_ide_api/src/references/classify.rs | |||
@@ -0,0 +1,179 @@ | |||
1 | //! Functions that are used to classify an element from its definition or reference. | ||
2 | |||
3 | use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; | ||
4 | use ra_db::FileId; | ||
5 | use ra_syntax::{ast, match_ast, AstNode, AstPtr}; | ||
6 | use test_utils::tested_by; | ||
7 | |||
8 | use super::{ | ||
9 | name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field}, | ||
10 | NameDefinition, NameKind, | ||
11 | }; | ||
12 | use crate::db::RootDatabase; | ||
13 | |||
14 | pub(crate) fn classify_name( | ||
15 | db: &RootDatabase, | ||
16 | file_id: FileId, | ||
17 | name: &ast::Name, | ||
18 | ) -> Option<NameDefinition> { | ||
19 | let parent = name.syntax().parent()?; | ||
20 | let file_id = file_id.into(); | ||
21 | |||
22 | // FIXME: add ast::MacroCall(it) | ||
23 | match_ast! { | ||
24 | match parent { | ||
25 | ast::BindPat(it) => { | ||
26 | from_pat(db, file_id, AstPtr::new(&it)) | ||
27 | }, | ||
28 | ast::RecordFieldDef(it) => { | ||
29 | let ast = hir::FieldSource::Named(it); | ||
30 | let src = hir::Source { file_id, ast }; | ||
31 | let field = hir::StructField::from_source(db, src)?; | ||
32 | Some(from_struct_field(db, field)) | ||
33 | }, | ||
34 | ast::Module(it) => { | ||
35 | let def = { | ||
36 | if !it.has_semi() { | ||
37 | let ast = hir::ModuleSource::Module(it); | ||
38 | let src = hir::Source { file_id, ast }; | ||
39 | hir::Module::from_definition(db, src) | ||
40 | } else { | ||
41 | let src = hir::Source { file_id, ast: it }; | ||
42 | hir::Module::from_declaration(db, src) | ||
43 | } | ||
44 | }?; | ||
45 | Some(from_module_def(db, def.into(), None)) | ||
46 | }, | ||
47 | ast::StructDef(it) => { | ||
48 | let src = hir::Source { file_id, ast: it }; | ||
49 | let def = hir::Struct::from_source(db, src)?; | ||
50 | Some(from_module_def(db, def.into(), None)) | ||
51 | }, | ||
52 | ast::EnumDef(it) => { | ||
53 | let src = hir::Source { file_id, ast: it }; | ||
54 | let def = hir::Enum::from_source(db, src)?; | ||
55 | Some(from_module_def(db, def.into(), None)) | ||
56 | }, | ||
57 | ast::TraitDef(it) => { | ||
58 | let src = hir::Source { file_id, ast: it }; | ||
59 | let def = hir::Trait::from_source(db, src)?; | ||
60 | Some(from_module_def(db, def.into(), None)) | ||
61 | }, | ||
62 | ast::StaticDef(it) => { | ||
63 | let src = hir::Source { file_id, ast: it }; | ||
64 | let def = hir::Static::from_source(db, src)?; | ||
65 | Some(from_module_def(db, def.into(), None)) | ||
66 | }, | ||
67 | ast::EnumVariant(it) => { | ||
68 | let src = hir::Source { file_id, ast: it }; | ||
69 | let def = hir::EnumVariant::from_source(db, src)?; | ||
70 | Some(from_module_def(db, def.into(), None)) | ||
71 | }, | ||
72 | ast::FnDef(it) => { | ||
73 | let src = hir::Source { file_id, ast: it }; | ||
74 | let def = hir::Function::from_source(db, src)?; | ||
75 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | ||
76 | Some(from_assoc_item(db, def.into())) | ||
77 | } else { | ||
78 | Some(from_module_def(db, def.into(), None)) | ||
79 | } | ||
80 | }, | ||
81 | ast::ConstDef(it) => { | ||
82 | let src = hir::Source { file_id, ast: it }; | ||
83 | let def = hir::Const::from_source(db, src)?; | ||
84 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | ||
85 | Some(from_assoc_item(db, def.into())) | ||
86 | } else { | ||
87 | Some(from_module_def(db, def.into(), None)) | ||
88 | } | ||
89 | }, | ||
90 | ast::TypeAliasDef(it) => { | ||
91 | let src = hir::Source { file_id, ast: it }; | ||
92 | let def = hir::TypeAlias::from_source(db, src)?; | ||
93 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | ||
94 | Some(from_assoc_item(db, def.into())) | ||
95 | } else { | ||
96 | Some(from_module_def(db, def.into(), None)) | ||
97 | } | ||
98 | }, | ||
99 | _ => None, | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | pub(crate) fn classify_name_ref( | ||
105 | db: &RootDatabase, | ||
106 | file_id: FileId, | ||
107 | name_ref: &ast::NameRef, | ||
108 | ) -> Option<NameDefinition> { | ||
109 | use PathResolution::*; | ||
110 | |||
111 | let parent = name_ref.syntax().parent()?; | ||
112 | let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | ||
113 | |||
114 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | ||
115 | tested_by!(goto_definition_works_for_methods); | ||
116 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | ||
117 | return Some(from_assoc_item(db, func.into())); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | ||
122 | tested_by!(goto_definition_works_for_fields); | ||
123 | if let Some(field) = analyzer.resolve_field(&field_expr) { | ||
124 | return Some(from_struct_field(db, field)); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | ||
129 | tested_by!(goto_definition_works_for_record_fields); | ||
130 | if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) { | ||
131 | let variant_def = analyzer.resolve_record_literal(&record_lit)?; | ||
132 | let hir_path = Path::from_name_ref(name_ref); | ||
133 | let hir_name = hir_path.as_ident()?; | ||
134 | let field = variant_def.field(db, hir_name)?; | ||
135 | return Some(from_struct_field(db, field)); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | let ast = ModuleSource::from_child_node(db, file_id, &parent); | ||
140 | let file_id = file_id.into(); | ||
141 | // FIXME: find correct container and visibility for each case | ||
142 | let container = Module::from_definition(db, Source { file_id, ast })?; | ||
143 | let visibility = None; | ||
144 | |||
145 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | ||
146 | tested_by!(goto_definition_works_for_macros); | ||
147 | if let Some(macro_def) = analyzer.resolve_macro_call(db, ¯o_call) { | ||
148 | let kind = NameKind::Macro(macro_def); | ||
149 | return Some(NameDefinition { kind, container, visibility }); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; | ||
154 | let resolved = analyzer.resolve_path(db, &path)?; | ||
155 | match resolved { | ||
156 | Def(def) => Some(from_module_def(db, def, Some(container))), | ||
157 | AssocItem(item) => Some(from_assoc_item(db, item)), | ||
158 | LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat), | ||
159 | LocalBinding(Either::B(par)) => { | ||
160 | let kind = NameKind::SelfParam(par); | ||
161 | Some(NameDefinition { kind, container, visibility }) | ||
162 | } | ||
163 | GenericParam(par) => { | ||
164 | // FIXME: get generic param def | ||
165 | let kind = NameKind::GenericParam(par); | ||
166 | Some(NameDefinition { kind, container, visibility }) | ||
167 | } | ||
168 | Macro(def) => { | ||
169 | let kind = NameKind::Macro(def); | ||
170 | Some(NameDefinition { kind, container, visibility }) | ||
171 | } | ||
172 | SelfType(impl_block) => { | ||
173 | let ty = impl_block.target_ty(db); | ||
174 | let kind = NameKind::SelfType(ty); | ||
175 | let container = impl_block.module(); | ||
176 | Some(NameDefinition { kind, container, visibility }) | ||
177 | } | ||
178 | } | ||
179 | } | ||
diff --git a/crates/ra_ide_api/src/references/name_definition.rs b/crates/ra_ide_api/src/references/name_definition.rs new file mode 100644 index 000000000..4580bc789 --- /dev/null +++ b/crates/ra_ide_api/src/references/name_definition.rs | |||
@@ -0,0 +1,113 @@ | |||
1 | //! `NameDefinition` keeps information about the element we want to search references for. | ||
2 | //! The element is represented by `NameKind`. It's located inside some `container` and | ||
3 | //! has a `visibility`, which defines a search scope. | ||
4 | //! Note that the reference search is possible for not all of the classified items. | ||
5 | |||
6 | use hir::{ | ||
7 | db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef, | ||
8 | Module, ModuleDef, StructField, Ty, VariantDef, | ||
9 | }; | ||
10 | use ra_syntax::{ast, ast::VisibilityOwner, match_ast, AstNode, AstPtr}; | ||
11 | |||
12 | use crate::db::RootDatabase; | ||
13 | |||
14 | #[derive(Debug, PartialEq, Eq)] | ||
15 | pub enum NameKind { | ||
16 | Macro(MacroDef), | ||
17 | Field(StructField), | ||
18 | AssocItem(AssocItem), | ||
19 | Def(ModuleDef), | ||
20 | SelfType(Ty), | ||
21 | Pat((DefWithBody, AstPtr<ast::BindPat>)), | ||
22 | SelfParam(AstPtr<ast::SelfParam>), | ||
23 | GenericParam(u32), | ||
24 | } | ||
25 | |||
26 | #[derive(PartialEq, Eq)] | ||
27 | pub(crate) struct NameDefinition { | ||
28 | pub visibility: Option<ast::Visibility>, | ||
29 | pub container: Module, | ||
30 | pub kind: NameKind, | ||
31 | } | ||
32 | |||
33 | pub(super) fn from_pat( | ||
34 | db: &RootDatabase, | ||
35 | file_id: HirFileId, | ||
36 | pat: AstPtr<ast::BindPat>, | ||
37 | ) -> Option<NameDefinition> { | ||
38 | let root = db.parse_or_expand(file_id)?; | ||
39 | let def = pat.to_node(&root).syntax().ancestors().find_map(|node| { | ||
40 | match_ast! { | ||
41 | match node { | ||
42 | ast::FnDef(it) => { | ||
43 | let src = hir::Source { file_id, ast: it }; | ||
44 | Some(hir::Function::from_source(db, src)?.into()) | ||
45 | }, | ||
46 | ast::ConstDef(it) => { | ||
47 | let src = hir::Source { file_id, ast: it }; | ||
48 | Some(hir::Const::from_source(db, src)?.into()) | ||
49 | }, | ||
50 | ast::StaticDef(it) => { | ||
51 | let src = hir::Source { file_id, ast: it }; | ||
52 | Some(hir::Static::from_source(db, src)?.into()) | ||
53 | }, | ||
54 | _ => None, | ||
55 | } | ||
56 | } | ||
57 | })?; | ||
58 | let kind = NameKind::Pat((def, pat)); | ||
59 | let container = def.module(db); | ||
60 | Some(NameDefinition { kind, container, visibility: None }) | ||
61 | } | ||
62 | |||
63 | pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition { | ||
64 | let container = item.module(db); | ||
65 | let visibility = match item { | ||
66 | AssocItem::Function(f) => f.source(db).ast.visibility(), | ||
67 | AssocItem::Const(c) => c.source(db).ast.visibility(), | ||
68 | AssocItem::TypeAlias(a) => a.source(db).ast.visibility(), | ||
69 | }; | ||
70 | let kind = NameKind::AssocItem(item); | ||
71 | NameDefinition { kind, container, visibility } | ||
72 | } | ||
73 | |||
74 | pub(super) fn from_struct_field(db: &RootDatabase, field: StructField) -> NameDefinition { | ||
75 | let kind = NameKind::Field(field); | ||
76 | let parent = field.parent_def(db); | ||
77 | let container = parent.module(db); | ||
78 | let visibility = match parent { | ||
79 | VariantDef::Struct(s) => s.source(db).ast.visibility(), | ||
80 | VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), | ||
81 | }; | ||
82 | NameDefinition { kind, container, visibility } | ||
83 | } | ||
84 | |||
85 | pub(super) fn from_module_def( | ||
86 | db: &RootDatabase, | ||
87 | def: ModuleDef, | ||
88 | module: Option<Module>, | ||
89 | ) -> NameDefinition { | ||
90 | let kind = NameKind::Def(def); | ||
91 | let (container, visibility) = match def { | ||
92 | ModuleDef::Module(it) => { | ||
93 | let container = it.parent(db).or_else(|| Some(it)).unwrap(); | ||
94 | let visibility = it.declaration_source(db).and_then(|s| s.ast.visibility()); | ||
95 | (container, visibility) | ||
96 | } | ||
97 | ModuleDef::EnumVariant(it) => { | ||
98 | let container = it.module(db); | ||
99 | let visibility = it.source(db).ast.parent_enum().visibility(); | ||
100 | (container, visibility) | ||
101 | } | ||
102 | ModuleDef::Function(it) => (it.module(db), it.source(db).ast.visibility()), | ||
103 | ModuleDef::Const(it) => (it.module(db), it.source(db).ast.visibility()), | ||
104 | ModuleDef::Static(it) => (it.module(db), it.source(db).ast.visibility()), | ||
105 | ModuleDef::Trait(it) => (it.module(db), it.source(db).ast.visibility()), | ||
106 | ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).ast.visibility()), | ||
107 | ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).ast.visibility()), | ||
108 | ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).ast.visibility()), | ||
109 | ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).ast.visibility()), | ||
110 | ModuleDef::BuiltinType(..) => (module.unwrap(), None), | ||
111 | }; | ||
112 | NameDefinition { kind, container, visibility } | ||
113 | } | ||
diff --git a/crates/ra_ide_api/src/references/rename.rs b/crates/ra_ide_api/src/references/rename.rs new file mode 100644 index 000000000..0e2e088e0 --- /dev/null +++ b/crates/ra_ide_api/src/references/rename.rs | |||
@@ -0,0 +1,469 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::ModuleSource; | ||
4 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | ||
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; | ||
6 | use relative_path::{RelativePath, RelativePathBuf}; | ||
7 | |||
8 | use crate::{ | ||
9 | db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, | ||
10 | SourceFileEdit, TextRange, | ||
11 | }; | ||
12 | |||
13 | use super::find_all_refs; | ||
14 | |||
15 | pub(crate) fn rename( | ||
16 | db: &RootDatabase, | ||
17 | position: FilePosition, | ||
18 | new_name: &str, | ||
19 | ) -> Option<RangeInfo<SourceChange>> { | ||
20 | let parse = db.parse(position.file_id); | ||
21 | if let Some((ast_name, ast_module)) = | ||
22 | find_name_and_module_at_offset(parse.tree().syntax(), position) | ||
23 | { | ||
24 | let range = ast_name.syntax().text_range(); | ||
25 | rename_mod(db, &ast_name, &ast_module, position, new_name) | ||
26 | .map(|info| RangeInfo::new(range, info)) | ||
27 | } else { | ||
28 | rename_reference(db, position, new_name) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | fn find_name_and_module_at_offset( | ||
33 | syntax: &SyntaxNode, | ||
34 | position: FilePosition, | ||
35 | ) -> Option<(ast::Name, ast::Module)> { | ||
36 | let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; | ||
37 | let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; | ||
38 | Some((ast_name, ast_module)) | ||
39 | } | ||
40 | |||
41 | fn source_edit_from_file_id_range( | ||
42 | file_id: FileId, | ||
43 | range: TextRange, | ||
44 | new_name: &str, | ||
45 | ) -> SourceFileEdit { | ||
46 | SourceFileEdit { | ||
47 | file_id, | ||
48 | edit: { | ||
49 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
50 | builder.replace(range, new_name.into()); | ||
51 | builder.finish() | ||
52 | }, | ||
53 | } | ||
54 | } | ||
55 | |||
56 | fn rename_mod( | ||
57 | db: &RootDatabase, | ||
58 | ast_name: &ast::Name, | ||
59 | ast_module: &ast::Module, | ||
60 | position: FilePosition, | ||
61 | new_name: &str, | ||
62 | ) -> Option<SourceChange> { | ||
63 | let mut source_file_edits = Vec::new(); | ||
64 | let mut file_system_edits = Vec::new(); | ||
65 | let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() }; | ||
66 | if let Some(module) = hir::Module::from_declaration(db, module_src) { | ||
67 | let src = module.definition_source(db); | ||
68 | let file_id = src.file_id.original_file(db); | ||
69 | match src.ast { | ||
70 | ModuleSource::SourceFile(..) => { | ||
71 | let mod_path: RelativePathBuf = db.file_relative_path(file_id); | ||
72 | // mod is defined in path/to/dir/mod.rs | ||
73 | let dst_path = if mod_path.file_stem() == Some("mod") { | ||
74 | mod_path | ||
75 | .parent() | ||
76 | .and_then(|p| p.parent()) | ||
77 | .or_else(|| Some(RelativePath::new(""))) | ||
78 | .map(|p| p.join(new_name).join("mod.rs")) | ||
79 | } else { | ||
80 | Some(mod_path.with_file_name(new_name).with_extension("rs")) | ||
81 | }; | ||
82 | if let Some(path) = dst_path { | ||
83 | let move_file = FileSystemEdit::MoveFile { | ||
84 | src: file_id, | ||
85 | dst_source_root: db.file_source_root(position.file_id), | ||
86 | dst_path: path, | ||
87 | }; | ||
88 | file_system_edits.push(move_file); | ||
89 | } | ||
90 | } | ||
91 | ModuleSource::Module(..) => {} | ||
92 | } | ||
93 | } | ||
94 | |||
95 | let edit = SourceFileEdit { | ||
96 | file_id: position.file_id, | ||
97 | edit: { | ||
98 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
99 | builder.replace(ast_name.syntax().text_range(), new_name.into()); | ||
100 | builder.finish() | ||
101 | }, | ||
102 | }; | ||
103 | source_file_edits.push(edit); | ||
104 | |||
105 | Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) | ||
106 | } | ||
107 | |||
108 | fn rename_reference( | ||
109 | db: &RootDatabase, | ||
110 | position: FilePosition, | ||
111 | new_name: &str, | ||
112 | ) -> Option<RangeInfo<SourceChange>> { | ||
113 | let RangeInfo { range, info: refs } = find_all_refs(db, position)?; | ||
114 | |||
115 | let edit = refs | ||
116 | .into_iter() | ||
117 | .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name)) | ||
118 | .collect::<Vec<_>>(); | ||
119 | |||
120 | if edit.is_empty() { | ||
121 | return None; | ||
122 | } | ||
123 | |||
124 | Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) | ||
125 | } | ||
126 | |||
127 | #[cfg(test)] | ||
128 | mod tests { | ||
129 | use crate::{ | ||
130 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, | ||
131 | ReferenceSearchResult, | ||
132 | }; | ||
133 | use insta::assert_debug_snapshot; | ||
134 | use test_utils::assert_eq_text; | ||
135 | |||
136 | #[test] | ||
137 | fn test_find_all_refs_for_local() { | ||
138 | let code = r#" | ||
139 | fn main() { | ||
140 | let mut i = 1; | ||
141 | let j = 1; | ||
142 | i = i<|> + j; | ||
143 | |||
144 | { | ||
145 | i = 0; | ||
146 | } | ||
147 | |||
148 | i = 5; | ||
149 | }"#; | ||
150 | |||
151 | let refs = get_all_refs(code); | ||
152 | assert_eq!(refs.len(), 5); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn test_find_all_refs_for_param_inside() { | ||
157 | let code = r#" | ||
158 | fn foo(i : u32) -> u32 { | ||
159 | i<|> | ||
160 | }"#; | ||
161 | |||
162 | let refs = get_all_refs(code); | ||
163 | assert_eq!(refs.len(), 2); | ||
164 | } | ||
165 | |||
166 | #[test] | ||
167 | fn test_find_all_refs_for_fn_param() { | ||
168 | let code = r#" | ||
169 | fn foo(i<|> : u32) -> u32 { | ||
170 | i | ||
171 | }"#; | ||
172 | |||
173 | let refs = get_all_refs(code); | ||
174 | assert_eq!(refs.len(), 2); | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn test_find_all_refs_field_name() { | ||
179 | let code = r#" | ||
180 | //- /lib.rs | ||
181 | struct Foo { | ||
182 | pub spam<|>: u32, | ||
183 | } | ||
184 | |||
185 | fn main(s: Foo) { | ||
186 | let f = s.spam; | ||
187 | } | ||
188 | "#; | ||
189 | |||
190 | let refs = get_all_refs(code); | ||
191 | assert_eq!(refs.len(), 2); | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_find_all_refs_impl_item_name() { | ||
196 | let code = r#" | ||
197 | //- /lib.rs | ||
198 | struct Foo; | ||
199 | impl Foo { | ||
200 | fn f<|>(&self) { } | ||
201 | } | ||
202 | "#; | ||
203 | |||
204 | let refs = get_all_refs(code); | ||
205 | assert_eq!(refs.len(), 1); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn test_find_all_refs_enum_var_name() { | ||
210 | let code = r#" | ||
211 | //- /lib.rs | ||
212 | enum Foo { | ||
213 | A, | ||
214 | B<|>, | ||
215 | C, | ||
216 | } | ||
217 | "#; | ||
218 | |||
219 | let refs = get_all_refs(code); | ||
220 | assert_eq!(refs.len(), 1); | ||
221 | } | ||
222 | |||
223 | #[test] | ||
224 | fn test_find_all_refs_modules() { | ||
225 | let code = r#" | ||
226 | //- /lib.rs | ||
227 | pub mod foo; | ||
228 | pub mod bar; | ||
229 | |||
230 | fn f() { | ||
231 | let i = foo::Foo { n: 5 }; | ||
232 | } | ||
233 | |||
234 | //- /foo.rs | ||
235 | use crate::bar; | ||
236 | |||
237 | pub struct Foo { | ||
238 | pub n: u32, | ||
239 | } | ||
240 | |||
241 | fn f() { | ||
242 | let i = bar::Bar { n: 5 }; | ||
243 | } | ||
244 | |||
245 | //- /bar.rs | ||
246 | use crate::foo; | ||
247 | |||
248 | pub struct Bar { | ||
249 | pub n: u32, | ||
250 | } | ||
251 | |||
252 | fn f() { | ||
253 | let i = foo::Foo<|> { n: 5 }; | ||
254 | } | ||
255 | "#; | ||
256 | |||
257 | let (analysis, pos) = analysis_and_position(code); | ||
258 | let refs = analysis.find_all_refs(pos).unwrap().unwrap(); | ||
259 | assert_eq!(refs.len(), 3); | ||
260 | } | ||
261 | |||
262 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | ||
263 | let (analysis, position) = single_file_with_position(text); | ||
264 | analysis.find_all_refs(position).unwrap().unwrap() | ||
265 | } | ||
266 | |||
267 | #[test] | ||
268 | fn test_rename_for_local() { | ||
269 | test_rename( | ||
270 | r#" | ||
271 | fn main() { | ||
272 | let mut i = 1; | ||
273 | let j = 1; | ||
274 | i = i<|> + j; | ||
275 | |||
276 | { | ||
277 | i = 0; | ||
278 | } | ||
279 | |||
280 | i = 5; | ||
281 | }"#, | ||
282 | "k", | ||
283 | r#" | ||
284 | fn main() { | ||
285 | let mut k = 1; | ||
286 | let j = 1; | ||
287 | k = k + j; | ||
288 | |||
289 | { | ||
290 | k = 0; | ||
291 | } | ||
292 | |||
293 | k = 5; | ||
294 | }"#, | ||
295 | ); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
299 | fn test_rename_for_param_inside() { | ||
300 | test_rename( | ||
301 | r#" | ||
302 | fn foo(i : u32) -> u32 { | ||
303 | i<|> | ||
304 | }"#, | ||
305 | "j", | ||
306 | r#" | ||
307 | fn foo(j : u32) -> u32 { | ||
308 | j | ||
309 | }"#, | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_rename_refs_for_fn_param() { | ||
315 | test_rename( | ||
316 | r#" | ||
317 | fn foo(i<|> : u32) -> u32 { | ||
318 | i | ||
319 | }"#, | ||
320 | "new_name", | ||
321 | r#" | ||
322 | fn foo(new_name : u32) -> u32 { | ||
323 | new_name | ||
324 | }"#, | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_rename_for_mut_param() { | ||
330 | test_rename( | ||
331 | r#" | ||
332 | fn foo(mut i<|> : u32) -> u32 { | ||
333 | i | ||
334 | }"#, | ||
335 | "new_name", | ||
336 | r#" | ||
337 | fn foo(mut new_name : u32) -> u32 { | ||
338 | new_name | ||
339 | }"#, | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
344 | fn test_rename_mod() { | ||
345 | let (analysis, position) = analysis_and_position( | ||
346 | " | ||
347 | //- /lib.rs | ||
348 | mod bar; | ||
349 | |||
350 | //- /bar.rs | ||
351 | mod foo<|>; | ||
352 | |||
353 | //- /bar/foo.rs | ||
354 | // emtpy | ||
355 | ", | ||
356 | ); | ||
357 | let new_name = "foo2"; | ||
358 | let source_change = analysis.rename(position, new_name).unwrap(); | ||
359 | assert_debug_snapshot!(&source_change, | ||
360 | @r###" | ||
361 | Some( | ||
362 | RangeInfo { | ||
363 | range: [4; 7), | ||
364 | info: SourceChange { | ||
365 | label: "rename", | ||
366 | source_file_edits: [ | ||
367 | SourceFileEdit { | ||
368 | file_id: FileId( | ||
369 | 2, | ||
370 | ), | ||
371 | edit: TextEdit { | ||
372 | atoms: [ | ||
373 | AtomTextEdit { | ||
374 | delete: [4; 7), | ||
375 | insert: "foo2", | ||
376 | }, | ||
377 | ], | ||
378 | }, | ||
379 | }, | ||
380 | ], | ||
381 | file_system_edits: [ | ||
382 | MoveFile { | ||
383 | src: FileId( | ||
384 | 3, | ||
385 | ), | ||
386 | dst_source_root: SourceRootId( | ||
387 | 0, | ||
388 | ), | ||
389 | dst_path: "bar/foo2.rs", | ||
390 | }, | ||
391 | ], | ||
392 | cursor_position: None, | ||
393 | }, | ||
394 | }, | ||
395 | ) | ||
396 | "###); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn test_rename_mod_in_dir() { | ||
401 | let (analysis, position) = analysis_and_position( | ||
402 | " | ||
403 | //- /lib.rs | ||
404 | mod fo<|>o; | ||
405 | //- /foo/mod.rs | ||
406 | // emtpy | ||
407 | ", | ||
408 | ); | ||
409 | let new_name = "foo2"; | ||
410 | let source_change = analysis.rename(position, new_name).unwrap(); | ||
411 | assert_debug_snapshot!(&source_change, | ||
412 | @r###" | ||
413 | Some( | ||
414 | RangeInfo { | ||
415 | range: [4; 7), | ||
416 | info: SourceChange { | ||
417 | label: "rename", | ||
418 | source_file_edits: [ | ||
419 | SourceFileEdit { | ||
420 | file_id: FileId( | ||
421 | 1, | ||
422 | ), | ||
423 | edit: TextEdit { | ||
424 | atoms: [ | ||
425 | AtomTextEdit { | ||
426 | delete: [4; 7), | ||
427 | insert: "foo2", | ||
428 | }, | ||
429 | ], | ||
430 | }, | ||
431 | }, | ||
432 | ], | ||
433 | file_system_edits: [ | ||
434 | MoveFile { | ||
435 | src: FileId( | ||
436 | 2, | ||
437 | ), | ||
438 | dst_source_root: SourceRootId( | ||
439 | 0, | ||
440 | ), | ||
441 | dst_path: "foo2/mod.rs", | ||
442 | }, | ||
443 | ], | ||
444 | cursor_position: None, | ||
445 | }, | ||
446 | }, | ||
447 | ) | ||
448 | "### | ||
449 | ); | ||
450 | } | ||
451 | |||
452 | fn test_rename(text: &str, new_name: &str, expected: &str) { | ||
453 | let (analysis, position) = single_file_with_position(text); | ||
454 | let source_change = analysis.rename(position, new_name).unwrap(); | ||
455 | let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); | ||
456 | let mut file_id: Option<FileId> = None; | ||
457 | if let Some(change) = source_change { | ||
458 | for edit in change.info.source_file_edits { | ||
459 | file_id = Some(edit.file_id); | ||
460 | for atom in edit.edit.as_atoms() { | ||
461 | text_edit_builder.replace(atom.delete, atom.insert.clone()); | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | let result = | ||
466 | text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); | ||
467 | assert_eq_text!(expected, &*result); | ||
468 | } | ||
469 | } | ||
diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs new file mode 100644 index 000000000..5cb69b8fc --- /dev/null +++ b/crates/ra_ide_api/src/references/search_scope.rs | |||
@@ -0,0 +1,92 @@ | |||
1 | //! Generally, `search_scope` returns files that might contain references for the element. | ||
2 | //! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. | ||
3 | //! In some cases, the location of the references is known to within a `TextRange`, | ||
4 | //! e.g. for things like local variables. | ||
5 | |||
6 | use hir::{DefWithBody, HasSource, ModuleSource}; | ||
7 | use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; | ||
8 | use ra_syntax::{AstNode, TextRange}; | ||
9 | use rustc_hash::FxHashSet; | ||
10 | |||
11 | use crate::db::RootDatabase; | ||
12 | |||
13 | use super::{NameDefinition, NameKind}; | ||
14 | |||
15 | impl NameDefinition { | ||
16 | pub(crate) fn search_scope(&self, db: &RootDatabase) -> FxHashSet<(FileId, Option<TextRange>)> { | ||
17 | let module_src = self.container.definition_source(db); | ||
18 | let file_id = module_src.file_id.original_file(db); | ||
19 | |||
20 | if let NameKind::Pat((def, _)) = self.kind { | ||
21 | let mut res = FxHashSet::default(); | ||
22 | let range = match def { | ||
23 | DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(), | ||
24 | DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), | ||
25 | DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), | ||
26 | }; | ||
27 | res.insert((file_id, Some(range))); | ||
28 | return res; | ||
29 | } | ||
30 | |||
31 | let vis = | ||
32 | self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or("".to_string()); | ||
33 | |||
34 | if vis.as_str() == "pub(super)" { | ||
35 | if let Some(parent_module) = self.container.parent(db) { | ||
36 | let mut files = FxHashSet::default(); | ||
37 | let parent_src = parent_module.definition_source(db); | ||
38 | let file_id = parent_src.file_id.original_file(db); | ||
39 | |||
40 | match parent_src.ast { | ||
41 | ModuleSource::Module(m) => { | ||
42 | let range = Some(m.syntax().text_range()); | ||
43 | files.insert((file_id, range)); | ||
44 | } | ||
45 | ModuleSource::SourceFile(_) => { | ||
46 | files.insert((file_id, None)); | ||
47 | files.extend(parent_module.children(db).map(|m| { | ||
48 | let src = m.definition_source(db); | ||
49 | (src.file_id.original_file(db), None) | ||
50 | })); | ||
51 | } | ||
52 | } | ||
53 | return files; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | if vis.as_str() != "" { | ||
58 | let source_root_id = db.file_source_root(file_id); | ||
59 | let source_root = db.source_root(source_root_id); | ||
60 | let mut files = | ||
61 | source_root.walk().map(|id| (id.into(), None)).collect::<FxHashSet<_>>(); | ||
62 | |||
63 | // FIXME: add "pub(in path)" | ||
64 | |||
65 | if vis.as_str() == "pub(crate)" { | ||
66 | return files; | ||
67 | } | ||
68 | if vis.as_str() == "pub" { | ||
69 | let krate = self.container.krate(db).unwrap(); | ||
70 | let crate_graph = db.crate_graph(); | ||
71 | for crate_id in crate_graph.iter() { | ||
72 | let mut crate_deps = crate_graph.dependencies(crate_id); | ||
73 | if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { | ||
74 | let root_file = crate_graph.crate_root(crate_id); | ||
75 | let source_root_id = db.file_source_root(root_file); | ||
76 | let source_root = db.source_root(source_root_id); | ||
77 | files.extend(source_root.walk().map(|id| (id.into(), None))); | ||
78 | } | ||
79 | } | ||
80 | return files; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | let mut res = FxHashSet::default(); | ||
85 | let range = match module_src.ast { | ||
86 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | ||
87 | ModuleSource::SourceFile(_) => None, | ||
88 | }; | ||
89 | res.insert((file_id, range)); | ||
90 | res | ||
91 | } | ||
92 | } | ||
diff --git a/crates/ra_ide_api/src/snapshots/highlighting.html b/crates/ra_ide_api/src/snapshots/highlighting.html index ae30ebba3..b39c4d371 100644 --- a/crates/ra_ide_api/src/snapshots/highlighting.html +++ b/crates/ra_ide_api/src/snapshots/highlighting.html | |||
@@ -19,7 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
19 | .keyword\.unsafe { color: #DFAF8F; } | 19 | .keyword\.unsafe { color: #DFAF8F; } |
20 | .keyword\.control { color: #F0DFAF; font-weight: bold; } | 20 | .keyword\.control { color: #F0DFAF; font-weight: bold; } |
21 | </style> | 21 | </style> |
22 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute text">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> | 22 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> |
23 | <span class="keyword">struct</span> <span class="type">Foo</span> { | 23 | <span class="keyword">struct</span> <span class="type">Foo</span> { |
24 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type">i32</span>, | 24 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type">i32</span>, |
25 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type">i32</span>, | 25 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type">i32</span>, |
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 797e9926f..5729eb5b3 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -29,7 +29,7 @@ use std::{ | |||
29 | use fst::{self, Streamer}; | 29 | use fst::{self, Streamer}; |
30 | use ra_db::{ | 30 | use ra_db::{ |
31 | salsa::{self, ParallelDatabase}, | 31 | salsa::{self, ParallelDatabase}, |
32 | SourceDatabase, SourceRootId, | 32 | SourceDatabaseExt, SourceRootId, |
33 | }; | 33 | }; |
34 | use ra_syntax::{ | 34 | use ra_syntax::{ |
35 | ast::{self, NameOwner}, | 35 | ast::{self, NameOwner}, |
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 9ae2dc061..33f3caceb 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs | |||
@@ -14,7 +14,7 @@ use ra_syntax::{ | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | db::RootDatabase, | 16 | db::RootDatabase, |
17 | name_ref_kind::{classify_name_ref, NameRefKind::*}, | 17 | references::{classify_name_ref, NameKind::*}, |
18 | FileId, | 18 | FileId, |
19 | }; | 19 | }; |
20 | 20 | ||
@@ -97,13 +97,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
97 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", | 97 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", |
98 | ATTR => "attribute", | 98 | ATTR => "attribute", |
99 | NAME_REF => { | 99 | NAME_REF => { |
100 | if node.ancestors().any(|it| it.kind() == ATTR) { | ||
101 | continue; | ||
102 | } | ||
100 | if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) { | 103 | if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) { |
101 | // FIXME: try to reuse the SourceAnalyzers | 104 | let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); |
102 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 105 | match name_kind { |
103 | match classify_name_ref(db, &analyzer, &name_ref) { | ||
104 | Some(Method(_)) => "function", | ||
105 | Some(Macro(_)) => "macro", | 106 | Some(Macro(_)) => "macro", |
106 | Some(FieldAccess(_)) => "field", | 107 | Some(Field(_)) => "field", |
107 | Some(AssocItem(hir::AssocItem::Function(_))) => "function", | 108 | Some(AssocItem(hir::AssocItem::Function(_))) => "function", |
108 | Some(AssocItem(hir::AssocItem::Const(_))) => "constant", | 109 | Some(AssocItem(hir::AssocItem::Const(_))) => "constant", |
109 | Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type", | 110 | Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type", |
@@ -117,7 +118,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
117 | Some(Def(hir::ModuleDef::TypeAlias(_))) => "type", | 118 | Some(Def(hir::ModuleDef::TypeAlias(_))) => "type", |
118 | Some(Def(hir::ModuleDef::BuiltinType(_))) => "type", | 119 | Some(Def(hir::ModuleDef::BuiltinType(_))) => "type", |
119 | Some(SelfType(_)) => "type", | 120 | Some(SelfType(_)) => "type", |
120 | Some(Pat(ptr)) => { | 121 | Some(Pat((_, ptr))) => { |
121 | let pat = ptr.to_node(&root); | 122 | let pat = ptr.to_node(&root); |
122 | if let Some(name) = pat.name() { | 123 | if let Some(name) = pat.name() { |
123 | let text = name.text(); | 124 | let text = name.text(); |
@@ -127,6 +128,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
127 | Some(calc_binding_hash(file_id, &text, *shadow_count)) | 128 | Some(calc_binding_hash(file_id, &text, *shadow_count)) |
128 | } | 129 | } |
129 | 130 | ||
131 | let analyzer = | ||
132 | hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | ||
130 | if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) { | 133 | if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) { |
131 | "variable.mut" | 134 | "variable.mut" |
132 | } else { | 135 | } else { |