aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/call_info.rs30
-rw-r--r--crates/ra_ide/src/completion.rs23
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs12
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs39
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs18
-rw-r--r--crates/ra_ide/src/completion/completion_config.rs35
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs15
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs36
-rw-r--r--crates/ra_ide/src/completion/presentation.rs342
-rw-r--r--crates/ra_ide/src/display/function_signature.rs69
-rw-r--r--crates/ra_ide/src/lib.rs4
-rw-r--r--crates/ra_ide/src/marks.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs8
15 files changed, 556 insertions, 109 deletions
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index f95b6baf3..5da254a6e 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -19,10 +19,24 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
19 call_info_for_token(&sema, token) 19 call_info_for_token(&sema, token)
20} 20}
21 21
22pub(crate) fn call_info_for_token( 22#[derive(Debug)]
23 sema: &Semantics<RootDatabase>, 23pub(crate) struct ActiveParameter {
24 token: SyntaxToken, 24 /// FIXME: should be `Type` and `Name
25) -> Option<CallInfo> { 25 pub(crate) ty: String,
26 pub(crate) name: String,
27}
28
29impl ActiveParameter {
30 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
31 call_info(db, position)?.into_active_parameter()
32 }
33
34 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
35 call_info_for_token(sema, token)?.into_active_parameter()
36 }
37}
38
39fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> {
26 // Find the calling expression and it's NameRef 40 // Find the calling expression and it's NameRef
27 let calling_node = FnCallNode::with_node(&token.parent())?; 41 let calling_node = FnCallNode::with_node(&token.parent())?;
28 42
@@ -160,6 +174,14 @@ impl FnCallNode {
160} 174}
161 175
162impl CallInfo { 176impl CallInfo {
177 fn into_active_parameter(self) -> Option<ActiveParameter> {
178 let idx = self.active_parameter?;
179 let ty = self.signature.parameter_types.get(idx)?.clone();
180 let name = self.signature.parameter_names.get(idx)?.clone();
181 let res = ActiveParameter { ty, name };
182 Some(res)
183 }
184
163 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self { 185 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
164 let signature = FunctionSignature::from_hir(db, function); 186 let signature = FunctionSignature::from_hir(db, function);
165 187
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 4a1a2a04a..f0e02180b 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -1,5 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod completion_config;
3mod completion_item; 4mod completion_item;
4mod completion_context; 5mod completion_context;
5mod presentation; 6mod presentation;
@@ -28,27 +29,11 @@ use crate::{
28 FilePosition, 29 FilePosition,
29}; 30};
30 31
31pub use crate::completion::completion_item::{ 32pub use crate::completion::{
32 CompletionItem, CompletionItemKind, InsertTextFormat, 33 completion_config::CompletionConfig,
34 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
33}; 35};
34 36
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct CompletionConfig {
37 pub enable_postfix_completions: bool,
38 pub add_call_parenthesis: bool,
39 pub add_call_argument_snippets: bool,
40}
41
42impl Default for CompletionConfig {
43 fn default() -> Self {
44 CompletionConfig {
45 enable_postfix_completions: true,
46 add_call_parenthesis: true,
47 add_call_argument_snippets: true,
48 }
49 }
50}
51
52/// Main entry point for completion. We run completion as a two-phase process. 37/// Main entry point for completion. We run completion as a two-phase process.
53/// 38///
54/// First, we look at the position and collect a so-called `CompletionContext. 39/// First, we look at the position and collect a so-called `CompletionContext.
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index f433faef3..b93153b48 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -2,9 +2,11 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind;
6use crate::{ 5use crate::{
7 completion::{completion_context::CompletionContext, completion_item::Completions}, 6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
8 CompletionItem, 10 CompletionItem,
9}; 11};
10use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 38f9c34e7..adefb290e 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
42} 42}
43 43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword) 46 .kind(CompletionItemKind::Keyword);
47 .insert_snippet(snippet) 47
48 .build() 48 match ctx.config.snippet_cap {
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 }
52 .build()
49} 53}
50 54
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 29c2881c6..8d397b0fe 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -6,6 +6,7 @@ use ra_syntax::{
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
9use crate::{ 10use crate::{
10 completion::{ 11 completion::{
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
@@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
32 None => return, 33 None => return,
33 }; 34 };
34 35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40
35 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 41 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
36 postfix_snippet( 42 postfix_snippet(
37 ctx, 43 ctx,
44 cap,
38 &dot_receiver, 45 &dot_receiver,
39 "if", 46 "if",
40 "if expr {}", 47 "if expr {}",
@@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
43 .add_to(acc); 50 .add_to(acc);
44 postfix_snippet( 51 postfix_snippet(
45 ctx, 52 ctx,
53 cap,
46 &dot_receiver, 54 &dot_receiver,
47 "while", 55 "while",
48 "while expr {}", 56 "while expr {}",
@@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
52 } 60 }
53 61
54 // !&&&42 is a compiler error, ergo process it before considering the references 62 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc);
56 65
57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 66 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc); 67 .add_to(acc);
68 postfix_snippet(
69 ctx,
70 cap,
71 &dot_receiver,
72 "refm",
73 "&mut expr",
74 &format!("&mut {}", receiver_text),
75 )
76 .add_to(acc);
60 77
61 // The rest of the postfix completions create an expression that moves an argument, 78 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation 79 // so it's better to consider references now to avoid breaking the compilation
@@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
66 83
67 postfix_snippet( 84 postfix_snippet(
68 ctx, 85 ctx,
86 cap,
69 &dot_receiver, 87 &dot_receiver,
70 "match", 88 "match",
71 "match expr {}", 89 "match expr {}",
@@ -75,6 +93,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
75 93
76 postfix_snippet( 94 postfix_snippet(
77 ctx, 95 ctx,
96 cap,
78 &dot_receiver, 97 &dot_receiver,
79 "box", 98 "box",
80 "Box::new(expr)", 99 "Box::new(expr)",
@@ -82,8 +101,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
82 ) 101 )
83 .add_to(acc); 102 .add_to(acc);
84 103
85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) 104 postfix_snippet(
86 .add_to(acc); 105 ctx,
106 cap,
107 &dot_receiver,
108 "dbg",
109 "dbg!(expr)",
110 &format!("dbg!({})", receiver_text),
111 )
112 .add_to(acc);
87} 113}
88 114
89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 115fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@@ -108,6 +134,7 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
108 134
109fn postfix_snippet( 135fn postfix_snippet(
110 ctx: &CompletionContext, 136 ctx: &CompletionContext,
137 cap: SnippetCap,
111 receiver: &ast::Expr, 138 receiver: &ast::Expr,
112 label: &str, 139 label: &str,
113 detail: &str, 140 detail: &str,
@@ -121,7 +148,7 @@ fn postfix_snippet(
121 }; 148 };
122 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 149 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
123 .detail(detail) 150 .detail(detail)
124 .snippet_edit(edit) 151 .snippet_edit(cap, edit)
125} 152}
126 153
127#[cfg(test)] 154#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index f731e9b9a..4bccfbfed 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::completion::{ 3use crate::completion::{
4 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionKind, Completions, 5 CompletionItemKind, CompletionKind, Completions,
6}; 6};
7 7
8fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder { 8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(snippet) 10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet) 11 .kind(CompletionItemKind::Snippet)
12} 12}
13 13
@@ -15,17 +15,27 @@ pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { 15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return; 16 return;
17 } 17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
18 22
19 snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
20 snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
21} 25}
22 26
23pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 27pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
24 if !ctx.is_new_item { 28 if !ctx.is_new_item {
25 return; 29 return;
26 } 30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
27 snippet( 36 snippet(
28 ctx, 37 ctx,
38 cap,
29 "Test function", 39 "Test function",
30 "\ 40 "\
31#[test] 41#[test]
@@ -36,8 +46,8 @@ fn ${1:feature}() {
36 .lookup_by("tfn") 46 .lookup_by("tfn")
37 .add_to(acc); 47 .add_to(acc);
38 48
39 snippet(ctx, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 49 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
40 snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); 50 snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
41} 51}
42 52
43#[cfg(test)] 53#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 2ec0e7ce9..c39943252 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -122,7 +122,7 @@ fn add_function_impl(
122 ctx: &CompletionContext, 122 ctx: &CompletionContext,
123 func: &hir::Function, 123 func: &hir::Function,
124) { 124) {
125 let display = FunctionSignature::from_hir(ctx.db, *func); 125 let signature = FunctionSignature::from_hir(ctx.db, *func);
126 126
127 let fn_name = func.name(ctx.db).to_string(); 127 let fn_name = func.name(ctx.db).to_string();
128 128
@@ -141,12 +141,20 @@ fn add_function_impl(
141 } else { 141 } else {
142 CompletionItemKind::Function 142 CompletionItemKind::Function
143 }; 143 };
144
145 let snippet = format!("{} {{\n $0\n}}", display);
146
147 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); 144 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
148 145
149 builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); 146 match ctx.config.snippet_cap {
147 Some(cap) => {
148 let snippet = format!("{} {{\n $0\n}}", signature);
149 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
150 }
151 None => {
152 let header = format!("{} {{", signature);
153 builder.text_edit(TextEdit::replace(range, header))
154 }
155 }
156 .kind(completion_kind)
157 .add_to(acc);
150} 158}
151 159
152fn add_type_alias_impl( 160fn add_type_alias_impl(
diff --git a/crates/ra_ide/src/completion/completion_config.rs b/crates/ra_ide/src/completion/completion_config.rs
new file mode 100644
index 000000000..71b49ace8
--- /dev/null
+++ b/crates/ra_ide/src/completion/completion_config.rs
@@ -0,0 +1,35 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15impl CompletionConfig {
16 pub fn allow_snippets(&mut self, yes: bool) {
17 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct SnippetCap {
23 _private: (),
24}
25
26impl Default for CompletionConfig {
27 fn default() -> Self {
28 CompletionConfig {
29 enable_postfix_completions: true,
30 add_call_parenthesis: true,
31 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }),
33 }
34 }
35}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index cfc5c34df..a76d1ce45 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -31,7 +31,10 @@ pub(crate) struct CompletionContext<'a> {
31 pub(super) use_item_syntax: Option<ast::UseItem>, 31 pub(super) use_item_syntax: Option<ast::UseItem>,
32 pub(super) record_lit_syntax: Option<ast::RecordLit>, 32 pub(super) record_lit_syntax: Option<ast::RecordLit>,
33 pub(super) record_pat_syntax: Option<ast::RecordPat>, 33 pub(super) record_pat_syntax: Option<ast::RecordPat>,
34 pub(super) record_field_syntax: Option<ast::RecordField>,
34 pub(super) impl_def: Option<ast::ImplDef>, 35 pub(super) impl_def: Option<ast::ImplDef>,
36 /// FIXME: `ActiveParameter` is string-based, which is very wrong
37 pub(super) active_parameter: Option<ActiveParameter>,
35 pub(super) is_param: bool, 38 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 39 /// If a name-binding or reference to a const in a pattern.
37 /// Irrefutable patterns (like let) are excluded. 40 /// Irrefutable patterns (like let) are excluded.
@@ -94,7 +97,9 @@ impl<'a> CompletionContext<'a> {
94 use_item_syntax: None, 97 use_item_syntax: None,
95 record_lit_syntax: None, 98 record_lit_syntax: None,
96 record_pat_syntax: None, 99 record_pat_syntax: None,
100 record_field_syntax: None,
97 impl_def: None, 101 impl_def: None,
102 active_parameter: ActiveParameter::at(db, position),
98 is_param: false, 103 is_param: false,
99 is_pat_binding_or_const: false, 104 is_pat_binding_or_const: false,
100 is_trivial_path: false, 105 is_trivial_path: false,
@@ -279,6 +284,14 @@ impl<'a> CompletionContext<'a> {
279 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 284 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
280 .find_map(ast::FnDef::cast); 285 .find_map(ast::FnDef::cast);
281 286
287 self.record_field_syntax = self
288 .sema
289 .ancestors_with_macros(self.token.parent())
290 .take_while(|it| {
291 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
292 })
293 .find_map(ast::RecordField::cast);
294
282 let parent = match name_ref.syntax().parent() { 295 let parent = match name_ref.syntax().parent() {
283 Some(it) => it, 296 Some(it) => it,
284 None => return, 297 None => return,
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index bc0f1aff5..fb06cc125 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -2,6 +2,7 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use super::completion_config::SnippetCap;
5use hir::Documentation; 6use hir::Documentation;
6use ra_syntax::TextRange; 7use ra_syntax::TextRange;
7use ra_text_edit::TextEdit; 8use ra_text_edit::TextEdit;
@@ -51,6 +52,9 @@ pub struct CompletionItem {
51 /// If completing a function call, ask the editor to show parameter popup 52 /// If completing a function call, ask the editor to show parameter popup
52 /// after completion. 53 /// after completion.
53 trigger_call_info: bool, 54 trigger_call_info: bool,
55
56 /// Score is useful to pre select or display in better order completion items
57 score: Option<CompletionScore>,
54} 58}
55 59
56// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 60// We use custom debug for CompletionItem to make `insta`'s diffs more readable.
@@ -80,6 +84,9 @@ impl fmt::Debug for CompletionItem {
80 if self.deprecated { 84 if self.deprecated {
81 s.field("deprecated", &true); 85 s.field("deprecated", &true);
82 } 86 }
87 if let Some(score) = &self.score {
88 s.field("score", score);
89 }
83 if self.trigger_call_info { 90 if self.trigger_call_info {
84 s.field("trigger_call_info", &true); 91 s.field("trigger_call_info", &true);
85 } 92 }
@@ -87,6 +94,14 @@ impl fmt::Debug for CompletionItem {
87 } 94 }
88} 95}
89 96
97#[derive(Debug, Clone, Copy)]
98pub enum CompletionScore {
99 /// If only type match
100 TypeMatch,
101 /// If type and name match
102 TypeAndNameMatch,
103}
104
90#[derive(Debug, Clone, Copy, PartialEq, Eq)] 105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum CompletionItemKind { 106pub enum CompletionItemKind {
92 Snippet, 107 Snippet,
@@ -147,6 +162,7 @@ impl CompletionItem {
147 text_edit: None, 162 text_edit: None,
148 deprecated: None, 163 deprecated: None,
149 trigger_call_info: None, 164 trigger_call_info: None,
165 score: None,
150 } 166 }
151 } 167 }
152 /// What user sees in pop-up in the UI. 168 /// What user sees in pop-up in the UI.
@@ -175,7 +191,7 @@ impl CompletionItem {
175 } 191 }
176 /// What string is used for filtering. 192 /// What string is used for filtering.
177 pub fn lookup(&self) -> &str { 193 pub fn lookup(&self) -> &str {
178 self.lookup.as_deref().unwrap_or_else(|| self.label()) 194 self.lookup.as_deref().unwrap_or(&self.label)
179 } 195 }
180 196
181 pub fn kind(&self) -> Option<CompletionItemKind> { 197 pub fn kind(&self) -> Option<CompletionItemKind> {
@@ -186,6 +202,10 @@ impl CompletionItem {
186 self.deprecated 202 self.deprecated
187 } 203 }
188 204
205 pub fn score(&self) -> Option<CompletionScore> {
206 self.score
207 }
208
189 pub fn trigger_call_info(&self) -> bool { 209 pub fn trigger_call_info(&self) -> bool {
190 self.trigger_call_info 210 self.trigger_call_info
191 } 211 }
@@ -206,6 +226,7 @@ pub(crate) struct Builder {
206 text_edit: Option<TextEdit>, 226 text_edit: Option<TextEdit>,
207 deprecated: Option<bool>, 227 deprecated: Option<bool>,
208 trigger_call_info: Option<bool>, 228 trigger_call_info: Option<bool>,
229 score: Option<CompletionScore>,
209} 230}
210 231
211impl Builder { 232impl Builder {
@@ -235,6 +256,7 @@ impl Builder {
235 completion_kind: self.completion_kind, 256 completion_kind: self.completion_kind,
236 deprecated: self.deprecated.unwrap_or(false), 257 deprecated: self.deprecated.unwrap_or(false),
237 trigger_call_info: self.trigger_call_info.unwrap_or(false), 258 trigger_call_info: self.trigger_call_info.unwrap_or(false),
259 score: self.score,
238 } 260 }
239 } 261 }
240 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 262 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -249,7 +271,11 @@ impl Builder {
249 self.insert_text = Some(insert_text.into()); 271 self.insert_text = Some(insert_text.into());
250 self 272 self
251 } 273 }
252 pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { 274 pub(crate) fn insert_snippet(
275 mut self,
276 _cap: SnippetCap,
277 snippet: impl Into<String>,
278 ) -> Builder {
253 self.insert_text_format = InsertTextFormat::Snippet; 279 self.insert_text_format = InsertTextFormat::Snippet;
254 self.insert_text(snippet) 280 self.insert_text(snippet)
255 } 281 }
@@ -261,7 +287,7 @@ impl Builder {
261 self.text_edit = Some(edit); 287 self.text_edit = Some(edit);
262 self 288 self
263 } 289 }
264 pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { 290 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
265 self.insert_text_format = InsertTextFormat::Snippet; 291 self.insert_text_format = InsertTextFormat::Snippet;
266 self.text_edit(edit) 292 self.text_edit(edit)
267 } 293 }
@@ -285,6 +311,10 @@ impl Builder {
285 self.deprecated = Some(deprecated); 311 self.deprecated = Some(deprecated);
286 self 312 self
287 } 313 }
314 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
315 self.score = Some(score);
316 self
317 }
288 pub(crate) fn trigger_call_info(mut self) -> Builder { 318 pub(crate) fn trigger_call_info(mut self) -> Builder {
289 self.trigger_call_info = Some(true); 319 self.trigger_call_info = Some(true);
290 self 320 self
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 2189cef65..5e2b8b920 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -11,7 +11,7 @@ use crate::{
11 CompletionKind, Completions, 11 CompletionKind, Completions,
12 }, 12 },
13 display::{const_label, macro_label, type_label, FunctionSignature}, 13 display::{const_label, macro_label, type_label, FunctionSignature},
14 RootDatabase, 14 CompletionScore, RootDatabase,
15}; 15};
16 16
17impl Completions { 17impl Completions {
@@ -22,16 +22,20 @@ impl Completions {
22 ty: &Type, 22 ty: &Type,
23 ) { 23 ) {
24 let is_deprecated = is_deprecated(field, ctx.db); 24 let is_deprecated = is_deprecated(field, ctx.db);
25 CompletionItem::new( 25 let ty = ty.display(ctx.db).to_string();
26 CompletionKind::Reference, 26 let name = field.name(ctx.db);
27 ctx.source_range(), 27 let mut completion_item =
28 field.name(ctx.db).to_string(), 28 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
29 ) 29 .kind(CompletionItemKind::Field)
30 .kind(CompletionItemKind::Field) 30 .detail(ty.clone())
31 .detail(ty.display(ctx.db).to_string()) 31 .set_documentation(field.docs(ctx.db))
32 .set_documentation(field.docs(ctx.db)) 32 .set_deprecated(is_deprecated);
33 .set_deprecated(is_deprecated) 33
34 .add_to(self); 34 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
35 completion_item = completion_item.set_score(score);
36 }
37
38 completion_item.add_to(self);
35 } 39 }
36 40
37 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { 41 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
@@ -110,17 +114,19 @@ impl Completions {
110 114
111 // Add `<>` for generic types 115 // Add `<>` for generic types
112 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
113 let has_non_default_type_params = match resolution { 117 if let Some(cap) = ctx.config.snippet_cap {
114 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 118 let has_non_default_type_params = match resolution {
115 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 119 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
116 _ => false, 120 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
117 }; 121 _ => false,
118 if has_non_default_type_params { 122 };
119 tested_by!(inserts_angle_brackets_for_generics); 123 if has_non_default_type_params {
120 completion_item = completion_item 124 tested_by!(inserts_angle_brackets_for_generics);
121 .lookup_by(local_name.clone()) 125 completion_item = completion_item
122 .label(format!("{}<…>", local_name)) 126 .lookup_by(local_name.clone())
123 .insert_snippet(format!("{}<$0>", local_name)); 127 .label(format!("{}<…>", local_name))
128 .insert_snippet(cap, format!("{}<$0>", local_name));
129 }
124 } 130 }
125 } 131 }
126 132
@@ -180,13 +186,16 @@ impl Completions {
180 .set_deprecated(is_deprecated(macro_, ctx.db)) 186 .set_deprecated(is_deprecated(macro_, ctx.db))
181 .detail(detail); 187 .detail(detail);
182 188
183 builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call { 189 builder = match ctx.config.snippet_cap {
184 tested_by!(dont_insert_macro_call_parens_unncessary); 190 Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
185 builder.insert_text(name) 191 let macro_braces_to_insert =
186 } else { 192 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
187 let macro_braces_to_insert = 193 builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
188 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); 194 }
189 builder.insert_snippet(macro_declaration + macro_braces_to_insert) 195 _ => {
196 tested_by!(dont_insert_macro_call_parens_unncessary);
197 builder.insert_text(name)
198 }
190 }; 199 };
191 200
192 self.add(builder); 201 self.add(builder);
@@ -300,6 +309,42 @@ impl Completions {
300 } 309 }
301} 310}
302 311
312pub(crate) fn compute_score(
313 ctx: &CompletionContext,
314 // FIXME: this definitely should be a `Type`
315 ty: &str,
316 name: &str,
317) -> Option<CompletionScore> {
318 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
319 tested_by!(test_struct_field_completion_in_record_lit);
320 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
321 (
322 struct_field.name(ctx.db).to_string(),
323 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
324 )
325 } else if let Some(active_parameter) = &ctx.active_parameter {
326 tested_by!(test_struct_field_completion_in_func_call);
327 (active_parameter.name.clone(), active_parameter.ty.clone())
328 } else {
329 return None;
330 };
331
332 // Compute score
333 // For the same type
334 if &active_type != ty {
335 return None;
336 }
337
338 let mut res = CompletionScore::TypeMatch;
339
340 // If same type + same name then go top position
341 if active_name == name {
342 res = CompletionScore::TypeAndNameMatch
343 }
344
345 Some(res)
346}
347
303enum Params { 348enum Params {
304 Named(Vec<String>), 349 Named(Vec<String>),
305 Anonymous(usize), 350 Anonymous(usize),
@@ -326,6 +371,10 @@ impl Builder {
326 if ctx.use_item_syntax.is_some() || ctx.is_call { 371 if ctx.use_item_syntax.is_some() || ctx.is_call {
327 return self; 372 return self;
328 } 373 }
374 let cap = match ctx.config.snippet_cap {
375 Some(it) => it,
376 None => return self,
377 };
329 // If not an import, add parenthesis automatically. 378 // If not an import, add parenthesis automatically.
330 tested_by!(inserts_parens_for_function_calls); 379 tested_by!(inserts_parens_for_function_calls);
331 380
@@ -347,7 +396,7 @@ impl Builder {
347 396
348 (snippet, format!("{}(…)", name)) 397 (snippet, format!("{}(…)", name))
349 }; 398 };
350 self.lookup_by(name).label(label).insert_snippet(snippet) 399 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
351 } 400 }
352} 401}
353 402
@@ -1031,4 +1080,237 @@ mod tests {
1031 "### 1080 "###
1032 ); 1081 );
1033 } 1082 }
1083
1084 #[test]
1085 fn test_struct_field_completion_in_func_call() {
1086 covers!(test_struct_field_completion_in_func_call);
1087 assert_debug_snapshot!(
1088 do_reference_completion(
1089 r"
1090 struct A { another_field: i64, the_field: u32, my_string: String }
1091 fn test(my_param: u32) -> u32 { my_param }
1092 fn foo(a: A) {
1093 test(a.<|>)
1094 }
1095 ",
1096 ),
1097 @r###"
1098 [
1099 CompletionItem {
1100 label: "another_field",
1101 source_range: [201; 201),
1102 delete: [201; 201),
1103 insert: "another_field",
1104 kind: Field,
1105 detail: "i64",
1106 },
1107 CompletionItem {
1108 label: "my_string",
1109 source_range: [201; 201),
1110 delete: [201; 201),
1111 insert: "my_string",
1112 kind: Field,
1113 detail: "{unknown}",
1114 },
1115 CompletionItem {
1116 label: "the_field",
1117 source_range: [201; 201),
1118 delete: [201; 201),
1119 insert: "the_field",
1120 kind: Field,
1121 detail: "u32",
1122 score: TypeMatch,
1123 },
1124 ]
1125 "###
1126 );
1127 }
1128
1129 #[test]
1130 fn test_struct_field_completion_in_func_call_with_type_and_name() {
1131 assert_debug_snapshot!(
1132 do_reference_completion(
1133 r"
1134 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1135 fn test(the_field: u32) -> u32 { the_field }
1136 fn foo(a: A) {
1137 test(a.<|>)
1138 }
1139 ",
1140 ),
1141 @r###"
1142 [
1143 CompletionItem {
1144 label: "another_field",
1145 source_range: [208; 208),
1146 delete: [208; 208),
1147 insert: "another_field",
1148 kind: Field,
1149 detail: "i64",
1150 },
1151 CompletionItem {
1152 label: "another_good_type",
1153 source_range: [208; 208),
1154 delete: [208; 208),
1155 insert: "another_good_type",
1156 kind: Field,
1157 detail: "u32",
1158 score: TypeMatch,
1159 },
1160 CompletionItem {
1161 label: "the_field",
1162 source_range: [208; 208),
1163 delete: [208; 208),
1164 insert: "the_field",
1165 kind: Field,
1166 detail: "u32",
1167 score: TypeAndNameMatch,
1168 },
1169 ]
1170 "###
1171 );
1172 }
1173
1174 #[test]
1175 fn test_struct_field_completion_in_record_lit() {
1176 covers!(test_struct_field_completion_in_func_call);
1177 assert_debug_snapshot!(
1178 do_reference_completion(
1179 r"
1180 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1181 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1182 fn foo(a: A) {
1183 let b = B {
1184 the_field: a.<|>
1185 };
1186 }
1187 ",
1188 ),
1189 @r###"
1190 [
1191 CompletionItem {
1192 label: "another_field",
1193 source_range: [270; 270),
1194 delete: [270; 270),
1195 insert: "another_field",
1196 kind: Field,
1197 detail: "i64",
1198 },
1199 CompletionItem {
1200 label: "another_good_type",
1201 source_range: [270; 270),
1202 delete: [270; 270),
1203 insert: "another_good_type",
1204 kind: Field,
1205 detail: "u32",
1206 score: TypeMatch,
1207 },
1208 CompletionItem {
1209 label: "the_field",
1210 source_range: [270; 270),
1211 delete: [270; 270),
1212 insert: "the_field",
1213 kind: Field,
1214 detail: "u32",
1215 score: TypeAndNameMatch,
1216 },
1217 ]
1218 "###
1219 );
1220 }
1221
1222 #[test]
1223 fn test_struct_field_completion_in_record_lit_and_fn_call() {
1224 assert_debug_snapshot!(
1225 do_reference_completion(
1226 r"
1227 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1228 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1229 fn test(the_field: i64) -> i64 { the_field }
1230 fn foo(a: A) {
1231 let b = B {
1232 the_field: test(a.<|>)
1233 };
1234 }
1235 ",
1236 ),
1237 @r###"
1238 [
1239 CompletionItem {
1240 label: "another_field",
1241 source_range: [336; 336),
1242 delete: [336; 336),
1243 insert: "another_field",
1244 kind: Field,
1245 detail: "i64",
1246 score: TypeMatch,
1247 },
1248 CompletionItem {
1249 label: "another_good_type",
1250 source_range: [336; 336),
1251 delete: [336; 336),
1252 insert: "another_good_type",
1253 kind: Field,
1254 detail: "u32",
1255 },
1256 CompletionItem {
1257 label: "the_field",
1258 source_range: [336; 336),
1259 delete: [336; 336),
1260 insert: "the_field",
1261 kind: Field,
1262 detail: "u32",
1263 },
1264 ]
1265 "###
1266 );
1267 }
1268
1269 #[test]
1270 fn test_struct_field_completion_in_fn_call_and_record_lit() {
1271 assert_debug_snapshot!(
1272 do_reference_completion(
1273 r"
1274 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1275 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1276 fn test(the_field: i64) -> i64 { the_field }
1277 fn foo(a: A) {
1278 test(B {
1279 the_field: a.<|>
1280 });
1281 }
1282 ",
1283 ),
1284 @r###"
1285 [
1286 CompletionItem {
1287 label: "another_field",
1288 source_range: [328; 328),
1289 delete: [328; 328),
1290 insert: "another_field",
1291 kind: Field,
1292 detail: "i64",
1293 },
1294 CompletionItem {
1295 label: "another_good_type",
1296 source_range: [328; 328),
1297 delete: [328; 328),
1298 insert: "another_good_type",
1299 kind: Field,
1300 detail: "u32",
1301 score: TypeMatch,
1302 },
1303 CompletionItem {
1304 label: "the_field",
1305 source_range: [328; 328),
1306 delete: [328; 328),
1307 insert: "the_field",
1308 kind: Field,
1309 detail: "u32",
1310 score: TypeAndNameMatch,
1311 },
1312 ]
1313 "###
1314 );
1315 }
1034} 1316}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index b967a6816..b5e2785fe 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -36,6 +36,8 @@ pub struct FunctionSignature {
36 pub parameters: Vec<String>, 36 pub parameters: Vec<String>,
37 /// Parameter names of the function 37 /// Parameter names of the function
38 pub parameter_names: Vec<String>, 38 pub parameter_names: Vec<String>,
39 /// Parameter types of the function
40 pub parameter_types: Vec<String>,
39 /// Optional return type 41 /// Optional return type
40 pub ret_type: Option<String>, 42 pub ret_type: Option<String>,
41 /// Where predicates 43 /// Where predicates
@@ -62,14 +64,20 @@ impl FunctionSignature {
62 return None; 64 return None;
63 }; 65 };
64 66
65 let params = st 67 let mut params = vec![];
66 .fields(db) 68 let mut parameter_types = vec![];
67 .into_iter() 69 for field in st.fields(db).into_iter() {
68 .map(|field: hir::StructField| { 70 let ty = field.signature_ty(db);
69 let ty = field.signature_ty(db); 71 let raw_param = format!("{}", ty.display(db));
70 format!("{}", ty.display(db)) 72
71 }) 73 if let Some(param_type) = raw_param.split(':').nth(1) {
72 .collect(); 74 parameter_types.push(param_type[1..].to_string());
75 } else {
76 // useful when you have tuple struct
77 parameter_types.push(raw_param.clone());
78 }
79 params.push(raw_param);
80 }
73 81
74 Some( 82 Some(
75 FunctionSignature { 83 FunctionSignature {
@@ -79,6 +87,7 @@ impl FunctionSignature {
79 ret_type: node.name().map(|n| n.text().to_string()), 87 ret_type: node.name().map(|n| n.text().to_string()),
80 parameters: params, 88 parameters: params,
81 parameter_names: vec![], 89 parameter_names: vec![],
90 parameter_types,
82 generic_parameters: generic_parameters(&node), 91 generic_parameters: generic_parameters(&node),
83 where_predicates: where_predicates(&node), 92 where_predicates: where_predicates(&node),
84 doc: None, 93 doc: None,
@@ -99,15 +108,21 @@ impl FunctionSignature {
99 108
100 let name = format!("{}::{}", parent_name, variant.name(db)); 109 let name = format!("{}::{}", parent_name, variant.name(db));
101 110
102 let params = variant 111 let mut params = vec![];
103 .fields(db) 112 let mut parameter_types = vec![];
104 .into_iter() 113 for field in variant.fields(db).into_iter() {
105 .map(|field: hir::StructField| { 114 let ty = field.signature_ty(db);
106 let name = field.name(db); 115 let raw_param = format!("{}", ty.display(db));
107 let ty = field.signature_ty(db); 116 if let Some(param_type) = raw_param.split(':').nth(1) {
108 format!("{}: {}", name, ty.display(db)) 117 parameter_types.push(param_type[1..].to_string());
109 }) 118 } else {
110 .collect(); 119 // The unwrap_or_else is useful when you have tuple
120 parameter_types.push(raw_param);
121 }
122 let name = field.name(db);
123
124 params.push(format!("{}: {}", name, ty.display(db)));
125 }
111 126
112 Some( 127 Some(
113 FunctionSignature { 128 FunctionSignature {
@@ -117,6 +132,7 @@ impl FunctionSignature {
117 ret_type: None, 132 ret_type: None,
118 parameters: params, 133 parameters: params,
119 parameter_names: vec![], 134 parameter_names: vec![],
135 parameter_types,
120 generic_parameters: vec![], 136 generic_parameters: vec![],
121 where_predicates: vec![], 137 where_predicates: vec![],
122 doc: None, 138 doc: None,
@@ -139,6 +155,7 @@ impl FunctionSignature {
139 ret_type: None, 155 ret_type: None,
140 parameters: params, 156 parameters: params,
141 parameter_names: vec![], 157 parameter_names: vec![],
158 parameter_types: vec![],
142 generic_parameters: vec![], 159 generic_parameters: vec![],
143 where_predicates: vec![], 160 where_predicates: vec![],
144 doc: None, 161 doc: None,
@@ -151,18 +168,27 @@ impl FunctionSignature {
151 168
152impl From<&'_ ast::FnDef> for FunctionSignature { 169impl From<&'_ ast::FnDef> for FunctionSignature {
153 fn from(node: &ast::FnDef) -> FunctionSignature { 170 fn from(node: &ast::FnDef) -> FunctionSignature {
154 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>) { 171 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
155 let mut res = vec![]; 172 let mut res = vec![];
173 let mut res_types = vec![];
156 let mut has_self_param = false; 174 let mut has_self_param = false;
157 if let Some(param_list) = node.param_list() { 175 if let Some(param_list) = node.param_list() {
158 if let Some(self_param) = param_list.self_param() { 176 if let Some(self_param) = param_list.self_param() {
159 has_self_param = true; 177 has_self_param = true;
160 res.push(self_param.syntax().text().to_string()) 178 let raw_param = self_param.syntax().text().to_string();
179
180 res_types.push(
181 raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(),
182 );
183 res.push(raw_param);
161 } 184 }
162 185
163 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 186 res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
187 res_types.extend(param_list.params().map(|param| {
188 param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string()
189 }));
164 } 190 }
165 (has_self_param, res) 191 (has_self_param, res, res_types)
166 } 192 }
167 193
168 fn param_name_list(node: &ast::FnDef) -> Vec<String> { 194 fn param_name_list(node: &ast::FnDef) -> Vec<String> {
@@ -192,7 +218,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
192 res 218 res
193 } 219 }
194 220
195 let (has_self_param, parameters) = param_list(node); 221 let (has_self_param, parameters, parameter_types) = param_list(node);
196 222
197 FunctionSignature { 223 FunctionSignature {
198 kind: CallableKind::Function, 224 kind: CallableKind::Function,
@@ -204,6 +230,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
204 .map(|n| n.syntax().text().to_string()), 230 .map(|n| n.syntax().text().to_string()),
205 parameters, 231 parameters,
206 parameter_names: param_name_list(node), 232 parameter_names: param_name_list(node),
233 parameter_types,
207 generic_parameters: generic_parameters(node), 234 generic_parameters: generic_parameters(node),
208 where_predicates: where_predicates(node), 235 where_predicates: where_predicates(node),
209 // docs are processed separately 236 // docs are processed separately
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 5599f143f..f692fbaa2 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -67,7 +67,9 @@ use crate::display::ToNav;
67pub use crate::{ 67pub use crate::{
68 assists::{Assist, AssistId}, 68 assists::{Assist, AssistId},
69 call_hierarchy::CallItem, 69 call_hierarchy::CallItem,
70 completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat}, 70 completion::{
71 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
72 },
71 diagnostics::Severity, 73 diagnostics::Severity,
72 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 74 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
73 expand_macro::ExpandedMacro, 75 expand_macro::ExpandedMacro,
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index eee44e886..bea30fe2a 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -9,4 +9,6 @@ test_utils::marks!(
9 search_filters_by_range 9 search_filters_by_range
10 dont_insert_macro_call_parens_unncessary 10 dont_insert_macro_call_parens_unncessary
11 self_fulfilling_completion 11 self_fulfilling_completion
12 test_struct_field_completion_in_func_call
13 test_struct_field_completion_in_record_lit
12); 14);
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 93d502875..d9912155b 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -19,7 +19,7 @@ use ra_syntax::{
19}; 19};
20use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
21 21
22use crate::{call_info::call_info_for_token, Analysis, FileId}; 22use crate::{call_info::ActiveParameter, Analysis, FileId};
23 23
24pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
25pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; 25pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
@@ -364,10 +364,8 @@ fn highlight_injection(
364 literal: ast::RawString, 364 literal: ast::RawString,
365 expanded: SyntaxToken, 365 expanded: SyntaxToken,
366) -> Option<()> { 366) -> Option<()> {
367 let call_info = call_info_for_token(&sema, expanded)?; 367 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
368 let idx = call_info.active_parameter?; 368 if !active_parameter.name.starts_with("ra_fixture") {
369 let name = call_info.signature.parameter_names.get(idx)?;
370 if !name.starts_with("ra_fixture") {
371 return None; 369 return None;
372 } 370 }
373 let value = literal.value()?; 371 let value = literal.value()?;