diff options
Diffstat (limited to 'crates/ra_ide')
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; | |||
10 | mod complete_fn_param; | 10 | mod complete_fn_param; |
11 | mod complete_keyword; | 11 | mod complete_keyword; |
12 | mod complete_snippet; | 12 | mod complete_snippet; |
13 | mod complete_path; | 13 | mod complete_qualified_path; |
14 | mod complete_scope; | 14 | mod complete_unqualified_path; |
15 | mod complete_postfix; | 15 | mod complete_postfix; |
16 | mod complete_macro_in_item_position; | 16 | mod complete_macro_in_item_position; |
17 | mod complete_trait_impl; | 17 | mod 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. |
6 | pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 7 | use crate::completion::{CompletionContext, Completions}; |
8 | 8 | ||
9 | pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(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. |
2 | use crate::completion::{CompletionContext, Completions}; | 2 | use crate::completion::{CompletionContext, Completions}; |
3 | use ra_syntax::{ast, ast::NameOwner, SmolStr}; | ||
4 | 3 | ||
5 | pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 4 | pub(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 | |||
31 | fn 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 | ||
45 | fn 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)] |
60 | mod tests { | 20 | mod 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 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | use hir::{ModuleDef, ScopeDef}; | ||
5 | 4 | ||
6 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(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 | ||
8 | use crate::{ | 8 | use 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(¯o_name) { | 132 | for (idx, s) in docs.match_indices(¯o_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 | |||
297 | enum Params { | ||
298 | Named(Vec<String>), | ||
299 | Anonymous(usize), | ||
300 | } | ||
301 | |||
302 | impl 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 | |||
315 | impl 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)] | ||
14 | macro_rules! eprintln { | ||
15 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | ||
16 | } | ||
17 | |||
13 | pub mod mock_analysis; | 18 | pub mod mock_analysis; |
14 | mod source_change; | 19 | mod 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> { | |||
49 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 49 | fn 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}; | |||
5 | use ra_ide_db::symbol_index::SymbolsDatabase; | 5 | use ra_ide_db::symbol_index::SymbolsDatabase; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::ast::make::try_expr_from_text; | 7 | use ra_syntax::ast::make::try_expr_from_text; |
8 | use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit}; | 8 | use ra_syntax::ast::{ |
9 | use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; | 9 | ArgList, AstToken, CallExpr, Comment, Expr, MethodCallExpr, RecordField, RecordLit, |
10 | }; | ||
11 | use ra_syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode}; | ||
10 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 12 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
11 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
12 | use std::collections::HashMap; | 14 | use std::collections::HashMap; |
13 | use std::str::FromStr; | 15 | use std::{iter::once, str::FromStr}; |
14 | 16 | ||
15 | #[derive(Debug, PartialEq)] | 17 | #[derive(Debug, PartialEq)] |
16 | pub struct SsrError(String); | 18 | pub 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)] |
45 | pub enum HighlightModifier { | 45 | pub 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 | ||
92 | impl HighlightModifier { | 92 | impl 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", |