aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/Cargo.toml3
-rw-r--r--crates/ra_ide_api/src/change.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs185
-rw-r--r--crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs62
-rw-r--r--crates/ra_ide_api/src/completion/complete_path.rs166
-rw-r--r--crates/ra_ide_api/src/completion/complete_postfix.rs67
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs679
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs19
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs389
-rw-r--r--crates/ra_ide_api/src/db.rs25
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs13
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs6
-rw-r--r--crates/ra_ide_api/src/extend_selection.rs3
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs12
-rw-r--r--crates/ra_ide_api/src/hover.rs11
-rw-r--r--crates/ra_ide_api/src/lib.rs14
-rw-r--r--crates/ra_ide_api/src/marks.rs1
-rw-r--r--crates/ra_ide_api/src/name_ref_kind.rs98
-rw-r--r--crates/ra_ide_api/src/references.rs524
-rw-r--r--crates/ra_ide_api/src/references/classify.rs179
-rw-r--r--crates/ra_ide_api/src/references/name_definition.rs113
-rw-r--r--crates/ra_ide_api/src/references/rename.rs469
-rw-r--r--crates/ra_ide_api/src/references/search_scope.rs92
-rw-r--r--crates/ra_ide_api/src/snapshots/highlighting.html2
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs2
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs17
27 files changed, 2121 insertions, 1036 deletions
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index f919a2d61..f9bf0c686 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -12,13 +12,14 @@ format-buf = "1.0.0"
12itertools = "0.8.0" 12itertools = "0.8.0"
13join_to_string = "0.1.3" 13join_to_string = "0.1.3"
14log = "0.4.5" 14log = "0.4.5"
15relative-path = "0.4.0" 15relative-path = "1.0.0"
16rayon = "1.0.2" 16rayon = "1.0.2"
17fst = { version = "0.3.1", default-features = false } 17fst = { version = "0.3.1", default-features = false }
18rustc-hash = "1.0" 18rustc-hash = "1.0"
19unicase = "2.2.0" 19unicase = "2.2.0"
20superslice = "1.0.0" 20superslice = "1.0.0"
21rand = { version = "0.7.0", features = ["small_rng"] } 21rand = { version = "0.7.0", features = ["small_rng"] }
22once_cell = "1.2.0"
22 23
23ra_syntax = { path = "../ra_syntax" } 24ra_syntax = { path = "../ra_syntax" }
24ra_text_edit = { path = "../ra_text_edit" } 25ra_text_edit = { path = "../ra_text_edit" }
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
5use ra_db::{ 5use 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};
9use ra_prof::{memory_usage, profile, Bytes}; 9use ra_prof::{memory_usage, profile, Bytes};
10use ra_syntax::SourceFile; 10use 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};
10use hir::{Ty, TypeCtor}; 10use hir::{Ty, TypeCtor};
11use ra_syntax::{ast::AstNode, TextRange}; 11use ra_syntax::{ast::AstNode, TextRange, TextUnit};
12use ra_text_edit::TextEditBuilder; 12use ra_text_edit::TextEditBuilder;
13 13
14fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { 14fn 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
43pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 43pub(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
45impl<'a> CompletionContext<'a> { 48impl<'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
3use hir::{Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; 3use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
4use join_to_string::join; 4use join_to_string::join;
5use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::completion::{ 8use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 9 db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12use crate::display::{const_label, function_label, macro_label, type_label}; 12use 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(&macro_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 &macro_declaration, 179 &macro_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
277fn 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)]
226mod tests { 283mod 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
5use ra_db::{ 5use 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};
10use relative_path::RelativePath;
9use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
10 12
11use crate::{ 13use 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
37impl 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
34impl hir::debug::HirDebugHelper for RootDatabase { 53impl 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
107fn line_index(db: &impl ra_db::SourceDatabase, file_id: FileId) -> Arc<LineIndex> { 126fn 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
5use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; 5use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
6use itertools::Itertools; 6use itertools::Itertools;
7use ra_db::SourceDatabase; 7use ra_db::{SourceDatabase, SourceDatabaseExt};
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ 9use 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};
14use ra_text_edit::{TextEdit, TextEditBuilder}; 14use ra_text_edit::{TextEdit, TextEditBuilder};
15use relative_path::RelativePath;
15 16
16use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; 17use 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#"
250const FOO: [usize; 2] = [ 253const 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::{
10use crate::{ 10use 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;
19mod status; 19mod status;
20mod completion; 20mod completion;
21mod runnables; 21mod runnables;
22mod name_ref_kind;
23mod goto_definition; 22mod goto_definition;
24mod goto_type_definition; 23mod goto_type_definition;
25mod extend_selection; 24mod extend_selection;
@@ -52,7 +51,7 @@ use std::sync::Arc;
52use ra_cfg::CfgOptions; 51use ra_cfg::CfgOptions;
53use ra_db::{ 52use ra_db::{
54 salsa::{self, ParallelDatabase}, 53 salsa::{self, ParallelDatabase},
55 CheckCanceled, SourceDatabase, 54 CheckCanceled, FileLoader, SourceDatabase,
56}; 55};
57use ra_syntax::{SourceFile, TextRange, TextUnit}; 56use ra_syntax::{SourceFile, TextRange, TextUnit};
58use ra_text_edit::TextEdit; 57use 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
3test_utils::marks!( 3test_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
3use hir::Either;
4use ra_syntax::{ast, AstNode, AstPtr};
5use test_utils::tested_by;
6
7use crate::db::RootDatabase;
8
9pub 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
21pub(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, &macro_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`
3use hir::{Either, ModuleSource}; 3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
4use ra_db::SourceDatabase; 4//! try to resolve the direct tree parent of this element, otherwise we
5use 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
6use 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
8use 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
12mod classify;
13mod name_definition;
14mod rename;
15mod search_scope;
16
17use once_cell::unsync::Lazy;
18use ra_db::{SourceDatabase, SourceDatabaseExt};
19use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit};
20
21use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo};
22
23pub(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
90pub(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
107fn find_name_and_module_at_offset( 93fn 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
116fn 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
131fn rename_mod( 109fn 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
183fn 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)]
203mod tests { 142mod 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
3use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
4use ra_db::FileId;
5use ra_syntax::{ast, match_ast, AstNode, AstPtr};
6use test_utils::tested_by;
7
8use super::{
9 name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field},
10 NameDefinition, NameKind,
11};
12use crate::db::RootDatabase;
13
14pub(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
104pub(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, &macro_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
6use hir::{
7 db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef,
8 Module, ModuleDef, StructField, Ty, VariantDef,
9};
10use ra_syntax::{ast, ast::VisibilityOwner, match_ast, AstNode, AstPtr};
11
12use crate::db::RootDatabase;
13
14#[derive(Debug, PartialEq, Eq)]
15pub 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)]
27pub(crate) struct NameDefinition {
28 pub visibility: Option<ast::Visibility>,
29 pub container: Module,
30 pub kind: NameKind,
31}
32
33pub(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
63pub(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
74pub(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
85pub(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
3use hir::ModuleSource;
4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
6use relative_path::{RelativePath, RelativePathBuf};
7
8use crate::{
9 db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange,
10 SourceFileEdit, TextRange,
11};
12
13use super::find_all_refs;
14
15pub(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
32fn 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
41fn 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
56fn 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
108fn 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)]
128mod 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
6use hir::{DefWithBody, HasSource, ModuleSource};
7use ra_db::{FileId, SourceDatabase, SourceDatabaseExt};
8use ra_syntax::{AstNode, TextRange};
9use rustc_hash::FxHashSet;
10
11use crate::db::RootDatabase;
12
13use super::{NameDefinition, NameKind};
14
15impl 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::{
29use fst::{self, Streamer}; 29use fst::{self, Streamer};
30use ra_db::{ 30use ra_db::{
31 salsa::{self, ParallelDatabase}, 31 salsa::{self, ParallelDatabase},
32 SourceDatabase, SourceRootId, 32 SourceDatabaseExt, SourceRootId,
33}; 33};
34use ra_syntax::{ 34use 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
15use crate::{ 15use 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 {