aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/call_info.rs14
-rw-r--r--crates/ra_ide/src/completion.rs8
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs58
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs (renamed from crates/ra_ide/src/completion/complete_path.rs)26
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs101
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs (renamed from crates/ra_ide/src/completion/complete_scope.rs)12
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs35
-rw-r--r--crates/ra_ide/src/completion/presentation.rs278
-rw-r--r--crates/ra_ide/src/diagnostics.rs10
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs42
-rw-r--r--crates/ra_ide/src/display/structure.rs16
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs6
-rw-r--r--crates/ra_ide/src/lib.rs5
-rw-r--r--crates/ra_ide/src/marks.rs1
-rw-r--r--crates/ra_ide/src/runnables.rs4
-rw-r--r--crates/ra_ide/src/ssr.rs122
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs6
21 files changed, 506 insertions, 252 deletions
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 39d09a07f..ca57eceff 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -109,7 +109,7 @@ impl FnCallNode {
109 syntax.ancestors().find_map(|node| { 109 syntax.ancestors().find_map(|node| {
110 match_ast! { 110 match_ast! {
111 match node { 111 match node {
112 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, 112 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
113 ast::MethodCallExpr(it) => { 113 ast::MethodCallExpr(it) => {
114 let arg_list = it.arg_list()?; 114 let arg_list = it.arg_list()?;
115 if !syntax.text_range().is_subrange(&arg_list.syntax().text_range()) { 115 if !syntax.text_range().is_subrange(&arg_list.syntax().text_range()) {
@@ -117,8 +117,8 @@ impl FnCallNode {
117 } 117 }
118 Some(FnCallNode::MethodCallExpr(it)) 118 Some(FnCallNode::MethodCallExpr(it))
119 }, 119 },
120 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) }, 120 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
121 _ => { None }, 121 _ => None,
122 } 122 }
123 } 123 }
124 }) 124 })
@@ -127,10 +127,10 @@ impl FnCallNode {
127 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> { 127 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
128 match_ast! { 128 match_ast! {
129 match node { 129 match node {
130 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, 130 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
131 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) }, 131 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
132 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) }, 132 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
133 _ => { None }, 133 _ => None,
134 } 134 }
135 } 135 }
136 } 136 }
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 93157bbba..4a1a2a04a 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -10,8 +10,8 @@ mod complete_pattern;
10mod complete_fn_param; 10mod complete_fn_param;
11mod complete_keyword; 11mod complete_keyword;
12mod complete_snippet; 12mod complete_snippet;
13mod complete_path; 13mod complete_qualified_path;
14mod complete_scope; 14mod complete_unqualified_path;
15mod complete_postfix; 15mod complete_postfix;
16mod complete_macro_in_item_position; 16mod complete_macro_in_item_position;
17mod complete_trait_impl; 17mod complete_trait_impl;
@@ -85,8 +85,8 @@ pub(crate) fn completions(
85 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx); 85 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
86 complete_snippet::complete_expr_snippet(&mut acc, &ctx); 86 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
87 complete_snippet::complete_item_snippet(&mut acc, &ctx); 87 complete_snippet::complete_item_snippet(&mut acc, &ctx);
88 complete_path::complete_path(&mut acc, &ctx); 88 complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
89 complete_scope::complete_scope(&mut acc, &ctx); 89 complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
90 complete_dot::complete_dot(&mut acc, &ctx); 90 complete_dot::complete_dot(&mut acc, &ctx);
91 complete_record::complete_record(&mut acc, &ctx); 91 complete_record::complete_record(&mut acc, &ctx);
92 complete_pattern::complete_pattern(&mut acc, &ctx); 92 complete_pattern::complete_pattern(&mut acc, &ctx);
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 82ec16913..f433faef3 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -61,7 +61,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
61 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 61 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m))
62 && seen_methods.insert(func.name(ctx.db)) 62 && seen_methods.insert(func.name(ctx.db))
63 { 63 {
64 acc.add_function(ctx, func); 64 acc.add_function(ctx, func, None);
65 } 65 }
66 None::<()> 66 None::<()>
67 }); 67 });
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index 9226ac055..62ae5ccb4 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -18,8 +18,8 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
18 for node in ctx.token.parent().ancestors() { 18 for node in ctx.token.parent().ancestors() {
19 match_ast! { 19 match_ast! {
20 match node { 20 match node {
21 ast::SourceFile(it) => { process(it, &mut params) }, 21 ast::SourceFile(it) => process(it, &mut params),
22 ast::ItemList(it) => { process(it, &mut params) }, 22 ast::ItemList(it) => process(it, &mut params),
23 _ => (), 23 _ => (),
24 } 24 }
25 } 25 }
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 1e053ea4a..38f9c34e7 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -86,9 +86,9 @@ fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
86 } 86 }
87 let loop_body = match_ast! { 87 let loop_body = match_ast! {
88 match node { 88 match node {
89 ast::ForExpr(it) => { it.loop_body() }, 89 ast::ForExpr(it) => it.loop_body(),
90 ast::WhileExpr(it) => { it.loop_body() }, 90 ast::WhileExpr(it) => it.loop_body(),
91 ast::LoopExpr(it) => { it.loop_body() }, 91 ast::LoopExpr(it) => it.loop_body(),
92 _ => None, 92 _ => None,
93 } 93 }
94 }; 94 };
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index bc8fade6f..1b7d3122f 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -4,23 +4,25 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Completes constats and paths in patterns. 5/// Completes constats and paths in patterns.
6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding { 7 if !ctx.is_pat_binding_or_const {
8 return; 8 return;
9 } 9 }
10 // FIXME: ideally, we should look at the type we are matching against and 10 // FIXME: ideally, we should look at the type we are matching against and
11 // suggest variants + auto-imports 11 // suggest variants + auto-imports
12 ctx.scope().process_all_names(&mut |name, res| { 12 ctx.scope().process_all_names(&mut |name, res| {
13 let def = match &res { 13 match &res {
14 hir::ScopeDef::ModuleDef(def) => def, 14 hir::ScopeDef::ModuleDef(def) => match def {
15 hir::ModuleDef::Adt(hir::Adt::Enum(..))
16 | hir::ModuleDef::Adt(hir::Adt::Struct(..))
17 | hir::ModuleDef::EnumVariant(..)
18 | hir::ModuleDef::Const(..)
19 | hir::ModuleDef::Module(..) => (),
20 _ => return,
21 },
22 hir::ScopeDef::MacroDef(_) => (),
15 _ => return, 23 _ => return,
16 }; 24 };
17 match def { 25
18 hir::ModuleDef::Adt(hir::Adt::Enum(..))
19 | hir::ModuleDef::EnumVariant(..)
20 | hir::ModuleDef::Const(..)
21 | hir::ModuleDef::Module(..) => (),
22 _ => return,
23 }
24 acc.add_resolution(ctx, name.to_string(), &res) 26 acc.add_resolution(ctx, name.to_string(), &res)
25 }); 27 });
26} 28}
@@ -70,32 +72,12 @@ mod tests {
70 kind: Enum, 72 kind: Enum,
71 }, 73 },
72 CompletionItem { 74 CompletionItem {
73 label: "E",
74 source_range: [246; 246),
75 delete: [246; 246),
76 insert: "E",
77 kind: Enum,
78 },
79 CompletionItem {
80 label: "X", 75 label: "X",
81 source_range: [246; 246), 76 source_range: [246; 246),
82 delete: [246; 246), 77 delete: [246; 246),
83 insert: "X", 78 insert: "X",
84 kind: EnumVariant, 79 kind: EnumVariant,
85 }, 80 detail: "()",
86 CompletionItem {
87 label: "X",
88 source_range: [246; 246),
89 delete: [246; 246),
90 insert: "X",
91 kind: EnumVariant,
92 },
93 CompletionItem {
94 label: "Z",
95 source_range: [246; 246),
96 delete: [246; 246),
97 insert: "Z",
98 kind: Const,
99 }, 81 },
100 CompletionItem { 82 CompletionItem {
101 label: "Z", 83 label: "Z",
@@ -111,13 +93,6 @@ mod tests {
111 insert: "m", 93 insert: "m",
112 kind: Module, 94 kind: Module,
113 }, 95 },
114 CompletionItem {
115 label: "m",
116 source_range: [246; 246),
117 delete: [246; 246),
118 insert: "m",
119 kind: Module,
120 },
121 ] 96 ]
122 "###); 97 "###);
123 } 98 }
@@ -146,13 +121,6 @@ mod tests {
146 kind: Enum, 121 kind: Enum,
147 }, 122 },
148 CompletionItem { 123 CompletionItem {
149 label: "E",
150 source_range: [151; 151),
151 delete: [151; 151),
152 insert: "E",
153 kind: Enum,
154 },
155 CompletionItem {
156 label: "m!", 124 label: "m!",
157 source_range: [151; 151), 125 source_range: [151; 151),
158 delete: [151; 151), 126 delete: [151; 151),
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index 3db17f15f..d98523406 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -6,7 +6,7 @@ use test_utils::tested_by;
6 6
7use crate::completion::{CompletionContext, Completions}; 7use crate::completion::{CompletionContext, Completions};
8 8
9pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 let path = match &ctx.path_prefix { 10 let path = match &ctx.path_prefix {
11 Some(path) => path.clone(), 11 Some(path) => path.clone(),
12 _ => return, 12 _ => return,
@@ -38,7 +38,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
38 hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { 38 hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
39 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { 39 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
40 for variant in e.variants(ctx.db) { 40 for variant in e.variants(ctx.db) {
41 acc.add_enum_variant(ctx, variant); 41 acc.add_enum_variant(ctx, variant, None);
42 } 42 }
43 } 43 }
44 let ty = match def { 44 let ty = match def {
@@ -58,7 +58,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
58 match item { 58 match item {
59 hir::AssocItem::Function(func) => { 59 hir::AssocItem::Function(func) => {
60 if !func.has_self_param(ctx.db) { 60 if !func.has_self_param(ctx.db) {
61 acc.add_function(ctx, func); 61 acc.add_function(ctx, func, None);
62 } 62 }
63 } 63 }
64 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 64 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
@@ -87,7 +87,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
87 match item { 87 match item {
88 hir::AssocItem::Function(func) => { 88 hir::AssocItem::Function(func) => {
89 if !func.has_self_param(ctx.db) { 89 if !func.has_self_param(ctx.db) {
90 acc.add_function(ctx, func); 90 acc.add_function(ctx, func, None);
91 } 91 }
92 } 92 }
93 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 93 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
@@ -355,15 +355,17 @@ mod tests {
355 @r###" 355 @r###"
356 [ 356 [
357 CompletionItem { 357 CompletionItem {
358 label: "Bar", 358 label: "Bar(…)",
359 source_range: [116; 116), 359 source_range: [116; 116),
360 delete: [116; 116), 360 delete: [116; 116),
361 insert: "Bar", 361 insert: "Bar($0)",
362 kind: EnumVariant, 362 kind: EnumVariant,
363 lookup: "Bar",
363 detail: "(i32)", 364 detail: "(i32)",
364 documentation: Documentation( 365 documentation: Documentation(
365 "Bar Variant with i32", 366 "Bar Variant with i32",
366 ), 367 ),
368 trigger_call_info: true,
367 }, 369 },
368 CompletionItem { 370 CompletionItem {
369 label: "Foo", 371 label: "Foo",
@@ -403,15 +405,17 @@ mod tests {
403 @r###" 405 @r###"
404 [ 406 [
405 CompletionItem { 407 CompletionItem {
406 label: "Bar", 408 label: "Bar(…)",
407 source_range: [180; 180), 409 source_range: [180; 180),
408 delete: [180; 180), 410 delete: [180; 180),
409 insert: "Bar", 411 insert: "Bar($0)",
410 kind: EnumVariant, 412 kind: EnumVariant,
413 lookup: "Bar",
411 detail: "(i32, u32)", 414 detail: "(i32, u32)",
412 documentation: Documentation( 415 documentation: Documentation(
413 "Bar Variant with i32 and u32", 416 "Bar Variant with i32 and u32",
414 ), 417 ),
418 trigger_call_info: true,
415 }, 419 },
416 CompletionItem { 420 CompletionItem {
417 label: "Foo", 421 label: "Foo",
@@ -425,15 +429,17 @@ mod tests {
425 ), 429 ),
426 }, 430 },
427 CompletionItem { 431 CompletionItem {
428 label: "S", 432 label: "S(…)",
429 source_range: [180; 180), 433 source_range: [180; 180),
430 delete: [180; 180), 434 delete: [180; 180),
431 insert: "S", 435 insert: "S($0)",
432 kind: EnumVariant, 436 kind: EnumVariant,
437 lookup: "S",
433 detail: "(S)", 438 detail: "(S)",
434 documentation: Documentation( 439 documentation: Documentation(
435 "", 440 "",
436 ), 441 ),
442 trigger_call_info: true,
437 }, 443 },
438 ] 444 ]
439 "### 445 "###
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
index 01dd8c6db..f46bcee5c 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -1,67 +1,28 @@
1//! Complete fields in record literals and patterns. 1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions}; 2use crate::completion::{CompletionContext, Completions};
3use ra_syntax::{ast, ast::NameOwner, SmolStr};
4 3
5pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
6 let (ty, variant, already_present_fields) = 5 let missing_fields = match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
7 match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { 6 (None, None) => return None,
8 (None, None) => return None, 7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
9 (Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"), 8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
10 (Some(record_pat), _) => ( 9 (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
11 ctx.sema.type_of_pat(&record_pat.clone().into())?, 10 };
12 ctx.sema.resolve_record_pattern(record_pat)?,
13 pattern_ascribed_fields(record_pat),
14 ),
15 (_, Some(record_lit)) => (
16 ctx.sema.type_of_expr(&record_lit.clone().into())?,
17 ctx.sema.resolve_record_literal(record_lit)?,
18 literal_ascribed_fields(record_lit),
19 ),
20 };
21 11
22 for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| { 12 for (field, ty) in missing_fields {
23 // FIXME: already_present_names better be `Vec<hir::Name>` 13 acc.add_field(ctx, field, &ty)
24 !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
25 }) {
26 acc.add_field(ctx, field, &field_ty);
27 } 14 }
28 Some(())
29}
30
31fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
32 record_lit
33 .record_field_list()
34 .map(|field_list| field_list.fields())
35 .map(|fields| {
36 fields
37 .into_iter()
38 .filter_map(|field| field.name_ref())
39 .map(|name_ref| name_ref.text().clone())
40 .collect()
41 })
42 .unwrap_or_default()
43}
44 15
45fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> { 16 Some(())
46 record_pat
47 .record_field_pat_list()
48 .map(|pat_list| {
49 pat_list
50 .record_field_pats()
51 .filter_map(|fild_pat| fild_pat.name())
52 .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
53 .map(|name| name.text().clone())
54 .collect()
55 })
56 .unwrap_or_default()
57} 17}
58 18
59#[cfg(test)] 19#[cfg(test)]
60mod tests { 20mod tests {
61 mod record_lit_tests { 21 mod record_pat_tests {
62 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
63 use insta::assert_debug_snapshot; 22 use insta::assert_debug_snapshot;
64 23
24 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
25
65 fn complete(code: &str) -> Vec<CompletionItem> { 26 fn complete(code: &str) -> Vec<CompletionItem> {
66 do_completion(code, CompletionKind::Reference) 27 do_completion(code, CompletionKind::Reference)
67 } 28 }
@@ -203,10 +164,11 @@ mod tests {
203 } 164 }
204 } 165 }
205 166
206 mod record_pat_tests { 167 mod record_lit_tests {
207 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
208 use insta::assert_debug_snapshot; 168 use insta::assert_debug_snapshot;
209 169
170 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
171
210 fn complete(code: &str) -> Vec<CompletionItem> { 172 fn complete(code: &str) -> Vec<CompletionItem> {
211 do_completion(code, CompletionKind::Reference) 173 do_completion(code, CompletionKind::Reference)
212 } 174 }
@@ -407,5 +369,38 @@ mod tests {
407 ] 369 ]
408 "###); 370 "###);
409 } 371 }
372
373 #[test]
374 fn completes_functional_update() {
375 let completions = complete(
376 r"
377 struct S {
378 foo1: u32,
379 foo2: u32,
380 }
381
382 fn main() {
383 let foo1 = 1;
384 let s = S {
385 foo1,
386 <|>
387 .. loop {}
388 }
389 }
390 ",
391 );
392 assert_debug_snapshot!(completions, @r###"
393 [
394 CompletionItem {
395 label: "foo2",
396 source_range: [221; 221),
397 delete: [221; 221),
398 insert: "foo2",
399 kind: Field,
400 detail: "u32",
401 },
402 ]
403 "###);
404 }
410 } 405 }
411} 406}
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 2ca552733..efde9bf73 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,19 +1,13 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4use hir::{ModuleDef, ScopeDef};
5 4
6pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_trivial_path && !ctx.is_pat_binding_and_path { 6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const) {
8 return; 7 return;
9 } 8 }
10 9
11 ctx.scope().process_all_names(&mut |name, res| match (ctx.is_pat_binding_and_path, &res) { 10 ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res));
12 (true, ScopeDef::ModuleDef(ModuleDef::Function(..))) => (),
13 (true, ScopeDef::ModuleDef(ModuleDef::Static(..))) => (),
14 (true, ScopeDef::Local(..)) => (),
15 _ => acc.add_resolution(ctx, name.to_string(), &res),
16 });
17} 11}
18 12
19#[cfg(test)] 13#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index fdc0da2c5..f833d2a9a 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -35,10 +35,7 @@ pub(crate) struct CompletionContext<'a> {
35 pub(super) is_param: bool, 35 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 36 /// If a name-binding or reference to a const in a pattern.
37 /// Irrefutable patterns (like let) are excluded. 37 /// Irrefutable patterns (like let) are excluded.
38 pub(super) is_pat_binding: bool, 38 pub(super) is_pat_binding_or_const: bool,
39 // A bind battern which may also be part of a path.
40 // if let Some(En<|>) = Some(Enum::A)
41 pub(super) is_pat_binding_and_path: bool,
42 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 39 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
43 pub(super) is_trivial_path: bool, 40 pub(super) is_trivial_path: bool,
44 /// If not a trivial path, the prefix (qualifier). 41 /// If not a trivial path, the prefix (qualifier).
@@ -53,6 +50,8 @@ pub(crate) struct CompletionContext<'a> {
53 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 50 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
54 /// If this is a call (method or function) in particular, i.e. the () are already there. 51 /// If this is a call (method or function) in particular, i.e. the () are already there.
55 pub(super) is_call: bool, 52 pub(super) is_call: bool,
53 /// If this is a macro call, i.e. the () are already there.
54 pub(super) is_macro_call: bool,
56 pub(super) is_path_type: bool, 55 pub(super) is_path_type: bool,
57 pub(super) has_type_args: bool, 56 pub(super) has_type_args: bool,
58} 57}
@@ -97,8 +96,7 @@ impl<'a> CompletionContext<'a> {
97 record_lit_pat: None, 96 record_lit_pat: None,
98 impl_def: None, 97 impl_def: None,
99 is_param: false, 98 is_param: false,
100 is_pat_binding: false, 99 is_pat_binding_or_const: false,
101 is_pat_binding_and_path: false,
102 is_trivial_path: false, 100 is_trivial_path: false,
103 path_prefix: None, 101 path_prefix: None,
104 after_if: false, 102 after_if: false,
@@ -106,6 +104,7 @@ impl<'a> CompletionContext<'a> {
106 is_new_item: false, 104 is_new_item: false,
107 dot_receiver: None, 105 dot_receiver: None,
108 is_call: false, 106 is_call: false,
107 is_macro_call: false,
109 is_path_type: false, 108 is_path_type: false,
110 has_type_args: false, 109 has_type_args: false,
111 dot_receiver_is_ambiguous_float_literal: false, 110 dot_receiver_is_ambiguous_float_literal: false,
@@ -190,18 +189,19 @@ impl<'a> CompletionContext<'a> {
190 // suggest declaration names, see `CompletionKind::Magic`. 189 // suggest declaration names, see `CompletionKind::Magic`.
191 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { 190 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
192 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { 191 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
193 let parent = bind_pat.syntax().parent(); 192 self.is_pat_binding_or_const = true;
194 if parent.clone().and_then(ast::MatchArm::cast).is_some() 193 if bind_pat.has_at() || bind_pat.is_ref() || bind_pat.is_mutable() {
195 || parent.clone().and_then(ast::Condition::cast).is_some() 194 self.is_pat_binding_or_const = false;
196 {
197 self.is_pat_binding = true;
198 } 195 }
199 196 if bind_pat.syntax().parent().and_then(ast::RecordFieldPatList::cast).is_some() {
200 if parent.and_then(ast::RecordFieldPatList::cast).is_none() 197 self.is_pat_binding_or_const = false;
201 && bind_pat.pat().is_none() 198 }
202 && !bind_pat.is_ref() 199 if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
203 { 200 if let Some(pat) = let_stmt.pat() {
204 self.is_pat_binding_and_path = true; 201 if bind_pat.syntax().text_range().is_subrange(&pat.syntax().text_range()) {
202 self.is_pat_binding_or_const = false;
203 }
204 }
205 } 205 }
206 } 206 }
207 if is_node::<ast::Param>(name.syntax()) { 207 if is_node::<ast::Param>(name.syntax()) {
@@ -272,6 +272,7 @@ impl<'a> CompletionContext<'a> {
272 .and_then(ast::PathExpr::cast) 272 .and_then(ast::PathExpr::cast)
273 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 273 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
274 .is_some(); 274 .is_some();
275 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
275 276
276 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 277 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
277 self.has_type_args = segment.type_arg_list().is_some(); 278 self.has_type_args = segment.type_arg_list().is_some();
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 1c7c0924d..55f75b15a 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -7,7 +7,8 @@ use test_utils::tested_by;
7 7
8use crate::{ 8use crate::{
9 completion::{ 9 completion::{
10 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 10 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
11 CompletionKind, Completions,
11 }, 12 },
12 display::{const_label, macro_label, type_label, FunctionSignature}, 13 display::{const_label, macro_label, type_label, FunctionSignature},
13 RootDatabase, 14 RootDatabase,
@@ -56,14 +57,16 @@ impl Completions {
56 let kind = match resolution { 57 let kind = match resolution {
57 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, 58 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
58 ScopeDef::ModuleDef(Function(func)) => { 59 ScopeDef::ModuleDef(Function(func)) => {
59 return self.add_function_with_name(ctx, Some(local_name), *func); 60 return self.add_function(ctx, *func, Some(local_name));
60 } 61 }
61 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, 62 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
62 // FIXME: add CompletionItemKind::Union 63 // FIXME: add CompletionItemKind::Union
63 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, 64 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
64 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, 65 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
65 66
66 ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant, 67 ScopeDef::ModuleDef(EnumVariant(var)) => {
68 return self.add_enum_variant(ctx, *var, Some(local_name));
69 }
67 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, 70 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
68 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, 71 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
69 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, 72 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
@@ -124,10 +127,6 @@ impl Completions {
124 completion_item.kind(kind).set_documentation(docs).add_to(self) 127 completion_item.kind(kind).set_documentation(docs).add_to(self)
125 } 128 }
126 129
127 pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) {
128 self.add_function_with_name(ctx, None, func)
129 }
130
131 fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { 130 fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
132 let mut votes = [0, 0, 0]; 131 let mut votes = [0, 0, 0];
133 for (idx, s) in docs.match_indices(&macro_name) { 132 for (idx, s) in docs.match_indices(&macro_name) {
@@ -175,7 +174,8 @@ impl Completions {
175 .set_deprecated(is_deprecated(macro_, ctx.db)) 174 .set_deprecated(is_deprecated(macro_, ctx.db))
176 .detail(detail); 175 .detail(detail);
177 176
178 builder = if ctx.use_item_syntax.is_some() { 177 builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call {
178 tested_by!(dont_insert_macro_call_parens_unncessary);
179 builder.insert_text(name) 179 builder.insert_text(name)
180 } else { 180 } else {
181 let macro_braces_to_insert = 181 let macro_braces_to_insert =
@@ -186,16 +186,15 @@ impl Completions {
186 self.add(builder); 186 self.add(builder);
187 } 187 }
188 188
189 fn add_function_with_name( 189 pub(crate) fn add_function(
190 &mut self, 190 &mut self,
191 ctx: &CompletionContext, 191 ctx: &CompletionContext,
192 name: Option<String>,
193 func: hir::Function, 192 func: hir::Function,
193 local_name: Option<String>,
194 ) { 194 ) {
195 let has_self_param = func.has_self_param(ctx.db); 195 let has_self_param = func.has_self_param(ctx.db);
196 let params = func.params(ctx.db);
197 196
198 let name = name.unwrap_or_else(|| func.name(ctx.db).to_string()); 197 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
199 let ast_node = func.source(ctx.db).value; 198 let ast_node = func.source(ctx.db).value;
200 let function_signature = FunctionSignature::from(&ast_node); 199 let function_signature = FunctionSignature::from(&ast_node);
201 200
@@ -210,32 +209,14 @@ impl Completions {
210 .set_deprecated(is_deprecated(func, ctx.db)) 209 .set_deprecated(is_deprecated(func, ctx.db))
211 .detail(function_signature.to_string()); 210 .detail(function_signature.to_string());
212 211
213 // If not an import, add parenthesis automatically. 212 let params = function_signature
214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis { 213 .parameter_names
215 tested_by!(inserts_parens_for_function_calls); 214 .iter()
216 215 .skip(if function_signature.has_self_param { 1 } else { 0 })
217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 216 .cloned()
218 (format!("{}()$0", name), format!("{}()", name)) 217 .collect();
219 } else {
220 builder = builder.trigger_call_info();
221 let snippet = if ctx.config.add_call_argument_snippets {
222 let to_skip = if has_self_param { 1 } else { 0 };
223 let function_params_snippet = function_signature
224 .parameter_names
225 .iter()
226 .skip(to_skip)
227 .enumerate()
228 .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
229 .sep_by(", ");
230 format!("{}({})$0", name, function_params_snippet)
231 } else {
232 format!("{}($0)", name)
233 };
234 218
235 (snippet, format!("{}(…)", name)) 219 builder = builder.add_call_parens(ctx, name, Params::Named(params));
236 };
237 builder = builder.lookup_by(name).label(label).insert_snippet(snippet);
238 }
239 220
240 self.add(builder) 221 self.add(builder)
241 } 222 }
@@ -272,14 +253,20 @@ impl Completions {
272 .add_to(self); 253 .add_to(self);
273 } 254 }
274 255
275 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { 256 pub(crate) fn add_enum_variant(
257 &mut self,
258 ctx: &CompletionContext,
259 variant: hir::EnumVariant,
260 local_name: Option<String>,
261 ) {
276 let is_deprecated = is_deprecated(variant, ctx.db); 262 let is_deprecated = is_deprecated(variant, ctx.db);
277 let name = variant.name(ctx.db); 263 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
278 let detail_types = variant 264 let detail_types = variant
279 .fields(ctx.db) 265 .fields(ctx.db)
280 .into_iter() 266 .into_iter()
281 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); 267 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
282 let detail = match variant.kind(ctx.db) { 268 let variant_kind = variant.kind(ctx.db);
269 let detail = match variant_kind {
283 StructKind::Tuple | StructKind::Unit => detail_types 270 StructKind::Tuple | StructKind::Unit => detail_types
284 .map(|(_, t)| t.display(ctx.db).to_string()) 271 .map(|(_, t)| t.display(ctx.db).to_string())
285 .sep_by(", ") 272 .sep_by(", ")
@@ -291,12 +278,70 @@ impl Completions {
291 .surround_with("{ ", " }") 278 .surround_with("{ ", " }")
292 .to_string(), 279 .to_string(),
293 }; 280 };
294 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 281 let mut res =
295 .kind(CompletionItemKind::EnumVariant) 282 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
296 .set_documentation(variant.docs(ctx.db)) 283 .kind(CompletionItemKind::EnumVariant)
297 .set_deprecated(is_deprecated) 284 .set_documentation(variant.docs(ctx.db))
298 .detail(detail) 285 .set_deprecated(is_deprecated)
299 .add_to(self); 286 .detail(detail);
287
288 if variant_kind == StructKind::Tuple {
289 let params = Params::Anonymous(variant.fields(ctx.db).len());
290 res = res.add_call_parens(ctx, name, params)
291 }
292
293 res.add_to(self);
294 }
295}
296
297enum Params {
298 Named(Vec<String>),
299 Anonymous(usize),
300}
301
302impl Params {
303 fn len(&self) -> usize {
304 match self {
305 Params::Named(xs) => xs.len(),
306 Params::Anonymous(len) => *len,
307 }
308 }
309
310 fn is_empty(&self) -> bool {
311 self.len() == 0
312 }
313}
314
315impl Builder {
316 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
317 if !ctx.config.add_call_parenthesis {
318 return self;
319 }
320 if ctx.use_item_syntax.is_some() || ctx.is_call {
321 return self;
322 }
323 // If not an import, add parenthesis automatically.
324 tested_by!(inserts_parens_for_function_calls);
325
326 let (snippet, label) = if params.is_empty() {
327 (format!("{}()$0", name), format!("{}()", name))
328 } else {
329 self = self.trigger_call_info();
330 let snippet = match (ctx.config.add_call_argument_snippets, params) {
331 (true, Params::Named(params)) => {
332 let function_params_snippet = params
333 .iter()
334 .enumerate()
335 .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
336 .sep_by(", ");
337 format!("{}({})$0", name, function_params_snippet)
338 }
339 _ => format!("{}($0)", name),
340 };
341
342 (snippet, format!("{}(…)", name))
343 };
344 self.lookup_by(name).label(label).insert_snippet(snippet)
300 } 345 }
301} 346}
302 347
@@ -366,12 +411,14 @@ mod tests {
366 @r###" 411 @r###"
367 [ 412 [
368 CompletionItem { 413 CompletionItem {
369 label: "Foo", 414 label: "Foo(…)",
370 source_range: [115; 117), 415 source_range: [115; 117),
371 delete: [115; 117), 416 delete: [115; 117),
372 insert: "Foo", 417 insert: "Foo($0)",
373 kind: EnumVariant, 418 kind: EnumVariant,
419 lookup: "Foo",
374 detail: "(i32, i32)", 420 detail: "(i32, i32)",
421 trigger_call_info: true,
375 }, 422 },
376 ]"### 423 ]"###
377 ); 424 );
@@ -546,6 +593,101 @@ mod tests {
546 } 593 }
547 594
548 #[test] 595 #[test]
596 fn inserts_parens_for_tuple_enums() {
597 assert_debug_snapshot!(
598 do_reference_completion(
599 r"
600 enum Option<T> { Some(T), None }
601 use Option::*;
602 fn main() -> Option<i32> {
603 Som<|>
604 }
605 "
606 ),
607 @r###"
608 [
609 CompletionItem {
610 label: "None",
611 source_range: [144; 147),
612 delete: [144; 147),
613 insert: "None",
614 kind: EnumVariant,
615 detail: "()",
616 },
617 CompletionItem {
618 label: "Option",
619 source_range: [144; 147),
620 delete: [144; 147),
621 insert: "Option",
622 kind: Enum,
623 },
624 CompletionItem {
625 label: "Some(…)",
626 source_range: [144; 147),
627 delete: [144; 147),
628 insert: "Some($0)",
629 kind: EnumVariant,
630 lookup: "Some",
631 detail: "(T)",
632 trigger_call_info: true,
633 },
634 CompletionItem {
635 label: "main()",
636 source_range: [144; 147),
637 delete: [144; 147),
638 insert: "main()$0",
639 kind: Function,
640 lookup: "main",
641 detail: "fn main() -> Option<i32>",
642 },
643 ]
644 "###
645 );
646 assert_debug_snapshot!(
647 do_reference_completion(
648 r"
649 enum Option<T> { Some(T), None }
650 use Option::*;
651 fn main(value: Option<i32>) {
652 match value {
653 Som<|>
654 }
655 }
656 "
657 ),
658 @r###"
659 [
660 CompletionItem {
661 label: "None",
662 source_range: [185; 188),
663 delete: [185; 188),
664 insert: "None",
665 kind: EnumVariant,
666 detail: "()",
667 },
668 CompletionItem {
669 label: "Option",
670 source_range: [185; 188),
671 delete: [185; 188),
672 insert: "Option",
673 kind: Enum,
674 },
675 CompletionItem {
676 label: "Some(…)",
677 source_range: [185; 188),
678 delete: [185; 188),
679 insert: "Some($0)",
680 kind: EnumVariant,
681 lookup: "Some",
682 detail: "(T)",
683 trigger_call_info: true,
684 },
685 ]
686 "###
687 );
688 }
689
690 #[test]
549 fn arg_snippets_for_method_call() { 691 fn arg_snippets_for_method_call() {
550 assert_debug_snapshot!( 692 assert_debug_snapshot!(
551 do_reference_completion( 693 do_reference_completion(
@@ -819,7 +961,8 @@ mod tests {
819 } 961 }
820 962
821 #[test] 963 #[test]
822 fn dont_insert_macro_call_braces_in_use() { 964 fn dont_insert_macro_call_parens_unncessary() {
965 covers!(dont_insert_macro_call_parens_unncessary);
823 assert_debug_snapshot!( 966 assert_debug_snapshot!(
824 do_reference_completion( 967 do_reference_completion(
825 r" 968 r"
@@ -845,6 +988,41 @@ mod tests {
845 }, 988 },
846 ] 989 ]
847 "### 990 "###
848 ) 991 );
992
993 assert_debug_snapshot!(
994 do_reference_completion(
995 r"
996 //- /main.rs
997 macro_rules frobnicate {
998 () => ()
999 }
1000 fn main() {
1001 frob<|>!();
1002 }
1003 "
1004 ),
1005 @r###"
1006 [
1007 CompletionItem {
1008 label: "frobnicate!",
1009 source_range: [56; 60),
1010 delete: [56; 60),
1011 insert: "frobnicate",
1012 kind: Macro,
1013 detail: "macro_rules! frobnicate",
1014 },
1015 CompletionItem {
1016 label: "main()",
1017 source_range: [56; 60),
1018 delete: [56; 60),
1019 insert: "main()$0",
1020 kind: Function,
1021 lookup: "main",
1022 detail: "fn main()",
1023 },
1024 ]
1025 "###
1026 );
849 } 1027 }
850} 1028}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index c1d7ddaf2..901ad104c 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -101,6 +101,14 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
101 fix, 101 fix,
102 }) 102 })
103 }) 103 })
104 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
105 res.borrow_mut().push(Diagnostic {
106 range: d.highlight_range(),
107 message: d.message(),
108 severity: Severity::Error,
109 fix: None,
110 })
111 })
104 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 112 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
105 let node = d.ast(db); 113 let node = d.ast(db);
106 let replacement = format!("Ok({})", node.syntax()); 114 let replacement = format!("Ok({})", node.syntax());
@@ -291,7 +299,7 @@ mod tests {
291 fn check_no_diagnostic(content: &str) { 299 fn check_no_diagnostic(content: &str) {
292 let (analysis, file_id) = single_file(content); 300 let (analysis, file_id) = single_file(content);
293 let diagnostics = analysis.diagnostics(file_id).unwrap(); 301 let diagnostics = analysis.diagnostics(file_id).unwrap();
294 assert_eq!(diagnostics.len(), 0); 302 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one");
295 } 303 }
296 304
297 #[test] 305 #[test]
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index d57451cc8..e61846995 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -399,17 +399,17 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option
399 399
400 match_ast! { 400 match_ast! {
401 match node { 401 match node {
402 ast::FnDef(it) => { it.doc_comment_text() }, 402 ast::FnDef(it) => it.doc_comment_text(),
403 ast::StructDef(it) => { it.doc_comment_text() }, 403 ast::StructDef(it) => it.doc_comment_text(),
404 ast::EnumDef(it) => { it.doc_comment_text() }, 404 ast::EnumDef(it) => it.doc_comment_text(),
405 ast::TraitDef(it) => { it.doc_comment_text() }, 405 ast::TraitDef(it) => it.doc_comment_text(),
406 ast::Module(it) => { it.doc_comment_text() }, 406 ast::Module(it) => it.doc_comment_text(),
407 ast::TypeAliasDef(it) => { it.doc_comment_text() }, 407 ast::TypeAliasDef(it) => it.doc_comment_text(),
408 ast::ConstDef(it) => { it.doc_comment_text() }, 408 ast::ConstDef(it) => it.doc_comment_text(),
409 ast::StaticDef(it) => { it.doc_comment_text() }, 409 ast::StaticDef(it) => it.doc_comment_text(),
410 ast::RecordFieldDef(it) => { it.doc_comment_text() }, 410 ast::RecordFieldDef(it) => it.doc_comment_text(),
411 ast::EnumVariant(it) => { it.doc_comment_text() }, 411 ast::EnumVariant(it) => it.doc_comment_text(),
412 ast::MacroCall(it) => { it.doc_comment_text() }, 412 ast::MacroCall(it) => it.doc_comment_text(),
413 _ => None, 413 _ => None,
414 } 414 }
415 } 415 }
@@ -424,16 +424,16 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
424 424
425 match_ast! { 425 match_ast! {
426 match node { 426 match node {
427 ast::FnDef(it) => { it.short_label() }, 427 ast::FnDef(it) => it.short_label(),
428 ast::StructDef(it) => { it.short_label() }, 428 ast::StructDef(it) => it.short_label(),
429 ast::EnumDef(it) => { it.short_label() }, 429 ast::EnumDef(it) => it.short_label(),
430 ast::TraitDef(it) => { it.short_label() }, 430 ast::TraitDef(it) => it.short_label(),
431 ast::Module(it) => { it.short_label() }, 431 ast::Module(it) => it.short_label(),
432 ast::TypeAliasDef(it) => { it.short_label() }, 432 ast::TypeAliasDef(it) => it.short_label(),
433 ast::ConstDef(it) => { it.short_label() }, 433 ast::ConstDef(it) => it.short_label(),
434 ast::StaticDef(it) => { it.short_label() }, 434 ast::StaticDef(it) => it.short_label(),
435 ast::RecordFieldDef(it) => { it.short_label() }, 435 ast::RecordFieldDef(it) => it.short_label(),
436 ast::EnumVariant(it) => { it.short_label() }, 436 ast::EnumVariant(it) => it.short_label(),
437 _ => None, 437 _ => None,
438 } 438 }
439 } 439 }
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index 5774e9a8b..7a774785c 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -117,18 +117,18 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
117 117
118 decl_with_detail(it, Some(detail)) 118 decl_with_detail(it, Some(detail))
119 }, 119 },
120 ast::StructDef(it) => { decl(it) }, 120 ast::StructDef(it) => decl(it),
121 ast::EnumDef(it) => { decl(it) }, 121 ast::EnumDef(it) => decl(it),
122 ast::EnumVariant(it) => { decl(it) }, 122 ast::EnumVariant(it) => decl(it),
123 ast::TraitDef(it) => { decl(it) }, 123 ast::TraitDef(it) => decl(it),
124 ast::Module(it) => { decl(it) }, 124 ast::Module(it) => decl(it),
125 ast::TypeAliasDef(it) => { 125 ast::TypeAliasDef(it) => {
126 let ty = it.type_ref(); 126 let ty = it.type_ref();
127 decl_with_type_ref(it, ty) 127 decl_with_type_ref(it, ty)
128 }, 128 },
129 ast::RecordFieldDef(it) => { decl_with_ascription(it) }, 129 ast::RecordFieldDef(it) => decl_with_ascription(it),
130 ast::ConstDef(it) => { decl_with_ascription(it) }, 130 ast::ConstDef(it) => decl_with_ascription(it),
131 ast::StaticDef(it) => { decl_with_ascription(it) }, 131 ast::StaticDef(it) => decl_with_ascription(it),
132 ast::ImplDef(it) => { 132 ast::ImplDef(it) => {
133 let target_type = it.target_type()?; 133 let target_type = it.target_type()?;
134 let target_trait = it.target_trait(); 134 let target_trait = it.target_trait();
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 869a4708b..bd2688df1 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -18,9 +18,9 @@ pub(crate) fn goto_type_definition(
18 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { 18 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
19 let ty = match_ast! { 19 let ty = match_ast! {
20 match node { 20 match node {
21 ast::Expr(expr) => { sema.type_of_expr(&expr)? }, 21 ast::Expr(expr) => sema.type_of_expr(&expr)?,
22 ast::Pat(pat) => { sema.type_of_pat(&pat)? }, 22 ast::Pat(pat) => sema.type_of_pat(&pat)?,
23 _ => { return None }, 23 _ => return None,
24 } 24 }
25 }; 25 };
26 26
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 285381086..5599f143f 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -10,6 +10,11 @@
10// For proving that RootDatabase is RefUnwindSafe. 10// For proving that RootDatabase is RefUnwindSafe.
11#![recursion_limit = "128"] 11#![recursion_limit = "128"]
12 12
13#[allow(unused)]
14macro_rules! eprintln {
15 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
16}
17
13pub mod mock_analysis; 18pub mod mock_analysis;
14mod source_change; 19mod source_change;
15 20
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index 1236cb773..5e1f135c5 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -7,4 +7,5 @@ test_utils::marks!(
7 dont_complete_current_use 7 dont_complete_current_use
8 test_resolve_parent_module_on_module_decl 8 test_resolve_parent_module_on_module_decl
9 search_filters_by_range 9 search_filters_by_range
10 dont_insert_macro_call_parens_unncessary
10); 11);
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 74877e90f..9433f3a24 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -49,8 +49,8 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
49fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 49fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
50 match_ast! { 50 match_ast! {
51 match item { 51 match item {
52 ast::FnDef(it) => { runnable_fn(sema, it) }, 52 ast::FnDef(it) => runnable_fn(sema, it),
53 ast::Module(it) => { runnable_mod(sema, it) }, 53 ast::Module(it) => runnable_mod(sema, it),
54 _ => None, 54 _ => None,
55 } 55 }
56 } 56 }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1abb891c1..7b93ff2d2 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -5,12 +5,14 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase; 5use ra_ide_db::symbol_index::SymbolsDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::make::try_expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
8use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit}; 8use ra_syntax::ast::{
9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9 ArgList, AstToken, CallExpr, Comment, Expr, MethodCallExpr, RecordField, RecordLit,
10};
11use ra_syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder}; 12use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
12use std::collections::HashMap; 14use std::collections::HashMap;
13use std::str::FromStr; 15use std::{iter::once, str::FromStr};
14 16
15#[derive(Debug, PartialEq)] 17#[derive(Debug, PartialEq)]
16pub struct SsrError(String); 18pub struct SsrError(String);
@@ -219,6 +221,50 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
219 ) 221 )
220 } 222 }
221 223
224 fn check_call_and_method_call(
225 pattern: CallExpr,
226 code: MethodCallExpr,
227 placeholders: &[Var],
228 match_: Match,
229 ) -> Option<Match> {
230 let (pattern_name, pattern_type_args) = if let Some(Expr::PathExpr(path_exr)) =
231 pattern.expr()
232 {
233 let segment = path_exr.path().and_then(|p| p.segment());
234 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
235 } else {
236 (None, None)
237 };
238 let match_ = check_opt_nodes(pattern_name, code.name_ref(), placeholders, match_)?;
239 let match_ =
240 check_opt_nodes(pattern_type_args, code.type_arg_list(), placeholders, match_)?;
241 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
242 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
243 let code_args = once(code.expr()?).chain(code_args);
244 check_iter(pattern_args, code_args, placeholders, match_)
245 }
246
247 fn check_method_call_and_call(
248 pattern: MethodCallExpr,
249 code: CallExpr,
250 placeholders: &[Var],
251 match_: Match,
252 ) -> Option<Match> {
253 let (code_name, code_type_args) = if let Some(Expr::PathExpr(path_exr)) = code.expr() {
254 let segment = path_exr.path().and_then(|p| p.segment());
255 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
256 } else {
257 (None, None)
258 };
259 let match_ = check_opt_nodes(pattern.name_ref(), code_name, placeholders, match_)?;
260 let match_ =
261 check_opt_nodes(pattern.type_arg_list(), code_type_args, placeholders, match_)?;
262 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
263 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
264 let pattern_args = once(pattern.expr()?).chain(pattern_args);
265 check_iter(pattern_args, code_args, placeholders, match_)
266 }
267
222 fn check_opt_nodes( 268 fn check_opt_nodes(
223 pattern: Option<impl AstNode>, 269 pattern: Option<impl AstNode>,
224 code: Option<impl AstNode>, 270 code: Option<impl AstNode>,
@@ -227,8 +273,8 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
227 ) -> Option<Match> { 273 ) -> Option<Match> {
228 match (pattern, code) { 274 match (pattern, code) {
229 (Some(pattern), Some(code)) => check( 275 (Some(pattern), Some(code)) => check(
230 &SyntaxElement::from(pattern.syntax().clone()), 276 &pattern.syntax().clone().into(),
231 &SyntaxElement::from(code.syntax().clone()), 277 &code.syntax().clone().into(),
232 placeholders, 278 placeholders,
233 match_, 279 match_,
234 ), 280 ),
@@ -237,6 +283,33 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
237 } 283 }
238 } 284 }
239 285
286 fn check_iter<T, I1, I2>(
287 mut pattern: I1,
288 mut code: I2,
289 placeholders: &[Var],
290 match_: Match,
291 ) -> Option<Match>
292 where
293 T: AstNode,
294 I1: Iterator<Item = T>,
295 I2: Iterator<Item = T>,
296 {
297 pattern
298 .by_ref()
299 .zip(code.by_ref())
300 .fold(Some(match_), |accum, (a, b)| {
301 accum.and_then(|match_| {
302 check(
303 &a.syntax().clone().into(),
304 &b.syntax().clone().into(),
305 placeholders,
306 match_,
307 )
308 })
309 })
310 .filter(|_| pattern.next().is_none() && code.next().is_none())
311 }
312
240 fn check( 313 fn check(
241 pattern: &SyntaxElement, 314 pattern: &SyntaxElement,
242 code: &SyntaxElement, 315 code: &SyntaxElement,
@@ -260,6 +333,14 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
260 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone())) 333 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
261 { 334 {
262 check_record_lit(pattern, code, placeholders, match_) 335 check_record_lit(pattern, code, placeholders, match_)
336 } else if let (Some(pattern), Some(code)) =
337 (CallExpr::cast(pattern.clone()), MethodCallExpr::cast(code.clone()))
338 {
339 check_call_and_method_call(pattern, code, placeholders, match_)
340 } else if let (Some(pattern), Some(code)) =
341 (MethodCallExpr::cast(pattern.clone()), CallExpr::cast(code.clone()))
342 {
343 check_method_call_and_call(pattern, code, placeholders, match_)
263 } else { 344 } else {
264 let mut pattern_children = pattern 345 let mut pattern_children = pattern
265 .children_with_tokens() 346 .children_with_tokens()
@@ -290,16 +371,15 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
290 let kind = pattern.pattern.kind(); 371 let kind = pattern.pattern.kind();
291 let matches = code 372 let matches = code
292 .descendants() 373 .descendants()
293 .filter(|n| n.kind() == kind) 374 .filter(|n| {
375 n.kind() == kind
376 || (kind == SyntaxKind::CALL_EXPR && n.kind() == SyntaxKind::METHOD_CALL_EXPR)
377 || (kind == SyntaxKind::METHOD_CALL_EXPR && n.kind() == SyntaxKind::CALL_EXPR)
378 })
294 .filter_map(|code| { 379 .filter_map(|code| {
295 let match_ = 380 let match_ =
296 Match { place: code.clone(), binding: HashMap::new(), ignored_comments: vec![] }; 381 Match { place: code.clone(), binding: HashMap::new(), ignored_comments: vec![] };
297 check( 382 check(&pattern.pattern.clone().into(), &code.into(), &pattern.vars, match_)
298 &SyntaxElement::from(pattern.pattern.clone()),
299 &SyntaxElement::from(code),
300 &pattern.vars,
301 match_,
302 )
303 }) 383 })
304 .collect(); 384 .collect();
305 SsrMatches { matches } 385 SsrMatches { matches }
@@ -498,4 +578,22 @@ mod tests {
498 "fn main() { foo::new(1, 2) }", 578 "fn main() { foo::new(1, 2) }",
499 ) 579 )
500 } 580 }
581
582 #[test]
583 fn ssr_call_and_method_call() {
584 assert_ssr_transform(
585 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)",
586 "fn main() { get().bar.foo::<'a>(1); }",
587 "fn main() { foo2(get().bar, 1); }",
588 )
589 }
590
591 #[test]
592 fn ssr_method_call_and_call() {
593 assert_ssr_transform(
594 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)",
595 "fn main() { X::foo::<i32>(x, 1); }",
596 "fn main() { x.foo2(1); }",
597 )
598 }
501} 599}
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 7d908f987..83d161f45 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -281,7 +281,7 @@ fn highlight_element(
281 | T![loop] 281 | T![loop]
282 | T![match] 282 | T![match]
283 | T![return] 283 | T![return]
284 | T![while] => h | HighlightModifier::Control, 284 | T![while] => h | HighlightModifier::ControlFlow,
285 T![unsafe] => h | HighlightModifier::Unsafe, 285 T![unsafe] => h | HighlightModifier::Unsafe,
286 _ => h, 286 _ => h,
287 } 287 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 8835a5de2..e8b138e1a 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -44,7 +44,7 @@ pub enum HighlightTag {
44#[repr(u8)] 44#[repr(u8)]
45pub enum HighlightModifier { 45pub enum HighlightModifier {
46 /// Used with keywords like `if` and `break`. 46 /// Used with keywords like `if` and `break`.
47 Control = 0, 47 ControlFlow = 0,
48 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 48 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
49 /// not. 49 /// not.
50 Definition, 50 Definition,
@@ -91,7 +91,7 @@ impl fmt::Display for HighlightTag {
91 91
92impl HighlightModifier { 92impl HighlightModifier {
93 const ALL: &'static [HighlightModifier] = &[ 93 const ALL: &'static [HighlightModifier] = &[
94 HighlightModifier::Control, 94 HighlightModifier::ControlFlow,
95 HighlightModifier::Definition, 95 HighlightModifier::Definition,
96 HighlightModifier::Mutable, 96 HighlightModifier::Mutable,
97 HighlightModifier::Unsafe, 97 HighlightModifier::Unsafe,
@@ -99,7 +99,7 @@ impl HighlightModifier {
99 99
100 fn as_str(self) -> &'static str { 100 fn as_str(self) -> &'static str {
101 match self { 101 match self {
102 HighlightModifier::Control => "control", 102 HighlightModifier::ControlFlow => "control",
103 HighlightModifier::Definition => "declaration", 103 HighlightModifier::Definition => "declaration",
104 HighlightModifier::Mutable => "mutable", 104 HighlightModifier::Mutable => "mutable",
105 HighlightModifier::Unsafe => "unsafe", 105 HighlightModifier::Unsafe => "unsafe",